rename getwd to getcwd for POSIX.1 compatibility
authorKeith Bostic <bostic@ucbvax.Berkeley.EDU>
Thu, 21 Feb 1991 00:26:10 +0000 (16:26 -0800)
committerKeith Bostic <bostic@ucbvax.Berkeley.EDU>
Thu, 21 Feb 1991 00:26:10 +0000 (16:26 -0800)
add System V, X/Open style path malloc'ing if user argument is NULL.

SCCS-vsn: lib/libc/gen/getcwd.c 5.9
SCCS-vsn: lib/libc/gen/getcwd.3 6.3

usr/src/lib/libc/gen/getcwd.3
usr/src/lib/libc/gen/getcwd.c

index 40243a7..a08d8ba 100644 (file)
-.\" Copyright (c) 1983 Regents of the University of California.
-.\" All rights reserved.  The Berkeley software License Agreement
-.\" specifies the terms and conditions for redistribution.
+.\" Copyright (c) 1991 The Regents of the University of California.
+.\" All rights reserved.
 .\"
 .\"
-.\"    @(#)getcwd.3    6.2 (Berkeley) %G%
+.\" %sccs.include.redist.man%
+.\"
+.\"    @(#)getcwd.3    6.3 (Berkeley) %G%
 .\"
 .TH GETWD 3 ""
 .UC 5
 .SH NAME
 .\"
 .TH GETWD 3 ""
 .UC 5
 .SH NAME
-getwd \- get current working directory pathname
+getwd \- get working directory pathname
 .SH SYNOPSIS
 .nf
 .SH SYNOPSIS
 .nf
-.B char *getwd(pathname)
-.B char *pathname;
+.ft B
+char *getcwd(char *buf, size_t size);
+char *getwd(char *buf);
+.ft R
 .fi
 .SH DESCRIPTION
 .fi
 .SH DESCRIPTION
+The
+.I getcwd
+function copies the absolute pathname of the current working directory
+into the memory referenced by
+.I buf
+and returns a pointer to
+.IR buf .
+The
+.I size
+argument is the size, in bytes, of the array referenced by
+.IR buf .
+.PP
+If
+.I buf
+is NULL, space is allocated as necessary to store the pathname.
+This space may later be
+.IR free 'd.
+.PP
+The function
+.I getwd
+is a compatibility routine which calls
+.I getcwd
+with its
+.I buf
+argument and a size of MAXPATHLEN (as defined in the include
+file <sys/param.h>).
+Obviously,
+.I buf
+should be at least MAXPATHLEN bytes in length.
+.PP
+These routines have traditionally been used by programs to save the
+name of a working directory for the purpose of returning to it.
+A much faster and less error-prone method of accomplishing this is to
+open the current directory (``.'') and use the
+.IR fchdir (2)
+function to return.
+.SH RETURN VALUES
+Upon successful completion, a pointer to the pathname is returned.
+Otherwise a NULL pointer is returned and
+.I errno
+is set to indicate the error.
+In addition,
+.I getwd
+copies the error message associated with
+.I errno
+into the memory referenced by
+.IR buf .
+.SH ERRORS
+.I Getcwd
+will fail if:
+.TP 15
+[EACCESS]
+Read or search permission was denied for a component of the pathname.
+.TP 15
+[EINVAL]
+The
+.I size
+argument is zero.
+.TP 15
+[ENOENT]
+A component of the pathname no longer exists.
+.TP 15
+[ENOMEM]
+Insufficient memory is available.
+.TP 15
+[ERANGE]
+The
+.I size 
+argument is greater than zero but smaller than the length of the pathname
+plus 1.
+.SH BUGS
 .I Getwd
 .I Getwd
