xref: /freebsd/contrib/nvi/ex/ex_argv.c (revision aa0a1e58)
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