tools/llvm: Do not build with symbols
[minix3.git] / external / bsd / nvi / dist / ex / ex_argv.c
blobfa47bceba6b7354c4dd365e6820f7d5d745ae613
1 /* $NetBSD: ex_argv.c,v 1.2 2013/11/22 15:52:05 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.
8 * See the LICENSE file for redistribution information.
9 */
11 #include "config.h"
13 #ifndef lint
14 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 ";
15 #endif /* not lint */
17 #include <sys/types.h>
18 #include <sys/queue.h>
20 #include <bitstring.h>
21 #include <ctype.h>
22 #include <dirent.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
30 #include "../common/common.h"
32 static int argv_alloc __P((SCR *, size_t));
33 static int argv_comp __P((const void *, const void *));
34 static int argv_fexp __P((SCR *, EXCMD *,
35 const CHAR_T *, size_t, CHAR_T *, size_t *, CHAR_T **, size_t *, int));
36 static int argv_lexp __P((SCR *, EXCMD *, const char *));
37 static int argv_sexp __P((SCR *, CHAR_T **, size_t *, size_t *));
40 * argv_init --
41 * Build a prototype arguments list.
43 * PUBLIC: int argv_init __P((SCR *, EXCMD *));
45 int
46 argv_init(SCR *sp, EXCMD *excp)
48 EX_PRIVATE *exp;
50 exp = EXP(sp);
51 exp->argsoff = 0;
52 argv_alloc(sp, 1);
54 excp->argv = exp->args;
55 excp->argc = exp->argsoff;
56 return (0);
60 * argv_exp0 --
61 * Append a string to the argument list.
63 * PUBLIC: int argv_exp0 __P((SCR *, EXCMD *, const CHAR_T *, size_t));
65 int
66 argv_exp0(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen)
68 EX_PRIVATE *exp;
70 exp = EXP(sp);
71 argv_alloc(sp, cmdlen);
72 MEMCPY(exp->args[exp->argsoff]->bp, cmd, cmdlen);
73 exp->args[exp->argsoff]->bp[cmdlen] = '\0';
74 exp->args[exp->argsoff]->len = cmdlen;
75 ++exp->argsoff;
76 excp->argv = exp->args;
77 excp->argc = exp->argsoff;
78 return (0);
82 * argv_exp1 --
83 * Do file name expansion on a string, and append it to the
84 * argument list.
86 * PUBLIC: int argv_exp1 __P((SCR *, EXCMD *, const CHAR_T *, size_t, int));
88 int
89 argv_exp1(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen, int is_bang)
91 size_t blen, len;
92 CHAR_T *p, *t, *bp;
94 GET_SPACE_RETW(sp, bp, blen, 512);
96 len = 0;
97 if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) {
98 FREE_SPACEW(sp, bp, blen);
99 return (1);
102 /* If it's empty, we're done. */
103 if (len != 0) {
104 for (p = bp, t = bp + len; p < t; ++p)
105 if (!ISBLANK(*p))
106 break;
107 if (p == t)
108 goto ret;
109 } else
110 goto ret;
112 (void)argv_exp0(sp, excp, bp, len);
114 ret: FREE_SPACEW(sp, bp, blen);
115 return (0);
119 * argv_exp2 --
120 * Do file name and shell expansion on a string, and append it to
121 * the argument list.
123 * PUBLIC: int argv_exp2 __P((SCR *, EXCMD *, const CHAR_T *, size_t));
126 argv_exp2(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen)
128 size_t blen, len, n;
129 int rval;
130 CHAR_T *bp, *p;
131 const char *mp, *np;
133 GET_SPACE_RETW(sp, bp, blen, 512);
135 #define SHELLECHO "echo "
136 #define SHELLOFFSET (sizeof(SHELLECHO) - 1)
137 p = bp;
138 *p++ = 'e';
139 *p++ = 'c';
140 *p++ = 'h';
141 *p++ = 'o';
142 *p++ = ' ';
143 len = SHELLOFFSET;
145 #if defined(DEBUG) && 0
146 vtrace(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd);
147 #endif
149 if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, 0)) {
150 rval = 1;
151 goto err;
154 #if defined(DEBUG) && 0
155 vtrace(sp, "before shell: %d: {%s}\n", len, bp);
156 #endif
159 * Do shell word expansion -- it's very, very hard to figure out what
160 * magic characters the user's shell expects. Historically, it was a
161 * union of v7 shell and csh meta characters. We match that practice
162 * by default, so ":read \%" tries to read a file named '%'. It would
163 * make more sense to pass any special characters through the shell,
164 * but then, if your shell was csh, the above example will behave
165 * differently in nvi than in vi. If you want to get other characters
166 * passed through to your shell, change the "meta" option.
168 * To avoid a function call per character, we do a first pass through
169 * the meta characters looking for characters that aren't expected
170 * to be there, and then we can ignore them in the user's argument.
172 if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1))
173 n = 0;
174 else {
175 for (np = mp = O_STR(sp, O_SHELLMETA); *np != '\0'; ++np)
176 if (ISBLANK((UCHAR_T)*np) ||
177 ISALNUM((UCHAR_T)*np))
178 break;
179 p = bp + SHELLOFFSET;
180 n = len - SHELLOFFSET;
181 if (*p != '\0') {
182 for (; n > 0; --n, ++p)
183 if (strchr(mp, *p) != NULL)
184 break;
185 } else
186 for (; n > 0; --n, ++p)
187 if (!ISBLANK((UCHAR_T)*p) &&
188 !ISALNUM((UCHAR_T)*p) &&
189 strchr(mp, *p) != NULL)
190 break;
194 * If we found a meta character in the string, fork a shell to expand
195 * it. Unfortunately, this is comparatively slow. Historically, it
196 * didn't matter much, since users don't enter meta characters as part
197 * of pathnames that frequently. The addition of filename completion
198 * broke that assumption because it's easy to use. As a result, lots
199 * folks have complained that the expansion code is too slow. So, we
200 * detect filename completion as a special case, and do it internally.
201 * Note that this code assumes that the <asterisk> character is the
202 * match-anything meta character. That feels safe -- if anyone writes
203 * a shell that doesn't follow that convention, I'd suggest giving them
204 * a festive hot-lead enema.
206 switch (n) {
207 case 0:
208 p = bp + SHELLOFFSET;
209 len -= SHELLOFFSET;
210 rval = argv_exp3(sp, excp, p, len);
211 break;
212 case 1:
213 if (*p == '*') {
214 const char *np1;
215 char *d;
216 size_t nlen;
218 *p = '\0';
219 INT2CHAR(sp, bp + SHELLOFFSET,
220 STRLEN(bp + SHELLOFFSET) + 1, np1, nlen);
221 d = strdup(np1);
222 rval = argv_lexp(sp, excp, d);
223 free (d);
224 break;
226 /* FALLTHROUGH */
227 default:
228 if (argv_sexp(sp, &bp, &blen, &len)) {
229 rval = 1;
230 goto err;
232 p = bp;
233 rval = argv_exp3(sp, excp, p, len);
234 break;
237 err: FREE_SPACEW(sp, bp, blen);
238 return (rval);
242 * argv_exp3 --
243 * Take a string and break it up into an argv, which is appended
244 * to the argument list.
246 * PUBLIC: int argv_exp3 __P((SCR *, EXCMD *, const CHAR_T *, size_t));
249 argv_exp3(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen)
251 EX_PRIVATE *exp;
252 size_t len;
253 ARG_CHAR_T ch;
254 int off;
255 const CHAR_T *ap;
256 CHAR_T *p;
258 for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) {
259 /* Skip any leading whitespace. */
260 for (; cmdlen > 0; --cmdlen, ++cmd) {
261 ch = (UCHAR_T)*cmd;
262 if (!ISBLANK(ch))
263 break;
265 if (cmdlen == 0)
266 break;
269 * Determine the length of this whitespace delimited
270 * argument.
272 * QUOTING NOTE:
274 * Skip any character preceded by the user's quoting
275 * character.
277 for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) {
278 ch = (UCHAR_T)*cmd;
279 if (IS_ESCAPE(sp, excp, ch) && cmdlen > 1) {
280 ++cmd;
281 --cmdlen;
282 } else if (ISBLANK(ch))
283 break;
287 * Copy the argument into place.
289 * QUOTING NOTE:
291 * Lose quote chars.
293 argv_alloc(sp, len);
294 off = exp->argsoff;
295 exp->args[off]->len = len;
296 for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++)
297 if (IS_ESCAPE(sp, excp, *ap))
298 ++ap;
299 *p = '\0';
301 excp->argv = exp->args;
302 excp->argc = exp->argsoff;
304 #if defined(DEBUG) && 0
305 for (cnt = 0; cnt < exp->argsoff; ++cnt)
306 vtrace(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]);
307 #endif
308 return (0);
312 * argv_fexp --
313 * Do file name and bang command expansion.
315 static int
316 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)
318 EX_PRIVATE *exp;
319 char *t;
320 size_t blen, len, off, tlen;
321 CHAR_T *bp;
322 const CHAR_T *wp;
323 size_t wlen;
325 /* Replace file name characters. */
326 for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd)
327 switch (*cmd) {
328 case '!':
329 if (!is_bang)
330 goto ins_ch;
331 exp = EXP(sp);
332 if (exp->lastbcomm == NULL) {
333 msgq(sp, M_ERR,
334 "115|No previous command to replace \"!\"");
335 return (1);
337 len += tlen = STRLEN(exp->lastbcomm);
338 off = p - bp;
339 ADD_SPACE_RETW(sp, bp, blen, len);
340 p = bp + off;
341 MEMCPY(p, exp->lastbcomm, tlen);
342 p += tlen;
343 F_SET(excp, E_MODIFY);
344 break;
345 case '%':
346 if ((t = sp->frp->name) == NULL) {
347 msgq(sp, M_ERR,
348 "116|No filename to substitute for %%");
349 return (1);
351 tlen = strlen(t);
352 len += tlen;
353 off = p - bp;
354 ADD_SPACE_RETW(sp, bp, blen, len);
355 p = bp + off;
356 CHAR2INT(sp, t, tlen, wp, wlen);
357 MEMCPY(p, wp, wlen);
358 p += wlen;
359 F_SET(excp, E_MODIFY);
360 break;
361 case '#':
362 if ((t = sp->alt_name) == NULL) {
363 msgq(sp, M_ERR,
364 "117|No filename to substitute for #");
365 return (1);
367 len += tlen = strlen(t);
368 off = p - bp;
369 ADD_SPACE_RETW(sp, bp, blen, len);
370 p = bp + off;
371 CHAR2INT(sp, t, tlen, wp, wlen);
372 MEMCPY(p, wp, wlen);
373 p += tlen;
374 F_SET(excp, E_MODIFY);
375 break;
376 case '\\':
378 * QUOTING NOTE:
380 * Strip any backslashes that protected the file
381 * expansion characters.
383 if (cmdlen > 1 &&
384 (cmd[1] == '%' || cmd[1] == '#' || cmd[1] == '!')) {
385 ++cmd;
386 --cmdlen;
388 /* FALLTHROUGH */
389 default:
390 ins_ch: ++len;
391 off = p - bp;
392 ADD_SPACE_RETW(sp, bp, blen, len);
393 p = bp + off;
394 *p++ = *cmd;
397 /* Nul termination. */
398 ++len;
399 off = p - bp;
400 ADD_SPACE_RETW(sp, bp, blen, len);
401 p = bp + off;
402 *p = '\0';
404 /* Return the new string length, buffer, buffer length. */
405 *lenp = len - 1;
406 *bpp = bp;
407 *blenp = blen;
408 return (0);
412 * argv_alloc --
413 * Make more space for arguments.
415 static int
416 argv_alloc(SCR *sp, size_t len)
418 ARGS *ap;
419 EX_PRIVATE *exp;
420 int cnt, off;
423 * Allocate room for another argument, always leaving
424 * enough room for an ARGS structure with a length of 0.
426 #define INCREMENT 20
427 exp = EXP(sp);
428 off = exp->argsoff;
429 if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) {
430 cnt = exp->argscnt + INCREMENT;
431 REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *));
432 if (exp->args == NULL) {
433 (void)argv_free(sp);
434 goto mem;
436 memset(&exp->args[exp->argscnt], 0, INCREMENT * sizeof(ARGS *));
437 exp->argscnt = cnt;
440 /* First argument. */
441 if (exp->args[off] == NULL) {
442 CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
443 if (exp->args[off] == NULL)
444 goto mem;
447 /* First argument buffer. */
448 ap = exp->args[off];
449 ap->len = 0;
450 if (ap->blen < len + 1) {
451 ap->blen = len + 1;
452 REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T));
453 if (ap->bp == NULL) {
454 ap->bp = NULL;
455 ap->blen = 0;
456 F_CLR(ap, A_ALLOCATED);
457 mem: msgq(sp, M_SYSERR, NULL);
458 return (1);
460 F_SET(ap, A_ALLOCATED);
463 /* Second argument. */
464 if (exp->args[++off] == NULL) {
465 CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
466 if (exp->args[off] == NULL)
467 goto mem;
469 /* 0 length serves as end-of-argument marker. */
470 exp->args[off]->len = 0;
471 return (0);
475 * argv_free --
476 * Free up argument structures.
478 * PUBLIC: int argv_free __P((SCR *));
481 argv_free(SCR *sp)
483 EX_PRIVATE *exp;
484 int off;
486 exp = EXP(sp);
487 if (exp->args != NULL) {
488 for (off = 0; off < exp->argscnt; ++off) {
489 if (exp->args[off] == NULL)
490 continue;
491 if (F_ISSET(exp->args[off], A_ALLOCATED))
492 free(exp->args[off]->bp);
493 free(exp->args[off]);
495 free(exp->args);
497 exp->args = NULL;
498 exp->argscnt = 0;
499 exp->argsoff = 0;
500 return (0);
504 * argv_lexp --
505 * Find all file names matching the prefix and append them to the
506 * buffer.
508 static int
509 argv_lexp(SCR *sp, EXCMD *excp, const char *path)
511 struct dirent *dp;
512 DIR *dirp;
513 EX_PRIVATE *exp;
514 int off;
515 size_t dlen, len, nlen;
516 const char *dname, *name;
517 char *p;
518 size_t wlen;
519 const CHAR_T *wp;
520 CHAR_T *n;
522 exp = EXP(sp);
524 /* Set up the name and length for comparison. */
525 if ((p = strrchr(path, '/')) == NULL) {
526 dname = ".";
527 dlen = 0;
528 name = path;
529 } else {
530 if (p == path) {
531 dname = "/";
532 dlen = 1;
533 } else {
534 *p = '\0';
535 dname = path;
536 dlen = strlen(path);
538 name = p + 1;
540 nlen = strlen(name);
543 * XXX
544 * We don't use the d_namlen field, it's not portable enough; we
545 * assume that d_name is nul terminated, instead.
547 if ((dirp = opendir(dname)) == NULL) {
548 msgq_str(sp, M_SYSERR, dname, "%s");
549 return (1);
551 for (off = exp->argsoff; (dp = readdir(dirp)) != NULL;) {
552 if (nlen == 0) {
553 if (dp->d_name[0] == '.')
554 continue;
555 len = strlen(dp->d_name);
556 } else {
557 len = strlen(dp->d_name);
558 if (len < nlen || memcmp(dp->d_name, name, nlen))
559 continue;
562 /* Directory + name + slash + null. */
563 argv_alloc(sp, dlen + len + 2);
564 n = exp->args[exp->argsoff]->bp;
565 if (dlen != 0) {
566 CHAR2INT(sp, dname, dlen, wp, wlen);
567 MEMCPY(n, wp, wlen);
568 n += dlen;
569 if (dlen > 1 || dname[0] != '/')
570 *n++ = '/';
572 CHAR2INT(sp, dp->d_name, len + 1, wp, wlen);
573 MEMCPY(n, wp, wlen);
574 exp->args[exp->argsoff]->len = dlen + len + 1;
575 ++exp->argsoff;
576 excp->argv = exp->args;
577 excp->argc = exp->argsoff;
579 closedir(dirp);
581 if (off == exp->argsoff) {
583 * If we didn't find a match, complain that the expansion
584 * failed. We can't know for certain that's the error, but
585 * it's a good guess, and it matches historic practice.
587 msgq(sp, M_ERR, "304|Shell expansion failed");
588 return (1);
590 qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
591 return (0);
595 * argv_comp --
596 * Alphabetic comparison.
598 static int
599 argv_comp(const void *a, const void *b)
601 return (STRCMP((*(const ARGS * const*)a)->bp, (*(const ARGS * const*)b)->bp));
604 static pid_t
605 runcmd(SCR *sp, const char *sh_path, const char *sh, const char *np,
606 int *std_output)
608 pid_t pid;
610 * Do the minimal amount of work possible, the shell is going to run
611 * briefly and then exit. We sincerely hope.
613 switch (pid = vfork()) {
614 case -1: /* Error. */
615 msgq(sp, M_SYSERR, "vfork");
616 return (pid_t)-1;
617 case 0: /* Utility. */
618 /* Redirect stdout to the write end of the pipe. */
619 (void)dup2(std_output[1], STDOUT_FILENO);
621 /* Close the utility's file descriptors. */
622 (void)close(std_output[0]);
623 (void)close(std_output[1]);
624 (void)close(STDERR_FILENO);
627 * XXX
628 * Assume that all shells have -c.
630 execl(sh_path, sh, "-c", np, (char *)NULL);
631 msgq_str(sp, M_SYSERR, sh_path, "118|Error: execl: %s");
632 _exit(127);
633 default: /* Parent. */
634 /* Close the pipe ends the parent won't use. */
635 (void)close(std_output[1]);
636 return pid;
641 * argv_sexp --
642 * Fork a shell, pipe a command through it, and read the output into
643 * a buffer.
645 static int
646 argv_sexp(SCR *sp, CHAR_T **bpp, size_t *blenp, size_t *lenp)
648 enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval;
649 FILE *ifp;
650 pid_t pid;
651 size_t blen, len;
652 int ch, std_output[2];
653 CHAR_T *bp, *p;
654 const char *sh, *sh_path;
655 const char *np;
656 size_t nlen;
658 /* Secure means no shell access. */
659 if (O_ISSET(sp, O_SECURE)) {
660 msgq(sp, M_ERR,
661 "289|Shell expansions not supported when the secure edit option is set");
662 return (1);
665 sh_path = O_STR(sp, O_SHELL);
666 if ((sh = strrchr(sh_path, '/')) == NULL)
667 sh = sh_path;
668 else
669 ++sh;
671 /* Local copies of the buffer variables. */
672 bp = *bpp;
673 blen = *blenp;
676 * There are two different processes running through this code, named
677 * the utility (the shell) and the parent. The utility reads standard
678 * input and writes standard output and standard error output. The
679 * parent writes to the utility, reads its standard output and ignores
680 * its standard error output. Historically, the standard error output
681 * was discarded by vi, as it produces a lot of noise when file patterns
682 * don't match.
684 * The parent reads std_output[0], and the utility writes std_output[1].
686 ifp = NULL;
687 std_output[0] = std_output[1] = -1;
688 if (pipe(std_output) < 0) {
689 msgq(sp, M_SYSERR, "pipe");
690 return (1);
692 if ((ifp = fdopen(std_output[0], "r")) == NULL) {
693 msgq(sp, M_SYSERR, "fdopen");
694 goto err;
696 INT2CHAR(sp, bp, STRLEN(bp)+1, np, nlen);
697 pid = runcmd(sp, sh_path, sh, np, std_output);
698 if (pid == -1)
699 goto err;
702 * Copy process standard output into a buffer.
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.
709 for (p = bp, len = 0, ch = EOF;
710 (ch = getc(ifp)) != EOF; *p++ = ch, blen-=sizeof(CHAR_T), ++len)
711 if (blen < 5) {
712 ADD_SPACE_GOTOW(sp, bp, *blenp, *blenp * 2);
713 p = bp + len;
714 blen = *blenp - len;
717 /* Delete the final newline, nul terminate the string. */
718 if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) {
719 --p;
720 --len;
722 *p = '\0';
723 *lenp = len;
724 *bpp = bp; /* *blenp is already updated. */
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;
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.
743 if (proc_wait(sp, (long)pid, sh, 1, 0))
744 rval = SEXP_EXPANSION_ERR;
746 for (p = bp; len; ++p, --len)
747 if (!ISBLANK(*p))
748 break;
749 if (len == 0)
750 rval = SEXP_EXPANSION_ERR;
752 if (rval == SEXP_EXPANSION_ERR)
753 msgq(sp, M_ERR, "304|Shell expansion failed");
755 return (rval == SEXP_OK ? 0 : 1);
756 err: if (ifp != NULL)
757 (void)fclose(ifp);
758 else if (std_output[0] != -1)
759 close(std_output[0]);
760 if (std_output[1] != -1)
761 close(std_output[0]);
762 return 1;