1 /* $NetBSD: ex_argv.c,v 1.3 2014/01/26 21:43:45 christos Exp $ */
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.
13 #include <sys/cdefs.h>
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 ";
19 __RCSID("$NetBSD: ex_argv.c,v 1.3 2014/01/26 21:43:45 christos Exp $");
22 #include <sys/types.h>
23 #include <sys/queue.h>
25 #include <bitstring.h>
35 #include "../common/common.h"
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 *));
46 * Build a prototype arguments list.
48 * PUBLIC: int argv_init __P((SCR *, EXCMD *));
51 argv_init(SCR
*sp
, EXCMD
*excp
)
59 excp
->argv
= exp
->args
;
60 excp
->argc
= exp
->argsoff
;
66 * Append a string to the argument list.
68 * PUBLIC: int argv_exp0 __P((SCR *, EXCMD *, const CHAR_T *, size_t));
71 argv_exp0(SCR
*sp
, EXCMD
*excp
, const CHAR_T
*cmd
, size_t cmdlen
)
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
;
81 excp
->argv
= exp
->args
;
82 excp
->argc
= exp
->argsoff
;
88 * Do file name expansion on a string, and append it to the
91 * PUBLIC: int argv_exp1 __P((SCR *, EXCMD *, const CHAR_T *, size_t, int));
94 argv_exp1(SCR
*sp
, EXCMD
*excp
, const CHAR_T
*cmd
, size_t cmdlen
, int is_bang
)
99 GET_SPACE_RETW(sp
, bp
, blen
, 512);
102 if (argv_fexp(sp
, excp
, cmd
, cmdlen
, bp
, &len
, &bp
, &blen
, is_bang
)) {
103 FREE_SPACEW(sp
, bp
, blen
);
107 /* If it's empty, we're done. */
109 for (p
= bp
, t
= bp
+ len
; p
< t
; ++p
)
117 (void)argv_exp0(sp
, excp
, bp
, len
);
119 ret
: FREE_SPACEW(sp
, bp
, blen
);
125 * Do file name and shell expansion on a string, and append it to
128 * PUBLIC: int argv_exp2 __P((SCR *, EXCMD *, const CHAR_T *, size_t));
131 argv_exp2(SCR
*sp
, EXCMD
*excp
, const CHAR_T
*cmd
, size_t cmdlen
)
138 GET_SPACE_RETW(sp
, bp
, blen
, 512);
140 #define SHELLECHO "echo "
141 #define SHELLOFFSET (sizeof(SHELLECHO) - 1)
150 #if defined(DEBUG) && 0
151 vtrace(sp
, "file_argv: {%.*s}\n", (int)cmdlen
, cmd
);
154 if (argv_fexp(sp
, excp
, cmd
, cmdlen
, p
, &len
, &bp
, &blen
, 0)) {
159 #if defined(DEBUG) && 0
160 vtrace(sp
, "before shell: %d: {%s}\n", len
, bp
);
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.
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.
177 if (opts_empty(sp
, O_SHELL
, 1) || opts_empty(sp
, O_SHELLMETA
, 1))
180 for (np
= mp
= O_STR(sp
, O_SHELLMETA
); *np
!= '\0'; ++np
)
181 if (ISBLANK((UCHAR_T
)*np
) ||
182 ISALNUM((UCHAR_T
)*np
))
184 p
= bp
+ SHELLOFFSET
;
185 n
= len
- SHELLOFFSET
;
187 for (; n
> 0; --n
, ++p
)
188 if (strchr(mp
, *p
) != NULL
)
191 for (; n
> 0; --n
, ++p
)
192 if (!ISBLANK((UCHAR_T
)*p
) &&
193 !ISALNUM((UCHAR_T
)*p
) &&
194 strchr(mp
, *p
) != NULL
)
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.
213 p
= bp
+ SHELLOFFSET
;
215 rval
= argv_exp3(sp
, excp
, p
, len
);
224 INT2CHAR(sp
, bp
+ SHELLOFFSET
,
225 STRLEN(bp
+ SHELLOFFSET
) + 1, np1
, nlen
);
227 rval
= argv_lexp(sp
, excp
, d
);
233 if (argv_sexp(sp
, &bp
, &blen
, &len
)) {
238 rval
= argv_exp3(sp
, excp
, p
, len
);
242 err
: FREE_SPACEW(sp
, bp
, blen
);
248 * Take a string and break it up into an argv, which is appended
249 * to the argument list.
251 * PUBLIC: int argv_exp3 __P((SCR *, EXCMD *, const CHAR_T *, size_t));
254 argv_exp3(SCR
*sp
, EXCMD
*excp
, const CHAR_T
*cmd
, size_t cmdlen
)
263 for (exp
= EXP(sp
); cmdlen
> 0; ++exp
->argsoff
) {
264 /* Skip any leading whitespace. */
265 for (; cmdlen
> 0; --cmdlen
, ++cmd
) {
274 * Determine the length of this whitespace delimited
279 * Skip any character preceded by the user's quoting
282 for (ap
= cmd
, len
= 0; cmdlen
> 0; ++cmd
, --cmdlen
, ++len
) {
284 if (IS_ESCAPE(sp
, excp
, ch
) && cmdlen
> 1) {
287 } else if (ISBLANK(ch
))
292 * Copy the argument into place.
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
))
306 excp
->argv
= exp
->args
;
307 excp
->argc
= exp
->argsoff
;
309 #if defined(DEBUG) && 0
310 for (cnt
= 0; cnt
< exp
->argsoff
; ++cnt
)
311 vtrace(sp
, "arg %d: {%s}\n", cnt
, exp
->argv
[cnt
]);
318 * Do file name and bang command expansion.
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
)
325 size_t blen
, len
, off
, tlen
;
330 /* Replace file name characters. */
331 for (bp
= *bpp
, blen
= *blenp
, len
= *lenp
; cmdlen
> 0; --cmdlen
, ++cmd
)
337 if (exp
->lastbcomm
== NULL
) {
339 "115|No previous command to replace \"!\"");
342 len
+= tlen
= STRLEN(exp
->lastbcomm
);
344 ADD_SPACE_RETW(sp
, bp
, blen
, len
);
346 MEMCPY(p
, exp
->lastbcomm
, tlen
);
348 F_SET(excp
, E_MODIFY
);
351 if ((t
= sp
->frp
->name
) == NULL
) {
353 "116|No filename to substitute for %%");
359 ADD_SPACE_RETW(sp
, bp
, blen
, len
);
361 CHAR2INT(sp
, t
, tlen
, wp
, wlen
);
364 F_SET(excp
, E_MODIFY
);
367 if ((t
= sp
->alt_name
) == NULL
) {
369 "117|No filename to substitute for #");
372 len
+= tlen
= strlen(t
);
374 ADD_SPACE_RETW(sp
, bp
, blen
, len
);
376 CHAR2INT(sp
, t
, tlen
, wp
, wlen
);
379 F_SET(excp
, E_MODIFY
);
385 * Strip any backslashes that protected the file
386 * expansion characters.
389 (cmd
[1] == '%' || cmd
[1] == '#' || cmd
[1] == '!')) {
397 ADD_SPACE_RETW(sp
, bp
, blen
, len
);
402 /* Nul termination. */
405 ADD_SPACE_RETW(sp
, bp
, blen
, len
);
409 /* Return the new string length, buffer, buffer length. */
418 * Make more space for arguments.
421 argv_alloc(SCR
*sp
, size_t len
)
428 * Allocate room for another argument, always leaving
429 * enough room for an ARGS structure with a length of 0.
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
) {
441 memset(&exp
->args
[exp
->argscnt
], 0, INCREMENT
* sizeof(ARGS
*));
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
)
452 /* First argument buffer. */
455 if (ap
->blen
< len
+ 1) {
457 REALLOC(sp
, ap
->bp
, CHAR_T
*, ap
->blen
* sizeof(CHAR_T
));
458 if (ap
->bp
== NULL
) {
461 F_CLR(ap
, A_ALLOCATED
);
462 mem
: msgq(sp
, M_SYSERR
, NULL
);
465 F_SET(ap
, A_ALLOCATED
);
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
)
474 /* 0 length serves as end-of-argument marker. */
475 exp
->args
[off
]->len
= 0;
481 * Free up argument structures.
483 * PUBLIC: int argv_free __P((SCR *));
492 if (exp
->args
!= NULL
) {
493 for (off
= 0; off
< exp
->argscnt
; ++off
) {
494 if (exp
->args
[off
] == NULL
)
496 if (F_ISSET(exp
->args
[off
], A_ALLOCATED
))
497 free(exp
->args
[off
]->bp
);
498 free(exp
->args
[off
]);
510 * Find all file names matching the prefix and append them to the
514 argv_lexp(SCR
*sp
, EXCMD
*excp
, const char *path
)
520 size_t dlen
, len
, nlen
;
521 const char *dname
, *name
;
529 /* Set up the name and length for comparison. */
530 if ((p
= strrchr(path
, '/')) == NULL
) {
549 * We don't use the d_namlen field, it's not portable enough; we
550 * assume that d_name is nul terminated, instead.
552 if ((dirp
= opendir(dname
)) == NULL
) {
553 msgq_str(sp
, M_SYSERR
, dname
, "%s");
556 for (off
= exp
->argsoff
; (dp
= readdir(dirp
)) != NULL
;) {
558 if (dp
->d_name
[0] == '.')
560 len
= strlen(dp
->d_name
);
562 len
= strlen(dp
->d_name
);
563 if (len
< nlen
|| memcmp(dp
->d_name
, name
, nlen
))
567 /* Directory + name + slash + null. */
568 argv_alloc(sp
, dlen
+ len
+ 2);
569 n
= exp
->args
[exp
->argsoff
]->bp
;
571 CHAR2INT(sp
, dname
, dlen
, wp
, wlen
);
574 if (dlen
> 1 || dname
[0] != '/')
577 CHAR2INT(sp
, dp
->d_name
, len
+ 1, wp
, wlen
);
579 exp
->args
[exp
->argsoff
]->len
= dlen
+ len
+ 1;
581 excp
->argv
= exp
->args
;
582 excp
->argc
= exp
->argsoff
;
586 if (off
== exp
->argsoff
) {
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.
592 msgq(sp
, M_ERR
, "304|Shell expansion failed");
595 qsort(exp
->args
+ off
, exp
->argsoff
- off
, sizeof(ARGS
*), argv_comp
);
601 * Alphabetic comparison.
604 argv_comp(const void *a
, const void *b
)
606 return (STRCMP((*(const ARGS
* const*)a
)->bp
, (*(const ARGS
* const*)b
)->bp
));
610 runcmd(SCR
*sp
, const char *sh_path
, const char *sh
, const char *np
,
615 * Do the minimal amount of work possible, the shell is going to run
616 * briefly and then exit. We sincerely hope.
618 switch (pid
= vfork()) {
619 case -1: /* Error. */
620 msgq(sp
, M_SYSERR
, "vfork");
622 case 0: /* Utility. */
623 /* Redirect stdout to the write end of the pipe. */
624 (void)dup2(std_output
[1], STDOUT_FILENO
);
626 /* Close the utility's file descriptors. */
627 (void)close(std_output
[0]);
628 (void)close(std_output
[1]);
629 (void)close(STDERR_FILENO
);
633 * Assume that all shells have -c.
635 execl(sh_path
, sh
, "-c", np
, (char *)NULL
);
636 msgq_str(sp
, M_SYSERR
, sh_path
, "118|Error: execl: %s");
638 default: /* Parent. */
639 /* Close the pipe ends the parent won't use. */
640 (void)close(std_output
[1]);
647 * Fork a shell, pipe a command through it, and read the output into
651 argv_sexp(SCR
*sp
, CHAR_T
**bpp
, size_t *blenp
, size_t *lenp
)
653 enum { SEXP_ERR
, SEXP_EXPANSION_ERR
, SEXP_OK
} rval
;
657 int ch
, std_output
[2];
659 const char *sh
, *sh_path
;
663 /* Secure means no shell access. */
664 if (O_ISSET(sp
, O_SECURE
)) {
666 "289|Shell expansions not supported when the secure edit option is set");
670 sh_path
= O_STR(sp
, O_SHELL
);
671 if ((sh
= strrchr(sh_path
, '/')) == NULL
)
676 /* Local copies of the buffer variables. */
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
689 * The parent reads std_output[0], and the utility writes std_output[1].
692 std_output
[0] = std_output
[1] = -1;
693 if (pipe(std_output
) < 0) {
694 msgq(sp
, M_SYSERR
, "pipe");
697 if ((ifp
= fdopen(std_output
[0], "r")) == NULL
) {
698 msgq(sp
, M_SYSERR
, "fdopen");
701 INT2CHAR(sp
, bp
, STRLEN(bp
)+1, np
, nlen
);
702 pid
= runcmd(sp
, sh_path
, sh
, np
, std_output
);
707 * Copy process standard output into a buffer.
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.
714 for (p
= bp
, len
= 0, ch
= EOF
;
715 (ch
= getc(ifp
)) != EOF
; *p
++ = ch
, blen
-=sizeof(CHAR_T
), ++len
)
717 ADD_SPACE_GOTOW(sp
, bp
, *blenp
, *blenp
* 2);
722 /* Delete the final newline, nul terminate the string. */
723 if (p
> bp
&& (p
[-1] == '\n' || p
[-1] == '\r')) {
729 *bpp
= bp
; /* *blenp is already updated. */
734 ioerr
: msgq_str(sp
, M_ERR
, sh
, "119|I/O error: %s");
735 alloc_err
: rval
= SEXP_ERR
;
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.
748 if (proc_wait(sp
, (long)pid
, sh
, 1, 0))
749 rval
= SEXP_EXPANSION_ERR
;
751 for (p
= bp
; len
; ++p
, --len
)
755 rval
= SEXP_EXPANSION_ERR
;
757 if (rval
== SEXP_EXPANSION_ERR
)
758 msgq(sp
, M_ERR
, "304|Shell expansion failed");
760 return (rval
== SEXP_OK
? 0 : 1);
761 err
: if (ifp
!= NULL
)
763 else if (std_output
[0] != -1)
764 close(std_output
[0]);
765 if (std_output
[1] != -1)
766 close(std_output
[0]);