| File: | src/usr.bin/vi/build/../ex/ex_filter.c |
| Warning: | line 157, column 9 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 | } |