Module Name: src Committed By: kre Date: Thu Jul 21 09:47:31 UTC 2022
Modified Files: src/usr.bin/realpath: realpath.1 realpath.c Log Message: Make realpath(1) compat with the planned (not yet approved) specification for POSIX.8 (its next version). (Should the requirements change, the code here can be updated). This adds two new options. -e and -E -e just tells realpath to do what it has done since it was imported here. -E makes realpath more compatible with the coreutils version, and allows the final component of the path to not exist (the final component after all symlinks have been expanded, not of the arg on the command line - though that one not existing is one case of the more general spec.). POSIX is not going to specify which of those is the default - instead is planning to require users to always explicitly specify one. The default (now) here is -E. This makes us more compat with coreutils. realpath was added in the first place because it is (apparently) used in real world scripts - the more we can support, the better. Note that in all cases where realpath -e succeeds, realpath -E will succeed as well. This means that any uses of "realpath file" that have been working in HEAD will still work. Some cases that would have failed will work (by default) now. To generate a diff of this commit: cvs rdiff -u -r1.1 -r1.2 src/usr.bin/realpath/realpath.1 \ src/usr.bin/realpath/realpath.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/usr.bin/realpath/realpath.1 diff -u src/usr.bin/realpath/realpath.1:1.1 src/usr.bin/realpath/realpath.1:1.2 --- src/usr.bin/realpath/realpath.1:1.1 Sun Feb 2 21:49:44 2020 +++ src/usr.bin/realpath/realpath.1 Thu Jul 21 09:47:31 2022 @@ -1,4 +1,4 @@ -.\" $NetBSD: realpath.1,v 1.1 2020/02/02 21:49:44 kamil Exp $ +.\" $NetBSD: realpath.1,v 1.2 2022/07/21 09:47:31 kre Exp $ .\"- .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -34,15 +34,15 @@ .\" From: src/bin/pwd/pwd.1,v 1.11 2000/11/20 11:39:39 ru Exp .\" $FreeBSD: head/bin/realpath/realpath.1 314436 2017-02-28 23:42:47Z imp $ .\" -.Dd February 2, 2020 +.Dd July 21, 2022 .Dt REALPATH 1 .Os .Sh NAME .Nm realpath -.Nd return resolved physical path +.Nd return resolved canonical path .Sh SYNOPSIS .Nm -.Op Fl q +.Op Fl eEq .Op Ar path ... .Sh DESCRIPTION The @@ -55,27 +55,78 @@ characters and references to .Pa /./ and .Pa /../ -in -.Ar path . +in each +.Ar path , +and writes the result of each to standard output +followed by a newline. If .Ar path is absent, the current working directory .Pq Sq Pa .\& is assumed. .Pp +With the +.Fl E +option +(the default) +it is not an error for the final component +of the resolved pathname to reference a file +which does not exist. +.Pp +The +.Fl e +option reverses the effect of +.Fl E , +requiring the +.Ar path +to resolve to an existing pathname. +.Pp If .Fl q is specified, warnings will not be printed when .Xr realpath 3 fails. +Messages about other errors, such as bad usage, are still printed. +.Pp +On error, nothing is written +to standard output for that +.Ar path . +If +.Fl q +was not given a diagnostic is written to standard error. .Sh EXIT STATUS .Ex -std +Any failure to resolve a +.Ar path +is an error for this purpose, +the +.Fl q +option is irrelevant. .Sh SEE ALSO +.Xr readlink 1 .Xr realpath 3 +.Sh STANDARDS +The +.Nm +utility is expected to comply with the forthcoming +edition of the POSIX standard. +To be fully POSIX compliant, applications must use +either the +.Fl e +or +.Fl E +option, as which of those (if in fact either, and +not some other behaviour) applies in their absence +is unspecified. +The standard requires support for only a single, mandatory, +.Ar path +argument. .Sh HISTORY The .Nm utility first appeared in .Fx 4.3 and was imported into -.Nx 10 . +.Nx 10 , +but modified to be slightly more compatible with the coreutils version, +and the proposed POSIX standard requirements. Index: src/usr.bin/realpath/realpath.c diff -u src/usr.bin/realpath/realpath.c:1.1 src/usr.bin/realpath/realpath.c:1.2 --- src/usr.bin/realpath/realpath.c:1.1 Sun Feb 2 21:49:44 2020 +++ src/usr.bin/realpath/realpath.c Thu Jul 21 09:47:31 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: realpath.c,v 1.1 2020/02/02 21:49:44 kamil Exp $ */ +/* $NetBSD: realpath.c,v 1.2 2022/07/21 09:47:31 kre Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * @@ -35,34 +35,49 @@ #if 0 __FBSDID("$FreeBSD: head/bin/realpath/realpath.c 326025 2017-11-20 19:49:47Z pfg $"); #else -__RCSID("$NetBSD: realpath.c,v 1.1 2020/02/02 21:49:44 kamil Exp $"); +__RCSID("$NetBSD: realpath.c,v 1.2 2022/07/21 09:47:31 kre Exp $"); #endif #endif /* not lint */ #include <sys/param.h> +#include <sys/stat.h> #include <err.h> +#include <errno.h> #include <stdio.h> +#include <stdbool.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> +static bool process(char *path); static void usage(void) __dead; +static const char options[] = "Eeq"; + +char dot[] = "."; + +bool eflag = false; /* default to -E mode */ +bool qflag = false; + int main(int argc, char *argv[]) { - char buf[PATH_MAX]; - char *p; - const char *path; - int ch, qflag, rval; + char *path; + int ch, rval; setprogname(argv[0]); - qflag = 0; - while ((ch = getopt(argc, argv, "q")) != -1) { + while ((ch = getopt(argc, argv, options)) != -1) { switch (ch) { + case 'e': + eflag = true; + break; + case 'E': + eflag = false; + break; case 'q': - qflag = 1; + qflag = true; break; case '?': default: @@ -71,23 +86,164 @@ main(int argc, char *argv[]) } argc -= optind; argv += optind; - path = *argv != NULL ? *argv++ : "."; - rval = 0; + + path = *argv != NULL ? *argv++ : dot; + rval = 0; do { - if ((p = realpath(path, buf)) == NULL) { - if (!qflag) - warn("%s", path); + if (path[0] == '\0') { + /* ignore -q for this one */ + warnx("Invalid path ''"); rval = 1; - } else - (void)printf("%s\n", p); + continue; + } + if (!process(path)) + rval = 1;; } while ((path = *argv++) != NULL); exit(rval); } +static bool +process(char *path) +{ + char buf[PATH_MAX]; + char buf2[sizeof buf]; + char lbuf[PATH_MAX]; + char *pp, *p, *q, *r, *s; + struct stat sb; + bool dir_reqd = false; + + if ((p = realpath(path, buf)) != NULL) { + (void)printf("%s\n", p); + return true; + } + + if (eflag || errno != ENOENT) { + if (!qflag) + warn("%s", path); + return false; + } + + p = strrchr(path, '/'); + while (p != NULL && p > &path[1] && p[1] == '\0') { + dir_reqd = true; + *p = '\0'; + p = strrchr(path, '/'); + } + + if (p == NULL) { + p = realpath(".", buf); + if ((size_t)snprintf(buf2, sizeof buf2, "%s/%s", buf, path) + >= sizeof buf2) { + if (!qflag) + warnx("%s/%s: path too long", p, path); + return false; + } + path = buf2; + p = strrchr(path, '/'); + if (p == NULL) + abort(); + } + + *p = '\0'; + pp = ++p; + + q = path; r = buf; s = buf2; + while (realpath(*q ? q : "/", r) != NULL) { + ssize_t llen; + + if (strcmp(r, "/") == 0 || strcmp(r, "//") == 0) + r++; + if ((size_t)snprintf(s, sizeof buf, "%s/%s", r, pp) + >= sizeof buf) + return false; + + if (lstat(s, &sb) == -1 || !S_ISLNK(sb.st_mode)) { + (void)printf("%s\n", s); + return true; + } + + q = strchr(r, '\0'); + if (q >= &r[sizeof buf - 3]) { + *p = '/'; + if (!qflag) + warnx("Expanded path for %s too long\n", path); + return false; + } + + if ((llen = readlink(s, lbuf, sizeof lbuf - 2)) == -1) { + *p = '/'; + if (!qflag) + warn("%s", path); + return false; + } + lbuf[llen] = '\0'; + + if (lbuf[0] == '/') { + q = lbuf; + if (dir_reqd) { + lbuf[llen++] = '/'; + lbuf[llen] = '\0'; + } + } else { + if (q != buf2) { + q = buf2; + r = buf; + } else { + q = buf; + r = buf2; + } + + if ((size_t)snprintf(q, sizeof buf, "%s/%s%s", r, lbuf, + (dir_reqd ? "/" : "")) >= sizeof buf) { + *p = '/'; + if (!qflag) + warnx("Expanded path for %s too long\n", + path); + return false; + } + } + + s = realpath(q, r); + if (s != NULL) { + /* this case should almost never happen (race) */ + (void)printf("%s\n", s); + return true; + } + if (errno != ENOENT) { + *p = '/'; + if (!qflag) + warn("%s", path); + return false; + } + + pp = strrchr(q, '/'); + if (pp == NULL) { + /* we just put one there, where did it go? */ + abort(); + } + if (dir_reqd) { + *pp = '\0'; + pp = strrchr(q, '/'); + if (pp == NULL) + abort(); + } + *pp++ = '\0'; + + s = q; + } + + *p = '/'; + + if (!qflag) + warn("%s", path); + return false; +} + static void usage(void) { - (void)fprintf(stderr, "usage: %s [-q] [path ...]\n", getprogname()); + (void)fprintf(stderr, "usage: %s [-%s] [path ...]\n", + getprogname(), options); exit(1); }