Formerly posix/getcwd.c.~9~
[kopensolaris-gnu/glibc.git] / sysdeps / posix / getcwd.c
index 743caa7..3b39e18 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991 Free Software Foundation, Inc.
+/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
 This file is part of the GNU C Library.
 
 The GNU C Library is free software; you can redistribute it and/or
@@ -38,31 +38,57 @@ Cambridge, MA 02139, USA.  */
 char *
 DEFUN(getcwd, (buf, size), char *buf AND size_t size)
 {
+  static CONST char dots[]
+    = "../../../../../../../../../../../../../../../../../../../../../../../\
+../../../../../../../../../../../../../../../../../../../../../../../../../../\
+../../../../../../../../../../../../../../../../../../../../../../../../../..";
+  CONST char *dotp, *dotlist;
+  size_t dotsize;
   dev_t rootdev, thisdev;
   ino_t rootino, thisino;
-  char path[PATH_MAX + 1];
+  char *path;
   register char *pathp;
   struct stat st;
 
-  if (buf != NULL && size == 0)
+if (size == 0)
+  {
+    if (buf != NULL)
+      {
+       errno = EINVAL;
+       return NULL;
+      }
+
+#ifndef PATH_MAX
+#define        PATH_MAX 1024
+#endif
+    size = PATH_MAX + 1;
+  }
+
+  if (buf != NULL)
+    path = buf;
+  else
     {
-      errno = EINVAL;
-      return NULL;
+      path = malloc (size);
+      if (path == NULL)
+       return NULL;
     }
 
-  pathp = &path[sizeof(path)];
+  pathp = path + size;
   *--pathp = '\0';
 
-  if (stat(".", &st) < 0)
+  if (stat (".", &st) < 0)
     return NULL;
   thisdev = st.st_dev;
   thisino = st.st_ino;
 
-  if (stat("/", &st) < 0)
+  if (stat ("/", &st) < 0)
     return NULL;
   rootdev = st.st_dev;
   rootino = st.st_ino;
 
+  dotsize = sizeof (dots) - 1;
+  dotp = &dots[sizeof (dots)];
+  dotlist = dots;
   while (!(thisdev == rootdev && thisino == rootino))
     {
       register DIR *dirstream;
@@ -71,44 +97,63 @@ DEFUN(getcwd, (buf, size), char *buf AND size_t size)
       ino_t dotino;
       char mount_point;
 
-      /* Move up a directory.  */
-      if (chdir("..") < 0)
+      /* Look at the parent directory.  */
+      if (dotp == dotlist)
        {
-         if (pathp != &path[sizeof(path) - 1])
+         /* My, what a deep directory tree you have, Grandma.  */
+         char *new;
+         if (dotlist == dots)
            {
-             /* Try to get back to the original directory.
-                This is the only place where this is possible.  */
-             int save = errno;
-             (void) chdir (pathp);
-             errno = save;
+             new = malloc (dotsize * 2 + 1);
+             if (new == NULL)
+               return NULL;
+             memcpy (new, dots, dotsize);
            }
-         return NULL;
+         else
+           {
+             new = realloc ((PTR) dotlist, dotsize * 2 + 1);
+             if (new == NULL)
+               goto lose;
+           }
+         memcpy (&new[dotsize], new, dotsize);
+         dotp = &new[dotsize];
+         dotsize *= 2;
+         new[dotsize] = '\0';
+         dotlist = new;
        }
 
+      dotp -= 3;
+
       /* Figure out if this directory is a mount point.  */
-      if (stat(".", &st) < 0)
-       return NULL;
+      if (stat (dotp, &st) < 0)
+       goto lose;
       dotdev = st.st_dev;
       dotino = st.st_ino;
       mount_point = dotdev != thisdev;
 
       /* Search for the last directory.  */
-      dirstream = opendir(".");
+      dirstream = opendir (dotp);
       if (dirstream == NULL)
-       return NULL;
-      while ((d = readdir(dirstream)) != NULL)
+       goto lose;
+      while ((d = readdir (dirstream)) != NULL)
        {
          if (d->d_name[0] == '.' &&
              (d->d_namlen == 1 || (d->d_namlen == 2 && d->d_name[1] == '.')))
            continue;
          if (mount_point || d->d_fileno == thisino)
            {
-             if (stat(d->d_name, &st) < 0)
+             char *name = __alloca (dotlist + dotsize - dotp +
+                                    1 + d->d_namlen + 1);
+             memcpy (name, dotp, dotlist + dotsize - dotp);
+             name[dotlist + dotsize - dotp] = '/';
+             memcpy (&name[dotlist + dotsize - dotp + 1],
+                     d->d_name, d->d_namlen + 1);
+             if (stat (name, &st) < 0)
                {
                  int save = errno;
-                 (void) closedir(dirstream);
+                 (void) closedir (dirstream);
                  errno = save;
-                 return NULL;
+                 goto lose;
                }
              if (st.st_dev == thisdev && st.st_ino == thisino)
                break;
@@ -117,45 +162,55 @@ DEFUN(getcwd, (buf, size), char *buf AND size_t size)
       if (d == NULL)
        {
          int save = errno;
-         (void) closedir(dirstream);
+         (void) closedir (dirstream);
          errno = save;
-         return NULL;
+         goto lose;
        }
       else
        {
+         if (pathp - path < d->d_namlen + 1)
+           {
+             if (buf != NULL)
+               {
+                 errno = ERANGE;
+                 return NULL;
+               }
+             else
+               {
+                 size *= 2;
+                 buf = realloc (path, size);
+                 if (buf == NULL)
+                   {
+                     (void) closedir (dirstream);
+                     free (path);
+                     errno = ENOMEM; /* closedir might have changed it.  */
+                     return NULL;
+                   }
+                 pathp = &buf[pathp - path];
+                 path = buf;
+               }
+           }
          pathp -= d->d_namlen;
-         (void) memcpy(pathp, d->d_name, d->d_namlen);
+         (void) closedir (dirstream);
+         (void) memcpy (pathp, d->d_name, d->d_namlen);
          *--pathp = '/';
-         (void) closedir(dirstream);
+         (void) closedir (dirstream);
        }
 
       thisdev = dotdev;
       thisino = dotino;
     }
 
-  if (pathp == &path[sizeof(path) - 1])
+  if (pathp == &path[size - 1])
     *--pathp = '/';
 
-  if (chdir(pathp) < 0)
-    return NULL;
+  if (dotlist != dots)
+    free ((PTR) dotlist);
 
-  {
-    size_t len = &path[sizeof(path)] - pathp;
-    if (buf == NULL)
-      {
-       if (len < (size_t) size)
-         len = size;
-       buf = (char *) malloc(len);
-       if (buf == NULL)
-         return NULL;
-      }
-    else if ((size_t) size < len)
-      {
-       errno = ERANGE;
-       return NULL;
-      }
-    (void) memcpy((PTR) buf, (PTR) pathp, len);
-  }
+  return memmove (path, pathp, path + size - pathp);
 
-  return buf;
+ lose:
+  if (dotlist != dots)
+    free ((PTR) dotlist);
+  return NULL;
 }