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