fdwalk should return 0 on an empty directory
[kopensolaris-gnu/glibc.git] / dirent / scandir.c
1 /* Copyright (C) 1992-1998, 2000, 2002, 2003 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <dirent.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <bits/libc-lock.h>
24
25 #ifndef SCANDIR
26 #define SCANDIR scandir
27 #define READDIR __readdir
28 #define DIRENT_TYPE struct dirent
29 #endif
30
31 #ifndef SCANDIR_CANCEL
32 #define SCANDIR_CANCEL
33 struct scandir_cancel_struct
34 {
35   DIR *dp;
36   void *v;
37   size_t cnt;
38 };
39
40 static void
41 cancel_handler (void *arg)
42 {
43   struct scandir_cancel_struct *cp = arg;
44   size_t i;
45   void **v = cp->v;
46
47   for (i = 0; i < cp->cnt; ++i)
48     free (v[i]);
49   free (v);
50   (void) __closedir (cp->dp);
51 }
52 #endif
53
54
55 int
56 SCANDIR (dir, namelist, select, cmp)
57      const char *dir;
58      DIRENT_TYPE ***namelist;
59      int (*select) (const DIRENT_TYPE *);
60      int (*cmp) (const void *, const void *);
61 {
62   DIR *dp = __opendir (dir);
63   DIRENT_TYPE **v = NULL;
64   size_t vsize = 0;
65   struct scandir_cancel_struct c;
66   DIRENT_TYPE *d;
67   int save;
68
69   if (dp == NULL)
70     return -1;
71
72   save = errno;
73   __set_errno (0);
74
75   c.dp = dp;
76   c.v = NULL;
77   c.cnt = 0;
78   __libc_cleanup_push (cancel_handler, &c);
79
80   while ((d = READDIR (dp)) != NULL)
81     {
82       int use_it = select == NULL;
83
84       if (! use_it)
85         {
86           use_it = select (d);
87           /* The select function might have changed errno.  It was
88              zero before and it need to be again to make the latter
89              tests work.  */
90           __set_errno (0);
91         }
92
93       if (use_it)
94         {
95           DIRENT_TYPE *vnew;
96           size_t dsize;
97
98           /* Ignore errors from select or readdir */
99           __set_errno (0);
100
101           if (__builtin_expect (c.cnt == vsize, 0))
102             {
103               DIRENT_TYPE **new;
104               if (vsize == 0)
105                 vsize = 10;
106               else
107                 vsize *= 2;
108               new = (DIRENT_TYPE **) realloc (v, vsize * sizeof (*v));
109               if (new == NULL)
110                 break;
111               v = new;
112               c.v = (void *) v;
113             }
114
115           dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d;
116           vnew = (DIRENT_TYPE *) malloc (dsize);
117           if (vnew == NULL)
118             break;
119
120           v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize);
121         }
122     }
123
124   if (__builtin_expect (errno, 0) != 0)
125     {
126       save = errno;
127
128       while (c.cnt > 0)
129         free (v[--c.cnt]);
130       free (v);
131       c.cnt = -1;
132     }
133   else
134     {
135       /* Sort the list if we have a comparison function to sort with.  */
136       if (cmp != NULL)
137         qsort (v, c.cnt, sizeof (*v), cmp);
138
139       *namelist = v;
140     }
141
142   __libc_cleanup_pop (0);
143
144   (void) __closedir (dp);
145   __set_errno (save);
146
147   return c.cnt;
148 }