* lib/regcomp.c (re_compile_fastmap_iter, init_dfa, init_word_char):
[gnulib.git] / lib / getcwd.c
index 1bc7ab6..86070ea 100644 (file)
@@ -1,5 +1,5 @@
-/* Copyright (C) 1991,92,93,94,95,96,97,98,99,2004 Free Software Foundation,
-   Inc.
+/* Copyright (C) 1991,92,93,94,95,96,97,98,99,2004,2005 Free Software
+   Foundation, Inc.
    This file is part of the GNU C Library.
 
    This program is free software; you can redistribute it and/or modify
@@ -14,7 +14,7 @@
 
    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation,
-   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
 
 #ifdef HAVE_CONFIG_H
 # include "config.h"
@@ -30,9 +30,7 @@
 #include <stdbool.h>
 #include <stddef.h>
 
-#if HAVE_FCNTL_H
-# include <fcntl.h> /* For AT_FDCWD on Solaris 9.  */
-#endif
+#include <fcntl.h> /* For AT_FDCWD on Solaris 9.  */
 
 #ifndef __set_errno
 # define __set_errno(val) (errno = (val))
 
 #include <limits.h>
 
+#ifdef ENAMETOOLONG
+# define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
+#else
+# define is_ENAMETOOLONG(x) 0
+#endif
+
 #ifndef MAX
 # define MAX(a, b) ((a) < (b) ? (b) : (a))
 #endif
 # define __readdir readdir
 #endif
 \f
-/* Get the pathname of the current working directory, and put it in SIZE
+/* Get the name of the current working directory, and put it in SIZE
    bytes of BUF.  Returns NULL if the directory couldn't be determined or
    SIZE was too small.  If successful, returns BUF.  In GNU, if BUF is
    NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
@@ -141,11 +145,24 @@ __getcwd (char *buf, size_t size)
   DIR *dirstream = NULL;
   dev_t rootdev, thisdev;
   ino_t rootino, thisino;
-  char *path;
-  register char *pathp;
+  char *dir;
+  register char *dirp;
   struct stat st;
-  int prev_errno = errno;
   size_t allocated = size;
+  size_t used;
+
+#if HAVE_PARTLY_WORKING_GETCWD && !defined AT_FDCWD
+  /* The system getcwd works, except it sometimes fails when it
+     shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT.  If
+     AT_FDCWD is not defined, the algorithm below is O(N**2) and this
+     is much slower than the system getcwd (at least on GNU/Linux).
+     So trust the system getcwd's results unless they look
+     suspicious.  */
+# undef getcwd
+  dir = getcwd (buf, size);
+  if (dir || (errno != ERANGE && !is_ENAMETOOLONG (errno) && errno != ENOENT))
+    return dir;
+#endif
 
   if (size == 0)
     {
@@ -158,17 +175,17 @@ __getcwd (char *buf, size_t size)
       allocated = BIG_FILE_NAME_LENGTH + 1;
     }
 
-  if (buf != NULL)
-    path = buf;
-  else
+  if (buf == NULL)
     {
-      path = malloc (allocated);
-      if (path == NULL)
+      dir = malloc (allocated);
+      if (dir == NULL)
        return NULL;
     }
+  else
+    dir = buf;
 
-  pathp = path + allocated;
-  *--pathp = '\0';
+  dirp = dir + allocated;
+  *--dirp = '\0';
 
   if (__lstat (".", &st) < 0)
     goto lose;
@@ -299,10 +316,10 @@ __getcwd (char *buf, size_t size)
        }
       else
        {
-         size_t pathroom = pathp - path;
+         size_t dirroom = dirp - dir;
          size_t namlen = _D_EXACT_NAMLEN (d);
 
-         if (pathroom <= namlen)
+         if (dirroom <= namlen)
            {
              if (size != 0)
                {
@@ -316,20 +333,20 @@ __getcwd (char *buf, size_t size)
 
                  allocated += MAX (allocated, namlen);
                  if (allocated < oldsize
-                     || ! (tmp = realloc (path, allocated)))
+                     || ! (tmp = realloc (dir, allocated)))
                    goto memory_exhausted;
 
                  /* Move current contents up to the end of the buffer.
                     This is guaranteed to be non-overlapping.  */
-                 pathp = memcpy (tmp + allocated - (oldsize - pathroom),
-                                 tmp + pathroom,
-                                 oldsize - pathroom);
-                 path = tmp;
+                 dirp = memcpy (tmp + allocated - (oldsize - dirroom),
+                                tmp + dirroom,
+                                oldsize - dirroom);
+                 dir = tmp;
                }
            }
-         pathp -= namlen;
-         memcpy (pathp, d->d_name, namlen);
-         *--pathp = '/';
+         dirp -= namlen;
+         memcpy (dirp, d->d_name, namlen);
+         *--dirp = '/';
        }
 
       thisdev = dotdev;
@@ -342,20 +359,27 @@ __getcwd (char *buf, size_t size)
       goto lose;
     }
 
-  if (pathp == &path[allocated - 1])
-    *--pathp = '/';
+  if (dirp == &dir[allocated - 1])
+    *--dirp = '/';
 
 #ifndef AT_FDCWD
   if (dotlist != dots)
     free (dotlist);
 #endif
 
-  memmove (path, pathp, path + allocated - pathp);
+  used = dir + allocated - dirp;
+  memmove (dir, dirp, used);
+
+  if (buf == NULL && size == 0)
+    /* Ensure that the buffer is only as large as necessary.  */
+    buf = realloc (dir, used);
 
-  /* Restore errno on successful return.  */
-  __set_errno (prev_errno);
+  if (buf == NULL)
+    /* Either buf was NULL all along, or `realloc' failed but
+       we still have the original string.  */
+    buf = dir;
 
-  return path;
+  return buf;
 
  memory_exhausted:
   __set_errno (ENOMEM);
@@ -372,7 +396,7 @@ __getcwd (char *buf, size_t size)
       free (dotlist);
 #endif
     if (buf == NULL)
-      free (path);
+      free (dir);
     __set_errno (save);
   }
   return NULL;