2 * Copyright (c) 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
7 * See the LICENSE file for redistribution information.
12 #include <sys/types.h>
13 #include <sys/queue.h>
16 #include <bitstring.h>
27 #include "../common/common.h"
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_T *, size_t, CHAR_T *, size_t *, CHAR_T **, size_t *, int);
33 static int argv_sexp(SCR *, CHAR_T **, size_t *, size_t *);
34 static int argv_flt_user(SCR *, EXCMD *, CHAR_T *, size_t);
38 * Build a prototype arguments list.
40 * PUBLIC: int argv_init(SCR *, EXCMD *);
43 argv_init(SCR *sp, EXCMD *excp)
51 excp->argv = exp->args;
52 excp->argc = exp->argsoff;
58 * Append a string to the argument list.
60 * PUBLIC: int argv_exp0(SCR *, EXCMD *, CHAR_T *, size_t);
63 argv_exp0(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
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;
73 excp->argv = exp->args;
74 excp->argc = exp->argsoff;
80 * Do file name expansion on a string, and append it to the
83 * PUBLIC: int argv_exp1(SCR *, EXCMD *, CHAR_T *, size_t, int);
86 argv_exp1(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen, int is_bang)
92 GET_SPACE_RETW(sp, bp, blen, 512);
96 if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) {
97 FREE_SPACEW(sp, bp, blen);
101 /* If it's empty, we're done. */
103 for (p = bp, t = bp + len; p < t; ++p)
111 (void)argv_exp0(sp, excp, bp, len);
113 ret: FREE_SPACEW(sp, bp, blen);
119 * Do file name and shell expansion on a string, and append it to
122 * PUBLIC: int argv_exp2(SCR *, EXCMD *, CHAR_T *, size_t);
125 argv_exp2(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
131 GET_SPACE_RETW(sp, bp, blen, 512);
133 #define SHELLECHO L("echo ")
134 #define SHELLOFFSET (SIZE(SHELLECHO) - 1)
135 MEMCPY(bp, SHELLECHO, SHELLOFFSET);
136 p = bp + SHELLOFFSET;
139 #if defined(DEBUG) && 0
140 TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd);
143 if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, 0)) {
148 #if defined(DEBUG) && 0
149 TRACE(sp, "before shell: %d: {%s}\n", len, bp);
153 * Do shell word expansion -- it's very, very hard to figure out what
154 * magic characters the user's shell expects. Historically, it was a
155 * union of v7 shell and csh meta characters. We match that practice
156 * by default, so ":read \%" tries to read a file named '%'. It would
157 * make more sense to pass any special characters through the shell,
158 * but then, if your shell was csh, the above example will behave
159 * differently in nvi than in vi. If you want to get other characters
160 * passed through to your shell, change the "meta" option.
162 if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1))
165 p = bp + SHELLOFFSET;
166 n = len - SHELLOFFSET;
167 for (; n > 0; --n, ++p)
168 if (IS_SHELLMETA(sp, *p))
173 * If we found a meta character in the string, fork a shell to expand
174 * it. Unfortunately, this is comparatively slow. Historically, it
175 * didn't matter much, since users don't enter meta characters as part
176 * of pathnames that frequently. The addition of filename completion
177 * broke that assumption because it's easy to use. To increase the
178 * completion performance, nvi used to have an internal routine to
179 * handle "filename*". However, the shell special characters does not
180 * limit to "shellmeta", so such a hack breaks historic practice.
181 * After it all, we split the completion logic out from here.
185 p = bp + SHELLOFFSET;
187 rval = argv_exp3(sp, excp, p, len);
190 if (argv_sexp(sp, &bp, &blen, &len)) {
195 rval = argv_exp3(sp, excp, p, len);
199 err: FREE_SPACEW(sp, bp, blen);
205 * Take a string and break it up into an argv, which is appended
206 * to the argument list.
208 * PUBLIC: int argv_exp3(SCR *, EXCMD *, CHAR_T *, size_t);
211 argv_exp3(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
218 for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) {
219 /* Skip any leading whitespace. */
220 for (; cmdlen > 0; --cmdlen, ++cmd) {
229 * Determine the length of this whitespace delimited
234 * Skip any character preceded by the user's quoting
237 for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) {
239 if (IS_ESCAPE(sp, excp, ch) && cmdlen > 1) {
242 } else if (cmdskip(ch))
247 * Copy the argument into place.
255 exp->args[off]->len = len;
256 for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++)
257 if (IS_ESCAPE(sp, excp, *ap))
261 excp->argv = exp->args;
262 excp->argc = exp->argsoff;
264 #if defined(DEBUG) && 0
265 for (cnt = 0; cnt < exp->argsoff; ++cnt)
266 TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]);
273 * Filter the ex commands with a prefix, and append the results to
276 * PUBLIC: int argv_flt_ex(SCR *, EXCMD *, CHAR_T *, size_t);
279 argv_flt_ex(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
288 for (off = exp->argsoff, cp = cmds; cp->name != NULL; ++cp) {
289 len = STRLEN(cp->name);
291 (cmdlen > len || MEMCMP(cmd, cp->name, cmdlen)))
294 /* Copy the matched ex command name. */
295 argv_alloc(sp, len + 1);
296 MEMCPY(exp->args[exp->argsoff]->bp, cp->name, len + 1);
297 exp->args[exp->argsoff]->len = len;
299 excp->argv = exp->args;
300 excp->argc = exp->argsoff;
308 * Filter the ~user list on the system with a prefix, and append
309 * the results to the argument list.
312 argv_flt_user(SCR *sp, EXCMD *excp, CHAR_T *uname, size_t ulen)
323 /* The input must come with a leading '~'. */
324 INT2CHAR(sp, uname + 1, ulen - 1, np, nlen);
325 if ((np = v_strdup(sp, np, nlen)) == NULL)
329 while ((pw = getpwent()) != NULL) {
330 len = strlen(pw->pw_name);
332 (nlen > len || memcmp(np, pw->pw_name, nlen)))
335 /* Copy '~' + the matched user name. */
336 CHAR2INT(sp, pw->pw_name, len + 1, uname, ulen);
337 argv_alloc(sp, ulen + 1);
338 exp->args[exp->argsoff]->bp[0] = '~';
339 MEMCPY(exp->args[exp->argsoff]->bp + 1, uname, ulen);
340 exp->args[exp->argsoff]->len = ulen;
342 excp->argv = exp->args;
343 excp->argc = exp->argsoff;
348 qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
354 * Do file name and bang command expansion.
357 argv_fexp(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen, CHAR_T *p, size_t *lenp, CHAR_T **bpp, size_t *blenp, int is_bang)
361 size_t blen, len, off, tlen;
366 /* Replace file name characters. */
367 for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd)
373 if (exp->lastbcomm == NULL) {
375 "115|No previous command to replace \"!\"");
378 len += tlen = STRLEN(exp->lastbcomm);
380 ADD_SPACE_RETW(sp, bp, blen, len);
382 MEMCPY(p, exp->lastbcomm, tlen);
384 F_SET(excp, E_MODIFY);
387 if ((t = sp->frp->name) == NULL) {
389 "116|No filename to substitute for %%");
395 ADD_SPACE_RETW(sp, bp, blen, len);
397 CHAR2INT(sp, t, tlen, wp, wlen);
400 F_SET(excp, E_MODIFY);
403 if ((t = sp->alt_name) == NULL) {
405 "117|No filename to substitute for #");
408 len += tlen = strlen(t);
410 ADD_SPACE_RETW(sp, bp, blen, len);
412 CHAR2INT(sp, t, tlen, wp, wlen);
415 F_SET(excp, E_MODIFY);
421 * Strip any backslashes that protected the file
422 * expansion characters.
425 (cmd[1] == '%' || cmd[1] == '#' || cmd[1] == '!')) {
433 ADD_SPACE_RETW(sp, bp, blen, len);
438 /* Nul termination. */
441 ADD_SPACE_RETW(sp, bp, blen, len);
445 /* Return the new string length, buffer, buffer length. */
454 * Make more space for arguments.
457 argv_alloc(SCR *sp, size_t len)
464 * Allocate room for another argument, always leaving
465 * enough room for an ARGS structure with a length of 0.
470 if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) {
471 cnt = exp->argscnt + INCREMENT;
472 REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *));
473 if (exp->args == NULL) {
477 memset(&exp->args[exp->argscnt], 0, INCREMENT * sizeof(ARGS *));
481 /* First argument. */
482 if (exp->args[off] == NULL) {
483 CALLOC(sp, exp->args[off], 1, sizeof(ARGS));
484 if (exp->args[off] == NULL)
488 /* First argument buffer. */
491 if (ap->blen < len + 1) {
493 REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T));
494 if (ap->bp == NULL) {
497 F_CLR(ap, A_ALLOCATED);
498 mem: msgq(sp, M_SYSERR, NULL);
501 F_SET(ap, A_ALLOCATED);
504 /* Second argument. */
505 if (exp->args[++off] == NULL) {
506 CALLOC(sp, exp->args[off], 1, sizeof(ARGS));
507 if (exp->args[off] == NULL)
510 /* 0 length serves as end-of-argument marker. */
511 exp->args[off]->len = 0;
517 * Free up argument structures.
519 * PUBLIC: int argv_free(SCR *);
528 if (exp->args != NULL) {
529 for (off = 0; off < exp->argscnt; ++off) {
530 if (exp->args[off] == NULL)
532 if (F_ISSET(exp->args[off], A_ALLOCATED))
533 free(exp->args[off]->bp);
534 free(exp->args[off]);
546 * Find all file names matching the prefix and append them to the
549 * PUBLIC: int argv_flt_path(SCR *, EXCMD *, CHAR_T *, size_t);
552 argv_flt_path(SCR *sp, EXCMD *excp, CHAR_T *path, size_t plen)
558 size_t dlen, len, nlen;
561 char *name, *tp, *epd = NULL;
567 /* Set up the name and length for comparison. */
568 if ((path = v_wstrdup(sp, path, plen)) == NULL)
570 if ((p = STRRCHR(path, '/')) == NULL) {
574 /* Filter ~user list instead. */
575 rc = argv_flt_user(sp, excp, path, plen);
594 INT2CHAR(sp, dname, dlen + 1, tp, nlen);
595 if ((epd = expanduser(tp)) != NULL)
597 if ((dirp = opendir(tp)) == NULL) {
604 INT2CHAR(sp, np, STRLEN(np), tp, nlen);
605 if ((name = v_strdup(sp, tp, nlen)) == NULL) {
610 for (off = exp->argsoff; (dp = readdir(dirp)) != NULL;) {
612 if (dp->d_name[0] == '.')
614 #ifdef HAVE_DIRENT_D_NAMLEN
617 len = strlen(dp->d_name);
620 #ifdef HAVE_DIRENT_D_NAMLEN
623 len = strlen(dp->d_name);
625 if (len < nlen || memcmp(dp->d_name, name, nlen))
629 /* Directory + name + slash + null. */
630 CHAR2INT(sp, dp->d_name, len + 1, wp, wlen);
631 argv_alloc(sp, dlen + wlen + 1);
632 n = exp->args[exp->argsoff]->bp;
634 MEMCPY(n, dname, dlen);
636 if (dlen > 1 || dname[0] != '/')
638 exp->args[exp->argsoff]->len = dlen + 1;
641 exp->args[exp->argsoff]->len += wlen - 1;
643 excp->argv = exp->args;
644 excp->argc = exp->argsoff;
650 qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
656 * Alphabetic comparison.
659 argv_comp(const void *a, const void *b)
661 return (STRCMP((*(ARGS **)a)->bp, (*(ARGS **)b)->bp));
666 * Fork a shell, pipe a command through it, and read the output into
670 argv_sexp(SCR *sp, CHAR_T **bpp, size_t *blenp, size_t *lenp)
672 enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval;
676 int ch, std_output[2];
682 /* Secure means no shell access. */
683 if (O_ISSET(sp, O_SECURE)) {
685 "289|Shell expansions not supported when the secure edit option is set");
689 sh_path = O_STR(sp, O_SHELL);
690 if ((sh = strrchr(sh_path, '/')) == NULL)
695 /* Local copies of the buffer variables. */
700 * There are two different processes running through this code, named
701 * the utility (the shell) and the parent. The utility reads standard
702 * input and writes standard output and standard error output. The
703 * parent writes to the utility, reads its standard output and ignores
704 * its standard error output. Historically, the standard error output
705 * was discarded by vi, as it produces a lot of noise when file patterns
708 * The parent reads std_output[0], and the utility writes std_output[1].
711 std_output[0] = std_output[1] = -1;
712 if (pipe(std_output) < 0) {
713 msgq(sp, M_SYSERR, "pipe");
716 if ((ifp = fdopen(std_output[0], "r")) == NULL) {
717 msgq(sp, M_SYSERR, "fdopen");
722 * Do the minimal amount of work possible, the shell is going to run
723 * briefly and then exit. We sincerely hope.
725 switch (pid = vfork()) {
726 case -1: /* Error. */
727 msgq(sp, M_SYSERR, "vfork");
728 err: if (ifp != NULL)
730 else if (std_output[0] != -1)
731 close(std_output[0]);
732 if (std_output[1] != -1)
733 close(std_output[0]);
735 case 0: /* Utility. */
736 /* Redirect stdout to the write end of the pipe. */
737 (void)dup2(std_output[1], STDOUT_FILENO);
739 /* Close the utility's file descriptors. */
740 (void)close(std_output[0]);
741 (void)close(std_output[1]);
742 (void)close(STDERR_FILENO);
746 * Assume that all shells have -c.
748 INT2CHAR(sp, bp, STRLEN(bp)+1, np, nlen);
749 execl(sh_path, sh, "-c", np, (char *)NULL);
750 msgq_str(sp, M_SYSERR, sh_path, "118|Error: execl: %s");
752 default: /* Parent. */
753 /* Close the pipe ends the parent won't use. */
754 (void)close(std_output[1]);
759 * Copy process standard output into a buffer.
762 * Historic vi apparently discarded leading \n and \r's from
763 * the shell output stream. We don't on the grounds that any
764 * shell that does that is broken.
766 for (p = bp, len = 0, ch = EOF;
767 (ch = GETC(ifp)) != EOF; *p++ = ch, blen-=sizeof(CHAR_T), ++len)
769 ADD_SPACE_GOTOW(sp, bp, *blenp, *blenp * 2);
774 /* Delete the final newline, nul terminate the string. */
775 if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) {
781 *bpp = bp; /* *blenp is already updated. */
786 ioerr: msgq_str(sp, M_ERR, sh, "119|I/O error: %s");
787 alloc_err: rval = SEXP_ERR;
792 * Wait for the process. If the shell process fails (e.g., "echo $q"
793 * where q wasn't a defined variable) or if the returned string has
794 * no characters or only blank characters, (e.g., "echo $5"), complain
795 * that the shell expansion failed. We can't know for certain that's
796 * the error, but it's a good guess, and it matches historic practice.
797 * This won't catch "echo foo_$5", but that's not a common error and
798 * historic vi didn't catch it either.
800 if (proc_wait(sp, (long)pid, sh, 1, 0))
801 rval = SEXP_EXPANSION_ERR;
803 for (p = bp; len; ++p, --len)
807 rval = SEXP_EXPANSION_ERR;
809 if (rval == SEXP_EXPANSION_ERR)
810 msgq(sp, M_ERR, "304|Shell expansion failed");
812 return (rval == SEXP_OK ? 0 : 1);
817 * Escape a string into an ex and shell argument.
819 * PUBLIC: CHAR_T *argv_esc(SCR *, EXCMD *, CHAR_T *, size_t);
822 argv_esc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len)
828 GET_SPACE_GOTOW(sp, bp, blen, len + 1);
831 * Leaving the first '~' unescaped causes the user to need a
832 * "./" prefix to edit a file which really starts with a '~'.
833 * However, the file completion happens to not work for these
834 * files without the prefix.
836 * All ex expansion characters, "!%#", are double escaped.
838 for (p = bp; len > 0; ++str, --len) {
841 if (blen / sizeof(CHAR_T) - off < 3) {
842 ADD_SPACE_GOTOW(sp, bp, blen, off + 3);
845 if (cmdskip(ch) || ch == '\n' ||
846 IS_ESCAPE(sp, excp, ch)) /* Ex. */
849 case '~': /* ~user. */
853 case '+': /* Ex +cmd. */
857 case '!': case '%': case '#': /* Ex exp. */
861 case ',': case '-': case '.': case '/': /* Safe. */
862 case ':': case '=': case '@': case '_':
864 default: /* Unsafe. */
865 if (isascii(ch) && !isalnum(ch))
880 * Unescape an escaped ex and shell argument.
882 * PUBLIC: CHAR_T *argv_uesc(SCR *, EXCMD *, CHAR_T *, size_t);
885 argv_uesc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len)
890 GET_SPACE_GOTOW(sp, bp, blen, len + 1);
892 for (p = bp; len > 0; ++str, --len) {
893 if (IS_ESCAPE(sp, excp, *str)) {
897 } else if (*str == '\\') {
902 /* Check for double escaping. */
903 if (*str == '\\' && len > 1)
905 case '!': case '%': case '#':