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