From bec57325b5a681c56c1cc2bfed723016776cc410 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Sun, 20 Nov 2022 16:37:32 -0600 Subject: [PATCH] Expanding readlink and realpath (in progress). --- tests/readlink.test | 7 +++- toys/other/readlink.c | 96 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 83 insertions(+), 20 deletions(-) diff --git a/tests/readlink.test b/tests/readlink.test index cc728dbf..cc07cd9f 100755 --- a/tests/readlink.test +++ b/tests/readlink.test @@ -32,12 +32,14 @@ testing "-f link->link (recursive)" \ "readlink -f link 2>/dev/null || echo yes" "yes\n" "" "" testing "-q notlink" "readlink -q file || echo yes" "yes\n" "" "" -testing "-q link" "readlink -q link && echo yes" "yes\n" "" "" +testing "-q link" "readlink -q link && echo yes" "link\nyes\n" "" "" testing "-q notfound" "readlink -q notfound || echo yes" "yes\n" "" "" testing "-e found" "readlink -e file" "$APWD/file\n" "" "" testing "-e notfound" \ "readlink -e notfound 2>/dev/null || echo yes" "yes\n" "" "" testing "-nf ." "readlink -nf ." "$APWD" "" "" +# -n means no newline at _end_. I.E. on last argument. +toyonly testcmd '-nf multiple args' '-n link link' "link\nlink" '' '' mkdir sub && ln -s . here && @@ -51,7 +53,8 @@ testing "-f /dev/null/file" \ "readlink -f /dev/null/file 2>/dev/null || echo yes" "yes\n" "" "" testing "-m missing/dir" "readlink -m sub/two/three" "$APWD/sub/two/three\n" "" "" testing "-m missing/../elsewhere" "readlink -m sub/two/../../three" "$APWD/three\n" "" "" -testing "-m file/dir" "readlink -m sub/bang/two 2>/dev/null || echo err" "err\n" "" "" +# TODO: host bug? That's not missing, that's "cannot exist". +toyonly testing "-m file/dir" "readlink -m sub/bang/two 2>/dev/null || echo err" "err\n" "" "" rm link ln -sf / link || exit 1 testing "-f link->/" "readlink -e link/dev" "/dev\n" "" "" diff --git a/toys/other/readlink.c b/toys/other/readlink.c index 3155dcc5..be596c58 100644 --- a/toys/other/readlink.c +++ b/toys/other/readlink.c @@ -2,9 +2,8 @@ * * Copyright 2007 Rob Landley -// -ef positions match ABS_FILE ABS_PATH -USE_READLINK(NEWTOY(readlink, "<1nqmef(canonicalize)[-mef]", TOYFLAG_USR|TOYFLAG_BIN)) -USE_REALPATH(OLDTOY(realpath, readlink, TOYFLAG_USR|TOYFLAG_BIN)) +USE_READLINK(NEWTOY(readlink, "<1vnf(canonicalize)emqz[-mef][-qv]", TOYFLAG_USR|TOYFLAG_BIN)) +USE_REALPATH(NEWTOY(realpath, "<1(relative-base):R(relative-to):s(no-symlinks)LPemqz[-Ps][-LP][-me]", TOYFLAG_USR|TOYFLAG_BIN)) config READLINK bool "readlink" @@ -20,36 +19,97 @@ config READLINK -f Full path (fail if directory missing) -m Ignore missing entries, show where it would be -n No trailing newline - -q Quiet (no output, just error code) + -q Quiet (no error messages) + -z NUL instead of newline config REALPATH bool "realpath" default y help - usage: realpath FILE... + usage: realpath [-LPemqsz] [--relative-base DIR] [-R DIR] FILE... Display the canonical absolute pathname + + -R Show ../path relative to DIR (--relative-to) + -L Logical path (resolve .. before symlinks) + -P Physical path (default) + -e Canonical path to existing entry (fail if missing) + -m Ignore missing entries, show where it would be + -q Quiet (no error messages) + -s Don't expand symlinks + -z NUL instead of newline + --relative-base Paths below DIR aren't absolute */ -#define FOR_readlink +/* TODO +# relative-to is affected by flags +$ realpath --relative-to=nothing/potato . +realpath: nothing/potato: No such file or directory +$ realpath -m --relative-to=nothing/potato . +../.. + +# -L and -s are similar but not the same +$ realpath -s --relative-to=. ccc +ccc +$ realpath -L --relative-to=. ccc +../../mcm/ccc +*/ + + + +#define FOR_realpath #define FORCE_FLAGS +#define TT this.readlink // workaround: first FOR_ doesn't match filename #include "toys.h" -void readlink_main(void) +GLOBALS( + char *R, *relative_base; +) + +// test TT.relative_base -RsmLP +// Trim .. out early for -s and -L. TODO: in place in the input string. + +static char *resolve(char *arg) +{ + int flags = FLAG(e) ? ABS_FILE : FLAG(m) ? 0 : ABS_PATH; + char *s; + + if (FLAG(s)) flags |= ABS_KEEP; + if (!(s = xabspath(arg, flags)) && !FLAG(q)) perror_msg("%s", arg); + + return s; +} + +// Uses realpath flag context: flags (1 = resolve, 2 = -n) +static void do_paths(int flags) { char **arg, *s; - if (toys.which->name[3]=='l') toys.optflags |= FLAG_f; + if (TT.R && !(TT.R = resolve(TT.R))) xexit(); + if (TT.relative_base && !(TT.relative_base = resolve(TT.relative_base))) + xexit(); + for (arg = toys.optargs; *arg; arg++) { - // Calculating full canonical path? - // Take advantage of flag positions: m = 0, f = ABS_PATH, e = ABS_FILE - if (toys.optflags & (FLAG_f|FLAG_e|FLAG_m)) - s = xabspath(*arg, toys.optflags&(FLAG_f|FLAG_e)); - else s = xreadlink(*arg); - - if (s) { - if (!FLAG(q)) xprintf("%s%s", s, (FLAG(n) && !arg[1]) ? "" : "\n"); - free(s); - } else toys.exitval = 1; + if (!(s = (flags&1) ? resolve(*arg) : xreadlink(*arg))) toys.exitval = 1; + else xprintf(((flags&2) && !arg[1]) ? "%s" : "%s%c", s, '\n'*!FLAG(z)); + free(s); } } + +void realpath_main(void) +{ + do_paths(1); +} + +#define FOR_readlink +#include "generated/flags.h" + +// Convert readlink flag context to realpath (feeding in -nf separately) +void readlink_main(void) +{ + int nf = (toys.optflags/FLAG_f)|!!(FLAG(m)|FLAG(e)); + + toys.optflags &= FLAG_f-1; + if (!FLAG(v)) toys.optflags |= FLAG_q; + do_paths(nf); +} -- 2.39.2