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)

Reply via email to