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 goto uwait; 183 } 184 185 /* 186 * FILTER_BANG, FILTER_WRITE 187 * 188 * Here we need both a reader and a writer. Temporary files are 189 * expensive and we'd like to avoid disk I/O. Using pipes has the 190 * obvious starvation conditions. It's done as follows: 191 * 192 * fork 193 * child 194 * write lines out 195 * exit 196 * parent 197 * FILTER_BANG: 198 * read lines into the file 199 * delete old lines 200 * FILTER_WRITE 201 * read and display lines 202 * wait for child 203 * 204 * XXX 205 * We get away without locking the underlying database because we know 206 * that none of the records that we're reading will be modified until 207 * after we've read them. This depends on the fact that the current 208 * B+tree implementation doesn't balance pages or similar things when 209 * it inserts new records. When the DB code has locking, we should 210 * treat vi as if it were multiple applications sharing a database, and 211 * do the required locking. If necessary a work-around would be to do 212 * explicit locking in the line.c:db_get() code, based on the flag set 213 * here. 214 */ 215 F_SET(sp->ep, F_MULTILOCK); 216 switch (parent_writer_pid = fork()) { 217 case -1: /* Error. */ 218 msgq(sp, M_SYSERR, "fork"); 219 (void)close(input[1]); 220 (void)close(output[0]); 221 rval = 1; 222 break; 223 case 0: /* Parent-writer. */ 224 /* 225 * Write the selected lines to the write end of the input 226 * pipe. This instance of ifp is closed by ex_writefp. 227 */ 228 (void)close(output[0]); 229 if ((ifp = fdopen(input[1], "w")) == NULL) 230 _exit (1); 231 _exit(ex_writefp(sp, "filter", ifp, fm, tm, NULL, NULL, 1)); 232 233 /* NOTREACHED */ 234 default: /* Parent-reader. */ 235 (void)close(input[1]); 236 if (ftype == FILTER_WRITE) { 237 /* 238 * Read the output from the read end of the output 239 * pipe and display it. Filter_ldisplay closes ofp. 240 */ 241 if (filter_ldisplay(sp, ofp)) 242 rval = 1; 243 } else { 244 /* 245 * Read the output from the read end of the output 246 * pipe. Ex_readfp appends to the MARK and closes 247 * ofp. 248 */ 249 if (ex_readfp(sp, "filter", ofp, tm, &nread, 1)) 250 rval = 1; 251 sp->rptlines[L_ADDED] += nread; 252 } 253 254 /* Wait for the parent-writer. */ 255 if (proc_wait(sp, 256 (long)parent_writer_pid, "parent-writer", 0, 1)) 257 rval = 1; 258 259 /* Delete any lines written to the utility. */ 260 if (rval == 0 && ftype == FILTER_BANG && 261 (cut(sp, NULL, fm, tm, CUT_LINEMODE) || 262 del(sp, fm, tm, 1))) { 263 rval = 1; 264 break; 265 } 266 267 /* 268 * If the filter had no output, we may have just deleted 269 * the cursor. Don't do any real error correction, we'll 270 * try and recover later. 271 */ 272 if (rp->lno > 1 && !db_exist(sp, rp->lno)) 273 --rp->lno; 274 break; 275 } 276 F_CLR(sp->ep, F_MULTILOCK); 277 278 /* 279 * !!! 280 * Ignore errors on vi file reads, to make reads prettier. It's 281 * completely inconsistent, and historic practice. 282 */ 283 uwait: INT2CHAR(sp, cmd, STRLEN(cmd) + 1, np, nlen); 284 return (proc_wait(sp, (long)utility_pid, np, 285 ftype == FILTER_READ && F_ISSET(sp, SC_VI) ? 1 : 0, 0) || rval); 286 } 287 288 /* 289 * filter_ldisplay -- 290 * Display output from a utility. 291 * 292 * !!! 293 * Historically, the characters were passed unmodified to the terminal. 294 * We use the ex print routines to make sure they're printable. 295 */ 296 static int 297 filter_ldisplay(SCR *sp, FILE *fp) 298 { 299 size_t len; 300 size_t wlen; 301 CHAR_T *wp; 302 303 EX_PRIVATE *exp; 304 305 for (exp = EXP(sp); !ex_getline(sp, fp, &len) && !INTERRUPTED(sp);) { 306 FILE2INT5(sp, exp->ibcw, exp->ibp, len, wp, wlen); 307 if (ex_ldisplay(sp, wp, wlen, 0, 0)) 308 break; 309 } 310 if (ferror(fp)) 311 msgq(sp, M_SYSERR, "filter read"); 312 (void)fclose(fp); 313 return (0); 314 } 315