From 3806f4f65ae6cb6106c82397a4ebba739336175f Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sun, 18 Jan 2009 12:28:05 +0100 Subject: [PATCH] New module 'scandir'. --- ChangeLog | 14 +++ doc/posix-functions/scandir.texi | 12 ++- lib/dirent.in.h | 22 +++- lib/scandir.c | 172 +++++++++++++++++++++++++++++++ m4/dirent_h.m4 | 6 +- m4/scandir.m4 | 23 +++++ modules/dirent | 2 + modules/scandir | 26 +++++ 8 files changed, 270 insertions(+), 7 deletions(-) create mode 100644 lib/scandir.c create mode 100644 m4/scandir.m4 create mode 100644 modules/scandir diff --git a/ChangeLog b/ChangeLog index f779accbb0..fd7c3d9de0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2009-01-18 Bruno Haible + + New module 'scandir'. + * lib/dirent.in.h (scandir): New declaration. + * lib/scandir.c: New file, from glibc with modifications. + * m4/scandir.m4: New file. + * modules/scandir: New file. + * m4/dirent_h.m4 (gl_DIRENT_H_DEFAULTS): Initialize GNULIB_SCANDIR, + HAVE_SCANDIR. + * modules/dirent (Makefile.am): Substitute GNULIB_SCANDIR, + HAVE_SCANDIR. + * doc/posix-functions/scandir.texi: Mention the new module and the + portability problems. + 2009-01-17 Bruno Haible * gnulib-tool (func_remove_prefix): Escape all dots in the prefix. diff --git a/doc/posix-functions/scandir.texi b/doc/posix-functions/scandir.texi index 5735877bd9..ca37bcbd7d 100644 --- a/doc/posix-functions/scandir.texi +++ b/doc/posix-functions/scandir.texi @@ -4,15 +4,21 @@ POSIX specification: @url{http://www.opengroup.org/onlinepubs/9699919799/functions/scandir.html} -Gnulib module: --- +Gnulib module: scandir Portability problems fixed by Gnulib: @itemize +@item +This function is missing on some platforms: +Solaris 9, mingw, BeOS. @end itemize Portability problems not fixed by Gnulib: @itemize @item -This function is missing on some platforms: -Solaris 9, mingw, BeOS. +The fourth parameter of this function is declared as @code{int (*) (const void *, const void *)} on some platforms: +glibc 2.3.6, MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, Interix 3.5. +@item +The fourth parameter of this function is declared as @code{int (*) (void *, void *)} on some platforms: +AIX 5.1. @end itemize diff --git a/lib/dirent.in.h b/lib/dirent.in.h index 2b1d0a52a2..40824c56b7 100644 --- a/lib/dirent.in.h +++ b/lib/dirent.in.h @@ -1,5 +1,5 @@ /* A GNU-like . - Copyright (C) 2006-2008 Free Software Foundation, Inc. + Copyright (C) 2006-2009 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,7 +42,7 @@ extern DIR * opendir (const char *); extern int closedir (DIR *); #endif -/* Declare GNU extensions. */ +/* Declare other POSIX functions. */ #if @GNULIB_DIRFD@ # if !@HAVE_DECL_DIRFD@ && !defined dirfd @@ -58,6 +58,24 @@ extern int dirfd (DIR const *dir); dirfd (d)) #endif +#if @GNULIB_SCANDIR@ +/* Scan the directory DIR, calling FILTER on each directory entry. + Entries for which FILTER returns nonzero are individually malloc'd, + sorted using qsort with CMP, and collected in a malloc'd array in + *NAMELIST. Returns the number of entries selected, or -1 on error. */ +# if !@HAVE_SCANDIR@ +extern int scandir (const char *dir, struct dirent ***namelist, + int (*filter) (const struct dirent *), + int (*cmp) (const struct dirent **, const struct dirent **)); +# endif +#elif defined GNULIB_POSIXCHECK +# undef scandir +# define scandir(d,n,f,c) \ + (GL_LINK_WARNING ("scandir is unportable - " \ + "use gnulib module scandir for portability"), \ + scandir (d, n, f, c)) +#endif + #ifdef __cplusplus } #endif diff --git a/lib/scandir.c b/lib/scandir.c new file mode 100644 index 0000000000..8b34070e86 --- /dev/null +++ b/lib/scandir.c @@ -0,0 +1,172 @@ +/* Copyright (C) 1992-1998, 2000, 2002, 2003, 2009 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 it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +#include + +#include +#include +#include +#if _LIBC +# include +#endif + +#if ! defined __builtin_expect && __GNUC__ < 3 +# define __builtin_expect(expr, expected) (expr) +#endif + +#undef select + +#if _LIBC +# ifndef SCANDIR +# define SCANDIR scandir +# define READDIR __readdir +# define DIRENT_TYPE struct dirent +# endif +#else +# define SCANDIR scandir +# define READDIR readdir +# define DIRENT_TYPE struct dirent +# define __opendir opendir +# define __closedir closedir +# define __set_errno(val) errno = (val) +#endif + +#ifndef SCANDIR_CANCEL +# define SCANDIR_CANCEL +struct scandir_cancel_struct +{ + DIR *dp; + void *v; + size_t cnt; +}; + +# if _LIBC +static void +cancel_handler (void *arg) +{ + struct scandir_cancel_struct *cp = arg; + size_t i; + void **v = cp->v; + + for (i = 0; i < cp->cnt; ++i) + free (v[i]); + free (v); + (void) __closedir (cp->dp); +} +# endif +#endif + + +int +SCANDIR (const char *dir, + DIRENT_TYPE ***namelist, + int (*select) (const DIRENT_TYPE *), + int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **)) +{ + DIR *dp = __opendir (dir); + DIRENT_TYPE **v = NULL; + size_t vsize = 0; + struct scandir_cancel_struct c; + DIRENT_TYPE *d; + int save; + + if (dp == NULL) + return -1; + + save = errno; + __set_errno (0); + + c.dp = dp; + c.v = NULL; + c.cnt = 0; +#if _LIBC + __libc_cleanup_push (cancel_handler, &c); +#endif + + while ((d = READDIR (dp)) != NULL) + { + int use_it = select == NULL; + + if (! use_it) + { + use_it = select (d); + /* The select function might have changed errno. It was + zero before and it need to be again to make the latter + tests work. */ + __set_errno (0); + } + + if (use_it) + { + DIRENT_TYPE *vnew; + size_t dsize; + + /* Ignore errors from select or readdir */ + __set_errno (0); + + if (__builtin_expect (c.cnt == vsize, 0)) + { + DIRENT_TYPE **new; + if (vsize == 0) + vsize = 10; + else + vsize *= 2; + new = (DIRENT_TYPE **) realloc (v, vsize * sizeof (*v)); + if (new == NULL) + break; + v = new; + c.v = (void *) v; + } + + dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d; + vnew = (DIRENT_TYPE *) malloc (dsize); + if (vnew == NULL) + break; + + v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize); + } + } + + if (__builtin_expect (errno, 0) != 0) + { + save = errno; + + while (c.cnt > 0) + free (v[--c.cnt]); + free (v); + c.cnt = -1; + } + else + { + /* Sort the list if we have a comparison function to sort with. */ + if (cmp != NULL) + qsort (v, c.cnt, sizeof (*v), (int (*) (const void *, const void *)) cmp); + + *namelist = v; + } + +#if _LIBC + __libc_cleanup_pop (0); +#endif + + (void) __closedir (dp); + __set_errno (save); + + return c.cnt; +} diff --git a/m4/dirent_h.m4 b/m4/dirent_h.m4 index a72fc36297..38cc118474 100644 --- a/m4/dirent_h.m4 +++ b/m4/dirent_h.m4 @@ -1,5 +1,5 @@ -# dirent_h.m4 serial 2 -dnl Copyright (C) 2008 Free Software Foundation, Inc. +# dirent_h.m4 serial 3 +dnl Copyright (C) 2008-2009 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. @@ -33,7 +33,9 @@ AC_DEFUN([gl_DIRENT_H_DEFAULTS], [ AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) dnl for REPLACE_FCHDIR GNULIB_DIRFD=0; AC_SUBST([GNULIB_DIRFD]) + GNULIB_SCANDIR=0; AC_SUBST([GNULIB_SCANDIR]) dnl Assume proper GNU behavior unless another module says otherwise. HAVE_DECL_DIRFD=1; AC_SUBST([HAVE_DECL_DIRFD]) + HAVE_SCANDIR=1; AC_SUBST([HAVE_SCANDIR]) DIRENT_H=''; AC_SUBST([DIRENT_H]) ]) diff --git a/m4/scandir.m4 b/m4/scandir.m4 new file mode 100644 index 0000000000..7a1ba83ac6 --- /dev/null +++ b/m4/scandir.m4 @@ -0,0 +1,23 @@ +# scandir.m4 serial 1 +dnl Copyright (C) 2009 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_SCANDIR], +[ + AC_REQUIRE([gl_DIRENT_H_DEFAULTS]) + + dnl Persuade glibc and Solaris to declare scandir(). + AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + + AC_CHECK_FUNCS([scandir]) + if test $ac_cv_func_scandir = no; then + HAVE_SCANDIR=0 + AC_LIBOBJ([scandir]) + gl_PREREQ_SCANDIR + fi +]) + +# Prerequisites of lib/scandir.c. +AC_DEFUN([gl_PREREQ_SCANDIR], [:]) diff --git a/modules/dirent b/modules/dirent index bf6063b1f5..cfe36ceae9 100644 --- a/modules/dirent +++ b/modules/dirent @@ -25,7 +25,9 @@ dirent.h: dirent.in.h -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ -e 's|@''NEXT_DIRENT_H''@|$(NEXT_DIRENT_H)|g' \ -e 's|@''GNULIB_DIRFD''@|$(GNULIB_DIRFD)|g' \ + -e 's|@''GNULIB_SCANDIR''@|$(GNULIB_SCANDIR)|g' \ -e 's|@''HAVE_DECL_DIRFD''@|$(HAVE_DECL_DIRFD)|g' \ + -e 's|@''HAVE_SCANDIR''@|$(HAVE_SCANDIR)|g' \ -e 's|@''REPLACE_FCHDIR''@|$(REPLACE_FCHDIR)|g' \ -e '/definition of GL_LINK_WARNING/r $(LINK_WARNING_H)' \ < $(srcdir)/dirent.in.h; \ diff --git a/modules/scandir b/modules/scandir new file mode 100644 index 0000000000..d226f118da --- /dev/null +++ b/modules/scandir @@ -0,0 +1,26 @@ +Description: +scandir() function: collect entries of a directory + +Files: +lib/scandir.c +m4/scandir.m4 + +Depends-on: +dirent +extensions + +configure.ac: +gl_FUNC_SCANDIR +gl_DIRENT_MODULE_INDICATOR([scandir]) + +Makefile.am: + +Include: + + +License: +LGPL + +Maintainer: +all, glibc + -- 2.30.2