Please disregard for now; this introduces an issue since in main()
paths are resolved from the working directory of the current buffer so
`mg foo/bar' works (i.e. opens $PWD/foo/bar) but `mg foo/bar foo/baz'
doesn't since it first opens $PWD/foo/bar and then foo/baz relatively
to it, i.e. $PWD/foo/foo/baz.

On 2023/04/23 18:08:18 +0200, Omar Polo <o...@omarpolo.com> wrote:
> 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