And this is the other frequently encountered issue for people trying to port our mg changes elsewhere. dirname and basename are usually stupid on other platforms, modifying their input strings, and other such silliness.
This fixes this issue by wrapping everything in allocating routines. It requires a couple of extra malloc/frees, but is infinitely harder to get wrong. I thought about passing a buffer and length to the basename wrapper, but this is easier, consistent with xdirname, and, did I mention, easier to get right? It looks like xdirname was added to avoid this bug on such platforms, but as written, it fails to do so. Probably my bad. It is now fixed. Also, all uses of basename are moved to a consistent xbasename. Comments? Index: buffer.c =================================================================== RCS file: /cvs/src/usr.bin/mg/buffer.c,v retrieving revision 1.75 diff -u -r1.75 buffer.c --- buffer.c 18 Jan 2011 17:35:42 -0000 1.75 +++ buffer.c 19 Jan 2011 03:51:36 -0000 @@ -665,8 +666,11 @@ { int count; size_t remain, len; + char *s; - len = strlcpy(bn, basename(fn), bs); + s = xbasename(fn); + len = strlcpy(bn, s, bs); + free(s); if (len >= bs) return (FALSE); Index: def.h =================================================================== RCS file: /cvs/src/usr.bin/mg/def.h,v retrieving revision 1.115 diff -u -r1.115 def.h --- def.h 18 Jan 2011 16:25:40 -0000 1.115 +++ def.h 19 Jan 2011 03:51:36 -0000 @@ -357,6 +358,7 @@ int makebkfile(int, int); int writeout(struct buffer *, char *); void upmodes(struct buffer *); +char *xbasename(const char *); /* line.c X */ struct line *lalloc(int); Index: dired.c =================================================================== RCS file: /cvs/src/usr.bin/mg/dired.c,v retrieving revision 1.47 diff -u -r1.47 dired.c --- dired.c 18 Jan 2011 17:35:42 -0000 1.47 +++ dired.c 19 Jan 2011 03:51:36 -0000 @@ -336,6 +336,7 @@ { struct line *lp, *nlp; char fname[NFILEN]; + char *s; for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) { nlp = lforw(lp); @@ -346,15 +347,17 @@ return (FALSE); case FALSE: if (unlink(fname) < 0) { - ewprintf("Could not delete '%s'", - basename(fname)); + s = xbasename(fname); + ewprintf("Could not delete '%s'", s); + free(s); return (FALSE); } break; case TRUE: if (rmdir(fname) < 0) { - ewprintf("Could not delete directory '%s'", - basename(fname)); + s = xbasename(fname); + ewprintf("Could not delete directory '%s'", s); + free(s); return (FALSE); } break; @@ -371,7 +374,7 @@ int d_copy(int f, int n) { - char frname[NFILEN], toname[NFILEN], *bufp; + char frname[NFILEN], toname[NFILEN], *bufp, *s; int ret; size_t off; struct buffer *bp; @@ -385,8 +388,11 @@ ewprintf("Directory name too long"); return (FALSE); } - if ((bufp = eread("Copy %s to: ", toname, sizeof(toname), - EFDEF | EFNEW | EFCR, basename(frname))) == NULL) + s = xbasename(frname); + bufp = eread("Copy %s to: ", toname, sizeof(toname), + EFDEF | EFNEW | EFCR, s); + free(s); + if (bufp == NULL) return (ABORT); else if (bufp[0] == '\0') return (FALSE); @@ -401,7 +407,7 @@ int d_rename(int f, int n) { - char frname[NFILEN], toname[NFILEN], *bufp; + char frname[NFILEN], toname[NFILEN], *bufp, *s; int ret; size_t off; struct buffer *bp; @@ -415,8 +421,11 @@ ewprintf("Directory name too long"); return (FALSE); } - if ((bufp = eread("Rename %s to: ", toname, - sizeof(toname), EFDEF | EFNEW | EFCR, basename(frname))) == NULL) + s = xbasename(frname); + bufp = eread("Rename %s to: ", toname, + sizeof(toname), EFDEF | EFNEW | EFCR, basename(frname)); + free(s); + if (bufp == NULL) return (ABORT); else if (bufp[0] == '\0') return (FALSE); @@ -445,7 +454,7 @@ int d_shell_command(int f, int n) { - char command[512], fname[MAXPATHLEN], buf[BUFSIZ], *bufp, *cp; + char command[512], fname[MAXPATHLEN], buf[BUFSIZ], *bufp, *cp, *s; int infd, fds[2]; pid_t pid; struct sigaction olda, newa; @@ -463,8 +472,10 @@ } command[0] = '\0'; - if ((bufp = eread("! on %s: ", command, sizeof(command), EFNEW, - basename(fname))) == NULL) + s = xbasename(fname); + bufp = eread("! on %s: ", command, sizeof(command), EFNEW, s); + free(s); + if (bufp == NULL) return (ABORT); infd = open(fname, O_RDONLY); if (infd == -1) { Index: file.c =================================================================== RCS file: /cvs/src/usr.bin/mg/file.c,v retrieving revision 1.73 diff -u -r1.73 file.c --- file.c 18 Jan 2011 16:29:37 -0000 1.73 +++ file.c 19 Jan 2011 03:51:36 -0000 @@ -665,15 +665,35 @@ * place of "/". This means we can always add a trailing * slash and be correct. * Unlike dirname, we allocate. Caller must free. + * Address portability issues by copying argument + * before using. Some implementations modify the input string. */ static char * xdirname(const char *path) { - char *dp; - - dp = dirname(path); + char *dp, *ts; + + ts = strdup(path); + dp = strdup(dirname(ts)); if (*dp && dp[0] == '/' && dp[1] == '\0') - return (strdup("")); - - return (strdup(dp)); + dp[0] = '\0'; + free(ts); + return (dp); +} + +/* + * Allocating version of basename. + * Address portability issue by copying argument + * before using: some implementations modify the input string. + */ +char * +xbasename(const char *path) +{ + char *s, *ts; + + ts = strdup(path); + s = strdup(basename(ts)); + free(ts); + + return (s); }