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