xref: /openbsd/usr.bin/vi/ex/ex_argv.c (revision 721c3ea3)
1 /*	$OpenBSD: ex_argv.c,v 1.20 2016/05/27 09:18:12 martijn 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 #include <sys/types.h>
15 #include <sys/queue.h>
16 
17 #include <bitstring.h>
18 #include <ctype.h>
19 #include <dirent.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include "../common/common.h"
28 
29 static int argv_alloc(SCR *, size_t);
30 static int argv_comp(const void *, const void *);
31 static int argv_fexp(SCR *, EXCMD *,
32 	char *, size_t, char *, size_t *, char **, size_t *, int);
33 static int argv_lexp(SCR *, EXCMD *, char *);
34 static int argv_sexp(SCR *, char **, size_t *, size_t *);
35 
36 /*
37  * argv_init --
38  *	Build  a prototype arguments list.
39  *
40  * PUBLIC: int argv_init(SCR *, EXCMD *);
41  */
42 int
argv_init(SCR * sp,EXCMD * excp)43 argv_init(SCR *sp, EXCMD *excp)
44 {
45 	EX_PRIVATE *exp;
46 
47 	exp = EXP(sp);
48 	exp->argsoff = 0;
49 	argv_alloc(sp, 1);
50 
51 	excp->argv = exp->args;
52 	excp->argc = exp->argsoff;
53 	return (0);
54 }
55 
56 /*
57  * argv_exp0 --
58  *	Append a string to the argument list.
59  *
60  * PUBLIC: int argv_exp0(SCR *, EXCMD *, char *, size_t);
61  */
62 int
argv_exp0(SCR * sp,EXCMD * excp,char * cmd,size_t cmdlen)63 argv_exp0(SCR *sp, EXCMD *excp, char *cmd, size_t cmdlen)
64 {
65 	EX_PRIVATE *exp;
66 
67 	exp = EXP(sp);
68 	argv_alloc(sp, cmdlen);
69 	memcpy(exp->args[exp->argsoff]->bp, cmd, cmdlen);
70 	exp->args[exp->argsoff]->bp[cmdlen] = '\0';
71 	exp->args[exp->argsoff]->len = cmdlen;
72 	++exp->argsoff;
73 	excp->argv = exp->args;
74 	excp->argc = exp->argsoff;
75 	return (0);
76 }
77 
78 /*
79  * argv_exp1 --
80  *	Do file name expansion on a string, and append it to the
81  *	argument list.
82  *
83  * PUBLIC: int argv_exp1(SCR *, EXCMD *, char *, size_t, int);
84  */
85 int
argv_exp1(SCR * sp,EXCMD * excp,char * cmd,size_t cmdlen,int is_bang)86 argv_exp1(SCR *sp, EXCMD *excp, char *cmd, size_t cmdlen, int is_bang)
87 {
88 	size_t blen, len;
89 	char *bp, *p, *t;
90 
91 	GET_SPACE_RET(sp, bp, blen, 512);
92 
93 	len = 0;
94 	if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) {
95 		FREE_SPACE(sp, bp, blen);
96 		return (1);
97 	}
98 
99 	/* If it's empty, we're done. */
100 	if (len != 0) {
101 		for (p = bp, t = bp + len; p < t; ++p)
102 			if (!isblank(*p))
103 				break;
104 		if (p == t)
105 			goto ret;
106 	} else
107 		goto ret;
108 
109 	(void)argv_exp0(sp, excp, bp, len);
110 
111 ret:	FREE_SPACE(sp, bp, blen);
112 	return (0);
113 }
114 
115 /*
116  * argv_exp2 --
117  *	Do file name and shell expansion on a string, and append it to
118  *	the argument list.
119  *
120  * PUBLIC: int argv_exp2(SCR *, EXCMD *, char *, size_t);
121  */
122 int
argv_exp2(SCR * sp,EXCMD * excp,char * cmd,size_t cmdlen)123 argv_exp2(SCR *sp, EXCMD *excp, char *cmd, size_t cmdlen)
124 {
125 	size_t blen, len, n;
126 	int rval;
127 	char *bp, *mp, *p;
128 
129 	GET_SPACE_RET(sp, bp, blen, 512);
130 
131 #define	SHELLECHO	"echo "
132 #define	SHELLOFFSET	(sizeof(SHELLECHO) - 1)
133 	memcpy(bp, SHELLECHO, SHELLOFFSET);
134 	p = bp + SHELLOFFSET;
135 	len = SHELLOFFSET;
136 
137 #if defined(DEBUG) && 0
138 	TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd);
139 #endif
140 
141 	if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, 0)) {
142 		rval = 1;
143 		goto err;
144 	}
145 
146 #if defined(DEBUG) && 0
147 	TRACE(sp, "before shell: %d: {%s}\n", len, bp);
148 #endif
149 
150 	/*
151 	 * Do shell word expansion -- it's very, very hard to figure out what
152 	 * magic characters the user's shell expects.  Historically, it was a
153 	 * union of v7 shell and csh meta characters.  We match that practice
154 	 * by default, so ":read \%" tries to read a file named '%'.  It would
155 	 * make more sense to pass any special characters through the shell,
156 	 * but then, if your shell was csh, the above example will behave
157 	 * differently in nvi than in vi.  If you want to get other characters
158 	 * passed through to your shell, change the "meta" option.
159 	 *
160 	 * To avoid a function call per character, we do a first pass through
161 	 * the meta characters looking for characters that aren't expected
162 	 * to be there, and then we can ignore them in the user's argument.
163 	 */
164 	if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1))
165 		n = 0;
166 	else {
167 		for (p = mp = O_STR(sp, O_SHELLMETA); *p != '\0'; ++p)
168 			if (isblank(*p) || isalnum(*p))
169 				break;
170 		p = bp + SHELLOFFSET;
171 		n = len - SHELLOFFSET;
172 		if (*p != '\0') {
173 			for (; n > 0; --n, ++p)
174 				if (strchr(mp, *p) != NULL)
175 					break;
176 		} else
177 			for (; n > 0; --n, ++p)
178 				if (!isblank(*p) &&
179 				    !isalnum(*p) && strchr(mp, *p) != NULL)
180 					break;
181 	}
182 
183 	/*
184 	 * If we found a meta character in the string, fork a shell to expand
185 	 * it.  Unfortunately, this is comparatively slow.  Historically, it
186 	 * didn't matter much, since users don't enter meta characters as part
187 	 * of pathnames that frequently.  The addition of filename completion
188 	 * broke that assumption because it's easy to use.  As a result, lots
189 	 * folks have complained that the expansion code is too slow.  So, we
190 	 * detect filename completion as a special case, and do it internally.
191 	 * Note that this code assumes that the <asterisk> character is the
192 	 * match-anything meta character.  That feels safe -- if anyone writes
193 	 * a shell that doesn't follow that convention, I'd suggest giving them
194 	 * a festive hot-lead enema.
195 	 */
196 	switch (n) {
197 	case 0:
198 		p = bp + SHELLOFFSET;
199 		len -= SHELLOFFSET;
200 		rval = argv_exp3(sp, excp, p, len);
201 		break;
202 	case 1:
203 		if (*p == '*') {
204 			*p = '\0';
205 			rval = argv_lexp(sp, excp, bp + SHELLOFFSET);
206 			break;
207 		}
208 		/* FALLTHROUGH */
209 	default:
210 		if (argv_sexp(sp, &bp, &blen, &len)) {
211 			rval = 1;
212 			goto err;
213 		}
214 		p = bp;
215 		rval = argv_exp3(sp, excp, p, len);
216 		break;
217 	}
218 
219 err:	FREE_SPACE(sp, bp, blen);
220 	return (rval);
221 }
222 
223 /*
224  * argv_exp3 --
225  *	Take a string and break it up into an argv, which is appended
226  *	to the argument list.
227  *
228  * PUBLIC: int argv_exp3(SCR *, EXCMD *, char *, size_t);
229  */
230 int
argv_exp3(SCR * sp,EXCMD * excp,char * cmd,size_t cmdlen)231 argv_exp3(SCR *sp, EXCMD *excp, char *cmd, size_t cmdlen)
232 {
233 	EX_PRIVATE *exp;
234 	size_t len;
235 	int ch, off;
236 	char *ap, *p;
237 
238 	for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) {
239 		/* Skip any leading whitespace. */
240 		for (; cmdlen > 0; --cmdlen, ++cmd) {
241 			ch = *cmd;
242 			if (!isblank(ch))
243 				break;
244 		}
245 		if (cmdlen == 0)
246 			break;
247 
248 		/*
249 		 * Determine the length of this whitespace delimited
250 		 * argument.
251 		 *
252 		 * QUOTING NOTE:
253 		 *
254 		 * Skip any character preceded by the user's quoting
255 		 * character.
256 		 */
257 		for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) {
258 			ch = *cmd;
259 			if (IS_ESCAPE(sp, excp, ch) && cmdlen > 1) {
260 				++cmd;
261 				--cmdlen;
262 			} else if (isblank(ch))
263 				break;
264 		}
265 
266 		/*
267 		 * Copy the argument into place.
268 		 *
269 		 * QUOTING NOTE:
270 		 *
271 		 * Lose quote chars.
272 		 */
273 		argv_alloc(sp, len);
274 		off = exp->argsoff;
275 		exp->args[off]->len = len;
276 		for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++)
277 			if (IS_ESCAPE(sp, excp, *ap))
278 				++ap;
279 		*p = '\0';
280 	}
281 	excp->argv = exp->args;
282 	excp->argc = exp->argsoff;
283 
284 #if defined(DEBUG) && 0
285 	for (cnt = 0; cnt < exp->argsoff; ++cnt)
286 		TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]);
287 #endif
288 	return (0);
289 }
290 
291 /*
292  * argv_fexp --
293  *	Do file name and bang command expansion.
294  */
295 static int
argv_fexp(SCR * sp,EXCMD * excp,char * cmd,size_t cmdlen,char * p,size_t * lenp,char ** bpp,size_t * blenp,int is_bang)296 argv_fexp(SCR *sp, EXCMD *excp, char *cmd, size_t cmdlen, char *p,
297     size_t *lenp, char **bpp, size_t *blenp, int is_bang)
298 {
299 	EX_PRIVATE *exp;
300 	char *bp, *t;
301 	size_t blen, len, off, tlen;
302 
303 	/* Replace file name characters. */
304 	for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd)
305 		switch (*cmd) {
306 		case '!':
307 			if (!is_bang)
308 				goto ins_ch;
309 			exp = EXP(sp);
310 			if (exp->lastbcomm == NULL) {
311 				msgq(sp, M_ERR,
312 				    "No previous command to replace \"!\"");
313 				return (1);
314 			}
315 			len += tlen = strlen(exp->lastbcomm);
316 			off = p - bp;
317 			ADD_SPACE_RET(sp, bp, blen, len);
318 			p = bp + off;
319 			memcpy(p, exp->lastbcomm, tlen);
320 			p += tlen;
321 			F_SET(excp, E_MODIFY);
322 			break;
323 		case '%':
324 			if ((t = sp->frp->name) == NULL) {
325 				msgq(sp, M_ERR,
326 				    "No filename to substitute for %%");
327 				return (1);
328 			}
329 			tlen = strlen(t);
330 			len += tlen;
331 			off = p - bp;
332 			ADD_SPACE_RET(sp, bp, blen, len);
333 			p = bp + off;
334 			memcpy(p, t, tlen);
335 			p += tlen;
336 			F_SET(excp, E_MODIFY);
337 			break;
338 		case '#':
339 			if ((t = sp->alt_name) == NULL) {
340 				msgq(sp, M_ERR,
341 				    "No filename to substitute for #");
342 				return (1);
343 			}
344 			len += tlen = strlen(t);
345 			off = p - bp;
346 			ADD_SPACE_RET(sp, bp, blen, len);
347 			p = bp + off;
348 			memcpy(p, t, tlen);
349 			p += tlen;
350 			F_SET(excp, E_MODIFY);
351 			break;
352 		case '\\':
353 			/*
354 			 * QUOTING NOTE:
355 			 *
356 			 * Strip any backslashes that protected the file
357 			 * expansion characters.
358 			 */
359 			if (cmdlen > 1 &&
360 			    (cmd[1] == '%' || cmd[1] == '#' || cmd[1] == '!')) {
361 				++cmd;
362 				--cmdlen;
363 			}
364 			/* FALLTHROUGH */
365 		default:
366 ins_ch:			++len;
367 			off = p - bp;
368 			ADD_SPACE_RET(sp, bp, blen, len);
369 			p = bp + off;
370 			*p++ = *cmd;
371 		}
372 
373 	/* Nul termination. */
374 	++len;
375 	off = p - bp;
376 	ADD_SPACE_RET(sp, bp, blen, len);
377 	p = bp + off;
378 	*p = '\0';
379 
380 	/* Return the new string length, buffer, buffer length. */
381 	*lenp = len - 1;
382 	*bpp = bp;
383 	*blenp = blen;
384 	return (0);
385 }
386 
387 /*
388  * argv_alloc --
389  *	Make more space for arguments.
390  */
391 static int
argv_alloc(SCR * sp,size_t len)392 argv_alloc(SCR *sp, size_t len)
393 {
394 	ARGS *ap;
395 	EX_PRIVATE *exp;
396 	int cnt, off;
397 
398 	/*
399 	 * Allocate room for another argument, always leaving
400 	 * enough room for an ARGS structure with a length of 0.
401 	 */
402 #define	INCREMENT	20
403 	exp = EXP(sp);
404 	off = exp->argsoff;
405 	if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) {
406 		cnt = exp->argscnt + INCREMENT;
407 		REALLOCARRAY(sp, exp->args, cnt, sizeof(ARGS *));
408 		if (exp->args == NULL) {
409 			(void)argv_free(sp);
410 			goto mem;
411 		}
412 		memset(&exp->args[exp->argscnt], 0, INCREMENT * sizeof(ARGS *));
413 		exp->argscnt = cnt;
414 	}
415 
416 	/* First argument. */
417 	if (exp->args[off] == NULL) {
418 		CALLOC(sp, exp->args[off], 1, sizeof(ARGS));
419 		if (exp->args[off] == NULL)
420 			goto mem;
421 	}
422 
423 	/* First argument buffer. */
424 	ap = exp->args[off];
425 	ap->len = 0;
426 	if (ap->blen < len + 1) {
427 		ap->blen = len + 1;
428 		REALLOCARRAY(sp, ap->bp, ap->blen, sizeof(CHAR_T));
429 		if (ap->bp == NULL) {
430 			ap->bp = NULL;
431 			ap->blen = 0;
432 			F_CLR(ap, A_ALLOCATED);
433 mem:			msgq(sp, M_SYSERR, NULL);
434 			return (1);
435 		}
436 		F_SET(ap, A_ALLOCATED);
437 	}
438 
439 	/* Second argument. */
440 	if (exp->args[++off] == NULL) {
441 		CALLOC(sp, exp->args[off], 1, sizeof(ARGS));
442 		if (exp->args[off] == NULL)
443 			goto mem;
444 	}
445 	/* 0 length serves as end-of-argument marker. */
446 	exp->args[off]->len = 0;
447 	return (0);
448 }
449 
450 /*
451  * argv_free --
452  *	Free up argument structures.
453  *
454  * PUBLIC: int argv_free(SCR *);
455  */
456 int
argv_free(SCR * sp)457 argv_free(SCR *sp)
458 {
459 	EX_PRIVATE *exp;
460 	int off;
461 
462 	exp = EXP(sp);
463 	if (exp->args != NULL) {
464 		for (off = 0; off < exp->argscnt; ++off) {
465 			if (exp->args[off] == NULL)
466 				continue;
467 			if (F_ISSET(exp->args[off], A_ALLOCATED))
468 				free(exp->args[off]->bp);
469 			free(exp->args[off]);
470 		}
471 		free(exp->args);
472 	}
473 	exp->args = NULL;
474 	exp->argscnt = 0;
475 	exp->argsoff = 0;
476 	return (0);
477 }
478 
479 /*
480  * argv_lexp --
481  *	Find all file names matching the prefix and append them to the
482  *	buffer.
483  */
484 static int
argv_lexp(SCR * sp,EXCMD * excp,char * path)485 argv_lexp(SCR *sp, EXCMD *excp, char *path)
486 {
487 	struct dirent *dp;
488 	DIR *dirp;
489 	EX_PRIVATE *exp;
490 	int off;
491 	size_t dlen, nlen;
492 	char *dname, *name, *p;
493 
494 	exp = EXP(sp);
495 
496 	/* Set up the name and length for comparison. */
497 	if ((p = strrchr(path, '/')) == NULL) {
498 		dname = ".";
499 		dlen = 0;
500 		name = path;
501 	} else {
502 		if (p == path) {
503 			dname = "/";
504 			dlen = 1;
505 		} else {
506 			*p = '\0';
507 			dname = path;
508 			dlen = strlen(path);
509 		}
510 		name = p + 1;
511 	}
512 	nlen = strlen(name);
513 
514 	if ((dirp = opendir(dname)) == NULL) {
515 		msgq_str(sp, M_SYSERR, dname, "%s");
516 		return (1);
517 	}
518 	for (off = exp->argsoff; (dp = readdir(dirp)) != NULL;) {
519 		if (nlen == 0) {
520 			if (dp->d_name[0] == '.')
521 				continue;
522 		} else {
523 			if (dp->d_namlen < nlen ||
524 			    memcmp(dp->d_name, name, nlen))
525 				continue;
526 		}
527 
528 		/* Directory + name + slash + null. */
529 		argv_alloc(sp, dlen + dp->d_namlen + 2);
530 		p = exp->args[exp->argsoff]->bp;
531 		if (dlen != 0) {
532 			memcpy(p, dname, dlen);
533 			p += dlen;
534 			if (dlen > 1 || dname[0] != '/')
535 				*p++ = '/';
536 		}
537 		memcpy(p, dp->d_name, dp->d_namlen + 1);
538 		exp->args[exp->argsoff]->len = dlen + dp->d_namlen + 1;
539 		++exp->argsoff;
540 		excp->argv = exp->args;
541 		excp->argc = exp->argsoff;
542 	}
543 	closedir(dirp);
544 
545 	if (off == exp->argsoff) {
546 		/*
547 		 * If we didn't find a match, complain that the expansion
548 		 * failed.  We can't know for certain that's the error, but
549 		 * it's a good guess, and it matches historic practice.
550 		 */
551 		msgq(sp, M_ERR, "Shell expansion failed");
552 		return (1);
553 	}
554 	qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
555 	return (0);
556 }
557 
558 /*
559  * argv_comp --
560  *	Alphabetic comparison.
561  */
562 static int
argv_comp(const void * a,const void * b)563 argv_comp(const void *a, const void *b)
564 {
565 	return (strcmp((char *)(*(ARGS **)a)->bp, (char *)(*(ARGS **)b)->bp));
566 }
567 
568 /*
569  * argv_sexp --
570  *	Fork a shell, pipe a command through it, and read the output into
571  *	a buffer.
572  */
573 static int
argv_sexp(SCR * sp,char ** bpp,size_t * blenp,size_t * lenp)574 argv_sexp(SCR *sp, char **bpp, size_t *blenp, size_t *lenp)
575 {
576 	enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval;
577 	FILE *ifp;
578 	pid_t pid;
579 	size_t blen, len;
580 	int ch, std_output[2];
581 	char *bp, *p, *sh, *sh_path;
582 
583 	/* Secure means no shell access. */
584 	if (O_ISSET(sp, O_SECURE)) {
585 		msgq(sp, M_ERR,
586 "Shell expansions not supported when the secure edit option is set");
587 		return (1);
588 	}
589 
590 	sh_path = O_STR(sp, O_SHELL);
591 	if ((sh = strrchr(sh_path, '/')) == NULL)
592 		sh = sh_path;
593 	else
594 		++sh;
595 
596 	/* Local copies of the buffer variables. */
597 	bp = *bpp;
598 	blen = *blenp;
599 
600 	/*
601 	 * There are two different processes running through this code, named
602 	 * the utility (the shell) and the parent. The utility reads standard
603 	 * input and writes standard output and standard error output.  The
604 	 * parent writes to the utility, reads its standard output and ignores
605 	 * its standard error output.  Historically, the standard error output
606 	 * was discarded by vi, as it produces a lot of noise when file patterns
607 	 * don't match.
608 	 *
609 	 * The parent reads std_output[0], and the utility writes std_output[1].
610 	 */
611 	ifp = NULL;
612 	std_output[0] = std_output[1] = -1;
613 	if (pipe(std_output) < 0) {
614 		msgq(sp, M_SYSERR, "pipe");
615 		return (1);
616 	}
617 	if ((ifp = fdopen(std_output[0], "r")) == NULL) {
618 		msgq(sp, M_SYSERR, "fdopen");
619 		goto err;
620 	}
621 
622 	/*
623 	 * Do the minimal amount of work possible, the shell is going to run
624 	 * briefly and then exit.  We sincerely hope.
625 	 */
626 	switch (pid = vfork()) {
627 	case -1:			/* Error. */
628 		msgq(sp, M_SYSERR, "vfork");
629 err:		if (ifp != NULL)
630 			(void)fclose(ifp);
631 		else if (std_output[0] != -1)
632 			close(std_output[0]);
633 		if (std_output[1] != -1)
634 			close(std_output[0]);
635 		return (1);
636 	case 0:				/* Utility. */
637 		/* Redirect stdout to the write end of the pipe. */
638 		(void)dup2(std_output[1], STDOUT_FILENO);
639 
640 		/* Close the utility's file descriptors. */
641 		(void)close(std_output[0]);
642 		(void)close(std_output[1]);
643 		(void)close(STDERR_FILENO);
644 
645 		/*
646 		 * XXX
647 		 * Assume that all shells have -c.
648 		 */
649 		execl(sh_path, sh, "-c", bp, (char *)NULL);
650 		msgq_str(sp, M_SYSERR, sh_path, "Error: execl: %s");
651 		_exit(127);
652 	default:			/* Parent. */
653 		/* Close the pipe ends the parent won't use. */
654 		(void)close(std_output[1]);
655 		break;
656 	}
657 
658 	/*
659 	 * Copy process standard output into a buffer.
660 	 *
661 	 * !!!
662 	 * Historic vi apparently discarded leading \n and \r's from
663 	 * the shell output stream.  We don't on the grounds that any
664 	 * shell that does that is broken.
665 	 */
666 	for (p = bp, len = 0, ch = EOF;
667 	    (ch = getc(ifp)) != EOF; *p++ = ch, --blen, ++len)
668 		if (blen < 5) {
669 			ADD_SPACE_GOTO(sp, bp, *blenp, *blenp * 2);
670 			p = bp + len;
671 			blen = *blenp - len;
672 		}
673 
674 	/* Delete the final newline, nul terminate the string. */
675 	if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) {
676 		--p;
677 		--len;
678 	}
679 	*p = '\0';
680 	*lenp = len;
681 	*bpp = bp;		/* *blenp is already updated. */
682 
683 	if (ferror(ifp))
684 		goto ioerr;
685 	if (fclose(ifp)) {
686 ioerr:		msgq_str(sp, M_ERR, sh, "I/O error: %s");
687 alloc_err:	rval = SEXP_ERR;
688 	} else
689 		rval = SEXP_OK;
690 
691 	/*
692 	 * Wait for the process.  If the shell process fails (e.g., "echo $q"
693 	 * where q wasn't a defined variable) or if the returned string has
694 	 * no characters or only blank characters, (e.g., "echo $5"), complain
695 	 * that the shell expansion failed.  We can't know for certain that's
696 	 * the error, but it's a good guess, and it matches historic practice.
697 	 * This won't catch "echo foo_$5", but that's not a common error and
698 	 * historic vi didn't catch it either.
699 	 */
700 	if (proc_wait(sp, pid, sh, 1, 0))
701 		rval = SEXP_EXPANSION_ERR;
702 
703 	for (p = bp; len; ++p, --len)
704 		if (!isblank(*p))
705 			break;
706 	if (len == 0)
707 		rval = SEXP_EXPANSION_ERR;
708 
709 	if (rval == SEXP_EXPANSION_ERR)
710 		msgq(sp, M_ERR, "Shell expansion failed");
711 
712 	return (rval == SEXP_OK ? 0 : 1);
713 }
714