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
[] = "$Id: ex_filter.c,v 10.38 2000/11/26 11:00:20 skimo Exp $ (Berkeley) $Date: 2000/11/26 11:00:20 $";
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 *));
31 static CHAR_T filter
[] = { 'f', 'i', 'l', 't', 'e', 'r', 0 };
35 * Run a range of lines through a filter utility and optionally
36 * replace the original text with the stdout/stderr output of
39 * PUBLIC: int ex_filter __P((SCR *,
40 * PUBLIC: EXCMD *, MARK *, MARK *, MARK *, CHAR_T *, enum filtertype));
43 ex_filter(sp
, cmdp
, fm
, tm
, rp
, cmd
, ftype
)
48 enum filtertype ftype
;
51 pid_t parent_writer_pid
, utility_pid
;
53 int input
[2], output
[2], rval
;
60 /* Set return cursor position, which is never less than line 1. */
65 /* We're going to need a shell. */
66 if (opts_empty(sp
, O_SHELL
, 0))
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
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].
82 * Historically, in the FILTER_READ case, the utility reads from
83 * the terminal (e.g. :r! cat works). Otherwise open up utility
87 input
[0] = input
[1] = output
[0] = output
[1] = -1;
88 if (ftype
!= FILTER_READ
&& pipe(input
) < 0) {
89 msgq(sp
, M_SYSERR
, "pipe");
93 /* Open up utility output pipe. */
94 if (pipe(output
) < 0) {
95 msgq(sp
, M_SYSERR
, "pipe");
98 if ((ofp
= fdopen(output
[0], "r")) == NULL
) {
99 msgq(sp
, M_SYSERR
, "fdopen");
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]);
110 (void)close(input
[1]);
113 else if (output
[0] != -1)
114 (void)close(output
[0]);
116 (void)close(output
[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.
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.
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. */
136 (void)close(input
[0]);
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
);
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");
152 default: /* Parent-reader, parent-writer. */
153 /* Close the pipe ends neither parent will use. */
155 (void)close(input
[0]);
156 (void)close(output
[1]);
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
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
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))
185 sp
->rptlines
[L_ADDED
] += nread
;
186 if (ftype
== FILTER_READ
)
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:
207 * read lines into the file
210 * read and display lines
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
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]);
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
)
240 _exit(ex_writefp(sp
, "filter", ifp
, fm
, tm
, NULL
, NULL
, 1));
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
))
254 * Read the output from the read end of the output
255 * pipe. Ex_readfp appends to the MARK and closes
258 if (ex_readfp(sp
, filter
, ofp
, tm
, &nread
, 1))
260 sp
->rptlines
[L_ADDED
] += nread
;
263 /* Wait for the parent-writer. */
265 (long)parent_writer_pid
, "parent-writer", 0, 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))) {
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
))
285 F_CLR(sp
->ep
, F_MULTILOCK
);
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
);
299 * Display output from a utility.
302 * Historically, the characters were passed unmodified to the terminal.
303 * We use the ex print routines to make sure they're printable.
306 filter_ldisplay(sp
, fp
)
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))
322 msgq(sp
, M_SYSERR
, "filter read");