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