tests: test-update-copyright.sh: remove unnecessary "rm" commands
[gnulib.git] / lib / scandir.c
1 /* Copyright (C) 1992-1998, 2000, 2002-2003, 2009-2011 Free Software
2    Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    This program is free software; you can redistribute it and/or modify it
6    under the terms of the GNU General Public License as published by the
7    Free Software Foundation; either version 2, or (at your option) any
8    later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19 #include <config.h>
20
21 #include <dirent.h>
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #if _LIBC
27 # include <bits/libc-lock.h>
28 #endif
29
30 #if ! defined __builtin_expect && __GNUC__ < 3
31 # define __builtin_expect(expr, expected) (expr)
32 #endif
33
34 #undef select
35
36 #ifndef _D_EXACT_NAMLEN
37 # define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
38 #endif
39 #ifndef _D_ALLOC_NAMLEN
40 # define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
41 #endif
42
43 #if _LIBC
44 # ifndef SCANDIR
45 #  define SCANDIR scandir
46 #  define READDIR __readdir
47 #  define DIRENT_TYPE struct dirent
48 # endif
49 #else
50 # define SCANDIR scandir
51 # define READDIR readdir
52 # define DIRENT_TYPE struct dirent
53 # define __opendir opendir
54 # define __closedir closedir
55 # define __set_errno(val) errno = (val)
56
57 /* The results of opendir() in this file are not used with dirfd and fchdir,
58    and we do not leak fds to any single-threaded code that could use stdio,
59    therefore save some unnecessary recursion in fchdir.c and opendir_safer.c.
60    FIXME - if the kernel ever adds support for multi-thread safety for
61    avoiding standard fds, then we should use opendir_safer.  */
62 # undef opendir
63 # undef closedir
64 #endif
65
66 #ifndef SCANDIR_CANCEL
67 # define SCANDIR_CANCEL
68 struct scandir_cancel_struct
69 {
70   DIR *dp;
71   void *v;
72   size_t cnt;
73 };
74
75 # if _LIBC
76 static void
77 cancel_handler (void *arg)
78 {
79   struct scandir_cancel_struct *cp = arg;
80   size_t i;
81   void **v = cp->v;
82
83   for (i = 0; i < cp->cnt; ++i)
84     free (v[i]);
85   free (v);
86   (void) __closedir (cp->dp);
87 }
88 # endif
89 #endif
90
91
92 int
93 SCANDIR (const char *dir,
94          DIRENT_TYPE ***namelist,
95          int (*select) (const DIRENT_TYPE *),
96          int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **))
97 {
98   DIR *dp = __opendir (dir);
99   DIRENT_TYPE **v = NULL;
100   size_t vsize = 0;
101   struct scandir_cancel_struct c;
102   DIRENT_TYPE *d;
103   int save;
104
105   if (dp == NULL)
106     return -1;
107
108   save = errno;
109   __set_errno (0);
110
111   c.dp = dp;
112   c.v = NULL;
113   c.cnt = 0;
114 #if _LIBC
115   __libc_cleanup_push (cancel_handler, &c);
116 #endif
117
118   while ((d = READDIR (dp)) != NULL)
119     {
120       int use_it = select == NULL;
121
122       if (! use_it)
123         {
124           use_it = select (d);
125           /* The select function might have changed errno.  It was
126              zero before and it need to be again to make the latter
127              tests work.  */
128           __set_errno (0);
129         }
130
131       if (use_it)
132         {
133           DIRENT_TYPE *vnew;
134           size_t dsize;
135
136           /* Ignore errors from select or readdir */
137           __set_errno (0);
138
139           if (__builtin_expect (c.cnt == vsize, 0))
140             {
141               DIRENT_TYPE **new;
142               if (vsize == 0)
143                 vsize = 10;
144               else
145                 vsize *= 2;
146               new = (DIRENT_TYPE **) realloc (v, vsize * sizeof (*v));
147               if (new == NULL)
148                 break;
149               v = new;
150               c.v = (void *) v;
151             }
152
153           dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d;
154           vnew = (DIRENT_TYPE *) malloc (dsize);
155           if (vnew == NULL)
156             break;
157
158           v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize);
159         }
160     }
161
162   if (__builtin_expect (errno, 0) != 0)
163     {
164       save = errno;
165
166       while (c.cnt > 0)
167         free (v[--c.cnt]);
168       free (v);
169       c.cnt = -1;
170     }
171   else
172     {
173       /* Sort the list if we have a comparison function to sort with.  */
174       if (cmp != NULL)
175         qsort (v, c.cnt, sizeof (*v), (int (*) (const void *, const void *)) cmp);
176
177       *namelist = v;
178     }
179
180 #if _LIBC
181   __libc_cleanup_pop (0);
182 #endif
183
184   (void) __closedir (dp);
185   __set_errno (save);
186
187   return c.cnt;
188 }