1 /* scandir: Scan a directory, collecting all (selected) items into a an array.
2 *
3 * This code borrowed from 'libit', which can be found here:
4 *
5 * http://www.iro.umontreal.ca/~pinard/libit/dist/scandir/
6 *
7 * The original author put this code in the public domain.
8 * It has been modified slightly to get rid of warnings, etc.
9 *
10 * Below is the email I received from pinard@iro.umontreal.ca (François Pinard)
11 * when I sent him an email asking him about the license, etc. of this
12 * code which I obtained from his site.
13 *
14 * I think the correct spelling of his name is Rich Salz. I think he's now
15 * rsalz@datapower.com...
16 * --
17 * Rich Salz, Chief Security Architect
18 * DataPower Technology http://www.datapower.com
19 * XS40 XML Security Gateway http://www.datapower.com/products/xs40.html
20 *
21 * Copyright(C): none (public domain)
22 * License: none (public domain)
23 * Author: Rich Salz <rsalz@datapower.com>
24 *
25 *
26 *
27 * -- Alan Robertson
28 * alanr@unix.sh
29 *
30 **************************************************************************
31 *
32 * Subject: Re: Scandir replacement function
33 * Date: 18 May 2001 12:00:48 -0400
34 * From: pinard@iro.umontreal.ca (François Pinard)
35 * To: Alan Robertson <alanr@unix.sh>
36 * References: 1
37 *
38 *
39 * [Alan Robertson]
40 *
41 * > Hi, I'd like to use your scandir replacement function found here:
42 * > http://www.iro.umontreal.ca/~pinard/libit/dist/scandir/ But, it does
43 * > not indicate authorship or licensing terms in it. Could you tell me
44 * > who wrote this code, under what license you distribute it, and whether
45 * > and under what terms I may further distribute it?
46 *
47 * Hello, Alan. These are (somewhat) explained in UNSHAR.HDR found in the
48 * same directory. The routines have been written by Rick Saltz (I'm not
49 * completely sure of the spelling) a long while ago. I think that nowadays,
50 * Rick is better known as the main author of the nice INN package.
51 *
52 **************************************************************************
53 *
54 * I spent a little time verifying this with Rick Salz.
55 * The results are below:
56 *
57 **************************************************************************
58 *
59 * Date: Tue, 20 Sep 2005 21:52:09 -0400 (EDT)
60 * From: Rich Salz <rsalz@datapower.com>
61 * To: Alan Robertson <alanr@unix.sh>
62 * Subject: Re: Verifying permissions/licenses/etc on some old code of yours -
63 * scandir.c
64 * In-Reply-To: <433071CA.8000107@unix.sh>
65 * Message-ID: <Pine.LNX.4.44L0.0509202151270.9198-100000@smtp.datapower.com>
66 * Content-Type: TEXT/PLAIN; charset=US-ASCII
67 *
68 * yes, it's most definitely in the public domain.
69 *
70 * I'm glad you find it useful. I'm surprised it hasn't been replaced by,
71 * e.g,. something in GLibC. Ii'm impressed you tracked me down.
72 *
73 * /r$
74 *
75 * --
76 * Rich Salz Chief Security Architect
77 * DataPower Technology http://www.datapower.com
78 * XS40 XML Security Gateway http://www.datapower.com/products/xs40.html
79 * ---------------------------------------------------------------------->
80 * Subject: scandir, ftw REDUX
81 * Date: 1 Jan 88 00:47:01 GMT
82 * From: rsalz@pebbles.bbn.com
83 * Newsgroups: comp.sources.misc
84 *
85 *
86 * Forget my previous message -- I just decided for completeness's sake to
87 * implement the SysV ftw(3) routine, too.
88 *
89 * To repeat, these are public-domain implementations of the SystemV ftw()
90 * routine, the BSD scandir() and alphasort() routines, and documentation for
91 * same. The FTW manpage could be more readable, but so it goes.
92 *
93 * Anyhow, feel free to post these, and incorporate them into your existing
94 * packages. I have readdir() routiens for MSDOS and the Amiga if anyone
95 * wants them, and should have them for VMS by the end of January; let me
96 * know if you want copies.
97 *
98 * Yours in filesystems,
99 * /r$
100 *
101 * Anyhow, feel free to post
102 * ----------------------------------------------------------------------<
103 *
104 */
105
106 #include <crm_internal.h>
107 #include <sys/types.h>
108 #include <dirent.h>
109 #include <stdlib.h>
110 #include <stddef.h>
111 #include <string.h>
112
113 #ifndef NULL
114 # define NULL ((void *) 0)
115 #endif
116
117 /* Initial guess at directory allocated. */
118 #define INITIAL_ALLOCATION 20
119
120 int
121
122
123 scandir(const char *directory_name,
124 struct dirent ***array_pointer, int (*select_function) (const struct dirent *),
125 #ifdef USE_SCANDIR_COMPARE_STRUCT_DIRENT
126 /* This is what the Linux man page says */
127 int (*compare_function) (const struct dirent **, const struct dirent **)
128 #else
129 /* This is what the Linux header file says ... */
130 int (*compare_function) (const void *, const void *)
131 #endif
132 );
133
134 int
135 scandir(const char *directory_name,
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
136 struct dirent ***array_pointer, int (*select_function) (const struct dirent *),
137 #ifdef USE_SCANDIR_COMPARE_STRUCT_DIRENT
138 /* This is what the linux man page says */
139 int (*compare_function) (const struct dirent **, const struct dirent **)
140 #else
141 /* This is what the linux header file says ... */
142 int (*compare_function) (const void *, const void *)
143 #endif
144 )
145 {
146 DIR *directory;
147 struct dirent **array;
148 struct dirent *entry;
149 struct dirent *copy;
150 int allocated = INITIAL_ALLOCATION;
151 int counter = 0;
152
153 /* Get initial list space and open directory. */
154
155 if ((directory = opendir(directory_name)) == NULL) {
156 return -1;
157 }
158
159 if ((array = (struct dirent **)malloc(allocated * sizeof(struct dirent *)))
160 == NULL) {
161 closedir(directory);
162 return -1;
163 }
164
165 /* Read entries in the directory. */
166
167 while (entry = readdir(directory), entry)
168 if (select_function == NULL || (*select_function) (entry)) {
169 /* User wants them all, or he wants this one. Copy the entry. */
170
171 /*
172 * On some OSes the declaration of "entry->d_name" is a minimal-length
173 * placeholder. Example: Solaris:
174 * /usr/include/sys/dirent.h:
175 * "char d_name[1];"
176 * man page "dirent(3)":
177 * The field d_name is the beginning of the character array
178 * giving the name of the directory entry. This name is
179 * null terminated and may have at most MAXNAMLEN chars.
180 * So our malloc length may need to be increased accordingly.
181 * sizeof(entry->d_name): space (possibly minimal) in struct.
182 * strlen(entry->d_name): actual length of the entry.
183 *
184 * John Kavadias <john_kavadias@hotmail.com>
185 * David Lee <t.d.lee@durham.ac.uk>
186 */
187 int namelength = strlen(entry->d_name) + 1; /* length with NULL */
188 int extra = 0;
189
190 if (sizeof(entry->d_name) <= namelength) {
191 /* allocated space <= required space */
192 extra += namelength - sizeof(entry->d_name);
193 }
194
195 if ((copy = (struct dirent *)malloc(sizeof(struct dirent) + extra)) == NULL) {
196 closedir(directory);
197 free(array);
198 return -1;
199 }
200 copy->d_ino = entry->d_ino;
201 copy->d_reclen = entry->d_reclen;
202 strcpy(copy->d_name, entry->d_name);
203
204 /* Save the copy. */
205
206 if (counter + 1 == allocated) {
207 allocated <<= 1;
208 array = pcmk__realloc((char *)array,
209 allocated * sizeof(struct dirent *));
210 if (array == NULL) {
211 closedir(directory);
212 free(array);
213 free(copy);
214 return -1;
215 }
216 }
217 array[counter++] = copy;
218 }
219
220 /* Close things off. */
221
222 array[counter] = NULL;
223 *array_pointer = array;
224 closedir(directory);
225
226 /* Sort? */
227
228 if (counter > 1 && compare_function)
229 qsort((char *)array, counter, sizeof(struct dirent *)
230 , (int (*)(const void *, const void *))(compare_function));
231
232 return counter;
233 }