-copies the absolute pathname of the current working directory to
-.I pathname
-and returns a pointer to the result.
-.SH LIMITATIONS
-Maximum pathname length is MAXPATHLEN characters (1024),
-as defined in
-.I <sys/param.h>.
-.SH DIAGNOSTICS
-.I Getwd
-returns zero and places a message in
-.I pathname
-if an error occurs.
+does not do sufficient error checking and is not able to return very
+long, but valid, paths.
+It is provided for compatibility.
+.SH SEE ALSO
+chdir(2), fchdir(2), malloc(3), strerror(3)
+.SH STANDARDS
+.I Getcwd
+conforms to IEEE Std 1003.1-1988 (``POSIX'').
+The ability to specify a NULL pointer and have
+.I getcwd
+allocate memory as necessary is an extension.
index 1c5659f..4e2519f 100644 (file)
@@ -1,17 +1,20 @@
 /*
 /*
- * Copyright (c) 1989 The Regents of the University of California.
+ * Copyright (c) 1989, 1991 The Regents of the University of California.
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)getcwd.c   5.8 (Berkeley) %G%";
+static char sccsid[] = "@(#)getcwd.c   5.9 (Berkeley) %G%";
 #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 <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
 #define        ISDOT(dp) \
 #include <string.h>
 
 #define        ISDOT(dp) \
@@ -19,121 +22,180 @@ static char sccsid[] = "@(#)getcwd.c      5.8 (Berkeley) %G%";
            dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
 
 char *
            dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
 
 char *
-getwd(store)
-       char *store;
+getcwd(pt, size)
+       char *pt;
+       size_t size;
 {
 {
-       extern int errno;
        register struct dirent *dp;
        register DIR *dir;
        register struct dirent *dp;
        register DIR *dir;
+       register dev_t dev;
        register ino_t ino;
        register ino_t ino;
-       register char *pp, *pu;
        register int first;
        register int first;
+       register char *bpt, *bup;
        struct stat s;
        struct stat s;
-       dev_t root_dev, dev;
+       dev_t root_dev;
        ino_t root_ino;
        ino_t root_ino;
-       int save_errno, found;
-       char path[MAXPATHLEN], up[MAXPATHLEN], *file;
+       size_t ptsize, upsize;
+       int save_errno;
+       char *ept, *eup, *up;
+
+       /*
+        * If no buffer specified by the user, allocate one as necessary.
+        * If a buffer is specified, the size has to be non-zero.  The path
+        * is built from the end of the buffer backwards.
+        */
+       if (pt) {
+               ptsize = 0;
+               if (!size) {
+                       errno = EINVAL;
+                       return((char *)NULL);
+               }
+               ept = pt + size;
+       } else {
+               if (!(pt = (char *)malloc(ptsize = 1024 - 4)))
+                       return((char *)NULL);
+               ept = pt + ptsize;
+       }
+       bpt = ept - 1;
+       *ept = '\0';
+
+       /*
+        * 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 "..".
+        */
+       if (!(up = (char *)malloc(upsize = 1024 - 4)))
+               goto err;
+       eup = up + MAXPATHLEN;
+       bup = up;
+       up[0] = '.';
+       up[1] = '\0';
 
 
-       /* save root values */
-       if (stat("/", &s)) {
-               file = "/";
+       /* Save root values, so know when to stop. */
+       if (stat("/", &s))
                goto err;
                goto err;
-       }
        root_dev = s.st_dev;
        root_ino = s.st_ino;
 
        root_dev = s.st_dev;
        root_ino = s.st_ino;
 
-       /* init path pointer; built from the end of the buffer */
-       pp = path + sizeof(path) - 1;
-       *pp = '\0';
+       errno = 0;                      /* XXX readdir has no error return. */
 
 
-       /* special case first stat, it's ".", not ".." */
-       up[0] = '.';
-       up[1] = '\0';
-
-       for (pu = up, first = 1;; first = 0) {
-               /* stat current level */
-               if (lstat(up, &s)) {
-                       file = up;
+       for (first = 1;; first = 0) {
+               /* Stat the current level. */
+               if (lstat(up, &s))
                        goto err;
                        goto err;
-               }
 
 
-               /* save current node values */
+               /* Save current node values. */
                ino = s.st_ino;
                dev = s.st_dev;
 
                ino = s.st_ino;
                dev = s.st_dev;
 
-               /* check for root */
+               /* Check for reaching root. */
                if (root_dev == dev && root_ino == ino) {
                if (root_dev == dev && root_ino == ino) {
-                       *store = '/';
-                       (void) strcpy(store + 1, pp);
-                       return (store);
+                       *--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);
                }
 
                }
 
-               *pu++ = '.';
-               *pu++ = '.';
-               *pu = '\0';
+               /*
+                * Build pointer to the parent directory, allocating memory
+                * as necessary.  Max length is 3 for "../", the largest
+                * possible component name, plus a trailing NULL.
+                */
+               if (bup + 3  + MAXNAMLEN + 1 >= eup) {
+                       if (!(up = (char *)realloc(up, upsize *= 2)))
+                               goto err;
+                       eup = up + upsize;
+               }
+               *bup++ = '.';
+               *bup++ = '.';
+               *bup = '\0';
 
 
-               /* open and stat parent */
-               if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) {
-                       file = up;
+               /* Open and stat parent directory. */
+               if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))
                        goto err;
                        goto err;
