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

Reply via email to