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