sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

commit a531865fe516215b7e37aca4ba5ed3f20e577c16
parent e6c20fe3678eac2245898d0cc765aa3d3703340d
Author: FRIGN <dev@frign.de>
Date:   Fri, 20 Mar 2015 22:09:47 +0100

Audit readlink(1)

1) Properly document e, f and m-flags in the manpage.
2) Clear up the code for the m-flag-handling. Add idiomatic
   '/'-path-traversal as already seen in mkdir(1).
3) Unwrap the SWAP_BUF()-macro.
4) BUGFIX: Actually handle the f-flag properly. Only resolve
   the dirname and append the basename later.
5) Use fputs() instead of printf("%s", ...).

Diffstat:
MREADME | 2+-
Mreadlink.1 | 31++++++++++++++-----------------
Mreadlink.c | 57+++++++++++++++++++++++++++------------------------------
3 files changed, 42 insertions(+), 48 deletions(-)

diff --git a/README b/README @@ -55,7 +55,7 @@ The following tools are implemented ('*' == finished, '#' == UTF-8 support, =*| printenv non-posix none #*| printf yes none =*| pwd yes none -= readlink non-posix none +=*| readlink non-posix none =*| renice yes none =*| rm yes none (-i) =*| rmdir yes none diff --git a/readlink.1 b/readlink.1 @@ -1,4 +1,4 @@ -.Dd January 31, 2015 +.Dd March 20, 2015 .Dt READLINK 1 .Os sbase .Sh NAME @@ -6,30 +6,27 @@ .Nd print symbolic link target or canonical file name .Sh SYNOPSIS .Nm -.Op Fl fn -.Ar name +.Op Fl e | Fl f | Fl m +.Op Fl n +.Ar path .Sh DESCRIPTION -If -.Ar name -is a symbolic link, .Nm -writes its target to stdout. -If -.Fl f -is not set and -.Ar name -is not a symbolic link, +writes the target of +.Ar path , +if it is a symbolic link, to stdout. +If not, .Nm -exits with a nonzero exit code without printing anything. +exits with a non-zero return value. .Sh OPTIONS .Bl -tag -width Ds -.It Fl f +.It Fl e | Fl f | Fl m Canonicalize .Ar name , -which does not need to be a symlink, -by recursively following every symlink in its components. +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 output the trailing newline. +Do not print the terminating newline. .El .Sh SEE ALSO .Xr readlink 2 , diff --git a/readlink.c b/readlink.c @@ -1,6 +1,7 @@ /* See LICENSE file for copyright and license details. */ #include <sys/stat.h> +#include <libgen.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> @@ -11,18 +12,17 @@ static void usage(void) { - eprintf("usage: %s [-efmn] name\n", argv0); + eprintf("usage: %s [-e | -f | -m] [-n] path\n", argv0); } int main(int argc, char *argv[]) { - char buf1[PATH_MAX], buf2[PATH_MAX], arg[PATH_MAX]; - int nflag = 0; - int mefflag = 0; - ssize_t n; struct stat st; - char *p = arg, *lp = NULL, *b = buf1; + ssize_t n; + int nflag = 0, mefflag = 0; + char buf1[PATH_MAX], buf2[PATH_MAX], arg[PATH_MAX], + *p, *slash, *prefix, *lp, *b = buf1; ARGBEGIN { case 'm': @@ -40,57 +40,54 @@ main(int argc, char *argv[]) if (argc != 1) usage(); - if (strlen(argv[0]) > PATH_MAX - 1) + if (strlen(argv[0]) >= PATH_MAX) eprintf("path too long\n"); -#define SWAP_BUF() (b = (b == buf1 ? buf2 : buf1)); switch (mefflag) { case 'm': - if (argv[0][0] == '/') { /* case when path is on '/' */ - arg[0] = '/'; - arg[1] = '\0'; - p++; - } else if (!strchr(argv[0], '/')) { /* relative path */ - arg[0] = '.'; - arg[1] = '/'; - arg[2] = '\0'; - } else - arg[0] = '\0'; + slash = strchr(argv[0], '/'); + prefix = (slash == argv[0]) ? "/" : (!slash) ? "./" : ""; + + estrlcpy(arg, prefix, sizeof(arg)); estrlcat(arg, argv[0], sizeof(arg)); - while ((p = strchr(p, '/'))) { + + for (lp = "", p = arg + (argv[0][0] == '/'); *p; p++) { + if (*p != '/') + continue; *p = '\0'; if (!realpath(arg, b)) { *p = '/'; goto mdone; } - SWAP_BUF(); + b = (b == buf1) ? buf2 : buf1; lp = p; - *p++ = '/'; + *p = '/'; } if (!realpath(arg, b)) { mdone: - SWAP_BUF(); - if (lp) { - /* drop the extra '/' on root */ - lp += (argv[0][0] == '/' && - lp - arg == 1); - estrlcat(b, lp, sizeof(arg)); - } + b = (b == buf1) ? buf2 : buf1; + estrlcat(b, lp, sizeof(arg)); } break; case 'e': if (stat(argv[0], &st) < 0) eprintf("stat %s:", argv[0]); - case 'f': /* fallthrough */ if (!realpath(argv[0], b)) eprintf("realpath %s:", argv[0]); break; + case 'f': + p = dirname(estrdup(argv[0])); + if (!realpath(p, b)) + eprintf("realpath %s:", p); + estrlcat(b, "/", sizeof(arg)); + estrlcat(b, basename(estrdup(argv[0])), sizeof(arg)); + break; default: if ((n = readlink(argv[0], b, PATH_MAX - 1)) < 0) eprintf("readlink %s:", argv[0]); b[n] = '\0'; } - printf("%s", b); + fputs(b, stdout); if (!nflag) putchar('\n');