changeset 707:977e19296b3f

Update readlink so -f works. Add -menq while there.
author Rob Landley <rob@landley.net>
date Tue, 20 Nov 2012 09:21:52 -0600
parents 35a9f9c5b53f
children 50d759f8b371
files lib/lib.c lib/lib.h toys/other/readlink.c
diffstat 3 files changed, 46 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/lib/lib.c	Tue Nov 20 01:00:17 2012 -0600
+++ b/lib/lib.c	Tue Nov 20 09:21:52 2012 -0600
@@ -323,52 +323,37 @@
   if(stat(path, st)) perror_exit("Can't stat %s", path);
 }
 
-// Cannonicalizes path by removing ".", "..", and "//" elements.  This is not
-// the same as realpath(), where "dir/.." could wind up somewhere else by
-// following symlinks.
-char *xabspath(char *path)
+// Cannonicalize path, even to file with one or more missing components at end
+char *xabspath(char *path, unsigned missing) 
 {
-  char *from, *to;
+  char *apath, *temp, *slash;
+  int i=0;
 
   // If this isn't an absolute path, make it one with cwd.
   if (path[0]!='/') {
-    char *cwd=xgetcwd();
-    path = xmsprintf("%s/%s", cwd, path);
-    free(cwd);
-  } else path = xstrdup(path);
-
-  // Loop through path elements
-  from = to = path;
-  while (*from) {
-
-    // Continue any current path component.
-    if (*from!='/') {
-      *(to++) = *(from++);
-      continue;
-    }
-
-    // Skip duplicate slashes.
-    while (*from=='/') from++;
+    char *temp=xgetcwd();
+    apath = xmsprintf("%s/%s", temp, path);
+    free(temp);
+  } else apath = path;
+  slash = apath+strlen(apath);
 
-    // Start of a new filename.  Handle . and ..
-    while (*from=='.') {
-      // Skip .
-      if (from[1]=='/') from += 2;
-      else if (!from[1]) from++;
-      // Back up for ..
-      else if (from[1]=='.') {
-        if (from[2]=='/') from +=3;
-        else if(!from[2]) from+=2;
-        else break;
-        while (to>path && *(--to)!='/');
-      } else break;
-    }
-    // Add directory separator slash.
-    *(to++) = '/';
+  for (;;) {
+    temp = realpath(apath, NULL);
+    if (i) *slash = '/';
+    if (temp || ++i > missing) break;
+    while (slash>apath) if (*--slash == '/') break;
+    *slash=0;
+    free(temp);
   }
-  *to = 0;
 
-  return path;
+  if (i && temp) {
+    slash = xmsprintf("%s%s", temp, slash);
+    free(temp);
+    temp = slash;
+  }
+
+  if (path != apath) free(apath); 
+  return temp;
 }
 
 // Resolve all symlinks, returning malloc() memory.
--- a/lib/lib.h	Tue Nov 20 01:00:17 2012 -0600
+++ b/lib/lib.h	Tue Nov 20 09:21:52 2012 -0600
@@ -116,7 +116,7 @@
 char *xreadfile(char *name);
 char *xgetcwd(void);
 void xstat(char *path, struct stat *st);
-char *xabspath(char *path);
+char *xabspath(char *path, unsigned missing);
 char *xrealpath(char *path);
 void xchdir(char *path);
 void xmkpath(char *path, int mode);
--- a/toys/other/readlink.c	Tue Nov 20 01:00:17 2012 -0600
+++ b/toys/other/readlink.c	Tue Nov 20 09:21:52 2012 -0600
@@ -2,27 +2,26 @@
  *
  * Copyright 2007 Rob Landley <rob@landley.net>
 
-USE_READLINK(NEWTOY(readlink, "<1f", TOYFLAG_BIN))
+USE_READLINK(NEWTOY(readlink, "<1>1femnq[-fem]", TOYFLAG_BIN))
 
 config READLINK
   bool "readlink"
   default n
   help
-    usage: readlink
+    usage: readlink FILE
 
-    Show what a symbolic link points to.
+    With no options, show what symlink points to, return error if not symlink.
+
+    Options for producing cannonical paths (all symlinks/./.. resolved):
 
-config READLINK_F
-  bool "readlink -f"
-  default n
-  depends on READLINK
-  help
-    usage: readlink [-f]
-
-    -f	Show full cannonical path, with no symlinks in it.  Returns
-    	nonzero if nothing could currently exist at this location.
+    -e	cannonical path to existing file (fail if does not exist)
+    -f	cannonical path to creatable file (fail if directory does not exist)
+    -m	cannonical path
+    -n	no trailing newline
+    -q	quiet (no output, just error code)
 */
 
+#define FOR_readlink
 #include "toys.h"
 
 void readlink_main(void)
@@ -31,11 +30,18 @@
 
   // Calculating full cannonical path?
 
-  if (CFG_READLINK_F && toys.optflags) s = xrealpath(*toys.optargs);
-  else s = xreadlink(*toys.optargs);
+  if (toys.optflags & (FLAG_f|FLAG_e|FLAG_m)) {
+    unsigned u = 0;
+
+    if (toys.optflags & FLAG_f) u++;
+    if (toys.optflags & FLAG_m) u=999999999;
+
+    s = xabspath(*toys.optargs, u);
+  } else s = xreadlink(*toys.optargs);
 
   if (s) {
-    xputs(s);
+    if (!(toys.optflags & FLAG_q))
+      xprintf((toys.optflags & FLAG_n) ? "%s" : "%s\n", s);
     if (CFG_TOYBOX_FREE) free(s);
   } else toys.exitval = 1;
 }