This is needed as the basis for the readlink -f option.

Signed-off-by: Ben Hutchings <b...@decadent.org.uk>
---
--- a/usr/include/stdlib.h
+++ b/usr/include/stdlib.h
@@ -92,4 +92,6 @@ static __inline__ int grantpt(int __fd)
        return 0;               /* devpts does this all for us! */
 }
 
+__extern char *realpath(const char *, char *);
+
 #endif                         /* _STDLIB_H */
--- a/usr/klibc/Kbuild
+++ b/usr/klibc/Kbuild
@@ -60,7 +60,7 @@ klib-y += vsnprintf.o snprintf.o vsprint
          send.o recv.o \
          access.o chmod.o chown.o dup2.o mknod.o poll.o rename.o stat.o \
          lchown.o link.o rmdir.o unlink.o utimes.o lstat.o mkdir.o \
-         readlink.o select.o symlink.o pipe.o \
+         readlink.o realpath.o select.o symlink.o pipe.o \
          ctype/isalnum.o ctype/isalpha.o ctype/isascii.o \
          ctype/isblank.o ctype/iscntrl.o ctype/isdigit.o \
          ctype/isgraph.o ctype/islower.o ctype/isprint.o \
--- /dev/null
+++ b/usr/klibc/realpath.c
@@ -0,0 +1,147 @@
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static char *__realpath(const char *name, char *resolved_name, int recurse)
+{
+       char link_target[PATH_MAX];
+       struct stat st;
+       char *p, *end;
+       size_t comp_len;
+       int link_len;
+       int exists = 1;
+       int is_dir = 1;
+
+       /* Keep or ignore base dir depending on whether name is relative */
+       p = resolved_name;
+       if (*name != '/')
+               p += strlen(p);
+
+       /* Find end of buffer */
+       end = resolved_name + PATH_MAX;
+
+       /* Iterate over name components */
+       while (*name) {
+               comp_len = strcspn(name, "/");
+
+               /* Only slashes are allowed after a nonexistent component */
+               if (comp_len != 0 && !exists) {
+                       errno = ENOENT;
+                       return NULL;
+               }
+
+               if (comp_len == 0 || (comp_len == 1 && *name == '.')) {
+                       /* Skip empty or "." */
+               } else if (comp_len == 2 && name[0] == '.' && name[1] == '.') {
+                       /* Handle ".." */
+                       p = memrchr(resolved_name, '/', p - resolved_name);
+                       if (!p)
+                               p = resolved_name;
+               } else {
+                       /* Add directory separator and component */
+                       if (end - p < comp_len + 2) {
+                               errno = ENAMETOOLONG;
+                               return NULL;
+                       }
+                       *p = '/';
+                       memcpy(p + 1, name, comp_len);
+                       p[1 + comp_len] = 0;
+
+                       link_len = readlink(resolved_name, link_target,
+                                           sizeof(link_target) - 1);
+                       if (link_len < 0) {
+                               if (errno == EINVAL || errno == ENOENT) {
+                                       /* Not a symlink - continue */
+                                       p += 1 + comp_len;
+                               } else {
+                                       /* Couldn't read symlink */
+                                       return NULL;
+                               }
+                       } else {
+                               /* Limit recursion */
+                               if (recurse == 0) {
+                                       errno = ELOOP;
+                                       return NULL;
+                               }
+
+                               /* Replace component with link target */
+                               *p = 0;
+                               link_target[link_len] = 0;
+                               if (!__realpath(link_target, resolved_name,
+                                               recurse - 1))
+                                       return NULL;
+                               p = resolved_name + strlen(resolved_name);
+                       }
+
+                       if (lstat(resolved_name, &st)) {
+                               if (errno != ENOENT)
+                                       return NULL;
+                               exists = 0;
+                       } else if (!S_ISDIR(st.st_mode)) {
+                               is_dir = 0;
+                       }
+               }
+
+               name += comp_len;
+               if (*name == '/') {
+                       /*
+                        * No slashes are allowed after an existing
+                        * non-directory
+                        */
+                       if (exists && !is_dir) {
+                               errno = ENOTDIR;
+                               return NULL;
+                       }
+                       ++name;
+               }
+       }
+
+       *p = 0;
+       return resolved_name;
+}
+
+char *realpath(const char *name, char *resolved_name)
+{
+       int allocated = 0;
+
+       /* Empty components are allowed, but not a completely empty string */
+       if (*name == 0) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       if (!resolved_name) {
+               resolved_name = malloc(PATH_MAX);
+               if (!resolved_name)
+                       return NULL;
+               allocated = 1;
+       }
+
+       /* Set base dir to cwd if necessary */
+       if (*name != '/') {
+               if (!getcwd(resolved_name, PATH_MAX)) {
+                       if (errno == ERANGE)
+                               errno = ENAMETOOLONG;
+                       goto fail;
+               }
+
+               /* __realpath() never wants a trailing slash */
+               if (!strcmp(resolved_name, "/"))
+                       *resolved_name = 0;
+       }
+
+       if (__realpath(name, resolved_name, 8)) {
+               /* Never return an empty string */
+               if (*resolved_name == 0)
+                       strcpy(resolved_name, "/");
+
+               return resolved_name;
+       }
+
+fail:
+       if (allocated)
+               free(resolved_name);
+       return NULL;
+}

-- 
Ben Hutchings
The two most common things in the universe are hydrogen and stupidity.

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to