Another new function in POSIX:2008 is scandir. I cannot judge how useful it is, but it's easy to port the implementation from glibc anyway.
2009-01-18 Bruno Haible <br...@clisp.org> 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. ================================== lib/scandir.c ============================== /* 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 <config.h> #include <dirent.h> #include <stdlib.h> #include <string.h> #include <errno.h> #if _LIBC # include <bits/libc-lock.h> #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; } ================================== m4/scandir.m4 ============================== # 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 <dirent.h> 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], [:]) ================================= modules/scandir ============================= 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: <dirent.h> License: LGPL Maintainer: all, glibc =============================================================================== --- lib/dirent.in.h.orig 2009-01-18 12:22:31.000000000 +0100 +++ lib/dirent.in.h 2009-01-18 11:39:52.000000000 +0100 @@ -1,5 +1,5 @@ /* A GNU-like <dirent.h>. - 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 int closedir (DIR *); #endif -/* Declare GNU extensions. */ +/* Declare other POSIX functions. */ #if @GNULIB_DIRFD@ # if !...@have_decl_dirfd@ && !defined dirfd @@ -58,6 +58,24 @@ 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 --- m4/dirent_h.m4.orig 2009-01-18 12:22:31.000000000 +0100 +++ m4/dirent_h.m4 2009-01-18 11:40:30.000000000 +0100 @@ -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_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]) ]) --- modules/dirent.orig 2009-01-18 12:22:31.000000000 +0100 +++ modules/dirent 2009-01-18 11:41:01.000000000 +0100 @@ -25,7 +25,9 @@ -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; \ --- doc/posix-functions/scandir.texi.orig 2009-01-18 12:22:31.000000000 +0100 +++ doc/posix-functions/scandir.texi 2009-01-18 12:11:04.000000000 +0100 @@ -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