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