-               }
-               found = save_errno = 0;
 
 
-               *pu++ = '/';
+               /* Add trailing slash for next directory. */
+               *bup++ = '/';
 
                /*
 
                /*
-                * if it's a mount point you have to stat each element because
+                * 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.
                 */
                 * the inode number in the directory is for the entry in the
                 * parent directory, not the inode number of the mounted file.
                 */
+               save_errno = 0;
                if (s.st_dev == dev) {
                if (s.st_dev == dev) {
-                       while (dp = readdir(dir))
+                       for (;;) {
+                               if (!(dp = readdir(dir)))
+                                       goto notfound;
                                if (dp->d_fileno == ino)
                                if (dp->d_fileno == ino)
-                                       goto hit;
-               } else {
-                       while (dp = readdir(dir)) {
+                                       break;
+                       }
+               } else
+                       for (;;) {
+                               if (!(dp = readdir(dir)))
+                                       goto notfound;
                                if (ISDOT(dp))
                                        continue;
                                if (ISDOT(dp))
                                        continue;
-                               bcopy(dp->d_name, pu, dp->d_namlen + 1);
+                               bcopy(dp->d_name, bup, dp->d_namlen + 1);
+
+                               /* Save the first error for later. */
                                if (lstat(up, &s)) {
                                if (lstat(up, &s)) {
-                                       file = dp->d_name;
-                                       save_errno = errno;
+                                       if (!save_errno)
+                                               save_errno = errno;
                                        errno = 0;
                                        continue;
                                }
                                        errno = 0;
                                        continue;
                                }
-                               if (s.st_dev == dev && s.st_ino == ino) {
-hit:                                   if (!first)
-                                               *--pp = '/';
-                                       pp -= dp->d_namlen;
-                                       bcopy(dp->d_name, pp, dp->d_namlen);
-                                       found = 1;
+                               if (s.st_dev == dev && s.st_ino == ino)
                                        break;
                                        break;
-                               }
-                       }
-                       if (errno) {
-                               file = up;
-                               save_errno = errno;
                        }
                        }
-               }
-               (void) closedir(dir);
 
 
-               *pu = '\0';
+               /*
+                * 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 (!found) {
-                       /*
-                        * We didn't find the current level in its parent
-                        * directory; figure out what to complain about.
-                        */
-                       if (save_errno) {
-                               errno = save_errno;
+                       if (!ptsize) {
+                               errno = ERANGE;
                                goto err;
                        }
                                goto err;
                        }
-                       (void) sprintf(store, "%s not found in %s?\n",
-                               first ? "." : pp, up);
-                       return ((char *)NULL);
+                       off = bpt - pt;
+                       len = ept - bpt;
+                       if (!(pt = (char *)realloc(pt, ptsize *= 2)))
+                               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';
        }
        }
+
+notfound:
+       /*
+        * If readdir set errno, use it, not any saved error; otherwise,
+        * didn't find the current directory in its parent directory, set
+        * errno to ENOENT.
+        */
+       if (!errno)
+               errno = save_errno ? save_errno : ENOENT;
+       /* FALLTHROUGH */
 err:
 err:
-       (void) sprintf(store, "getwd: %s: %s", file, strerror(errno));
-       return ((char *)NULL);
+       if (ptsize)
+               free(pt);
+       free(up);
+       return((char *)NULL);
 }
 }