Browse Source

Expanding readlink and realpath (in progress).

Rob Landley 1 year ago
parent
commit
bec57325b5
2 changed files with 83 additions and 20 deletions
  1. 5 2
      tests/readlink.test
  2. 78 18
      toys/other/readlink.c

+ 5 - 2
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" "" ""

+ 78 - 18
toys/other/readlink.c

@@ -2,9 +2,8 @@
  *
  * Copyright 2007 Rob Landley <rob@landley.net>
 
-// -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);
+}