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 } 316