[PATCH 2/2] restore readlink -e and -m flags, add realpath

From: Brad Barden <brad_AT_13os.net>
Date: Fri, 20 Nov 2015 14:12:23 -0600

-e and -m are added back to the readlink utility, now working as
intended

when called as 'realpath', readlink's default behavior is now
readlink -f

realpath is implemented with a link to readlink

man page for readlink is updated and man page for realpath is added
---
 Makefile   |   5 +--
 readlink.1 |  52 +++++++++++++++++++----------
 readlink.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++-------------
 realpath.1 |  16 +++++++++
 4 files changed, 141 insertions(+), 43 deletions(-)
 create mode 100644 realpath.1
diff --git a/Makefile b/Makefile
index c4ca41d..93babdd 100644
--- a/Makefile
+++ b/Makefile
_AT_@ -190,7 +190,7 @@ $(LIBUTIL): $(LIBUTILOBJ)
 install: all
 	mkdir -p $(DESTDIR)$(PREFIX)/bin
 	cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin
-	cd $(DESTDIR)$(PREFIX)/bin && ln -f test [ && chmod 755 $(BIN)
+	cd $(DESTDIR)$(PREFIX)/bin && ln -f readlink realpath && ln -f test [ && chmod 755 $(BIN)
 	mkdir -p $(DESTDIR)$(MANPREFIX)/man1
 	for m in $(MAN); do sed "s/^\.Os sbase/.Os sbase $(VERSION)/g" < "$$m" > $(DESTDIR)$(MANPREFIX)/man1/"$$m"; done
 	cd $(DESTDIR)$(MANPREFIX)/man1 && chmod 644 $(MAN)
_AT_@ -219,8 +219,9 @@ sbase-box: $(LIB) $(SRC)
 	echo 'int main(int argc, char *argv[]) { char *s = basename(argv[0]);'                               >> build/$_AT_.c
 	echo 'if(!strcmp(s,"sbase-box")) { argc--; argv++; s = basename(argv[0]); } if(0) ;'                 >> build/$_AT_.c
 	echo "else if (!strcmp(s, \"[\")) return test_main(argc, argv);"                                     >> build/$_AT_.c
+	echo "else if (!strcmp(s, \"realpath\")) return readlink_main(argc, argv);"                          >> build/$_AT_.c
 	for f in $(SRC); do echo "else if(!strcmp(s, \"$${f%.c}\")) return $${f%.c}_main(argc, argv);"; done >> build/$_AT_.c
-	echo 'else { fputs("[ ", stdout);'                                                                   >> build/$_AT_.c
+	echo 'else { fputs("[ realpath ", stdout);'                                                          >> build/$_AT_.c
 	for f in $(SRC); do echo "fputs(\"$${f%.c} \", stdout);"; done                                       >> build/$_AT_.c
 	echo 'putchar(0xa); }; return 0; }'                                                                  >> build/$_AT_.c
 	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $_AT_ build/*.c $(LIB)
diff --git a/readlink.1 b/readlink.1
index 46b4cad..3de0855 100644
--- a/readlink.1
+++ b/readlink.1
_AT_@ -1,32 +1,48 @@
-.Dd 2015-11-16
+.Dd 2015-11-14
 .Dt READLINK 1
 .Os sbase
 .Sh NAME
 .Nm readlink
-.Nd print symbolic link target or canonical file name
+.Nd Print symbolic link targets or canonical paths
 .Sh SYNOPSIS
 .Nm
-.Op Fl f
-.Op Fl n
-.Ar path
+.Op Fl e | f | m
+.Op Fl n | z
+.Op Fl q
+.Ar file
+.Op file ...
 .Sh DESCRIPTION
 .Nm
-writes the target of
-.Ar path ,
-if it is a symbolic link, to stdout.
-If not,
+prints the target of each
+.Ar file
+which must be an existing symbolic link. With the
+.Fl e, f,
+or
+.Fl m
+flags,
 .Nm
-exits with a non-zero return value.
+instead prints a canonicalized path.
 .Sh OPTIONS
 .Bl -tag -width Ds
-.It Fl f
-Canonicalize
-.Ar path ,
-which needn't be a symlink,
-by recursively following every symlink in its path components.
-.It Fl n
-Do not print the terminating newline.
+.It Fl e | f | m
+All | All but the last | None of the path components must exist.
+.Fl f
+is identical to
+.Xr realpath 1
+behavior.
+.It Fl n | Fl z
+Nothing | NUL is printed after each resolved path. By default, a newline is
+printed.
+.It Fl q
+Suppress error messages. Exit status will indicate if errors were encountered.
 .El
+.Sh EXIT STATUS
+.Bl -tag -width Ds
+.It 0
+All paths were successfully resolved.
+.It 1
+An error was encountered resolving at least one of the given paths.
 .Sh SEE ALSO
-.Xr readlink 2 ,
+.Xr readlink 3
 .Xr realpath 3
+.Xr realpath 1
diff --git a/readlink.c b/readlink.c
index d059584..731b580 100644
--- a/readlink.c
+++ b/readlink.c
_AT_@ -1,54 +1,119 @@
 /* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+#include <errno.h>
+#include <libgen.h>
 #include <limits.h>
-#include <stdio.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "util.h"
 
+static int efmflag = 0;
+static int nflag = 0;
+static int qflag = 0;
+static int zflag = 0;
+
 static void
 usage(void)
 {
-	eprintf("usage: %s [-fn] path\n", argv0);
+	eprintf("usage: %s [-e | -f | -m] [-z | -n] [-q] file [file ...]\n", argv0);
 }
 
 int
 main(int argc, char *argv[])
 {
-	char buf[PATH_MAX];
-	ssize_t n;
-	int nflag = 0, fflag = 0;
+	int i, n, e = 0, ret = 0;
+	char rp[PATH_MAX], le, *p;
+	struct stat s;
+
+	if (!strcmp(basename(argv[0]), "realpath"))
+		efmflag = 'f';
+	else
+		efmflag = 'L';
 
 	ARGBEGIN {
+	case 'e':
 	case 'f':
-		fflag = ARGC();
+	case 'm':
+		efmflag = ARGC();
 		break;
 	case 'n':
 		nflag = 1;
 		break;
+	case 'q':
+		qflag = 1;
+		break;
+	case 'z':
+		zflag = 1;
+		break;
 	default:
 		usage();
 	} ARGEND
 
-	if (argc != 1)
+	if (!argc)
 		usage();
 
-	if (strlen(argv[0]) >= PATH_MAX)
-		eprintf("path too long\n");
-
-	if (fflag) {
-		if (!realpath(argv[0], buf))
-			eprintf("realpath %s:", argv[0]);
-	} else {
-		if ((n = readlink(argv[0], buf, PATH_MAX - 1)) < 0)
-			eprintf("readlink %s:", argv[0]);
-		buf[n] = '\0';
+	if (zflag)
+		le = '\0';
+	else if (!nflag)
+		le = '\n';
+	for (i = 0; i < argc; i++) {
+		switch (efmflag) {
+		case 'e':
+			if (!realpath(argv[i], rp)) {
+				if (!qflag)
+					weprintf("realpath '%s':", argv[i]);
+				e = 1;
+			}
+			break;
+		case 'm':
+			if (!realpathm(argv[i], rp)) {
+				if (!qflag)
+					weprintf("realpathm '%s':", argv[i]);
+				e = 1;
+			}
+			break;
+		case 'f':
+			if (!realpathm(argv[i], rp)) {
+				if (!qflag)
+					weprintf("realpathm '%s':", argv[i]);
+				e = 1;
+			} else {
+				p = strrchr(rp, '/');
+				if (p == rp)
+					p = NULL;
+			}
+			if (p) {
+				*p = '\0';
+				if (stat(rp, &s) < 0) {
+					if (!qflag)
+						weprintf("stat '%s':", rp);
+					e = 1;
+					break;
+				} else {
+					*p = '/';
+				}
+			}
+			break;
+		case 'L':
+			if ((n = readlink(argv[i], rp, PATH_MAX-1)) < 0) {
+				if (!qflag)
+					weprintf("readlink '%s':", argv[i]);
+				e = 1;
+			} else {
+				rp[n] = '\0';
+			}
+			break;
+		}
+		if (e) {
+			ret = 1;
+			e = 0;
+		} else {
+			fputs(rp, stdout);
+			putchar(le);
+		}
 	}
-
-	fputs(buf, stdout);
-	if (!nflag)
-		putchar('\n');
-
-	return fshut(stdout, "<stdout>");
+	return ret;
 }
diff --git a/realpath.1 b/realpath.1
new file mode 100644
index 0000000..e1f5f7e
--- /dev/null
+++ b/realpath.1
_AT_@ -0,0 +1,16 @@
+.Dd 2015-11-14
+.Dt REALPATH 1
+.Os sbase
+.Sh NAME
+.Nm realpath
+.Nd Print resolved canonical paths
+.Sh SYNOPSIS
+.Nm
+behaves identically to
+.Xr readlink 1
+with the
+.Fl f
+flag. All other flags are supported.
+.Sh SEE ALSO
+.Xr readlink 1
+.Xr realpath 3
-- 
2.3.6
--dkEUBIird37B8yKS--
Received on Mon Sep 17 2001 - 00:00:00 CEST

This archive was generated by hypermail 2.3.0 : Sat Nov 21 2015 - 07:36:10 CET