For programs that aren't worried about being invoked from an arbitrarily long current working directory (perhaps because it always does chdir to a sane location first), the getcwd module is overkill, given that all modern portability have a getcwd that works on short paths.
* modules/getcwd-lgpl: New module. * doc/posix-functions/getcwd.texi (getcwd): Document it. * MODULES.html.sh (lacking POSIX:2008): Likewise. * m4/getcwd.m4 (gl_PREREQ_GETCWD): Define extra witness. (gl_FUNC_GETCWD_LGPL): New macro. * lib/getcwd.c (rpl_getcwd): Provide alternate LGPL implementation. Signed-off-by: Eric Blake <[email protected]> --- ChangeLog | 9 ++ MODULES.html.sh | 1 + doc/posix-functions/getcwd.texi | 13 ++- lib/getcwd.c | 208 +++++++++++++++++++++++++-------------- m4/getcwd.m4 | 30 +++++- modules/getcwd-lgpl | 25 +++++ 6 files changed, 205 insertions(+), 81 deletions(-) create mode 100644 modules/getcwd-lgpl diff --git a/ChangeLog b/ChangeLog index f113c64..1105b88 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,14 @@ 2011-04-25 Eric Blake <[email protected]> + getcwd-lgpl: new module + * modules/getcwd-lgpl: New module. + * doc/posix-functions/getcwd.texi (getcwd): Document it. + * MODULES.html.sh (lacking POSIX:2008): Likewise. + * m4/getcwd.m4 (gl_PREREQ_GETCWD): Define extra witness. + (gl_FUNC_GETCWD_LGPL): New macro. + * lib/getcwd.c (rpl_getcwd): Provide alternate LGPL + implementation. + getcwd: consolidate m4 files * m4/getcwd-abort-bug.m4, m4/getcwd-path-max.m4: Delete, moving contents... diff --git a/MODULES.html.sh b/MODULES.html.sh index 8d32a21..a584c4e 100755 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -2353,6 +2353,7 @@ func_all_modules () func_module futimens func_module getaddrinfo func_module getcwd + func_module getcwd-lgpl func_module getgroups func_module gethostname func_module getlogin diff --git a/doc/posix-functions/getcwd.texi b/doc/posix-functions/getcwd.texi index 4d00af8..a8dba96 100644 --- a/doc/posix-functions/getcwd.texi +++ b/doc/posix-functions/getcwd.texi @@ -4,15 +4,20 @@ getcwd POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/getcwd.html} -Gnulib module: getcwd +Gnulib module: getcwd or getcwd-lgpl -Portability problems fixed by Gnulib: +Portability problems fixed by either Gnulib module @code{getcwd} or +@code{getcwd-lgpl}: @itemize @item -This function is missing on some older platforms. -@item On glibc platforms, @code{getcwd (NULL, n)} allocates memory for the result. On other platforms, this call is not allowed. +@end itemize + +Portability problems fixed by Gnulib module @code{getcwd}: +@itemize +@item +This function is missing on some older platforms. @item This function does not handle long file names (greater than @code{PATH_MAX}) correctly on some platforms. diff --git a/lib/getcwd.c b/lib/getcwd.c index e52af18..c221ecc 100644 --- a/lib/getcwd.c +++ b/lib/getcwd.c @@ -20,73 +20,77 @@ #endif #include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> #include <stdbool.h> -#include <stddef.h> +#include <string.h> + +#if USE_GPL_GETCWD + +# include <sys/types.h> +# include <sys/stat.h> +# include <stddef.h> -#include <fcntl.h> /* For AT_FDCWD on Solaris 9. */ +# include <fcntl.h> /* For AT_FDCWD on Solaris 9. */ /* If this host provides the openat function, then enable code below to make getcwd more efficient and robust. */ -#ifdef HAVE_OPENAT -# define HAVE_OPENAT_SUPPORT 1 -#else -# define HAVE_OPENAT_SUPPORT 0 -#endif +# ifdef HAVE_OPENAT +# define HAVE_OPENAT_SUPPORT 1 +# else +# define HAVE_OPENAT_SUPPORT 0 +# endif -#ifndef __set_errno -# define __set_errno(val) (errno = (val)) -#endif +# ifndef __set_errno +# define __set_errno(val) (errno = (val)) +# endif -#include <dirent.h> -#ifndef _D_EXACT_NAMLEN -# define _D_EXACT_NAMLEN(d) strlen ((d)->d_name) -#endif -#ifndef _D_ALLOC_NAMLEN -# define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1) -#endif +# include <dirent.h> +# ifndef _D_EXACT_NAMLEN +# define _D_EXACT_NAMLEN(d) strlen ((d)->d_name) +# endif +# ifndef _D_ALLOC_NAMLEN +# define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1) +# endif -#include <unistd.h> -#include <stdlib.h> -#include <string.h> +# include <unistd.h> +# include <stdlib.h> +# include <string.h> -#if _LIBC -# ifndef mempcpy -# define mempcpy __mempcpy +# if _LIBC +# ifndef mempcpy +# define mempcpy __mempcpy +# endif # endif -#endif -#include <limits.h> +# include <limits.h> -#ifndef MAX -# define MAX(a, b) ((a) < (b) ? (b) : (a)) -#endif -#ifndef MIN -# define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif +# ifndef MAX +# define MAX(a, b) ((a) < (b) ? (b) : (a)) +# endif +# ifndef MIN +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +# endif -#ifndef PATH_MAX -# ifdef MAXPATHLEN -# define PATH_MAX MAXPATHLEN -# else -# define PATH_MAX 1024 +# ifndef PATH_MAX +# ifdef MAXPATHLEN +# define PATH_MAX MAXPATHLEN +# else +# define PATH_MAX 1024 +# endif # endif -#endif -#if D_INO_IN_DIRENT -# define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino)) -#else -# define MATCHING_INO(dp, ino) true -#endif +# if D_INO_IN_DIRENT +# define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino)) +# else +# define MATCHING_INO(dp, ino) true +# endif -#if !_LIBC -# define __getcwd rpl_getcwd -# define __lstat lstat -# define __closedir closedir -# define __opendir opendir -# define __readdir readdir -#endif +# if !_LIBC +# define __getcwd rpl_getcwd +# define __lstat lstat +# define __closedir closedir +# define __opendir opendir +# define __readdir readdir +# endif /* The results of opendir() in this file are not used with dirfd and fchdir, and we do not leak fds to any single-threaded code that could use stdio, @@ -94,8 +98,8 @@ FIXME - if the kernel ever adds support for multi-thread safety for avoiding standard fds, then we should use opendir_safer and openat_safer. */ -#undef opendir -#undef closedir +# undef opendir +# undef closedir /* 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 @@ -118,15 +122,15 @@ __getcwd (char *buf, size_t size) DEEP_NESTING = 100 }; -#if HAVE_OPENAT_SUPPORT +# if HAVE_OPENAT_SUPPORT int fd = AT_FDCWD; bool fd_needs_closing = false; -#else +# else char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1]; char *dotlist = dots; size_t dotsize = sizeof dots; size_t dotlen = 0; -#endif +# endif DIR *dirstream = NULL; dev_t rootdev, thisdev; ino_t rootino, thisino; @@ -136,7 +140,7 @@ __getcwd (char *buf, size_t size) size_t allocated = size; size_t used; -#if HAVE_RAW_DECL_GETCWD +# if HAVE_RAW_DECL_GETCWD /* 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 @@ -146,7 +150,7 @@ __getcwd (char *buf, size_t size) system getcwd works even when a parent is unreadable, while the openat-based approach does not. */ -# undef getcwd +# undef getcwd dir = getcwd (buf, size); if (dir) return dir; @@ -163,13 +167,13 @@ __getcwd (char *buf, size_t size) return strdup (dir); } -# if HAVE_PARTLY_WORKING_GETCWD +# if HAVE_PARTLY_WORKING_GETCWD /* The system getcwd works, except it sometimes fails when it shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT. */ if (errno != ERANGE && errno != ENAMETOOLONG && errno != ENOENT) return NULL; +# endif # endif -#endif if (size == 0) { @@ -216,18 +220,18 @@ __getcwd (char *buf, size_t size) bool use_d_ino = true; /* Look at the parent directory. */ -#if HAVE_OPENAT_SUPPORT +# if HAVE_OPENAT_SUPPORT fd = openat (fd, "..", O_RDONLY); if (fd < 0) goto lose; fd_needs_closing = true; parent_status = fstat (fd, &st); -#else +# else dotlist[dotlen++] = '.'; dotlist[dotlen++] = '.'; dotlist[dotlen] = '\0'; parent_status = __lstat (dotlist, &st); -#endif +# endif if (parent_status != 0) goto lose; @@ -243,17 +247,17 @@ __getcwd (char *buf, size_t size) mount_point = dotdev != thisdev; /* Search for the last directory. */ -#if HAVE_OPENAT_SUPPORT +# if HAVE_OPENAT_SUPPORT dirstream = fdopendir (fd); if (dirstream == NULL) goto lose; fd_needs_closing = false; -#else +# else dirstream = __opendir (dotlist); if (dirstream == NULL) goto lose; dotlist[dotlen++] = '/'; -#endif +# endif for (;;) { /* Clear errno to distinguish EOF from error if readdir returns @@ -297,9 +301,9 @@ __getcwd (char *buf, size_t size) { int entry_status; -#if HAVE_OPENAT_SUPPORT +# if HAVE_OPENAT_SUPPORT entry_status = fstatat (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW); -#else +# else /* Compute size needed for this file name, or for the file name ".." in the same directory, whichever is larger. Room for ".." might be needed the next time through @@ -336,7 +340,7 @@ __getcwd (char *buf, size_t size) memcpy (dotlist + dotlen, d->d_name, _D_ALLOC_NAMLEN (d)); entry_status = __lstat (dotlist, &st); -#endif +# endif /* We don't fail here if we cannot stat() a directory entry. This can happen when (network) file systems fail. If this entry is in fact the one we are looking for we will find @@ -393,10 +397,10 @@ __getcwd (char *buf, size_t size) if (dirp == &dir[allocated - 1]) *--dirp = '/'; -#if ! HAVE_OPENAT_SUPPORT +# if ! HAVE_OPENAT_SUPPORT if (dotlist != dots) free (dotlist); -#endif +# endif used = dir + allocated - dirp; memmove (dir, dirp, used); @@ -419,13 +423,13 @@ __getcwd (char *buf, size_t size) int save = errno; if (dirstream) __closedir (dirstream); -#if HAVE_OPENAT_SUPPORT +# if HAVE_OPENAT_SUPPORT if (fd_needs_closing) close (fd); -#else +# else if (dotlist != dots) free (dotlist); -#endif +# endif if (buf == NULL) free (dir); __set_errno (save); @@ -433,6 +437,60 @@ __getcwd (char *buf, size_t size) return NULL; } -#ifdef weak_alias +# ifdef weak_alias weak_alias (__getcwd, getcwd) +# endif + +#else +/* LGPL version - only guarantees that NULL can be used as first + argument. */ + +# undef getcwd +char * +rpl_getcwd (char *buf, size_t size) +{ + char *tmp = NULL; + bool first = true; + + if (buf) + return getcwd (buf, size); + + do + { + tmp = buf; + if (first) + { + size = size ? size : 4096; + first = false; + } + else + { + size <<= 1; + } + if ((buf = realloc (tmp, size)) == NULL) + { + free (tmp); + errno = ENOMEM; + return NULL; + } + tmp = getcwd (buf, size); + } + while (!tmp && errno == ERANGE); + + if (!tmp) + { + int saved_errno = errno; + free (buf); + errno = saved_errno; + } + else + { + /* Trim to fit, if possible. */ + tmp = realloc (buf, strlen (buf) + 1); + if (!tmp) + tmp = buf; + } + return tmp; +} + #endif diff --git a/m4/getcwd.m4 b/m4/getcwd.m4 index 5016a1b..79f5276 100644 --- a/m4/getcwd.m4 +++ b/m4/getcwd.m4 @@ -6,7 +6,7 @@ # with or without modifications, as long as this notice is preserved. # Written by Paul Eggert. -# serial 4 +# serial 5 AC_DEFUN([gl_FUNC_GETCWD_NULL], [ @@ -333,6 +333,28 @@ main () ]) +dnl Check just for getcwd behavior on NULL. Assumes that either the +dnl system getcwd works, or that the code is okay with spurious +dnl failures when run from super-deep hierarchies. +dnl +dnl Assumes that getcwd exists; if you are worried about obsolete +dnl platforms that lacked getcwd(), then you need to use the GPL module. +AC_DEFUN([gl_FUNC_GETCWD_LGPL], +[ + AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) + AC_REQUIRE([gl_FUNC_GETCWD_NULL]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + + case $gl_cv_func_getcwd_null in + *yes) ;; + *) + dnl Minimal replacement + REPLACE_GETCWD=1 + AC_LIBOBJ([getcwd]) + esac +]) + + dnl Check for all known getcwd bugs; useful for a program likely to be dnl executed from an arbitrary location, by augmenting native getcwd. AC_DEFUN([gl_FUNC_GETCWD], @@ -353,16 +375,20 @@ AC_DEFUN([gl_FUNC_GETCWD], case $gl_cv_func_getcwd_null,$gl_cv_func_getcwd_path_max,$gl_abort_bug in *yes,yes,no) ;; *) + dnl Full replacement. REPLACE_GETCWD=1 AC_LIBOBJ([getcwd]) gl_PREREQ_GETCWD;; esac ]) -# Prerequisites of lib/getcwd.c. +# Prerequisites of lib/getcwd.c, when full replacement is in effect. AC_DEFUN([gl_PREREQ_GETCWD], [ AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) AC_REQUIRE([gl_CHECK_TYPE_STRUCT_DIRENT_D_INO]) + AC_DEFINE([USE_GPL_GETCWD], [1], + [Define to 1 if GPL code should be used to attempt to work around + native getcwd failures.]) : ]) diff --git a/modules/getcwd-lgpl b/modules/getcwd-lgpl new file mode 100644 index 0000000..e043623 --- /dev/null +++ b/modules/getcwd-lgpl @@ -0,0 +1,25 @@ +Description: +Ensure getcwd(NULL, 0) returns a buffer allocated by malloc(). + +Files: +lib/getcwd.c +m4/getcwd.m4 + +Depends-on: +stdbool +unistd + +configure.ac: +gl_FUNC_GETCWD_LGPL +gl_UNISTD_MODULE_INDICATOR([getcwd]) + +Makefile.am: + +Include: +<unistd.h> + +License: +LGPLv2+ + +Maintainer: +Eric Blake -- 1.7.4.4
