BSD 4_4_Lite2 release
[unix-history] / usr / src / lib / libc / gen / getcwd.c
index 4797f7c..90ecfd7 100644 (file)
@@ -1,7 +1,10 @@
 /*
 /*
- * Copyright (c) 1989, 1991, 1993
+ * Copyright (c) 1989, 1991, 1993, 1995
  *     The Regents of the University of California.  All rights reserved.
  *
  *     The Regents of the University of California.  All rights reserved.
  *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)getcwd.c   8.1 (Berkeley) 6/4/93";
+static char sccsid[] = "@(#)getcwd.c   8.5 (Berkeley) 2/7/95";
 #endif /* LIBC_SCCS and not lint */
 
 #include <sys/param.h>
 #include <sys/stat.h>
 
 #endif /* LIBC_SCCS and not lint */
 
 #include <sys/param.h>
 #include <sys/stat.h>
 
-#include <errno.h>
 #include <dirent.h>
 #include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
+static char *getcwd_physical __P((char *, size_t));
+
 #define        ISDOT(dp) \
        (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
            dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
 #define        ISDOT(dp) \
        (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
            dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
@@ -53,6 +59,154 @@ char *
 getcwd(pt, size)
        char *pt;
        size_t size;
 getcwd(pt, size)
        char *pt;
        size_t size;
+{
+       char *pwd;
+       size_t pwdlen;
+       dev_t dev;
+       ino_t ino;
+       struct stat s;
+
+       /* Check $PWD -- if it's right, it's fast. */
+       if ((pwd = getenv("PWD")) != NULL && pwd[0] == '/' && !stat(pwd, &s)) {
+               dev = s.st_dev;
+               ino = s.st_ino;
+               if (!stat(".", &s) && dev == s.st_dev && ino == s.st_ino) {
+                       pwdlen = strlen(pwd);
+                       if (size != 0) {
+                               if (pwdlen + 1 > size) {
+                                       errno = ERANGE;
+                                       return (NULL);
+                               }
+                       } else if ((pt = malloc(pwdlen + 1)) == NULL)
+                               return (NULL);
+                       memmove(pt, pwd, pwdlen);
+                       pt[pwdlen] = '\0';
+                       return (pt);
+               }
+       }
+               
+       return (getcwd_physical(pt, size));
+}
+
+/*
+ * char *realpath(const char *path, char resolved_path[MAXPATHLEN]);
+ *
+ * Find the real name of path, by removing all ".", ".." and symlink
+ * components.  Returns (resolved) on success, or (NULL) on failure,
+ * in which case the path which caused trouble is left in (resolved).
+ */
+char *
+realpath(path, resolved)
+       const char *path;
+       char *resolved;
+{
+       struct stat sb;
+       int fd, n, rootd, serrno;
+       char *p, *q, wbuf[MAXPATHLEN];
+
+       /* Save the starting point. */
+       if ((fd = open(".", O_RDONLY)) < 0) {
+               (void)strcpy(resolved, ".");
+               return (NULL);
+       }
+
+       /*
+        * Find the dirname and basename from the path to be resolved.
+        * Change directory to the dirname component.
+        * lstat the basename part.
+        *     if it is a symlink, read in the value and loop.
+        *     if it is a directory, then change to that directory.
+        * get the current directory name and append the basename.
+        */
+       (void)strncpy(resolved, path, MAXPATHLEN - 1);
+       resolved[MAXPATHLEN - 1] = '\0';
+loop:
+       q = strrchr(resolved, '/');
+       if (q != NULL) {
+               p = q + 1;
+               if (q == resolved)
+                       q = "/";
+               else {
+                       do {
+                               --q;
+                       } while (q > resolved && *q == '/');
+                       q[1] = '\0';
+                       q = resolved;
+               }
+               if (chdir(q) < 0)
+                       goto err1;
+       } else
+               p = resolved;
+
+       /* Deal with the last component. */
+       if (lstat(p, &sb) == 0) {
+               if (S_ISLNK(sb.st_mode)) {
+                       n = readlink(p, resolved, MAXPATHLEN);
+                       if (n < 0)
+                               goto err1;
+                       resolved[n] = '\0';
+                       goto loop;
+               }
+               if (S_ISDIR(sb.st_mode)) {
+                       if (chdir(p) < 0)
+                               goto err1;
+                       p = "";
+               }
+       }
+
+       /*
+        * Save the last component name and get the full pathname of
+        * the current directory.
+        */
+       (void)strcpy(wbuf, p);
+
+       /*
+        * Call the inernal internal version of getcwd which
+        * does a physical search rather than using the $PWD short-cut
+        */
+       if (getcwd_physical(resolved, MAXPATHLEN) == 0)
+               goto err1;
+
+       /*
+        * Join the two strings together, ensuring that the right thing
+        * happens if the last component is empty, or the dirname is root.
+        */
+       if (resolved[0] == '/' && resolved[1] == '\0')
+               rootd = 1;
+       else
+               rootd = 0;
+
+       if (*wbuf) {
+               if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) {
+                       errno = ENAMETOOLONG;
+                       goto err1;
+               }
+               if (rootd == 0)
+                       (void)strcat(resolved, "/");
+               (void)strcat(resolved, wbuf);
+       }
+
+       /* Go back to where we came from. */
+       if (fchdir(fd) < 0) {
+               serrno = errno;
+               goto err2;
+       }
+
+       /* It's okay if the close fails, what's an fd more or less? */
+       (void)close(fd);
+       return (resolved);
+
+err1:  serrno = errno;
+       (void)fchdir(fd);
+err2:  (void)close(fd);
+       errno = serrno;
+       return (NULL);
+}
+
+static char *
+getcwd_physical(pt, size)
+       char *pt;
+       size_t size;
 {
        register struct dirent *dp;
        register DIR *dir;
 {
        register struct dirent *dp;
        register DIR *dir;
@@ -90,7 +244,7 @@ getcwd(pt, size)
        /*
         * Allocate bytes (1024 - malloc space) for the string of "../"'s.
         * Should always be enough (it's 340 levels).  If it's not, allocate
        /*
         * Allocate bytes (1024 - malloc space) for the string of "../"'s.
         * Should always be enough (it's 340 levels).  If it's not, allocate
-        * as necessary.  Special case the first stat, it's ".", not "..".
+        * as necessary.  Special case the first stat, it's ".", not "..".
         */
        if ((up = malloc(upsize = 1024 - 4)) == NULL)
                goto err;
         */
        if ((up = malloc(upsize = 1024 - 4)) == NULL)
                goto err;