home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / ex / ex_argv.c < prev    next >
C/C++ Source or Header  |  1996-09-20  |  17KB  |  757 lines

  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[] = "@(#)ex_argv.c    10.26 (Berkeley) 9/20/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18.  
  19. #include <bitstring.h>
  20. #include <ctype.h>
  21. #include <dirent.h>
  22. #include <errno.h>
  23. #include <limits.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <unistd.h>
  28.  
  29. #include "../common/common.h"
  30.  
  31. static int argv_alloc __P((SCR *, size_t));
  32. static int argv_comp __P((const void *, const void *));
  33. static int argv_fexp __P((SCR *, EXCMD *,
  34.     char *, size_t, char *, size_t *, char **, size_t *, int));
  35. static int argv_lexp __P((SCR *, EXCMD *, char *));
  36. static int argv_sexp __P((SCR *, char **, size_t *, size_t *));
  37.  
  38. /*
  39.  * argv_init --
  40.  *    Build  a prototype arguments list.
  41.  *
  42.  * PUBLIC: int argv_init __P((SCR *, EXCMD *));
  43.  */
  44. int
  45. argv_init(sp, excp)
  46.     SCR *sp;
  47.     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 *, size_t));
  65.  */
  66. int
  67. argv_exp0(sp, excp, cmd, cmdlen)
  68.     SCR *sp;
  69.     EXCMD *excp;
  70.     char *cmd;
  71.     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 *, char *, size_t, int));
  92.  */
  93. int
  94. argv_exp1(sp, excp, cmd, cmdlen, is_bang)
  95.     SCR *sp;
  96.     EXCMD *excp;
  97.     char *cmd;
  98.     size_t cmdlen;
  99.     int is_bang;
  100. {
  101.     EX_PRIVATE *exp;
  102.     size_t blen, len;
  103.     char *bp, *p, *t;
  104.  
  105.     GET_SPACE_RET(sp, bp, blen, 512);
  106.  
  107.     len = 0;
  108.     exp = EXP(sp);
  109.     if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) {
  110.         FREE_SPACE(sp, bp, blen);
  111.         return (1);
  112.     }
  113.  
  114.     /* If it's empty, we're done. */
  115.     if (len != 0) {
  116.         for (p = bp, t = bp + len; p < t; ++p)
  117.             if (!isblank(*p))
  118.                 break;
  119.         if (p == t)
  120.             goto ret;
  121.     } else
  122.         goto ret;
  123.  
  124.     (void)argv_exp0(sp, excp, bp, len);
  125.  
  126. ret:    FREE_SPACE(sp, bp, blen);
  127.     return (0);
  128. }
  129.  
  130. /*
  131.  * argv_exp2 --
  132.  *    Do file name and shell expansion on a string, and append it to
  133.  *    the argument list.
  134.  *
  135.  * PUBLIC: int argv_exp2 __P((SCR *, EXCMD *, char *, size_t));
  136.  */
  137. int
  138. argv_exp2(sp, excp, cmd, cmdlen)
  139.     SCR *sp;
  140.     EXCMD *excp;
  141.     char *cmd;
  142.     size_t cmdlen;
  143. {
  144.     size_t blen, len, n;
  145.     int rval;
  146.     char *bp, *mp, *p;
  147.  
  148.     GET_SPACE_RET(sp, bp, blen, 512);
  149.  
  150. #define    SHELLECHO    "echo "
  151. #define    SHELLOFFSET    (sizeof(SHELLECHO) - 1)
  152.     memcpy(bp, SHELLECHO, SHELLOFFSET);
  153.     p = bp + SHELLOFFSET;
  154.     len = SHELLOFFSET;
  155.  
  156. #if defined(DEBUG) && 0
  157.     TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd);
  158. #endif
  159.  
  160.     if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, 0)) {
  161.         rval = 1;
  162.         goto err;
  163.     }
  164.  
  165. #if defined(DEBUG) && 0
  166.     TRACE(sp, "before shell: %d: {%s}\n", len, bp);
  167. #endif
  168.  
  169.     /*
  170.      * Do shell word expansion -- it's very, very hard to figure out what
  171.      * magic characters the user's shell expects.  Historically, it was a
  172.      * union of v7 shell and csh meta characters.  We match that practice
  173.      * by default, so ":read \%" tries to read a file named '%'.  It would
  174.      * make more sense to pass any special characters through the shell,
  175.      * but then, if your shell was csh, the above example will behave
  176.      * differently in nvi than in vi.  If you want to get other characters
  177.      * passed through to your shell, change the "meta" option.
  178.      *
  179.      * To avoid a function call per character, we do a first pass through
  180.      * the meta characters looking for characters that aren't expected
  181.      * to be there, and then we can ignore them in the user's argument.
  182.      */
  183.     if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1))
  184.         n = 0;
  185.     else {
  186.         for (p = mp = O_STR(sp, O_SHELLMETA); *p != '\0'; ++p)
  187.             if (isblank(*p) || isalnum(*p))
  188.                 break;
  189.         p = bp + SHELLOFFSET;
  190.         n = len - SHELLOFFSET;
  191.         if (*p != '\0') {
  192.             for (; n > 0; --n, ++p)
  193.                 if (strchr(mp, *p) != NULL)
  194.                     break;
  195.         } else
  196.             for (; n > 0; --n, ++p)
  197.                 if (!isblank(*p) &&
  198.                     !isalnum(*p) && strchr(mp, *p) != NULL)
  199.                     break;
  200.     }
  201.  
  202.     /*
  203.      * If we found a meta character in the string, fork a shell to expand
  204.      * it.  Unfortunately, this is comparatively slow.  Historically, it
  205.      * didn't matter much, since users don't enter meta characters as part
  206.      * of pathnames that frequently.  The addition of filename completion
  207.      * broke that assumption because it's easy to use.  As a result, lots
  208.      * folks have complained that the expansion code is too slow.  So, we
  209.      * detect filename completion as a special case, and do it internally.
  210.      * Note that this code assumes that the <asterisk> character is the
  211.      * match-anything meta character.  That feels safe -- if anyone writes
  212.      * a shell that doesn't follow that convention, I'd suggest giving them
  213.      * a festive hot-lead enema.
  214.      */
  215.     switch (n) {
  216.     case 0:
  217.         p = bp + SHELLOFFSET;
  218.         len -= SHELLOFFSET;
  219.         rval = argv_exp3(sp, excp, p, len);
  220.         break;
  221.     case 1:
  222.         if (*p == '*') {
  223.             *p = '\0';
  224.             rval = argv_lexp(sp, excp, bp + SHELLOFFSET);
  225.             break;
  226.         }
  227.         /* FALLTHROUGH */
  228.     default:
  229.         if (argv_sexp(sp, &bp, &blen, &len)) {
  230.             rval = 1;
  231.             goto err;
  232.         }
  233.         p = bp;
  234.         rval = argv_exp3(sp, excp, p, len);
  235.         break;
  236.     }
  237.  
  238. err:    FREE_SPACE(sp, bp, blen);
  239.     return (rval);
  240. }
  241.  
  242. /*
  243.  * argv_exp3 --
  244.  *    Take a string and break it up into an argv, which is appended
  245.  *    to the argument list.
  246.  *
  247.  * PUBLIC: int argv_exp3 __P((SCR *, EXCMD *, char *, size_t));
  248.  */
  249. int
  250. argv_exp3(sp, excp, cmd, cmdlen)
  251.     SCR *sp;
  252.     EXCMD *excp;
  253.     char *cmd;
  254.     size_t cmdlen;
  255. {
  256.     EX_PRIVATE *exp;
  257.     size_t len;
  258.     int ch, off;
  259.     char *ap, *p;
  260.  
  261.     for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) {
  262.         /* Skip any leading whitespace. */
  263.         for (; cmdlen > 0; --cmdlen, ++cmd) {
  264.             ch = *cmd;
  265.             if (!isblank(ch))
  266.                 break;
  267.         }
  268.         if (cmdlen == 0)
  269.             break;
  270.  
  271.         /*
  272.          * Determine the length of this whitespace delimited
  273.          * argument.
  274.          *
  275.          * QUOTING NOTE:
  276.          *
  277.          * Skip any character preceded by the user's quoting
  278.          * character.
  279.          */
  280.         for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) {
  281.             ch = *cmd;
  282.             if (IS_ESCAPE(sp, excp, ch) && cmdlen > 1) {
  283.                 ++cmd;
  284.                 --cmdlen;
  285.             } else if (isblank(ch))
  286.                 break;
  287.         }
  288.  
  289.         /*
  290.          * Copy the argument into place.
  291.          *
  292.          * QUOTING NOTE:
  293.          *
  294.          * Lose quote chars.
  295.          */
  296.         argv_alloc(sp, len);
  297.         off = exp->argsoff;
  298.         exp->args[off]->len = len;
  299.         for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++)
  300.             if (IS_ESCAPE(sp, excp, *ap))
  301.                 ++ap;
  302.         *p = '\0';
  303.     }
  304.     excp->argv = exp->args;
  305.     excp->argc = exp->argsoff;
  306.  
  307. #if defined(DEBUG) && 0
  308.     for (cnt = 0; cnt < exp->argsoff; ++cnt)
  309.         TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]);
  310. #endif
  311.     return (0);
  312. }
  313.  
  314. /*
  315.  * argv_fexp --
  316.  *    Do file name and bang command expansion.
  317.  */
  318. static int
  319. argv_fexp(sp, excp, cmd, cmdlen, p, lenp, bpp, blenp, is_bang)
  320.     SCR *sp;
  321.     EXCMD *excp;
  322.     char *cmd, *p, **bpp;
  323.     size_t cmdlen, *lenp, *blenp;
  324.     int is_bang;
  325. {
  326.     EX_PRIVATE *exp;
  327.     char *bp, *t;
  328.     size_t blen, len, off, tlen;
  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_RET(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_RET(sp, bp, blen, len);
  360.             p = bp + off;
  361.             memcpy(p, t, tlen);
  362.             p += tlen;
  363.             F_SET(excp, E_MODIFY);
  364.             break;
  365.         case '#':
  366.             if ((t = sp->alt_name) == NULL) {
  367.                 msgq(sp, M_ERR,
  368.                     "117|No filename to substitute for #");
  369.                 return (1);
  370.             }
  371.             len += tlen = strlen(t);
  372.             off = p - bp;
  373.             ADD_SPACE_RET(sp, bp, blen, len);
  374.             p = bp + off;
  375.             memcpy(p, t, tlen);
  376.             p += tlen;
  377.             F_SET(excp, E_MODIFY);
  378.             break;
  379.         case '\\':
  380.             /*
  381.              * QUOTING NOTE:
  382.              *
  383.              * Strip any backslashes that protected the file
  384.              * expansion characters.
  385.              */
  386.             if (cmdlen > 1 &&
  387.                 (cmd[1] == '%' || cmd[1] == '#' || cmd[1] == '!')) {
  388.                 ++cmd;
  389.                 --cmdlen;
  390.             }
  391.             /* FALLTHROUGH */
  392.         default:
  393. ins_ch:            ++len;
  394.             off = p - bp;
  395.             ADD_SPACE_RET(sp, bp, blen, len);
  396.             p = bp + off;
  397.             *p++ = *cmd;
  398.         }
  399.  
  400.     /* Nul termination. */
  401.     ++len;
  402.     off = p - bp;
  403.     ADD_SPACE_RET(sp, bp, blen, len);
  404.     p = bp + off;
  405.     *p = '\0';
  406.  
  407.     /* Return the new string length, buffer, buffer length. */
  408.     *lenp = len - 1;
  409.     *bpp = bp;
  410.     *blenp = blen;
  411.     return (0);
  412. }
  413.  
  414. /*
  415.  * argv_alloc --
  416.  *    Make more space for arguments.
  417.  */
  418. static int
  419. argv_alloc(sp, len)
  420.     SCR *sp;
  421.     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(sp)
  487.     SCR *sp;
  488. {
  489.     EX_PRIVATE *exp;
  490.     int off;
  491.  
  492.     exp = EXP(sp);
  493.     if (exp->args != NULL) {
  494.         for (off = 0; off < exp->argscnt; ++off) {
  495.             if (exp->args[off] == NULL)
  496.                 continue;
  497.             if (F_ISSET(exp->args[off], A_ALLOCATED))
  498.                 free(exp->args[off]->bp);
  499.             free(exp->args[off]);
  500.         }
  501.         free(exp->args);
  502.     }
  503.     exp->args = NULL;
  504.     exp->argscnt = 0;
  505.     exp->argsoff = 0;
  506.     return (0);
  507. }
  508.  
  509. /*
  510.  * argv_lexp --
  511.  *    Find all file names matching the prefix and append them to the
  512.  *    buffer.
  513.  */
  514. static int
  515. argv_lexp(sp, excp, path)
  516.     SCR *sp;
  517.     EXCMD *excp;
  518.     char *path;
  519. {
  520.     struct dirent *dp;
  521.     DIR *dirp;
  522.     EX_PRIVATE *exp;
  523.     int off;
  524.     size_t dlen, len, nlen;
  525.     char *dname, *name, *p;
  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.         p = exp->args[exp->argsoff]->bp;
  570.         if (dlen != 0) {
  571.             memcpy(p, dname, dlen);
  572.             p += dlen;
  573.             if (dlen > 1 || dname[0] != '/')
  574.                 *p++ = '/';
  575.         }
  576.         memcpy(p, dp->d_name, len + 1);
  577.         exp->args[exp->argsoff]->len = dlen + len + 1;
  578.         ++exp->argsoff;
  579.         excp->argv = exp->args;
  580.         excp->argc = exp->argsoff;
  581.     }
  582.     closedir(dirp);
  583.  
  584.     if (off == exp->argsoff) {
  585.         /*
  586.          * If we didn't find a match, complain that the expansion
  587.          * failed.  We can't know for certain that's the error, but
  588.          * it's a good guess, and it matches historic practice. 
  589.          */
  590.         msgq(sp, M_ERR, "304|Shell expansion failed");
  591.         return (1);
  592.     }
  593.     qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
  594.     return (0);
  595. }
  596.  
  597. /*
  598.  * argv_comp --
  599.  *    Alphabetic comparison.
  600.  */
  601. static int
  602. argv_comp(a, b)
  603.     const void *a, *b;
  604. {
  605.     return (strcmp((char *)(*(ARGS **)a)->bp, (char *)(*(ARGS **)b)->bp));
  606. }
  607.  
  608. /*
  609.  * argv_sexp --
  610.  *    Fork a shell, pipe a command through it, and read the output into
  611.  *    a buffer.
  612.  */
  613. static int
  614. argv_sexp(sp, bpp, blenp, lenp)
  615.     SCR *sp;
  616.     char **bpp;
  617.     size_t *blenp, *lenp;
  618. {
  619.     enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval;
  620.     FILE *ifp;
  621.     pid_t pid;
  622.     size_t blen, len;
  623.     int ch, std_output[2];
  624.     char *bp, *p, *sh, *sh_path;
  625.  
  626.     /* Secure means no shell access. */
  627.     if (O_ISSET(sp, O_SECURE)) {
  628.         msgq(sp, M_ERR,
  629. "289|Shell expansions not supported when the secure edit option is set");
  630.         return (1);
  631.     }
  632.  
  633.     sh_path = O_STR(sp, O_SHELL);
  634.     if ((sh = strrchr(sh_path, '/')) == NULL)
  635.         sh = sh_path;
  636.     else
  637.         ++sh;
  638.  
  639.     /* Local copies of the buffer variables. */
  640.     bp = *bpp;
  641.     blen = *blenp;
  642.  
  643.     /*
  644.      * There are two different processes running through this code, named
  645.      * the utility (the shell) and the parent. The utility reads standard
  646.      * input and writes standard output and standard error output.  The
  647.      * parent writes to the utility, reads its standard output and ignores
  648.      * its standard error output.  Historically, the standard error output
  649.      * was discarded by vi, as it produces a lot of noise when file patterns
  650.      * don't match.
  651.      *
  652.      * The parent reads std_output[0], and the utility writes std_output[1].
  653.      */
  654.     ifp = NULL;
  655.     std_output[0] = std_output[1] = -1;
  656.     if (pipe(std_output) < 0) {
  657.         msgq(sp, M_SYSERR, "pipe");
  658.         return (1);
  659.     }
  660.     if ((ifp = fdopen(std_output[0], "r")) == NULL) {
  661.         msgq(sp, M_SYSERR, "fdopen");
  662.         goto err;
  663.     }
  664.  
  665.     /*
  666.      * Do the minimal amount of work possible, the shell is going to run
  667.      * briefly and then exit.  We sincerely hope.
  668.      */
  669.     switch (pid = vfork()) {
  670.     case -1:            /* Error. */
  671.         msgq(sp, M_SYSERR, "vfork");
  672. err:        if (ifp != NULL)
  673.             (void)fclose(ifp);
  674.         else if (std_output[0] != -1)
  675.             close(std_output[0]);
  676.         if (std_output[1] != -1)
  677.             close(std_output[0]);
  678.         return (1);
  679.     case 0:                /* Utility. */
  680.         /* Redirect stdout to the write end of the pipe. */
  681.         (void)dup2(std_output[1], STDOUT_FILENO);
  682.  
  683.         /* Close the utility's file descriptors. */
  684.         (void)close(std_output[0]);
  685.         (void)close(std_output[1]);
  686.         (void)close(STDERR_FILENO);
  687.  
  688.         /*
  689.          * XXX
  690.          * Assume that all shells have -c.
  691.          */
  692.         execl(sh_path, sh, "-c", bp, NULL);
  693.         msgq_str(sp, M_SYSERR, sh_path, "118|Error: execl: %s");
  694.         _exit(127);
  695.     default:            /* Parent. */
  696.         /* Close the pipe ends the parent won't use. */
  697.         (void)close(std_output[1]);
  698.         break;
  699.     }
  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, ++len)
  711.         if (blen < 5) {
  712.             ADD_SPACE_GOTO(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. }
  757.