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.
13 static const char sccsid[] = "@(#)ex_filter.c 10.34 (Berkeley) 10/23/96";
16 #include <sys/types.h>
17 #include <sys/queue.h>
19 #include <bitstring.h>
28 #include "../common/common.h"
30 static int filter_ldisplay __P((SCR *, FILE *));
34 * Run a range of lines through a filter utility and optionally
35 * replace the original text with the stdout/stderr output of
38 * PUBLIC: int ex_filter __P((SCR *,
39 * PUBLIC: EXCMD *, MARK *, MARK *, MARK *, char *, enum filtertype));
42 ex_filter(sp, cmdp, fm, tm, rp, cmd, ftype)
47 enum filtertype ftype;
50 pid_t parent_writer_pid, utility_pid;
52 int input[2], output[2], rval;
57 /* Set return cursor position, which is never less than line 1. */
62 /* We're going to need a shell. */
63 if (opts_empty(sp, O_SHELL, 0))
67 * There are three different processes running through this code.
68 * They are the utility, the parent-writer and the parent-reader.
69 * The parent-writer is the process that writes from the file to
70 * the utility, the parent reader is the process that reads from
73 * Input and output are named from the utility's point of view.
74 * The utility reads from input[0] and the parent(s) write to
75 * input[1]. The parent(s) read from output[0] and the utility
76 * writes to output[1].
79 * Historically, in the FILTER_READ case, the utility reads from
80 * the terminal (e.g. :r! cat works). Otherwise open up utility
84 input[0] = input[1] = output[0] = output[1] = -1;
85 if (ftype != FILTER_READ && pipe(input) < 0) {
86 msgq(sp, M_SYSERR, "pipe");
90 /* Open up utility output pipe. */
91 if (pipe(output) < 0) {
92 msgq(sp, M_SYSERR, "pipe");
95 if ((ofp = fdopen(output[0], "r")) == NULL) {
96 msgq(sp, M_SYSERR, "fdopen");
100 /* Fork off the utility process. */
101 switch (utility_pid = vfork()) {
102 case -1: /* Error. */
103 msgq(sp, M_SYSERR, "vfork");
104 err: if (input[0] != -1)
105 (void)close(input[0]);
107 (void)close(input[1]);
110 else if (output[0] != -1)
111 (void)close(output[0]);
113 (void)close(output[1]);
115 case 0: /* Utility. */
117 * Redirect stdin from the read end of the input pipe, and
118 * redirect stdout/stderr to the write end of the output pipe.
121 * Historically, ex only directed stdout into the input pipe,
122 * letting stderr come out on the terminal as usual. Vi did
123 * not, directing both stdout and stderr into the input pipe.
124 * We match that practice in both ex and vi for consistency.
127 (void)dup2(input[0], STDIN_FILENO);
128 (void)dup2(output[1], STDOUT_FILENO);
129 (void)dup2(output[1], STDERR_FILENO);
131 /* Close the utility's file descriptors. */
133 (void)close(input[0]);
135 (void)close(input[1]);
136 (void)close(output[0]);
137 (void)close(output[1]);
139 if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
140 name = O_STR(sp, O_SHELL);
144 execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL);
145 msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
148 default: /* Parent-reader, parent-writer. */
149 /* Close the pipe ends neither parent will use. */
151 (void)close(input[0]);
152 (void)close(output[1]);
157 * FILTER_RBANG, FILTER_READ:
159 * Reading is the simple case -- we don't need a parent writer,
160 * so the parent reads the output from the read end of the output
161 * pipe until it finishes, then waits for the child. Ex_readfp
162 * appends to the MARK, and closes ofp.
164 * For FILTER_RBANG, there is nothing to write to the utility.
165 * Make sure it doesn't wait forever by closing its standard
169 * Set the return cursor to the last line read in for FILTER_READ.
170 * Historically, this behaves differently from ":r file" command,
171 * which leaves the cursor at the first line read in. Check to
172 * make sure that it's not past EOF because we were reading into an
175 if (ftype == FILTER_RBANG || ftype == FILTER_READ) {
176 if (ftype == FILTER_RBANG)
177 (void)close(input[1]);
179 if (ex_readfp(sp, "filter", ofp, fm, &nread, 1))
181 sp->rptlines[L_ADDED] += nread;
182 if (ftype == FILTER_READ)
191 * FILTER_BANG, FILTER_WRITE
193 * Here we need both a reader and a writer. Temporary files are
194 * expensive and we'd like to avoid disk I/O. Using pipes has the
195 * obvious starvation conditions. It's done as follows:
203 * read lines into the file
206 * read and display lines
210 * We get away without locking the underlying database because we know
211 * that none of the records that we're reading will be modified until
212 * after we've read them. This depends on the fact that the current
213 * B+tree implementation doesn't balance pages or similar things when
214 * it inserts new records. When the DB code has locking, we should
215 * treat vi as if it were multiple applications sharing a database, and
216 * do the required locking. If necessary a work-around would be to do
217 * explicit locking in the line.c:db_get() code, based on the flag set
220 F_SET(sp->ep, F_MULTILOCK);
221 switch (parent_writer_pid = fork()) {
222 case -1: /* Error. */
223 msgq(sp, M_SYSERR, "fork");
224 (void)close(input[1]);
225 (void)close(output[0]);
228 case 0: /* Parent-writer. */
230 * Write the selected lines to the write end of the input
231 * pipe. This instance of ifp is closed by ex_writefp.
233 (void)close(output[0]);
234 if ((ifp = fdopen(input[1], "w")) == NULL)
236 _exit(ex_writefp(sp, "filter", ifp, fm, tm, NULL, NULL, 1));
239 default: /* Parent-reader. */
240 (void)close(input[1]);
241 if (ftype == FILTER_WRITE) {
243 * Read the output from the read end of the output
244 * pipe and display it. Filter_ldisplay closes ofp.
246 if (filter_ldisplay(sp, ofp))
250 * Read the output from the read end of the output
251 * pipe. Ex_readfp appends to the MARK and closes
254 if (ex_readfp(sp, "filter", ofp, tm, &nread, 1))
256 sp->rptlines[L_ADDED] += nread;
259 /* Wait for the parent-writer. */
261 (long)parent_writer_pid, "parent-writer", 0, 1))
264 /* Delete any lines written to the utility. */
265 if (rval == 0 && ftype == FILTER_BANG &&
266 (cut(sp, NULL, fm, tm, CUT_LINEMODE) ||
267 del(sp, fm, tm, 1))) {
273 * If the filter had no output, we may have just deleted
274 * the cursor. Don't do any real error correction, we'll
275 * try and recover later.
277 if (rp->lno > 1 && !db_exist(sp, rp->lno))
281 F_CLR(sp->ep, F_MULTILOCK);
285 * Ignore errors on vi file reads, to make reads prettier. It's
286 * completely inconsistent, and historic practice.
288 uwait: return (proc_wait(sp, (long)utility_pid, cmd,
289 ftype == FILTER_READ && F_ISSET(sp, SC_VI) ? 1 : 0, 0) || rval);
294 * Display output from a utility.
297 * Historically, the characters were passed unmodified to the terminal.
298 * We use the ex print routines to make sure they're printable.
301 filter_ldisplay(sp, fp)
309 for (exp = EXP(sp); !ex_getline(sp, fp, &len) && !INTERRUPTED(sp);)
310 if (ex_ldisplay(sp, exp->ibp, len, 0, 0))
313 msgq(sp, M_SYSERR, "filter read");