AM_INIT_AUTOMAKE seems to include AC_ARG_PROGRAM; remove explicit call
[nvi.git] / ex / ex_filter.c
blob96fc2cd6cc10ef5a0c6907a42a3334fe5e081072
1 /*-
2 * Copyright (c) 1991, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1991, 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
7 * See the LICENSE file for redistribution information.
8 */
10 #include "config.h"
12 #ifndef lint
13 static const char sccsid[] = "$Id: ex_filter.c,v 10.38 2000/11/26 11:00:20 skimo Exp $ (Berkeley) $Date: 2000/11/26 11:00:20 $";
14 #endif /* not lint */
16 #include <sys/types.h>
17 #include <sys/queue.h>
19 #include <bitstring.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
28 #include "../common/common.h"
30 static int filter_ldisplay __P((SCR *, FILE *));
31 static CHAR_T filter[] = { 'f', 'i', 'l', 't', 'e', 'r', 0 };
34 * ex_filter --
35 * Run a range of lines through a filter utility and optionally
36 * replace the original text with the stdout/stderr output of
37 * the utility.
39 * PUBLIC: int ex_filter __P((SCR *,
40 * PUBLIC: EXCMD *, MARK *, MARK *, MARK *, CHAR_T *, enum filtertype));
42 int
43 ex_filter(sp, cmdp, fm, tm, rp, cmd, ftype)
44 SCR *sp;
45 EXCMD *cmdp;
46 MARK *fm, *tm, *rp;
47 CHAR_T *cmd;
48 enum filtertype ftype;
50 FILE *ifp, *ofp;
51 pid_t parent_writer_pid, utility_pid;
52 db_recno_t nread;
53 int input[2], output[2], rval;
54 char *name;
55 char *np;
56 size_t nlen;
58 rval = 0;
60 /* Set return cursor position, which is never less than line 1. */
61 *rp = *fm;
62 if (rp->lno == 0)
63 rp->lno = 1;
65 /* We're going to need a shell. */
66 if (opts_empty(sp, O_SHELL, 0))
67 return (1);
70 * There are three different processes running through this code.
71 * They are the utility, the parent-writer and the parent-reader.
72 * The parent-writer is the process that writes from the file to
73 * the utility, the parent reader is the process that reads from
74 * the utility.
76 * Input and output are named from the utility's point of view.
77 * The utility reads from input[0] and the parent(s) write to
78 * input[1]. The parent(s) read from output[0] and the utility
79 * writes to output[1].
81 * !!!
82 * Historically, in the FILTER_READ case, the utility reads from
83 * the terminal (e.g. :r! cat works). Otherwise open up utility
84 * input pipe.
86 ofp = NULL;
87 input[0] = input[1] = output[0] = output[1] = -1;
88 if (ftype != FILTER_READ && pipe(input) < 0) {
89 msgq(sp, M_SYSERR, "pipe");
90 goto err;
93 /* Open up utility output pipe. */
94 if (pipe(output) < 0) {
95 msgq(sp, M_SYSERR, "pipe");
96 goto err;
98 if ((ofp = fdopen(output[0], "r")) == NULL) {
99 msgq(sp, M_SYSERR, "fdopen");
100 goto err;
103 /* Fork off the utility process. */
104 switch (utility_pid = vfork()) {
105 case -1: /* Error. */
106 msgq(sp, M_SYSERR, "vfork");
107 err: if (input[0] != -1)
108 (void)close(input[0]);
109 if (input[1] != -1)
110 (void)close(input[1]);
111 if (ofp != NULL)
112 (void)fclose(ofp);
113 else if (output[0] != -1)
114 (void)close(output[0]);
115 if (output[1] != -1)
116 (void)close(output[1]);
117 return (1);
118 case 0: /* Utility. */
120 * Redirect stdin from the read end of the input pipe, and
121 * redirect stdout/stderr to the write end of the output pipe.
123 * !!!
124 * Historically, ex only directed stdout into the input pipe,
125 * letting stderr come out on the terminal as usual. Vi did
126 * not, directing both stdout and stderr into the input pipe.
127 * We match that practice in both ex and vi for consistency.
129 if (input[0] != -1)
130 (void)dup2(input[0], STDIN_FILENO);
131 (void)dup2(output[1], STDOUT_FILENO);
132 (void)dup2(output[1], STDERR_FILENO);
134 /* Close the utility's file descriptors. */
135 if (input[0] != -1)
136 (void)close(input[0]);
137 if (input[1] != -1)
138 (void)close(input[1]);
139 (void)close(output[0]);
140 (void)close(output[1]);
142 if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
143 name = O_STR(sp, O_SHELL);
144 else
145 ++name;
147 INT2CHAR(sp, cmd, v_strlen(cmd)+1, np, nlen);
148 execl(O_STR(sp, O_SHELL), name, "-c", np, NULL);
149 msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
150 _exit (127);
151 /* NOTREACHED */
152 default: /* Parent-reader, parent-writer. */
153 /* Close the pipe ends neither parent will use. */
154 if (input[0] != -1)
155 (void)close(input[0]);
156 (void)close(output[1]);
157 break;
161 * FILTER_RBANG, FILTER_READ:
163 * Reading is the simple case -- we don't need a parent writer,
164 * so the parent reads the output from the read end of the output
165 * pipe until it finishes, then waits for the child. Ex_readfp
166 * appends to the MARK, and closes ofp.
168 * For FILTER_RBANG, there is nothing to write to the utility.
169 * Make sure it doesn't wait forever by closing its standard
170 * input.
172 * !!!
173 * Set the return cursor to the last line read in for FILTER_READ.
174 * Historically, this behaves differently from ":r file" command,
175 * which leaves the cursor at the first line read in. Check to
176 * make sure that it's not past EOF because we were reading into an
177 * empty file.
179 if (ftype == FILTER_RBANG || ftype == FILTER_READ) {
180 if (ftype == FILTER_RBANG)
181 (void)close(input[1]);
183 if (ex_readfp(sp, filter, ofp, fm, &nread, 1))
184 rval = 1;
185 sp->rptlines[L_ADDED] += nread;
186 if (ftype == FILTER_READ)
187 if (fm->lno == 0)
188 rp->lno = nread;
189 else
190 rp->lno += nread;
191 goto uwait;
195 * FILTER_BANG, FILTER_WRITE
197 * Here we need both a reader and a writer. Temporary files are
198 * expensive and we'd like to avoid disk I/O. Using pipes has the
199 * obvious starvation conditions. It's done as follows:
201 * fork
202 * child
203 * write lines out
204 * exit
205 * parent
206 * FILTER_BANG:
207 * read lines into the file
208 * delete old lines
209 * FILTER_WRITE
210 * read and display lines
211 * wait for child
213 * XXX
214 * We get away without locking the underlying database because we know
215 * that none of the records that we're reading will be modified until
216 * after we've read them. This depends on the fact that the current
217 * B+tree implementation doesn't balance pages or similar things when
218 * it inserts new records. When the DB code has locking, we should
219 * treat vi as if it were multiple applications sharing a database, and
220 * do the required locking. If necessary a work-around would be to do
221 * explicit locking in the line.c:db_get() code, based on the flag set
222 * here.
224 F_SET(sp->ep, F_MULTILOCK);
225 switch (parent_writer_pid = fork()) {
226 case -1: /* Error. */
227 msgq(sp, M_SYSERR, "fork");
228 (void)close(input[1]);
229 (void)close(output[0]);
230 rval = 1;
231 break;
232 case 0: /* Parent-writer. */
234 * Write the selected lines to the write end of the input
235 * pipe. This instance of ifp is closed by ex_writefp.
237 (void)close(output[0]);
238 if ((ifp = fdopen(input[1], "w")) == NULL)
239 _exit (1);
240 _exit(ex_writefp(sp, "filter", ifp, fm, tm, NULL, NULL, 1));
242 /* NOTREACHED */
243 default: /* Parent-reader. */
244 (void)close(input[1]);
245 if (ftype == FILTER_WRITE) {
247 * Read the output from the read end of the output
248 * pipe and display it. Filter_ldisplay closes ofp.
250 if (filter_ldisplay(sp, ofp))
251 rval = 1;
252 } else {
254 * Read the output from the read end of the output
255 * pipe. Ex_readfp appends to the MARK and closes
256 * ofp.
258 if (ex_readfp(sp, filter, ofp, tm, &nread, 1))
259 rval = 1;
260 sp->rptlines[L_ADDED] += nread;
263 /* Wait for the parent-writer. */
264 if (proc_wait(sp,
265 (long)parent_writer_pid, "parent-writer", 0, 1))
266 rval = 1;
268 /* Delete any lines written to the utility. */
269 if (rval == 0 && ftype == FILTER_BANG &&
270 (cut(sp, NULL, fm, tm, CUT_LINEMODE) ||
271 del(sp, fm, tm, 1))) {
272 rval = 1;
273 break;
277 * If the filter had no output, we may have just deleted
278 * the cursor. Don't do any real error correction, we'll
279 * try and recover later.
281 if (rp->lno > 1 && !db_exist(sp, rp->lno))
282 --rp->lno;
283 break;
285 F_CLR(sp->ep, F_MULTILOCK);
288 * !!!
289 * Ignore errors on vi file reads, to make reads prettier. It's
290 * completely inconsistent, and historic practice.
292 uwait: INT2CHAR(sp, cmd, v_strlen(cmd) + 1, np, nlen);
293 return (proc_wait(sp, (long)utility_pid, np,
294 ftype == FILTER_READ && F_ISSET(sp, SC_VI) ? 1 : 0, 0) || rval);
298 * filter_ldisplay --
299 * Display output from a utility.
301 * !!!
302 * Historically, the characters were passed unmodified to the terminal.
303 * We use the ex print routines to make sure they're printable.
305 static int
306 filter_ldisplay(sp, fp)
307 SCR *sp;
308 FILE *fp;
310 size_t len;
311 char *np;
312 size_t nlen;
314 EX_PRIVATE *exp;
316 for (exp = EXP(sp); !ex_getline(sp, fp, &len) && !INTERRUPTED(sp);) {
317 INT2CHAR(sp, exp->ibp, len, np, nlen);
318 if (ex_ldisplay(sp, np, nlen, 0, 0))
319 break;
321 if (ferror(fp))
322 msgq(sp, M_SYSERR, "filter read");
323 (void)fclose(fp);
324 return (0);