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