Hi people, the following patch allows vi (and ex) to do tilde expansion without forking off a shell. The algorithm is similar to the one in mg, modulo adaptions for the way vi handles line buffers and such. This allows editing files like `~/foobar` in secure (-S) mode, where fork and exec are disabled.
There is a slight difference in behaviour compared to vi without this patch, which is that the old vi allowed disabling tilde expansion by setting the `shellmeta` option to something that does not contain a `~`. This can be re-added if it's needed though. I sent the same patch to Anthony a few weeks back, but I assume he hasn't gotten to taking a look at it yet. -- Gregor Index: common/options.c =================================================================== RCS file: /mnt/media/cvs/src/usr.bin/vi/common/options.c,v retrieving revision 1.19 diff -u -p -r1.19 options.c --- common/options.c 24 Nov 2015 10:28:14 -0000 1.19 +++ common/options.c 9 Dec 2015 17:26:18 -0000 @@ -362,7 +362,7 @@ opts_init(SCR *sp, int *oargs) (void)snprintf(b1, sizeof(b1), "shell=%s", (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s); OI(O_SHELL, b1); - OI(O_SHELLMETA, "shellmeta=~{[*?$`'\"\\"); + OI(O_SHELLMETA, "shellmeta={[*?$`'\"\\"); OI(O_SHIFTWIDTH, "shiftwidth=8"); OI(O_SIDESCROLL, "sidescroll=16"); OI(O_TABSTOP, "tabstop=8"); Index: ex/ex_argv.c =================================================================== RCS file: /mnt/media/cvs/src/usr.bin/vi/ex/ex_argv.c,v retrieving revision 1.17 diff -u -p -r1.17 ex_argv.c --- ex/ex_argv.c 7 Dec 2015 20:39:19 -0000 1.17 +++ ex/ex_argv.c 9 Dec 2015 17:27:57 -0000 @@ -11,6 +11,7 @@ #include "config.h" +#include <sys/stat.h> #include <sys/types.h> #include <sys/queue.h> @@ -19,6 +20,7 @@ #include <dirent.h> #include <errno.h> #include <limits.h> +#include <pwd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -288,6 +290,58 @@ argv_exp3(SCR *sp, EXCMD *excp, char *cm return (0); } +/* Logic and code similar to mg: + * - expand tilde at beginning of file name + * - if ./~foo exists, don't expand + * - expand only one tilde + * - if file name matches ~foo/bar and foo is a user, replace ~foo by users' home dir + * - else replace ~ with current users home dir + */ +int +expand_tilde(SCR *sp, char *cmd, char **p, char **bpp, size_t *blen, size_t *len) { + size_t tlen, off, ulen; + struct stat statbuf; + struct passwd *pw; + char un[LOGIN_NAME_MAX]; + char *tmp; + + if (cmd[0] != '~' || stat(cmd, &statbuf) == 0) + goto out; + + if ((tmp = strchr(cmd, '/')) == NULL) + tmp = cmd + strlen(cmd); /* point to end of string */ + + ulen = tmp - &cmd[1]; + if (ulen >= sizeof(un)) { + goto out; + } + if (ulen == 0) { + if ((tmp = getlogin()) == NULL) + goto out; + (void) strlcpy(un, tmp, sizeof(un)); + } else { + memcpy(un, &cmd[1], ulen); + un[ulen] = '\0'; + } + + if ((pw = getpwnam(un)) == NULL) + goto out; /* XXX: print a warning message? */ + + tlen = strlen(pw->pw_dir); + *len += tlen; + off = *p - *bpp; + ADD_SPACE_GOTO(sp, *bpp, *blen, *len); + *p = *bpp + off; + memcpy(*p, pw->pw_dir, tlen); + *p += tlen; + + return ulen + 1; + +out: +alloc_err: /* ADD_SPACE_GOTO jumps here */ + return 0; +} + /* * argv_fexp -- * Do file name and bang command expansion. @@ -299,6 +353,10 @@ argv_fexp(SCR *sp, EXCMD *excp, char *cm EX_PRIVATE *exp; char *bp, *t; size_t blen, len, off, tlen; + + tlen = expand_tilde(sp, cmd, &p, bpp, blenp, lenp); + cmd += tlen; + cmdlen -= tlen; /* Replace file name characters. */ for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd)