1*10465441SEvalZero /*
2*10465441SEvalZero Copyright (c) 2013-2017, tinydir authors:
3*10465441SEvalZero - Cong Xu
4*10465441SEvalZero - Lautis Sun
5*10465441SEvalZero - Baudouin Feildel
6*10465441SEvalZero - Andargor <[email protected]>
7*10465441SEvalZero All rights reserved.
8*10465441SEvalZero
9*10465441SEvalZero Redistribution and use in source and binary forms, with or without
10*10465441SEvalZero modification, are permitted provided that the following conditions are met:
11*10465441SEvalZero
12*10465441SEvalZero 1. Redistributions of source code must retain the above copyright notice, this
13*10465441SEvalZero list of conditions and the following disclaimer.
14*10465441SEvalZero 2. Redistributions in binary form must reproduce the above copyright notice,
15*10465441SEvalZero this list of conditions and the following disclaimer in the documentation
16*10465441SEvalZero and/or other materials provided with the distribution.
17*10465441SEvalZero
18*10465441SEvalZero THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19*10465441SEvalZero ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20*10465441SEvalZero WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21*10465441SEvalZero DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22*10465441SEvalZero ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23*10465441SEvalZero (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24*10465441SEvalZero LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25*10465441SEvalZero ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*10465441SEvalZero (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27*10465441SEvalZero SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*10465441SEvalZero */
29*10465441SEvalZero #ifndef TINYDIR_H
30*10465441SEvalZero #define TINYDIR_H
31*10465441SEvalZero
32*10465441SEvalZero #ifdef __cplusplus
33*10465441SEvalZero extern "C" {
34*10465441SEvalZero #endif
35*10465441SEvalZero
36*10465441SEvalZero #if ((defined _UNICODE) && !(defined UNICODE))
37*10465441SEvalZero #define UNICODE
38*10465441SEvalZero #endif
39*10465441SEvalZero
40*10465441SEvalZero #if ((defined UNICODE) && !(defined _UNICODE))
41*10465441SEvalZero #define _UNICODE
42*10465441SEvalZero #endif
43*10465441SEvalZero
44*10465441SEvalZero #include <errno.h>
45*10465441SEvalZero #include <stdlib.h>
46*10465441SEvalZero #include <string.h>
47*10465441SEvalZero #ifdef _MSC_VER
48*10465441SEvalZero # define WIN32_LEAN_AND_MEAN
49*10465441SEvalZero # include <windows.h>
50*10465441SEvalZero # include <tchar.h>
51*10465441SEvalZero # pragma warning(push)
52*10465441SEvalZero # pragma warning (disable : 4996)
53*10465441SEvalZero #else
54*10465441SEvalZero # include <dirent.h>
55*10465441SEvalZero # include <libgen.h>
56*10465441SEvalZero # include <sys/stat.h>
57*10465441SEvalZero # include <stddef.h>
58*10465441SEvalZero #endif
59*10465441SEvalZero #ifdef __MINGW32__
60*10465441SEvalZero # include <tchar.h>
61*10465441SEvalZero #endif
62*10465441SEvalZero
63*10465441SEvalZero
64*10465441SEvalZero /* types */
65*10465441SEvalZero
66*10465441SEvalZero /* Windows UNICODE wide character support */
67*10465441SEvalZero #if defined _MSC_VER || defined __MINGW32__
68*10465441SEvalZero #define _tinydir_char_t TCHAR
69*10465441SEvalZero #define TINYDIR_STRING(s) _TEXT(s)
70*10465441SEvalZero #define _tinydir_strlen _tcslen
71*10465441SEvalZero #define _tinydir_strcpy _tcscpy
72*10465441SEvalZero #define _tinydir_strcat _tcscat
73*10465441SEvalZero #define _tinydir_strcmp _tcscmp
74*10465441SEvalZero #define _tinydir_strrchr _tcsrchr
75*10465441SEvalZero #define _tinydir_strncmp _tcsncmp
76*10465441SEvalZero #else
77*10465441SEvalZero #define _tinydir_char_t char
78*10465441SEvalZero #define TINYDIR_STRING(s) s
79*10465441SEvalZero #define _tinydir_strlen strlen
80*10465441SEvalZero #define _tinydir_strcpy strcpy
81*10465441SEvalZero #define _tinydir_strcat strcat
82*10465441SEvalZero #define _tinydir_strcmp strcmp
83*10465441SEvalZero #define _tinydir_strrchr strrchr
84*10465441SEvalZero #define _tinydir_strncmp strncmp
85*10465441SEvalZero #endif
86*10465441SEvalZero
87*10465441SEvalZero #if (defined _MSC_VER || defined __MINGW32__)
88*10465441SEvalZero #include <windows.h>
89*10465441SEvalZero #define _TINYDIR_PATH_MAX MAX_PATH
90*10465441SEvalZero #elif defined __linux__
91*10465441SEvalZero #include <linux/limits.h>
92*10465441SEvalZero #define _TINYDIR_PATH_MAX PATH_MAX
93*10465441SEvalZero #else
94*10465441SEvalZero #define _TINYDIR_PATH_MAX 4096
95*10465441SEvalZero #endif
96*10465441SEvalZero
97*10465441SEvalZero #ifdef _MSC_VER
98*10465441SEvalZero /* extra chars for the "\\*" mask */
99*10465441SEvalZero # define _TINYDIR_PATH_EXTRA 2
100*10465441SEvalZero #else
101*10465441SEvalZero # define _TINYDIR_PATH_EXTRA 0
102*10465441SEvalZero #endif
103*10465441SEvalZero
104*10465441SEvalZero #define _TINYDIR_FILENAME_MAX 256
105*10465441SEvalZero
106*10465441SEvalZero #if (defined _MSC_VER || defined __MINGW32__)
107*10465441SEvalZero #define _TINYDIR_DRIVE_MAX 3
108*10465441SEvalZero #endif
109*10465441SEvalZero
110*10465441SEvalZero #ifdef _MSC_VER
111*10465441SEvalZero # define _TINYDIR_FUNC static __inline
112*10465441SEvalZero #elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
113*10465441SEvalZero # define _TINYDIR_FUNC static __inline__
114*10465441SEvalZero #else
115*10465441SEvalZero # define _TINYDIR_FUNC static inline
116*10465441SEvalZero #endif
117*10465441SEvalZero
118*10465441SEvalZero /* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */
119*10465441SEvalZero #ifdef TINYDIR_USE_READDIR_R
120*10465441SEvalZero
121*10465441SEvalZero /* readdir_r is a POSIX-only function, and may not be available under various
122*10465441SEvalZero * environments/settings, e.g. MinGW. Use readdir fallback */
123*10465441SEvalZero #if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\
124*10465441SEvalZero _POSIX_SOURCE
125*10465441SEvalZero # define _TINYDIR_HAS_READDIR_R
126*10465441SEvalZero #endif
127*10465441SEvalZero #if _POSIX_C_SOURCE >= 200112L
128*10465441SEvalZero # define _TINYDIR_HAS_FPATHCONF
129*10465441SEvalZero # include <unistd.h>
130*10465441SEvalZero #endif
131*10465441SEvalZero #if _BSD_SOURCE || _SVID_SOURCE || \
132*10465441SEvalZero (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
133*10465441SEvalZero # define _TINYDIR_HAS_DIRFD
134*10465441SEvalZero # include <sys/types.h>
135*10465441SEvalZero #endif
136*10465441SEvalZero #if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
137*10465441SEvalZero defined _PC_NAME_MAX
138*10465441SEvalZero # define _TINYDIR_USE_FPATHCONF
139*10465441SEvalZero #endif
140*10465441SEvalZero #if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
141*10465441SEvalZero !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
142*10465441SEvalZero # define _TINYDIR_USE_READDIR
143*10465441SEvalZero #endif
144*10465441SEvalZero
145*10465441SEvalZero /* Use readdir by default */
146*10465441SEvalZero #else
147*10465441SEvalZero # define _TINYDIR_USE_READDIR
148*10465441SEvalZero #endif
149*10465441SEvalZero
150*10465441SEvalZero /* MINGW32 has two versions of dirent, ASCII and UNICODE*/
151*10465441SEvalZero #ifndef _MSC_VER
152*10465441SEvalZero #if (defined __MINGW32__) && (defined _UNICODE)
153*10465441SEvalZero #define _TINYDIR_DIR _WDIR
154*10465441SEvalZero #define _tinydir_dirent _wdirent
155*10465441SEvalZero #define _tinydir_opendir _wopendir
156*10465441SEvalZero #define _tinydir_readdir _wreaddir
157*10465441SEvalZero #define _tinydir_closedir _wclosedir
158*10465441SEvalZero #else
159*10465441SEvalZero #define _TINYDIR_DIR DIR
160*10465441SEvalZero #define _tinydir_dirent dirent
161*10465441SEvalZero #define _tinydir_opendir opendir
162*10465441SEvalZero #define _tinydir_readdir readdir
163*10465441SEvalZero #define _tinydir_closedir closedir
164*10465441SEvalZero #endif
165*10465441SEvalZero #endif
166*10465441SEvalZero
167*10465441SEvalZero /* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */
168*10465441SEvalZero #if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE)
169*10465441SEvalZero #elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE)
170*10465441SEvalZero #else
171*10465441SEvalZero #error "Either define both alloc and free or none of them!"
172*10465441SEvalZero #endif
173*10465441SEvalZero
174*10465441SEvalZero #if !defined(_TINYDIR_MALLOC)
175*10465441SEvalZero #define _TINYDIR_MALLOC(_size) malloc(_size)
176*10465441SEvalZero #define _TINYDIR_FREE(_ptr) free(_ptr)
177*10465441SEvalZero #endif /* !defined(_TINYDIR_MALLOC) */
178*10465441SEvalZero
179*10465441SEvalZero typedef struct tinydir_file
180*10465441SEvalZero {
181*10465441SEvalZero _tinydir_char_t path[_TINYDIR_PATH_MAX];
182*10465441SEvalZero _tinydir_char_t name[_TINYDIR_FILENAME_MAX];
183*10465441SEvalZero _tinydir_char_t *extension;
184*10465441SEvalZero int is_dir;
185*10465441SEvalZero int is_reg;
186*10465441SEvalZero
187*10465441SEvalZero #ifndef _MSC_VER
188*10465441SEvalZero #ifdef __MINGW32__
189*10465441SEvalZero struct _stat _s;
190*10465441SEvalZero #else
191*10465441SEvalZero struct stat _s;
192*10465441SEvalZero #endif
193*10465441SEvalZero #endif
194*10465441SEvalZero } tinydir_file;
195*10465441SEvalZero
196*10465441SEvalZero typedef struct tinydir_dir
197*10465441SEvalZero {
198*10465441SEvalZero _tinydir_char_t path[_TINYDIR_PATH_MAX];
199*10465441SEvalZero int has_next;
200*10465441SEvalZero size_t n_files;
201*10465441SEvalZero
202*10465441SEvalZero tinydir_file *_files;
203*10465441SEvalZero #ifdef _MSC_VER
204*10465441SEvalZero HANDLE _h;
205*10465441SEvalZero WIN32_FIND_DATA _f;
206*10465441SEvalZero #else
207*10465441SEvalZero _TINYDIR_DIR *_d;
208*10465441SEvalZero struct _tinydir_dirent *_e;
209*10465441SEvalZero #ifndef _TINYDIR_USE_READDIR
210*10465441SEvalZero struct _tinydir_dirent *_ep;
211*10465441SEvalZero #endif
212*10465441SEvalZero #endif
213*10465441SEvalZero } tinydir_dir;
214*10465441SEvalZero
215*10465441SEvalZero
216*10465441SEvalZero /* declarations */
217*10465441SEvalZero
218*10465441SEvalZero _TINYDIR_FUNC
219*10465441SEvalZero int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path);
220*10465441SEvalZero _TINYDIR_FUNC
221*10465441SEvalZero int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path);
222*10465441SEvalZero _TINYDIR_FUNC
223*10465441SEvalZero void tinydir_close(tinydir_dir *dir);
224*10465441SEvalZero
225*10465441SEvalZero _TINYDIR_FUNC
226*10465441SEvalZero int tinydir_next(tinydir_dir *dir);
227*10465441SEvalZero _TINYDIR_FUNC
228*10465441SEvalZero int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
229*10465441SEvalZero _TINYDIR_FUNC
230*10465441SEvalZero int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
231*10465441SEvalZero _TINYDIR_FUNC
232*10465441SEvalZero int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
233*10465441SEvalZero
234*10465441SEvalZero _TINYDIR_FUNC
235*10465441SEvalZero int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path);
236*10465441SEvalZero _TINYDIR_FUNC
237*10465441SEvalZero void _tinydir_get_ext(tinydir_file *file);
238*10465441SEvalZero _TINYDIR_FUNC
239*10465441SEvalZero int _tinydir_file_cmp(const void *a, const void *b);
240*10465441SEvalZero #ifndef _MSC_VER
241*10465441SEvalZero #ifndef _TINYDIR_USE_READDIR
242*10465441SEvalZero _TINYDIR_FUNC
243*10465441SEvalZero size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
244*10465441SEvalZero #endif
245*10465441SEvalZero #endif
246*10465441SEvalZero
247*10465441SEvalZero
248*10465441SEvalZero /* definitions*/
249*10465441SEvalZero
250*10465441SEvalZero _TINYDIR_FUNC
tinydir_open(tinydir_dir * dir,const _tinydir_char_t * path)251*10465441SEvalZero int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
252*10465441SEvalZero {
253*10465441SEvalZero #ifndef _MSC_VER
254*10465441SEvalZero #ifndef _TINYDIR_USE_READDIR
255*10465441SEvalZero int error;
256*10465441SEvalZero int size; /* using int size */
257*10465441SEvalZero #endif
258*10465441SEvalZero #else
259*10465441SEvalZero _tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
260*10465441SEvalZero #endif
261*10465441SEvalZero _tinydir_char_t *pathp;
262*10465441SEvalZero
263*10465441SEvalZero if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
264*10465441SEvalZero {
265*10465441SEvalZero errno = EINVAL;
266*10465441SEvalZero return -1;
267*10465441SEvalZero }
268*10465441SEvalZero if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
269*10465441SEvalZero {
270*10465441SEvalZero errno = ENAMETOOLONG;
271*10465441SEvalZero return -1;
272*10465441SEvalZero }
273*10465441SEvalZero
274*10465441SEvalZero /* initialise dir */
275*10465441SEvalZero dir->_files = NULL;
276*10465441SEvalZero #ifdef _MSC_VER
277*10465441SEvalZero dir->_h = INVALID_HANDLE_VALUE;
278*10465441SEvalZero #else
279*10465441SEvalZero dir->_d = NULL;
280*10465441SEvalZero #ifndef _TINYDIR_USE_READDIR
281*10465441SEvalZero dir->_ep = NULL;
282*10465441SEvalZero #endif
283*10465441SEvalZero #endif
284*10465441SEvalZero tinydir_close(dir);
285*10465441SEvalZero
286*10465441SEvalZero _tinydir_strcpy(dir->path, path);
287*10465441SEvalZero /* Remove trailing slashes */
288*10465441SEvalZero pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
289*10465441SEvalZero while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/')))
290*10465441SEvalZero {
291*10465441SEvalZero *pathp = TINYDIR_STRING('\0');
292*10465441SEvalZero pathp++;
293*10465441SEvalZero }
294*10465441SEvalZero #ifdef _MSC_VER
295*10465441SEvalZero _tinydir_strcpy(path_buf, dir->path);
296*10465441SEvalZero _tinydir_strcat(path_buf, TINYDIR_STRING("\\*"));
297*10465441SEvalZero #if (defined WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP)
298*10465441SEvalZero dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0);
299*10465441SEvalZero #else
300*10465441SEvalZero dir->_h = FindFirstFile(path_buf, &dir->_f);
301*10465441SEvalZero #endif
302*10465441SEvalZero if (dir->_h == INVALID_HANDLE_VALUE)
303*10465441SEvalZero {
304*10465441SEvalZero errno = ENOENT;
305*10465441SEvalZero #else
306*10465441SEvalZero dir->_d = _tinydir_opendir(path);
307*10465441SEvalZero if (dir->_d == NULL)
308*10465441SEvalZero {
309*10465441SEvalZero #endif
310*10465441SEvalZero goto bail;
311*10465441SEvalZero }
312*10465441SEvalZero
313*10465441SEvalZero /* read first file */
314*10465441SEvalZero dir->has_next = 1;
315*10465441SEvalZero #ifndef _MSC_VER
316*10465441SEvalZero #ifdef _TINYDIR_USE_READDIR
317*10465441SEvalZero dir->_e = _tinydir_readdir(dir->_d);
318*10465441SEvalZero #else
319*10465441SEvalZero /* allocate dirent buffer for readdir_r */
320*10465441SEvalZero size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
321*10465441SEvalZero if (size == -1) return -1;
322*10465441SEvalZero dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
323*10465441SEvalZero if (dir->_ep == NULL) return -1;
324*10465441SEvalZero
325*10465441SEvalZero error = readdir_r(dir->_d, dir->_ep, &dir->_e);
326*10465441SEvalZero if (error != 0) return -1;
327*10465441SEvalZero #endif
328*10465441SEvalZero if (dir->_e == NULL)
329*10465441SEvalZero {
330*10465441SEvalZero dir->has_next = 0;
331*10465441SEvalZero }
332*10465441SEvalZero #endif
333*10465441SEvalZero
334*10465441SEvalZero return 0;
335*10465441SEvalZero
336*10465441SEvalZero bail:
337*10465441SEvalZero tinydir_close(dir);
338*10465441SEvalZero return -1;
339*10465441SEvalZero }
340*10465441SEvalZero
341*10465441SEvalZero _TINYDIR_FUNC
342*10465441SEvalZero int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
343*10465441SEvalZero {
344*10465441SEvalZero /* Count the number of files first, to pre-allocate the files array */
345*10465441SEvalZero size_t n_files = 0;
346*10465441SEvalZero if (tinydir_open(dir, path) == -1)
347*10465441SEvalZero {
348*10465441SEvalZero return -1;
349*10465441SEvalZero }
350*10465441SEvalZero while (dir->has_next)
351*10465441SEvalZero {
352*10465441SEvalZero n_files++;
353*10465441SEvalZero if (tinydir_next(dir) == -1)
354*10465441SEvalZero {
355*10465441SEvalZero goto bail;
356*10465441SEvalZero }
357*10465441SEvalZero }
358*10465441SEvalZero tinydir_close(dir);
359*10465441SEvalZero
360*10465441SEvalZero if (tinydir_open(dir, path) == -1)
361*10465441SEvalZero {
362*10465441SEvalZero return -1;
363*10465441SEvalZero }
364*10465441SEvalZero
365*10465441SEvalZero dir->n_files = 0;
366*10465441SEvalZero dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
367*10465441SEvalZero if (dir->_files == NULL)
368*10465441SEvalZero {
369*10465441SEvalZero goto bail;
370*10465441SEvalZero }
371*10465441SEvalZero while (dir->has_next)
372*10465441SEvalZero {
373*10465441SEvalZero tinydir_file *p_file;
374*10465441SEvalZero dir->n_files++;
375*10465441SEvalZero
376*10465441SEvalZero p_file = &dir->_files[dir->n_files - 1];
377*10465441SEvalZero if (tinydir_readfile(dir, p_file) == -1)
378*10465441SEvalZero {
379*10465441SEvalZero goto bail;
380*10465441SEvalZero }
381*10465441SEvalZero
382*10465441SEvalZero if (tinydir_next(dir) == -1)
383*10465441SEvalZero {
384*10465441SEvalZero goto bail;
385*10465441SEvalZero }
386*10465441SEvalZero
387*10465441SEvalZero /* Just in case the number of files has changed between the first and
388*10465441SEvalZero second reads, terminate without writing into unallocated memory */
389*10465441SEvalZero if (dir->n_files == n_files)
390*10465441SEvalZero {
391*10465441SEvalZero break;
392*10465441SEvalZero }
393*10465441SEvalZero }
394*10465441SEvalZero
395*10465441SEvalZero qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
396*10465441SEvalZero
397*10465441SEvalZero return 0;
398*10465441SEvalZero
399*10465441SEvalZero bail:
400*10465441SEvalZero tinydir_close(dir);
401*10465441SEvalZero return -1;
402*10465441SEvalZero }
403*10465441SEvalZero
404*10465441SEvalZero _TINYDIR_FUNC
405*10465441SEvalZero void tinydir_close(tinydir_dir *dir)
406*10465441SEvalZero {
407*10465441SEvalZero if (dir == NULL)
408*10465441SEvalZero {
409*10465441SEvalZero return;
410*10465441SEvalZero }
411*10465441SEvalZero
412*10465441SEvalZero memset(dir->path, 0, sizeof(dir->path));
413*10465441SEvalZero dir->has_next = 0;
414*10465441SEvalZero dir->n_files = 0;
415*10465441SEvalZero _TINYDIR_FREE(dir->_files);
416*10465441SEvalZero dir->_files = NULL;
417*10465441SEvalZero #ifdef _MSC_VER
418*10465441SEvalZero if (dir->_h != INVALID_HANDLE_VALUE)
419*10465441SEvalZero {
420*10465441SEvalZero FindClose(dir->_h);
421*10465441SEvalZero }
422*10465441SEvalZero dir->_h = INVALID_HANDLE_VALUE;
423*10465441SEvalZero #else
424*10465441SEvalZero if (dir->_d)
425*10465441SEvalZero {
426*10465441SEvalZero _tinydir_closedir(dir->_d);
427*10465441SEvalZero }
428*10465441SEvalZero dir->_d = NULL;
429*10465441SEvalZero dir->_e = NULL;
430*10465441SEvalZero #ifndef _TINYDIR_USE_READDIR
431*10465441SEvalZero _TINYDIR_FREE(dir->_ep);
432*10465441SEvalZero dir->_ep = NULL;
433*10465441SEvalZero #endif
434*10465441SEvalZero #endif
435*10465441SEvalZero }
436*10465441SEvalZero
437*10465441SEvalZero _TINYDIR_FUNC
438*10465441SEvalZero int tinydir_next(tinydir_dir *dir)
439*10465441SEvalZero {
440*10465441SEvalZero if (dir == NULL)
441*10465441SEvalZero {
442*10465441SEvalZero errno = EINVAL;
443*10465441SEvalZero return -1;
444*10465441SEvalZero }
445*10465441SEvalZero if (!dir->has_next)
446*10465441SEvalZero {
447*10465441SEvalZero errno = ENOENT;
448*10465441SEvalZero return -1;
449*10465441SEvalZero }
450*10465441SEvalZero
451*10465441SEvalZero #ifdef _MSC_VER
452*10465441SEvalZero if (FindNextFile(dir->_h, &dir->_f) == 0)
453*10465441SEvalZero #else
454*10465441SEvalZero #ifdef _TINYDIR_USE_READDIR
455*10465441SEvalZero dir->_e = _tinydir_readdir(dir->_d);
456*10465441SEvalZero #else
457*10465441SEvalZero if (dir->_ep == NULL)
458*10465441SEvalZero {
459*10465441SEvalZero return -1;
460*10465441SEvalZero }
461*10465441SEvalZero if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
462*10465441SEvalZero {
463*10465441SEvalZero return -1;
464*10465441SEvalZero }
465*10465441SEvalZero #endif
466*10465441SEvalZero if (dir->_e == NULL)
467*10465441SEvalZero #endif
468*10465441SEvalZero {
469*10465441SEvalZero dir->has_next = 0;
470*10465441SEvalZero #ifdef _MSC_VER
471*10465441SEvalZero if (GetLastError() != ERROR_SUCCESS &&
472*10465441SEvalZero GetLastError() != ERROR_NO_MORE_FILES)
473*10465441SEvalZero {
474*10465441SEvalZero tinydir_close(dir);
475*10465441SEvalZero errno = EIO;
476*10465441SEvalZero return -1;
477*10465441SEvalZero }
478*10465441SEvalZero #endif
479*10465441SEvalZero }
480*10465441SEvalZero
481*10465441SEvalZero return 0;
482*10465441SEvalZero }
483*10465441SEvalZero
484*10465441SEvalZero _TINYDIR_FUNC
485*10465441SEvalZero int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
486*10465441SEvalZero {
487*10465441SEvalZero if (dir == NULL || file == NULL)
488*10465441SEvalZero {
489*10465441SEvalZero errno = EINVAL;
490*10465441SEvalZero return -1;
491*10465441SEvalZero }
492*10465441SEvalZero #ifdef _MSC_VER
493*10465441SEvalZero if (dir->_h == INVALID_HANDLE_VALUE)
494*10465441SEvalZero #else
495*10465441SEvalZero if (dir->_e == NULL)
496*10465441SEvalZero #endif
497*10465441SEvalZero {
498*10465441SEvalZero errno = ENOENT;
499*10465441SEvalZero return -1;
500*10465441SEvalZero }
501*10465441SEvalZero if (_tinydir_strlen(dir->path) +
502*10465441SEvalZero _tinydir_strlen(
503*10465441SEvalZero #ifdef _MSC_VER
504*10465441SEvalZero dir->_f.cFileName
505*10465441SEvalZero #else
506*10465441SEvalZero dir->_e->d_name
507*10465441SEvalZero #endif
508*10465441SEvalZero ) + 1 + _TINYDIR_PATH_EXTRA >=
509*10465441SEvalZero _TINYDIR_PATH_MAX)
510*10465441SEvalZero {
511*10465441SEvalZero /* the path for the file will be too long */
512*10465441SEvalZero errno = ENAMETOOLONG;
513*10465441SEvalZero return -1;
514*10465441SEvalZero }
515*10465441SEvalZero if (_tinydir_strlen(
516*10465441SEvalZero #ifdef _MSC_VER
517*10465441SEvalZero dir->_f.cFileName
518*10465441SEvalZero #else
519*10465441SEvalZero dir->_e->d_name
520*10465441SEvalZero #endif
521*10465441SEvalZero ) >= _TINYDIR_FILENAME_MAX)
522*10465441SEvalZero {
523*10465441SEvalZero errno = ENAMETOOLONG;
524*10465441SEvalZero return -1;
525*10465441SEvalZero }
526*10465441SEvalZero
527*10465441SEvalZero _tinydir_strcpy(file->path, dir->path);
528*10465441SEvalZero _tinydir_strcat(file->path, TINYDIR_STRING("/"));
529*10465441SEvalZero _tinydir_strcpy(file->name,
530*10465441SEvalZero #ifdef _MSC_VER
531*10465441SEvalZero dir->_f.cFileName
532*10465441SEvalZero #else
533*10465441SEvalZero dir->_e->d_name
534*10465441SEvalZero #endif
535*10465441SEvalZero );
536*10465441SEvalZero _tinydir_strcat(file->path, file->name);
537*10465441SEvalZero #ifndef _MSC_VER
538*10465441SEvalZero #ifdef __MINGW32__
539*10465441SEvalZero if (_tstat(
540*10465441SEvalZero #else
541*10465441SEvalZero if (stat(
542*10465441SEvalZero #endif
543*10465441SEvalZero file->path, &file->_s) == -1)
544*10465441SEvalZero {
545*10465441SEvalZero return -1;
546*10465441SEvalZero }
547*10465441SEvalZero #endif
548*10465441SEvalZero _tinydir_get_ext(file);
549*10465441SEvalZero
550*10465441SEvalZero file->is_dir =
551*10465441SEvalZero #ifdef _MSC_VER
552*10465441SEvalZero !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
553*10465441SEvalZero #else
554*10465441SEvalZero S_ISDIR(file->_s.st_mode);
555*10465441SEvalZero #endif
556*10465441SEvalZero file->is_reg =
557*10465441SEvalZero #ifdef _MSC_VER
558*10465441SEvalZero !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
559*10465441SEvalZero (
560*10465441SEvalZero !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
561*10465441SEvalZero !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
562*10465441SEvalZero !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
563*10465441SEvalZero #ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
564*10465441SEvalZero !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
565*10465441SEvalZero #endif
566*10465441SEvalZero #ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
567*10465441SEvalZero !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
568*10465441SEvalZero #endif
569*10465441SEvalZero !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
570*10465441SEvalZero !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
571*10465441SEvalZero #else
572*10465441SEvalZero S_ISREG(file->_s.st_mode);
573*10465441SEvalZero #endif
574*10465441SEvalZero
575*10465441SEvalZero return 0;
576*10465441SEvalZero }
577*10465441SEvalZero
578*10465441SEvalZero _TINYDIR_FUNC
579*10465441SEvalZero int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
580*10465441SEvalZero {
581*10465441SEvalZero if (dir == NULL || file == NULL)
582*10465441SEvalZero {
583*10465441SEvalZero errno = EINVAL;
584*10465441SEvalZero return -1;
585*10465441SEvalZero }
586*10465441SEvalZero if (i >= dir->n_files)
587*10465441SEvalZero {
588*10465441SEvalZero errno = ENOENT;
589*10465441SEvalZero return -1;
590*10465441SEvalZero }
591*10465441SEvalZero
592*10465441SEvalZero memcpy(file, &dir->_files[i], sizeof(tinydir_file));
593*10465441SEvalZero _tinydir_get_ext(file);
594*10465441SEvalZero
595*10465441SEvalZero return 0;
596*10465441SEvalZero }
597*10465441SEvalZero
598*10465441SEvalZero _TINYDIR_FUNC
599*10465441SEvalZero int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
600*10465441SEvalZero {
601*10465441SEvalZero _tinydir_char_t path[_TINYDIR_PATH_MAX];
602*10465441SEvalZero if (dir == NULL)
603*10465441SEvalZero {
604*10465441SEvalZero errno = EINVAL;
605*10465441SEvalZero return -1;
606*10465441SEvalZero }
607*10465441SEvalZero if (i >= dir->n_files || !dir->_files[i].is_dir)
608*10465441SEvalZero {
609*10465441SEvalZero errno = ENOENT;
610*10465441SEvalZero return -1;
611*10465441SEvalZero }
612*10465441SEvalZero
613*10465441SEvalZero _tinydir_strcpy(path, dir->_files[i].path);
614*10465441SEvalZero tinydir_close(dir);
615*10465441SEvalZero if (tinydir_open_sorted(dir, path) == -1)
616*10465441SEvalZero {
617*10465441SEvalZero return -1;
618*10465441SEvalZero }
619*10465441SEvalZero
620*10465441SEvalZero return 0;
621*10465441SEvalZero }
622*10465441SEvalZero
623*10465441SEvalZero /* Open a single file given its path */
624*10465441SEvalZero _TINYDIR_FUNC
625*10465441SEvalZero int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
626*10465441SEvalZero {
627*10465441SEvalZero tinydir_dir dir;
628*10465441SEvalZero int result = 0;
629*10465441SEvalZero int found = 0;
630*10465441SEvalZero _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
631*10465441SEvalZero _tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
632*10465441SEvalZero _tinydir_char_t *dir_name;
633*10465441SEvalZero _tinydir_char_t *base_name;
634*10465441SEvalZero #if (defined _MSC_VER || defined __MINGW32__)
635*10465441SEvalZero _tinydir_char_t drive_buf[_TINYDIR_PATH_MAX];
636*10465441SEvalZero _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
637*10465441SEvalZero #endif
638*10465441SEvalZero
639*10465441SEvalZero if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
640*10465441SEvalZero {
641*10465441SEvalZero errno = EINVAL;
642*10465441SEvalZero return -1;
643*10465441SEvalZero }
644*10465441SEvalZero if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
645*10465441SEvalZero {
646*10465441SEvalZero errno = ENAMETOOLONG;
647*10465441SEvalZero return -1;
648*10465441SEvalZero }
649*10465441SEvalZero
650*10465441SEvalZero /* Get the parent path */
651*10465441SEvalZero #if (defined _MSC_VER || defined __MINGW32__)
652*10465441SEvalZero #if ((defined _MSC_VER) && (_MSC_VER >= 1400))
653*10465441SEvalZero _tsplitpath_s(
654*10465441SEvalZero path,
655*10465441SEvalZero drive_buf, _TINYDIR_DRIVE_MAX,
656*10465441SEvalZero dir_name_buf, _TINYDIR_FILENAME_MAX,
657*10465441SEvalZero file_name_buf, _TINYDIR_FILENAME_MAX,
658*10465441SEvalZero ext_buf, _TINYDIR_FILENAME_MAX);
659*10465441SEvalZero #else
660*10465441SEvalZero _tsplitpath(
661*10465441SEvalZero path,
662*10465441SEvalZero drive_buf,
663*10465441SEvalZero dir_name_buf,
664*10465441SEvalZero file_name_buf,
665*10465441SEvalZero ext_buf);
666*10465441SEvalZero #endif
667*10465441SEvalZero
668*10465441SEvalZero /* _splitpath_s not work fine with only filename and widechar support */
669*10465441SEvalZero #ifdef _UNICODE
670*10465441SEvalZero if (drive_buf[0] == L'\xFEFE')
671*10465441SEvalZero drive_buf[0] = '\0';
672*10465441SEvalZero if (dir_name_buf[0] == L'\xFEFE')
673*10465441SEvalZero dir_name_buf[0] = '\0';
674*10465441SEvalZero #endif
675*10465441SEvalZero
676*10465441SEvalZero if (errno)
677*10465441SEvalZero {
678*10465441SEvalZero errno = EINVAL;
679*10465441SEvalZero return -1;
680*10465441SEvalZero }
681*10465441SEvalZero /* Emulate the behavior of dirname by returning "." for dir name if it's
682*10465441SEvalZero empty */
683*10465441SEvalZero if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
684*10465441SEvalZero {
685*10465441SEvalZero _tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
686*10465441SEvalZero }
687*10465441SEvalZero /* Concatenate the drive letter and dir name to form full dir name */
688*10465441SEvalZero _tinydir_strcat(drive_buf, dir_name_buf);
689*10465441SEvalZero dir_name = drive_buf;
690*10465441SEvalZero /* Concatenate the file name and extension to form base name */
691*10465441SEvalZero _tinydir_strcat(file_name_buf, ext_buf);
692*10465441SEvalZero base_name = file_name_buf;
693*10465441SEvalZero #else
694*10465441SEvalZero _tinydir_strcpy(dir_name_buf, path);
695*10465441SEvalZero dir_name = dirname(dir_name_buf);
696*10465441SEvalZero _tinydir_strcpy(file_name_buf, path);
697*10465441SEvalZero base_name =basename(file_name_buf);
698*10465441SEvalZero #endif
699*10465441SEvalZero
700*10465441SEvalZero /* Open the parent directory */
701*10465441SEvalZero if (tinydir_open(&dir, dir_name) == -1)
702*10465441SEvalZero {
703*10465441SEvalZero return -1;
704*10465441SEvalZero }
705*10465441SEvalZero
706*10465441SEvalZero /* Read through the parent directory and look for the file */
707*10465441SEvalZero while (dir.has_next)
708*10465441SEvalZero {
709*10465441SEvalZero if (tinydir_readfile(&dir, file) == -1)
710*10465441SEvalZero {
711*10465441SEvalZero result = -1;
712*10465441SEvalZero goto bail;
713*10465441SEvalZero }
714*10465441SEvalZero if (_tinydir_strcmp(file->name, base_name) == 0)
715*10465441SEvalZero {
716*10465441SEvalZero /* File found */
717*10465441SEvalZero found = 1;
718*10465441SEvalZero break;
719*10465441SEvalZero }
720*10465441SEvalZero tinydir_next(&dir);
721*10465441SEvalZero }
722*10465441SEvalZero if (!found)
723*10465441SEvalZero {
724*10465441SEvalZero result = -1;
725*10465441SEvalZero errno = ENOENT;
726*10465441SEvalZero }
727*10465441SEvalZero
728*10465441SEvalZero bail:
729*10465441SEvalZero tinydir_close(&dir);
730*10465441SEvalZero return result;
731*10465441SEvalZero }
732*10465441SEvalZero
733*10465441SEvalZero _TINYDIR_FUNC
734*10465441SEvalZero void _tinydir_get_ext(tinydir_file *file)
735*10465441SEvalZero {
736*10465441SEvalZero _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
737*10465441SEvalZero if (period == NULL)
738*10465441SEvalZero {
739*10465441SEvalZero file->extension = &(file->name[_tinydir_strlen(file->name)]);
740*10465441SEvalZero }
741*10465441SEvalZero else
742*10465441SEvalZero {
743*10465441SEvalZero file->extension = period + 1;
744*10465441SEvalZero }
745*10465441SEvalZero }
746*10465441SEvalZero
747*10465441SEvalZero _TINYDIR_FUNC
748*10465441SEvalZero int _tinydir_file_cmp(const void *a, const void *b)
749*10465441SEvalZero {
750*10465441SEvalZero const tinydir_file *fa = (const tinydir_file *)a;
751*10465441SEvalZero const tinydir_file *fb = (const tinydir_file *)b;
752*10465441SEvalZero if (fa->is_dir != fb->is_dir)
753*10465441SEvalZero {
754*10465441SEvalZero return -(fa->is_dir - fb->is_dir);
755*10465441SEvalZero }
756*10465441SEvalZero return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
757*10465441SEvalZero }
758*10465441SEvalZero
759*10465441SEvalZero #ifndef _MSC_VER
760*10465441SEvalZero #ifndef _TINYDIR_USE_READDIR
761*10465441SEvalZero /*
762*10465441SEvalZero The following authored by Ben Hutchings <[email protected]>
763*10465441SEvalZero from https://womble.decadent.org.uk/readdir_r-advisory.html
764*10465441SEvalZero */
765*10465441SEvalZero /* Calculate the required buffer size (in bytes) for directory *
766*10465441SEvalZero * entries read from the given directory handle. Return -1 if this *
767*10465441SEvalZero * this cannot be done. *
768*10465441SEvalZero * *
769*10465441SEvalZero * This code does not trust values of NAME_MAX that are less than *
770*10465441SEvalZero * 255, since some systems (including at least HP-UX) incorrectly *
771*10465441SEvalZero * define it to be a smaller value. */
772*10465441SEvalZero _TINYDIR_FUNC
773*10465441SEvalZero size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
774*10465441SEvalZero {
775*10465441SEvalZero long name_max;
776*10465441SEvalZero size_t name_end;
777*10465441SEvalZero /* parameter may be unused */
778*10465441SEvalZero (void)dirp;
779*10465441SEvalZero
780*10465441SEvalZero #if defined _TINYDIR_USE_FPATHCONF
781*10465441SEvalZero name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
782*10465441SEvalZero if (name_max == -1)
783*10465441SEvalZero #if defined(NAME_MAX)
784*10465441SEvalZero name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
785*10465441SEvalZero #else
786*10465441SEvalZero return (size_t)(-1);
787*10465441SEvalZero #endif
788*10465441SEvalZero #elif defined(NAME_MAX)
789*10465441SEvalZero name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
790*10465441SEvalZero #else
791*10465441SEvalZero #error "buffer size for readdir_r cannot be determined"
792*10465441SEvalZero #endif
793*10465441SEvalZero name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
794*10465441SEvalZero return (name_end > sizeof(struct _tinydir_dirent) ?
795*10465441SEvalZero name_end : sizeof(struct _tinydir_dirent));
796*10465441SEvalZero }
797*10465441SEvalZero #endif
798*10465441SEvalZero #endif
799*10465441SEvalZero
800*10465441SEvalZero #ifdef __cplusplus
801*10465441SEvalZero }
802*10465441SEvalZero #endif
803*10465441SEvalZero
804*10465441SEvalZero # if defined (_MSC_VER)
805*10465441SEvalZero # pragma warning(pop)
806*10465441SEvalZero # endif
807*10465441SEvalZero
808*10465441SEvalZero #endif
809