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);