--- 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 - 07:36:10 CET