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)