[BACK]Return to getcwd.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / lib / libc / gen

Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.

Diff for /src/lib/libc/gen/getcwd.c between version 1.1.1.2 and 1.36.2.3

version 1.1.1.2, 1995/02/25 09:11:43 version 1.36.2.3, 2006/01/01 23:12:36
Line 1 
Line 1 
   /*      $NetBSD$        */
   
 /*  /*
  * 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   * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions   * modification, are permitted provided that the following conditions
  * are met:   * are met:
Line 10 
Line 15 
  * 2. Redistributions in binary form must reproduce the above copyright   * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the   *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.   *    documentation and/or other materials provided with the distribution.
  * 3. All advertising materials mentioning features or use of this software   * 3. Neither the name of the University nor the names of its contributors
  *    must display the following acknowledgement:  
  *      This product includes software developed by the University of  
  *      California, Berkeley and its contributors.  
  * 4. Neither the name of the University nor the names of its contributors  
  *    may be used to endorse or promote products derived from this software   *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.   *    without specific prior written permission.
  *   *
Line 31 
Line 32 
  * SUCH DAMAGE.   * SUCH DAMAGE.
  */   */
   
   #include <sys/cdefs.h>
 #if defined(LIBC_SCCS) && !defined(lint)  #if defined(LIBC_SCCS) && !defined(lint)
 static char sccsid[] = "@(#)getcwd.c    8.1 (Berkeley) 6/4/93";  #if 0
   static char sccsid[] = "@(#)getcwd.c    8.5 (Berkeley) 2/7/95";
   #else
   __RCSID("$NetBSD$");
   #endif
 #endif /* LIBC_SCCS and not lint */  #endif /* LIBC_SCCS and not lint */
   
   #include "namespace.h"
 #include <sys/param.h>  #include <sys/param.h>
 #include <sys/stat.h>  #include <sys/stat.h>
   
   #include <assert.h>
 #include <errno.h>  #include <errno.h>
 #include <dirent.h>  
 #include <stdio.h>  
 #include <stdlib.h>  #include <stdlib.h>
 #include <string.h>  #include <string.h>
 #include <unistd.h>  #include <unistd.h>
   
 #define ISDOT(dp) \  #include "extern.h"
         (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \  
             dp->d_name[1] == '.' && dp->d_name[2] == '\0'))  #ifdef __weak_alias
   __weak_alias(getcwd,_getcwd)
   __weak_alias(realpath,_realpath)
   #endif
   
   /*
    * char *realpath(const char *path, char resolved[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 *  char *
 getcwd(pt, size)  realpath(const char *path, char *resolved)
         char *pt;  
         size_t size;  
 {  {
         register struct dirent *dp;          struct stat sb;
         register DIR *dir;          int idx = 0, n, nlnk = 0;
         register dev_t dev;          const char *q;
         register ino_t ino;          char *p, wbuf[2][MAXPATHLEN];
         register int first;          size_t len;
         register char *bpt, *bup;  
         struct stat s;          _DIAGASSERT(path != NULL);
         dev_t root_dev;          _DIAGASSERT(resolved != NULL);
         ino_t root_ino;  
         size_t ptsize, upsize;  
         int save_errno;  
         char *ept, *eup, *up;  
   
         /*          /*
          * If no buffer specified by the user, allocate one as necessary.           * Build real path one by one with paying an attention to .,
          * If a buffer is specified, the size has to be non-zero.  The path           * .. and symbolic link.
          * is built from the end of the buffer backwards.  
          */           */
         if (pt) {  
                 ptsize = 0;          /*
                 if (!size) {           * `p' is where we'll put a new component with prepending
                         errno = EINVAL;           * a delimiter.
            */
           p = resolved;
   
           if (*path == 0) {
                   *p = 0;
                   errno = ENOENT;
                   return (NULL);
           }
   
           /* If relative path, start from current working directory. */
           if (*path != '/') {
                   if (getcwd(resolved, MAXPATHLEN) == NULL) {
                           p[0] = '.';
                           p[1] = 0;
                         return (NULL);                          return (NULL);
                 }                  }
                 ept = pt + size;                  len = strlen(resolved);
         } else {                  if (len > 1)
                 if ((pt = malloc(ptsize = 1024 - 4)) == NULL)                          p += len;
                         return (NULL);          }
                 ept = pt + ptsize;  
   loop:
           /* Skip any slash. */
           while (*path == '/')
                   path++;
   
           if (*path == 0) {
                   if (p == resolved)
                           *p++ = '/';
                   *p = 0;
                   return (resolved);
           }
   
           /* Find the end of this component. */
           q = path;
           do
                   q++;
           while (*q != '/' && *q != 0);
   
           /* Test . or .. */
           if (path[0] == '.') {
                   if (q - path == 1) {
                           path = q;
                           goto loop;
                   }
                   if (path[1] == '.' && q - path == 2) {
                           /* Trim the last component. */
                           if (p != resolved)
                                   while (*--p != '/')
                                           ;
                           path = q;
                           goto loop;
                   }
           }
   
           /* Append this component. */
           if (p - resolved + 1 + q - path + 1 > MAXPATHLEN) {
                   errno = ENAMETOOLONG;
                   if (p == resolved)
                           *p++ = '/';
                   *p = 0;
                   return (NULL);
         }          }
         bpt = ept - 1;          p[0] = '/';
         *bpt = '\0';          memcpy(&p[1], path,
               /* LINTED We know q > path. */
               q - path);
           p[1 + q - path] = 0;
   
         /*          /*
          * Allocate bytes (1024 - malloc space) for the string of "../"'s.           * If this component is a symlink, toss it and prepend link
          * Should always be enough (it's 340 levels).  If it's not, allocate           * target to unresolved path.
          * as necessary.  Special * case the first stat, it's ".", not "..".  
          */           */
         if ((up = malloc(upsize = 1024 - 4)) == NULL)          if (lstat(resolved, &sb) == -1) {
                 goto err;                  return (NULL);
         eup = up + MAXPATHLEN;          }
         bup = up;          if (S_ISLNK(sb.st_mode)) {
         up[0] = '.';                  if (nlnk++ >= MAXSYMLINKS) {
         up[1] = '\0';                          errno = ELOOP;
                           return (NULL);
         /* Save root values, so know when to stop. */                  }
         if (stat("/", &s))                  n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1);
                 goto err;                  if (n < 0)
         root_dev = s.st_dev;                          return (NULL);
         root_ino = s.st_ino;                  if (n == 0) {
                           errno = ENOENT;
         errno = 0;                      /* XXX readdir has no error return. */                          return (NULL);
   
         for (first = 1;; first = 0) {  
                 /* Stat the current level. */  
                 if (lstat(up, &s))  
                         goto err;  
   
                 /* Save current node values. */  
                 ino = s.st_ino;  
                 dev = s.st_dev;  
   
                 /* Check for reaching root. */  
                 if (root_dev == dev && root_ino == ino) {  
                         *--bpt = '/';  
                         /*  
                          * It's unclear that it's a requirement to copy the  
                          * path to the beginning of the buffer, but it's always  
                          * been that way and stuff would probably break.  
                          */  
                         (void)bcopy(bpt, pt, ept - bpt);  
                         free(up);  
                         return (pt);  
                 }                  }
   
                 /*                  /* Append unresolved path to link target and switch to it. */
                  * Build pointer to the parent directory, allocating memory                  if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) {
                  * as necessary.  Max length is 3 for "../", the largest                          errno = ENAMETOOLONG;
                  * possible component name, plus a trailing NULL.                          return (NULL);
                  */                  }
                 if (bup + 3  + MAXNAMLEN + 1 >= eup) {                  memcpy(&wbuf[idx][n], q, len + 1);
                         if ((up = realloc(up, upsize *= 2)) == NULL)                  path = wbuf[idx];
                                 goto err;                  idx ^= 1;
                         bup = up;  
                         eup = up + upsize;                  /* If absolute symlink, start from root. */
                 }                  if (*path == '/')
                 *bup++ = '.';                          p = resolved;
                 *bup++ = '.';                  goto loop;
                 *bup = '\0';          }
           if (*q == '/' && !S_ISDIR(sb.st_mode)) {
                 /* Open and stat parent directory. */                  errno = ENOTDIR;
                 if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))                  return (NULL);
                         goto err;          }
   
                 /* Add trailing slash for next directory. */          /* Advance both resolved and unresolved path. */
                 *bup++ = '/';          p += 1 + q - path;
           path = q;
                 /*          goto loop;
                  * If it's a mount point, have to stat each element because  }
                  * the inode number in the directory is for the entry in the  
                  * parent directory, not the inode number of the mounted file.  char *
                  */  getcwd(char *pt, size_t size)
                 save_errno = 0;  {
                 if (s.st_dev == dev) {          char *npt;
                         for (;;) {  
                                 if (!(dp = readdir(dir)))  
                                         goto notfound;  
                                 if (dp->d_fileno == ino)  
                                         break;  
                         }  
                 } else  
                         for (;;) {  
                                 if (!(dp = readdir(dir)))  
                                         goto notfound;  
                                 if (ISDOT(dp))  
                                         continue;  
                                 bcopy(dp->d_name, bup, dp->d_namlen + 1);  
   
                                 /* Save the first error for later. */  
                                 if (lstat(up, &s)) {  
                                         if (!save_errno)  
                                                 save_errno = errno;  
                                         errno = 0;  
                                         continue;  
                                 }  
                                 if (s.st_dev == dev && s.st_ino == ino)  
                                         break;  
                         }  
   
                 /*  
                  * Check for length of the current name, preceding slash,  
                  * leading slash.  
                  */  
                 if (bpt - pt <= dp->d_namlen + (first ? 1 : 2)) {  
                         size_t len, off;  
   
                         if (!ptsize) {  
                                 errno = ERANGE;  
                                 goto err;  
                         }  
                         off = bpt - pt;  
                         len = ept - bpt;  
                         if ((pt = realloc(pt, ptsize *= 2)) == NULL)  
                                 goto err;  
                         bpt = pt + off;  
                         ept = pt + ptsize;  
                         (void)bcopy(bpt, ept - len, len);  
                         bpt = ept - len;  
                 }  
                 if (!first)  
                         *--bpt = '/';  
                 bpt -= dp->d_namlen;  
                 bcopy(dp->d_name, bpt, dp->d_namlen);  
                 (void)closedir(dir);  
   
                 /* Truncate any file name. */          /*
                 *bup = '\0';           * If a buffer is specified, the size has to be non-zero.
            */
           if (pt != NULL) {
                   if (size == 0) {
                           /* __getcwd(pt, 0) results ERANGE. */
                           errno = EINVAL;
                           return (NULL);
                   }
                   if (__getcwd(pt, size) >= 0)
                           return (pt);
                   return (NULL);
         }          }
   
 notfound:  
         /*          /*
          * If readdir set errno, use it, not any saved error; otherwise,           * If no buffer specified by the user, allocate one as necessary.
          * didn't find the current directory in its parent directory, set  
          * errno to ENOENT.  
          */           */
         if (!errno)          size = 1024 >> 1;
                 errno = save_errno ? save_errno : ENOENT;          do {
         /* FALLTHROUGH */                  if ((npt = realloc(pt, size <<= 1)) == NULL)
 err:                          break;
         if (ptsize)                  pt = npt;
                 free(pt);                  if (__getcwd(pt, size) >= 0)
         free(up);                          return (pt);
           } while (size <= MAXPATHLEN * 4 && errno == ERANGE);
   
           free(pt);
         return (NULL);          return (NULL);
 }  }

Legend:
Removed from v.1.1.1.2  
changed lines
  Added in v.1.36.2.3

CVSweb <webmaster@jp.NetBSD.org>