1/* Logic guts of scandir*.
2 Copyright (C) 1992-2017 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, see
17 <http://www.gnu.org/licenses/>. */
18
19#include <dirent.h>
20#include <errno.h>
21#include <stdlib.h>
22#include <string.h>
23#include <libc-lock.h>
24
25#ifndef SCANDIR_TAIL
26# define SCANDIR_TAIL __scandir_tail
27# define READDIR __readdir
28# define DIRENT_TYPE struct dirent
29#endif
30
31internal_function
32int
33SCANDIR_TAIL (DIR *dp,
34 DIRENT_TYPE ***namelist,
35 int (*select) (const DIRENT_TYPE *),
36 int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **))
37{
38 if (dp == NULL)
39 return -1;
40
41 int save = errno;
42 __set_errno (0);
43
44 int result;
45 struct scandir_cancel_struct c = { .dp = dp };
46 __libc_cleanup_push (&__scandir_cancel_handler, &c);
47
48 DIRENT_TYPE **v = NULL;
49 size_t vsize = 0;
50 DIRENT_TYPE *d;
51 while ((d = READDIR (dp)) != NULL)
52 {
53 if (select != NULL)
54 {
55 int selected = (*select) (d);
56
57 /* The SELECT function might have changed errno. It was
58 zero before and it need to be again to make the later
59 tests work. */
60 __set_errno (0);
61
62 if (!selected)
63 continue;
64 }
65 else
66 __set_errno (0);
67
68 if (__glibc_unlikely (c.cnt == vsize))
69 {
70 if (vsize == 0)
71 vsize = 10;
72 else
73 vsize *= 2;
74 DIRENT_TYPE **new = realloc (v, vsize * sizeof *v);
75 if (new == NULL)
76 break;
77 c.v = v = new;
78 }
79
80 size_t dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d;
81 DIRENT_TYPE *vnew = malloc (dsize);
82 if (vnew == NULL)
83 break;
84 v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize);
85 }
86
87 if (__glibc_likely (errno == 0))
88 {
89 __closedir (dp);
90
91 /* Sort the list if we have a comparison function to sort with. */
92 if (cmp != NULL)
93 qsort (v, c.cnt, sizeof *v, (__compar_fn_t) cmp);
94
95 *namelist = v;
96 result = c.cnt;
97 }
98 else
99 {
100 /* This frees everything and calls closedir. */
101 __scandir_cancel_handler (&c);
102 result = -1;
103 }
104
105 __libc_cleanup_pop (0);
106
107 if (result >= 0)
108 __set_errno (save);
109 return result;
110}
111