1 /* $NetBSD: ex_argv.c,v 1.2 2013/11/22 15:52:05 christos Exp $ */ 2 /*- 3 * Copyright (c) 1993, 1994 4 * The Regents of the University of California. All rights reserved. 5 * Copyright (c) 1993, 1994, 1995, 1996 6 * Keith Bostic. All rights reserved. 7 * 8 * See the LICENSE file for redistribution information. 9 */ 10 11 #include "config.h" 12 13 #ifndef lint 14 static const char sccsid[] = "Id: ex_argv.c,v 10.39 2003/11/05 17:11:54 skimo Exp (Berkeley) Date: 2003/11/05 17:11:54 "; 15 #endif /* not lint */ 16 17 #include <sys/types.h> 18 #include <sys/queue.h> 19 20 #include <bitstring.h> 21 #include <ctype.h> 22 #include <dirent.h> 23 #include <errno.h> 24 #include <limits.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 30 #include "../common/common.h" 31 32 static int argv_alloc __P((SCR *, size_t)); 33 static int argv_comp __P((const void *, const void *)); 34 static int argv_fexp __P((SCR *, EXCMD *, 35 const CHAR_T *, size_t, CHAR_T *, size_t *, CHAR_T **, size_t *, int)); 36 static int argv_lexp __P((SCR *, EXCMD *, const char *)); 37 static int argv_sexp __P((SCR *, CHAR_T **, size_t *, size_t *)); 38 39 /* 40 * argv_init -- 41 * Build a prototype arguments list. 42 * 43 * PUBLIC: int argv_init __P((SCR *, EXCMD *)); 44 */ 45 int 46 argv_init(SCR *sp, EXCMD *excp) 47 { 48 EX_PRIVATE *exp; 49 50 exp = EXP(sp); 51 exp->argsoff = 0; 52 argv_alloc(sp, 1); 53 54 excp->argv = exp->args; 55 excp->argc = exp->argsoff; 56 return (0); 57 } 58 59 /* 60 * argv_exp0 -- 61 * Append a string to the argument list. 62 * 63 * PUBLIC: int argv_exp0 __P((SCR *, EXCMD *, const CHAR_T *, size_t)); 64 */ 65 int 66 argv_exp0(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen) 67 { 68 EX_PRIVATE *exp; 69 70 exp = EXP(sp); 71 argv_alloc(sp, cmdlen); 72 MEMCPY(exp->args[exp->argsoff]->bp, cmd, cmdlen); 73 exp->args[exp->argsoff]->bp[cmdlen] = '\0'; 74 exp->args[exp->argsoff]->len = cmdlen; 75 ++exp->argsoff; 76 excp->argv = exp->args; 77 excp->argc = exp->argsoff; 78 return (0); 79 } 80 81 /* 82 * argv_exp1 -- 83 * Do file name expansion on a string, and append it to the 84 * argument list. 85 * 86 * PUBLIC: int argv_exp1 __P((SCR *, EXCMD *, const CHAR_T *, size_t, int)); 87 */ 88 int 89 argv_exp1(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen, int is_bang) 90 { 91 size_t blen, len; 92 CHAR_T *p, *t, *bp; 93 94 GET_SPACE_RETW(sp, bp, blen, 512); 95 96 len = 0; 97 if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) { 98 FREE_SPACEW(sp, bp, blen); 99 return (1); 100 } 101 102 /* If it's empty, we're done. */ 103 if (len != 0) { 104 for (p = bp, t = bp + len; p < t; ++p) 105 if (!ISBLANK(*p)) 106 break; 107 if (p == t) 108 goto ret; 109 } else 110 goto ret; 111 112 (void)argv_exp0(sp, excp, bp, len); 113 114 ret: FREE_SPACEW(sp, bp, blen); 115 return (0); 116 } 117 118 /* 119 * argv_exp2 -- 120 * Do file name and shell expansion on a string, and append it to 121 * the argument list. 122 * 123 * PUBLIC: int argv_exp2 __P((SCR *, EXCMD *, const CHAR_T *, size_t)); 124 */ 125 int 126 argv_exp2(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen) 127 { 128 size_t blen, len, n; 129 int rval; 130 CHAR_T *bp, *p; 131 const char *mp, *np; 132 133 GET_SPACE_RETW(sp, bp, blen, 512); 134 135 #define SHELLECHO "echo " 136 #define SHELLOFFSET (sizeof(SHELLECHO) - 1) 137 p = bp; 138 *p++ = 'e'; 139 *p++ = 'c'; 140 *p++ = 'h'; 141 *p++ = 'o'; 142 *p++ = ' '; 143 len = SHELLOFFSET; 144 145 #if defined(DEBUG) && 0 146 vtrace(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd); 147 #endif 148 149 if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, 0)) { 150 rval = 1; 151 goto err; 152 } 153 154 #if defined(DEBUG) && 0 155 vtrace(sp, "before shell: %d: {%s}\n", len, bp); 156 #endif 157 158 /* 159 * Do shell word expansion -- it's very, very hard to figure out what 160 * magic characters the user's shell expects. Historically, it was a 161 * union of v7 shell and csh meta characters. We match that practice 162 * by default, so ":read \%" tries to read a file named '%'. It would 163 * make more sense to pass any special characters through the shell, 164 * but then, if your shell was csh, the above example will behave 165 * differently in nvi than in vi. If you want to get other characters 166 * passed through to your shell, change the "meta" option. 167 * 168 * To avoid a function call per character, we do a first pass through 169 * the meta characters looking for characters that aren't expected 170 * to be there, and then we can ignore them in the user's argument. 171 */ 172 if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1)) 173 n = 0; 174 else { 175 for (np = mp = O_STR(sp, O_SHELLMETA); *np != '\0'; ++np) 176 if (ISBLANK((UCHAR_T)*np) || 177 ISALNUM((UCHAR_T)*np)) 178 break; 179 p = bp + SHELLOFFSET; 180 n = len - SHELLOFFSET; 181 if (*p != '\0') { 182 for (; n > 0; --n, ++p) 183 if (strchr(mp, *p) != NULL) 184 break; 185 } else 186 for (; n > 0; --n, ++p) 187 if (!ISBLANK((UCHAR_T)*p) && 188 !ISALNUM((UCHAR_T)*p) && 189 strchr(mp, *p) != NULL) 190 break; 191 } 192 193 /* 194 * If we found a meta character in the string, fork a shell to expand 195 * it. Unfortunately, this is comparatively slow. Historically, it 196 * didn't matter much, since users don't enter meta characters as part 197 * of pathnames that frequently. The addition of filename completion 198 * broke that assumption because it's easy to use. As a result, lots 199 * folks have complained that the expansion code is too slow. So, we 200 * detect filename completion as a special case, and do it internally. 201 * Note that this code assumes that the <asterisk> character is the 202 * match-anything meta character. That feels safe -- if anyone writes 203 * a shell that doesn't follow that convention, I'd suggest giving them 204 * a festive hot-lead enema. 205 */ 206 switch (n) { 207 case 0: 208 p = bp + SHELLOFFSET; 209 len -= SHELLOFFSET; 210 rval = argv_exp3(sp, excp, p, len); 211 break; 212 case 1: 213 if (*p == '*') { 214 const char *np1; 215 char *d; 216 size_t nlen; 217 218 *p = '\0'; 219 INT2CHAR(sp, bp + SHELLOFFSET, 220 STRLEN(bp + SHELLOFFSET) + 1, np1, nlen); 221 d = strdup(np1); 222 rval = argv_lexp(sp, excp, d); 223 free (d); 224 break; 225 } 226 /* FALLTHROUGH */ 227 default: 228 if (argv_sexp(sp, &bp, &blen, &len)) { 229 rval = 1; 230 goto err; 231 } 232 p = bp; 233 rval = argv_exp3(sp, excp, p, len); 234 break; 235 } 236 237 err: FREE_SPACEW(sp, bp, blen); 238 return (rval); 239 } 240 241 /* 242 * argv_exp3 -- 243 * Take a string and break it up into an argv, which is appended 244 * to the argument list. 245 * 246 * PUBLIC: int argv_exp3 __P((SCR *, EXCMD *, const CHAR_T *, size_t)); 247 */ 248 int 249 argv_exp3(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen) 250 { 251 EX_PRIVATE *exp; 252 size_t len; 253 ARG_CHAR_T ch; 254 int off; 255 const CHAR_T *ap; 256 CHAR_T *p; 257 258 for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) { 259 /* Skip any leading whitespace. */ 260 for (; cmdlen > 0; --cmdlen, ++cmd) { 261 ch = (UCHAR_T)*cmd; 262 if (!ISBLANK(ch)) 263 break; 264 } 265 if (cmdlen == 0) 266 break; 267 268 /* 269 * Determine the length of this whitespace delimited 270 * argument. 271 * 272 * QUOTING NOTE: 273 * 274 * Skip any character preceded by the user's quoting 275 * character. 276 */ 277 for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) { 278 ch = (UCHAR_T)*cmd; 279 if (IS_ESCAPE(sp, excp, ch) && cmdlen > 1) { 280 ++cmd; 281 --cmdlen; 282 } else if (ISBLANK(ch)) 283 break; 284 } 285 286 /* 287 * Copy the argument into place. 288 * 289 * QUOTING NOTE: 290 * 291 * Lose quote chars. 292 */ 293 argv_alloc(sp, len); 294 off = exp->argsoff; 295 exp->args[off]->len = len; 296 for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++) 297 if (IS_ESCAPE(sp, excp, *ap)) 298 ++ap; 299 *p = '\0'; 300 } 301 excp->argv = exp->args; 302 excp->argc = exp->argsoff; 303 304 #if defined(DEBUG) && 0 305 for (cnt = 0; cnt < exp->argsoff; ++cnt) 306 vtrace(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]); 307 #endif 308 return (0); 309 } 310 311 /* 312 * argv_fexp -- 313 * Do file name and bang command expansion. 314 */ 315 static int 316 argv_fexp(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen, CHAR_T *p, size_t *lenp, CHAR_T **bpp, size_t *blenp, int is_bang) 317 { 318 EX_PRIVATE *exp; 319 char *t; 320 size_t blen, len, off, tlen; 321 CHAR_T *bp; 322 const CHAR_T *wp; 323 size_t wlen; 324 325 /* Replace file name characters. */ 326 for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd) 327 switch (*cmd) { 328 case '!': 329 if (!is_bang) 330 goto ins_ch; 331 exp = EXP(sp); 332 if (exp->lastbcomm == NULL) { 333 msgq(sp, M_ERR, 334 "115|No previous command to replace \"!\""); 335 return (1); 336 } 337 len += tlen = STRLEN(exp->lastbcomm); 338 off = p - bp; 339 ADD_SPACE_RETW(sp, bp, blen, len); 340 p = bp + off; 341 MEMCPY(p, exp->lastbcomm, tlen); 342 p += tlen; 343 F_SET(excp, E_MODIFY); 344 break; 345 case '%': 346 if ((t = sp->frp->name) == NULL) { 347 msgq(sp, M_ERR, 348 "116|No filename to substitute for %%"); 349 return (1); 350 } 351 tlen = strlen(t); 352 len += tlen; 353 off = p - bp; 354 ADD_SPACE_RETW(sp, bp, blen, len); 355 p = bp + off; 356 CHAR2INT(sp, t, tlen, wp, wlen); 357 MEMCPY(p, wp, wlen); 358 p += wlen; 359 F_SET(excp, E_MODIFY); 360 break; 361 case '#': 362 if ((t = sp->alt_name) == NULL) { 363 msgq(sp, M_ERR, 364 "117|No filename to substitute for #"); 365 return (1); 366 } 367 len += tlen = strlen(t); 368 off = p - bp; 369 ADD_SPACE_RETW(sp, bp, blen, len); 370 p = bp + off; 371 CHAR2INT(sp, t, tlen, wp, wlen); 372 MEMCPY(p, wp, wlen); 373 p += tlen; 374 F_SET(excp, E_MODIFY); 375 break; 376 case '\\': 377 /* 378 * QUOTING NOTE: 379 * 380 * Strip any backslashes that protected the file 381 * expansion characters. 382 */ 383 if (cmdlen > 1 && 384 (cmd[1] == '%' || cmd[1] == '#' || cmd[1] == '!')) { 385 ++cmd; 386 --cmdlen; 387 } 388 /* FALLTHROUGH */ 389 default: 390 ins_ch: ++len; 391 off = p - bp; 392 ADD_SPACE_RETW(sp, bp, blen, len); 393 p = bp + off; 394 *p++ = *cmd; 395 } 396 397 /* Nul termination. */ 398 ++len; 399 off = p - bp; 400 ADD_SPACE_RETW(sp, bp, blen, len); 401 p = bp + off; 402 *p = '\0'; 403 404 /* Return the new string length, buffer, buffer length. */ 405 *lenp = len - 1; 406 *bpp = bp; 407 *blenp = blen; 408 return (0); 409 } 410 411 /* 412 * argv_alloc -- 413 * Make more space for arguments. 414 */ 415 static int 416 argv_alloc(SCR *sp, size_t len) 417 { 418 ARGS *ap; 419 EX_PRIVATE *exp; 420 int cnt, off; 421 422 /* 423 * Allocate room for another argument, always leaving 424 * enough room for an ARGS structure with a length of 0. 425 */ 426 #define INCREMENT 20 427 exp = EXP(sp); 428 off = exp->argsoff; 429 if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) { 430 cnt = exp->argscnt + INCREMENT; 431 REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *)); 432 if (exp->args == NULL) { 433 (void)argv_free(sp); 434 goto mem; 435 } 436 memset(&exp->args[exp->argscnt], 0, INCREMENT * sizeof(ARGS *)); 437 exp->argscnt = cnt; 438 } 439 440 /* First argument. */ 441 if (exp->args[off] == NULL) { 442 CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS)); 443 if (exp->args[off] == NULL) 444 goto mem; 445 } 446 447 /* First argument buffer. */ 448 ap = exp->args[off]; 449 ap->len = 0; 450 if (ap->blen < len + 1) { 451 ap->blen = len + 1; 452 REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T)); 453 if (ap->bp == NULL) { 454 ap->bp = NULL; 455 ap->blen = 0; 456 F_CLR(ap, A_ALLOCATED); 457 mem: msgq(sp, M_SYSERR, NULL); 458 return (1); 459 } 460 F_SET(ap, A_ALLOCATED); 461 } 462 463 /* Second argument. */ 464 if (exp->args[++off] == NULL) { 465 CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS)); 466 if (exp->args[off] == NULL) 467 goto mem; 468 } 469 /* 0 length serves as end-of-argument marker. */ 470 exp->args[off]->len = 0; 471 return (0); 472 } 473 474 /* 475 * argv_free -- 476 * Free up argument structures. 477 * 478 * PUBLIC: int argv_free __P((SCR *)); 479 */ 480 int 481 argv_free(SCR *sp) 482 { 483 EX_PRIVATE *exp; 484 int off; 485 486 exp = EXP(sp); 487 if (exp->args != NULL) { 488 for (off = 0; off < exp->argscnt; ++off) { 489 if (exp->args[off] == NULL) 490 continue; 491 if (F_ISSET(exp->args[off], A_ALLOCATED)) 492 free(exp->args[off]->bp); 493 free(exp->args[off]); 494 } 495 free(exp->args); 496 } 497 exp->args = NULL; 498 exp->argscnt = 0; 499 exp->argsoff = 0; 500 return (0); 501 } 502 503 /* 504 * argv_lexp -- 505 * Find all file names matching the prefix and append them to the 506 * buffer. 507 */ 508 static int 509 argv_lexp(SCR *sp, EXCMD *excp, const char *path) 510 { 511 struct dirent *dp; 512 DIR *dirp; 513 EX_PRIVATE *exp; 514 int off; 515 size_t dlen, len, nlen; 516 const char *dname, *name; 517 char *p; 518 size_t wlen; 519 const CHAR_T *wp; 520 CHAR_T *n; 521 522 exp = EXP(sp); 523 524 /* Set up the name and length for comparison. */ 525 if ((p = strrchr(path, '/')) == NULL) { 526 dname = "."; 527 dlen = 0; 528 name = path; 529 } else { 530 if (p == path) { 531 dname = "/"; 532 dlen = 1; 533 } else { 534 *p = '\0'; 535 dname = path; 536 dlen = strlen(path); 537 } 538 name = p + 1; 539 } 540 nlen = strlen(name); 541 542 /* 543 * XXX 544 * We don't use the d_namlen field, it's not portable enough; we 545 * assume that d_name is nul terminated, instead. 546 */ 547 if ((dirp = opendir(dname)) == NULL) { 548 msgq_str(sp, M_SYSERR, dname, "%s"); 549 return (1); 550 } 551 for (off = exp->argsoff; (dp = readdir(dirp)) != NULL;) { 552 if (nlen == 0) { 553 if (dp->d_name[0] == '.') 554 continue; 555 len = strlen(dp->d_name); 556 } else { 557 len = strlen(dp->d_name); 558 if (len < nlen || memcmp(dp->d_name, name, nlen)) 559 continue; 560 } 561 562 /* Directory + name + slash + null. */ 563 argv_alloc(sp, dlen + len + 2); 564 n = exp->args[exp->argsoff]->bp; 565 if (dlen != 0) { 566 CHAR2INT(sp, dname, dlen, wp, wlen); 567 MEMCPY(n, wp, wlen); 568 n += dlen; 569 if (dlen > 1 || dname[0] != '/') 570 *n++ = '/'; 571 } 572 CHAR2INT(sp, dp->d_name, len + 1, wp, wlen); 573 MEMCPY(n, wp, wlen); 574 exp->args[exp->argsoff]->len = dlen + len + 1; 575 ++exp->argsoff; 576 excp->argv = exp->args; 577 excp->argc = exp->argsoff; 578 } 579 closedir(dirp); 580 581 if (off == exp->argsoff) { 582 /* 583 * If we didn't find a match, complain that the expansion 584 * failed. We can't know for certain that's the error, but 585 * it's a good guess, and it matches historic practice. 586 */ 587 msgq(sp, M_ERR, "304|Shell expansion failed"); 588 return (1); 589 } 590 qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp); 591 return (0); 592 } 593 594 /* 595 * argv_comp -- 596 * Alphabetic comparison. 597 */ 598 static int 599 argv_comp(const void *a, const void *b) 600 { 601 return (STRCMP((*(const ARGS * const*)a)->bp, (*(const ARGS * const*)b)->bp)); 602 } 603 604 static pid_t 605 runcmd(SCR *sp, const char *sh_path, const char *sh, const char *np, 606 int *std_output) 607 { 608 pid_t pid; 609 /* 610 * Do the minimal amount of work possible, the shell is going to run 611 * briefly and then exit. We sincerely hope. 612 */ 613 switch (pid = vfork()) { 614 case -1: /* Error. */ 615 msgq(sp, M_SYSERR, "vfork"); 616 return (pid_t)-1; 617 case 0: /* Utility. */ 618 /* Redirect stdout to the write end of the pipe. */ 619 (void)dup2(std_output[1], STDOUT_FILENO); 620 621 /* Close the utility's file descriptors. */ 622 (void)close(std_output[0]); 623 (void)close(std_output[1]); 624 (void)close(STDERR_FILENO); 625 626 /* 627 * XXX 628 * Assume that all shells have -c. 629 */ 630 execl(sh_path, sh, "-c", np, (char *)NULL); 631 msgq_str(sp, M_SYSERR, sh_path, "118|Error: execl: %s"); 632 _exit(127); 633 default: /* Parent. */ 634 /* Close the pipe ends the parent won't use. */ 635 (void)close(std_output[1]); 636 return pid; 637 } 638 } 639 640 /* 641 * argv_sexp -- 642 * Fork a shell, pipe a command through it, and read the output into 643 * a buffer. 644 */ 645 static int 646 argv_sexp(SCR *sp, CHAR_T **bpp, size_t *blenp, size_t *lenp) 647 { 648 enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval; 649 FILE *ifp; 650 pid_t pid; 651 size_t blen, len; 652 int ch, std_output[2]; 653 CHAR_T *bp, *p; 654 const char *sh, *sh_path; 655 const char *np; 656 size_t nlen; 657 658 /* Secure means no shell access. */ 659 if (O_ISSET(sp, O_SECURE)) { 660 msgq(sp, M_ERR, 661 "289|Shell expansions not supported when the secure edit option is set"); 662 return (1); 663 } 664 665 sh_path = O_STR(sp, O_SHELL); 666 if ((sh = strrchr(sh_path, '/')) == NULL) 667 sh = sh_path; 668 else 669 ++sh; 670 671 /* Local copies of the buffer variables. */ 672 bp = *bpp; 673 blen = *blenp; 674 675 /* 676 * There are two different processes running through this code, named 677 * the utility (the shell) and the parent. The utility reads standard 678 * input and writes standard output and standard error output. The 679 * parent writes to the utility, reads its standard output and ignores 680 * its standard error output. Historically, the standard error output 681 * was discarded by vi, as it produces a lot of noise when file patterns 682 * don't match. 683 * 684 * The parent reads std_output[0], and the utility writes std_output[1]. 685 */ 686 ifp = NULL; 687 std_output[0] = std_output[1] = -1; 688 if (pipe(std_output) < 0) { 689 msgq(sp, M_SYSERR, "pipe"); 690 return (1); 691 } 692 if ((ifp = fdopen(std_output[0], "r")) == NULL) { 693 msgq(sp, M_SYSERR, "fdopen"); 694 goto err; 695 } 696 INT2CHAR(sp, bp, STRLEN(bp)+1, np, nlen); 697 pid = runcmd(sp, sh_path, sh, np, std_output); 698 if (pid == -1) 699 goto err; 700 701 /* 702 * Copy process standard output into a buffer. 703 * 704 * !!! 705 * Historic vi apparently discarded leading \n and \r's from 706 * the shell output stream. We don't on the grounds that any 707 * shell that does that is broken. 708 */ 709 for (p = bp, len = 0, ch = EOF; 710 (ch = getc(ifp)) != EOF; *p++ = ch, blen-=sizeof(CHAR_T), ++len) 711 if (blen < 5) { 712 ADD_SPACE_GOTOW(sp, bp, *blenp, *blenp * 2); 713 p = bp + len; 714 blen = *blenp - len; 715 } 716 717 /* Delete the final newline, nul terminate the string. */ 718 if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) { 719 --p; 720 --len; 721 } 722 *p = '\0'; 723 *lenp = len; 724 *bpp = bp; /* *blenp is already updated. */ 725 726 if (ferror(ifp)) 727 goto ioerr; 728 if (fclose(ifp)) { 729 ioerr: msgq_str(sp, M_ERR, sh, "119|I/O error: %s"); 730 alloc_err: rval = SEXP_ERR; 731 } else 732 rval = SEXP_OK; 733 734 /* 735 * Wait for the process. If the shell process fails (e.g., "echo $q" 736 * where q wasn't a defined variable) or if the returned string has 737 * no characters or only blank characters, (e.g., "echo $5"), complain 738 * that the shell expansion failed. We can't know for certain that's 739 * the error, but it's a good guess, and it matches historic practice. 740 * This won't catch "echo foo_$5", but that's not a common error and 741 * historic vi didn't catch it either. 742 */ 743 if (proc_wait(sp, (long)pid, sh, 1, 0)) 744 rval = SEXP_EXPANSION_ERR; 745 746 for (p = bp; len; ++p, --len) 747 if (!ISBLANK(*p)) 748 break; 749 if (len == 0) 750 rval = SEXP_EXPANSION_ERR; 751 752 if (rval == SEXP_EXPANSION_ERR) 753 msgq(sp, M_ERR, "304|Shell expansion failed"); 754 755 return (rval == SEXP_OK ? 0 : 1); 756 err: if (ifp != NULL) 757 (void)fclose(ifp); 758 else if (std_output[0] != -1) 759 close(std_output[0]); 760 if (std_output[1] != -1) 761 close(std_output[0]); 762 return 1; 763 } 764