#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
char *path;
register char *pathp;
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
+ path = getcwd (buf, size);
+ if (path || (errno != ERANGE && !is_ENAMETOOLONG (errno) && errno != ENOENT))
+ return path;
+#endif
if (size == 0)
{
allocated = BIG_FILE_NAME_LENGTH + 1;
}
- if (buf != NULL)
- path = buf;
- else
+ if (buf == NULL)
{
path = malloc (allocated);
if (path == NULL)
return NULL;
}
+ else
+ path = buf;
pathp = path + allocated;
*--pathp = '\0';
free (dotlist);
#endif
- memmove (path, pathp, path + allocated - pathp);
+ used = path + allocated - pathp;
+ memmove (path, pathp, used);
+
+ if (buf == NULL && size == 0)
+ /* Ensure that the buffer is only as large as necessary. */
+ buf = realloc (path, 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 = path;
- return path;
+ return buf;
memory_exhausted:
__set_errno (ENOMEM);