|
|
1.1 root 1: /* system.c -- UNIX version */
2:
3: /* Author:
4: * Steve Kirkendall
5: * 14407 SW Teal Blvd. #C
6: * Beaverton, OR 97005
7: * [email protected]
8: */
9:
10:
11: /* This file contains a new version of the system() function and related stuff.
12: *
13: * Entry points are:
14: * system(cmd) - run a single shell command
15: * wildcard(names) - expand wildcard characters in filanames
16: * filter(m,n,cmd,back) - run text lines through a filter program
17: *
18: * This is probably the single least portable file in the program. The code
19: * shown here should work correctly if it links at all; it will work on UNIX
20: * and any O.S./Compiler combination which adheres to UNIX forking conventions.
21: */
22:
23: #include "config.h"
24: #include "vi.h"
25: extern char **environ;
26:
27: #if ANY_UNIX
28:
29: /* This is a new version of the system() function. The only difference
30: * between this one and the library one is: this one uses the o_shell option.
31: */
32: int system(cmd)
33: #ifdef __STDC__
34: const
35: #endif
36: char *cmd; /* a command to run */
37: {
38: int pid; /* process ID of child */
39: int died;
40: int status; /* exit status of the command */
41:
42:
43: signal(SIGINT, SIG_IGN);
44: pid = fork();
45: switch (pid)
46: {
47: case -1: /* error */
48: msg("fork() failed");
49: status = -1;
50: break;
51:
52: case 0: /* child */
53: /* for the child, close all files except stdin/out/err */
54: for (status = 3; status < 60 && (close(status), errno != EINVAL); status++)
55: {
56: }
57:
58: signal(SIGINT, SIG_DFL);
59: if (cmd == o_shell)
60: {
61: execle(o_shell, o_shell, (char *)0, environ);
62: }
63: else
64: {
65: execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
66: }
67: msg("execle(\"%s\", ...) failed", o_shell);
68: exit(1); /* if we get here, the exec failed */
69:
70: default: /* parent */
71: do
72: {
73: died = wait(&status);
74: } while (died >= 0 && died != pid);
75: if (died < 0)
76: {
77: status = -1;
78: }
79: #if __GNUC__
80: signal(SIGINT, (void (*)()) trapint);
81: #else
82: signal(SIGINT, trapint);
83: #endif
84: }
85:
86: return status;
87: }
88:
89: /* This private function opens a pipe from a filter. It is similar to the
90: * system() function above, and to popen(cmd, "r").
91: */
92: int rpipe(cmd, in)
93: char *cmd; /* the filter command to use */
94: int in; /* the fd to use for stdin */
95: {
96: int r0w1[2];/* the pipe fd's */
97:
98: /* make the pipe */
99: if (pipe(r0w1) < 0)
100: {
101: return -1; /* pipe failed */
102: }
103:
104: /* The parent process (elvis) ignores signals while the filter runs.
105: * The child (the filter program) will reset this, so that it can
106: * catch the signal.
107: */
108: signal(SIGINT, SIG_IGN);
109:
110: switch (fork())
111: {
112: case -1: /* error */
113: return -1;
114:
115: case 0: /* child */
116: /* close the "read" end of the pipe */
117: close(r0w1[0]);
118:
119: /* redirect stdout to go to the "write" end of the pipe */
120: close(1);
121: dup(r0w1[1]);
122: close(2);
123: dup(r0w1[1]);
124: close(r0w1[1]);
125:
126: /* redirect stdin */
127: if (in != 0)
128: {
129: close(0);
130: dup(in);
131: close(in);
132: }
133:
134: /* the filter should accept SIGINT signals */
135: signal(SIGINT, SIG_DFL);
136:
137: /* exec the shell to run the command */
138: execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
139: exit(1); /* if we get here, exec failed */
140:
141: default: /* parent */
142: /* close the "write" end of the pipe */
143: close(r0w1[1]);
144:
145: return r0w1[0];
146: }
147: }
148:
149: #endif /* non-DOS */
150:
151: #if OSK
152:
153: /* This private function opens a pipe from a filter. It is similar to the
154: * system() function above, and to popen(cmd, "r").
155: */
156: int rpipe(cmd, in)
157: char *cmd; /* the filter command to use */
158: int in; /* the fd to use for stdin */
159: {
160: return osk_popen(cmd, "r", in, 0);
161: }
162: #endif
163:
164: #if ANY_UNIX || OSK
165:
166: /* This function closes the pipe opened by rpipe(), and returns 0 for success */
167: int rpclose(fd)
168: int fd;
169: {
170: int status;
171:
172: close(fd);
173: wait(&status);
174: #if __GNUC__
175: signal(SIGINT, (void (*)()) trapint);
176: #else
177: signal(SIGINT, trapint);
178: #endif
179: return status;
180: }
181:
182: #endif /* non-DOS */
183:
184: /* This function expands wildcards in a filename or filenames. It does this
185: * by running the "echo" command on the filenames via the shell; it is assumed
186: * that the shell will expand the names for you. If for any reason it can't
187: * run echo, then it returns the names unmodified.
188: */
189:
190: #if MSDOS || TOS
191: #define PROG "wildcard "
192: #define PROGLEN 9
193: #include <string.h>
194: #else
195: #define PROG "echo "
196: #define PROGLEN 5
197: #endif
198:
199: #if !AMIGA
200: char *wildcard(names)
201: char *names;
202: {
203:
204: # if VMS
205: /*
206: We could use expand() [vmswild.c], but what's the point on VMS?
207: Anyway, echo is the wrong thing to do, it takes too long to build
208: a subprocess on VMS and any "echo" program would have to be supplied
209: by elvis. More importantly, many VMS utilities expand names
210: themselves (the shell doesn't do any expansion) so the concept is
211: non-native. jdc
212: */
213: return names;
214: # else
215:
216: int i, j, fd;
217: REG char *s, *d;
218:
219:
220: /* build the echo command */
221: if (names != tmpblk.c)
222: {
223: /* the names aren't in tmpblk.c, so we can do it the easy way */
224: strcpy(tmpblk.c, PROG);
225: strcat(tmpblk.c, names);
226: }
227: else
228: {
229: /* the names are already in tmpblk.c, so shift them to make
230: * room for the word "echo "
231: */
232: for (s = names + strlen(names) + 1, d = s + PROGLEN; s > names; )
233: {
234: *--d = *--s;
235: }
236: strncpy(names, PROG, PROGLEN);
237: }
238:
239: /* run the command & read the resulting names */
240: fd = rpipe(tmpblk.c, 0);
241: if (fd < 0) return names;
242: i = 0;
243: do
244: {
245: j = tread(fd, tmpblk.c + i, BLKSIZE - i);
246: i += j;
247: } while (j > 0);
248:
249: /* successful? */
250: if (rpclose(fd) == 0 && j == 0 && i < BLKSIZE && i > 0)
251: {
252: tmpblk.c[i-1] = '\0'; /* "i-1" so we clip off the newline */
253: return tmpblk.c;
254: }
255: else
256: {
257: return names;
258: }
259: # endif
260: }
261: #endif
262:
263: /* This function runs a range of lines through a filter program, and replaces
264: * the original text with the filtered version. As a special case, if "to"
265: * is MARK_UNSET, then it runs the filter program with stdin coming from
266: * /dev/null, and inserts any output lines.
267: */
268: int filter(from, to, cmd, back)
269: MARK from, to; /* the range of lines to filter */
270: char *cmd; /* the filter command */
271: int back; /* boolean: will we read lines back? */
272: {
273: int scratch; /* fd of the scratch file */
274: int fd; /* fd of the pipe from the filter */
275: char scrout[50]; /* name of the scratch out file */
276: MARK new; /* place where new text should go */
277: long sent, rcvd; /* number of lines sent/received */
278: int i, j;
279:
280: /* write the lines (if specified) to a temp file */
281: if (to)
282: {
283: /* we have lines */
284: #if MSDOS || TOS
285: strcpy(scrout, o_directory);
286: if ((i=strlen(scrout)) && !strchr("\\/:", scrout[i-1]))
287: scrout[i++]=SLASH;
288: strcpy(scrout+i, SCRATCHOUT+3);
289: #else
290: sprintf(scrout, SCRATCHOUT, o_directory);
291: #endif
292: mktemp(scrout);
293: cmd_write(from, to, CMD_BANG, FALSE, scrout);
294: sent = markline(to) - markline(from) + 1L;
295:
296: /* use those lines as stdin */
297: scratch = open(scrout, O_RDONLY);
298: if (scratch < 0)
299: {
300: unlink(scrout);
301: return -1;
302: }
303: }
304: else
305: {
306: scratch = 0;
307: sent = 0L;
308: }
309:
310: /* start the filter program */
311: #if VMS
312: /*
313: VMS doesn't know a thing about file descriptor 0. The rpipe
314: concept is non-portable. Hence we need a file name argument.
315: */
316: fd = rpipe(cmd, scratch, scrout);
317: #else
318: fd = rpipe(cmd, scratch);
319: #endif
320: if (fd < 0)
321: {
322: if (to)
323: {
324: close(scratch);
325: unlink(scrout);
326: }
327: return -1;
328: }
329:
330: if (back)
331: {
332: ChangeText
333: {
334: /* adjust MARKs for whole lines, and set "new" */
335: from &= ~(BLKSIZE - 1);
336: if (to)
337: {
338: to &= ~(BLKSIZE - 1);
339: to += BLKSIZE;
340: new = to;
341: }
342: else
343: {
344: new = from + BLKSIZE;
345: }
346:
347: #if VMS
348: /* Reading from a VMS mailbox (pipe) is record oriented... */
349: # define tread vms_pread
350: #endif
351:
352: /* repeatedly read in new text and add it */
353: rcvd = 0L;
354: while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
355: {
356: tmpblk.c[i] = '\0';
357: add(new, tmpblk.c);
358: #if VMS
359: /* What! An advantage to record oriented reads? */
360: new += (i - 1);
361: new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
362: rcvd++;
363: #else
364: for (i = 0; tmpblk.c[i]; i++)
365: {
366: if (tmpblk.c[i] == '\n')
367: {
368: new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
369: rcvd++;
370: }
371: else
372: {
373: new++;
374: }
375: }
376: #endif
377: }
378: }
379:
380: /* delete old text, if any */
381: if (to)
382: {
383: cut(from, to);
384: delete(from, to);
385: }
386: }
387: else
388: {
389: /* read the command's output, and copy it to the screen */
390: while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
391: {
392: for (j = 0; j < i; j++)
393: {
394: addch(tmpblk.c[j]);
395: }
396: }
397: rcvd = 0;
398: }
399:
400: /* Reporting... */
401: if (sent >= *o_report || rcvd >= *o_report)
402: {
403: if (sent > 0L && rcvd > 0L)
404: {
405: msg("%ld lines out, %ld lines back", sent, rcvd);
406: }
407: else if (sent > 0)
408: {
409: msg("%ld lines written to filter", sent);
410: }
411: else
412: {
413: msg("%ld lines read from filter", rcvd);
414: }
415: }
416: rptlines = 0L;
417:
418: /* cleanup */
419: rpclose(fd);
420: if (to)
421: {
422: close(scratch);
423: unlink(scrout);
424: }
425: return 0;
426: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.