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