Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / nvi / ex / ex_filter.c
blobec4ac247b5d3dc12e21a6f24841268632211ddad
1 /* $NetBSD: ex_filter.c,v 1.1.1.2 2008/05/18 14:31:15 aymeric Exp $ */
3 /*-
4 * Copyright (c) 1991, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 * Copyright (c) 1991, 1993, 1994, 1995, 1996
7 * Keith Bostic. All rights reserved.
9 * See the LICENSE file for redistribution information.
12 #include "config.h"
14 #ifndef lint
15 static const char sccsid[] = "Id: ex_filter.c,v 10.44 2003/11/05 17:11:54 skimo Exp (Berkeley) Date: 2003/11/05 17:11:54";
16 #endif /* not lint */
18 #include <sys/types.h>
19 #include <sys/queue.h>
21 #include <bitstring.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
30 #include "../common/common.h"
32 static int filter_ldisplay __P((SCR *, FILE *));
34 static pid_t
35 runcmd(SCR *sp, const char *np, int* input, int *output)
37 pid_t pid;
38 const char *name;
39 switch (pid = vfork()) {
40 case -1: /* Error. */
41 msgq(sp, M_SYSERR, "vfork");
42 return -1;
43 case 0: /* Utility. */
45 * Redirect stdin from the read end of the input pipe, and
46 * redirect stdout/stderr to the write end of the output pipe.
48 * !!!
49 * Historically, ex only directed stdout into the input pipe,
50 * letting stderr come out on the terminal as usual. Vi did
51 * not, directing both stdout and stderr into the input pipe.
52 * We match that practice in both ex and vi for consistency.
54 if (input[0] != -1)
55 (void)dup2(input[0], STDIN_FILENO);
56 (void)dup2(output[1], STDOUT_FILENO);
57 (void)dup2(output[1], STDERR_FILENO);
59 /* Close the utility's file descriptors. */
60 if (input[0] != -1)
61 (void)close(input[0]);
62 if (input[1] != -1)
63 (void)close(input[1]);
64 (void)close(output[0]);
65 (void)close(output[1]);
67 if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
68 name = O_STR(sp, O_SHELL);
69 else
70 ++name;
72 execl(O_STR(sp, O_SHELL), name, "-c", np, (char *)NULL);
73 msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
74 _exit (127);
75 /* NOTREACHED */
76 default: /* Parent-reader, parent-writer. */
77 /* Close the pipe ends neither parent will use. */
78 if (input[0] != -1)
79 (void)close(input[0]);
80 (void)close(output[1]);
81 return pid;
86 * ex_filter --
87 * Run a range of lines through a filter utility and optionally
88 * replace the original text with the stdout/stderr output of
89 * the utility.
91 * PUBLIC: int ex_filter __P((SCR *,
92 * PUBLIC: EXCMD *, MARK *, MARK *, MARK *, CHAR_T *, enum filtertype));
94 int
95 ex_filter(SCR *sp, EXCMD *cmdp, MARK *fm, MARK *tm, MARK *rp, CHAR_T *cmd, enum filtertype ftype)
97 FILE *ifp, *ofp;
98 pid_t parent_writer_pid, utility_pid;
99 db_recno_t nread;
100 int input[2], output[2], rval;
101 const char *np;
102 size_t nlen;
104 rval = 0;
106 /* Set return cursor position, which is never less than line 1. */
107 *rp = *fm;
108 if (rp->lno == 0)
109 rp->lno = 1;
111 /* We're going to need a shell. */
112 if (opts_empty(sp, O_SHELL, 0))
113 return (1);
116 * There are three different processes running through this code.
117 * They are the utility, the parent-writer and the parent-reader.
118 * The parent-writer is the process that writes from the file to
119 * the utility, the parent reader is the process that reads from
120 * the utility.
122 * Input and output are named from the utility's point of view.
123 * The utility reads from input[0] and the parent(s) write to
124 * input[1]. The parent(s) read from output[0] and the utility
125 * writes to output[1].
127 * !!!
128 * Historically, in the FILTER_READ case, the utility reads from
129 * the terminal (e.g. :r! cat works). Otherwise open up utility
130 * input pipe.
132 ofp = NULL;
133 input[0] = input[1] = output[0] = output[1] = -1;
134 if (ftype != FILTER_READ && pipe(input) < 0) {
135 msgq(sp, M_SYSERR, "pipe");
136 goto err;
139 /* Open up utility output pipe. */
140 if (pipe(output) < 0) {
141 msgq(sp, M_SYSERR, "pipe");
142 goto err;
144 if ((ofp = fdopen(output[0], "r")) == NULL) {
145 msgq(sp, M_SYSERR, "fdopen");
146 goto err;
149 /* Fork off the utility process. */
150 INT2SYS(sp, cmd, STRLEN(cmd)+1, np, nlen);
151 utility_pid = runcmd(sp, np, input, output);
154 * FILTER_RBANG, FILTER_READ:
156 * Reading is the simple case -- we don't need a parent writer,
157 * so the parent reads the output from the read end of the output
158 * pipe until it finishes, then waits for the child. Ex_readfp
159 * appends to the MARK, and closes ofp.
161 * For FILTER_RBANG, there is nothing to write to the utility.
162 * Make sure it doesn't wait forever by closing its standard
163 * input.
165 * !!!
166 * Set the return cursor to the last line read in for FILTER_READ.
167 * Historically, this behaves differently from ":r file" command,
168 * which leaves the cursor at the first line read in. Check to
169 * make sure that it's not past EOF because we were reading into an
170 * empty file.
172 if (ftype == FILTER_RBANG || ftype == FILTER_READ) {
173 if (ftype == FILTER_RBANG)
174 (void)close(input[1]);
176 if (ex_readfp(sp, "filter", ofp, fm, &nread, 1))
177 rval = 1;
178 sp->rptlines[L_ADDED] += nread;
179 if (ftype == FILTER_READ) {
180 if (fm->lno == 0)
181 rp->lno = nread;
182 else
183 rp->lno += nread;
185 goto uwait;
189 * FILTER_BANG, FILTER_WRITE
191 * Here we need both a reader and a writer. Temporary files are
192 * expensive and we'd like to avoid disk I/O. Using pipes has the
193 * obvious starvation conditions. It's done as follows:
195 * fork
196 * child
197 * write lines out
198 * exit
199 * parent
200 * FILTER_BANG:
201 * read lines into the file
202 * delete old lines
203 * FILTER_WRITE
204 * read and display lines
205 * wait for child
207 * XXX
208 * We get away without locking the underlying database because we know
209 * that none of the records that we're reading will be modified until
210 * after we've read them. This depends on the fact that the current
211 * B+tree implementation doesn't balance pages or similar things when
212 * it inserts new records. When the DB code has locking, we should
213 * treat vi as if it were multiple applications sharing a database, and
214 * do the required locking. If necessary a work-around would be to do
215 * explicit locking in the line.c:db_get() code, based on the flag set
216 * here.
218 F_SET(sp->ep, F_MULTILOCK);
219 switch (parent_writer_pid = fork()) {
220 case -1: /* Error. */
221 msgq(sp, M_SYSERR, "fork");
222 (void)close(input[1]);
223 (void)close(output[0]);
224 rval = 1;
225 break;
226 case 0: /* Parent-writer. */
228 * Write the selected lines to the write end of the input
229 * pipe. This instance of ifp is closed by ex_writefp.
231 (void)close(output[0]);
232 if ((ifp = fdopen(input[1], "w")) == NULL)
233 _exit (1);
234 _exit(ex_writefp(sp, "filter", ifp, fm, tm, NULL, NULL, 1));
236 /* NOTREACHED */
237 default: /* Parent-reader. */
238 (void)close(input[1]);
239 if (ftype == FILTER_WRITE) {
241 * Read the output from the read end of the output
242 * pipe and display it. Filter_ldisplay closes ofp.
244 if (filter_ldisplay(sp, ofp))
245 rval = 1;
246 } else {
248 * Read the output from the read end of the output
249 * pipe. Ex_readfp appends to the MARK and closes
250 * ofp.
252 if (ex_readfp(sp, "filter", ofp, tm, &nread, 1))
253 rval = 1;
254 sp->rptlines[L_ADDED] += nread;
257 /* Wait for the parent-writer. */
258 if (proc_wait(sp,
259 (long)parent_writer_pid, "parent-writer", 0, 1))
260 rval = 1;
262 /* Delete any lines written to the utility. */
263 if (rval == 0 && ftype == FILTER_BANG &&
264 (cut(sp, NULL, fm, tm, CUT_LINEMODE) ||
265 del(sp, fm, tm, 1))) {
266 rval = 1;
267 break;
271 * If the filter had no output, we may have just deleted
272 * the cursor. Don't do any real error correction, we'll
273 * try and recover later.
275 if (rp->lno > 1 && !db_exist(sp, rp->lno))
276 --rp->lno;
277 break;
279 F_CLR(sp->ep, F_MULTILOCK);
282 * !!!
283 * Ignore errors on vi file reads, to make reads prettier. It's
284 * completely inconsistent, and historic practice.
286 uwait: INT2CHAR(sp, cmd, STRLEN(cmd) + 1, np, nlen);
287 return (proc_wait(sp, (long)utility_pid, np,
288 ftype == FILTER_READ && F_ISSET(sp, SC_VI) ? 1 : 0, 0) || rval);
289 err: if (input[0] != -1)
290 (void)close(input[0]);
291 if (input[1] != -1)
292 (void)close(input[1]);
293 if (ofp != NULL)
294 (void)fclose(ofp);
295 else if (output[0] != -1)
296 (void)close(output[0]);
297 if (output[1] != -1)
298 (void)close(output[1]);
299 return 1;
303 * filter_ldisplay --
304 * Display output from a utility.
306 * !!!
307 * Historically, the characters were passed unmodified to the terminal.
308 * We use the ex print routines to make sure they're printable.
310 static int
311 filter_ldisplay(SCR *sp, FILE *fp)
313 size_t len;
314 size_t wlen;
315 const CHAR_T *wp;
317 EX_PRIVATE *exp;
319 for (exp = EXP(sp); !ex_getline(sp, fp, &len) && !INTERRUPTED(sp);) {
320 FILE2INT5(sp, exp->ibcw, exp->ibp, len, wp, wlen);
321 if (ex_ldisplay(sp, wp, wlen, 0, 0))
322 break;
324 if (ferror(fp))
325 msgq(sp, M_SYSERR, "filter read");
326 (void)fclose(fp);
327 return (0);