adjustname() should canonicalize the given path, but it does not do it
always, resulting in some bizzare situations.

it calls realpath(3) but when it fails the given path is returned
as-is.  This in practice can happen quite often since it's not usual
for an editor to visit new files.

A quick way to replicate the issue is to

        $ mg foo        # assuming `foo' does not exist
        type something then save
        C-x C-f foo RET

surprise, now you're in "foo<2>", a different buffer for the same
file.

On the other hand, expandtilde() has its own fair share of issues too.

We can instead syntactically clean the path.  It loose the "follow
symlink" part, but it always work and is generally less surprising.

I've checked that all the callers of adjustname() either provide an
absolute path or are happy with it being resolved via the current
buffer working directory.  getbufcwd() respects the global-wd-mode so
that case is also handled.

(Some of the calls can/should be bubbled up or dropped, but that's for
another diff.)

An annoying thing is that adjustname() returns a pointer to a static
area and it's not uncommon for that path to be fed to adjustname()
again, hence the temp buffer.  One quick way to hit that case is "mg
." which will adjustname() in main and then later again in dired_().

ok?


Index: cscope.c
===================================================================
RCS file: /home/cvs/src/usr.bin/mg/cscope.c,v
retrieving revision 1.22
diff -u -p -r1.22 cscope.c
--- cscope.c    8 Mar 2023 04:43:11 -0000       1.22
+++ cscope.c    23 Apr 2023 15:28:25 -0000
@@ -338,7 +338,7 @@ jumptomatch(void)
 
        if (curmatch == NULL || currecord == NULL)
                return (FALSE);
-       adjf = adjustname(currecord->filename, TRUE);
+       adjf = adjustname(currecord->filename);
        if (adjf == NULL)
                return (FALSE);
        if ((bp = findbuffer(adjf)) == NULL)
Index: def.h
===================================================================
RCS file: /home/cvs/src/usr.bin/mg/def.h,v
retrieving revision 1.180
diff -u -p -r1.180 def.h
--- def.h       21 Apr 2023 13:39:37 -0000      1.180
+++ def.h       23 Apr 2023 15:30:55 -0000
@@ -485,7 +485,7 @@ int          ffclose(FILE *, struct buffer *);
 int             ffputbuf(FILE *, struct buffer *, int);
 int             ffgetline(FILE *, char *, int, int *);
 int             fbackupfile(const char *);
-char           *adjustname(const char *, int);
+char           *adjustname(const char *);
 FILE           *startupfile(char *, char *, char *, size_t);
 int             copy(char *, char *);
 struct list    *make_file_list(char *);
Index: dir.c
===================================================================
RCS file: /home/cvs/src/usr.bin/mg/dir.c,v
retrieving revision 1.33
diff -u -p -r1.33 dir.c
--- dir.c       8 Mar 2023 04:43:11 -0000       1.33
+++ dir.c       23 Apr 2023 15:28:25 -0000
@@ -117,7 +117,7 @@ do_makedir(char *path)
        mode_t           dir_mode, f_mode, oumask;
        char            *slash;
 
-       if ((path = adjustname(path, TRUE)) == NULL)
+       if ((path = adjustname(path)) == NULL)
                return (FALSE);
 
        /* Remove trailing slashes */
Index: dired.c
===================================================================
RCS file: /home/cvs/src/usr.bin/mg/dired.c,v
retrieving revision 1.102
diff -u -p -r1.102 dired.c
--- dired.c     8 Mar 2023 04:43:11 -0000       1.102
+++ dired.c     23 Apr 2023 15:28:25 -0000
@@ -469,7 +469,7 @@ d_copy(int f, int n)
        else if (bufp[0] == '\0')
                return (FALSE);
 
-       topath = adjustname(toname, TRUE);
+       topath = adjustname(toname);
        if (stat(topath, &statbuf) == 0) {
                if (S_ISDIR(statbuf.st_mode)) {
                        ret = snprintf(toname, sizeof(toname), "%s/%s",
@@ -479,7 +479,7 @@ d_copy(int f, int n)
                                ewprintf("Directory name too long");
                                return (FALSE);
                        }
-                       topath = adjustname(toname, TRUE);
+                       topath = adjustname(toname);
                }
        }
        if (topath == NULL)
@@ -528,7 +528,7 @@ d_rename(int f, int n)
        else if (bufp[0] == '\0')
                return (FALSE);
 
