1 /*
2    exec_cmd.c
3    a set of procedures to support executing commands (including the
4    communication between two processes using pipes); used mainly to
5    support UNIX filters
6 
7    A. Stochniol, Last revision 1.09.1994
8 
9 */
10 
11 /*
12  * Copyright 1991 - 1994,  Andrzej Stochniol, London, UK
13  *
14  * ASEDIT text editor, both binary and source (hereafter, Software) is
15  * copyrighted by Andrzej Stochniol (hereafter, AS) and ownership remains
16  * with AS.
17  *
18  * AS grants you (hereafter, Licensee) a license to use the Software
19  * for academic, research and internal business purposes only, without a
20  * fee.  Licensee may distribute the binary and source code (if released)
21  * to third parties provided that the copyright notice and this statement
22  * appears on all copies and that no charge is associated with such copies.
23  *
24  * Licensee may make derivative works.  However, if Licensee distributes
25  * any derivative work based on or derived from the Software, then
26  * Licensee will:
27  * (1) notify AS regarding its distribution of the derivative work, and
28  * (2) clearly notify users that such derivative work is a modified version
29  *      and not the original ASEDIT distributed by AS.
30  *
31  * Any Licensee wishing to make commercial use of the Software should
32  * contact AS to negotiate an appropriate license for such commercial use.
33  * Commercial use includes:
34  * (1) integration of all or part of the source code into a product for sale
35  *     or license by or on behalf of Licensee to third parties, or
36  * (2) distribution of the binary code or source code to third parties that
37  *     need it to utilize a commercial product sold or licensed by or on
38  *     behalf of Licensee.
39  *
40  * A. STOCHNIOL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS
41  * SOFTWARE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR
42  * IMPLIED WARRANTY.  IN NO EVENT SHALL A. STOCHNIOL BE LIABLE FOR ANY
43  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
44  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
45  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
46  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
47  *
48  * By using or copying this Software, Licensee agrees to abide by the
49  * copyright law and all other applicable laws, and the terms of this
50  * license.
51  * AS shall have the right to terminate this license immediately by
52  * written notice upon Licensee's breach of, or non-compliance with, any
53  * of its terms.  Licensee may be held legally responsible for any
54  * copyright infringement that is caused or encouraged by Licensee's
55  * failure to abide by the terms of this license.
56  *
57  *
58  *      Andrzej Stochniol       (A.Stochniol@ic.ac.uk)
59  *      30 Hatch Road
60  *      London SW16 4PN
61  *      UK
62  */
63 
64 
65 #include <Xm/Text.h>
66 #include <Xm/MessageB.h>
67 #include <Xm/SelectioB.h>
68 #include <X11/Intrinsic.h>
69 #include <unistd.h>
70 #include <stdio.h>
71 #include <string.h>
72 #include <signal.h>
73 #include <errno.h>
74 #include <ctype.h>              /* to define toupper function */
75 
76 #include <fcntl.h>
77 #include <sys/types.h>
78 #include <sys/wait.h>
79 
80 
81 #ifdef SYSV
82 # ifndef __hpux
83 #  define vfork() fork()
84 # endif /* __hpux */
85 #endif  /* SYSV */
86 
87 #include <Xm/Protocols.h>    /* needed because of XmAddWMProtocolCallback;
88                                    available since X11R4 */
89 
90 
91 #include "asedit.h"
92 
93 
94 
95 
96 
97 extern Widget toplevel;
98 extern XmStringCharSet charset;
99 
100 #define PARTIAL_BUFSIZ 128
101 
102 typedef struct partialBufStruct
103 {
104         struct partialBufStruct *prev;          /* pointer to the previous partial buffer */
105         int    len;
106         char   txt[PARTIAL_BUFSIZ];
107 } partialBufStruct;
108 
109 
110 typedef struct
111 {
112         int fd;                         /* -1 for closed pipe */
113         Boolean collect;
114         char *txt;
115         long len;
116         long pos;
117 	partialBufStruct *pbufLast;     /* last of chained partial buffers (follow the chain using pbufLast->prev)*/
118 	void    *ccs;                   /* back-pointer to the childCommStruct (needs casting) */
119 } commPipeStruct;
120 
121 /* childOutDepot might be (defined as enum in asedit.h):
122 	TO_NONE
123 	TO_STDOUT
124 	TO_STDERR
125 	TO_CURRENT	-> with that the left and right make sense only
126 	TO_NEW
127 	TO_TEXT_DIALOG
128 */
129 
130 typedef struct
131 {
132     pid_t		childpid;	/* child process id */
133     aseditWindowStruct *win;		/* pointer to the parent edit structure */
134     int			childOutDepot;	/* where we want the child's stdout to be shown */
135     char	       *dlgTitle;	/* dialog title (valid only for TO_*_DIALOG case) */
136     char               *dialogsTitle;   /* title for all other dialogs (work, message etc) */
137     int                 noOutDialog;	/* type of dialog when no output was obtained */
138     XmString        	noOutMsg;	/* message when no output was obtained (and noOutDialog declared) */
139     XmTextPosition	left;		/* left position of the text to be replaced with the child's output */
140     XmTextPosition	right;		/* right ... (respectively); make sense only for the TO_CURRENT case */
141     commPipeStruct 	in;		/* input pipe structure */
142     commPipeStruct 	out;		/* output pipe structure (output will be deposited according to childOutDepot) */
143     commPipeStruct	err;		/* error pipe structure (error pipe output is always shown in a question dialog */
144     XtIntervalId	intervalId;	/* interval Id for the timeout procedure */
145     Widget		progress;	/* widget to show progress of the command */
146     Boolean		abortedByUser;	/* a flag set by destroy CB */
147 } childCommStruct;
148 
149 
150 #ifdef _NO_PROTO
closePipe(p)151 static void closePipe(p)
152     int *p;
153 #else  /* ! _NO_PROTO */
154 
155 static void closePipe(int *p)
156 #endif
157 {
158     if(p[0] >= 0) close(p[0]);
159     if(p[1] >= 0) close(p[1]);
160 }
161 
162 #ifdef _NO_PROTO
closePipes(inPipe,outPipe,errPipe)163 static void closePipes( inPipe, outPipe, errPipe)
164     int *inPipe;
165     int *outPipe;
166     int *errPipe;
167 #else  /* ! _NO_PROTO */
168 
169 static void closePipes( int *inPipe, int *outPipe, int *errPipe)
170 #endif
171 {
172     closePipe(inPipe);
173     closePipe(outPipe);
174     closePipe(errPipe);
175 }
176 
177 
178 #ifdef _NO_PROTO
acceptOrCancelCB(w,response,cbs)179 static void acceptOrCancelCB (w, response, cbs)
180     Widget 	w;
181     int 	*response;
182     XmAnyCallbackStruct *cbs;
183 
184 #else  /* ! _NO_PROTO */
185 
186 static void acceptOrCancelCB (Widget w, int *response, XmAnyCallbackStruct *cbs)
187 #endif
188 {
189     if (cbs->reason == XmCR_OK)
190 	*response = 1;
191     else if (cbs->reason == XmCR_CANCEL)
192 	*response = 0;
193     else	/* called from the WM_DELETE_WINDOW protocol message sent by the wm */
194 	*response = 0;		/* assign cancel situation */
195 
196 }   /* acceptOrCancelCB */
197 
198 
199 /* acceptChildOutput: stand-alone procedure to create modal dialog and ask
200    the user question about proceeding by showing the error string
201    Note that the instance name depends on the defaultButtonType
202 */
203 #ifdef _NO_PROTO
acceptChildOutput(parent,errstr,defaultButtonType)204 Boolean acceptChildOutput(parent, errstr, defaultButtonType)
205     Widget parent;
206     char   *errstr;
207     int    defaultButtonType;
208 #else  /* ! _NO_PROTO */
209 
210 Boolean acceptChildOutput(Widget parent,  char *errstr, int defaultButtonType)
211 #endif
212 {
213     Widget dlg, shell;
214     Arg                 al[5];          /*  arg list            */
215     register  int       ac = 0;         /*  arg count           */
216     XmString  xmstr;                    /* work XmString */
217     int       response = -1;
218     XtAppContext app = XtWidgetToApplicationContext(parent);
219     Atom WM_DELETE_WINDOW;
220 
221     if(errstr)		/* otherwise take the string from resources */
222     {
223     	xmstr = XmStringCreateLtoR (errstr, charset);
224     	XtSetArg(al[ac], XmNmessageString, xmstr);	ac++;
225     }
226     /* do NOT do anything when the user presses the Close button in the system menu;
227        what kind of answer would be that;
228     */
229     XtSetArg (al[ac], XmNdeleteResponse, XmDO_NOTHING);	ac++;
230     /****XtSetArg(al[ac], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); ac++; ***/
231     XtSetArg(al[ac], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ac++;
232     XtSetArg(al[ac], XmNdefaultButtonType, defaultButtonType);	       ac++;
233     if(defaultButtonType == XmDIALOG_CANCEL_BUTTON)
234     	dlg = XmCreateQuestionDialog (parent, "errorChild", al, ac);
235     else
236     	dlg = XmCreateQuestionDialog (parent, "messageChild", al, ac);
237 
238 
239     XtAddCallback (dlg, XmNokCallback,
240 		 (XtCallbackProc)acceptOrCancelCB, (XtPointer)&response);
241     XtAddCallback (dlg, XmNcancelCallback,
242 		 (XtCallbackProc)acceptOrCancelCB, (XtPointer)&response);
243 
244     XtUnmanageChild (XmMessageBoxGetChild (dlg, XmDIALOG_HELP_BUTTON));
245     XtManageChild (dlg);
246 
247     /* take care for the situation when the window manager sends the
248        WM_DELETE_WINDOW protocol message
249     */
250     shell = XtParent (dlg);
251     WM_DELETE_WINDOW = XmInternAtom
252         (XtDisplay (parent), "WM_DELETE_WINDOW", False);
253     XmAddWMProtocolCallback (shell, WM_DELETE_WINDOW,
254 		(XtCallbackProc)acceptOrCancelCB, (XtPointer)&response);
255 
256 
257     /* loop until we get the response */
258     while (response == -1)
259     {
260 	XtAppProcessEvent (app, XtIMAll);
261 	XSync (XtDisplay (dlg), 0);
262     }
263 
264     XtUnmanageChild (dlg);
265     XSync (XtDisplay (dlg), 0);
266     XmUpdateDisplay (dlg);
267 
268     XtDestroyWidget (dlg);
269 
270     if(response > 0) return True;
271     else             return False;
272 
273 }   /* acceptChildOutput */
274 
275 
276 #ifdef _NO_PROTO
freePartialBuffers(ps)277 static void freePartialBuffers(ps)
278     commPipeStruct *ps;
279 #else  /* ! _NO_PROTO */
280 
281 static void freePartialBuffers(commPipeStruct *ps)
282 #endif
283 {
284     partialBufStruct *pbuf, *pbuf_prev;
285 
286     /* we don't need partial buffers any more; free the memory (if it
287        wasn't freed yet)
288     */
289 
290     pbuf=ps->pbufLast;
291     while(pbuf != NULL)
292     {
293         pbuf_prev = pbuf->prev;
294         XtFree((char *)pbuf);
295         pbuf = pbuf_prev;
296     }
297     ps->pbufLast = (partialBufStruct *) NULL;
298 
299 }   /* freePartialBuffers */
300 
301 
302 #ifdef _NO_PROTO
dialogCloseCB(dialog,client_data,call_data)303 static void dialogCloseCB(dialog, client_data, call_data)
304     Widget      dialog;
305     XtPointer     client_data;
306     XtPointer     call_data;
307 #else  /* ! _NO_PROTO */
308 
309 static void dialogCloseCB(Widget dialog, XtPointer client_data, XtPointer call_data)
310 #endif
311 {
312     /* called when the user presses the cancel button; we simply destroy
313        the widget (that would cause the call to dialogDestroyCB);
314        when the user dsmisses the dialog by pressing Close button in the
315        window menu the progressDestroyCB is called directly;
316     */
317     XtDestroyWidget(dialog);
318 }
319 
320 
321 #ifdef _NO_PROTO
dialogDestroyCB(dialog,client_data,call_data)322 static void dialogDestroyCB(dialog, client_data, call_data)
323     Widget      dialog;
324     XtPointer     client_data;
325     XtPointer     call_data;
326 #else  /* ! _NO_PROTO */
327 
328 static void dialogDestroyCB(Widget dialog, XtPointer client_data, XtPointer call_data)
329 #endif
330 {
331     /* called when the dialog is destroyed; see above */
332     ;	/* do nothing here */
333 }
334 
335 #ifdef _NO_PROTO
showNewTextDialog(win,txt,dlgTitle)336 static void showNewTextDialog(win, txt, dlgTitle)
337     aseditWindowStruct *win;
338     char *txt;
339     char *dlgTitle;
340 #else  /* ! _NO_PROTO */
341 
342 static void showNewTextDialog(aseditWindowStruct *win, char *txt, char *dlgTitle)
343 #endif
344 {
345     /* create new, simple text dialog and show the txt in it; make the dialog as small as possible */
346     Widget kid[5];                 /* Children to unmanage */
347     Arg al[18];                    /* Arg List */
348     register int ac = 0;           /* Arg Count */
349     Dimension rows=1, cols=1;
350     long lines=1L, columns=1L, colsMax=1L;
351     long i, len;
352     int defColsMax=40, defRowsMax=12;	/* hardcoded in the free version (half of the standard window)*/
353     Widget dialog, text;
354     XmString      xmstr;        /* work XmString */
355     int    tab_size = win->tabsize;
356 
357 
358     XtSetArg(al[ac], XmNdeleteResponse, XmDESTROY); ac++;   /* for Close in control menu */
359     XtSetArg(al[ac], XmNdefaultButtonType, XmDIALOG_CANCEL_BUTTON); ac++;
360     if(dlgTitle)
361     {
362 	xmstr =XmStringCreateLtoR(dlgTitle, charset);
363 	XtSetArg (al[ac], XmNdialogTitle, xmstr);   ac++;
364     }
365 
366     if(xmUseVersion >= 1002)
367     	dialog = XmCreateWarningDialog(win->menu_bar, "text_dialog", al, ac);
368     else	/* use workaround for all versions before Motif 1.2 */
369      	dialog = XmCreatePromptDialog(win->menu_bar, "text_dialog", al, ac);
370 
371     if(dlgTitle) XmStringFree(xmstr);        /* free memory allocated for XmString */
372 
373     if(dialog)          /* safety check */
374     {
375         /* unmanage the unneeded buttons and elements */
376 	i = 0;
377 	if(xmUseVersion >= 1002)
378 	{
379             kid[i++] = XmMessageBoxGetChild(dialog, XmDIALOG_OK_BUTTON);
380             kid[i++] = XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON);
381 	    kid[i++] = XmMessageBoxGetChild(dialog, XmDIALOG_MESSAGE_LABEL);
382 	    kid[i++] = XmMessageBoxGetChild(dialog, XmDIALOG_SYMBOL_LABEL);
383 	}
384 	else
385 	{
386 	    kid[i++] = XmSelectionBoxGetChild(dialog, XmDIALOG_OK_BUTTON);
387             kid[i++] = XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON);
388             kid[i++] = XmSelectionBoxGetChild(dialog, XmDIALOG_SELECTION_LABEL);
389             kid[i++] = XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT);
390 	}
391 
392 
393 	XtUnmanageChildren (kid, i);
394 
395 	/* find the number of lines and the maximum line width of the text to be displayed
396 	   (it is used to minimize the size of the dialog; note that the algorithm is correct
397 	    only when there is no wrapping;)
398 	*/
399 
400 	if(txt && (len=strlen(txt)) > 0)
401 	{
402 	     for(i=0; i<len; i++)
403 	     {
404 		if(txt[i] == '\n')
405 		{
406 		    lines++;
407 		    if(columns > colsMax) colsMax = columns;
408 		    columns = 1L;
409 		    if(i == len -1) lines--;	/* do not show the last line if
410 						   it only consist of line feed */
411 		}
412 		else
413 		{
414 		    if(txt[i] != '\t') columns++;
415 		    else columns = ((columns+tab_size-1)/tab_size)*tab_size + 1L;
416 		}
417 	    }
418 	    /* check the last line */
419 	    if(columns > colsMax) colsMax = columns;
420 
421 
422 	}
423 
424 	if(colsMax > defColsMax) cols = defColsMax;
425 	else		     cols = colsMax;
426 
427 	if(lines > defRowsMax)   rows = defRowsMax;
428 	else		     rows = lines;
429 
430 	/* now add the scrolled text */
431         ac = 0;
432         XtSetArg (al[ac], XmNrows, rows);	   ac++;
433 	XtSetArg (al[ac], XmNcolumns, cols);	   ac++;
434         XtSetArg (al[ac], XmNresizeWidth, False);  ac++;
435         XtSetArg (al[ac], XmNresizeHeight, False);  ac++;
436         XtSetArg (al[ac], XmNscrollVertical, True);  ac++;
437 	XtSetArg (al[ac], XmNeditable, False);		  ac++;
438         XtSetArg (al[ac], XmNeditMode, XmMULTI_LINE_EDIT);  ac++;
439         XtSetArg (al[ac], XmNbackground, text_read_only_background);	ac++;
440 	XtSetArg (al[ac], XmNvalue,      txt);				ac++;
441 	/* add the vertical scrolling for long output */
442 	if(lines > defRowsMax)
443 	{
444 	    XtSetArg(al[ac], XmNscrollVertical, True);	ac++;
445 	}
446 	else
447 	{
448 	    XtSetArg(al[ac], XmNscrollVertical, False);	ac++;
449 	}
450 
451 	/* Note that we set wordWrap and scrollHorizontal in the app defaults file */
452 
453 	text = XmCreateScrolledText (dialog, "text", al, ac);
454 
455 	XtManageChild(text);
456 
457         XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)dialogCloseCB, NULL);
458         XtAddCallback(dialog, XmNdestroyCallback, (XtCallbackProc)dialogDestroyCB, NULL);
459         XtManageChild(dialog);
460     }
461 
462 }   /* showNewTextDialog */
463 
464 #ifdef _NO_PROTO
showNoOutDialog(parent,dlgType,title,msg)465 static void showNoOutDialog(parent, dlgType, title, msg)
466     Widget parent;
467     int dlgType;
468     char *title;
469     XmString msg;
470 #else  /* ! _NO_PROTO */
471 
472 static void showNoOutDialog(Widget parent, int dlgType, char *title, XmString msg)
473 #endif
474 {
475     register int  ac;           /* arg count                */
476     Arg           al[7];        /* arg list                 */
477     XmString      xmstr;        /* work XmString */
478     Widget	  dialog;
479 
480     ac = 0;
481     if(title)
482     {
483         xmstr =XmStringCreateLtoR(title, charset);
484         XtSetArg(al[ac], XmNdialogTitle, xmstr);        ac++;
485     }
486     if(msg != (XmString) NULL) { XtSetArg(al[ac], XmNmessageString, msg);     ac++; }
487     XtSetArg(al[ac], XmNdeleteResponse, XmDESTROY); ac++;   /* for Close in control menu */
488     XtSetArg(al[ac], XmNdefaultButtonType, XmDIALOG_CANCEL_BUTTON); ac++;
489 
490 
491     switch(dlgType)
492     {
493 	case XmDIALOG_ERROR:
494             dialog = XmCreateErrorDialog(parent, "noOutDialog", al, ac);
495 	    break;
496 
497 	case XmDIALOG_INFORMATION:
498             dialog = XmCreateInformationDialog(parent, "noOutDialog", al, ac);
499             break;
500 
501 	case XmDIALOG_MESSAGE:
502             dialog = XmCreateMessageDialog(parent, "noOutDialog", al, ac);
503             break;
504 
505 	case XmDIALOG_WARNING:
506             dialog = XmCreateWarningDialog(parent, "noOutDialog", al, ac);
507             break;
508 
509         default:
510             fprintf(stderr, "\nUnknown noOutDialog type!");
511             break;
512     }
513     if(dialog)          /* safety check */
514     {
515         XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_OK_BUTTON));
516         XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
517 
518         XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)dialogCloseCB, NULL);
519         XtAddCallback(dialog, XmNdestroyCallback, (XtCallbackProc)dialogDestroyCB, NULL);
520         XtManageChild(dialog);
521     }
522     if(title) XtFree((char *)xmstr);        /* free memory allocated */
523 
524 }   /* showNoOutDialog */
525 
526 
527 #ifdef _NO_PROTO
processChildOutput(ccs)528 static void processChildOutput(ccs)
529     childCommStruct *ccs;
530 #else  /* ! _NO_PROTO */
531 
532 static void processChildOutput(childCommStruct *ccs)
533 #endif
534 {
535     int status;
536     Boolean abnormal_end = False, acceptOutput;
537     aseditWindowStruct *win;
538     Widget dialog;
539     /* check if all pipes are closed; if so now is the time
540        to deal with the child output; if not return and wait for the
541        next time to be called
542     */
543 
544     if(ccs->out.fd >=0 || ccs->err.fd >= 0 || ccs->in.fd >= 0) return;
545 
546     /* wait for the child to complete (reap the child); check the return status*/
547     waitpid(ccs->childpid, &status, 0);
548 
549     /* if there is an error output, ask the user about proceeding; note
550        that error might be related to abnormal termination of the child (see status)
551        or just a message sent to stderr (like adding the final line feed); we
552        distinguish between severity of errors by providing different default button
553        for the user to decide what to do
554     */
555 
556 
557     /* If you get errors around WIFEXITED then read the note below:
558        Note: that on some systems you might have to add "-D_POSIX_SOURCE" definition
559        for the compiler to properly include the <sys/wait.h>;
560        That was the case for IBM RS/6000 AIX 3.2.3 without xmkmf (but on
561        AIX 3.2.5 all was OK).
562     */
563     if(WIFEXITED(status))
564     {
565 	abnormal_end = False;  /* normal termination; exit status might be
566 					  obtained using WEXITSTATUS(status) */
567 	if(WEXITSTATUS(status) != 0) abnormal_end = True;	/* lets mark
568 							this as abnormal as well;
569 							(e.g. this is the case
570 							when command caled from
571 							inside a shell was not found!) */
572     }
573     else if (WIFSIGNALED(status)) abnormal_end = True;	/* the third possiblity
574 							   is that the child stopped */
575 
576     /* we also mark the abnormal termination if any of the pipes was closed
577        with an error (for example as a result of kill command)
578     */
579     if(ccs->out.fd < -1 || ccs->err.fd < -1 ||  ccs->in.fd < -1) abnormal_end = True;
580 
581 
582     win = ccs->win;
583     if(ccs->abortedByUser)
584     {
585 	acceptOutput = False;	/* no matter how the child has  finished (or was reported to finish)
586 				   do not process the output */
587     }
588     else
589     {
590 	XFlush(XtDisplay(ccs->win->menu_bar));
591 	if(ccs->progress)
592 	{
593 	    /* get rid off the progress dialog */
594 	    XtDestroyWidget(ccs->progress);
595 	}
596 	else
597 	    XtRemoveTimeOut(ccs->intervalId);	/* remove the timer NOW */
598         if(abnormal_end)
599         {
600 	    /* if there was some output on stderr it would be used, if not
601 	       the string from resources will be used (because ccs->err.txt is NULL in such case)
602             */
603 	    acceptOutput = acceptChildOutput(win->menu_bar, ccs->err.txt, XmDIALOG_CANCEL_BUTTON);
604         }
605         else if (ccs->err.len)
606         {
607 	    acceptOutput = acceptChildOutput(win->menu_bar, ccs->err.txt, XmDIALOG_OK_BUTTON);
608         }
609         else acceptOutput = True;
610     }
611 
612     if(acceptOutput)
613     {
614 	/* do what is neccessary  ... */
615 	/* first check if the length of the output is zero and if the user
616   	   specified noOutDialog; if so show this dialog; if not process
617 	   as usual
618 	*/
619 	if(ccs->out.len == 0 && (ccs->noOutDialog == XmDIALOG_ERROR ||
620 				 ccs->noOutDialog == XmDIALOG_INFORMATION ||
621 				 ccs->noOutDialog == XmDIALOG_MESSAGE ||
622 				 ccs->noOutDialog == XmDIALOG_WARNING) )
623 	{
624 	    showNoOutDialog(win->menu_bar, ccs->noOutDialog, ccs->dialogsTitle, ccs->noOutMsg);
625 	}
626 	else switch(ccs->childOutDepot)
627 	{
628 	    case TO_NONE:
629 		;		/* do nothing (discard output) */
630 		break;
631 
632 	    case TO_STDOUT:
633 		if(ccs->out.txt) fprintf(stdout, ccs->out.txt);
634 		break;
635 
636 	    case TO_STDERR:
637 		if(ccs->out.txt) fprintf(stderr, ccs->out.txt);
638 		break;
639 
640 	    case TO_CURRENT:
641 		/* replace the original selection and be sure that the insertion
642 		   point is moved just after the inserted text
643 		*/
644 		disableRedisplay(win);      /* to prevent visual flashing */
645 		XmTextReplace(win->edit_text, ccs->left, ccs->right, ccs->out.txt);
646 		if(ccs->out.txt)	/* not NULL */
647 		{
648     		    /* set the selection again */
649 		    /* note that XmTextSelection position changes the insertion to the right pos. */
650     		    XmTextSetSelection(win->edit_text, ccs->left, ccs->left + strlen(ccs->out.txt), CurrentTime);
651 
652 		    /** XmTextSetInsertionPosition(win->edit_text, ccs->left + strlen(ccs->out.txt)); not needed */
653 		}
654 		enableRedisplay(win);
655 
656 
657 		break;
658 
659 	    case TO_NEW:
660 		next_window(asedit_last_window);	/* create next window */
661 		open_file_in_last_window(NULL);   /* open a new default file ...  */
662 		ccs->left = ccs->right = (XmTextPosition) 0;
663 		XmTextReplace(asedit_last_window->edit_text, ccs->left, ccs->right, ccs->out.txt);
664 		if(ccs->out.txt)	/* not NULL */
665 		    XmTextSetInsertionPosition(asedit_last_window->edit_text, ccs->left + strlen(ccs->out.txt));
666 
667 		break;
668 
669 	    case TO_TEXT_DIALOG:
670 		/* only show it if there was any output; otherwise show standard info */
671 		if(ccs->out.len > 0) showNewTextDialog(win, ccs->out.txt, ccs->dlgTitle);
672 		else
673 		     showNoOutDialog(win->menu_bar, XmDIALOG_INFORMATION, ccs->dialogsTitle, ccs->noOutMsg);
674 		break;
675 
676 	    default:
677 		fprintf(stderr,"\nUnknown depository for the child's output!");
678 		fprintf(stderr,"\nUsing parent's stderr! The output follows:");
679 		if(ccs->out.txt) fprintf(stderr, ccs->out.txt);
680 		break;
681 
682 
683 	}
684 
685     }
686 
687     /* now release all memory */
688 
689     freePartialBuffers(&ccs->in);
690     freePartialBuffers(&ccs->out);
691     freePartialBuffers(&ccs->err);
692 
693     if(ccs->in.txt)  XtFree(ccs->in.txt);
694     if(ccs->out.txt) XtFree(ccs->out.txt);
695     if(ccs->err.txt) XtFree(ccs->err.txt);
696 
697     /* free allocated memory */
698     if(ccs->dlgTitle) XtFree(ccs->dlgTitle);
699     if(ccs->dialogsTitle) XtFree(ccs->dialogsTitle);
700 
701 
702     if(ccs->progress)	/* if we were extremely unlucky get rid of the progress dialog NOW */
703             XtDestroyWidget(ccs->progress);
704 
705 
706     XtFree((char *)ccs);
707 
708 }   /* processChildOutput */
709 
710 
711 #ifdef _NO_PROTO
createTextFromPartialBuffers(ps)712 static void createTextFromPartialBuffers(ps)
713     commPipeStruct *ps;
714 #else  /* ! _NO_PROTO */
715 
716 static void createTextFromPartialBuffers(commPipeStruct *ps)
717 #endif
718 {
719     long l, len = 0L;
720     int  i;
721     partialBufStruct *pbuf;
722 
723     /* find the how much data is stored in partial buffers and allocate
724        memory for the whole lot
725     */
726 
727     for (pbuf=ps->pbufLast; pbuf != NULL; pbuf=pbuf->prev)
728 		len += pbuf->len;
729 
730     if(len == 0L) { ps->len = 0; return; }       /* nothing stored, return immediately */
731 
732     ps->txt = XtMalloc(len+1);
733 
734 
735     /* store the length, end the text with a null, then construct the text
736        starting from the end */
737     ps->len = l = len;
738     ps->txt[l--] = '\0';
739 
740     for (pbuf=ps->pbufLast; pbuf != NULL; pbuf=pbuf->prev)
741     {
742 	for(i = pbuf->len-1; i>=0; i--)
743 		ps->txt[l--] = pbuf->txt[i];
744     }
745 
746     /* set the initial position of the text (it must be now 0) */
747     ps->pos = l+1;
748 
749     /* we don't need partial buffers any more; free the memory */
750     freePartialBuffers(ps);
751 
752 
753 }   /* createTextFromPartialBuffers */
754 
755 #ifdef _NO_PROTO
childOutErrPipeCB(client_data,source,id)756 static void childOutErrPipeCB(client_data, source, id)
757     XtPointer 	client_data;
758     int 	*source;
759     XtInputId 	*id;
760 #else  /* ! _NO_PROTO */
761 
762 static void childOutErrPipeCB(XtPointer client_data, int *source,  XtInputId *id)
763 #endif
764 {
765     commPipeStruct  *cps = (commPipeStruct *) client_data;
766     partialBufStruct *pbuf;
767     int i, nbytes;
768 
769     pbuf = (partialBufStruct *) XtMalloc(sizeof(partialBufStruct));
770 
771 
772     nbytes = read(*source, pbuf->txt, PARTIAL_BUFSIZ);
773 
774     if( nbytes == -1)
775     {
776 	/* error */
777 	/* check for the following non-blocking and interrupt i/o */
778 	/* AIX returns EAGAIN where 4.3BSD used EWOULDBLOCK;
779 	   check for systems that return either EAGAIN or EWOULDBLOCK.
780 	*/
781 
782 	if (errno == EWOULDBLOCK || errno == EAGAIN)
783 	{
784 	    return;	/* do nothing */
785 	}
786 
787 
788     }
789 
790     if (nbytes == 0 || nbytes == -1)
791     {
792 	/*  nbytes=0 means EOF on pipe */
793 
794 	XtFree((char *)pbuf);
795 	XtRemoveInput(*id);
796 	close(*source);
797 
798 
799 	createTextFromPartialBuffers(cps);  /* put the partial buffers together */
800         cps->fd = nbytes-1;                /* source was closed; we use -2 to
801                                                 find that it was closed with an error */
802 
803 	/* check if the other pipes were closed already; if so now is the time
804 	   to deal with the child output
805 	*/
806 	processChildOutput(cps->ccs);
807 
808 	return;
809     }
810     else
811     {
812 
813 	/*   process the input, i.e. store it in the partial buffer */
814 	pbuf->len = nbytes;
815 	pbuf->prev = cps->pbufLast;
816 	cps->pbufLast = pbuf;
817 
818 
819     }
820 
821 
822 }   /* childOutErrPipeCB */
823 
824 #ifdef _NO_PROTO
childInPipeCB(client_data,source,id)825 static void childInPipeCB(client_data, source,  id)
826     XtPointer 	client_data;
827     int 	*source;
828     XtInputId 	*id;
829 #else  /* ! _NO_PROTO */
830 
831 static void childInPipeCB(XtPointer client_data, int *source,  XtInputId *id)
832 #endif
833 {
834     commPipeStruct  *cps = (commPipeStruct *) client_data;
835     int i, nbytes;
836 
837 
838     if(cps->pos < cps->len) nbytes = write(*source, cps->txt+cps->pos, cps->len - cps->pos);
839     else		    nbytes = 0;		/* we've already written out everything */
840 
841     if( nbytes == -1)
842     {
843 	/* error */
844 	/* check for the following non-blocking and interrupt i/o */
845 	/* AIX returns EAGAIN where 4.3BSD used EWOULDBLOCK;
846 	   check for systems that return either EAGAIN or EWOULDBLOCK.
847 	*/
848 
849 	/******old
850 	if (errno != EWOULDBLOCK && errno != EAGAIN)
851 	{
852 	    |* really fatal ! *|
853 	    |* perror .... *|
854 	}
855 	******/
856 	if (errno == EWOULDBLOCK || errno == EAGAIN)
857         {
858             return;     /* do nothing */
859         }
860 
861 
862 
863     }
864 
865     if (nbytes == 0 || nbytes == -1)
866     {
867 	/*  nbytes=0 means EOF on pipe */
868 
869 	XtRemoveInput(*id);
870 	close(*source);
871 
872 	cps->fd = nbytes-1;		   /* source was closed; we use -2 to
873 				  		find that it was closed with an error */
874 
875 	/* check if the other pipes were closed already; if so now is the time
876 	   to deal with the child output
877 	*/
878 	processChildOutput(cps->ccs);
879 
880 	return;
881     }
882     else
883     {
884 
885 	/* change the position in the parent output buffer */
886 	cps->pos += nbytes;
887 
888 
889 
890     }
891 
892 
893 }   /* childInPipeCB */
894 
895 
896 
897 
898 
899 /* forkAnPipe is used to execute a process (command) and establish two way
900    communications using pipes between the parent and the child processes
901    Returns childpid if succesfull.
902 */
903 #ifdef _NO_PROTO
forkAndPipe(w,cmd,inPipe,outPipe,errPipe)904 static pid_t forkAndPipe(w, cmd, inPipe, outPipe, errPipe)
905     Widget w;
906     String cmd;
907     int    *inPipe;
908     int    *outPipe;
909     int    *errPipe;
910 #else  /* ! _NO_PROTO */
911 
912 static pid_t forkAndPipe(Widget w, String cmd, int *inPipe, int *outPipe, int *errPipe)
913 #endif
914 {
915     /*  all *Pipe's should point to arrays of 2 !!! */
916     pid_t childpid;
917 
918 #ifdef __hpux
919     int (*istat)(), (*qstat)();
920 #else
921     void (*istat)(), (*qstat)();
922 #endif
923 
924 
925     /* set the default values to a value < 0 to recognize if a file descriptor exists */
926     inPipe[0] = inPipe[1] = -1;
927     errPipe[0] = errPipe[1] = -1;
928     outPipe[0] = outPipe[1] = -1;
929 
930 
931     if (pipe(inPipe) < 0 || pipe(outPipe) < 0 || pipe(errPipe) < 0  )
932     {
933 	closePipes(inPipe, outPipe, errPipe);
934 	return -2;    		/*we might want to treat that as fatal error */
935     }
936 
937 
938     if ( (childpid = fork()) < 0)		/** on some systems use vfork !!! ***/
939     {
940 	closePipes(inPipe, outPipe, errPipe);
941 	return -1;	/* we might want to treat that as fatal error */
942     }
943     else if (childpid == 0)
944     {
945 	/* first reset the handling of SIGINT, SIGQUIT and SIGHUP */
946 	signal(SIGINT, SIG_DFL);
947 	signal(SIGHUP, SIG_DFL);
948 #ifdef SIGQUIT          /* not all systems define that (HP-UX does not) */
949 	signal(SIGQUIT, SIG_DFL);
950 #endif
951 
952 
953 	/* child process */
954 	dup2(inPipe[0], fileno(stdin));		/* stdin */
955 	dup2(outPipe[1], fileno(stdout));	/* stdout */
956 	dup2(errPipe[1], fileno(stderr));	/* stderr */
957 
958 
959 	/* close unused pipe ends */
960 	close(inPipe[1]);			/* write end */
961 	close(outPipe[0]);			/* read end */
962 	close(errPipe[0]);
963 
964 	(void) close(XConnectionNumber(XtDisplay(w))); /**** or simply close(XConnectionNumber(dpy)); ***/
965 
966 	/* set the process group ID and session ID of the calling process to the process
967 	   ID of the calling process (in other words make it a process group leader);
968 	   this will allow us to kill this and all its
969 	   subprocesses with a single kill command (with negative pid value; on BSD
970 	   you can use killpg, but don't mix POSIX and BSD !)
971 	*/
972 	setsid();
973 
974 
975 	/* to run a program use:
976 		execlp("li", "li", "-al", 0);
977 	*/
978 	/* to run the shell to interpret the command use:
979 		execl("/usr/bin/sh", "sh", "-c", "li -l *.c", 0);
980 	*/
981 	execl("/bin/sh", "sh", "-c", cmd, 0);
982 
983 	_exit(127);
984 
985     }
986     else
987     {
988 	/* parent */
989 	/* close unused pipe ends for the parent side */
990 	close(inPipe[0]);
991 	close(outPipe[1]);
992 	close(errPipe[1]);
993 
994 	signal(SIGPIPE, SIG_IGN);	/* ignore pipe signals (we will catch the child
995 					   processed that died or was killed in our *PipeCB's*/
996 
997     }
998     return childpid;
999 
1000 }   /* forkAndPipe */
1001 
1002 
1003 
1004 #ifdef _NO_PROTO
set_nonblock(fd)1005 static void set_nonblock(fd)
1006     int fd;
1007 #else  /* ! _NO_PROTO */
1008 
1009 static void set_nonblock(int fd)
1010 #endif
1011 {
1012     int val;
1013     if ( (val = fcntl(fd, F_GETFL, 0)) < 0)
1014 	perror("asedit - set_nonblock: fcntl F_GETFL error");
1015     val |= O_NONBLOCK;
1016     if (fcntl(fd, F_SETFL, val) < 0)
1017 	perror("asedit - set_nonblock: fcntl F_SETFL error");
1018 }
1019 
1020 
1021 #ifdef _NO_PROTO
progressStopCB(dialog,client_data,call_data)1022 void progressStopCB(dialog, client_data, call_data)
1023     Widget 	dialog;
1024     XtPointer 	client_data;
1025     XtPointer 	call_data;
1026 #else  /* ! _NO_PROTO */
1027 
1028 void progressStopCB(Widget dialog, XtPointer client_data, XtPointer call_data)
1029 #endif
1030 {
1031     /* called when the user presses the OK button; we simply destroy
1032        the widget (that would cause the call to progressDestroyCB);
1033        when the user dsmisses the dialog by pressing Close button in the
1034        window menu the progressDestroyCB is called directly;
1035     */
1036     XtDestroyWidget(dialog);
1037 }
1038 
1039 
1040 #ifdef _NO_PROTO
progressDestroyCB(dialog,client_data,call_data)1041 void progressDestroyCB(dialog, client_data, call_data)
1042     Widget 	dialog;
1043     XtPointer 	client_data;
1044     XtPointer 	call_data;
1045 #else  /* ! _NO_PROTO */
1046 
1047 void progressDestroyCB(Widget dialog, XtPointer client_data, XtPointer call_data)
1048 #endif
1049 {
1050     /* called when the dialog is destroyed; see above */
1051     childCommStruct *ccs = (childCommStruct *)client_data;
1052 
1053     XtRemoveTimeOut(ccs->intervalId);
1054 
1055     kill(-ccs->childpid, SIGTERM);	/* kill the child process group;
1056 				           we will collect the output and release
1057 					   memory in other callbacks */
1058     ccs->abortedByUser = True;		/* set the flag so we won't ask the user
1059 					   if proceed with partial results
1060 					*/
1061     ccs->progress = NULL;
1062 
1063 }
1064 
1065 
1066 /* showCmdProgress - show progress of the command, restarts timer and
1067    saves timer id; first time creates the special dialog to show progress
1068    Note that if the output of the filter goes to the current window
1069    we create the dialog as a PRIMARY_APPLICATION_MODAL, so the user
1070    can't spoil anything in the editor.
1071 */
1072 
1073 #ifdef _NO_PROTO
showCmdProgress(client_data,id)1074 static void showCmdProgress(client_data, id)
1075     XtPointer 	 client_data;
1076     XtIntervalId *id;
1077 #else  /* ! _NO_PROTO */
1078 
1079 static void showCmdProgress(XtPointer client_data, XtIntervalId *id)
1080 #endif
1081 {
1082     childCommStruct *ccs = (childCommStruct *)client_data;
1083     XtAppContext app_context = XtWidgetToApplicationContext(ccs->win->menu_bar);
1084 
1085 
1086     if(ccs->progress == NULL)
1087     {
1088 	/* first create the progress dialog and register a callback
1089 	   to stop the command
1090 	*/
1091 	Arg                 al[5];          /*  arg list            */
1092 	register  int       ac = 0;         /*  arg count           */
1093 
1094 	XtSetArg(al[ac], XmNdeleteResponse, XmDESTROY);	ac++;	/* for Close in control menu */
1095         XtSetArg(al[ac], XmNdefaultButtonType, XmDIALOG_CANCEL_BUTTON); ac++;
1096 	if(ccs->childOutDepot == TO_CURRENT)
1097 	       { XtSetArg(al[ac], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ac++; }
1098 
1099 	ccs->progress = XmCreateWorkingDialog(ccs->win->menu_bar, "progress", al, ac);
1100 	if(ccs->progress == NULL) return;	/* should not happen */
1101 
1102 	XtUnmanageChild(XmMessageBoxGetChild(ccs->progress, XmDIALOG_OK_BUTTON));
1103 	XtUnmanageChild(XmMessageBoxGetChild(ccs->progress, XmDIALOG_HELP_BUTTON));
1104 
1105 	XtAddCallback(ccs->progress, XmNcancelCallback, (XtCallbackProc)progressStopCB, NULL);
1106 	XtAddCallback(ccs->progress, XmNdestroyCallback, (XtCallbackProc)progressDestroyCB, (XtPointer)ccs);
1107 
1108 	XtManageChild(ccs->progress);
1109 	XFlush(XtDisplay(ccs->progress));
1110 
1111 	/**** XtPopup(XtParent(ccs->progress), XtGrabNone); ****/
1112 
1113     }
1114     if(ccs->in.fd > 0)
1115     {
1116 	/* only attempt to show progress if the input pipe is opened */
1117         if(ccs->in.len > 0)
1118 	{
1119 #ifdef SHOW_PROGRESS_ON_INPUT_PIPE
1120 	    /* well, for most applications the prg percentage is a very crude estimate; it
1121 	       is mainly because the child standard input is usually buffered (4096 bytes typically);
1122 	       so starting from version 1.3 we don't use that at all (as standard) ; we just show
1123 	       the working dialog with a user suplied message;
1124 	    */
1125 	    Arg          al[2];          /*  arg list            */
1126     	    register int ac = 0;         /*  arg count           */
1127 	    int prg;
1128 	    char work[80];
1129 	    XmString xmstr;
1130 
1131 	    prg = 100.0 * ccs->in.pos/(float)ccs->in.len;
1132 	    sprintf(work, "Done in %d %s", prg, "%");
1133 	    xmstr = XmStringCreateLtoR(work, charset);
1134 
1135     	    XtSetArg(al[ac], XmNmessageString, xmstr);  ac++;
1136     	    XtSetValues(ccs->progress, al, ac);
1137     	    XmStringFree(xmstr);       /* free memory allocated for XmString */
1138 #endif
1139 	   ;			/* don't do anything in 1.3 here (unless you want) */
1140 	}
1141     }
1142 
1143 
1144 
1145 
1146     ccs->intervalId = XtAppAddTimeOut(app_context, (unsigned long)lstr.progressInterval,
1147 			   showCmdProgress, (XtPointer)ccs);
1148 
1149 
1150 }   /* showCmdProgress */
1151 
1152 
1153 #ifdef _NO_PROTO
executeShellCmd(win,cmd,cmdInput,left,right,childOutDepot,dlgTitle,dialogsTitle,noOutDialog,noOutMsg)1154 Boolean executeShellCmd(win, cmd, cmdInput, left, right, childOutDepot,
1155 			dlgTitle, dialogsTitle, noOutDialog, noOutMsg)
1156     aseditWindowStruct *win;
1157     char 	    *cmd;
1158     char 	    *cmdInput;
1159     XmTextPosition  left;
1160     XmTextPosition  right;
1161     int 	    childOutDepot;
1162     char 	    *dlgTitle;
1163     char 	    *dialogsTitle;
1164     int 	    noOutDialog;
1165     XmString 	    noOutMsg;
1166 #else  /* ! _NO_PROTO */
1167 
1168 Boolean executeShellCmd(aseditWindowStruct *win, char *cmd, char *cmdInput,
1169 		     XmTextPosition left, XmTextPosition right,
1170 		     int childOutDepot, char *dlgTitle,
1171 		     char *dialogsTitle, int noOutDialog,
1172 		     XmString noOutMsg)
1173 #endif
1174 {
1175     pid_t childpid;
1176     size_t len;
1177     int inPipe[2], outPipe[2], errPipe[2];
1178     childCommStruct *ccs = NULL;
1179     unsigned long    delay_interval;
1180     XtAppContext app_context = XtWidgetToApplicationContext(win->menu_bar);
1181 
1182     /* first call forkAndPipe ... */
1183 
1184     len = strlen(cmd);
1185     if(!len) return False;	/* empty command; shouldn't happen */
1186 
1187 
1188     if((childpid = forkAndPipe(win->menu_bar, cmd, inPipe, outPipe, errPipe)) < 0)
1189     {
1190 	char work[256];
1191 	/*
1192 		To FINISH OFF !!!!
1193 		strcpy(work, (char *)lstr.unable_to_fork);
1194 		show_error_message(win, work);
1195 	*/
1196 	/* error SHOW it to the user .... */
1197 
1198 	fprintf(stderr, "\n asedit: ERROR in forkAndPipe (Can't create pipes or fork another process!)");
1199 	return False;
1200     }
1201     else
1202     {
1203 	/* fork was OK */
1204 
1205 	/* allocate memory for the childCommStruct */
1206 
1207 	ccs = (childCommStruct *) XtMalloc(sizeof(childCommStruct));
1208 
1209 	/* store the basic parameters */
1210 	ccs->childpid = childpid;
1211 	ccs->win      = win;		/* the parent win structure */
1212 	ccs->childOutDepot = childOutDepot;
1213 	ccs->dlgTitle	  = dlgTitle;
1214 	ccs->dialogsTitle = dialogsTitle;
1215 	ccs->noOutDialog  = noOutDialog;
1216 	ccs->noOutMsg	  = noOutMsg;
1217 	ccs->left	  = left;
1218 	ccs->right	  = right;
1219 
1220 
1221 	/* set the back-pointers */
1222 	ccs->in.ccs = (void *)ccs;
1223 	ccs->out.ccs = (void *)ccs;
1224 	ccs->err.ccs = (void *)ccs;
1225 
1226 	/* set the pbufLast to NULL pointer in all pipes */
1227 	ccs->in.pbufLast  = (partialBufStruct *) NULL;
1228 	ccs->out.pbufLast = (partialBufStruct *) NULL;
1229 	ccs->err.pbufLast = (partialBufStruct *) NULL;
1230 
1231 	/* set the txt to NULL pointers in all pipes */
1232 	ccs->in.txt  = (char *)NULL;
1233 	ccs->out.txt = (char *)NULL;
1234 	ccs->err.txt = (char *)NULL;
1235 
1236 
1237 	/* set the file descriptors for the pipes (parent end) */
1238 	ccs->out.fd = outPipe[0];
1239 	ccs->err.fd = errPipe[0];
1240 	ccs->in.fd  = inPipe[1];
1241 
1242 	/* set non-blocking operations on all pipes (!! compulsory if we
1243 	   don't want to block the whole application !!
1244 	*/
1245         set_nonblock(ccs->in.fd);
1246         set_nonblock(ccs->out.fd);
1247         set_nonblock(ccs->err.fd);
1248 
1249 
1250 
1251 	/* set the command input */
1252 	ccs->in.txt = cmdInput;
1253         if(cmdInput != NULL)
1254 	     ccs->in.len = strlen(cmdInput);
1255 	else ccs->in.len = 0;
1256 	ccs->in.pos = 0;	/* nothing has been written yet */
1257 
1258 	ccs->abortedByUser = False;		/* set the abort flag */
1259 
1260 	/* register functions to handle output from the child process */
1261 
1262 
1263 	/* if you don't have app_context handy use: XtWidgetToApplicationContext(w) */
1264 	XtAppAddInput(app_context,	outPipe[0], (XtPointer) XtInputReadMask,
1265 			(XtInputCallbackProc)childOutErrPipeCB, (XtPointer)(&ccs->out));
1266 
1267 	XtAppAddInput(app_context,	errPipe[0], (XtPointer) XtInputReadMask,
1268 			(XtInputCallbackProc)childOutErrPipeCB, (XtPointer)(&ccs->err));
1269 
1270 	if(ccs->in.len) /* register the callback for the input pipe */
1271 		XtAppAddInput(app_context,	inPipe[1], (XtPointer) XtInputWriteMask,
1272 			(XtInputCallbackProc)childInPipeCB, (XtPointer)(&ccs->in));
1273 	else
1274 	{
1275 	    /* nothing to write; close the inPipe[1] and do NOT register any callback */
1276 	    close(inPipe[1]);
1277 	    ccs->in.fd = -1;	/* CLOSED */
1278 	}
1279 
1280 	ccs->progress = NULL;		/* the widget is not created yet */
1281 
1282 	/* register a timeout procedure to display a working dialog if
1283 	   the Shell command takes a while to complete
1284 	*/
1285 	/* if the output is to the current window show the dialog immediately
1286 	   and make it PRIMARY MODAL
1287 	*/
1288 	if(ccs->childOutDepot == TO_CURRENT) delay_interval = (unsigned long)lstr.progressShortInitialDelay;
1289 	else 				     delay_interval = (unsigned long)lstr.progressInitialDelay;
1290 	ccs->intervalId = XtAppAddTimeOut(app_context, delay_interval,
1291 			   showCmdProgress, (XtPointer)ccs);
1292 
1293 
1294 
1295     }
1296     return True;
1297 
1298 }   /* executeShellCmd */
1299 
1300 #ifdef _NO_PROTO
stringToValueCvt(s,stable,values,table_size,default_string,default_value)1301 static int stringToValueCvt(s, stable, values, table_size, default_string, default_value)
1302     char *s;
1303     char *stable[];
1304     int values[];
1305     int table_size;
1306     char *default_string;
1307     int default_value;
1308 #else  /* ! _NO_PROTO */
1309 
1310 static int stringToValueCvt(char *s, char *stable[], int values[], int table_size,
1311 	char *default_string, int default_value)
1312 #endif
1313 {
1314     /* returns a value from values[i] when s equals stable[i] (case insensitive);
1315        stable MUST be specified in upper case;
1316        both stable and values must be of table_size size;
1317        if s is not equal any of the entries in stable returns default_value;
1318        Note: this procedure changes s to uppercase!
1319     */
1320 
1321     int value = default_value;	/* when s is NULL or a value was not found */
1322 
1323     if(s != NULL)
1324     {
1325 	int i, n, len, len2;
1326 
1327 	len = strlen(s);
1328 	if(len)		/* do not process empty strings */
1329 	{
1330 	    /* change all to upper case*/
1331 	    for(i=0; i< len; i++) s[i] = toupper((unsigned char)s[i]);
1332 
1333 	    /* get the value from the values table when strings are the same */
1334 	    for(i=0; i< table_size; i++)
1335 	    {
1336 		len2 = strlen(stable[i]);		/* only compare if the string is long enough */
1337 		if(len >= len2 && strncmp(s, stable[i], len2)  == 0)
1338 		{
1339 		    value = values[i];
1340 		    break;
1341 		}
1342 	    }
1343 
1344 	    if(i == table_size)		/* string was not found; show error */
1345 	    {
1346 		fprintf(stderr, "\nError in stringToValueCvt: failed to convert '%s' (in uppercase) to a value", s);
1347 		fprintf(stderr, "\nUsing the default value for %s", default_string);
1348 	    }
1349 	}
1350     }
1351 
1352     return value;
1353 
1354 }
1355 
1356 static char *depotNames[] = { "TO_NONE", "TO_STDOUT", "TO_STDERR", "TO_CURRENT",
1357 				 "TO_NEW", "TO_TEXT_DIALOG"};
1358 static int  depotValues[] = { TO_NONE, TO_STDOUT, TO_STDERR, TO_CURRENT,
1359 				TO_NEW, TO_TEXT_DIALOG};
1360 
1361 static char *dialogNames[] = { "DIALOG_ERROR", 	"DIALOG_INFORMATION",
1362 				"DIALOG_MESSAGE", 	"DIALOG_WARNING"
1363 			     };
1364 static int   dialogType[] =  { 	XmDIALOG_ERROR, 	XmDIALOG_INFORMATION,
1365 				XmDIALOG_MESSAGE,	XmDIALOG_WARNING
1366 			     };
1367 
1368 enum extendSelectionValue    { 	NO_EXTEND, 		EXTEND_TO_LEFT_LF,
1369 				EXTEND_TO_RIGHT_LF,	EXTEND_TO_LINES,
1370 				EXTEND_TO_WHOLE_FILE, 	USE_WHOLE_FILE,
1371 				USE_FILE_OR_SELECTION
1372 			     };
1373 
1374 static char *extendNames[] = {  "NO_EXTEND", 		"EXTEND_TO_LEFT_LF",
1375 				"EXTEND_TO_RIGHT_LF", 	"EXTEND_TO_LINES",
1376 				"EXTEND_TO_WHOLE_FILE",	"USE_WHOLE_FILE",
1377 				"USE_FILE_OR_SELECTION"
1378 			     };
1379 static int   extendValues[]= {	NO_EXTEND,		EXTEND_TO_LEFT_LF,
1380 				EXTEND_TO_RIGHT_LF,	EXTEND_TO_LINES,
1381 				EXTEND_TO_WHOLE_FILE, 	USE_WHOLE_FILE,
1382 				USE_FILE_OR_SELECTION
1383 			     };
1384 
1385 #ifdef _NO_PROTO
decodeFilterExtensions(filterExt,needSelection,childOutDepot,outDialogTitle,dialogsTitle,noOutDialog,noOutMsg,extendTo)1386 void decodeFilterExtensions(filterExt, needSelection, childOutDepot,
1387 	outDialogTitle,	dialogsTitle, noOutDialog, noOutMsg, extendTo)
1388     XmStringTable      filterExt;
1389     Boolean	needSelection;
1390     int 	*childOutDepot;
1391     char 	**outDialogTitle;
1392     char 	**dialogsTitle;
1393     int 	*noOutDialog;
1394     XmString 	*noOutMsg;
1395     int 	*extendTo;
1396 #else  /* ! _NO_PROTO */
1397 
1398 void decodeFilterExtensions(XmStringTable filterExt, Boolean needSelection,
1399 	int *childOutDepot, char **outDialogTitle, char **dialogsTitle,
1400 	int *noOutDialog, XmString *noOutMsg, int *extendTo)
1401 #endif
1402 {
1403     char *txtPar1=NULL, *txtPar2=NULL;
1404     char *txtPar3=NULL;		/* {NO}EXTEND_TO_LF */
1405     Boolean	   stringTableEOF = False;
1406     Boolean	   extendToLF = True;		/* as default we extend the selection to the right line feed */
1407 
1408     /* set the default values first */
1409 
1410     *outDialogTitle = *dialogsTitle= NULL;
1411     *noOutMsg = NULL;
1412     *childOutDepot=TO_CURRENT;	/* default value */
1413     *noOutDialog  = TO_NONE;		/* = 0 */
1414 
1415     /* process (decode) the current table (specified as a resource)*/
1416 
1417 #define getString_n_checkEOF(nr, str)	if(!stringTableEOF)	\
1418     { 								\
1419 	XmStringGetLtoR(filterExt[nr], charset, &str);	\
1420 	if(!str) stringTableEOF = True; 			\
1421     }
1422 
1423 #define getXmString_n_checkEOF(nr, str)	if(!stringTableEOF)	\
1424     { 								\
1425 	if(filterExt[nr]) str = filterExt[nr];	\
1426 	else stringTableEOF = True; 			\
1427     }
1428 
1429 
1430     /* the structure of filterExt is: childOutDepot (int obtained from text comparison),
1431        outDialogTitle (text - to safeguard us from multi-line xm strings),
1432        dialogsTitle (text - to safeguard us from multi-line xm strings),
1433        noOutDialog (int obtained from text comparison), noOutMsg (xmstr)
1434     */
1435     getString_n_checkEOF(0,txtPar1);
1436     getString_n_checkEOF(1,*outDialogTitle);
1437     getString_n_checkEOF(2,*dialogsTitle);
1438 
1439     getString_n_checkEOF(3,txtPar2);
1440     getXmString_n_checkEOF(4,*noOutMsg);	/* Note this is XmString here ! */
1441     getString_n_checkEOF(5,txtPar3);
1442 
1443 
1444 
1445     *childOutDepot = stringToValueCvt(txtPar1, depotNames, depotValues,
1446 			XtNumber(depotNames), "TO_CURRENT", TO_CURRENT);
1447     if(!txtPar1) XtFree(txtPar1);
1448 
1449 
1450     *noOutDialog   = stringToValueCvt(txtPar2, dialogNames, dialogType,
1451 			XtNumber(dialogNames), "TO_NONE", TO_NONE);
1452     if(!txtPar2) XtFree(txtPar2);
1453 
1454 
1455     /* default value for extendTo depends on needSelection flag */
1456     if(needSelection)
1457 	*extendTo   = stringToValueCvt(txtPar3, extendNames, extendValues,
1458 			XtNumber(extendNames), "EXTEND_TO_LINES", EXTEND_TO_LINES);
1459     else
1460 	*extendTo   = stringToValueCvt(txtPar3, extendNames, extendValues,
1461 			XtNumber(extendNames), "NO_EXTEND", NO_EXTEND);
1462     if(!txtPar3) XtFree(txtPar3);
1463 
1464 
1465 }
1466 
1467 #ifdef _NO_PROTO
executeFilterCmd(win,cmd,filterExt,needSelection)1468 void executeFilterCmd(win, cmd, filterExt, needSelection)
1469     aseditWindowStruct *win;
1470     char 	       *cmd;
1471     XmStringTable      filterExt;
1472     Boolean needSelection;
1473 #else  /* ! _NO_PROTO */
1474 
1475 void executeFilterCmd(aseditWindowStruct *win, char *cmd, XmStringTable filterExt,
1476 			Boolean needSelection)
1477 #endif
1478 {
1479     char *cmdInput=NULL;
1480     int n;
1481     XmTextPosition left, right;
1482     int 	childOutDepot, noOutDialog, extendTo;
1483     char 	*outDialogTitle=NULL, *dialogsTitle=NULL;
1484     XmString 	noOutMsg;
1485     Boolean	ext_status;
1486 
1487     /* make sure that the cmd is not NULL; */
1488     if(cmd == NULL || strlen(cmd)== 0) return;		/* do not process empty command */
1489 
1490     /* get the command extended parameters first (if any); we have to do it before
1491        checking the needSelection because of the new WHOLE_FILE* flags */
1492 
1493     decodeFilterExtensions(filterExt, needSelection, &childOutDepot,
1494 		&outDialogTitle, &dialogsTitle, &noOutDialog,
1495 		&noOutMsg, &extendTo);
1496 
1497 
1498     /* the following two checks would probably never be used when
1499        needSelection is set to True;
1500        appropriate commands should be greyed out when there is no primary
1501        selection in the edit area (so this procedure would NOT be called
1502        when there is no selection and needSelection is True!)
1503     */
1504 
1505     if(needSelection && extendTo != EXTEND_TO_WHOLE_FILE &&
1506 			extendTo != USE_WHOLE_FILE	 &&
1507                         extendTo != USE_FILE_OR_SELECTION )
1508 
1509     {
1510 	/* classic filter case: check the selection and get the selection positions */
1511 	if(!XmTextGetSelectionPosition(win->edit_text, &left, &right) || left == right)
1512 	{
1513 	    if(outDialogTitle) XtFree(outDialogTitle);
1514             if(dialogsTitle)   XtFree(dialogsTitle);
1515 	    return;		/* nothing to process, return */
1516 	}
1517     }
1518     else
1519     {
1520 	/* usually the command case; we don't need the selection and we don't use
1521 	   it as the command input; but get the left and right position
1522 	   of the selection if it does exist;
1523 	   the command result will replace current selection (if it is not
1524 	   available or its length is zero, we will put it at the current insertion point)
1525 	*/
1526 
1527 	if(!XmTextGetSelectionPosition(win->edit_text, &left, &right) || left==right)
1528 		left = right = XmTextGetInsertionPosition(win->edit_text);
1529 
1530     }
1531 
1532 
1533     /* first extend the selection to encompass the last line feed (if not explicitly
1534        forbidden) ; alternatively
1535        we could extend the selection to whole lines; the important bit is to
1536        have the final line feed (sed works on complete lines!)
1537        Note we support also extend jut to the beginning of line (although
1538        right now I can't think of application that would use that)
1539     */
1540     ext_status = True;
1541     if      (extendTo == EXTEND_TO_RIGHT_LF)
1542 	ext_status = extendSelectionToLF(win, &left, &right);
1543     else if (extendTo == EXTEND_TO_LEFT_LF)
1544 	ext_status = extendSelectionToBOL(win, &left, &right);
1545     else if (extendTo == EXTEND_TO_LINES)
1546 	ext_status = extendSelectionToLines(win, &left, &right);
1547     else if (extendTo == EXTEND_TO_WHOLE_FILE)
1548 	ext_status = extendSelectionToWholeFile(win, &left, &right);
1549 
1550     if(!ext_status)	/* check the extent status */
1551     {
1552 	/* we couldn't extend and the user answered "Do NOT proceed" -- return */
1553 	if(outDialogTitle) XtFree(outDialogTitle);
1554 	if(dialogsTitle)   XtFree(dialogsTitle);
1555 	return;
1556     }
1557 
1558 
1559 
1560     /* a user can request to use the whole file (disregard the selection) */
1561     if(extendTo == USE_WHOLE_FILE)
1562     {
1563 	cmdInput = XmTextGetString(win->edit_text);
1564     }
1565     else if(extendTo == USE_FILE_OR_SELECTION)
1566     {
1567 	if(left == right) /* selection was empty, use the whole file */
1568 	    cmdInput = XmTextGetString(win->edit_text);
1569 	else
1570 	    cmdInput = XmTextGetSelection(win->edit_text);
1571     }
1572     else
1573     {
1574 	/* default, pre 1.32 situation */
1575     	if(needSelection)				/* get the selection */
1576 	    cmdInput = XmTextGetSelection(win->edit_text);
1577     	else
1578 	    cmdInput = NULL;
1579     }
1580 
1581 
1582 
1583     if(executeShellCmd(win, cmd, cmdInput, left, right,
1584 		childOutDepot, outDialogTitle, dialogsTitle,
1585 		noOutDialog, noOutMsg))
1586     {
1587 	/* the shell command is being executed */
1588 	;
1589     }
1590 
1591     /* do *NOT* free cmdInput, outDialogTitle, dialogsTitle; their memory will be freed
1592        when all input is processed (i.e. in processChildOutput)
1593     */
1594 
1595 
1596 }   /* executeFilterCmd */
1597 
1598