Back to index

glibc  2.9
scandir.c
Go to the documentation of this file.
00001 /* Copyright (C) 1992-1998, 2000, 2002, 2003 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003 
00004    The GNU C Library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Lesser General Public
00006    License as published by the Free Software Foundation; either
00007    version 2.1 of the License, or (at your option) any later version.
00008 
00009    The GNU C Library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Lesser General Public License for more details.
00013 
00014    You should have received a copy of the GNU Lesser General Public
00015    License along with the GNU C Library; if not, write to the Free
00016    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00017    02111-1307 USA.  */
00018 
00019 #include <dirent.h>
00020 #include <stdlib.h>
00021 #include <string.h>
00022 #include <errno.h>
00023 #include <bits/libc-lock.h>
00024 
00025 #ifndef SCANDIR
00026 #define SCANDIR scandir
00027 #define READDIR __readdir
00028 #define DIRENT_TYPE struct dirent
00029 #endif
00030 
00031 #ifndef SCANDIR_CANCEL
00032 #define SCANDIR_CANCEL
00033 struct scandir_cancel_struct
00034 {
00035   DIR *dp;
00036   void *v;
00037   size_t cnt;
00038 };
00039 
00040 static void
00041 cancel_handler (void *arg)
00042 {
00043   struct scandir_cancel_struct *cp = arg;
00044   size_t i;
00045   void **v = cp->v;
00046 
00047   for (i = 0; i < cp->cnt; ++i)
00048     free (v[i]);
00049   free (v);
00050   (void) __closedir (cp->dp);
00051 }
00052 #endif
00053 
00054 
00055 int
00056 SCANDIR (dir, namelist, select, cmp)
00057      const char *dir;
00058      DIRENT_TYPE ***namelist;
00059      int (*select) (const DIRENT_TYPE *);
00060      int (*cmp) (const void *, const void *);
00061 {
00062   DIR *dp = __opendir (dir);
00063   DIRENT_TYPE **v = NULL;
00064   size_t vsize = 0;
00065   struct scandir_cancel_struct c;
00066   DIRENT_TYPE *d;
00067   int save;
00068 
00069   if (dp == NULL)
00070     return -1;
00071 
00072   save = errno;
00073   __set_errno (0);
00074 
00075   c.dp = dp;
00076   c.v = NULL;
00077   c.cnt = 0;
00078   __libc_cleanup_push (cancel_handler, &c);
00079 
00080   while ((d = READDIR (dp)) != NULL)
00081     {
00082       int use_it = select == NULL;
00083 
00084       if (! use_it)
00085        {
00086          use_it = select (d);
00087          /* The select function might have changed errno.  It was
00088             zero before and it need to be again to make the latter
00089             tests work.  */
00090          __set_errno (0);
00091        }
00092 
00093       if (use_it)
00094        {
00095          DIRENT_TYPE *vnew;
00096          size_t dsize;
00097 
00098          /* Ignore errors from select or readdir */
00099          __set_errno (0);
00100 
00101          if (__builtin_expect (c.cnt == vsize, 0))
00102            {
00103              DIRENT_TYPE **new;
00104              if (vsize == 0)
00105               vsize = 10;
00106              else
00107               vsize *= 2;
00108              new = (DIRENT_TYPE **) realloc (v, vsize * sizeof (*v));
00109              if (new == NULL)
00110               break;
00111              v = new;
00112              c.v = (void *) v;
00113            }
00114 
00115          dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d;
00116          vnew = (DIRENT_TYPE *) malloc (dsize);
00117          if (vnew == NULL)
00118            break;
00119 
00120          v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize);
00121        }
00122     }
00123 
00124   if (__builtin_expect (errno, 0) != 0)
00125     {
00126       save = errno;
00127 
00128       while (c.cnt > 0)
00129        free (v[--c.cnt]);
00130       free (v);
00131       c.cnt = -1;
00132     }
00133   else
00134     {
00135       /* Sort the list if we have a comparison function to sort with.  */
00136       if (cmp != NULL)
00137        qsort (v, c.cnt, sizeof (*v), cmp);
00138 
00139       *namelist = v;
00140     }
00141 
00142   __libc_cleanup_pop (0);
00143 
00144   (void) __closedir (dp);
00145   __set_errno (save);
00146 
00147   return c.cnt;
00148 }