1*0a6a1f1dSLionel Sambuc /* $NetBSD: ex_argv.c,v 1.3 2014/01/26 21:43:45 christos Exp $ */
284d9c625SLionel Sambuc /*-
384d9c625SLionel Sambuc * Copyright (c) 1993, 1994
484d9c625SLionel Sambuc * The Regents of the University of California. All rights reserved.
584d9c625SLionel Sambuc * Copyright (c) 1993, 1994, 1995, 1996
684d9c625SLionel Sambuc * Keith Bostic. All rights reserved.
784d9c625SLionel Sambuc *
884d9c625SLionel Sambuc * See the LICENSE file for redistribution information.
984d9c625SLionel Sambuc */
1084d9c625SLionel Sambuc
1184d9c625SLionel Sambuc #include "config.h"
1284d9c625SLionel Sambuc
13*0a6a1f1dSLionel Sambuc #include <sys/cdefs.h>
14*0a6a1f1dSLionel Sambuc #if 0
1584d9c625SLionel Sambuc #ifndef lint
1684d9c625SLionel Sambuc 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 ";
1784d9c625SLionel Sambuc #endif /* not lint */
18*0a6a1f1dSLionel Sambuc #else
19*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: ex_argv.c,v 1.3 2014/01/26 21:43:45 christos Exp $");
20*0a6a1f1dSLionel Sambuc #endif
2184d9c625SLionel Sambuc
2284d9c625SLionel Sambuc #include <sys/types.h>
2384d9c625SLionel Sambuc #include <sys/queue.h>
2484d9c625SLionel Sambuc
2584d9c625SLionel Sambuc #include <bitstring.h>
2684d9c625SLionel Sambuc #include <ctype.h>
2784d9c625SLionel Sambuc #include <dirent.h>
2884d9c625SLionel Sambuc #include <errno.h>
2984d9c625SLionel Sambuc #include <limits.h>
3084d9c625SLionel Sambuc #include <stdio.h>
3184d9c625SLionel Sambuc #include <stdlib.h>
3284d9c625SLionel Sambuc #include <string.h>
3384d9c625SLionel Sambuc #include <unistd.h>
3484d9c625SLionel Sambuc
3584d9c625SLionel Sambuc #include "../common/common.h"
3684d9c625SLionel Sambuc
3784d9c625SLionel Sambuc static int argv_alloc __P((SCR *, size_t));
3884d9c625SLionel Sambuc static int argv_comp __P((const void *, const void *));
3984d9c625SLionel Sambuc static int argv_fexp __P((SCR *, EXCMD *,
4084d9c625SLionel Sambuc const CHAR_T *, size_t, CHAR_T *, size_t *, CHAR_T **, size_t *, int));
4184d9c625SLionel Sambuc static int argv_lexp __P((SCR *, EXCMD *, const char *));
4284d9c625SLionel Sambuc static int argv_sexp __P((SCR *, CHAR_T **, size_t *, size_t *));
4384d9c625SLionel Sambuc
4484d9c625SLionel Sambuc /*
4584d9c625SLionel Sambuc * argv_init --
4684d9c625SLionel Sambuc * Build a prototype arguments list.
4784d9c625SLionel Sambuc *
4884d9c625SLionel Sambuc * PUBLIC: int argv_init __P((SCR *, EXCMD *));
4984d9c625SLionel Sambuc */
5084d9c625SLionel Sambuc int
argv_init(SCR * sp,EXCMD * excp)5184d9c625SLionel Sambuc argv_init(SCR *sp, EXCMD *excp)
5284d9c625SLionel Sambuc {
5384d9c625SLionel Sambuc EX_PRIVATE *exp;
5484d9c625SLionel Sambuc
5584d9c625SLionel Sambuc exp = EXP(sp);
5684d9c625SLionel Sambuc exp->argsoff = 0;
5784d9c625SLionel Sambuc argv_alloc(sp, 1);
5884d9c625SLionel Sambuc
5984d9c625SLionel Sambuc excp->argv = exp->args;
6084d9c625SLionel Sambuc excp->argc = exp->argsoff;
6184d9c625SLionel Sambuc return (0);
6284d9c625SLionel Sambuc }
6384d9c625SLionel Sambuc
6484d9c625SLionel Sambuc /*
6584d9c625SLionel Sambuc * argv_exp0 --
6684d9c625SLionel Sambuc * Append a string to the argument list.
6784d9c625SLionel Sambuc *
6884d9c625SLionel Sambuc * PUBLIC: int argv_exp0 __P((SCR *, EXCMD *, const CHAR_T *, size_t));
6984d9c625SLionel Sambuc */
7084d9c625SLionel Sambuc int
argv_exp0(SCR * sp,EXCMD * excp,const CHAR_T * cmd,size_t cmdlen)7184d9c625SLionel Sambuc argv_exp0(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen)
7284d9c625SLionel Sambuc {
7384d9c625SLionel Sambuc EX_PRIVATE *exp;
7484d9c625SLionel Sambuc
7584d9c625SLionel Sambuc exp = EXP(sp);
7684d9c625SLionel Sambuc argv_alloc(sp, cmdlen);
7784d9c625SLionel Sambuc MEMCPY(exp->args[exp->argsoff]->bp, cmd, cmdlen);
7884d9c625SLionel Sambuc exp->args[exp->argsoff]->bp[cmdlen] = '\0';
7984d9c625SLionel Sambuc exp->args[exp->argsoff]->len = cmdlen;
8084d9c625SLionel Sambuc ++exp->argsoff;
8184d9c625SLionel Sambuc excp->argv = exp->args;
8284d9c625SLionel Sambuc excp->argc = exp->argsoff;
8384d9c625SLionel Sambuc return (0);
8484d9c625SLionel Sambuc }
8584d9c625SLionel Sambuc
8684d9c625SLionel Sambuc /*
8784d9c625SLionel Sambuc * argv_exp1 --
8884d9c625SLionel Sambuc * Do file name expansion on a string, and append it to the
8984d9c625SLionel Sambuc * argument list.
9084d9c625SLionel Sambuc *
9184d9c625SLionel Sambuc * PUBLIC: int argv_exp1 __P((SCR *, EXCMD *, const CHAR_T *, size_t, int));
9284d9c625SLionel Sambuc */
9384d9c625SLionel Sambuc int
argv_exp1(SCR * sp,EXCMD * excp,const CHAR_T * cmd,size_t cmdlen,int is_bang)9484d9c625SLionel Sambuc argv_exp1(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen, int is_bang)
9584d9c625SLionel Sambuc {
9684d9c625SLionel Sambuc size_t blen, len;
9784d9c625SLionel Sambuc CHAR_T *p, *t, *bp;
9884d9c625SLionel Sambuc
9984d9c625SLionel Sambuc GET_SPACE_RETW(sp, bp, blen, 512);
10084d9c625SLionel Sambuc
10184d9c625SLionel Sambuc len = 0;
10284d9c625SLionel Sambuc if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) {
10384d9c625SLionel Sambuc FREE_SPACEW(sp, bp, blen);
10484d9c625SLionel Sambuc return (1);
10584d9c625SLionel Sambuc }
10684d9c625SLionel Sambuc
10784d9c625SLionel Sambuc /* If it's empty, we're done. */
10884d9c625SLionel Sambuc if (len != 0) {
10984d9c625SLionel Sambuc for (p = bp, t = bp + len; p < t; ++p)
11084d9c625SLionel Sambuc if (!ISBLANK(*p))
11184d9c625SLionel Sambuc break;
11284d9c625SLionel Sambuc if (p == t)
11384d9c625SLionel Sambuc goto ret;
11484d9c625SLionel Sambuc } else
11584d9c625SLionel Sambuc goto ret;
11684d9c625SLionel Sambuc
11784d9c625SLionel Sambuc (void)argv_exp0(sp, excp, bp, len);
11884d9c625SLionel Sambuc
11984d9c625SLionel Sambuc ret: FREE_SPACEW(sp, bp, blen);
12084d9c625SLionel Sambuc return (0);
12184d9c625SLionel Sambuc }
12284d9c625SLionel Sambuc
12384d9c625SLionel Sambuc /*
12484d9c625SLionel Sambuc * argv_exp2 --
12584d9c625SLionel Sambuc * Do file name and shell expansion on a string, and append it to
12684d9c625SLionel Sambuc * the argument list.
12784d9c625SLionel Sambuc *
12884d9c625SLionel Sambuc * PUBLIC: int argv_exp2 __P((SCR *, EXCMD *, const CHAR_T *, size_t));
12984d9c625SLionel Sambuc */
13084d9c625SLionel Sambuc int
argv_exp2(SCR * sp,EXCMD * excp,const CHAR_T * cmd,size_t cmdlen)13184d9c625SLionel Sambuc argv_exp2(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen)
13284d9c625SLionel Sambuc {
13384d9c625SLionel Sambuc size_t blen, len, n;
13484d9c625SLionel Sambuc int rval;
13584d9c625SLionel Sambuc CHAR_T *bp, *p;
13684d9c625SLionel Sambuc const char *mp, *np;
13784d9c625SLionel Sambuc
13884d9c625SLionel Sambuc GET_SPACE_RETW(sp, bp, blen, 512);
13984d9c625SLionel Sambuc
14084d9c625SLionel Sambuc #define SHELLECHO "echo "
14184d9c625SLionel Sambuc #define SHELLOFFSET (sizeof(SHELLECHO) - 1)
14284d9c625SLionel Sambuc p = bp;
14384d9c625SLionel Sambuc *p++ = 'e';
14484d9c625SLionel Sambuc *p++ = 'c';
14584d9c625SLionel Sambuc *p++ = 'h';
14684d9c625SLionel Sambuc *p++ = 'o';
14784d9c625SLionel Sambuc *p++ = ' ';
14884d9c625SLionel Sambuc len = SHELLOFFSET;
14984d9c625SLionel Sambuc
15084d9c625SLionel Sambuc #if defined(DEBUG) && 0
15184d9c625SLionel Sambuc vtrace(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd);
15284d9c625SLionel Sambuc #endif
15384d9c625SLionel Sambuc
15484d9c625SLionel Sambuc if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, 0)) {
15584d9c625SLionel Sambuc rval = 1;
15684d9c625SLionel Sambuc goto err;
15784d9c625SLionel Sambuc }
15884d9c625SLionel Sambuc
15984d9c625SLionel Sambuc #if defined(DEBUG) && 0
16084d9c625SLionel Sambuc vtrace(sp, "before shell: %d: {%s}\n", len, bp);
16184d9c625SLionel Sambuc #endif
16284d9c625SLionel Sambuc
16384d9c625SLionel Sambuc /*
16484d9c625SLionel Sambuc * Do shell word expansion -- it's very, very hard to figure out what
16584d9c625SLionel Sambuc * magic characters the user's shell expects. Historically, it was a
16684d9c625SLionel Sambuc * union of v7 shell and csh meta characters. We match that practice
16784d9c625SLionel Sambuc * by default, so ":read \%" tries to read a file named '%'. It would
16884d9c625SLionel Sambuc * make more sense to pass any special characters through the shell,
16984d9c625SLionel Sambuc * but then, if your shell was csh, the above example will behave
17084d9c625SLionel Sambuc * differently in nvi than in vi. If you want to get other characters
17184d9c625SLionel Sambuc * passed through to your shell, change the "meta" option.
17284d9c625SLionel Sambuc *
17384d9c625SLionel Sambuc * To avoid a function call per character, we do a first pass through
17484d9c625SLionel Sambuc * the meta characters looking for characters that aren't expected
17584d9c625SLionel Sambuc * to be there, and then we can ignore them in the user's argument.
17684d9c625SLionel Sambuc */
17784d9c625SLionel Sambuc if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1))
17884d9c625SLionel Sambuc n = 0;
17984d9c625SLionel Sambuc else {
18084d9c625SLionel Sambuc for (np = mp = O_STR(sp, O_SHELLMETA); *np != '\0'; ++np)
18184d9c625SLionel Sambuc if (ISBLANK((UCHAR_T)*np) ||
18284d9c625SLionel Sambuc ISALNUM((UCHAR_T)*np))
18384d9c625SLionel Sambuc break;
18484d9c625SLionel Sambuc p = bp + SHELLOFFSET;
18584d9c625SLionel Sambuc n = len - SHELLOFFSET;
18684d9c625SLionel Sambuc if (*p != '\0') {
18784d9c625SLionel Sambuc for (; n > 0; --n, ++p)
18884d9c625SLionel Sambuc if (strchr(mp, *p) != NULL)
18984d9c625SLionel Sambuc break;
19084d9c625SLionel Sambuc } else
19184d9c625SLionel Sambuc for (; n > 0; --n, ++p)
19284d9c625SLionel Sambuc if (!ISBLANK((UCHAR_T)*p) &&
19384d9c625SLionel Sambuc !ISALNUM((UCHAR_T)*p) &&
19484d9c625SLionel Sambuc strchr(mp, *p) != NULL)
19584d9c625SLionel Sambuc break;
19684d9c625SLionel Sambuc }
19784d9c625SLionel Sambuc
19884d9c625SLionel Sambuc /*
19984d9c625SLionel Sambuc * If we found a meta character in the string, fork a shell to expand
20084d9c625SLionel Sambuc * it. Unfortunately, this is comparatively slow. Historically, it
20184d9c625SLionel Sambuc * didn't matter much, since users don't enter meta characters as part
20284d9c625SLionel Sambuc * of pathnames that frequently. The addition of filename completion
20384d9c625SLionel Sambuc * broke that assumption because it's easy to use. As a result, lots
20484d9c625SLionel Sambuc * folks have complained that the expansion code is too slow. So, we
20584d9c625SLionel Sambuc * detect filename completion as a special case, and do it internally.
20684d9c625SLionel Sambuc * Note that this code assumes that the <asterisk> character is the
20784d9c625SLionel Sambuc * match-anything meta character. That feels safe -- if anyone writes
20884d9c625SLionel Sambuc * a shell that doesn't follow that convention, I'd suggest giving them
20984d9c625SLionel Sambuc * a festive hot-lead enema.
21084d9c625SLionel Sambuc */
21184d9c625SLionel Sambuc switch (n) {
21284d9c625SLionel Sambuc case 0:
21384d9c625SLionel Sambuc p = bp + SHELLOFFSET;
21484d9c625SLionel Sambuc len -= SHELLOFFSET;
21584d9c625SLionel Sambuc rval = argv_exp3(sp, excp, p, len);
21684d9c625SLionel Sambuc break;
21784d9c625SLionel Sambuc case 1:
21884d9c625SLionel Sambuc if (*p == '*') {
21984d9c625SLionel Sambuc const char *np1;
22084d9c625SLionel Sambuc char *d;
22184d9c625SLionel Sambuc size_t nlen;
22284d9c625SLionel Sambuc
22384d9c625SLionel Sambuc *p = '\0';
22484d9c625SLionel Sambuc INT2CHAR(sp, bp + SHELLOFFSET,
22584d9c625SLionel Sambuc STRLEN(bp + SHELLOFFSET) + 1, np1, nlen);
22684d9c625SLionel Sambuc d = strdup(np1);
22784d9c625SLionel Sambuc rval = argv_lexp(sp, excp, d);
22884d9c625SLionel Sambuc free (d);
22984d9c625SLionel Sambuc break;
23084d9c625SLionel Sambuc }
23184d9c625SLionel Sambuc /* FALLTHROUGH */
23284d9c625SLionel Sambuc default:
23384d9c625SLionel Sambuc if (argv_sexp(sp, &bp, &blen, &len)) {
23484d9c625SLionel Sambuc rval = 1;
23584d9c625SLionel Sambuc goto err;
23684d9c625SLionel Sambuc }
23784d9c625SLionel Sambuc p = bp;
23884d9c625SLionel Sambuc rval = argv_exp3(sp, excp, p, len);
23984d9c625SLionel Sambuc break;
24084d9c625SLionel Sambuc }
24184d9c625SLionel Sambuc
24284d9c625SLionel Sambuc err: FREE_SPACEW(sp, bp, blen);
24384d9c625SLionel Sambuc return (rval);
24484d9c625SLionel Sambuc }
24584d9c625SLionel Sambuc
24684d9c625SLionel Sambuc /*
24784d9c625SLionel Sambuc * argv_exp3 --
24884d9c625SLionel Sambuc * Take a string and break it up into an argv, which is appended
24984d9c625SLionel Sambuc * to the argument list.
25084d9c625SLionel Sambuc *
25184d9c625SLionel Sambuc * PUBLIC: int argv_exp3 __P((SCR *, EXCMD *, const CHAR_T *, size_t));
25284d9c625SLionel Sambuc */
25384d9c625SLionel Sambuc int
argv_exp3(SCR * sp,EXCMD * excp,const CHAR_T * cmd,size_t cmdlen)25484d9c625SLionel Sambuc argv_exp3(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen)
25584d9c625SLionel Sambuc {
25684d9c625SLionel Sambuc EX_PRIVATE *exp;
25784d9c625SLionel Sambuc size_t len;
25884d9c625SLionel Sambuc ARG_CHAR_T ch;
25984d9c625SLionel Sambuc int off;
26084d9c625SLionel Sambuc const CHAR_T *ap;
26184d9c625SLionel Sambuc CHAR_T *p;
26284d9c625SLionel Sambuc
26384d9c625SLionel Sambuc for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) {
26484d9c625SLionel Sambuc /* Skip any leading whitespace. */
26584d9c625SLionel Sambuc for (; cmdlen > 0; --cmdlen, ++cmd) {
26684d9c625SLionel Sambuc ch = (UCHAR_T)*cmd;
26784d9c625SLionel Sambuc if (!ISBLANK(ch))
26884d9c625SLionel Sambuc break;
26984d9c625SLionel Sambuc }
27084d9c625SLionel Sambuc if (cmdlen == 0)
27184d9c625SLionel Sambuc break;
27284d9c625SLionel Sambuc
27384d9c625SLionel Sambuc /*
27484d9c625SLionel Sambuc * Determine the length of this whitespace delimited
27584d9c625SLionel Sambuc * argument.
27684d9c625SLionel Sambuc *
27784d9c625SLionel Sambuc * QUOTING NOTE:
27884d9c625SLionel Sambuc *
27984d9c625SLionel Sambuc * Skip any character preceded by the user's quoting
28084d9c625SLionel Sambuc * character.
28184d9c625SLionel Sambuc */
28284d9c625SLionel Sambuc for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) {
28384d9c625SLionel Sambuc ch = (UCHAR_T)*cmd;
28484d9c625SLionel Sambuc if (IS_ESCAPE(sp, excp, ch) && cmdlen > 1) {
28584d9c625SLionel Sambuc ++cmd;
28684d9c625SLionel Sambuc --cmdlen;
28784d9c625SLionel Sambuc } else if (ISBLANK(ch))
28884d9c625SLionel Sambuc break;
28984d9c625SLionel Sambuc }
29084d9c625SLionel Sambuc
29184d9c625SLionel Sambuc /*
29284d9c625SLionel Sambuc * Copy the argument into place.
29384d9c625SLionel Sambuc *
29484d9c625SLionel Sambuc * QUOTING NOTE:
29584d9c625SLionel Sambuc *
29684d9c625SLionel Sambuc * Lose quote chars.
29784d9c625SLionel Sambuc */
29884d9c625SLionel Sambuc argv_alloc(sp, len);
29984d9c625SLionel Sambuc off = exp->argsoff;
30084d9c625SLionel Sambuc exp->args[off]->len = len;
30184d9c625SLionel Sambuc for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++)
30284d9c625SLionel Sambuc if (IS_ESCAPE(sp, excp, *ap))
30384d9c625SLionel Sambuc ++ap;
30484d9c625SLionel Sambuc *p = '\0';
30584d9c625SLionel Sambuc }
30684d9c625SLionel Sambuc excp->argv = exp->args;
30784d9c625SLionel Sambuc excp->argc = exp->argsoff;
30884d9c625SLionel Sambuc
30984d9c625SLionel Sambuc #if defined(DEBUG) && 0
31084d9c625SLionel Sambuc for (cnt = 0; cnt < exp->argsoff; ++cnt)
31184d9c625SLionel Sambuc vtrace(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]);
31284d9c625SLionel Sambuc #endif
31384d9c625SLionel Sambuc return (0);
31484d9c625SLionel Sambuc }
31584d9c625SLionel Sambuc
31684d9c625SLionel Sambuc /*
31784d9c625SLionel Sambuc * argv_fexp --
31884d9c625SLionel Sambuc * Do file name and bang command expansion.
31984d9c625SLionel Sambuc */
32084d9c625SLionel Sambuc static int
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)32184d9c625SLionel Sambuc 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)
32284d9c625SLionel Sambuc {
32384d9c625SLionel Sambuc EX_PRIVATE *exp;
32484d9c625SLionel Sambuc char *t;
32584d9c625SLionel Sambuc size_t blen, len, off, tlen;
32684d9c625SLionel Sambuc CHAR_T *bp;
32784d9c625SLionel Sambuc const CHAR_T *wp;
32884d9c625SLionel Sambuc size_t wlen;
32984d9c625SLionel Sambuc
33084d9c625SLionel Sambuc /* Replace file name characters. */
33184d9c625SLionel Sambuc for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd)
33284d9c625SLionel Sambuc switch (*cmd) {
33384d9c625SLionel Sambuc case '!':
33484d9c625SLionel Sambuc if (!is_bang)
33584d9c625SLionel Sambuc goto ins_ch;
33684d9c625SLionel Sambuc exp = EXP(sp);
33784d9c625SLionel Sambuc if (exp->lastbcomm == NULL) {
33884d9c625SLionel Sambuc msgq(sp, M_ERR,
33984d9c625SLionel Sambuc "115|No previous command to replace \"!\"");
34084d9c625SLionel Sambuc return (1);
34184d9c625SLionel Sambuc }
34284d9c625SLionel Sambuc len += tlen = STRLEN(exp->lastbcomm);
34384d9c625SLionel Sambuc off = p - bp;
34484d9c625SLionel Sambuc ADD_SPACE_RETW(sp, bp, blen, len);
34584d9c625SLionel Sambuc p = bp + off;
34684d9c625SLionel Sambuc MEMCPY(p, exp->lastbcomm, tlen);
34784d9c625SLionel Sambuc p += tlen;
34884d9c625SLionel Sambuc F_SET(excp, E_MODIFY);
34984d9c625SLionel Sambuc break;
35084d9c625SLionel Sambuc case '%':
35184d9c625SLionel Sambuc if ((t = sp->frp->name) == NULL) {
35284d9c625SLionel Sambuc msgq(sp, M_ERR,
35384d9c625SLionel Sambuc "116|No filename to substitute for %%");
35484d9c625SLionel Sambuc return (1);
35584d9c625SLionel Sambuc }
35684d9c625SLionel Sambuc tlen = strlen(t);
35784d9c625SLionel Sambuc len += tlen;
35884d9c625SLionel Sambuc off = p - bp;
35984d9c625SLionel Sambuc ADD_SPACE_RETW(sp, bp, blen, len);
36084d9c625SLionel Sambuc p = bp + off;
36184d9c625SLionel Sambuc CHAR2INT(sp, t, tlen, wp, wlen);
36284d9c625SLionel Sambuc MEMCPY(p, wp, wlen);
36384d9c625SLionel Sambuc p += wlen;
36484d9c625SLionel Sambuc F_SET(excp, E_MODIFY);
36584d9c625SLionel Sambuc break;
36684d9c625SLionel Sambuc case '#':
36784d9c625SLionel Sambuc if ((t = sp->alt_name) == NULL) {
36884d9c625SLionel Sambuc msgq(sp, M_ERR,
36984d9c625SLionel Sambuc "117|No filename to substitute for #");
37084d9c625SLionel Sambuc return (1);
37184d9c625SLionel Sambuc }
37284d9c625SLionel Sambuc len += tlen = strlen(t);
37384d9c625SLionel Sambuc off = p - bp;
37484d9c625SLionel Sambuc ADD_SPACE_RETW(sp, bp, blen, len);
37584d9c625SLionel Sambuc p = bp + off;
37684d9c625SLionel Sambuc CHAR2INT(sp, t, tlen, wp, wlen);
37784d9c625SLionel Sambuc MEMCPY(p, wp, wlen);
37884d9c625SLionel Sambuc p += tlen;
37984d9c625SLionel Sambuc F_SET(excp, E_MODIFY);
38084d9c625SLionel Sambuc break;
38184d9c625SLionel Sambuc case '\\':
38284d9c625SLionel Sambuc /*
38384d9c625SLionel Sambuc * QUOTING NOTE:
38484d9c625SLionel Sambuc *
38584d9c625SLionel Sambuc * Strip any backslashes that protected the file
38684d9c625SLionel Sambuc * expansion characters.
38784d9c625SLionel Sambuc */
38884d9c625SLionel Sambuc if (cmdlen > 1 &&
38984d9c625SLionel Sambuc (cmd[1] == '%' || cmd[1] == '#' || cmd[1] == '!')) {
39084d9c625SLionel Sambuc ++cmd;
39184d9c625SLionel Sambuc --cmdlen;
39284d9c625SLionel Sambuc }
39384d9c625SLionel Sambuc /* FALLTHROUGH */
39484d9c625SLionel Sambuc default:
39584d9c625SLionel Sambuc ins_ch: ++len;
39684d9c625SLionel Sambuc off = p - bp;
39784d9c625SLionel Sambuc ADD_SPACE_RETW(sp, bp, blen, len);
39884d9c625SLionel Sambuc p = bp + off;
39984d9c625SLionel Sambuc *p++ = *cmd;
40084d9c625SLionel Sambuc }
40184d9c625SLionel Sambuc
40284d9c625SLionel Sambuc /* Nul termination. */
40384d9c625SLionel Sambuc ++len;
40484d9c625SLionel Sambuc off = p - bp;
40584d9c625SLionel Sambuc ADD_SPACE_RETW(sp, bp, blen, len);
40684d9c625SLionel Sambuc p = bp + off;
40784d9c625SLionel Sambuc *p = '\0';
40884d9c625SLionel Sambuc
40984d9c625SLionel Sambuc /* Return the new string length, buffer, buffer length. */
41084d9c625SLionel Sambuc *lenp = len - 1;
41184d9c625SLionel Sambuc *bpp = bp;
41284d9c625SLionel Sambuc *blenp = blen;
41384d9c625SLionel Sambuc return (0);
41484d9c625SLionel Sambuc }
41584d9c625SLionel Sambuc
41684d9c625SLionel Sambuc /*
41784d9c625SLionel Sambuc * argv_alloc --
41884d9c625SLionel Sambuc * Make more space for arguments.
41984d9c625SLionel Sambuc */
42084d9c625SLionel Sambuc static int
argv_alloc(SCR * sp,size_t len)42184d9c625SLionel Sambuc argv_alloc(SCR *sp, size_t len)
42284d9c625SLionel Sambuc {
42384d9c625SLionel Sambuc ARGS *ap;
42484d9c625SLionel Sambuc EX_PRIVATE *exp;
42584d9c625SLionel Sambuc int cnt, off;
42684d9c625SLionel Sambuc
42784d9c625SLionel Sambuc /*
42884d9c625SLionel Sambuc * Allocate room for another argument, always leaving
42984d9c625SLionel Sambuc * enough room for an ARGS structure with a length of 0.
43084d9c625SLionel Sambuc */
43184d9c625SLionel Sambuc #define INCREMENT 20
43284d9c625SLionel Sambuc exp = EXP(sp);
43384d9c625SLionel Sambuc off = exp->argsoff;
43484d9c625SLionel Sambuc if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) {
43584d9c625SLionel Sambuc cnt = exp->argscnt + INCREMENT;
43684d9c625SLionel Sambuc REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *));
43784d9c625SLionel Sambuc if (exp->args == NULL) {
43884d9c625SLionel Sambuc (void)argv_free(sp);
43984d9c625SLionel Sambuc goto mem;
44084d9c625SLionel Sambuc }
44184d9c625SLionel Sambuc memset(&exp->args[exp->argscnt], 0, INCREMENT * sizeof(ARGS *));
44284d9c625SLionel Sambuc exp->argscnt = cnt;
44384d9c625SLionel Sambuc }
44484d9c625SLionel Sambuc
44584d9c625SLionel Sambuc /* First argument. */
44684d9c625SLionel Sambuc if (exp->args[off] == NULL) {
44784d9c625SLionel Sambuc CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
44884d9c625SLionel Sambuc if (exp->args[off] == NULL)
44984d9c625SLionel Sambuc goto mem;
45084d9c625SLionel Sambuc }
45184d9c625SLionel Sambuc
45284d9c625SLionel Sambuc /* First argument buffer. */
45384d9c625SLionel Sambuc ap = exp->args[off];
45484d9c625SLionel Sambuc ap->len = 0;
45584d9c625SLionel Sambuc if (ap->blen < len + 1) {
45684d9c625SLionel Sambuc ap->blen = len + 1;
45784d9c625SLionel Sambuc REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T));
45884d9c625SLionel Sambuc if (ap->bp == NULL) {
45984d9c625SLionel Sambuc ap->bp = NULL;
46084d9c625SLionel Sambuc ap->blen = 0;
46184d9c625SLionel Sambuc F_CLR(ap, A_ALLOCATED);
46284d9c625SLionel Sambuc mem: msgq(sp, M_SYSERR, NULL);
46384d9c625SLionel Sambuc return (1);
46484d9c625SLionel Sambuc }
46584d9c625SLionel Sambuc F_SET(ap, A_ALLOCATED);
46684d9c625SLionel Sambuc }
46784d9c625SLionel Sambuc
46884d9c625SLionel Sambuc /* Second argument. */
46984d9c625SLionel Sambuc if (exp->args[++off] == NULL) {
47084d9c625SLionel Sambuc CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
47184d9c625SLionel Sambuc if (exp->args[off] == NULL)
47284d9c625SLionel Sambuc goto mem;
47384d9c625SLionel Sambuc }
47484d9c625SLionel Sambuc /* 0 length serves as end-of-argument marker. */
47584d9c625SLionel Sambuc exp->args[off]->len = 0;
47684d9c625SLionel Sambuc return (0);
47784d9c625SLionel Sambuc }
47884d9c625SLionel Sambuc
47984d9c625SLionel Sambuc /*
48084d9c625SLionel Sambuc * argv_free --
48184d9c625SLionel Sambuc * Free up argument structures.
48284d9c625SLionel Sambuc *
48384d9c625SLionel Sambuc * PUBLIC: int argv_free __P((SCR *));
48484d9c625SLionel Sambuc */
48584d9c625SLionel Sambuc int
argv_free(SCR * sp)48684d9c625SLionel Sambuc argv_free(SCR *sp)
48784d9c625SLionel Sambuc {
48884d9c625SLionel Sambuc EX_PRIVATE *exp;
48984d9c625SLionel Sambuc int off;
49084d9c625SLionel Sambuc
49184d9c625SLionel Sambuc exp = EXP(sp);
49284d9c625SLionel Sambuc if (exp->args != NULL) {
49384d9c625SLionel Sambuc for (off = 0; off < exp->argscnt; ++off) {
49484d9c625SLionel Sambuc if (exp->args[off] == NULL)
49584d9c625SLionel Sambuc continue;
49684d9c625SLionel Sambuc if (F_ISSET(exp->args[off], A_ALLOCATED))
49784d9c625SLionel Sambuc free(exp->args[off]->bp);
49884d9c625SLionel Sambuc free(exp->args[off]);
49984d9c625SLionel Sambuc }
50084d9c625SLionel Sambuc free(exp->args);
50184d9c625SLionel Sambuc }
50284d9c625SLionel Sambuc exp->args = NULL;
50384d9c625SLionel Sambuc exp->argscnt = 0;
50484d9c625SLionel Sambuc exp->argsoff = 0;
50584d9c625SLionel Sambuc return (0);
50684d9c625SLionel Sambuc }
50784d9c625SLionel Sambuc
50884d9c625SLionel Sambuc /*
50984d9c625SLionel Sambuc * argv_lexp --
51084d9c625SLionel Sambuc * Find all file names matching the prefix and append them to the
51184d9c625SLionel Sambuc * buffer.
51284d9c625SLionel Sambuc */
51384d9c625SLionel Sambuc static int
argv_lexp(SCR * sp,EXCMD * excp,const char * path)51484d9c625SLionel Sambuc argv_lexp(SCR *sp, EXCMD *excp, const char *path)
51584d9c625SLionel Sambuc {
51684d9c625SLionel Sambuc struct dirent *dp;
51784d9c625SLionel Sambuc DIR *dirp;
51884d9c625SLionel Sambuc EX_PRIVATE *exp;
51984d9c625SLionel Sambuc int off;
52084d9c625SLionel Sambuc size_t dlen, len, nlen;
52184d9c625SLionel Sambuc const char *dname, *name;
52284d9c625SLionel Sambuc char *p;
52384d9c625SLionel Sambuc size_t wlen;
52484d9c625SLionel Sambuc const CHAR_T *wp;
52584d9c625SLionel Sambuc CHAR_T *n;
52684d9c625SLionel Sambuc
52784d9c625SLionel Sambuc exp = EXP(sp);
52884d9c625SLionel Sambuc
52984d9c625SLionel Sambuc /* Set up the name and length for comparison. */
53084d9c625SLionel Sambuc if ((p = strrchr(path, '/')) == NULL) {
53184d9c625SLionel Sambuc dname = ".";
53284d9c625SLionel Sambuc dlen = 0;
53384d9c625SLionel Sambuc name = path;
53484d9c625SLionel Sambuc } else {
53584d9c625SLionel Sambuc if (p == path) {
53684d9c625SLionel Sambuc dname = "/";
53784d9c625SLionel Sambuc dlen = 1;
53884d9c625SLionel Sambuc } else {
53984d9c625SLionel Sambuc *p = '\0';
54084d9c625SLionel Sambuc dname = path;
54184d9c625SLionel Sambuc dlen = strlen(path);
54284d9c625SLionel Sambuc }
54384d9c625SLionel Sambuc name = p + 1;
54484d9c625SLionel Sambuc }
54584d9c625SLionel Sambuc nlen = strlen(name);
54684d9c625SLionel Sambuc
54784d9c625SLionel Sambuc /*
54884d9c625SLionel Sambuc * XXX
54984d9c625SLionel Sambuc * We don't use the d_namlen field, it's not portable enough; we
55084d9c625SLionel Sambuc * assume that d_name is nul terminated, instead.
55184d9c625SLionel Sambuc */
55284d9c625SLionel Sambuc if ((dirp = opendir(dname)) == NULL) {
55384d9c625SLionel Sambuc msgq_str(sp, M_SYSERR, dname, "%s");
55484d9c625SLionel Sambuc return (1);
55584d9c625SLionel Sambuc }
55684d9c625SLionel Sambuc for (off = exp->argsoff; (dp = readdir(dirp)) != NULL;) {
55784d9c625SLionel Sambuc if (nlen == 0) {
55884d9c625SLionel Sambuc if (dp->d_name[0] == '.')
55984d9c625SLionel Sambuc continue;
56084d9c625SLionel Sambuc len = strlen(dp->d_name);
56184d9c625SLionel Sambuc } else {
56284d9c625SLionel Sambuc len = strlen(dp->d_name);
56384d9c625SLionel Sambuc if (len < nlen || memcmp(dp->d_name, name, nlen))
56484d9c625SLionel Sambuc continue;
56584d9c625SLionel Sambuc }
56684d9c625SLionel Sambuc
56784d9c625SLionel Sambuc /* Directory + name + slash + null. */
56884d9c625SLionel Sambuc argv_alloc(sp, dlen + len + 2);
56984d9c625SLionel Sambuc n = exp->args[exp->argsoff]->bp;
57084d9c625SLionel Sambuc if (dlen != 0) {
57184d9c625SLionel Sambuc CHAR2INT(sp, dname, dlen, wp, wlen);
57284d9c625SLionel Sambuc MEMCPY(n, wp, wlen);
57384d9c625SLionel Sambuc n += dlen;
57484d9c625SLionel Sambuc if (dlen > 1 || dname[0] != '/')
57584d9c625SLionel Sambuc *n++ = '/';
57684d9c625SLionel Sambuc }
57784d9c625SLionel Sambuc CHAR2INT(sp, dp->d_name, len + 1, wp, wlen);
57884d9c625SLionel Sambuc MEMCPY(n, wp, wlen);
57984d9c625SLionel Sambuc exp->args[exp->argsoff]->len = dlen + len + 1;
58084d9c625SLionel Sambuc ++exp->argsoff;
58184d9c625SLionel Sambuc excp->argv = exp->args;
58284d9c625SLionel Sambuc excp->argc = exp->argsoff;
58384d9c625SLionel Sambuc }
58484d9c625SLionel Sambuc closedir(dirp);
58584d9c625SLionel Sambuc
58684d9c625SLionel Sambuc if (off == exp->argsoff) {
58784d9c625SLionel Sambuc /*
58884d9c625SLionel Sambuc * If we didn't find a match, complain that the expansion
58984d9c625SLionel Sambuc * failed. We can't know for certain that's the error, but
59084d9c625SLionel Sambuc * it's a good guess, and it matches historic practice.
59184d9c625SLionel Sambuc */
59284d9c625SLionel Sambuc msgq(sp, M_ERR, "304|Shell expansion failed");
59384d9c625SLionel Sambuc return (1);
59484d9c625SLionel Sambuc }
59584d9c625SLionel Sambuc qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
59684d9c625SLionel Sambuc return (0);
59784d9c625SLionel Sambuc }
59884d9c625SLionel Sambuc
59984d9c625SLionel Sambuc /*
60084d9c625SLionel Sambuc * argv_comp --
60184d9c625SLionel Sambuc * Alphabetic comparison.
60284d9c625SLionel Sambuc */
60384d9c625SLionel Sambuc static int
argv_comp(const void * a,const void * b)60484d9c625SLionel Sambuc argv_comp(const void *a, const void *b)
60584d9c625SLionel Sambuc {
60684d9c625SLionel Sambuc return (STRCMP((*(const ARGS * const*)a)->bp, (*(const ARGS * const*)b)->bp));
60784d9c625SLionel Sambuc }
60884d9c625SLionel Sambuc
60984d9c625SLionel Sambuc static pid_t
runcmd(SCR * sp,const char * sh_path,const char * sh,const char * np,int * std_output)61084d9c625SLionel Sambuc runcmd(SCR *sp, const char *sh_path, const char *sh, const char *np,
61184d9c625SLionel Sambuc int *std_output)
61284d9c625SLionel Sambuc {
61384d9c625SLionel Sambuc pid_t pid;
61484d9c625SLionel Sambuc /*
61584d9c625SLionel Sambuc * Do the minimal amount of work possible, the shell is going to run
61684d9c625SLionel Sambuc * briefly and then exit. We sincerely hope.
61784d9c625SLionel Sambuc */
61884d9c625SLionel Sambuc switch (pid = vfork()) {
61984d9c625SLionel Sambuc case -1: /* Error. */
62084d9c625SLionel Sambuc msgq(sp, M_SYSERR, "vfork");
62184d9c625SLionel Sambuc return (pid_t)-1;
62284d9c625SLionel Sambuc case 0: /* Utility. */
62384d9c625SLionel Sambuc /* Redirect stdout to the write end of the pipe. */
62484d9c625SLionel Sambuc (void)dup2(std_output[1], STDOUT_FILENO);
62584d9c625SLionel Sambuc
62684d9c625SLionel Sambuc /* Close the utility's file descriptors. */
62784d9c625SLionel Sambuc (void)close(std_output[0]);
62884d9c625SLionel Sambuc (void)close(std_output[1]);
62984d9c625SLionel Sambuc (void)close(STDERR_FILENO);
63084d9c625SLionel Sambuc
63184d9c625SLionel Sambuc /*
63284d9c625SLionel Sambuc * XXX
63384d9c625SLionel Sambuc * Assume that all shells have -c.
63484d9c625SLionel Sambuc */
63584d9c625SLionel Sambuc execl(sh_path, sh, "-c", np, (char *)NULL);
63684d9c625SLionel Sambuc msgq_str(sp, M_SYSERR, sh_path, "118|Error: execl: %s");
63784d9c625SLionel Sambuc _exit(127);
63884d9c625SLionel Sambuc default: /* Parent. */
63984d9c625SLionel Sambuc /* Close the pipe ends the parent won't use. */
64084d9c625SLionel Sambuc (void)close(std_output[1]);
64184d9c625SLionel Sambuc return pid;
64284d9c625SLionel Sambuc }
64384d9c625SLionel Sambuc }
64484d9c625SLionel Sambuc
64584d9c625SLionel Sambuc /*
64684d9c625SLionel Sambuc * argv_sexp --
64784d9c625SLionel Sambuc * Fork a shell, pipe a command through it, and read the output into
64884d9c625SLionel Sambuc * a buffer.
64984d9c625SLionel Sambuc */
65084d9c625SLionel Sambuc static int
argv_sexp(SCR * sp,CHAR_T ** bpp,size_t * blenp,size_t * lenp)65184d9c625SLionel Sambuc argv_sexp(SCR *sp, CHAR_T **bpp, size_t *blenp, size_t *lenp)
65284d9c625SLionel Sambuc {
65384d9c625SLionel Sambuc enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval;
65484d9c625SLionel Sambuc FILE *ifp;
65584d9c625SLionel Sambuc pid_t pid;
65684d9c625SLionel Sambuc size_t blen, len;
65784d9c625SLionel Sambuc int ch, std_output[2];
65884d9c625SLionel Sambuc CHAR_T *bp, *p;
65984d9c625SLionel Sambuc const char *sh, *sh_path;
66084d9c625SLionel Sambuc const char *np;
66184d9c625SLionel Sambuc size_t nlen;
66284d9c625SLionel Sambuc
66384d9c625SLionel Sambuc /* Secure means no shell access. */
66484d9c625SLionel Sambuc if (O_ISSET(sp, O_SECURE)) {
66584d9c625SLionel Sambuc msgq(sp, M_ERR,
66684d9c625SLionel Sambuc "289|Shell expansions not supported when the secure edit option is set");
66784d9c625SLionel Sambuc return (1);
66884d9c625SLionel Sambuc }
66984d9c625SLionel Sambuc
67084d9c625SLionel Sambuc sh_path = O_STR(sp, O_SHELL);
67184d9c625SLionel Sambuc if ((sh = strrchr(sh_path, '/')) == NULL)
67284d9c625SLionel Sambuc sh = sh_path;
67384d9c625SLionel Sambuc else
67484d9c625SLionel Sambuc ++sh;
67584d9c625SLionel Sambuc
67684d9c625SLionel Sambuc /* Local copies of the buffer variables. */
67784d9c625SLionel Sambuc bp = *bpp;
67884d9c625SLionel Sambuc blen = *blenp;
67984d9c625SLionel Sambuc
68084d9c625SLionel Sambuc /*
68184d9c625SLionel Sambuc * There are two different processes running through this code, named
68284d9c625SLionel Sambuc * the utility (the shell) and the parent. The utility reads standard
68384d9c625SLionel Sambuc * input and writes standard output and standard error output. The
68484d9c625SLionel Sambuc * parent writes to the utility, reads its standard output and ignores
68584d9c625SLionel Sambuc * its standard error output. Historically, the standard error output
68684d9c625SLionel Sambuc * was discarded by vi, as it produces a lot of noise when file patterns
68784d9c625SLionel Sambuc * don't match.
68884d9c625SLionel Sambuc *
68984d9c625SLionel Sambuc * The parent reads std_output[0], and the utility writes std_output[1].
69084d9c625SLionel Sambuc */
69184d9c625SLionel Sambuc ifp = NULL;
69284d9c625SLionel Sambuc std_output[0] = std_output[1] = -1;
69384d9c625SLionel Sambuc if (pipe(std_output) < 0) {
69484d9c625SLionel Sambuc msgq(sp, M_SYSERR, "pipe");
69584d9c625SLionel Sambuc return (1);
69684d9c625SLionel Sambuc }
69784d9c625SLionel Sambuc if ((ifp = fdopen(std_output[0], "r")) == NULL) {
69884d9c625SLionel Sambuc msgq(sp, M_SYSERR, "fdopen");
69984d9c625SLionel Sambuc goto err;
70084d9c625SLionel Sambuc }
70184d9c625SLionel Sambuc INT2CHAR(sp, bp, STRLEN(bp)+1, np, nlen);
70284d9c625SLionel Sambuc pid = runcmd(sp, sh_path, sh, np, std_output);
70384d9c625SLionel Sambuc if (pid == -1)
70484d9c625SLionel Sambuc goto err;
70584d9c625SLionel Sambuc
70684d9c625SLionel Sambuc /*
70784d9c625SLionel Sambuc * Copy process standard output into a buffer.
70884d9c625SLionel Sambuc *
70984d9c625SLionel Sambuc * !!!
71084d9c625SLionel Sambuc * Historic vi apparently discarded leading \n and \r's from
71184d9c625SLionel Sambuc * the shell output stream. We don't on the grounds that any
71284d9c625SLionel Sambuc * shell that does that is broken.
71384d9c625SLionel Sambuc */
71484d9c625SLionel Sambuc for (p = bp, len = 0, ch = EOF;
71584d9c625SLionel Sambuc (ch = getc(ifp)) != EOF; *p++ = ch, blen-=sizeof(CHAR_T), ++len)
71684d9c625SLionel Sambuc if (blen < 5) {
71784d9c625SLionel Sambuc ADD_SPACE_GOTOW(sp, bp, *blenp, *blenp * 2);
71884d9c625SLionel Sambuc p = bp + len;
71984d9c625SLionel Sambuc blen = *blenp - len;
72084d9c625SLionel Sambuc }
72184d9c625SLionel Sambuc
72284d9c625SLionel Sambuc /* Delete the final newline, nul terminate the string. */
72384d9c625SLionel Sambuc if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) {
72484d9c625SLionel Sambuc --p;
72584d9c625SLionel Sambuc --len;
72684d9c625SLionel Sambuc }
72784d9c625SLionel Sambuc *p = '\0';
72884d9c625SLionel Sambuc *lenp = len;
72984d9c625SLionel Sambuc *bpp = bp; /* *blenp is already updated. */
73084d9c625SLionel Sambuc
73184d9c625SLionel Sambuc if (ferror(ifp))
73284d9c625SLionel Sambuc goto ioerr;
73384d9c625SLionel Sambuc if (fclose(ifp)) {
73484d9c625SLionel Sambuc ioerr: msgq_str(sp, M_ERR, sh, "119|I/O error: %s");
73584d9c625SLionel Sambuc alloc_err: rval = SEXP_ERR;
73684d9c625SLionel Sambuc } else
73784d9c625SLionel Sambuc rval = SEXP_OK;
73884d9c625SLionel Sambuc
73984d9c625SLionel Sambuc /*
74084d9c625SLionel Sambuc * Wait for the process. If the shell process fails (e.g., "echo $q"
74184d9c625SLionel Sambuc * where q wasn't a defined variable) or if the returned string has
74284d9c625SLionel Sambuc * no characters or only blank characters, (e.g., "echo $5"), complain
74384d9c625SLionel Sambuc * that the shell expansion failed. We can't know for certain that's
74484d9c625SLionel Sambuc * the error, but it's a good guess, and it matches historic practice.
74584d9c625SLionel Sambuc * This won't catch "echo foo_$5", but that's not a common error and
74684d9c625SLionel Sambuc * historic vi didn't catch it either.
74784d9c625SLionel Sambuc */
74884d9c625SLionel Sambuc if (proc_wait(sp, (long)pid, sh, 1, 0))
74984d9c625SLionel Sambuc rval = SEXP_EXPANSION_ERR;
75084d9c625SLionel Sambuc
75184d9c625SLionel Sambuc for (p = bp; len; ++p, --len)
75284d9c625SLionel Sambuc if (!ISBLANK(*p))
75384d9c625SLionel Sambuc break;
75484d9c625SLionel Sambuc if (len == 0)
75584d9c625SLionel Sambuc rval = SEXP_EXPANSION_ERR;
75684d9c625SLionel Sambuc
75784d9c625SLionel Sambuc if (rval == SEXP_EXPANSION_ERR)
75884d9c625SLionel Sambuc msgq(sp, M_ERR, "304|Shell expansion failed");
75984d9c625SLionel Sambuc
76084d9c625SLionel Sambuc return (rval == SEXP_OK ? 0 : 1);
76184d9c625SLionel Sambuc err: if (ifp != NULL)
76284d9c625SLionel Sambuc (void)fclose(ifp);
76384d9c625SLionel Sambuc else if (std_output[0] != -1)
76484d9c625SLionel Sambuc close(std_output[0]);
76584d9c625SLionel Sambuc if (std_output[1] != -1)
76684d9c625SLionel Sambuc close(std_output[0]);
76784d9c625SLionel Sambuc return 1;
76884d9c625SLionel Sambuc }
769