pango is now required for gtk
[nvi.git] / ex / ex_filter.c
blob309ed16803fbac4b392061ec31458e2cee23fe2a
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.39 2000/11/26 20:10:19 skimo Exp $ (Berkeley) $Date: 2000/11/26 20:10:19 $";
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 *));
33 * ex_filter --
34 * Run a range of lines through a filter utility and optionally
35 * replace the original text with the stdout/stderr output of
36 * the utility.
38 * PUBLIC: int ex_filter __P((SCR *,
39 * PUBLIC: EXCMD *, MARK *, MARK *, MARK *, CHAR_T *, enum filtertype));
41 int
42 ex_filter(sp, cmdp, fm, tm, rp, cmd, ftype)
43 SCR *sp;
44 EXCMD *cmdp;
45 MARK *fm, *tm, *rp;
46 CHAR_T *cmd;
47 enum filtertype ftype;
49 FILE *ifp, *ofp;
50 pid_t parent_writer_pid, utility_pid;
51 db_recno_t nread;
52 int input[2], output[2], rval;
53 char *name;
54 char *np;
55 size_t nlen;
57 rval = 0;
59 /* Set return cursor position, which is never less than line 1. */
60 *rp = *fm;
61 if (rp->lno == 0)
62 rp->lno = 1;
64 /* We're going to need a shell. */
65 if (opts_empty(sp, O_SHELL, 0))
66 return (1);
69 * There are three different processes running through this code.
70 * They are the utility, the parent-writer and the parent-reader.
71 * The parent-writer is the process that writes from the file to
72 * the utility, the parent reader is the process that reads from
73 * the utility.
75 * Input and output are named from the utility's point of view.
76 * The utility reads from input[0] and the parent(s) write to
77 * input[1]. The parent(s) read from output[0] and the utility
78 * writes to output[1].
80 * !!!
81 * Historically, in the FILTER_READ case, the utility reads from
82 * the terminal (e.g. :r! cat works). Otherwise open up utility
83 * input pipe.
85 ofp = NULL;
86 input[0] = input[1] = output[0] = output[1] = -1;
87 if (ftype != FILTER_READ && pipe(input) < 0) {
88 msgq(sp, M_SYSERR, "pipe");
89 goto err;
92 /* Open up utility output pipe. */
93 if (pipe(output) < 0) {
94 msgq(sp, M_SYSERR, "pipe");
95 goto err;
97 if ((ofp = fdopen(output[0], "r")) == NULL) {
98 msgq(sp, M_SYSERR, "fdopen");
99 goto err;
102 /* Fork off the utility process. */
103 switch (utility_pid = vfork()) {
104 case -1: /* Error. */
105 msgq(sp, M_SYSERR, "vfork");
106 err: if (input[0] != -1)
107 (void)close(input[0]);
108 if (input[1] != -1)
109 (void)close(input[1]);
110 if (ofp != NULL)
111 (void)fclose(ofp);
112 else if (output[0] != -1)
113 (void)close(output[0]);
114 if (output[1] != -1)
115 (void)close(output[1]);
116 return (1);
117 case 0: /* Utility. */
119 * Redirect stdin from the read end of the input pipe, and
120 * redirect stdout/stderr to the write end of the output pipe.
122 * !!!
123 * Historically, ex only directed stdout into the input pipe,
124 * letting stderr come out on the terminal as usual. Vi did
125 * not, directing both stdout and stderr into the input pipe.
126 * We match that practice in both ex and vi for consistency.
128 if (input[0] != -1)
129 (void)dup2(input[0], STDIN_FILENO);
130 (void)dup2(output[1], STDOUT_FILENO);
131 (void)dup2(output[1], STDERR_FILENO);
133 /* Close the utility's file descriptors. */
134 if (input[0] != -1)
135 (void)close(input[0]);
136 if (input[1] != -1)
137 (void)close(input[1]);
138 (void)close(output[0]);
139 (void)close(output[1]);
141 if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
142 name = O_STR(sp, O_SHELL);
143 else
144 ++name;
146 INT2CHAR(sp, cmd, v_strlen(cmd)+1, np, nlen);
147 execl(O_STR(sp, O_SHELL), name, "-c", np, NULL);
148 msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
149 _exit (127);
150 /* NOTREACHED */
151 default: /* Parent-reader, parent-writer. */
152 /* Close the pipe ends neither parent will use. */
153 if (input[0] != -1)
154 (void)close(input[0]);
155 (void)close(output[1]);
156 break;
160 * FILTER_RBANG, FILTER_READ:
162 * Reading is the simple case -- we don't need a parent writer,
163 * so the parent reads the output from the read end of the output
164 * pipe until it finishes, then waits for the child. Ex_readfp
165 * appends to the MARK, and closes ofp.
167 * For FILTER_RBANG, there is nothing to write to the utility.
168 * Make sure it doesn't wait forever by closing its standard
169 * input.
171 * !!!
172 * Set the return cursor to the last line read in for FILTER_READ.
173 * Historically, this behaves differently from ":r file" command,
174 * which leaves the cursor at the first line read in. Check to
175 * make sure that it's not past EOF because we were reading into an
176 * empty file.
178 if (ftype == FILTER_RBANG || ftype == FILTER_READ) {
179 if (ftype == FILTER_RBANG)
180 (void)close(input[1]);
182 if (ex_readfp(sp, "filter", ofp, fm, &nread, 1))
183 rval = 1;
184 sp->rptlines[L_ADDED] += nread;
185 if (ftype == FILTER_READ)
186 if (fm->lno == 0)
187 rp->lno = nread;
188 else
189 rp->lno += nread;
190 goto uwait;
194 * FILTER_BANG, FILTER_WRITE
196 * Here we need both a reader and a writer. Temporary files are
197 * expensive and we'd like to avoid disk I/O. Using pipes has the
198 * obvious starvation conditions. It's done as follows:
200 * fork
201 * child
202 * write lines out
203 * exit
204 * parent
205 * FILTER_BANG:
206 * read lines into the file
207 * delete old lines
208 * FILTER_WRITE
209 * read and display lines
210 * wait for child
212 * XXX
213 * We get away without locking the underlying database because we know
214 * that none of the records that we're reading will be modified until
215 * after we've read them. This depends on the fact that the current
216 * B+tree implementation doesn't balance pages or similar things when
217 * it inserts new records. When the DB code has locking, we should
218 * treat vi as if it were multiple applications sharing a database, and
219 * do the required locking. If necessary a work-around would be to do
220 * explicit locking in the line.c:db_get() code, based on the flag set
221 * here.
223 F_SET(sp->ep, F_MULTILOCK);
224 switch (parent_writer_pid = fork()) {
225 case -1: /* Error. */
226 msgq(sp, M_SYSERR, "fork");
227 (void)close(input[1]);
228 (void)close(output[0]);
229 rval = 1;
230 break;
231 case 0: /* Parent-writer. */
233 * Write the selected lines to the write end of the input
234 * pipe. This instance of ifp is closed by ex_writefp.
236 (void)close(output[0]);
237 if ((ifp = fdopen(input[1], "w")) == NULL)
238 _exit (1);
239 _exit(ex_writefp(sp, "filter", ifp, fm, tm, NULL, NULL, 1));
241 /* NOTREACHED */
242 default: /* Parent-reader. */
243 (void)close(input[1]);
244 if (ftype == FILTER_WRITE) {
246 * Read the output from the read end of the output
247 * pipe and display it. Filter_ldisplay closes ofp.
249 if (filter_ldisplay(sp, ofp))
250 rval = 1;
251 } else {
253 * Read the output from the read end of the output
254 * pipe. Ex_readfp appends to the MARK and closes
255 * ofp.
257 if (ex_readfp(sp, "filter", ofp, tm, &nread, 1))
258 rval = 1;
259 sp->rptlines[L_ADDED] += nread;
262 /* Wait for the parent-writer. */
263 if (proc_wait(sp,
264 (long)parent_writer_pid, "parent-writer", 0, 1))
265 rval = 1;
267 /* Delete any lines written to the utility. */
268 if (rval == 0 && ftype == FILTER_BANG &&
269 (cut(sp, NULL, fm, tm, CUT_LINEMODE) ||
270 del(sp, fm, tm, 1))) {
271 rval = 1;
272 break;
276 * If the filter had no output, we may have just deleted
277 * the cursor. Don't do any real error correction, we'll
278 * try and recover later.
280 if (rp->lno > 1 && !db_exist(sp, rp->lno))
281 --rp->lno;
282 break;
284 F_CLR(sp->ep, F_MULTILOCK);
287 * !!!
288 * Ignore errors on vi file reads, to make reads prettier. It's
289 * completely inconsistent, and historic practice.
291 uwait: INT2CHAR(sp, cmd, v_strlen(cmd) + 1, np, nlen);
292 return (proc_wait(sp, (long)utility_pid, np,
293 ftype == FILTER_READ && F_ISSET(sp, SC_VI) ? 1 : 0, 0) || rval);
297 * filter_ldisplay --
298 * Display output from a utility.
300 * !!!
301 * Historically, the characters were passed unmodified to the terminal.
302 * We use the ex print routines to make sure they're printable.
304 static int
305 filter_ldisplay(sp, fp)
306 SCR *sp;
307 FILE *fp;
309 size_t len;
310 char *np;
311 size_t nlen;
313 EX_PRIVATE *exp;
315 for (exp = EXP(sp); !ex_getline(sp, fp, &len) && !INTERRUPTED(sp);) {
316 INT2CHAR(sp, exp->ibp, len, np, nlen);
317 if (ex_ldisplay(sp, np, nlen, 0, 0))
318 break;
320 if (ferror(fp))
321 msgq(sp, M_SYSERR, "filter read");
322 (void)fclose(fp);
323 return (0);