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