1 /*************************************************************************
2 
3   XEDITOR.C -- Simple text editor for JSPICE3
4   Copyright (C) Stephen R. Whiteley 1995, All rights reserved
5   Version 1.0 1/17/95
6 
7  *************************************************************************/
8 
9 #ifdef SPICE_MAKE
10 #include "spice.h"
11 #include "ftedefs.h"
12 #include "spfteext.h"
13 #else
14 #define HAVE_X11
15 #endif
16 
17 #ifdef HAVE_X11
18 
19 #include <stdio.h>
20 #include <signal.h>
21 #include <ctype.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <pwd.h>
26 
27 #include <X11/Intrinsic.h>
28 #include <X11/StringDefs.h>
29 #include <X11/Xaw/Paned.h>
30 #include <X11/Xaw/AsciiText.h>
31 #include <X11/Xaw/Label.h>
32 #include <X11/Xaw/Viewport.h>
33 #include <X11/Xaw/Command.h>
34 #include <X11/Xaw/Box.h>
35 #include <X11/Shell.h>
36 
37 typedef enum {QUIT, SAVE, SAVEAS, SOURCE, LOAD, TEXTMOD} event_type;
38 
39 #ifdef STAND_ALONE
40 typedef enum {NOGO, NO_EXIST, READ_OK, WRITE_OK} check_type;
41 typedef struct {
42     Widget shell;
43     Widget form;
44     Widget text;
45     Widget title;
46     Widget butbox;
47     Widget popup;
48     Widget popup_text;
49     Widget message;
50     Atom wm_delete;
51     String *popup_sens_list;
52     Bool TextChanged;
53     event_type Last;
54     String saved_as;
55 } widget_bag;
56 
57 #else
58 #define XEDITOR
59 #include "x11util.h"
60 #endif
61 
62 #ifdef __STDC__
63 extern int xeditor(char*);
64 #else
65 extern int xeditor();
66 #endif
67 
68 #ifdef __STDC__
69 static char *nextarg(void);
70 static int  errorhandler(Display*, XErrorEvent*);
71 static void CRaction(Widget, XEvent*, String*, Cardinal*);
72 static void QUaction(Widget, XEvent*, String*, Cardinal*);
73 static void Quit(Widget, XtPointer, XtPointer);
74 static void Save(Widget, XtPointer, XtPointer);
75 static void SaveAs(Widget, XtPointer, XtPointer);
76 static void Source(Widget, XtPointer, XtPointer);
77 static void Load(Widget, XtPointer, XtPointer);
78 static void Change(Widget, XtPointer, XtPointer);
79 static void DoSaveAs(Widget, XtPointer, XtPointer);
80 static Bool same(char*, char*);
81 static void DoLoad(Widget, XtPointer, XtPointer);
82 #ifdef STAND_ALONE
83 static void PopUpInput(String, String, void(*)(), widget_bag*);
84 static void PopDownInput(Widget, XtPointer, XtPointer);
85 static void PopUpMessage(String, widget_bag*);
86 static void PopDownMessage(Widget, XtPointer, XtPointer);
87 static void CenterWidgetOnWidget(Widget, Widget);
88 static check_type CheckFile(char*, int, widget_bag*);
89 static void ToTop(Widget, XtPointer, XEvent*, Boolean*);
90 static char *tilde_expand(char*);
91 #endif
92 static void PopUpHelp(Widget, XtPointer, XtPointer);
93 static void PopDownHelp(Widget, XtPointer, XtPointer);
94 
95 #else
96 
97 static char *nextarg();
98 static int  errorhandler();
99 static void CRaction();
100 static void QUaction();
101 static void Quit();
102 static void Save();
103 static void SaveAs();
104 static void Source();
105 static void Load();
106 static void Change();
107 static void DoSaveAs();
108 static Bool same();
109 static void DoLoad();
110 #ifdef STAND_ALONE
111 static void PopUpInput();
112 static void PopDownInput();
113 static void PopUpMessage();
114 static void PopDownMessage();
115 static void CenterWidgetOnWidget();
116 static check_type CheckFile();
117 static void ToTop();
118 static char *tilde_expand();
119 #endif
120 static void PopUpHelp();
121 static void PopDownHelp();
122 #endif
123 
124 #ifdef STAND_ALONE
125 Display *Xdisplay;
126 #else
127 extern Display *Xdisplay;
128 #endif
129 
130 static char *oneLineTranslations =
131     "<Key>Return: cr_action()\n<Key>Delete: delete-next-character()";
132 static char *textTranslations = "<Key>Delete: delete-next-character()";
133 static char *butTranslations = "<Btn1Up>: notify() reset()";
134 static char *wmTranslations = "<Message>WM_PROTOCOLS: quit_action()";
135 static char *sens_list[] = {"save_as", "load", 0};
136 
137 #ifdef STAND_ALONE
138 
139 static String fallback_resources[] = {
140 
141     "xeditor.xeditor.background: lightgrey",
142     "xeditor.xeditor.buttonbox.background: lightblue",
143     "xeditor.xeditor.buttonbox.Command.background: yellow",
144 
145     "xeditor.popup.form.background: lightblue",
146     "xeditor.popup.form.Command.background: yellow",
147     "xeditor.popup.form.Label.background: pink",
148 
149     "xeditor.popup_m.form_m.background: pink",
150     "xeditor.popup_m.form_m.Command.background: yellow",
151 
152     "xeditor.popup_h.form_h.background: lightgrey",
153     "xeditor.popup_h.form_h.buttonbox.background: lightblue",
154     "xeditor.popup_h.form_h.buttonbox.Command.background: yellow",
155     "xeditor.popup_h.form_h.buttonbox.Label.background: pink",
156     NULL
157 };
158 
159 static XtActionsRec actions[] = {
160     {"cr_action", CRaction},
161     {"quit_action", QUaction}
162 };
163 
164 int  NumArgs;
165 char **Args;
166 
167 
168 int
main(argc,argv)169 main(argc, argv)
170 
171 int argc;
172 char **argv;
173 {
174     XtAppContext app_con;
175 
176     /* initialize X toolkit */
177     XtToolkitInitialize();
178     app_con = XtCreateApplicationContext();
179     XtAppSetFallbackResources(app_con, fallback_resources);
180     Xdisplay = XtOpenDisplay(app_con, NULL, NULL, "Xeditor", NULL, 0,
181         &argc, argv);
182     if (!Xdisplay) {
183         fprintf(stderr, "Error: can't open display\n");
184         exit(1);
185     }
186     XtAppAddActions(app_con, actions, XtNumber(actions));
187     XSetErrorHandler(errorhandler);
188 
189     NumArgs = argc;
190     Args = argv;
191     if (NumArgs > 1) {
192         Args++;
193         NumArgs--;
194         xeditor(*Args);
195     }
196     else
197         xeditor(NULL);
198     XtAppMainLoop(app_con);
199     return (0);
200 }
201 
202 
203 static char *
nextarg()204 nextarg()
205 {
206     if (NumArgs > 1) {
207         Args++;
208         NumArgs--;
209         return (*Args);
210     }
211     return ("");
212 }
213 
214 
215 static int
errorhandler(display,errorev)216 errorhandler(display, errorev)
217 
218 Display *display;
219 XErrorEvent *errorev;
220 {
221     char ErrorMessage[1024];
222 
223     XGetErrorText(display, errorev->error_code, ErrorMessage, 1024);
224     printf(ErrorMessage);
225     return (0);
226 }
227 #endif
228 
229 
230 static void
CRaction(caller,call_data,params,numparams)231 CRaction(caller, call_data, params, numparams)
232 
233 Widget caller;
234 XEvent *call_data;
235 String *params;
236 Cardinal *numparams;
237 {
238     /* simulate a button press when CR entered in text of popup */
239     Widget action = XtNameToWidget(XtParent(caller), "action");
240 
241     if (action)
242         XtCallCallbacks(action, XtNcallback, NULL);
243 }
244 
245 
246 static void
QUaction(caller,call_data,params,numparams)247 QUaction(caller, call_data, params, numparams)
248 
249 Widget caller;
250 XEvent *call_data;
251 String *params;
252 Cardinal *numparams;
253 {
254     /* simulate a Quit button press when DeleteWindow requested */
255     Widget quit;
256 
257     quit = XtNameToWidget(caller, "xeditor.buttonbox.quit");
258     if (!quit)
259         quit = XtNameToWidget(caller, "form.cancel");
260     if (!quit)
261         quit = XtNameToWidget(caller, "form_h.buttonbox.cancel_h");
262     if (!quit)
263         quit = XtNameToWidget(caller, "form_m.cancel_m");
264 
265     if (quit)
266         XtCallCallbacks(quit, XtNcallback, NULL);
267 }
268 
269 
270 int
xeditor(fname)271 xeditor(fname)
272 char *fname;
273 
274 /* Create a new window... */
275 {
276     Widget button;
277     widget_bag *w;
278     check_type which;
279     char buf[256];
280     static char tmpl[] = "/tmp/xeXXXXXX";
281 
282     /* Assume Xt toolkit is initialized! */
283     w = (widget_bag*) XtMalloc(sizeof(widget_bag));
284 
285     w->shell = XtAppCreateShell(NULL, "Jspice3",
286         applicationShellWidgetClass, Xdisplay, NULL, 0);
287     XtOverrideTranslations(w->shell, XtParseTranslationTable(wmTranslations));
288     w->form = XtVaCreateManagedWidget("xeditor", formWidgetClass,
289         w->shell, NULL);
290 
291     w->butbox = XtVaCreateManagedWidget("buttonbox", boxWidgetClass,
292         w->form,
293         XtNwidth, 650,
294         XtNleft, XtChainLeft,
295         XtNright, XtChainLeft,
296         XtNbottom, XtChainTop,
297         XtNtop, XtChainTop,
298         NULL);
299 
300     w->title = XtVaCreateManagedWidget("titlelabel", labelWidgetClass,
301         w->form,
302         XtNborderWidth, 0,
303         XtNfromHoriz, w->butbox,
304         XtNvertDistance, 10,
305         XtNhorizDistance, 20,
306         XtNleft, XtChainLeft,
307         XtNright, XtChainLeft,
308         XtNbottom, XtChainTop,
309         XtNtop, XtChainTop,
310         XtNresizable, True,
311         NULL);
312 
313     button = XtVaCreateManagedWidget("quit", commandWidgetClass,
314         w->butbox,
315         XtNlabel, "Quit",
316         NULL);
317     XtAddCallback(button, XtNcallback, Quit, (XtPointer)w);
318     XtOverrideTranslations(button, XtParseTranslationTable(butTranslations));
319 
320     button = XtVaCreateManagedWidget("save", commandWidgetClass,
321         w->butbox,
322         XtNlabel, "Save",
323         NULL);
324     XtAddCallback(button, XtNcallback, Save, (XtPointer)w);
325     XtSetSensitive(button, False);
326 
327     button = XtVaCreateManagedWidget("save_as", commandWidgetClass,
328         w->butbox,
329         XtNlabel, "Save as",
330         NULL);
331     XtAddCallback(button, XtNcallback, SaveAs, (XtPointer)w);
332     XtOverrideTranslations(button, XtParseTranslationTable(butTranslations));
333 
334 #ifndef STAND_ALONE
335     button = XtVaCreateManagedWidget("source", commandWidgetClass,
336         w->butbox,
337         XtNlabel, "Source",
338         NULL);
339     XtAddCallback(button, XtNcallback, Source, (XtPointer)w);
340     XtOverrideTranslations(button, XtParseTranslationTable(butTranslations));
341 #endif
342 
343     button = XtVaCreateManagedWidget("load", commandWidgetClass,
344         w->butbox,
345         XtNlabel, "Load",
346         NULL);
347     XtAddCallback(button, XtNcallback, Load, (XtPointer)w);
348     XtOverrideTranslations(button, XtParseTranslationTable(butTranslations));
349 
350     button = XtVaCreateManagedWidget("help", commandWidgetClass,
351         w->butbox,
352         XtNlabel, "Help",
353         NULL);
354 #ifdef STAND_ALONE
355     XtAddCallback(button, XtNcallback, PopUpHelp, (XtPointer)w);
356     XtOverrideTranslations(button, XtParseTranslationTable(butTranslations));
357 #else
358     XtAddCallback(button, XtNcallback, PopUpHelp, (XtPointer)"xeditor");
359 #endif
360 
361     w->text = XtVaCreateManagedWidget("main_text", asciiTextWidgetClass,
362         w->form,
363         XtNtype, XawAsciiFile,
364         XtNeditType, XawtextRead,
365         XtNallowResize, True,
366         XtNscrollHorizontal, XawtextScrollWhenNeeded,
367         XtNscrollVertical, XawtextScrollWhenNeeded,
368         XtNfromVert, w->butbox,
369         XtNwidth, 660,
370         XtNheight, 350,
371         XtNstring, "/dev/null",
372         XtNtop, XtChainTop,
373         NULL);
374     XtAddCallback(XawTextGetSource(w->text), XtNcallback, Change,
375         (XtPointer)w);
376     XtOverrideTranslations(w->text, XtParseTranslationTable(textTranslations));
377 
378     XtRealizeWidget(w->shell);
379     w->wm_delete = XInternAtom(Xdisplay, "WM_DELETE_WINDOW", False);
380     XSetWMProtocols(Xdisplay, XtWindow(w->shell), &w->wm_delete, 1);
381 
382     if (fname && *fname) {
383         strcpy(buf, fname);
384         cp_pathfix(buf);
385         fname = buf;
386     }
387     which = CheckFile(fname, R_OK, w);
388     if (which == NOGO) {
389         fname = (char*)mktemp(tmpl);
390     }
391     else if (which == READ_OK) {
392         XtVaSetValues(w->text, XtNstring, fname, NULL);
393         /* switch to edit mode, file may be read only in which
394          * case the load will fail if in edit mode
395          */
396         XtVaSetValues(w->text, XtNeditType, XawtextEdit, NULL);
397     }
398     XtVaSetValues(w->title, XtNlabel, fname, NULL);
399     w->TextChanged = False;
400     w->popup = NULL;
401     w->saved_as = NULL;
402     w->Last = LOAD;
403     w->popup_sens_list = sens_list;
404 
405     return (0);
406 }
407 
408 
409 /* ARGSUSED */
410 static void
Quit(caller,client_data,call_data)411 Quit(caller, client_data, call_data)
412 
413 Widget caller;
414 XtPointer client_data, call_data;
415 {
416     widget_bag *w = (widget_bag*)client_data;
417 
418     if (w->TextChanged && (w->Last != QUIT)) {
419         w->Last = QUIT;
420         PopUpMessage(
421             "Text has been modified.  Hit Quit again to quit", w);
422         return;
423     }
424     XtDestroyWidget(w->shell);
425     XtFree((char*)w);
426 #ifdef STAND_ALONE
427     kill(0,SIGINT);
428 #endif
429 }
430 
431 
432 /* ARGSUSED */
433 static void
Save(caller,client_data,call_data)434 Save(caller, client_data, call_data)
435 
436 Widget caller;
437 XtPointer client_data, call_data;
438 {
439     widget_bag *w = (widget_bag*)client_data;
440     Arg args[1];
441     String fname;
442 
443     w->Last = SAVE;
444     XtVaGetValues(w->title, XtNlabel, &fname, NULL);
445     if (CheckFile(fname, W_OK, w) == NOGO)
446         return;
447 
448     if (!XawAsciiSaveAsFile(XawTextGetSource(w->text), fname)) {
449         PopUpMessage("Unknown error, text not saved", w);
450         return;
451     }
452     PopUpMessage("Text saved", w);
453     /* This can be called with TextChanged false if we saved
454      * under a new name, and made no subsequent changes.
455      */
456     if (w->TextChanged) {
457         w->TextChanged = False;
458         XtAddCallback(XawTextGetSource(w->text), XtNcallback, Change,
459             (XtPointer)w);
460     }
461     XtSetSensitive(caller, False);
462     if (w->saved_as) {
463         XtFree(w->saved_as);
464         w->saved_as = NULL;
465     }
466 }
467 
468 
469 /* ARGSUSED */
470 static void
SaveAs(caller,client_data,call_data)471 SaveAs(caller, client_data, call_data)
472 
473 Widget caller;
474 XtPointer client_data, call_data;
475 {
476     widget_bag *w = (widget_bag*)client_data;
477     String fname;
478     XawTextPosition begin, end;
479 
480     w->Last = SAVEAS;
481     XawTextGetSelectionPos(w->text, &begin, &end);
482     if (begin == end) {
483         XtVaGetValues(w->title, XtNlabel, &fname, NULL);
484         PopUpInput(fname, "Save File", DoSaveAs, (widget_bag*)client_data);
485     }
486     else
487         PopUpInput("", "Save Block", DoSaveAs, (widget_bag*)client_data);
488 }
489 
490 
491 #ifndef STAND_ALONE
492 /* ARGSUSED */
493 static void
Source(caller,client_data,call_data)494 Source(caller, client_data, call_data)
495 
496 Widget caller;
497 XtPointer client_data, call_data;
498 {
499     widget_bag *w = (widget_bag*)client_data;
500     char *fname;
501     FILE *fp;
502 
503     w->Last = SOURCE;
504     if (w->TextChanged) {
505         fname = smktemp("sp");
506         if ((fp = inp_pathopen(fname, "w")) == NULL) {
507             PopUpMessage("Unknown error, can't create temp file", w);
508             return;
509         }
510         fclose(fp);
511         if (!XawAsciiSaveAsFile(XawTextGetSource(w->text), fname)) {
512             PopUpMessage("Unknown error, can't write temp file", w);
513             return;
514         }
515         inp_srcedit(fname, False, False);
516         PopUpMessage("Text sourced (temporary file)", w);
517     }
518     else {
519         if (w->saved_as)
520             fname = w->saved_as;
521         else {
522             XtVaGetValues(w->title, XtNlabel, &fname, NULL);
523         }
524         fname = copy(fname);
525         inp_srcedit(fname, True, False);
526         PopUpMessage("Text sourced (permanent file)", w);
527     }
528 }
529 #endif
530 
531 
532 /* ARGSUSED */
533 static void
Load(caller,client_data,call_data)534 Load(caller, client_data, call_data)
535 
536 Widget caller;
537 XtPointer client_data, call_data;
538 {
539     widget_bag *w = (widget_bag*)client_data;
540     char *newfname = NULL;
541 
542     if (w->TextChanged && (w->Last != LOAD)) {
543         w->Last = LOAD;
544         PopUpMessage(
545             "Text has been modified.  Hit Load again to load", w);
546         return;
547     }
548 #ifdef STAND_ALONE
549     newfname = nextarg();
550 #endif
551     PopUpInput(newfname ? newfname : "", "Load file", DoLoad,
552         (widget_bag*)client_data);
553 }
554 
555 
556 /* ARGSUSED */
557 static void
Change(caller,client_data,call_data)558 Change(caller, client_data, call_data)
559 
560 Widget caller;
561 XtPointer client_data, call_data;
562 {
563     widget_bag *w = (widget_bag*)client_data;
564 
565     w->Last = TEXTMOD;
566     w->TextChanged = True;
567     XtSetSensitive(XtNameToWidget(w->butbox, "save"), True);
568     XtRemoveCallback(caller, XtNcallback, Change, (XtPointer)w);
569 }
570 
571 
572 /* ARGSUSED */
573 static void
DoSaveAs(caller,client_data,call_data)574 DoSaveAs(caller, client_data, call_data)
575 
576 Widget caller;
577 XtPointer client_data, call_data;
578 {
579     String fname, oldfname;
580     widget_bag *w = (widget_bag*)client_data;
581     XawTextPosition begin, end;
582     FILE *fp;
583     String s, string;
584     int len;
585     String mesg;
586     char buf[256];
587 
588     XtVaGetValues(w->popup_text, XtNstring, &fname, NULL);
589     if (fname && *fname) {
590         strcpy(buf, fname);
591         cp_pathfix(buf);
592         fname = buf;
593     }
594     if (CheckFile(fname, W_OK, w) == NOGO)
595         return;
596 
597     XawTextGetSelectionPos(w->text, &begin, &end);
598     if (begin == end) {
599         /* no selected text */
600         if (!XawAsciiSaveAsFile(XawTextGetSource(w->text), fname)) {
601             PopUpMessage("Unknown error, text not saved", w);
602             return;
603         }
604         XtVaGetValues(w->title, XtNlabel, &oldfname, NULL);
605         if (same(fname, oldfname)) {
606             if (!w->TextChanged) {
607                 PopUpMessage("Text not modified", w);
608                 return;
609             }
610             if (w->saved_as)
611                 XtFree(w->saved_as), w->saved_as = NULL;
612             w->TextChanged = False;
613             XtSetSensitive(XtNameToWidget(w->butbox, "save"), False);
614             XtAddCallback(XawTextGetSource(w->text), XtNcallback, Change,
615                 (XtPointer)w);
616             mesg = "Text saved";
617         }
618         else {
619             if (w->saved_as)
620                 XtFree(w->saved_as);
621             w->saved_as = XtMalloc(strlen(fname) + 1);
622             strcpy(w->saved_as, fname);
623             if (w->TextChanged) {
624                 w->TextChanged = False;
625                 XtAddCallback(XawTextGetSource(w->text), XtNcallback, Change,
626                     (XtPointer)w);
627             }
628             mesg = "Text saved under new name";
629         }
630     }
631     else {
632 
633         if ((fp = fopen(fname, "w")) == NULL) {
634             PopUpMessage("Unknown error, block not saved", w);
635             return;
636         }
637         string = XFetchBytes(Xdisplay, &len);
638         for (s = string; len; s++,len--)
639             putc(*s,fp);
640         fclose(fp);
641         XFree(string);
642         mesg = "Selected block saved";
643     }
644     PopDownInput(caller, client_data, call_data);
645     PopUpMessage(mesg, w);
646 }
647 
648 
649 static Bool
same(s,t)650 same(s, t)
651 
652 char *s, *t;
653 {
654     while (isspace(*s)) s++;
655     while (isspace(*t)) t++;
656     for (; *s && *t; s++, t++)
657         if (*s != *t) return (False);
658     if (*s && !isspace(*s)) return (False);
659     if (*t && !isspace(*t)) return (False);
660     return (True);
661 }
662 
663 
664 /* ARGSUSED */
665 static void
DoLoad(caller,client_data,call_data)666 DoLoad(caller, client_data, call_data)
667 
668 Widget caller;
669 XtPointer client_data, call_data;
670 {
671     String fname;
672     widget_bag *w = (widget_bag*)client_data;
673     check_type which;
674     char buf[256];
675 
676     XtVaGetValues(w->popup_text, XtNstring, &fname, NULL);
677     if (fname && *fname) {
678         strcpy(buf, fname);
679         cp_pathfix(buf);
680         fname = buf;
681     }
682     which = CheckFile(fname, R_OK, w);
683     if (which == NOGO)
684         return;
685     XtVaSetValues(w->text, XtNeditType, XawtextRead, NULL);
686     if (which == NO_EXIST)
687         XtVaSetValues(w->text, XtNstring, "/dev/null", NULL);
688     else
689         XtVaSetValues(w->text, XtNstring, fname, NULL);
690     XtVaSetValues(w->text, XtNeditType, XawtextEdit, NULL);
691 
692     /* ick! seems to be the only way to guarantee getting the
693      * right label size after a window resize
694      */
695     XtDestroyWidget(w->title);
696     w->title = XtVaCreateManagedWidget("titlelabel", labelWidgetClass,
697         w->form,
698         XtNborderWidth, 0,
699         XtNfromHoriz, w->butbox,
700         XtNvertDistance, 10,
701         XtNhorizDistance, 20,
702         XtNleft, XtChainLeft,
703         XtNright, XtChainLeft,
704         XtNbottom, XtChainTop,
705         XtNtop, XtChainTop,
706         XtNlabel, fname,
707         NULL);
708 
709     PopDownInput(caller, client_data, call_data);
710     if (w->TextChanged) {
711         w->TextChanged = False;
712         XtSetSensitive(XtNameToWidget(w->butbox, "save"), False);
713         XtAddCallback(XawTextGetSource(w->text), XtNcallback, Change,
714             (XtPointer)w);
715     }
716     if (w->saved_as) {
717         XtFree(w->saved_as);
718         w->saved_as = NULL;
719     }
720 }
721 
722 #ifdef STAND_ALONE
723 
724 static void
PopUpInput(initial_str,action_str,action_callback,w)725 PopUpInput(initial_str, action_str, action_callback, w)
726 
727 String initial_str, action_str;
728 void (*action_callback)();
729 widget_bag *w;
730 {
731     Widget form;
732     Widget label;
733     Widget cancel;
734     Widget action;
735     Dimension width, b_width, d;
736     char **p;
737 
738     w->popup = XtVaCreatePopupShell("popup", transientShellWidgetClass,
739         w->shell, NULL);
740     XtOverrideTranslations(w->popup, XtParseTranslationTable(wmTranslations));
741     XtAddEventHandler(w->popup, VisibilityChangeMask,
742         False, ToTop, (XtPointer)w);
743 
744     form = XtVaCreateManagedWidget("form", formWidgetClass, w->popup,
745         NULL);
746 
747     label = XtVaCreateManagedWidget("label", labelWidgetClass,
748         form,
749         XtNlabel, "Enter filename:                             ",
750         XtNleft, XtChainLeft,
751         XtNright, XtChainLeft,
752         XtNresizable, TRUE,
753         XtNborderWidth, 0,
754         NULL);
755 
756     w->popup_text = XtVaCreateManagedWidget("text", asciiTextWidgetClass,
757         form,
758         XtNfromVert, label,
759         XtNleft, XtChainLeft,
760         XtNright, XtChainRight,
761         XtNeditType, XawtextEdit,
762         XtNresizable, TRUE,
763         XtNresize, XawtextResizeWidth,
764         XtNstring, initial_str,
765         NULL);
766     XtOverrideTranslations(w->popup_text,
767         XtParseTranslationTable(oneLineTranslations));
768 
769     action = XtVaCreateManagedWidget("action", commandWidgetClass,
770         form,
771         XtNlabel, action_str,
772         XtNfromVert, w->popup_text,
773         XtNleft, XtChainLeft,
774         XtNright, XtChainLeft,
775         NULL);
776     XtOverrideTranslations(action, XtParseTranslationTable(butTranslations));
777 
778     cancel = XtVaCreateManagedWidget("cancel", commandWidgetClass,
779         form,
780         XtNlabel, "Cancel",
781         XtNfromVert, w->popup_text,
782         XtNfromHoriz, action,
783         XtNleft, XtChainLeft,
784         XtNright, XtChainLeft,
785         NULL);
786 
787     XtAddCallback(cancel, XtNcallback, PopDownInput, (XtPointer)w);
788     XtAddCallback(action, XtNcallback, action_callback, (XtPointer)w);
789     XtRealizeWidget(w->popup);
790     XSetWMProtocols(Xdisplay, XtWindow(w->popup), &w->wm_delete, 1);
791     CenterWidgetOnWidget(w->popup,w->shell);
792 
793     XtVaGetValues(form,
794         XtNwidth, &width,
795         XtNborderWidth, &b_width,
796         XtNdefaultDistance, &d,
797         NULL);
798     width -= 2*(b_width + d + 1);
799     XtVaSetValues(w->popup_text, XtNwidth, width, NULL);
800 
801     XtPopup(w->popup, XtGrabNone);
802     XtSetKeyboardFocus(w->popup, w->popup_text);
803     XtSetKeyboardFocus(w->shell, w->popup);
804     for (p = w->popup_sens_list; *p; p++)
805         XtSetSensitive(XtNameToWidget(w->butbox, *p), False);
806 }
807 
808 
809 /* ARGSUSED */
810 static void
PopDownInput(caller,client_data,call_data)811 PopDownInput(caller, client_data, call_data)
812 
813 Widget caller;
814 XtPointer client_data, call_data;
815 {
816     widget_bag *w = (widget_bag*)client_data;
817     char **p;
818 
819     XtSetKeyboardFocus(w->shell, None);
820     XtPopdown(w->popup);
821     for (p = w->popup_sens_list; *p; p++)
822         XtSetSensitive(XtNameToWidget(w->butbox, *p), True);
823     w->popup = NULL;
824 }
825 
826 
827 static void
PopUpMessage(message_str,w)828 PopUpMessage(message_str, w)
829 
830 String message_str;
831 widget_bag *w;
832 {
833     Widget popup;
834     Widget form;
835     Widget label;
836     Widget cancel;
837     XColor visualcolor, exactcolor;
838 
839     popup = XtVaCreatePopupShell("popup_m", transientShellWidgetClass,
840         w->shell, NULL);
841     XtOverrideTranslations(popup, XtParseTranslationTable(wmTranslations));
842     XtAddEventHandler(popup, VisibilityChangeMask,
843         False, ToTop, (XtPointer)w);
844 
845     form = XtVaCreateManagedWidget("form_m", formWidgetClass, popup,
846         NULL);
847 
848     cancel = XtVaCreateManagedWidget("cancel_m", commandWidgetClass,
849         form,
850         XtNlabel, "OK",
851         XtNleft, XtChainLeft,
852         XtNright, XtChainLeft,
853         NULL);
854 
855     XAllocNamedColor(Xdisplay,
856         DefaultColormap(Xdisplay, DefaultScreen(Xdisplay)),
857         "red", &exactcolor, &visualcolor);
858     label = XtVaCreateManagedWidget("label_m", labelWidgetClass,
859         form,
860         XtNlabel, message_str,
861         XtNleft, XtChainLeft,
862         XtNright, XtChainLeft,
863         XtNfromHoriz, cancel,
864         XtNresizable, TRUE,
865         XtNborderWidth, 2,
866         XtNborderColor, visualcolor.pixel,
867         NULL);
868 
869     w->message = popup;
870     XtAddCallback(cancel, XtNcallback, PopDownMessage, (XtPointer)w);
871     XtAddEventHandler(form, KeyPressMask, False, (XtEventHandler)PopDownMessage,
872         (XtPointer)w);
873     XtRealizeWidget(popup);
874     XSetWMProtocols(Xdisplay, XtWindow(popup), &w->wm_delete, 1);
875     CenterWidgetOnWidget(popup, w->shell);
876     if (w->popup)
877         XtSetSensitive(w->popup, False);
878     XtSetSensitive(w->form, False);
879     XtSetKeyboardFocus(w->shell, form);
880     XtPopup(popup, XtGrabExclusive);
881 }
882 
883 
884 /* ARGSUSED */
885 static void
PopDownMessage(caller,client_data,call_data)886 PopDownMessage(caller, client_data, call_data)
887 
888 Widget caller;
889 XtPointer client_data, call_data;
890 {
891     widget_bag *w = (widget_bag*)client_data;
892 
893     if (w->popup) {
894         XtSetKeyboardFocus(w->shell, w->popup);
895         XtPopdown(w->message);
896         XtSetSensitive(w->popup, True);
897     }
898     else {
899         XtSetKeyboardFocus(w->shell, None);
900         XtPopdown(w->message);
901     }
902     XtSetSensitive(w->form, True);
903 }
904 
905 
906 static void
CenterWidgetOnWidget(sub,master)907 CenterWidgetOnWidget(sub, master)
908 
909 Widget sub, master;
910 {
911     Dimension width, height, b_width;
912     Position x, y, max_x, max_y;
913 
914     XtVaGetValues(master,
915         XtNx, &x,
916         XtNy, &y,
917         XtNwidth, &width,
918         XtNheight, &height,
919         XtNborderWidth, &b_width,
920         NULL);
921 
922     width += 2*b_width;
923     height += 2*b_width;
924     height += 2*b_width;
925     x += (Position)width/2;
926     y += (Position)height/2;
927 
928     XtVaGetValues(sub,
929         XtNwidth, &width,
930         XtNheight, &height,
931         XtNborderWidth, &b_width,
932         NULL);
933 
934     width += 2*b_width;
935     height += 2*b_width;
936     x -= (Position)width/2;
937     if (x < 0) x = 0;
938     y -= (Position)height/2;
939     if (y < 0) y = 0;
940     if (y > (max_y = (Position) (XtScreen(sub)->height - height))) y = max_y;
941 
942     XtVaSetValues(sub, XtNx, x, XtNy, y, NULL);
943 }
944 
945 
946 static check_type
CheckFile(fname,mode,w)947 CheckFile(fname, mode, w)
948 
949 char *fname;
950 int mode;
951 widget_bag *w;
952 {
953     char buf[512];
954     struct stat st;
955     FILE *fp;
956     char *msg = "Error: can't %s file %s";
957 
958     /* check filename */
959     if (!fname) return (NOGO);
960     while (isspace(*fname)) fname++;
961     if (!*fname) return (NOGO);
962 
963     if (!access(fname, F_OK)) {
964         /* named file exists */
965         if (stat(fname, &st))
966             return (NOGO); /* shouldn't happen */
967 
968         if ((st.st_mode&S_IFMT) != S_IFLNK &&
969                 (st.st_mode&S_IFMT) != S_IFREG) goto bad;
970             /* not a simple file or symbolic link */
971     }
972 
973     switch (mode) {
974     case R_OK:
975         if (access(fname,R_OK)) {
976             /* can't open for reading */
977             if (!access(fname,F_OK)) goto bad; /* it exists, so error */
978             return (NO_EXIST);
979         }
980         return (READ_OK);
981     case W_OK:
982         if (access(fname,W_OK)) {
983             /* can't open for writing */
984             if (!access(fname,F_OK)) goto bad; /* it exists, so error */
985             fp = fopen(fname,"w");
986             if (!fp) goto bad;
987             fclose(fp);
988         }
989         return (WRITE_OK);
990     }
991 bad:
992     sprintf(buf, msg, (mode == R_OK) ? "read" : "write", fname);
993     PopUpMessage(buf, w);
994     return (NOGO);
995 }
996 
997 
998 /* ARGSUSED */
999 static void
ToTop(caller,clientdata,event,ctd)1000 ToTop(caller, clientdata, event, ctd)
1001 
1002 Widget caller;
1003 XtPointer clientdata;
1004 XEvent *event;
1005 Boolean *ctd;
1006 {
1007     /*  prevent windows from disappearing */
1008     widget_bag *w = (widget_bag*)clientdata;
1009 
1010     XVisibilityEvent *vev = (XVisibilityEvent*)event;
1011     XWindowChanges xv;
1012 
1013     if (vev->state == VisibilityFullyObscured) {
1014         xv.sibling = XtWindow(w->shell);
1015         xv.stack_mode = Above;
1016         XReconfigureWMWindow(vev->display, vev->window,
1017             DefaultScreen(vev->display), CWSibling|CWStackMode, &xv);
1018     }
1019 }
1020 
1021 
1022 static char help_text[] = "\
1023 Command buttons:\n\
1024   Quit:     Exit the editor\n\
1025   Save:     Save the current buffer to the named file\n\
1026   Save As:  Save the current buffer or marked block to a new file\n\
1027   Load:     Input a new text file for editing\n\
1028   Help:     Bring up help text\n\n\
1029 Key bindings:\n\
1030   Ctrl-a   Beginning Of Line          Meta-b   Backward Word\n\
1031   Ctrl-b   Backward Character         Meta-f   Forward Word\n\
1032   Ctrl-d   Delete Next Character      Meta-i   Insert File\n\
1033   Ctrl-e   End Of Line                Meta-k   Kill To End Of Paragraph\n\
1034   Ctrl-f   Forward Character          Meta-q   Form Paragraph\n\
1035   Ctrl-g   Multiply Reset             Meta-v   Previous Page\n\
1036   Ctrl-h   Delete Previous Character  Meta-y   Insert Current Selection\n\
1037   Ctrl-j   Newline And Indent         Meta-z   Scroll One Line Down\n\
1038   Ctrl-k   Kill To End Of Line        Meta-d   Delete Next Word\n\
1039   Ctrl-l   Redraw Display             Meta-D   Kill Word\n\
1040   Ctrl-m   Newline                    Meta-h   Delete Previous Word\n\
1041   Ctrl-n   Next Line                  Meta-H   Backward Kill Word\n\
1042   Ctrl-o   Newline And Backup         Meta-<   Beginning Of File\n\
1043   Ctrl-p   Previous Line              Meta->   End Of File\n\
1044   Ctrl-r   Search/Replace Backward    Meta-]   Forward Paragraph\n\
1045   Ctrl-s   Search/Replace Forward     Meta-[   Backward Paragraph\n\
1046   Ctrl-t   Transpose Characters       Delete   Delete next character\n\
1047   Ctrl-u   Multiply by 4         Meta-Delete   Delete Previous Word\n\
1048   Ctrl-v   Next Page             Meta-Shift-Delete    Kill Previous Word\n\
1049   Ctrl-w   Kill Selection        Meta-Backspace       Delete Previous Word\n\
1050   Ctrl-y   Unkill                Meta-Shift-Backspace Kill Previous Word\n\
1051   Ctrl-z   Scroll One Line Up\n\n\
1052 Pointer button bindings (cut and paste text):\n\
1053   Button 1 Down    Start Selection\n\
1054   Button 1 Motion  Adjust Selection\n\
1055   Button 1 Up      End Selection (cut)\n\n\
1056   Button 2 Down    Insert Current Selection (paste)\n\n\
1057   Button 3 Down    Extend Current Selection\n\
1058   Button 3 Motion  Adjust Selection\n\
1059   Button 3 Up      End Selection (cut)\n";
1060 
1061 
1062 
1063 /* ARGSUSED */
1064 static void
PopUpHelp(caller,client_data,call_data)1065 PopUpHelp(caller, client_data, call_data)
1066 
1067 Widget caller;
1068 XtPointer client_data, call_data;
1069 {
1070     Widget popup;
1071     Widget form;
1072     Widget butbox;
1073     Widget label;
1074     Widget cancel;
1075     Widget text;
1076     widget_bag *w = (widget_bag*)client_data;
1077     Position x, y;
1078     Widget *wpass;
1079 
1080     XtVaGetValues(w->shell, XtNx, &x, XtNy, &y, NULL);
1081 
1082 
1083     popup = XtVaCreatePopupShell("popup_h", transientShellWidgetClass,
1084         w->shell,
1085         XtNx, x + 50,
1086         XtNy, y + 50,
1087         NULL);
1088     XtOverrideTranslations(popup, XtParseTranslationTable(wmTranslations));
1089 
1090     form = XtVaCreateManagedWidget("form_h", formWidgetClass, popup,
1091         NULL);
1092 
1093     butbox = XtVaCreateManagedWidget("buttonbox", boxWidgetClass,
1094         form,
1095         XtNwidth, 650,
1096         XtNleft, XtChainLeft,
1097         XtNright, XtChainLeft,
1098         XtNbottom, XtChainTop,
1099         XtNtop, XtChainTop,
1100         NULL);
1101 
1102     label = XtVaCreateManagedWidget("label_h", labelWidgetClass,
1103         butbox,
1104         XtNlabel, "Xeditor Help",
1105         XtNborderWidth, 2,
1106         NULL);
1107 
1108     cancel = XtVaCreateManagedWidget("cancel_h", commandWidgetClass,
1109         butbox,
1110         XtNlabel, "Cancel",
1111         XtNfromHoriz, label,
1112         NULL);
1113 
1114     text = XtVaCreateManagedWidget("text_h", asciiTextWidgetClass,
1115         form,
1116         XtNtype, XawAsciiString,
1117         XtNeditType, XawtextRead,
1118         XtNallowResize, True,
1119         XtNscrollHorizontal, XawtextScrollWhenNeeded,
1120         XtNscrollVertical, XawtextScrollWhenNeeded,
1121         XtNfromVert, butbox,
1122         XtNwidth, 600,
1123         XtNheight, 400,
1124         XtNstring, help_text,
1125         XtNdisplayCaret, False,
1126         NULL);
1127 
1128     wpass = (Widget*)XtMalloc(2*sizeof(Widget));
1129     wpass[0] = popup;
1130     wpass[1] = w->butbox;
1131     XtAddCallback(cancel, XtNcallback, PopDownHelp, (XtPointer)wpass);
1132     XtRealizeWidget(popup);
1133     XSetWMProtocols(Xdisplay, XtWindow(popup), &w->wm_delete, 1);
1134     XtPopup(popup, XtGrabNone);
1135     XtSetSensitive(XtNameToWidget(w->butbox, "help"), False);
1136 }
1137 
1138 
1139 /* ARGSUSED */
1140 static void
PopDownHelp(caller,client_data,call_data)1141 PopDownHelp(caller, client_data, call_data)
1142 
1143 Widget caller;
1144 XtPointer client_data, call_data;
1145 {
1146     Widget *w = (Widget*)client_data;
1147 
1148     XtPopdown(w[0]);
1149     XtSetSensitive(XtNameToWidget(w[1], "help"), True);
1150     XtFree((char*)w);
1151 }
1152 
1153 
1154 void
cp_pathfix(buf)1155 cp_pathfix(buf)
1156 
1157 char *buf;
1158 {
1159     char *s;
1160 
1161     if (index(buf,'~')) {
1162         s = tilde_expand(buf);
1163         if (s) {
1164             strcpy(buf,s);
1165             XtFree(s);
1166         }
1167     }
1168 }
1169 
1170 
1171 static char *
tilde_expand(string)1172 tilde_expand(string)
1173 
1174 char *string;
1175 {
1176     struct passwd *pw;
1177     char    *tail;
1178     char    buf[BSIZE_SP];
1179     char    *k, c;
1180     char    *ret;
1181 
1182     while (*string && isspace(*string))
1183     string++;
1184 
1185     if (*string != '~') {
1186         ret = XtMalloc(strlen(string) + 1);
1187         strcpy(ret, string);
1188         return (ret);
1189     }
1190 
1191     string += 1;
1192 
1193     if (!*string || *string == '/') {
1194         pw = getpwuid(getuid());
1195         *buf = 0;
1196     }
1197     else {
1198         k = buf;
1199         while ((c = *string) && c != '/')
1200             *k++ = c, string++;
1201         *k = 0;
1202         pw = getpwnam(buf);
1203     }
1204 
1205     if (pw) {
1206         strcpy(buf, pw->pw_dir);
1207         if (*string)
1208             strcat(buf, string);
1209     }
1210     else
1211         return (NULL);
1212 
1213     ret = XtMalloc(strlen(buf) + 1);
1214     strcpy(ret, buf);
1215     return (ret);
1216 }
1217 
1218 
1219 #else /* STAND_ALONE */
1220 
1221 /* ARGSUSED */
1222 static void
PopUpHelp(caller,client_data,call_data)1223 PopUpHelp(caller, client_data, call_data)
1224 
1225 Widget caller;
1226 XtPointer client_data, call_data;
1227 {
1228     wordlist wl;
1229 
1230     wl.wl_prev = wl.wl_next = NULL;
1231     wl.wl_word = (char*)client_data;
1232     com_ghelp(&wl);
1233 }
1234 
1235 
1236 void
com_xeditor(wl)1237 com_xeditor(wl)
1238 
1239 wordlist *wl;
1240 {
1241     if (wl)
1242         xeditor(wl->wl_word);
1243     else
1244         xeditor(NULL);
1245 }
1246 
1247 
1248 #endif /* STAND_ALONE */
1249 
1250 #else /* HAVE_X11 */
1251 
1252 #ifdef STAND_ALONE
main()1253 main()
1254 { printf("This doesn't exist without X!\n"); }
1255 
1256 #else
1257 
1258 /* ARGSUSED */
1259 int
xeditor(s)1260 xeditor(s)
1261 char *s;
1262 { return (1); }
1263 
1264 
1265 void
com_xeditor(wl)1266 com_xeditor(wl)
1267 
1268 wordlist *wl;
1269 {
1270     fprintf(cp_err, "Xeditor is available under X only\n");
1271 }
1272 
1273 #endif /* STAND_ALONE */
1274 
1275 #endif /* HAVE_X11 */
1276