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