Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / nvi / ex / ex_argv.c
blob4f661f770a0e35be8932e66cdd35ee5349d825eb
1 /* $NetBSD: ex_argv.c,v 1.2 2008/12/05 22:51:42 christos Exp $ */
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.
9 * See the LICENSE file for redistribution information.
12 #include "config.h"
14 #ifndef lint
15 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";
16 #endif /* not lint */
18 #include <sys/types.h>
19 #include <sys/queue.h>
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>
31 #include "../common/common.h"
33 static int argv_alloc __P((SCR *, size_t));
34 static int argv_comp __P((const void *, const void *));
35 static int argv_fexp __P((SCR *, EXCMD *,
36 const CHAR_T *, size_t, CHAR_T *, size_t *, CHAR_T **, size_t *, int));
37 static int argv_lexp __P((SCR *, EXCMD *, const char *));
38 static int argv_sexp __P((SCR *, CHAR_T **, size_t *, size_t *));
41 * argv_init --
42 * Build a prototype arguments list.
44 * PUBLIC: int argv_init __P((SCR *, EXCMD *));
46 int
47 argv_init(SCR *sp, EXCMD *excp)
49 EX_PRIVATE *exp;
51 exp = EXP(sp);
52 exp->argsoff = 0;
53 argv_alloc(sp, 1);
55 excp->argv = exp->args;
56 excp->argc = exp->argsoff;
57 return (0);
61 * argv_exp0 --
62 * Append a string to the argument list.
64 * PUBLIC: int argv_exp0 __P((SCR *, EXCMD *, CHAR_T *, size_t));
66 int
67 argv_exp0(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen)
69 EX_PRIVATE *exp;
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);
83 * argv_exp1 --
84 * Do file name expansion on a string, and append it to the
85 * argument list.
87 * PUBLIC: int argv_exp1 __P((SCR *, EXCMD *, const CHAR_T *, size_t, int));
89 int
90 argv_exp1(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen, int is_bang)
92 EX_PRIVATE *exp;
93 size_t blen, len;
94 CHAR_T *p, *t, *bp;
96 GET_SPACE_RETW(sp, bp, blen, 512);
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);
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(*p))
109 break;
110 if (p == t)
111 goto ret;
112 } else
113 goto ret;
115 (void)argv_exp0(sp, excp, bp, len);
117 ret: FREE_SPACEW(sp, bp, blen);
118 return (0);
122 * argv_exp2 --
123 * Do file name and shell expansion on a string, and append it to
124 * the argument list.
126 * PUBLIC: int argv_exp2 __P((SCR *, EXCMD *, CHAR_T *, size_t));
129 argv_exp2(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen)
131 size_t blen, len, n;
132 int rval;
133 CHAR_T *bp, *p;
134 const char *mp, *np;
136 GET_SPACE_RETW(sp, bp, blen, 512);
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;
148 #if defined(DEBUG) && 0
149 vtrace(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd);
150 #endif
152 if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, 0)) {
153 rval = 1;
154 goto err;
157 #if defined(DEBUG) && 0
158 vtrace(sp, "before shell: %d: {%s}\n", len, bp);
159 #endif
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.
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.
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;
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.
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;
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;
228 /* FALLTHROUGH */
229 default:
230 if (argv_sexp(sp, &bp, &blen, &len)) {
231 rval = 1;
232 goto err;
234 p = bp;
235 rval = argv_exp3(sp, excp, p, len);
236 break;
239 err: FREE_SPACEW(sp, bp, blen);
240 return (rval);
244 * argv_exp3 --
245 * Take a string and break it up into an argv, which is appended
246 * to the argument list.
248 * PUBLIC: int argv_exp3 __P((SCR *, EXCMD *, CHAR_T *, size_t));
251 argv_exp3(SCR *sp, EXCMD *excp, const CHAR_T *cmd, size_t cmdlen)
253 EX_PRIVATE *exp;
254 size_t len;
255 int ch, off;
256 const CHAR_T *ap;
257 CHAR_T *p;
259 for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) {
260 /* Skip any leading whitespace. */
261 for (; cmdlen > 0; --cmdlen, ++cmd) {
262 ch = *cmd;
263 if (!isblank(ch))
264 break;
266 if (cmdlen == 0)
267 break;
270 * Determine the length of this whitespace delimited
271 * argument.
273 * QUOTING NOTE:
275 * Skip any character preceded by the user's quoting
276 * character.
278 for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) {
279 ch = *cmd;
280 if (IS_ESCAPE(sp, excp, ch) && cmdlen > 1) {
281 ++cmd;
282 --cmdlen;
283 } else if (isblank(ch))
284 break;
288 * Copy the argument into place.
290 * QUOTING NOTE:
292 * Lose quote chars.
294 argv_alloc(sp, len);
295 off = exp->argsoff;
296 exp->args[off]->len = len;
297 for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++)
298 if (IS_ESCAPE(sp, excp, *ap))
299 ++ap;
300 *p = '\0';
302 excp->argv = exp->args;
303 excp->argc = exp->argsoff;
305 #if defined(DEBUG) && 0
306 for (cnt = 0; cnt < exp->argsoff; ++cnt)
307 vtrace(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]);
308 #endif
309 return (0);
313 * argv_fexp --
314 * Do file name and bang command expansion.
316 static int
317 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)
319 EX_PRIVATE *exp;
320 char *t;
321 size_t blen, len, off, tlen;
322 CHAR_T *bp;
323 const CHAR_T *wp;
324 size_t wlen;
326 /* Replace file name characters. */
327 for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd)
328 switch (*cmd) {
329 case '!':
330 if (!is_bang)
331 goto ins_ch;
332 exp = EXP(sp);
333 if (exp->lastbcomm == NULL) {
334 msgq(sp, M_ERR,
335 "115|No previous command to replace \"!\"");
336 return (1);
338 len += tlen = STRLEN(exp->lastbcomm);
339 off = p - bp;
340 ADD_SPACE_RETW(sp, bp, blen, len);
341 p = bp + off;
342 MEMCPY(p, exp->lastbcomm, tlen);
343 p += tlen;
344 F_SET(excp, E_MODIFY);
345 break;
346 case '%':
347 if ((t = sp->frp->name) == NULL) {
348 msgq(sp, M_ERR,
349 "116|No filename to substitute for %%");
350 return (1);
352 tlen = strlen(t);
353 len += tlen;
354 off = p - bp;
355 ADD_SPACE_RETW(sp, bp, blen, len);
356 p = bp + off;
357 CHAR2INT(sp, t, tlen, wp, wlen);
358 MEMCPY(p, wp, wlen);
359 p += wlen;
360 F_SET(excp, E_MODIFY);
361 break;
362 case '#':
363 if ((t = sp->alt_name) == NULL) {
364 msgq(sp, M_ERR,
365 "117|No filename to substitute for #");
366 return (1);
368 len += tlen = strlen(t);
369 off = p - bp;
370 ADD_SPACE_RETW(sp, bp, blen, len);
371 p = bp + off;
372 CHAR2INT(sp, t, tlen, wp, wlen);
373 MEMCPY(p, wp, wlen);
374 p += tlen;
375 F_SET(excp, E_MODIFY);
376 break;
377 case '\\':
379 * QUOTING NOTE:
381 * Strip any backslashes that protected the file
382 * expansion characters.
384 if (cmdlen > 1 &&
385 (cmd[1] == '%' || cmd[1] == '#' || cmd[1] == '!')) {
386 ++cmd;
387 --cmdlen;
389 /* FALLTHROUGH */
390 default:
391 ins_ch: ++len;
392 off = p - bp;
393 ADD_SPACE_RETW(sp, bp, blen, len);
394 p = bp + off;
395 *p++ = *cmd;
398 /* Nul termination. */
399 ++len;
400 off = p - bp;
401 ADD_SPACE_RETW(sp, bp, blen, len);
402 p = bp + off;
403 *p = '\0';
405 /* Return the new string length, buffer, buffer length. */
406 *lenp = len - 1;
407 *bpp = bp;
408 *blenp = blen;
409 return (0);
413 * argv_alloc --
414 * Make more space for arguments.
416 static int
417 argv_alloc(SCR *sp, size_t len)
419 ARGS *ap;
420 EX_PRIVATE *exp;
421 int cnt, off;
424 * Allocate room for another argument, always leaving
425 * enough room for an ARGS structure with a length of 0.
427 #define INCREMENT 20
428 exp = EXP(sp);
429 off = exp->argsoff;
430 if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) {
431 cnt = exp->argscnt + INCREMENT;
432 REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *));
433 if (exp->args == NULL) {
434 (void)argv_free(sp);
435 goto mem;
437 memset(&exp->args[exp->argscnt], 0, INCREMENT * sizeof(ARGS *));
438 exp->argscnt = cnt;
441 /* First argument. */
442 if (exp->args[off] == NULL) {
443 CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
444 if (exp->args[off] == NULL)
445 goto mem;
448 /* First argument buffer. */
449 ap = exp->args[off];
450 ap->len = 0;
451 if (ap->blen < len + 1) {
452 ap->blen = len + 1;
453 REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T));
454 if (ap->bp == NULL) {
455 ap->bp = NULL;
456 ap->blen = 0;
457 F_CLR(ap, A_ALLOCATED);
458 mem: msgq(sp, M_SYSERR, NULL);
459 return (1);
461 F_SET(ap, A_ALLOCATED);
464 /* Second argument. */
465 if (exp->args[++off] == NULL) {
466 CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
467 if (exp->args[off] == NULL)
468 goto mem;
470 /* 0 length serves as end-of-argument marker. */
471 exp->args[off]->len = 0;
472 return (0);
476 * argv_free --
477 * Free up argument structures.
479 * PUBLIC: int argv_free __P((SCR *));
482 argv_free(SCR *sp)
484 EX_PRIVATE *exp;
485 int off;
487 exp = EXP(sp);
488 if (exp->args != NULL) {
489 for (off = 0; off < exp->argscnt; ++off) {
490 if (exp->args[off] == NULL)
491 continue;
492 if (F_ISSET(exp->args[off], A_ALLOCATED))
493 free(exp->args[off]->bp);
494 free(exp->args[off]);
496 free(exp->args);
498 exp->args = NULL;
499 exp->argscnt = 0;
500 exp->argsoff = 0;
501 return (0);
505 * argv_lexp --
506 * Find all file names matching the prefix and append them to the
507 * buffer.
509 static int
510 argv_lexp(SCR *sp, EXCMD *excp, const char *path)
512 struct dirent *dp;
513 DIR *dirp;
514 EX_PRIVATE *exp;
515 int off;
516 size_t dlen, len, nlen;
517 const char *dname, *name;
518 char *p;
519 size_t wlen;
520 const CHAR_T *wp;
521 CHAR_T *n;
523 exp = EXP(sp);
525 /* Set up the name and length for comparison. */
526 if ((p = strrchr(path, '/')) == NULL) {
527 dname = ".";
528 dlen = 0;
529 name = path;
530 } else {
531 if (p == path) {
532 dname = "/";
533 dlen = 1;
534 } else {
535 *p = '\0';
536 dname = path;
537 dlen = strlen(path);
539 name = p + 1;
541 nlen = strlen(name);
544 * XXX
545 * We don't use the d_namlen field, it's not portable enough; we
546 * assume that d_name is nul terminated, instead.
548 if ((dirp = opendir(dname)) == NULL) {
549 msgq_str(sp, M_SYSERR, dname, "%s");
550 return (1);
552 for (off = exp->argsoff; (dp = readdir(dirp)) != NULL;) {
553 if (nlen == 0) {
554 if (dp->d_name[0] == '.')
555 continue;
556 len = strlen(dp->d_name);
557 } else {
558 len = strlen(dp->d_name);
559 if (len < nlen || memcmp(dp->d_name, name, nlen))
560 continue;
563 /* Directory + name + slash + null. */
564 argv_alloc(sp, dlen + len + 2);
565 n = exp->args[exp->argsoff]->bp;
566 if (dlen != 0) {
567 CHAR2INT(sp, dname, dlen, wp, wlen);
568 MEMCPY(n, wp, wlen);
569 n += dlen;
570 if (dlen > 1 || dname[0] != '/')
571 *n++ = '/';
573 CHAR2INT(sp, dp->d_name, len + 1, wp, wlen);
574 MEMCPY(n, wp, wlen);
575 exp->args[exp->argsoff]->len = dlen + len + 1;
576 ++exp->argsoff;
577 excp->argv = exp->args;
578 excp->argc = exp->argsoff;
580 closedir(dirp);
582 if (off == exp->argsoff) {
584 * If we didn't find a match, complain that the expansion
585 * failed. We can't know for certain that's the error, but
586 * it's a good guess, and it matches historic practice.
588 msgq(sp, M_ERR, "304|Shell expansion failed");
589 return (1);
591 qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
592 return (0);
596 * argv_comp --
597 * Alphabetic comparison.
599 static int
600 argv_comp(const void *a, const void *b)
602 return (STRCMP((*(const ARGS * const*)a)->bp, (*(const ARGS * const*)b)->bp));
605 static pid_t
606 runcmd(SCR *sp, const char *sh_path, const char *sh, const char *np,
607 int *std_output)
609 pid_t pid;
611 * Do the minimal amount of work possible, the shell is going to run
612 * briefly and then exit. We sincerely hope.
614 switch (pid = vfork()) {
615 case -1: /* Error. */
616 msgq(sp, M_SYSERR, "vfork");
617 return (pid_t)-1;
618 case 0: /* Utility. */
619 /* Redirect stdout to the write end of the pipe. */
620 (void)dup2(std_output[1], STDOUT_FILENO);
622 /* Close the utility's file descriptors. */
623 (void)close(std_output[0]);
624 (void)close(std_output[1]);
625 (void)close(STDERR_FILENO);
628 * XXX
629 * Assume that all shells have -c.
631 execl(sh_path, sh, "-c", np, (char *)NULL);
632 msgq_str(sp, M_SYSERR, sh_path, "118|Error: execl: %s");
633 _exit(127);
634 default: /* Parent. */
635 /* Close the pipe ends the parent won't use. */
636 (void)close(std_output[1]);
637 return pid;
642 * argv_sexp --
643 * Fork a shell, pipe a command through it, and read the output into
644 * a buffer.
646 static int
647 argv_sexp(SCR *sp, CHAR_T **bpp, size_t *blenp, size_t *lenp)
649 enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval;
650 FILE *ifp;
651 pid_t pid;
652 size_t blen, len;
653 int ch, std_output[2];
654 CHAR_T *bp, *p;
655 const char *sh, *sh_path;
656 const char *np;
657 size_t nlen;
659 /* Secure means no shell access. */
660 if (O_ISSET(sp, O_SECURE)) {
661 msgq(sp, M_ERR,
662 "289|Shell expansions not supported when the secure edit option is set");
663 return (1);
666 sh_path = O_STR(sp, O_SHELL);
667 if ((sh = strrchr(sh_path, '/')) == NULL)
668 sh = sh_path;
669 else
670 ++sh;
672 /* Local copies of the buffer variables. */
673 bp = *bpp;
674 blen = *blenp;
677 * There are two different processes running through this code, named
678 * the utility (the shell) and the parent. The utility reads standard
679 * input and writes standard output and standard error output. The
680 * parent writes to the utility, reads its standard output and ignores
681 * its standard error output. Historically, the standard error output
682 * was discarded by vi, as it produces a lot of noise when file patterns
683 * don't match.
685 * The parent reads std_output[0], and the utility writes std_output[1].
687 ifp = NULL;
688 std_output[0] = std_output[1] = -1;
689 if (pipe(std_output) < 0) {
690 msgq(sp, M_SYSERR, "pipe");
691 return (1);
693 if ((ifp = fdopen(std_output[0], "r")) == NULL) {
694 msgq(sp, M_SYSERR, "fdopen");
695 goto err;
697 INT2CHAR(sp, bp, STRLEN(bp)+1, np, nlen);
698 pid = runcmd(sp, sh_path, sh, np, std_output);
699 if (pid == -1)
700 goto err;
703 * Copy process standard output into a buffer.
705 * !!!
706 * Historic vi apparently discarded leading \n and \r's from
707 * the shell output stream. We don't on the grounds that any
708 * shell that does that is broken.
710 for (p = bp, len = 0, ch = EOF;
711 (ch = getc(ifp)) != EOF; *p++ = ch, blen-=sizeof(CHAR_T), ++len)
712 if (blen < 5) {
713 ADD_SPACE_GOTOW(sp, bp, *blenp, *blenp * 2);
714 p = bp + len;
715 blen = *blenp - len;
718 /* Delete the final newline, nul terminate the string. */
719 if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) {
720 --p;
721 --len;
723 *p = '\0';
724 *lenp = len;
725 *bpp = bp; /* *blenp is already updated. */
727 if (ferror(ifp))
728 goto ioerr;
729 if (fclose(ifp)) {
730 ioerr: msgq_str(sp, M_ERR, sh, "119|I/O error: %s");
731 alloc_err: rval = SEXP_ERR;
732 } else
733 rval = SEXP_OK;
736 * Wait for the process. If the shell process fails (e.g., "echo $q"
737 * where q wasn't a defined variable) or if the returned string has
738 * no characters or only blank characters, (e.g., "echo $5"), complain
739 * that the shell expansion failed. We can't know for certain that's
740 * the error, but it's a good guess, and it matches historic practice.
741 * This won't catch "echo foo_$5", but that's not a common error and
742 * historic vi didn't catch it either.
744 if (proc_wait(sp, (long)pid, sh, 1, 0))
745 rval = SEXP_EXPANSION_ERR;
747 for (p = bp; len; ++p, --len)
748 if (!isblank(*p))
749 break;
750 if (len == 0)
751 rval = SEXP_EXPANSION_ERR;
753 if (rval == SEXP_EXPANSION_ERR)
754 msgq(sp, M_ERR, "304|Shell expansion failed");
756 return (rval == SEXP_OK ? 0 : 1);
757 err: if (ifp != NULL)
758 (void)fclose(ifp);
759 else if (std_output[0] != -1)
760 close(std_output[0]);
761 if (std_output[1] != -1)
762 close(std_output[0]);
763 return 1;