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