1 /*- 2 * Copyright (c) 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 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 #ifndef lint 13 static const char sccsid[] = "$Id: ex_argv.c,v 11.2 2012/10/09 23:00:29 zy Exp $"; 14 #endif /* not lint */ 15 16 #include <sys/types.h> 17 #include <sys/queue.h> 18 #include <sys/time.h> 19 20 #include <bitstring.h> 21 #include <ctype.h> 22 #include <dirent.h> 23 #include <errno.h> 24 #include <limits.h> 25 #include <pwd.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "../common/common.h" 32 33 static int argv_alloc(SCR *, size_t); 34 static int argv_comp(const void *, const void *); 35 static int argv_fexp(SCR *, EXCMD *, 36 CHAR_T *, size_t, CHAR_T *, size_t *, CHAR_T **, size_t *, int); 37 static int argv_sexp(SCR *, CHAR_T **, size_t *, size_t *); 38 static int argv_flt_user(SCR *, EXCMD *, CHAR_T *, size_t); 39 40 /* 41 * argv_init -- 42 * Build a prototype arguments list. 43 * 44 * PUBLIC: int argv_init(SCR *, EXCMD *); 45 */ 46 int 47 argv_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(SCR *, EXCMD *, CHAR_T *, size_t); 65 */ 66 int 67 argv_exp0(SCR *sp, EXCMD *excp, 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(SCR *, EXCMD *, CHAR_T *, size_t, int); 88 */ 89 int 90 argv_exp1(SCR *sp, EXCMD *excp, 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 (!cmdskip(*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 117 ret: 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(SCR *, EXCMD *, CHAR_T *, size_t); 127 */ 128 int 129 argv_exp2(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen) 130 { 131 size_t blen, len, n; 132 int rval; 133 CHAR_T *bp, *p; 134 135 GET_SPACE_RETW(sp, bp, blen, 512); 136 137 #define SHELLECHO L("echo ") 138 #define SHELLOFFSET (SIZE(SHELLECHO) - 1) 139 MEMCPY(bp, SHELLECHO, SHELLOFFSET); 140 p = bp + SHELLOFFSET; 141 len = SHELLOFFSET; 142 143 #if defined(DEBUG) && 0 144 TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd); 145 #endif 146 147 if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, 0)) { 148 rval = 1; 149 goto err; 150 } 151 152 #if defined(DEBUG) && 0 153 TRACE(sp, "before shell: %d: {%s}\n", len, bp); 154 #endif 155 156 /* 157 * Do shell word expansion -- it's very, very hard to figure out what 158 * magic characters the user's shell expects. Historically, it was a 159 * union of v7 shell and csh meta characters. We match that practice 160 * by default, so ":read \%" tries to read a file named '%'. It would 161 * make more sense to pass any special characters through the shell, 162 * but then, if your shell was csh, the above example will behave 163 * differently in nvi than in vi. If you want to get other characters 164 * passed through to your shell, change the "meta" option. 165 */ 166 if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1)) 167 n = 0; 168 else { 169 p = bp + SHELLOFFSET; 170 n = len - SHELLOFFSET; 171 for (; n > 0; --n, ++p) 172 if (IS_SHELLMETA(sp, *p)) 173 break; 174 } 175 176 /* 177 * If we found a meta character in the string, fork a shell to expand 178 * it. Unfortunately, this is comparatively slow. Historically, it 179 * didn't matter much, since users don't enter meta characters as part 180 * of pathnames that frequently. The addition of filename completion 181 * broke that assumption because it's easy to use. To increase the 182 * completion performance, nvi used to have an internal routine to 183 * handle "filename*". However, the shell special characters does not 184 * limit to "shellmeta", so such a hack breaks historic practice. 185 * After it all, we split the completion logic out from here. 186 */ 187 switch (n) { 188 case 0: 189 p = bp + SHELLOFFSET; 190 len -= SHELLOFFSET; 191 rval = argv_exp3(sp, excp, p, len); 192 break; 193 default: 194 if (argv_sexp(sp, &bp, &blen, &len)) { 195 rval = 1; 196 goto err; 197 } 198 p = bp; 199 rval = argv_exp3(sp, excp, p, len); 200 break; 201 } 202 203 err: FREE_SPACEW(sp, bp, blen); 204 return (rval); 205 } 206 207 /* 208 * argv_exp3 -- 209 * Take a string and break it up into an argv, which is appended 210 * to the argument list. 211 * 212 * PUBLIC: int argv_exp3(SCR *, EXCMD *, CHAR_T *, size_t); 213 */ 214 int 215 argv_exp3(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen) 216 { 217 EX_PRIVATE *exp; 218 size_t len; 219 int ch, off; 220 CHAR_T *ap, *p; 221 222 for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) { 223 /* Skip any leading whitespace. */ 224 for (; cmdlen > 0; --cmdlen, ++cmd) { 225 ch = *cmd; 226 if (!cmdskip(ch)) 227 break; 228 } 229 if (cmdlen == 0) 230 break; 231 232 /* 233 * Determine the length of this whitespace delimited 234 * argument. 235 * 236 * QUOTING NOTE: 237 * 238 * Skip any character preceded by the user's quoting 239 * character. 240 */ 241 for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) { 242 ch = *cmd; 243 if (IS_ESCAPE(sp, excp, ch) && cmdlen > 1) { 244 ++cmd; 245 --cmdlen; 246 } else if (cmdskip(ch)) 247 break; 248 } 249 250 /* 251 * Copy the argument into place. 252 * 253 * QUOTING NOTE: 254 * 255 * Lose quote chars. 256 */ 257 argv_alloc(sp, len); 258 off = exp->argsoff; 259 exp->args[off]->len = len; 260 for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++) 261 if (IS_ESCAPE(sp, excp, *ap)) 262 ++ap; 263 *p = '\0'; 264 } 265 excp->argv = exp->args; 266 excp->argc = exp->argsoff; 267 268 #if defined(DEBUG) && 0 269 for (cnt = 0; cnt < exp->argsoff; ++cnt) 270 TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]); 271 #endif 272 return (0); 273 } 274 275 /* 276 * argv_flt_ex -- 277 * Filter the ex commands with a prefix, and append the results to 278 * the argument list. 279 * 280 * PUBLIC: int argv_flt_ex(SCR *, EXCMD *, CHAR_T *, size_t); 281 */ 282 int 283 argv_flt_ex(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen) 284 { 285 EX_PRIVATE *exp; 286 EXCMDLIST const *cp; 287 int off; 288 size_t len; 289 290 exp = EXP(sp); 291 292 for (off = exp->argsoff, cp = cmds; cp->name != NULL; ++cp) { 293 len = STRLEN(cp->name); 294 if (cmdlen > 0 && 295 (cmdlen > len || MEMCMP(cmd, cp->name, cmdlen))) 296 continue; 297 298 /* Copy the matched ex command name. */ 299 argv_alloc(sp, len + 1); 300 MEMCPY(exp->args[exp->argsoff]->bp, cp->name, len + 1); 301 exp->args[exp->argsoff]->len = len; 302 ++exp->argsoff; 303 excp->argv = exp->args; 304 excp->argc = exp->argsoff; 305 } 306 307 return (0); 308 } 309 310 /* 311 * argv_flt_user -- 312 * Filter the ~user list on the system with a prefix, and append 313 * the results to the argument list. 314 */ 315 static int 316 argv_flt_user(SCR *sp, EXCMD *excp, CHAR_T *uname, size_t ulen) 317 { 318 EX_PRIVATE *exp; 319 struct passwd *pw; 320 int off; 321 char *np; 322 size_t len, nlen; 323 324 exp = EXP(sp); 325 off = exp->argsoff; 326 327 /* The input must come with a leading '~'. */ 328 INT2CHAR(sp, uname + 1, ulen - 1, np, nlen); 329 if ((np = v_strdup(sp, np, nlen)) == NULL) 330 return (1); 331 332 setpwent(); 333 while ((pw = getpwent()) != NULL) { 334 len = strlen(pw->pw_name); 335 if (nlen > 0 && 336 (nlen > len || memcmp(np, pw->pw_name, nlen))) 337 continue; 338 339 /* Copy '~' + the matched user name. */ 340 CHAR2INT(sp, pw->pw_name, len + 1, uname, ulen); 341 argv_alloc(sp, ulen + 1); 342 exp->args[exp->argsoff]->bp[0] = '~'; 343 MEMCPY(exp->args[exp->argsoff]->bp + 1, uname, ulen); 344 exp->args[exp->argsoff]->len = ulen; 345 ++exp->argsoff; 346 excp->argv = exp->args; 347 excp->argc = exp->argsoff; 348 } 349 endpwent(); 350 free(np); 351 352 qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp); 353 return (0); 354 } 355 356 /* 357 * argv_fexp -- 358 * Do file name and bang command expansion. 359 */ 360 static int 361 argv_fexp(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen, CHAR_T *p, size_t *lenp, CHAR_T **bpp, size_t *blenp, int is_bang) 362 { 363 EX_PRIVATE *exp; 364 char *t; 365 size_t blen, len, off, tlen; 366 CHAR_T *bp; 367 CHAR_T *wp; 368 size_t wlen; 369 370 /* Replace file name characters. */ 371 for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd) 372 switch (*cmd) { 373 case '!': 374 if (!is_bang) 375 goto ins_ch; 376 exp = EXP(sp); 377 if (exp->lastbcomm == NULL) { 378 msgq(sp, M_ERR, 379 "115|No previous command to replace \"!\""); 380 return (1); 381 } 382 len += tlen = STRLEN(exp->lastbcomm); 383 off = p - bp; 384 ADD_SPACE_RETW(sp, bp, blen, len); 385 p = bp + off; 386 MEMCPY(p, exp->lastbcomm, tlen); 387 p += tlen; 388 F_SET(excp, E_MODIFY); 389 break; 390 case '%': 391 if ((t = sp->frp->name) == NULL) { 392 msgq(sp, M_ERR, 393 "116|No filename to substitute for %%"); 394 return (1); 395 } 396 tlen = strlen(t); 397 len += tlen; 398 off = p - bp; 399 ADD_SPACE_RETW(sp, bp, blen, len); 400 p = bp + off; 401 CHAR2INT(sp, t, tlen, wp, wlen); 402 MEMCPY(p, wp, wlen); 403 p += wlen; 404 F_SET(excp, E_MODIFY); 405 break; 406 case '#': 407 if ((t = sp->alt_name) == NULL) { 408 msgq(sp, M_ERR, 409 "117|No filename to substitute for #"); 410 return (1); 411 } 412 len += tlen = strlen(t); 413 off = p - bp; 414 ADD_SPACE_RETW(sp, bp, blen, len); 415 p = bp + off; 416 CHAR2INT(sp, t, tlen, wp, wlen); 417 MEMCPY(p, wp, wlen); 418 p += wlen; 419 F_SET(excp, E_MODIFY); 420 break; 421 case '\\': 422 /* 423 * QUOTING NOTE: 424 * 425 * Strip any backslashes that protected the file 426 * expansion characters. 427 */ 428 if (cmdlen > 1 && 429 (cmd[1] == '%' || cmd[1] == '#' || cmd[1] == '!')) { 430 ++cmd; 431 --cmdlen; 432 } 433 /* FALLTHROUGH */ 434 default: 435 ins_ch: ++len; 436 off = p - bp; 437 ADD_SPACE_RETW(sp, bp, blen, len); 438 p = bp + off; 439 *p++ = *cmd; 440 } 441 442 /* Nul termination. */ 443 ++len; 444 off = p - bp; 445 ADD_SPACE_RETW(sp, bp, blen, len); 446 p = bp + off; 447 *p = '\0'; 448 449 /* Return the new string length, buffer, buffer length. */ 450 *lenp = len - 1; 451 *bpp = bp; 452 *blenp = blen; 453 return (0); 454 } 455 456 /* 457 * argv_alloc -- 458 * Make more space for arguments. 459 */ 460 static int 461 argv_alloc(SCR *sp, size_t len) 462 { 463 ARGS *ap; 464 EX_PRIVATE *exp; 465 int cnt, off; 466 467 /* 468 * Allocate room for another argument, always leaving 469 * enough room for an ARGS structure with a length of 0. 470 */ 471 #define INCREMENT 20 472 exp = EXP(sp); 473 off = exp->argsoff; 474 if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) { 475 cnt = exp->argscnt + INCREMENT; 476 REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *)); 477 if (exp->args == NULL) { 478 (void)argv_free(sp); 479 goto mem; 480 } 481 memset(&exp->args[exp->argscnt], 0, INCREMENT * sizeof(ARGS *)); 482 exp->argscnt = cnt; 483 } 484 485 /* First argument. */ 486 if (exp->args[off] == NULL) { 487 CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS)); 488 if (exp->args[off] == NULL) 489 goto mem; 490 } 491 492 /* First argument buffer. */ 493 ap = exp->args[off]; 494 ap->len = 0; 495 if (ap->blen < len + 1) { 496 ap->blen = len + 1; 497 REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T)); 498 if (ap->bp == NULL) { 499 ap->bp = NULL; 500 ap->blen = 0; 501 F_CLR(ap, A_ALLOCATED); 502 mem: msgq(sp, M_SYSERR, NULL); 503 return (1); 504 } 505 F_SET(ap, A_ALLOCATED); 506 } 507 508 /* Second argument. */ 509 if (exp->args[++off] == NULL) { 510 CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS)); 511 if (exp->args[off] == NULL) 512 goto mem; 513 } 514 /* 0 length serves as end-of-argument marker. */ 515 exp->args[off]->len = 0; 516 return (0); 517 } 518 519 /* 520 * argv_free -- 521 * Free up argument structures. 522 * 523 * PUBLIC: int argv_free(SCR *); 524 */ 525 int 526 argv_free(SCR *sp) 527 { 528 EX_PRIVATE *exp; 529 int off; 530 531 exp = EXP(sp); 532 if (exp->args != NULL) { 533 for (off = 0; off < exp->argscnt; ++off) { 534 if (exp->args[off] == NULL) 535 continue; 536 if (F_ISSET(exp->args[off], A_ALLOCATED)) 537 free(exp->args[off]->bp); 538 free(exp->args[off]); 539 } 540 free(exp->args); 541 } 542 exp->args = NULL; 543 exp->argscnt = 0; 544 exp->argsoff = 0; 545 return (0); 546 } 547 548 /* 549 * argv_flt_path -- 550 * Find all file names matching the prefix and append them to the 551 * argument list. 552 * 553 * PUBLIC: int argv_flt_path(SCR *, EXCMD *, CHAR_T *, size_t); 554 */ 555 int 556 argv_flt_path(SCR *sp, EXCMD *excp, CHAR_T *path, size_t plen) 557 { 558 struct dirent *dp; 559 DIR *dirp; 560 EX_PRIVATE *exp; 561 int off; 562 size_t dlen, len, nlen; 563 CHAR_T *dname; 564 CHAR_T *p, *np, *n; 565 char *name, *tp, *epd = NULL; 566 CHAR_T *wp; 567 size_t wlen; 568 569 exp = EXP(sp); 570 571 /* Set up the name and length for comparison. */ 572 if ((path = v_wstrdup(sp, path, plen)) == NULL) 573 return (1); 574 if ((p = STRRCHR(path, '/')) == NULL) { 575 if (*path == '~') { 576 int rc; 577 578 /* Filter ~user list instead. */ 579 rc = argv_flt_user(sp, excp, path, plen); 580 free(path); 581 return (rc); 582 } 583 dname = L("."); 584 dlen = 0; 585 np = path; 586 } else { 587 if (p == path) { 588 dname = L("/"); 589 dlen = 1; 590 } else { 591 *p = '\0'; 592 dname = path; 593 dlen = p - path; 594 } 595 np = p + 1; 596 } 597 598 INT2CHAR(sp, dname, dlen + 1, tp, nlen); 599 if ((epd = expanduser(tp)) != NULL) 600 tp = epd; 601 if ((dirp = opendir(tp)) == NULL) { 602 free(epd); 603 free(path); 604 return (1); 605 } 606 free(epd); 607 608 INT2CHAR(sp, np, STRLEN(np), tp, nlen); 609 if ((name = v_strdup(sp, tp, nlen)) == NULL) { 610 free(path); 611 return (1); 612 } 613 614 for (off = exp->argsoff; (dp = readdir(dirp)) != NULL;) { 615 if (nlen == 0) { 616 if (dp->d_name[0] == '.') 617 continue; 618 len = dp->d_namlen; 619 } else { 620 len = dp->d_namlen; 621 if (len < nlen || memcmp(dp->d_name, name, nlen)) 622 continue; 623 } 624 625 /* Directory + name + slash + null. */ 626 CHAR2INT(sp, dp->d_name, len + 1, wp, wlen); 627 argv_alloc(sp, dlen + wlen + 1); 628 n = exp->args[exp->argsoff]->bp; 629 if (dlen != 0) { 630 MEMCPY(n, dname, dlen); 631 n += dlen; 632 if (dlen > 1 || dname[0] != '/') 633 *n++ = '/'; 634 exp->args[exp->argsoff]->len = dlen + 1; 635 } 636 MEMCPY(n, wp, wlen); 637 exp->args[exp->argsoff]->len += wlen - 1; 638 ++exp->argsoff; 639 excp->argv = exp->args; 640 excp->argc = exp->argsoff; 641 } 642 closedir(dirp); 643 free(name); 644 free(path); 645 646 qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp); 647 return (0); 648 } 649 650 /* 651 * argv_comp -- 652 * Alphabetic comparison. 653 */ 654 static int 655 argv_comp(const void *a, const void *b) 656 { 657 return (STRCMP((*(ARGS **)a)->bp, (*(ARGS **)b)->bp)); 658 } 659 660 /* 661 * argv_sexp -- 662 * Fork a shell, pipe a command through it, and read the output into 663 * a buffer. 664 */ 665 static int 666 argv_sexp(SCR *sp, CHAR_T **bpp, size_t *blenp, size_t *lenp) 667 { 668 enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval; 669 FILE *ifp; 670 pid_t pid; 671 size_t blen, len; 672 int ch, std_output[2]; 673 CHAR_T *bp, *p; 674 char *sh, *sh_path; 675 char *np; 676 size_t nlen; 677 678 /* Secure means no shell access. */ 679 if (O_ISSET(sp, O_SECURE)) { 680 msgq(sp, M_ERR, 681 "289|Shell expansions not supported when the secure edit option is set"); 682 return (1); 683 } 684 685 sh_path = O_STR(sp, O_SHELL); 686 if ((sh = strrchr(sh_path, '/')) == NULL) 687 sh = sh_path; 688 else 689 ++sh; 690 691 /* Local copies of the buffer variables. */ 692 bp = *bpp; 693 blen = *blenp; 694 695 /* 696 * There are two different processes running through this code, named 697 * the utility (the shell) and the parent. The utility reads standard 698 * input and writes standard output and standard error output. The 699 * parent writes to the utility, reads its standard output and ignores 700 * its standard error output. Historically, the standard error output 701 * was discarded by vi, as it produces a lot of noise when file patterns 702 * don't match. 703 * 704 * The parent reads std_output[0], and the utility writes std_output[1]. 705 */ 706 ifp = NULL; 707 std_output[0] = std_output[1] = -1; 708 if (pipe(std_output) < 0) { 709 msgq(sp, M_SYSERR, "pipe"); 710 return (1); 711 } 712 if ((ifp = fdopen(std_output[0], "r")) == NULL) { 713 msgq(sp, M_SYSERR, "fdopen"); 714 goto err; 715 } 716 717 /* 718 * Do the minimal amount of work possible, the shell is going to run 719 * briefly and then exit. We sincerely hope. 720 */ 721 switch (pid = vfork()) { 722 case -1: /* Error. */ 723 msgq(sp, M_SYSERR, "vfork"); 724 err: if (ifp != NULL) 725 (void)fclose(ifp); 726 else if (std_output[0] != -1) 727 close(std_output[0]); 728 if (std_output[1] != -1) 729 close(std_output[0]); 730 return (1); 731 case 0: /* Utility. */ 732 /* Redirect stdout to the write end of the pipe. */ 733 (void)dup2(std_output[1], STDOUT_FILENO); 734 735 /* Close the utility's file descriptors. */ 736 (void)close(std_output[0]); 737 (void)close(std_output[1]); 738 (void)close(STDERR_FILENO); 739 740 /* 741 * XXX 742 * Assume that all shells have -c. 743 */ 744 INT2CHAR(sp, bp, STRLEN(bp)+1, np, nlen); 745 execl(sh_path, sh, "-c", np, (char *)NULL); 746 msgq_str(sp, M_SYSERR, sh_path, "118|Error: execl: %s"); 747 _exit(127); 748 default: /* Parent. */ 749 /* Close the pipe ends the parent won't use. */ 750 (void)close(std_output[1]); 751 break; 752 } 753 754 /* 755 * Copy process standard output into a buffer. 756 * 757 * !!! 758 * Historic vi apparently discarded leading \n and \r's from 759 * the shell output stream. We don't on the grounds that any 760 * shell that does that is broken. 761 */ 762 for (p = bp, len = 0, ch = EOF; 763 (ch = GETC(ifp)) != EOF; *p++ = ch, blen-=sizeof(CHAR_T), ++len) 764 if (blen < 5) { 765 ADD_SPACE_GOTOW(sp, bp, *blenp, *blenp * 2); 766 p = bp + len; 767 blen = *blenp - len; 768 } 769 770 /* Delete the final newline, nul terminate the string. */ 771 if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) { 772 --p; 773 --len; 774 } 775 *p = '\0'; 776 *lenp = len; 777 *bpp = bp; /* *blenp is already updated. */ 778 779 if (ferror(ifp)) 780 goto ioerr; 781 if (fclose(ifp)) { 782 ioerr: msgq_str(sp, M_ERR, sh, "119|I/O error: %s"); 783 alloc_err: rval = SEXP_ERR; 784 } else 785 rval = SEXP_OK; 786 787 /* 788 * Wait for the process. If the shell process fails (e.g., "echo $q" 789 * where q wasn't a defined variable) or if the returned string has 790 * no characters or only blank characters, (e.g., "echo $5"), complain 791 * that the shell expansion failed. We can't know for certain that's 792 * the error, but it's a good guess, and it matches historic practice. 793 * This won't catch "echo foo_$5", but that's not a common error and 794 * historic vi didn't catch it either. 795 */ 796 if (proc_wait(sp, (long)pid, sh, 1, 0)) 797 rval = SEXP_EXPANSION_ERR; 798 799 for (p = bp; len; ++p, --len) 800 if (!cmdskip(*p)) 801 break; 802 if (len == 0) 803 rval = SEXP_EXPANSION_ERR; 804 805 if (rval == SEXP_EXPANSION_ERR) 806 msgq(sp, M_ERR, "304|Shell expansion failed"); 807 808 return (rval == SEXP_OK ? 0 : 1); 809 } 810 811 /* 812 * argv_esc -- 813 * Escape a string into an ex and shell argument. 814 * 815 * PUBLIC: CHAR_T *argv_esc(SCR *, EXCMD *, CHAR_T *, size_t); 816 */ 817 CHAR_T * 818 argv_esc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len) 819 { 820 size_t blen, off; 821 CHAR_T *bp, *p; 822 int ch; 823 824 GET_SPACE_GOTOW(sp, bp, blen, len + 1); 825 826 /* 827 * Leaving the first '~' unescaped causes the user to need a 828 * "./" prefix to edit a file which really starts with a '~'. 829 * However, the file completion happens to not work for these 830 * files without the prefix. 831 * 832 * All ex expansion characters, "!%#", are double escaped. 833 */ 834 for (p = bp; len > 0; ++str, --len) { 835 ch = *str; 836 off = p - bp; 837 if (blen / sizeof(CHAR_T) - off < 3) { 838 ADD_SPACE_GOTOW(sp, bp, blen, off + 3); 839 p = bp + off; 840 } 841 if (cmdskip(ch) || ch == '\n' || 842 IS_ESCAPE(sp, excp, ch)) /* Ex. */ 843 *p++ = CH_LITERAL; 844 else switch (ch) { 845 case '~': /* ~user. */ 846 if (p != bp) 847 *p++ = '\\'; 848 break; 849 case '+': /* Ex +cmd. */ 850 if (p == bp) 851 *p++ = '\\'; 852 break; 853 case '!': case '%': case '#': /* Ex exp. */ 854 *p++ = '\\'; 855 *p++ = '\\'; 856 break; 857 case ',': case '-': case '.': case '/': /* Safe. */ 858 case ':': case '=': case '@': case '_': 859 break; 860 default: /* Unsafe. */ 861 if (isascii(ch) && !isalnum(ch)) 862 *p++ = '\\'; 863 } 864 *p++ = ch; 865 } 866 *p = '\0'; 867 868 return bp; 869 870 alloc_err: 871 return NULL; 872 } 873 874 /* 875 * argv_uesc -- 876 * Unescape an escaped ex and shell argument. 877 * 878 * PUBLIC: CHAR_T *argv_uesc(SCR *, EXCMD *, CHAR_T *, size_t); 879 */ 880 CHAR_T * 881 argv_uesc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len) 882 { 883 size_t blen; 884 CHAR_T *bp, *p; 885 886 GET_SPACE_GOTOW(sp, bp, blen, len + 1); 887 888 for (p = bp; len > 0; ++str, --len) { 889 if (IS_ESCAPE(sp, excp, *str)) { 890 if (--len < 1) 891 break; 892 ++str; 893 } else if (*str == '\\') { 894 if (--len < 1) 895 break; 896 ++str; 897 898 /* Check for double escaping. */ 899 if (*str == '\\' && len > 1) 900 switch (str[1]) { 901 case '!': case '%': case '#': 902 ++str; 903 --len; 904 } 905 } 906 *p++ = *str; 907 } 908 *p = '\0'; 909 910 return bp; 911 912 alloc_err: 913 return NULL; 914 } 915