[PATCH] New/fixed readlink utility

From: Brad Barden <brad_AT_13os.net>
Date: Sat, 14 Nov 2015 08:46:29 -0600

---
 readlink.1 |  53 ++++++++------
 readlink.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++---------------
 2 files changed, 210 insertions(+), 78 deletions(-)
diff --git a/readlink.1 b/readlink.1
index 43ac58a..e279753 100644
--- a/readlink.1
+++ b/readlink.1
_AT_@ -1,33 +1,46 @@
-.Dd 2015-10-08
+.Dd 2015-11-14
 .Dt READLINK 1
 .Os sbase
 .Sh NAME
 .Nm readlink
-.Nd print symbolic link target or canonical file name
+.Nd Print resolved symbolic links or canonical paths
 .Sh SYNOPSIS
 .Nm
-.Op Fl e | Fl f | Fl m
-.Op Fl n
-.Ar path
+.Op Fl 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,
-.Nm
-exits with a non-zero return value.
+prints the target of a symbolic link (default) or canonical path for each given
+.Ar file
+(
+.Fl f
+or
+.Fl m
+)
 .Sh OPTIONS
 .Bl -tag -width Ds
-.It Fl e | Fl f | Fl m
-Canonicalize
-.Ar name ,
-which needn't be a symlink,
-by recursively following every symlink in its path components.
-All | All but the last | No path components must exist.
-.It Fl n
-Do not print the terminating newline.
+.It Fl f
+All but the last path components must exist.
+.Fl f
+is identical to
+.Xr realpath 1
+default behavior.
+.It Fl m
+None of the path components must exist.
+.It Fl n | Fl z
+Newline | NUL is printed after each resolved path.
+.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
diff --git a/readlink.c b/readlink.c
index 8d2a98d..b231467 100644
--- a/readlink.c
+++ b/readlink.c
_AT_@ -1,92 +1,211 @@
 /* See LICENSE file for copyright and license details. */
 #include <sys/stat.h>
-
-#include <libgen.h>
-#include <unistd.h>
-#include <stdio.h>
+#include <errno.h>
+#include <limits.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 [-e | -f | -m] [-n] path\n", argv0);
+	eprintf("usage: %s [-f | -m] [-z | -n] [q] file [file ...]\n", argv0);
+}
+
+char *
+realpathm(const char * path, char * resolvedpath) {
+	const char *p = path;
+	char buf1[PATH_MAX] = {0}, buf2[PATH_MAX] = {0},
+			 *a = buf1, *b = buf2, *ret = resolvedpath;
+	int i = 0, m = 0;
+
+	if (!*p) {
+		errno = ENOENT;
+		ret = NULL;
+		goto badpath;
+	}
+
+	if (strnlen(path, PATH_MAX) == PATH_MAX) {
+		errno = ENAMETOOLONG;
+		ret = NULL;
+		goto badpath;
+	}
+
+	if (*p == '/') {
+		p++;
+	} else { 
+		if (!realpath(".", a)) {
+			ret = NULL;
+			goto badpath;
+		}
+	}
+	strcat(a, "/");
+
+	while (*p) {
+		i = strlen(a);
+		while (*p && *p != '/') {
+			if (i >= PATH_MAX) {
+				errno = ENAMETOOLONG;
+				ret = NULL;
+				goto badpath;
+			}
+			a[i++] = *p++;
+		}
+		a[i] = '\0';
+		if (*p)
+			++p;
+		if (i > 2 && !strcmp(&a[i-3], "/..")) {
+			if (m)
+				--m;
+			if (i == 3) {
+				strcpy(a, "/");
+				i = 1;
+			}
+			else {
+				i -= 3;
+				while (i > 0 && a[--i] != '/') ;
+				a[i] = '\0';
+			}
+		} else if (i > 1 && !strcmp(&a[i-2], "/.")) {
+			if (i == 2) {
+				strcpy(a, "/");
+				i = 1;
+			} else {
+				i -= 2;
+				a[i] = '\0';
+			}
+		} else if (a[i] == '/') {
+			if (i > 1)
+				a[--i] = '\0';
+		} else if (m) {
+				++m;
+		} else if (!realpath(a, b)) {
+			if (errno == ENOENT) {
+				++m;
+			} else {
+				ret = NULL;
+				goto badpath;
+			}
+		} else if (a == buf1) {
+			a = buf2;
+			b = buf1;
+		} else {
+			a = buf1;
+			b = buf2;
+		}
+		if (a[i-1] != '/' && strlcat(a, "/", PATH_MAX) >= PATH_MAX) {
+			errno = ENAMETOOLONG;
+			ret = NULL;
+			goto badpath;
+		}
+	}
+	i = strlen(a)-1;
+	if (a[i] == '/')
+		a[i] = '\0';
+	if (!ret)
+		ret = emalloc(PATH_MAX);
+	estrlcpy(ret, a, PATH_MAX);
+
+badpath:
+	return ret;
 }
 
 int
 main(int argc, char *argv[])
 {
-	struct stat st;
-	ssize_t n;
-	int nflag = 0, mefflag = 0;
-	char buf1[PATH_MAX], buf2[PATH_MAX], arg[PATH_MAX],
-	     *p, *slash, *prefix, *lp, *b = buf1;
+	int i, n, e = 0, ret = 0;
+	char rp[PATH_MAX], tmp[PATH_MAX], le, *p;
+	struct stat s;
 
 	ARGBEGIN {
-	case 'm':
 	case 'e':
 	case 'f':
-		mefflag = 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");
-
-	switch (mefflag) {
-	case 'm':
-		slash = strchr(argv[0], '/');
-		prefix = (slash == argv[0]) ? "/" : (!slash) ? "./" : "";
-
-		estrlcpy(arg, prefix, sizeof(arg));
-		estrlcat(arg, argv[0], sizeof(arg));
-
-		for (lp = "", p = arg + (argv[0][0] == '/'); *p; p++) {
-			if (*p != '/')
-				continue;
-			*p = '\0';
-			if (!realpath(arg, b)) {
-				*p = '/';
-				goto mdone;
+	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;
 			}
-			b = (b == buf1) ? buf2 : buf1;
-			lp = p;
-			*p = '/';
+			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;
+		default:
+			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 (!realpath(arg, b)) {
-mdone:
-			b = (b == buf1) ? buf2 : buf1;
-			estrlcat(b, lp, sizeof(arg));
+		if (e) {
+			ret = 1;
+			e = 0;
+		} else {
+			fputs(rp, stdout);
+			putchar(le);
 		}
-		break;
-	case 'e':
-		if (stat(argv[0], &st) < 0)
-			eprintf("stat %s:", argv[0]);
-		if (!realpath(argv[0], b))
-			eprintf("realpath %s:", argv[0]);
-		break;
-	case 'f':
-		if (!realpath(argv[0], b))
-			eprintf("realpath %s:", argv[0]);
-		break;
-	default:
-		if ((n = readlink(argv[0], b, PATH_MAX - 1)) < 0)
-			eprintf("readlink %s:", argv[0]);
-		b[n] = '\0';
 	}
-	fputs(b, stdout);
-	if (!nflag)
-		putchar('\n');
-
-	return fshut(stdout, "<stdout>");
+	return ret;
 }
-- 
2.3.6
--NyChO5MpGs3JHJbz--
Received on Mon Sep 17 2001 - 00:00:00 CEST

This archive was generated by hypermail 2.3.0 : Wed Nov 18 2015 - 04:48:11 CET