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