diff options
| author | Ian MacArthur <imacarthur@gmail.com> | 2013-03-06 22:15:01 +0000 |
|---|---|---|
| committer | Ian MacArthur <imacarthur@gmail.com> | 2013-03-06 22:15:01 +0000 |
| commit | 8e789d61a3da520643898dbb0829419401355e97 (patch) | |
| tree | d7da2f4b87dfefcb908aec9fc5eee5064ec0641b | |
| parent | 3de79d1fd0faa283928a3a4b53353dde53ae3a43 (diff) | |
This check-in brings in the simplified version of
Michael Baeuerle's patch to restore scandir like
operation on *nix like hosts that do not provide a
native scandir implementation, notably SunOS and
some others.
STR #2931 refers...
These changes are tested to have no effect on OSX,
Win32 or Linux systems, and indeed on any system that
has a working scandir implementation that configure
can detect.
Michael has released these changes to the fltk project,
and they are licensed under our usual conditons.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9832 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
| -rw-r--r-- | src/scandir.c | 38 | ||||
| -rw-r--r-- | src/scandir_posix.c | 205 |
2 files changed, 209 insertions, 34 deletions
diff --git a/src/scandir.c b/src/scandir.c index ab2a50d3f..2cfbce27e 100644 --- a/src/scandir.c +++ b/src/scandir.c @@ -21,40 +21,10 @@ #if defined(WIN32) && !defined(__CYGWIN__) # include "scandir_win32.c" #else - -# include "flstring.h" - -/* NOTE: Most (all?) modern non-WIN32 hosts DO have a usable scandir */ -# if !HAVE_SCANDIR -# include <stdlib.h> -# include <sys/types.h> -# include <errno.h> - -# if HAVE_DIRENT_H -# include <dirent.h> -# define NAMLEN(dirent) strlen((dirent)->d_name) -# else /* HAVE_DIRENT_H */ -# define dirent direct -# define NAMLEN(dirent) (dirent)->d_namlen -# if HAVE_SYS_NDIR_H -# include <sys/ndir.h> -# endif /* HAVE_SYS_NDIR_H */ -# if HAVE_SYS_DIR_H -# include <sys/dir.h> -# endif /* HAVE_SYS_DIR_H */ -# if HAVE_NDIR_H -# include <ndir.h> -# endif /* HAVE_NDIR_H */ -# endif - -/* This warning added to help identify any non-WIN32 hosts that actually try to use - * our "private" implementation of the scandir function, which was suspect... */ -# if defined(__GNUC__) -# warning Attempting to use the deprecated scandir() replacement function -# endif /*__GNUC__*/ -# error No compatible scandir implementation found (STR 2687 applies!) - -# endif /* !HAVE_SCANDIR */ +# include "../config.h" +# ifndef HAVE_SCANDIR +# include "scandir_posix.c" +# endif /* HAVE_SCANDIR */ #endif /* diff --git a/src/scandir_posix.c b/src/scandir_posix.c new file mode 100644 index 000000000..64eaaf519 --- /dev/null +++ b/src/scandir_posix.c @@ -0,0 +1,205 @@ +/* + * "$Id$" + * + * This implementation of 'scandir()' is intended to be POSIX.1-2008 compliant. + * A POSIX.1-1990 compliant system is required as minimum base. + * Note: + * The 'const' declarations were removed to match FLTK 1.3 wrapper (STR #2931) + * + * Copyright (c) 2013 by Michael Baeuerle + * + * This library is free software. Distribution and use rights are outlined in + * the file "COPYING" which should have been included with this file. If this + * file is missing or damaged, see the license at: + * + * http://www.fltk.org/COPYING.php + * + * Please report all bugs and problems on the following page: + * + * http://www.fltk.org/str.php + * + * It is required that 'SIZE_MAX' is at least 'INT_MAX'. + * Don't use a C++ compiler to build this module. + * + * The build system must define 'HAVE_PTHREAD' and link against a potentially + * required library to switch this implementation into thread-safe mode. + * The POSIX.1c-1995 extension is required if 'HAVE_PTHREAD' is defined. + * + * Note: + * In theory, a system that provide threads should also provide 'readdir_r()', + * a thread-safe version of 'readdir()'. In reality this is not always the case. + * In addition there may be a race condition that can lead to a buffer overflow: + * http://womble.decadent.org.uk/readdir_r-advisory.html + */ + +#ifndef HAVE_PTHREAD + /* Switch system headers into POSIX.1-1990 mode */ +# define _POSIX_SOURCE +#else /* HAVE_PTHREAD */ + /* Switch system headers into POSIX.1c-1995 mode */ +# define _POSIX_C_SOURCE 199506L +#endif /* HAVE_PTHREAD */ + +#include <sys/types.h> /* XPG2 require this for '*dir()' functions */ +#include <dirent.h> +#include <errno.h> +#include <stdlib.h> /* For 'malloc()', 'realloc()' and 'qsort()' */ +#include <stddef.h> /* For 'offsetof()', 'NULL' and 'size_t' */ +#include <limits.h> /* For 'INT_MAX' */ +#include <string.h> /* For 'memcpy()' */ +#if defined(HAVE_PTHREAD) && defined(HAVE_PTHREAD_H) +# include <pthread.h> +#endif /* HAVE_PTHREAD */ + + +/* ========================================================================== */ +/* At startup allocate memory for this number of result array elements */ +#define ENTRIES_MIN (size_t) 32 + + +/* ========================================================================== */ +#ifdef HAVE_PTHREAD +static pthread_mutex_t scandir_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif /* HAVE_PTHREAD */ + + +/* ========================================================================== */ +/* + * This function reads the next entry from the directory referenced by 'dirp', + * allocate a buffer for the entry and copy it into this buffer. + * A pointer to this buffer is written to 'entryp' and the size of the buffer is + * written to 'len'. + * Success and a NULL pointer is returned for 'entryp' if there are no more + * entries in the directory. + * On sucess zero is returned and the caller is responsible for 'free()'ing the + * buffer after use. + * On error the return value is nonzero, 'entryp' and 'len' are invalid. + * + * Should be declared as 'static inline' if the compiler support that. + */ +static int +readentry(DIR *dirp, struct dirent **entryp, size_t *len) +{ + int result = -1; + struct dirent *e; + +#ifdef HAVE_PTHREAD + if (!pthread_mutex_lock(&scandir_mutex)) + { + /* Ensure that there is no code path that bypass the '_unlock()' call! */ +#endif /* HAVE_PTHREAD */ + errno = 0; + e = readdir(dirp); + if (NULL == e) + { + if (!errno) + { + /* No more entries in directory */ + *entryp = NULL; + *len = 0; + result = 0; + } + } + else + { + /* Entry found, allocate local buffer */ + *len = offsetof(struct dirent, d_name) + strlen(e->d_name) + (size_t) 1; + *entryp = (struct dirent *) malloc(*len); + if (NULL != *entryp) + { + memcpy((void *) *entryp, (void *) e, *len); + /* Force NUL termination at end of buffer */ + ((char *) *entryp)[*len - (size_t) 1] = 0; + result = 0; + } + } +#ifdef HAVE_PTHREAD + /* + * In a multithreading environment the systems dirent buffer may be shared + * between all threads. Therefore the mutex must stay locked until we have + * copied the data to our thread local buffer. + */ + pthread_mutex_unlock(&scandir_mutex); + } +#endif /* HAVE_PTHREAD */ + + return result; +} + + +/* ========================================================================== */ +int +fl_scandir(const char *dir, struct dirent ***namelist, + int (*sel)(struct dirent *), + int (*compar)(struct dirent **, struct dirent **)) +{ + int result = -1; + DIR *dirp; + size_t len, num = 0, max = ENTRIES_MIN; + struct dirent *entryp, **entries, **p; + + entries = (struct dirent **) malloc(sizeof(*entries) * max); + if (NULL != entries) + { + /* Open directory 'dir' (and verify that it really is a directory) */ + dirp = opendir(dir); + if (NULL != dirp) + { + /* Read next directory entry */ + while (!readentry(dirp, &entryp, &len)) + { + if (NULL == entryp) + { + /* EOD => Return number of directory entries */ + result = (int) num; + break; + } + /* Apply select function if there is one provided */ + if (NULL != sel) { if (!sel(entryp)) continue; } + entries[num++] = entryp; + if (num >= max) + { + /* Allocate exponentially increasing sized memory chunks */ + if (INT_MAX / 2 >= (int) max) { max *= (size_t) 2; } + else + { + errno = ENOMEM; + break; + } + p = (struct dirent **) realloc((void *) entries, + sizeof(*entries) * max); + if (NULL != p) { entries = p; } + else break; + } + } + closedir(dirp); + /* + * A standard compliant 'closedir()' is allowed to fail with 'EINTR', but + * the state of the directory structure is undefined in this case. + * Therefore we ignore the return value because we can't call 'closedir()' + * again and must hope that the system has released all ressources. + */ + } + /* Sort entries in array if there is a compare function provided */ + if (NULL != compar) + { + qsort((void *) entries, num, sizeof(*entries), + (int (*)(const void *, const void *)) compar); + } + *namelist = entries; + } + + /* Check for error */ + if (-1 == result) + { + /* Free all memory we have allocated */ + while (num--) { free(entries[num]); } + free(entries); + } + + return result; +} + +/* + * End of "$Id$". + */ |
