aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/lib.c63
-rw-r--r--lib/lib.h2
-rw-r--r--toys/other/readlink.c34
3 files changed, 45 insertions, 54 deletions
diff --git a/lib/lib.c b/lib/lib.c
index 49f2be5e..83b9bb2e 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -323,52 +323,37 @@ void xstat(char *path, struct stat *st)
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;
- }
+ char *temp=xgetcwd();
+ apath = xmsprintf("%s/%s", temp, path);
+ free(temp);
+ } else apath = path;
+ slash = apath+strlen(apath);
- // Skip duplicate slashes.
- while (*from=='/') from++;
-
- // 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);
+ }
+
+ if (i && temp) {
+ slash = xmsprintf("%s%s", temp, slash);
+ free(temp);
+ temp = slash;
}
- *to = 0;
- return path;
+ if (path != apath) free(apath);
+ return temp;
}
// Resolve all symlinks, returning malloc() memory.
diff --git a/lib/lib.h b/lib/lib.h
index 5e5cbdf1..34ded000 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -116,7 +116,7 @@ char *readfile(char *name);
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);
diff --git a/toys/other/readlink.c b/toys/other/readlink.c
index b7f77f91..b237a510 100644
--- a/toys/other/readlink.c
+++ b/toys/other/readlink.c
@@ -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.
-config READLINK_F
- bool "readlink -f"
- default n
- depends on READLINK
- help
- usage: readlink [-f]
+ Options for producing cannonical paths (all symlinks/./.. resolved):
- -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 @@ void readlink_main(void)
// 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;
}