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