Initial revision
[nvi.git] / ex / ex_filter.c
blob2bb0e252067cf65c018e979badeb945ab0798965
1 /* system.c -- UNIX version */
3 /* Author:
4 * Steve Kirkendall
5 * 14407 SW Teal Blvd. #C
6 * Beaverton, OR 97005
7 * kirkenda@cs.pdx.edu
8 */
11 /* This file contains a new version of the system() function and related stuff.
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) - run text lines through a filter program
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.
23 #include "config.h"
24 #include "vi.h"
25 #include <signal.h>
26 extern char **environ;
28 #if ANY_UNIX
30 /* This is a new version of the system() function. The only difference
31 * between this one and the library one is: this one uses the o_shell option.
33 int system(cmd)
34 char *cmd; /* a command to run */
36 int pid; /* process ID of child */
37 int died;
38 int status; /* exit status of the command */
41 signal(SIGINT, SIG_IGN);
42 pid = fork();
43 switch (pid)
45 case -1: /* error */
46 msg("fork() failed");
47 status = -1;
48 break;
50 case 0: /* child */
51 /* for the child, close all files except stdin/out/err */
52 for (status = 3; status < 60 && (close(status), errno != EINVAL); status++)
56 signal(SIGINT, SIG_DFL);
57 if (cmd == o_shell)
59 execle(o_shell, o_shell, (char *)0, environ);
61 else
63 execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
65 msg("execle(\"%s\", ...) failed", o_shell);
66 exit(1); /* if we get here, the exec failed */
68 default: /* parent */
71 died = wait(&status);
72 } while (died >= 0 && died != pid);
73 if (died < 0)
75 status = -1;
77 #if __GNUC__
78 signal(SIGINT, (void (*)()) trapint);
79 #else
80 signal(SIGINT, trapint);
81 #endif
84 return status;
87 /* This private function opens a pipe from a filter. It is similar to the
88 * system() function above, and to popen(cmd, "r").
90 int rpipe(cmd, in)
91 char *cmd; /* the filter command to use */
92 int in; /* the fd to use for stdin */
94 int r0w1[2];/* the pipe fd's */
96 /* make the pipe */
97 if (pipe(r0w1) < 0)
99 return -1; /* pipe failed */
102 /* The parent process (elvis) ignores signals while the filter runs.
103 * The child (the filter program) will reset this, so that it can
104 * catch the signal.
106 signal(SIGINT, SIG_IGN);
108 switch (fork())
110 case -1: /* error */
111 return -1;
113 case 0: /* child */
114 /* close the "read" end of the pipe */
115 close(r0w1[0]);
117 /* redirect stdout to go to the "write" end of the pipe */
118 close(1);
119 dup(r0w1[1]);
120 close(2);
121 dup(r0w1[1]);
122 close(r0w1[1]);
124 /* redirect stdin */
125 if (in != 0)
127 close(0);
128 dup(in);
129 close(in);
132 /* the filter should accept SIGINT signals */
133 signal(SIGINT, SIG_DFL);
135 /* exec the shell to run the command */
136 execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
137 exit(1); /* if we get here, exec failed */
139 default: /* parent */
140 /* close the "write" end of the pipe */
141 close(r0w1[1]);
143 return r0w1[0];
147 #endif /* non-DOS */
149 #if OSK
151 /* This private function opens a pipe from a filter. It is similar to the
152 * system() function above, and to popen(cmd, "r").
154 int rpipe(cmd, in)
155 char *cmd; /* the filter command to use */
156 int in; /* the fd to use for stdin */
158 return osk_popen(cmd, "r", in, 0);
160 #endif
162 #if ANY_UNIX || OSK
164 /* This function closes the pipe opened by rpipe(), and returns 0 for success */
165 int rpclose(fd)
166 int fd;
168 int status;
170 close(fd);
171 wait(&status);
172 #if __GNUC__
173 signal(SIGINT, (void (*)()) trapint);
174 #else
175 signal(SIGINT, trapint);
176 #endif
177 return status;
180 #endif /* non-DOS */
182 /* This function expands wildcards in a filename or filenames. It does this
183 * by running the "echo" command on the filenames via the shell; it is assumed
184 * that the shell will expand the names for you. If for any reason it can't
185 * run echo, then it returns the names unmodified.
188 #if MSDOS || TOS
189 #define PROG "wildcard "
190 #define PROGLEN 9
191 #include <string.h>
192 #else
193 #define PROG "echo "
194 #define PROGLEN 5
195 #endif
197 char *wildcard(names)
198 char *names;
201 # if VMS
203 We could use expand() [vmswild.c], but what's the point on VMS?
204 Anyway, echo is the wrong thing to do, it takes too long to build
205 a subprocess on VMS and any "echo" program would have to be supplied
206 by elvis. More importantly, many VMS utilities expand names
207 themselves (the shell doesn't do any expansion) so the concept is
208 non-native. jdc
210 return names;
211 #else
213 int i, j, fd;
214 REG char *s, *d;
217 /* build the echo command */
218 if (names != tmpblk.c)
220 /* the names aren't in tmpblk.c, so we can do it the easy way */
221 strcpy(tmpblk.c, PROG);
222 strcat(tmpblk.c, names);
224 else
226 /* the names are already in tmpblk.c, so shift them to make
227 * room for the word "echo "
229 for (s = names + strlen(names) + 1, d = s + PROGLEN; s > names; )
231 *--d = *--s;
233 strncpy(names, PROG, PROGLEN);
236 /* run the command & read the resulting names */
237 fd = rpipe(tmpblk.c, 0);
238 if (fd < 0) return names;
239 i = 0;
242 j = tread(fd, tmpblk.c + i, BLKSIZE - i);
243 i += j;
244 } while (j > 0);
246 /* successful? */
247 if (rpclose(fd) == 0 && j == 0 && i < BLKSIZE && i > 0)
249 tmpblk.c[i-1] = '\0'; /* "i-1" so we clip off the newline */
250 return tmpblk.c;
252 else
254 return names;
256 #endif
259 /* This function runs a range of lines through a filter program, and replaces
260 * the original text with the filtered version. As a special case, if "to"
261 * is MARK_UNSET, then it runs the filter program with stdin coming from
262 * /dev/null, and inserts any output lines.
264 int filter(from, to, cmd)
265 MARK from, to; /* the range of lines to filter */
266 char *cmd; /* the filter command */
268 int scratch; /* fd of the scratch file */
269 int fd; /* fd of the pipe from the filter */
270 char scrout[50]; /* name of the scratch out file */
271 MARK new; /* place where new text should go */
272 int i;
274 /* write the lines (if specified) to a temp file */
275 if (to)
277 /* we have lines */
278 #if MSDOS || TOS
279 strcpy(scrout, o_directory);
280 if ((i=strlen(scrout)) && !strchr("\\/:", scrout[i-1]))
281 scrout[i++]=SLASH;
282 strcpy(scrout+i, SCRATCHOUT+3);
283 #else
284 sprintf(scrout, SCRATCHOUT, o_directory);
285 #endif
286 mktemp(scrout);
287 cmd_write(from, to, CMD_BANG, 0, scrout);
289 /* use those lines as stdin */
290 scratch = open(scrout, O_RDONLY);
291 if (scratch < 0)
293 unlink(scrout);
294 return -1;
297 else
299 scratch = 0;
302 /* start the filter program */
303 #if VMS
305 VMS doesn't know a thing about file descriptor 0. The rpipe
306 concept is non-portable. Hence we need a file name argument.
308 fd = rpipe(cmd, scratch, scrout);
309 #else
310 fd = rpipe(cmd, scratch);
311 #endif
312 if (fd < 0)
314 if (to)
316 close(scratch);
317 unlink(scrout);
319 return -1;
322 ChangeText
324 /* adjust MARKs for whole lines, and set "new" */
325 from &= ~(BLKSIZE - 1);
326 if (to)
328 to &= ~(BLKSIZE - 1);
329 to += BLKSIZE;
330 new = to;
332 else
334 new = from + BLKSIZE;
337 #if VMS
338 /* Reading from a VMS mailbox (pipe) is record oriented... */
339 # define tread vms_pread
340 #endif
342 /* repeatedly read in new text and add it */
343 while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
345 tmpblk.c[i] = '\0';
346 add(new, tmpblk.c);
347 #if VMS
348 /* What! An advantage to record oriented reads? */
349 new += (i - 1);
350 new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
351 #else
352 for (i = 0; tmpblk.c[i]; i++)
354 if (tmpblk.c[i] == '\n')
356 new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
358 else
360 new++;
363 #endif
367 /* delete old text, if any */
368 if (to)
370 delete(from, to);
373 /* Reporting... */
374 rptlabel = "more";
375 if (rptlines < 0)
377 rptlines = -rptlines;
378 rptlabel = "less";
381 /* cleanup */
382 rpclose(fd);
383 if (to)
385 close(scratch);
386 unlink(scrout);
388 return 0;