1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
32
33 #include "shell.h"
34 #include "textBuf.h"
35 #include "text.h"
36 #include "nedit.h"
37 #include "window.h"
38 #include "preferences.h"
39 #include "file.h"
40 #include "macro.h"
41 #include "interpret.h"
42 #include "../util/DialogF.h"
43 #include "../util/misc.h"
44 #include "../util/nedit_malloc.h"
45 #include "menu.h"
46
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <signal.h>
51 #include <sys/types.h>
52 #include <sys/param.h>
53 #include <sys/wait.h>
54 #include <unistd.h>
55 #include <fcntl.h>
56 #include <ctype.h>
57 #include <errno.h>
58 #ifdef notdef
59 #ifdef IBM
60 #define NBBY 8
61 #include <sys/select.h>
62 #endif
63 #include <time.h>
64 #endif
65 #ifdef __EMX__
66 #include <process.h>
67 #endif
68
69 #include <Xm/Xm.h>
70 #include <Xm/MessageB.h>
71 #include <Xm/Text.h>
72 #include <Xm/Form.h>
73 #include <Xm/PushBG.h>
74
75 #ifdef HAVE_DEBUG_H
76 #include "../debug.h"
77 #endif
78
79
80
81 #define IO_BUF_SIZE 4096
82 #define MAX_OUT_DIALOG_ROWS 30
83 #define MAX_OUT_DIALOG_COLS 80
84 #define OUTPUT_FLUSH_FREQ 1000
85
86 #define BANNER_WAIT_TIME 6000
87
88
89
90 #define ACCUMULATE 1
91 #define ERROR_DIALOGS 2
92 #define REPLACE_SELECTION 4
93 #define RELOAD_FILE_AFTER 8
94 #define OUTPUT_TO_DIALOG 16
95 #define OUTPUT_TO_STRING 32
96
97
98 typedef struct bufElem {
99 struct bufElem *next;
100 int length;
101 char contents[
IO_BUF_SIZE];
102 } buffer;
103
104
105
106 typedef struct {
107 int flags;
108 int stdinFD, stdoutFD, stderrFD;
109 pid_t childPid;
110 XtInputId stdinInputID, stdoutInputID, stderrInputID;
111 buffer *outBufs, *errBufs;
112 char *input;
113 char *inPtr;
114 Widget textW;
115 int leftPos, rightPos;
116 int inLength;
117 XtIntervalId bannerTimeoutID, flushTimeoutID;
118 char bannerIsUp;
119 char fromMacro;
120 } shellCmdInfo;
121
122 static void issueCommand(WindowInfo *window,
const char *command,
char *input,
123 int inputLen,
int flags, Widget textW,
int replaceLeft,
124 int replaceRight,
int fromMacro);
125 static void stdoutReadProc(XtPointer clientData,
int *source, XtInputId *id);
126 static void stderrReadProc(XtPointer clientData,
int *source, XtInputId *id);
127 static void stdinWriteProc(XtPointer clientData,
int *source, XtInputId *id);
128 static void finishCmdExecution(WindowInfo *window,
int terminatedOnError);
129 static pid_t forkCommand(Widget parent,
const char *command,
const char *cmdDir,
130 int *stdinFD,
int *stdoutFD,
int *stderrFD);
131 static void addOutput(buffer **bufList, buffer *buf);
132 static char *coalesceOutput(buffer **bufList,
int *length);
133 static void freeBufList(buffer **bufList);
134 static void removeTrailingNewlines(
char *string);
135 static void createOutputDialog(Widget parent,
char *text);
136 static void destroyOutDialogCB(Widget w, XtPointer callback, XtPointer closure);
137 static void measureText(
char *text,
int wrapWidth,
int *rows,
int *cols,
138 int *wrapped);
139 static void truncateString(
char *string,
int length);
140 static void bannerTimeoutProc(XtPointer clientData, XtIntervalId *id);
141 static void flushTimeoutProc(XtPointer clientData, XtIntervalId *id);
142 static void safeBufReplace(textBuffer *buf,
int *start,
int *end,
143 const char *text);
144 static char *shellCommandSubstitutes(
const char *inStr,
const char *fileStr,
145 const char *lineStr);
146 static int shellSubstituter(
char *outStr,
const char *inStr,
const char *fileStr,
147 const char *lineStr,
int outLen,
int predictOnly);
148
149
150
151
152
153
154 void FilterSelection(WindowInfo *window,
const char *command,
int fromMacro)
155 {
156 int left, right, textLen;
157 char *text;
158
159
160 if (window->shellCmdData !=
NULL) {
161 XBell(TheDisplay,
0);
162 return;
163 }
164
165
166
167 text = BufGetSelectionText(window->buffer);
168 if (*text ==
'\0') {
169 NEditFree(text);
170 XBell(TheDisplay,
0);
171 return;
172 }
173 textLen = strlen(text);
174 BufUnsubstituteNullChars(text, window->buffer);
175 left = window->buffer->primary.start;
176 right = window->buffer->primary.end;
177
178
179 issueCommand(window, command, text, textLen,
ACCUMULATE |
ERROR_DIALOGS |
180 REPLACE_SELECTION, window->lastFocus, left, right, fromMacro);
181 }
182
183
184
185
186
187
188 void ExecShellCommand(WindowInfo *window,
const char *command,
int fromMacro)
189 {
190 int left, right, flags =
0;
191 char *subsCommand, fullName[
MAXPATHLEN];
192 int pos, line, column;
193 char lineNumber[
16];
194
195
196 if (window->shellCmdData !=
NULL) {
197 XBell(TheDisplay,
0);
198 return;
199 }
200
201
202 pos = TextGetCursorPos(window->lastFocus);
203 if (GetSimpleSelection(window->buffer, &left, &right))
204 flags =
ACCUMULATE |
REPLACE_SELECTION;
205 else
206 left = right = pos;
207
208
209
210 strcpy(fullName, window->path);
211 strcat(fullName, window->filename);
212 if(!TextPosToLineAndCol(window->lastFocus, pos, &line, &column)) {
213 line = BufCountLines(window->buffer,
0, pos) +
1;
214 }
215 snprintf(lineNumber,
16,
"%d", line);
216 subsCommand = shellCommandSubstitutes(command, fullName, lineNumber);
217 if (subsCommand ==
NULL)
218 {
219 DialogF(
DF_ERR, window->shell,
1,
"Shell Command",
220 "Shell command is too long due to\n"
221 "filename substitutions with ''%%'' or\n"
222 "line number substitutions with ''#''",
"OK");
223 return;
224 }
225
226
227 issueCommand(window, subsCommand,
NULL,
0, flags, window->lastFocus, left,
228 right, fromMacro);
229 free(subsCommand);
230 }
231
232
233
234
235
236 void ShellCmdToMacroString(WindowInfo *window,
const char *command,
237 const char *input)
238 {
239 char *inputCopy;
240
241
242
243 inputCopy = *input ==
'\0' ?
NULL : NEditStrdup(input);
244
245
246 issueCommand(window, command, inputCopy, strlen(input),
247 ACCUMULATE |
OUTPUT_TO_STRING,
NULL,
0,
0, True);
248 }
249
250
251
252
253
254 void ExecCursorLine(WindowInfo *window,
int fromMacro)
255 {
256 char *cmdText;
257 int left, right, insertPos;
258 char *subsCommand, fullName[
MAXPATHLEN];
259 int pos, line, column;
260 char lineNumber[
16];
261
262
263 if (window->shellCmdData !=
NULL) {
264 XBell(TheDisplay,
0);
265 return;
266 }
267
268
269 pos = TextGetCursorPos(window->lastFocus);
270 if (!GetSimpleSelection(window->buffer, &left, &right)) {
271 left = right = pos;
272 left = BufStartOfLine(window->buffer, left);
273 right = BufEndOfLine(window->buffer, right);
274 insertPos = right;
275 }
else
276 insertPos = BufEndOfLine(window->buffer, right);
277 cmdText = BufGetRange(window->buffer, left, right);
278 BufUnsubstituteNullChars(cmdText, window->buffer);
279
280
281 BufInsert(window->buffer, insertPos,
"\n");
282
283
284
285 strcpy(fullName, window->path);
286 strcat(fullName, window->filename);
287 if(!TextPosToLineAndCol(window->lastFocus, pos, &line, &column)) {
288 line = BufCountLines(window->buffer,
0, pos) +
1;
289 }
290 snprintf(lineNumber,
16,
"%d", line);
291
292 subsCommand = shellCommandSubstitutes(cmdText, fullName, lineNumber);
293 if (subsCommand ==
NULL)
294 {
295 DialogF(
DF_ERR, window->shell,
1,
"Shell Command",
296 "Shell command is too long due to\n"
297 "filename substitutions with ''%%'' or\n"
298 "line number substitutions with ''#''",
"OK");
299 return;
300 }
301
302
303 issueCommand(window, subsCommand,
NULL,
0,
0, window->lastFocus, insertPos+
1,
304 insertPos+
1, fromMacro);
305 free(subsCommand);
306 NEditFree(cmdText);
307 }
308
309
310
311
312
313
314 void DoShellMenuCmd(WindowInfo *window,
const char *command,
315 int input,
int output,
316 int outputReplacesInput,
int saveFirst,
int loadAfter,
int fromMacro)
317 {
318 int flags =
0;
319 char *text;
320 char *subsCommand, fullName[
MAXPATHLEN];
321 int left =
0, right =
0, textLen;
322 int pos, line, column;
323 char lineNumber[
16];
324 WindowInfo *inWindow = window;
325 Widget outWidget;
326
327
328 if (window->shellCmdData !=
NULL) {
329 XBell(TheDisplay,
0);
330 return;
331 }
332
333
334
335 strcpy(fullName, window->path);
336 strcat(fullName, window->filename);
337 pos = TextGetCursorPos(window->lastFocus);
338 if(!TextPosToLineAndCol(window->lastFocus, pos, &line, &column)) {
339 line = BufCountLines(window->buffer,
0, pos) +
1;
340 }
341 snprintf(lineNumber,
16,
"%d", line);
342
343 subsCommand = shellCommandSubstitutes(command, fullName, lineNumber);
344 if (subsCommand ==
NULL)
345 {
346 DialogF(
DF_ERR, window->shell,
1,
"Shell Command",
347 "Shell command is too long due to\n"
348 "filename substitutions with ''%%'' or\n"
349 "line number substitutions with ''#''",
"OK");
350 return;
351 }
352
353
354
355 if (input ==
FROM_SELECTION) {
356 text = BufGetSelectionText(window->buffer);
357 if (*text ==
'\0') {
358 NEditFree(text);
359 NEditFree(subsCommand);
360 XBell(TheDisplay,
0);
361 return;
362 }
363 flags |=
ACCUMULATE |
ERROR_DIALOGS;
364 }
else if (input ==
FROM_WINDOW) {
365 text = BufGetAll(window->buffer);
366 flags |=
ACCUMULATE |
ERROR_DIALOGS;
367 }
else if (input ==
FROM_EITHER) {
368 text = BufGetSelectionText(window->buffer);
369 if (*text ==
'\0') {
370 NEditFree(text);
371 text = BufGetAll(window->buffer);
372 }
373 flags |=
ACCUMULATE |
ERROR_DIALOGS;
374 }
else
375 text =
NULL;
376
377
378
379 if (text !=
NULL) {
380 textLen = strlen(text);
381 BufUnsubstituteNullChars(text, window->buffer);
382 }
else
383 textLen =
0;
384
385
386
387
388 if (output ==
TO_DIALOG) {
389 outWidget =
NULL;
390 flags |=
OUTPUT_TO_DIALOG;
391 left = right =
0;
392 }
else if (output ==
TO_NEW_WINDOW) {
393 EditNewFile(GetPrefOpenInTab()?inWindow:
NULL,
NULL, False,
NULL, window->path);
394 outWidget = WindowList->textArea;
395 inWindow = WindowList;
396 left = right =
0;
397 CheckCloseDim();
398 }
else {
399 outWidget = window->lastFocus;
400 if (outputReplacesInput && input !=
FROM_NONE) {
401 if (input ==
FROM_WINDOW) {
402 left =
0;
403 right = window->buffer->length;
404 }
else if (input ==
FROM_SELECTION) {
405 GetSimpleSelection(window->buffer, &left, &right);
406 flags |=
ACCUMULATE |
REPLACE_SELECTION;
407 }
else if (input ==
FROM_EITHER) {
408 if (GetSimpleSelection(window->buffer, &left, &right))
409 flags |=
ACCUMULATE |
REPLACE_SELECTION;
410 else {
411 left =
0;
412 right = window->buffer->length;
413 }
414 }
415 }
else {
416 if (GetSimpleSelection(window->buffer, &left, &right))
417 flags |=
ACCUMULATE |
REPLACE_SELECTION;
418 else
419 left = right = TextGetCursorPos(window->lastFocus);
420 }
421 }
422
423
424 if (saveFirst) {
425 if (!SaveWindow(window)) {
426 if (input !=
FROM_NONE)
427 NEditFree(text);
428 free(subsCommand);
429 return;
430 }
431 }
432
433
434
435 if (loadAfter)
436 flags |=
RELOAD_FILE_AFTER;
437
438
439 issueCommand(inWindow, subsCommand, text, textLen, flags, outWidget, left,
440 right, fromMacro);
441 free(subsCommand);
442 }
443
444
445
446
447 void AbortShellCommand(WindowInfo *window)
448 {
449 shellCmdInfo *cmdData = window->shellCmdData;
450
451 if (cmdData ==
NULL)
452 return;
453 kill(- cmdData->childPid,
SIGTERM);
454 finishCmdExecution(window, True);
455 }
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480 static void issueCommand(WindowInfo *window,
const char *command,
char *input,
481 int inputLen,
int flags, Widget textW,
int replaceLeft,
482 int replaceRight,
int fromMacro)
483 {
484 int stdinFD, stdoutFD, stderrFD =
0;
485 XtAppContext context = XtWidgetToApplicationContext(window->shell);
486 shellCmdInfo *cmdData;
487 pid_t childPid;
488
489
490 if ((flags &
ERROR_DIALOGS || flags &
REPLACE_SELECTION ||
491 flags &
OUTPUT_TO_STRING) && !(flags &
ACCUMULATE))
492 return;
493
494
495
496
497 if (fromMacro)
498 window = MacroRunWindow();
499
500
501 if (!fromMacro)
502 BeginWait(window->shell);
503
504
505 if (!fromMacro)
506 SetSensitive(window, window->cancelShellItem, True);
507
508
509 childPid = forkCommand(window->shell, command, window->path, &stdinFD,
510 &stdoutFD, (flags &
ERROR_DIALOGS) ? &stderrFD :
NULL);
511
512
513 if (fcntl(stdinFD,
F_SETFL,
O_NONBLOCK) <
0)
514 perror(
"xnedit: Internal error (fcntl)");
515 if (fcntl(stdoutFD,
F_SETFL,
O_NONBLOCK) <
0)
516 perror(
"xnedit: Internal error (fcntl1)");
517 if (flags &
ERROR_DIALOGS) {
518 if (fcntl(stderrFD,
F_SETFL,
O_NONBLOCK) <
0)
519 perror(
"xnedit: Internal error (fcntl2)");
520 }
521
522
523 if (input ==
NULL)
524 close(stdinFD);
525
526
527
528 cmdData = (shellCmdInfo *)NEditMalloc(
sizeof(shellCmdInfo));
529 window->shellCmdData = cmdData;
530 cmdData->flags = flags;
531 cmdData->stdinFD = stdinFD;
532 cmdData->stdoutFD = stdoutFD;
533 cmdData->stderrFD = stderrFD;
534 cmdData->childPid = childPid;
535 cmdData->outBufs =
NULL;
536 cmdData->errBufs =
NULL;
537 cmdData->input = input;
538 cmdData->inPtr = input;
539 cmdData->textW = textW;
540 cmdData->bannerIsUp = False;
541 cmdData->fromMacro = fromMacro;
542 cmdData->leftPos = replaceLeft;
543 cmdData->rightPos = replaceRight;
544 cmdData->inLength = inputLen;
545
546
547 if (fromMacro)
548 cmdData->bannerTimeoutID =
0;
549 else
550 cmdData->bannerTimeoutID = XtAppAddTimeOut(context,
BANNER_WAIT_TIME,
551 bannerTimeoutProc, window);
552
553
554 if ((flags &
ACCUMULATE) || textW ==
NULL)
555 cmdData->flushTimeoutID =
0;
556 else
557 cmdData->flushTimeoutID = XtAppAddTimeOut(context,
OUTPUT_FLUSH_FREQ,
558 flushTimeoutProc, window);
559
560
561 cmdData->stdoutInputID = XtAppAddInput(context, stdoutFD,
562 (XtPointer)XtInputReadMask, stdoutReadProc, window);
563 if (input !=
NULL)
564 cmdData->stdinInputID = XtAppAddInput(context, stdinFD,
565 (XtPointer)XtInputWriteMask, stdinWriteProc, window);
566 else
567 cmdData->stdinInputID =
0;
568 if (flags &
ERROR_DIALOGS)
569 cmdData->stderrInputID = XtAppAddInput(context, stderrFD,
570 (XtPointer)XtInputReadMask, stderrReadProc, window);
571 else
572 cmdData->stderrInputID =
0;
573
574
575
576 if (fromMacro)
577 PreemptMacro();
578 }
579
580
581
582
583
584 static void stdoutReadProc(XtPointer clientData,
int *source, XtInputId *id)
585 {
586 WindowInfo *window = (WindowInfo *)clientData;
587 shellCmdInfo *cmdData = window->shellCmdData;
588 buffer *buf;
589 int nRead;
590
591
592 buf = (buffer *)NEditMalloc(
sizeof(buffer));
593 nRead = read(cmdData->stdoutFD, buf->contents,
IO_BUF_SIZE);
594
595
596 if (nRead == -
1) {
597 if (errno !=
EWOULDBLOCK && errno !=
EAGAIN) {
598 perror(
"xnedit: Error reading shell command output");
599 NEditFree(buf);
600 finishCmdExecution(window, True);
601 }
602 return;
603 }
604
605
606
607 if (nRead ==
0) {
608 NEditFree(buf);
609 XtRemoveInput(cmdData->stdoutInputID);
610 cmdData->stdoutInputID =
0;
611 if (cmdData->stderrInputID ==
0)
612 finishCmdExecution(window, False);
613 return;
614 }
615
616
617 buf->length = nRead;
618 addOutput(&cmdData->outBufs, buf);
619 }
620
621
622
623
624
625 static void stderrReadProc(XtPointer clientData,
int *source, XtInputId *id)
626 {
627 WindowInfo *window = (WindowInfo *)clientData;
628 shellCmdInfo *cmdData = window->shellCmdData;
629 buffer *buf;
630 int nRead;
631
632
633 buf = (buffer *)NEditMalloc(
sizeof(buffer));
634 nRead = read(cmdData->stderrFD, buf->contents,
IO_BUF_SIZE);
635
636
637 if (nRead == -
1) {
638 if (errno !=
EWOULDBLOCK && errno !=
EAGAIN) {
639 perror(
"xnedit: Error reading shell command error stream");
640 NEditFree(buf);
641 finishCmdExecution(window, True);
642 }
643 return;
644 }
645
646
647
648 if (nRead ==
0) {
649 NEditFree(buf);
650 XtRemoveInput(cmdData->stderrInputID);
651 cmdData->stderrInputID =
0;
652 if (cmdData->stdoutInputID ==
0)
653 finishCmdExecution(window, False);
654 return;
655 }
656
657
658 buf->length = nRead;
659 addOutput(&cmdData->errBufs, buf);
660 }
661
662
663
664
665
666 static void stdinWriteProc(XtPointer clientData,
int *source, XtInputId *id)
667 {
668 WindowInfo *window = (WindowInfo *)clientData;
669 shellCmdInfo *cmdData = window->shellCmdData;
670 int nWritten;
671
672 nWritten = write(cmdData->stdinFD, cmdData->inPtr, cmdData->inLength);
673 if (nWritten == -
1) {
674 if (errno ==
EPIPE) {
675
676
677 XtRemoveInput(cmdData->stdinInputID);
678 cmdData->stdinInputID =
0;
679 close(cmdData->stdinFD);
680 cmdData->inPtr =
NULL;
681 }
else if (errno !=
EWOULDBLOCK && errno !=
EAGAIN) {
682 perror(
"xnedit: Write to shell command failed");
683 finishCmdExecution(window, True);
684 }
685 }
else {
686 cmdData->inPtr += nWritten;
687 cmdData->inLength -= nWritten;
688 if (cmdData->inLength <=
0) {
689 XtRemoveInput(cmdData->stdinInputID);
690 cmdData->stdinInputID =
0;
691 close(cmdData->stdinFD);
692 cmdData->inPtr =
NULL;
693 }
694 }
695 }
696
697
698
699
700
701 #define MAX_TIMEOUT_MSG_LEN (
MAX_ACCEL_LEN +
60)
702 static void bannerTimeoutProc(XtPointer clientData, XtIntervalId *id)
703 {
704 WindowInfo *window = (WindowInfo *)clientData;
705 shellCmdInfo *cmdData = window->shellCmdData;
706 XmString xmCancel;
707 char* cCancel;
708 char message[
MAX_TIMEOUT_MSG_LEN];
709
710 cmdData->bannerIsUp = True;
711
712
713 XtVaGetValues(window->cancelShellItem, XmNacceleratorText, &xmCancel,
NULL);
714
715
716 cCancel = GetXmStringText(xmCancel);
717
718
719 XmStringFree(xmCancel);
720
721
722 if (
'\0' == cCancel[
0])
723 {
724 strncpy(message,
"Shell Command in Progress",
MAX_TIMEOUT_MSG_LEN);
725 message[
MAX_TIMEOUT_MSG_LEN -
1] =
'\0';
726 }
else
727 {
728 snprintf(message,
729 MAX_TIMEOUT_MSG_LEN,
730 "Shell Command in Progress -- Press %s to Cancel",
731 cCancel);
732 }
733
734
735 NEditFree(cCancel);
736
737 SetModeMessage(window, message);
738 cmdData->bannerTimeoutID =
0;
739 }
740
741
742
743
744
745
746
747 static void safeBufReplace(textBuffer *buf,
int *start,
int *end,
748 const char *text)
749 {
750 if (*start > buf->length)
751 *start = buf->length;
752 if (*end > buf->length)
753 *end = buf->length;
754 BufReplace(buf, *start, *end, text);
755 }
756
757
758
759
760
761 static void flushTimeoutProc(XtPointer clientData, XtIntervalId *id)
762 {
763 WindowInfo *window = (WindowInfo *)clientData;
764 shellCmdInfo *cmdData = window->shellCmdData;
765 textBuffer *buf = TextGetBuffer(cmdData->textW);
766 int len;
767 char *outText;
768
769
770 if (cmdData->textW ==
NULL)
771 return;
772
773 outText = coalesceOutput(&cmdData->outBufs, &len);
774 if (len !=
0) {
775 if (BufSubstituteNullChars(outText, len, buf)) {
776 safeBufReplace(buf, &cmdData->leftPos, &cmdData->rightPos, outText);
777 TextSetCursorPos(cmdData->textW, cmdData->leftPos+strlen(outText));
778 cmdData->leftPos += len;
779 cmdData->rightPos = cmdData->leftPos;
780 }
else
781 fprintf(stderr,
"xnedit: Too much binary data\n");
782 }
783 NEditFree(outText);
784
785
786 cmdData->flushTimeoutID = XtAppAddTimeOut(
787 XtWidgetToApplicationContext(window->shell),
788 OUTPUT_FLUSH_FREQ, flushTimeoutProc, clientData);
789 }
790
791
792
793
794
795
796
797
798 static void finishCmdExecution(WindowInfo *window,
int terminatedOnError)
799 {
800 shellCmdInfo *cmdData = window->shellCmdData;
801 textBuffer *buf;
802 int status, failure, errorReport, reselectStart, outTextLen, errTextLen;
803 int resp, cancel = False, fromMacro = cmdData->fromMacro;
804 char *outText, *errText =
NULL;
805
806
807 if (cmdData->stdoutInputID !=
0)
808 XtRemoveInput(cmdData->stdoutInputID);
809 if (cmdData->stdinInputID !=
0)
810 XtRemoveInput(cmdData->stdinInputID);
811 if (cmdData->stderrInputID !=
0)
812 XtRemoveInput(cmdData->stderrInputID);
813
814
815 close(cmdData->stdoutFD);
816 if (cmdData->flags &
ERROR_DIALOGS)
817 close(cmdData->stderrFD);
818 if (cmdData->inPtr !=
NULL)
819 close(cmdData->stdinFD);
820
821
822 NEditFree(cmdData->input);
823
824
825 if (cmdData->flushTimeoutID !=
0)
826 XtRemoveTimeOut(cmdData->flushTimeoutID);
827 if (cmdData->bannerTimeoutID !=
0)
828 XtRemoveTimeOut(cmdData->bannerTimeoutID);
829
830
831 if (!cmdData->fromMacro) {
832 EndWait(window->shell);
833 SetSensitive(window, window->cancelShellItem, False);
834 if (cmdData->bannerIsUp)
835 ClearModeMessage(window);
836 }
837
838
839 if (terminatedOnError) {
840 freeBufList(&cmdData->outBufs);
841 freeBufList(&cmdData->errBufs);
842 waitpid(cmdData->childPid, &status,
0);
843 goto cmdDone;
844 }
845
846
847
848 outText = coalesceOutput(&cmdData->outBufs, &outTextLen);
849 if (cmdData->flags &
ERROR_DIALOGS)
850 errText = coalesceOutput(&cmdData->errBufs, &errTextLen);
851
852
853 waitpid(cmdData->childPid, &status,
0);
854
855
856
857
858 if (cmdData->flags &
ERROR_DIALOGS)
859 {
860 failure =
WIFEXITED(status) &&
WEXITSTATUS(status) !=
0;
861 errorReport = *errText !=
'\0';
862
863 if (failure && errorReport)
864 {
865 removeTrailingNewlines(errText);
866 truncateString(errText,
DF_MAX_MSG_LENGTH);
867 resp = DialogF(
DF_WARN, window->shell,
2,
"Warning",
"%s",
"Cancel",
868 "Proceed", errText);
869 cancel = resp ==
1;
870 }
else if (failure)
871 {
872 truncateString(outText,
DF_MAX_MSG_LENGTH-
70);
873 resp = DialogF(
DF_WARN, window->shell,
2,
"Command Failure",
874 "Command reported failed exit status.\n"
875 "Output from command:\n%s",
"Cancel",
"Proceed", outText);
876 cancel = resp ==
1;
877 }
else if (errorReport)
878 {
879 removeTrailingNewlines(errText);
880 truncateString(errText,
DF_MAX_MSG_LENGTH);
881 resp = DialogF(
DF_INF, window->shell,
2,
"Information",
"%s",
882 "Proceed",
"Cancel", errText);
883 cancel = resp ==
2;
884 }
885
886 NEditFree(errText);
887 if (cancel)
888 {
889 NEditFree(outText);
890 goto cmdDone;
891 }
892 }
893
894
895
896
897 if (cmdData->flags &
OUTPUT_TO_DIALOG) {
898 removeTrailingNewlines(outText);
899 if (*outText !=
'\0')
900 createOutputDialog(window->shell, outText);
901 }
else if (cmdData->flags &
OUTPUT_TO_STRING) {
902 ReturnShellCommandOutput(window,outText,
WEXITSTATUS(status));
903 }
else {
904 buf = TextGetBuffer(cmdData->textW);
905 if (!BufSubstituteNullChars(outText, outTextLen, buf)) {
906 fprintf(stderr,
"xnedit: Too much binary data in shell cmd output\n");
907 outText[
0] =
'\0';
908 }
909 if (cmdData->flags &
REPLACE_SELECTION) {
910 reselectStart = buf->primary.rectangular ? -
1 : buf->primary.start;
911 BufReplaceSelected(buf, outText);
912 TextSetCursorPos(cmdData->textW, buf->cursorPosHint);
913 if (reselectStart != -
1)
914 BufSelect(buf, reselectStart, reselectStart + strlen(outText));
915 }
else {
916 safeBufReplace(buf, &cmdData->leftPos, &cmdData->rightPos, outText);
917 TextSetCursorPos(cmdData->textW, cmdData->leftPos+strlen(outText));
918 }
919 }
920
921
922 if (cmdData->flags &
RELOAD_FILE_AFTER)
923 RevertToSaved(window,
NULL);
924
925
926 NEditFree(outText);
927 cmdDone:
928 NEditFree(cmdData);
929 window->shellCmdData =
NULL;
930 if (fromMacro)
931 ResumeMacroExecution(window);
932 }
933
934
935
936
937
938
939
940
941
942 static pid_t forkCommand(Widget parent,
const char *command,
const char *cmdDir,
943 int *stdinFD,
int *stdoutFD,
int *stderrFD)
944 {
945 int childStdoutFD, childStdinFD, childStderrFD, pipeFDs[
2];
946 int dupFD;
947 pid_t childPid;
948
949
950
951 signal(
SIGPIPE,
SIG_IGN);
952
953
954
955
956 if (pipe(pipeFDs) !=
0) {
957 perror(
"xnedit: Internal error (opening stdout pipe)");
958 return -
1;
959 }
960 *stdoutFD = pipeFDs[
0];
961 childStdoutFD = pipeFDs[
1];
962 if (pipe(pipeFDs) !=
0) {
963 perror(
"xnedit: Internal error (opening stdin pipe)");
964 return -
1;
965 }
966 *stdinFD = pipeFDs[
1];
967 childStdinFD = pipeFDs[
0];
968 if (stderrFD ==
NULL)
969 childStderrFD = childStdoutFD;
970 else {
971 if (pipe(pipeFDs) !=
0) {
972 perror(
"xnedit: Internal error (opening stdin pipe)");
973 return -
1;
974 }
975 *stderrFD = pipeFDs[
0];
976 childStderrFD = pipeFDs[
1];
977 }
978
979
980 #ifdef VMS
981 childPid = vfork();
982 #else
983 childPid = fork();
984 #endif
985
986
987
988
989
990 if (
0 == childPid) {
991
992 close(*stdinFD);
993 close(*stdoutFD);
994 if (stderrFD !=
NULL)
995 close(*stderrFD);
996
997
998
999 close(fileno(stdin));
1000 close(fileno(stdout));
1001 close(fileno(stderr));
1002
1003
1004
1005 dupFD = dup2(childStdinFD, fileno(stdin));
1006 if (dupFD == -
1) {
1007 perror(
"dup of stdin failed");
1008 }
1009 dupFD = dup2(childStdoutFD, fileno(stdout));
1010 if (dupFD == -
1) {
1011 perror(
"dup of stdout failed");
1012 }
1013 dupFD = dup2(childStderrFD, fileno(stderr));
1014 if (dupFD == -
1) {
1015 perror(
"dup of stderr failed");
1016 }
1017
1018
1019
1020 close(childStdinFD);
1021 close(childStdoutFD);
1022 close(childStderrFD);
1023
1024
1025
1026 setsid();
1027
1028
1029
1030 if (cmdDir[
0] !=
0) {
1031 if (chdir(cmdDir) == -
1) {
1032 perror(
"chdir to directory of current file failed");
1033 }
1034 }
1035
1036
1037 execlp(GetPrefShell(), GetPrefShell(),
"-c", command,
NULL);
1038
1039
1040 fprintf(stderr,
"Error starting shell: %s\n", GetPrefShell());
1041 exit(
EXIT_FAILURE);
1042 }
1043
1044
1045 if (childPid == -
1)
1046 {
1047 DialogF(
DF_ERR, parent,
1,
"Shell Command",
1048 "Error starting shell command process\n(fork failed)",
1049 "OK");
1050 }
1051
1052
1053 close(childStdinFD);
1054 close(childStdoutFD);
1055 if (stderrFD !=
NULL)
1056 close(childStderrFD);
1057
1058 return childPid;
1059 }
1060
1061
1062
1063
1064 static void addOutput(buffer **bufList, buffer *buf)
1065 {
1066 buf->next = *bufList;
1067 *bufList = buf;
1068 }
1069
1070
1071
1072
1073
1074
1075 static char *coalesceOutput(buffer **bufList,
int *outLength)
1076 {
1077 buffer *buf, *rBufList =
NULL;
1078 char *outBuf, *outPtr, *p;
1079 int i, length =
0;
1080
1081
1082 for (buf=*bufList; buf!=
NULL; buf=buf->next)
1083 length += buf->length;
1084
1085
1086 outBuf = (
char*)NEditMalloc(length+
1);
1087
1088
1089 while (*bufList !=
NULL) {
1090 buf = *bufList;
1091 *bufList = buf->next;
1092 buf->next = rBufList;
1093 rBufList = buf;
1094 }
1095
1096
1097 outPtr = outBuf;
1098 for (buf=rBufList; buf!=
NULL; buf=buf->next) {
1099 p = buf->contents;
1100 for (i=
0; i<buf->length; i++)
1101 *outPtr++ = *p++;
1102 }
1103
1104
1105 *outPtr =
'\0';
1106
1107
1108 freeBufList(&rBufList);
1109
1110 *outLength = outPtr - outBuf;
1111 return outBuf;
1112 }
1113
1114 static void freeBufList(buffer **bufList)
1115 {
1116 buffer *buf;
1117
1118 while (*bufList !=
NULL) {
1119 buf = *bufList;
1120 *bufList = buf->next;
1121 NEditFree(buf);
1122 }
1123 }
1124
1125
1126
1127
1128 static void removeTrailingNewlines(
char *string)
1129 {
1130 char *endPtr = &string[strlen(string)-
1];
1131
1132 while (endPtr >= string && *endPtr ==
'\n')
1133 *endPtr-- =
'\0';
1134 }
1135
1136
1137
1138
1139
1140 static void createOutputDialog(Widget parent,
char *text)
1141 {
1142 Arg al[
50];
1143 int ac, rows, cols, hasScrollBar, wrapped;
1144 Widget form, textW, button;
1145 XmString st1;
1146
1147
1148 measureText(text,
MAX_OUT_DIALOG_COLS, &rows, &cols, &wrapped);
1149 if (rows >
MAX_OUT_DIALOG_ROWS) {
1150 rows =
MAX_OUT_DIALOG_ROWS;
1151 hasScrollBar = True;
1152 }
else
1153 hasScrollBar = False;
1154 if (cols >
MAX_OUT_DIALOG_COLS)
1155 cols =
MAX_OUT_DIALOG_COLS;
1156 if (cols ==
0)
1157 cols =
1;
1158
1159
1160
1161
1162 if (wrapped)
1163 hasScrollBar = True;
1164 ac =
0;
1165 form = CreateFormDialog(parent,
"shellOutForm", al, ac);
1166
1167 ac =
0;
1168 XtSetArg(al[ac], XmNlabelString, st1=
MKSTRING(
"OK")); ac++;
1169 XtSetArg(al[ac], XmNmarginWidth,
BUTTON_WIDTH_MARGIN); ac++;
1170 XtSetArg(al[ac], XmNhighlightThickness,
2); ac++;
1171 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1172 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1173 button = XmCreatePushButtonGadget(form,
"ok", al, ac);
1174 XtManageChild(button);
1175 XtVaSetValues(form, XmNdefaultButton, button,
NULL);
1176 XtVaSetValues(form, XmNcancelButton, button,
NULL);
1177 XmStringFree(st1);
1178 XtAddCallback(button, XmNactivateCallback, destroyOutDialogCB,
1179 XtParent(form));
1180
1181 ac =
0;
1182 XtSetArg(al[ac], XmNrows, rows); ac++;
1183 XtSetArg(al[ac], XmNcolumns, cols); ac++;
1184 XtSetArg(al[ac], XmNresizeHeight, False); ac++;
1185 XtSetArg(al[ac], XmNtraversalOn, True); ac++;
1186 XtSetArg(al[ac], XmNwordWrap, True); ac++;
1187 XtSetArg(al[ac], XmNscrollHorizontal, False); ac++;
1188 XtSetArg(al[ac], XmNscrollVertical, hasScrollBar); ac++;
1189 XtSetArg(al[ac], XmNhighlightThickness,
2); ac++;
1190 XtSetArg(al[ac], XmNspacing,
0); ac++;
1191 XtSetArg(al[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++;
1192 XtSetArg(al[ac], XmNeditable, False); ac++;
1193 XtSetArg(al[ac], XmNvalue, text); ac++;
1194 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1195 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1196 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1197 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1198 XtSetArg(al[ac], XmNbottomWidget, button); ac++;
1199 textW = XmCreateScrolledText(form,
"outText", al, ac);
1200 AddMouseWheelSupport(textW);
1201 MakeSingleLineTextW(textW);
1202 XtManageChild(textW);
1203
1204 XtVaSetValues(XtParent(form), XmNtitle,
"Output from Command",
NULL);
1205 ManageDialogCenteredOnPointer(form);
1206
1207 #ifdef LESSTIF_VERSION
1208
1209
1210
1211
1212
1213 XmProcessTraversal(button, XmTRAVERSE_CURRENT);
1214 #else
1215 XmProcessTraversal(textW, XmTRAVERSE_CURRENT);
1216 #endif
1217 }
1218
1219
1220
1221
1222 static void destroyOutDialogCB(Widget w, XtPointer callback, XtPointer closure)
1223 {
1224 XtDestroyWidget((Widget)callback);
1225 }
1226
1227
1228
1229
1230
1231 static void measureText(
char *text,
int wrapWidth,
int *rows,
int *cols,
1232 int *wrapped)
1233 {
1234 int maxCols =
0, line =
1, col =
0, wrapCol;
1235 char *c;
1236
1237 *wrapped =
0;
1238 for (c=text; *c!=
'\0'; c++) {
1239 if (*c==
'\n') {
1240 line++;
1241 col =
0;
1242 continue;
1243 }
1244
1245 if (*c ==
'\t') {
1246 col +=
8 - (col %
8);
1247 wrapCol =
0;
1248 }
else if (*c ==
' ') {
1249 col++;
1250 wrapCol =
0;
1251 }
else {
1252 col++;
1253 wrapCol =
1;
1254 }
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270 if (col > wrapWidth) {
1271 line++;
1272 *wrapped =
1;
1273 col = wrapCol;
1274 }
else if (col > maxCols) {
1275 maxCols = col;
1276 }
1277 }
1278 *rows = line;
1279 *cols = maxCols;
1280 }
1281
1282
1283
1284
1285
1286
1287 static void truncateString(
char *string,
int length)
1288 {
1289 if ((
int)strlen(string) > length)
1290 memcpy(&string[length-
3],
"...",
4);
1291 }
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301 static int shellSubstituter(
char *outStr,
const char *inStr,
const char *fileStr,
1302 const char *lineStr,
int outLen,
int predictOnly)
1303 {
1304 const char *inChar;
1305 char *outChar =
NULL;
1306 int outWritten =
0;
1307 int fileLen, lineLen;
1308
1309 inChar = inStr;
1310 if (!predictOnly) {
1311 outChar = outStr;
1312 }
1313 fileLen = strlen(fileStr);
1314 lineLen = strlen(lineStr);
1315
1316 while (*inChar !=
'\0') {
1317
1318 if (!predictOnly && outWritten >= outLen) {
1319 return(-
1);
1320 }
1321
1322 if (*inChar ==
'%') {
1323 if (*(inChar +
1) ==
'%') {
1324 inChar +=
2;
1325 if (!predictOnly) {
1326 *outChar++ =
'%';
1327 }
1328 outWritten++;
1329 }
else {
1330 if (!predictOnly) {
1331 if (outWritten + fileLen >= outLen) {
1332 return(-
1);
1333 }
1334 strncpy(outChar, fileStr, fileLen);
1335 outChar += fileLen;
1336 }
1337 outWritten += fileLen;
1338 inChar++;
1339 }
1340 }
else if (*inChar ==
'#') {
1341 if (*(inChar +
1) ==
'#') {
1342 inChar +=
2;
1343 if (!predictOnly) {
1344 *outChar++ =
'#';
1345 }
1346 outWritten++;
1347 }
else {
1348 if (!predictOnly) {
1349 if (outWritten + lineLen >= outLen) {
1350 return(-
1);
1351 }
1352 strncpy(outChar, lineStr, lineLen);
1353 outChar += lineLen;
1354 }
1355 outWritten += lineLen;
1356 inChar++;
1357 }
1358 }
else {
1359 if (!predictOnly) {
1360 *outChar++ = *inChar;
1361 }
1362 inChar++;
1363 outWritten++;
1364 }
1365 }
1366
1367 if (!predictOnly) {
1368 if (outWritten >= outLen) {
1369 return(-
1);
1370 }
1371 *outChar =
'\0';
1372 }
1373 ++outWritten;
1374 return(outWritten);
1375 }
1376
1377 static char *shellCommandSubstitutes(
const char *inStr,
const char *fileStr,
1378 const char *lineStr)
1379 {
1380 int cmdLen;
1381 char *subsCmdStr =
NULL;
1382
1383 cmdLen = shellSubstituter(
NULL, inStr, fileStr, lineStr,
0,
1);
1384 if (cmdLen >=
0) {
1385 subsCmdStr = (
char*)NEditMalloc(cmdLen);
1386 if (subsCmdStr) {
1387 cmdLen = shellSubstituter(subsCmdStr, inStr, fileStr, lineStr, cmdLen,
0);
1388 if (cmdLen <
0) {
1389 free(subsCmdStr);
1390 subsCmdStr =
NULL;
1391 }
1392 }
1393 }
1394 return(subsCmdStr);
1395 }
1396