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