-       topath = adjustname(toname, TRUE);
+       topath = adjustname(toname);
        if (stat(topath, &statbuf) == 0) {
                if (S_ISDIR(statbuf.st_mode)) {
                        ret = snprintf(toname, sizeof(toname), "%s/%s",
@@ -538,7 +538,7 @@ d_rename(int f, int n)
                                ewprintf("Directory name too long");
                                return (FALSE);
                        }
-                       topath = adjustname(toname, TRUE);
+                       topath = adjustname(toname);
                }
        }
        if (topath == NULL)
@@ -901,7 +901,7 @@ dired_(char *dname)
        int              i;
        size_t           len;
 
-       if ((dname = adjustname(dname, TRUE)) == NULL) {
+       if ((dname = adjustname(dname)) == NULL) {
                dobeep();
                ewprintf("Bad directory name");
                return (NULL);
@@ -1110,7 +1110,7 @@ dired_jump(int f, int n)
        if (ret != TRUE)
                return ret;
 
-       fname = adjustname(fname, TRUE);
+       fname = adjustname(fname);
        if (fname != NULL)
                gotofile(fname);
 
@@ -1134,7 +1134,7 @@ d_gotofile(int f, int n)
        else if (fnp[0] == '\0')
                return (FALSE);
 
-       fpth = adjustname(fpath, TRUE);         /* Removes last '/' if dir...  
*/
+       fpth = adjustname(fpath);               /* Removes last '/' if dir...  
*/
        if (fpth == NULL || strlen(fpth) == lenfpath - 1) { /* ...hence -1.    
*/
                ewprintf("No file to find");    /* Current directory given so  
*/
                return (TRUE);                  /* return at present location. 
*/
Index: extend.c
===================================================================
RCS file: /home/cvs/src/usr.bin/mg/extend.c,v
retrieving revision 1.80
diff -u -p -r1.80 extend.c
--- extend.c    17 Apr 2023 10:11:30 -0000      1.80
+++ extend.c    23 Apr 2023 15:28:25 -0000
@@ -629,7 +629,7 @@ evalfile(int f, int n)
                return (ABORT);
        if (bufp[0] == '\0')
                return (FALSE);
-       if ((bufp = adjustname(fname, TRUE)) == NULL)
+       if ((bufp = adjustname(fname)) == NULL)
                return (FALSE);
        ret = ffropen(&ffp, bufp, NULL);
        if (ret == FIODIR)
Index: file.c
===================================================================
RCS file: /home/cvs/src/usr.bin/mg/file.c,v
retrieving revision 1.103
diff -u -p -r1.103 file.c
--- file.c      8 Mar 2023 04:43:11 -0000       1.103
+++ file.c      23 Apr 2023 15:28:25 -0000
@@ -37,7 +37,7 @@ fileinsert(int f, int n)
                return (ABORT);
        else if (bufp[0] == '\0')
                return (FALSE);
-       adjf = adjustname(bufp, TRUE);
+       adjf = adjustname(bufp);
        if (adjf == NULL)
                return (FALSE);
        return (insertfile(adjf, NULL, FALSE));
@@ -64,7 +64,7 @@ filevisit(int f, int n)
                return (ABORT);
        else if (bufp[0] == '\0')
                return (FALSE);
-       adjf = adjustname(fname, TRUE);
+       adjf = adjustname(fname);
        if (adjf == NULL)
                return (FALSE);
        if (fisdir(adjf) == TRUE)
@@ -116,7 +116,7 @@ do_filevisitalt(char *fn)
        if (status == ABORT || status == FALSE)
                return (ABORT);
 
-       adjf = adjustname(fn, TRUE);
+       adjf = adjustname(fn);
        if (adjf == NULL)
                return (FALSE);
        if (fisdir(adjf) == TRUE)
@@ -165,7 +165,7 @@ poptofile(int f, int n)
                return (ABORT);
        else if (bufp[0] == '\0')
                return (FALSE);
-       adjf = adjustname(fname, TRUE);
+       adjf = adjustname(fname);
        if (adjf == NULL)
                return (FALSE);
        if (fisdir(adjf) == TRUE)
@@ -519,7 +519,7 @@ filewrite(int f, int n)
        else if (bufp[0] == '\0')
                return (FALSE);
 
-       adjfname = adjustname(fname, TRUE);
+       adjfname = adjustname(fname);
        if (adjfname == NULL)
                return (FALSE);
 
Index: fileio.c
===================================================================
RCS file: /home/cvs/src/usr.bin/mg/fileio.c,v
retrieving revision 1.111
diff -u -p -r1.111 fileio.c
--- fileio.c    30 Mar 2023 19:00:02 -0000      1.111
+++ fileio.c    23 Apr 2023 16:02:37 -0000
@@ -288,37 +288,73 @@ fbackupfile(const char *fn)
 }
 
 /*
- * Convert "fn" to a canonicalized absolute filename, replacing
- * a leading ~/ with the user's home dir, following symlinks, and
- * remove all occurrences of /./ and /../
+ * Convert "p" to a canonicalized absolute filename, replacing
+ * a leading ~/ with the user's home dir and resolving all
+ * occurrences of /./ and /../
  */
 char *
-adjustname(const char *fn, int slashslash)
+adjustname(const char *p)
 {
        static char      fnb[PATH_MAX];
-       const char      *cp, *ep = NULL;
-       char            *path;
+       const char      *ep;
+       struct passwd   *pw;
+       char            *q, buf[PATH_MAX], user[LOGIN_NAME_MAX];
+       size_t           ulen;
 
-       if (slashslash == TRUE) {
-               cp = fn + strlen(fn) - 1;
-               for (; cp >= fn; cp--) {
-                       if (ep && (*cp == '/')) {
-                               fn = ep;
-                               break;
-                       }
-                       if (*cp == '/' || *cp == '~')
-                               ep = cp;
-                       else
-                               ep = NULL;
+       buf[0] = '\0';
+       if (*p == '~') {
+               p++;
+               ep = strchr(p, '/');
+               if (ep == NULL)
+                       ep = strchr(p, '\0');
+               ulen = ep - p;
+               if (ulen == 0)
+                       pw = getpwuid(geteuid());
+               else {
+                       if (ulen >= sizeof(user))
+                               return (NULL);
+                       memcpy(user, p, ulen);
+                       user[ulen] = '\0';
+                       pw = getpwnam(user);
                }
-       }
-       if ((path = expandtilde(fn)) == NULL)
+               if (pw == NULL ||
+                   strlcpy(buf, pw->pw_dir, sizeof(buf)) >= sizeof(buf) ||
+                   strlcat(buf, "/", sizeof(buf)) >= sizeof(buf)) {
+                       dobeep_msg("can't expand tilde");
+                       return (NULL);
+               }
+               p = ep;
+       } else if (*p != '/' && !getbufcwd(buf, sizeof(buf)))
                return (NULL);
 
-       if (realpath(path, fnb) == NULL)
-               (void)strlcpy(fnb, path, sizeof(fnb));
+       if (strlcat(buf, p, sizeof(buf)) >= sizeof(buf)) {
+               dobeep_msg("Path too long");
+               return (NULL);
+       }
 
-       free(path);
+       p = q = buf;
+       while (*p && (q - buf) < sizeof(buf)) {
+               if (p[0] == '/' && (p[1] == '/' || p[1] == '\0'))
+                       p++;
+               else if (p[0] == '/' && p[1] == '.' &&
+                   (p[2] == '/' || p[2] == '\0'))
+                       p += 2;
+               else if (p[0] == '/' && p[1] == '.' && p[2] == '.' &&
+                   (p[3] == '/' || p[3] == '\0')) {
+                       p += 3;
+                       while (q != buf && *--q != '/')
+                               ; /* nop */
+               } else
+                       *q++ = *p++;
+       }
+       if (*p != '\0' || q - buf >= sizeof(buf)) {
+               dobeep_msg("Path too long");
+               return (NULL);
+       }
+       if (q == buf)
+               *q++ = '/';
+       *q = '\0';
+       (void)strlcpy(fnb, buf, sizeof(fnb));
        return (fnb);
 }
 
@@ -464,10 +500,10 @@ make_file_list(char *buf)
        len = strlen(buf);
        if (len && buf[len - 1] == '.') {
                buf[len - 1] = 'x';
-               dir = adjustname(buf, TRUE);
+               dir = adjustname(buf);
                buf[len - 1] = '.';
        } else
-               dir = adjustname(buf, TRUE);
+               dir = adjustname(buf);
        if (dir == NULL)
                return (NULL);
        /*
@@ -648,7 +684,7 @@ backuptohomedir(int f, int n)
        char            *p;
 
        if (bkupdir == NULL) {
-               p = adjustname(c, TRUE);
+               p = adjustname(c);
                bkupdir = strndup(p, NFILEN);
                if (bkupdir == NULL)
                        return(FALSE);
@@ -691,67 +727,4 @@ bkupleavetmp(const char *fn)
                return (TRUE);
 
        return (FALSE);
-}
-
-/*
- * Expand file names beginning with '~' if appropriate:
- *   1, if ./~fn exists, continue without expanding tilde.
- *   2, else, if username 'fn' exists, expand tilde with home directory path.
- *   3, otherwise, continue and create new buffer called ~fn.
- */
-char *
-expandtilde(const char *fn)
-{
-       struct passwd   *pw;
-       struct stat      statbuf;
-       const char      *cp;
-       char             user[LOGIN_NAME_MAX], path[NFILEN];
-       char            *ret;
-       size_t           ulen, plen;
-
-       path[0] = '\0';
-
-       if (fn[0] != '~' || stat(fn, &statbuf) == 0) {
-               if ((ret = strndup(fn, NFILEN)) == NULL)
-                       return (NULL);
-               return(ret);
-       }
-       cp = strchr(fn, '/');
-       if (cp == NULL)
-               cp = fn + strlen(fn); /* point to the NUL byte */
-       ulen = cp - &fn[1];
-       if (ulen >= sizeof(user)) {
-               if ((ret = strndup(fn, NFILEN)) == NULL)
-                       return (NULL);
-               return(ret);
-       }
-       if (ulen == 0) /* ~/ or ~ */
-               pw = getpwuid(geteuid());
-       else { /* ~user/ or ~user */
-               memcpy(user, &fn[1], ulen);
-               user[ulen] = '\0';
-               pw = getpwnam(user);
-       }
-       if (pw != NULL) {
-               plen = strlcpy(path, pw->pw_dir, sizeof(path));
-               if (plen == 0 || path[plen - 1] != '/') {
-                       if (strlcat(path, "/", sizeof(path)) >= sizeof(path)) {
-                               dobeep();                               
-                               ewprintf("Path too long");
-                               return (NULL);
-                       }
-               }
-               fn = cp;
-               if (*fn == '/')
-                       fn++;
-       }
-       if (strlcat(path, fn, sizeof(path)) >= sizeof(path)) {
-               dobeep();
-               ewprintf("Path too long");
-               return (NULL);
-       }
-       if ((ret = strndup(path, NFILEN)) == NULL)
-               return (NULL);
-
-       return (ret);
 }
Index: grep.c
===================================================================
RCS file: /home/cvs/src/usr.bin/mg/grep.c,v
retrieving revision 1.50
diff -u -p -r1.50 grep.c
--- grep.c      8 Mar 2023 04:43:11 -0000       1.50
+++ grep.c      23 Apr 2023 15:28:25 -0000
@@ -288,7 +288,7 @@ compile_goto_error(int f, int n)
                        goto fail;
                adjf = path;
        } else {
-               adjf = adjustname(fname, TRUE);
+               adjf = adjustname(fname);
        }
        free(line);
 
Index: main.c
===================================================================
RCS file: /home/cvs/src/usr.bin/mg/main.c,v
retrieving revision 1.95
diff -u -p -r1.95 main.c
--- main.c      14 Apr 2023 15:34:08 -0000      1.95
+++ main.c      23 Apr 2023 15:28:25 -0000
@@ -200,7 +200,7 @@ main(int argc, char **argv)
                        startrow = lval;
                } else {
 notnum:
-                       cp = adjustname(argv[i], FALSE);
+                       cp = adjustname(argv[i]);
                        if (cp != NULL) {
                                if (nfiles == 1)
                                        splitwind(0, 1);
Index: tags.c
===================================================================
RCS file: /home/cvs/src/usr.bin/mg/tags.c,v
retrieving revision 1.27
diff -u -p -r1.27 tags.c
--- tags.c      29 Mar 2023 19:09:04 -0000      1.27
+++ tags.c      23 Apr 2023 15:28:25 -0000
@@ -508,7 +508,7 @@ loadbuffer(char *bname)
                        return (FALSE);
                }
        } else {
-               if ((adjf = adjustname(bname, TRUE)) == NULL)
+               if ((adjf = adjustname(bname)) == NULL)
                        return (FALSE);
                if ((bufp = findbuffer(adjf)) == NULL)
                        return (FALSE);

Reply via email to