]> Creatis software - cpPlugins.git/blob - lib/cpPlugins/OS/tinydir.h
2d4418bc0cca6834c4026e6540fe92779d454672
[cpPlugins.git] / lib / cpPlugins / OS / tinydir.h
1 /*
2 Copyright (c) 2013-2016, 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
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         dir->_h = FindFirstFile(path_buf, &dir->_f);
298         if (dir->_h == INVALID_HANDLE_VALUE)
299         {
300                 errno = ENOENT;
301 #else
302         dir->_d = _tinydir_opendir(path);
303         if (dir->_d == NULL)
304         {
305 #endif
306                 goto bail;
307         }
308
309         /* read first file */
310         dir->has_next = 1;
311 #ifndef _MSC_VER
312 #ifdef _TINYDIR_USE_READDIR
313         dir->_e = _tinydir_readdir(dir->_d);
314 #else
315         /* allocate dirent buffer for readdir_r */
316         size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
317         if (size == -1) return -1;
318         dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
319         if (dir->_ep == NULL) return -1;
320
321         error = readdir_r(dir->_d, dir->_ep, &dir->_e);
322         if (error != 0) return -1;
323 #endif
324         if (dir->_e == NULL)
325         {
326                 dir->has_next = 0;
327         }
328 #endif
329
330         return 0;
331
332 bail:
333         tinydir_close(dir);
334         return -1;
335 }
336
337 _TINYDIR_FUNC
338 int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
339 {
340         /* Count the number of files first, to pre-allocate the files array */
341         size_t n_files = 0;
342         if (tinydir_open(dir, path) == -1)
343         {
344                 return -1;
345         }
346         while (dir->has_next)
347         {
348                 n_files++;
349                 if (tinydir_next(dir) == -1)
350                 {
351                         goto bail;
352                 }
353         }
354         tinydir_close(dir);
355
356         if (tinydir_open(dir, path) == -1)
357         {
358                 return -1;
359         }
360
361         dir->n_files = 0;
362         dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
363         if (dir->_files == NULL)
364         {
365                 goto bail;
366         }
367         while (dir->has_next)
368         {
369                 tinydir_file *p_file;
370                 dir->n_files++;
371
372                 p_file = &dir->_files[dir->n_files - 1];
373                 if (tinydir_readfile(dir, p_file) == -1)
374                 {
375                         goto bail;
376                 }
377
378                 if (tinydir_next(dir) == -1)
379                 {
380                         goto bail;
381                 }
382
383                 /* Just in case the number of files has changed between the first and
384                 second reads, terminate without writing into unallocated memory */
385                 if (dir->n_files == n_files)
386                 {
387                         break;
388                 }
389         }
390
391         qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
392
393         return 0;
394
395 bail:
396         tinydir_close(dir);
397         return -1;
398 }
399
400 _TINYDIR_FUNC
401 void tinydir_close(tinydir_dir *dir)
402 {
403         if (dir == NULL)
404         {
405                 return;
406         }
407
408         memset(dir->path, 0, sizeof(dir->path));
409         dir->has_next = 0;
410         dir->n_files = 0;
411         _TINYDIR_FREE(dir->_files);
412         dir->_files = NULL;
413 #ifdef _MSC_VER
414         if (dir->_h != INVALID_HANDLE_VALUE)
415         {
416                 FindClose(dir->_h);
417         }
418         dir->_h = INVALID_HANDLE_VALUE;
419 #else
420         if (dir->_d)
421         {
422                 _tinydir_closedir(dir->_d);
423         }
424         dir->_d = NULL;
425         dir->_e = NULL;
426 #ifndef _TINYDIR_USE_READDIR
427         _TINYDIR_FREE(dir->_ep);
428         dir->_ep = NULL;
429 #endif
430 #endif
431 }
432
433 _TINYDIR_FUNC
434 int tinydir_next(tinydir_dir *dir)
435 {
436         if (dir == NULL)
437         {
438                 errno = EINVAL;
439                 return -1;
440         }
441         if (!dir->has_next)
442         {
443                 errno = ENOENT;
444                 return -1;
445         }
446
447 #ifdef _MSC_VER
448         if (FindNextFile(dir->_h, &dir->_f) == 0)
449 #else
450 #ifdef _TINYDIR_USE_READDIR
451         dir->_e = _tinydir_readdir(dir->_d);
452 #else
453         if (dir->_ep == NULL)
454         {
455                 return -1;
456         }
457         if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
458         {
459                 return -1;
460         }
461 #endif
462         if (dir->_e == NULL)
463 #endif
464         {
465                 dir->has_next = 0;
466 #ifdef _MSC_VER
467                 if (GetLastError() != ERROR_SUCCESS &&
468                         GetLastError() != ERROR_NO_MORE_FILES)
469                 {
470                         tinydir_close(dir);
471                         errno = EIO;
472                         return -1;
473                 }
474 #endif
475         }
476
477         return 0;
478 }
479
480 _TINYDIR_FUNC
481 int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
482 {
483         if (dir == NULL || file == NULL)
484         {
485                 errno = EINVAL;
486                 return -1;
487         }
488 #ifdef _MSC_VER
489         if (dir->_h == INVALID_HANDLE_VALUE)
490 #else
491         if (dir->_e == NULL)
492 #endif
493         {
494                 errno = ENOENT;
495                 return -1;
496         }
497         if (_tinydir_strlen(dir->path) +
498                 _tinydir_strlen(
499 #ifdef _MSC_VER
500                         dir->_f.cFileName
501 #else
502                         dir->_e->d_name
503 #endif
504                 ) + 1 + _TINYDIR_PATH_EXTRA >=
505                 _TINYDIR_PATH_MAX)
506         {
507                 /* the path for the file will be too long */
508                 errno = ENAMETOOLONG;
509                 return -1;
510         }
511         if (_tinydir_strlen(
512 #ifdef _MSC_VER
513                         dir->_f.cFileName
514 #else
515                         dir->_e->d_name
516 #endif
517                 ) >= _TINYDIR_FILENAME_MAX)
518         {
519                 errno = ENAMETOOLONG;
520                 return -1;
521         }
522
523         _tinydir_strcpy(file->path, dir->path);
524         _tinydir_strcat(file->path, TINYDIR_STRING("/"));
525         _tinydir_strcpy(file->name,
526 #ifdef _MSC_VER
527                 dir->_f.cFileName
528 #else
529                 dir->_e->d_name
530 #endif
531         );
532         _tinydir_strcat(file->path, file->name);
533 #ifndef _MSC_VER
534 #ifdef __MINGW32__
535         if (_tstat(
536 #else
537         if (stat(
538 #endif
539                 file->path, &file->_s) == -1)
540         {
541                 return -1;
542         }
543 #endif
544         _tinydir_get_ext(file);
545
546         file->is_dir =
547 #ifdef _MSC_VER
548                 !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
549 #else
550                 S_ISDIR(file->_s.st_mode);
551 #endif
552         file->is_reg =
553 #ifdef _MSC_VER
554                 !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
555                 (
556                         !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
557                         !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
558                         !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
559 #ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
560                         !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
561 #endif
562 #ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
563                         !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
564 #endif
565                         !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
566                         !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
567 #else
568                 S_ISREG(file->_s.st_mode);
569 #endif
570
571         return 0;
572 }
573
574 _TINYDIR_FUNC
575 int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
576 {
577         if (dir == NULL || file == NULL)
578         {
579                 errno = EINVAL;
580                 return -1;
581         }
582         if (i >= dir->n_files)
583         {
584                 errno = ENOENT;
585                 return -1;
586         }
587
588         memcpy(file, &dir->_files[i], sizeof(tinydir_file));
589         _tinydir_get_ext(file);
590
591         return 0;
592 }
593
594 _TINYDIR_FUNC
595 int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
596 {
597         _tinydir_char_t path[_TINYDIR_PATH_MAX];
598         if (dir == NULL)
599         {
600                 errno = EINVAL;
601                 return -1;
602         }
603         if (i >= dir->n_files || !dir->_files[i].is_dir)
604         {
605                 errno = ENOENT;
606                 return -1;
607         }
608
609         _tinydir_strcpy(path, dir->_files[i].path);
610         tinydir_close(dir);
611         if (tinydir_open_sorted(dir, path) == -1)
612         {
613                 return -1;
614         }
615
616         return 0;
617 }
618
619 /* Open a single file given its path */
620 _TINYDIR_FUNC
621 int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
622 {
623         tinydir_dir dir;
624         int result = 0;
625         int found = 0;
626         _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
627         _tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
628         _tinydir_char_t *dir_name;
629         _tinydir_char_t *base_name;
630 #if (defined _MSC_VER || defined __MINGW32__)
631         _tinydir_char_t drive_buf[_TINYDIR_DRIVE_MAX];
632         _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
633 #endif
634
635         if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
636         {
637                 errno = EINVAL;
638                 return -1;
639         }
640         if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
641         {
642                 errno = ENAMETOOLONG;
643                 return -1;
644         }
645
646         /* Get the parent path */
647 #if (defined _MSC_VER || defined __MINGW32__)
648 #if ((defined _MSC_VER) && (_MSC_VER >= 1400))
649                 _tsplitpath_s(
650                         path,
651                         drive_buf, _TINYDIR_DRIVE_MAX,
652                         dir_name_buf, _TINYDIR_FILENAME_MAX,
653                         file_name_buf, _TINYDIR_FILENAME_MAX,
654                         ext_buf, _TINYDIR_FILENAME_MAX);
655 #else
656                 _tsplitpath(
657                         path,
658                         drive_buf,
659                         dir_name_buf,
660                         file_name_buf,
661                         ext_buf);
662 #endif
663
664 /* _splitpath_s not work fine with only filename and widechar support */
665 #ifdef _UNICODE
666                 if (drive_buf[0] == L'\xFEFE')
667                         drive_buf[0] = '\0';
668                 if (dir_name_buf[0] == L'\xFEFE')
669                         dir_name_buf[0] = '\0';
670 #endif
671
672         if (errno)
673         {
674                 errno = EINVAL;
675                 return -1;
676         }
677         /* Emulate the behavior of dirname by returning "." for dir name if it's
678         empty */
679         if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
680         {
681                 _tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
682         }
683         /* Concatenate the drive letter and dir name to form full dir name */
684         _tinydir_strcat(drive_buf, dir_name_buf);
685         dir_name = drive_buf;
686         /* Concatenate the file name and extension to form base name */
687         _tinydir_strcat(file_name_buf, ext_buf);
688         base_name = file_name_buf;
689 #else
690         _tinydir_strcpy(dir_name_buf, path);
691         dir_name = dirname(dir_name_buf);
692         _tinydir_strcpy(file_name_buf, path);
693         base_name =basename(file_name_buf);
694 #endif
695
696         /* Open the parent directory */
697         if (tinydir_open(&dir, dir_name) == -1)
698         {
699                 return -1;
700         }
701
702         /* Read through the parent directory and look for the file */
703         while (dir.has_next)
704         {
705                 if (tinydir_readfile(&dir, file) == -1)
706                 {
707                         result = -1;
708                         goto bail;
709                 }
710                 if (_tinydir_strcmp(file->name, base_name) == 0)
711                 {
712                         /* File found */
713                         found = 1;
714                         break;
715                 }
716                 tinydir_next(&dir);
717         }
718         if (!found)
719         {
720                 result = -1;
721                 errno = ENOENT;
722         }
723
724 bail:
725         tinydir_close(&dir);
726         return result;
727 }
728
729 _TINYDIR_FUNC
730 void _tinydir_get_ext(tinydir_file *file)
731 {
732         _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
733         if (period == NULL)
734         {
735                 file->extension = &(file->name[_tinydir_strlen(file->name)]);
736         }
737         else
738         {
739                 file->extension = period + 1;
740         }
741 }
742
743 _TINYDIR_FUNC
744 int _tinydir_file_cmp(const void *a, const void *b)
745 {
746         const tinydir_file *fa = (const tinydir_file *)a;
747         const tinydir_file *fb = (const tinydir_file *)b;
748         if (fa->is_dir != fb->is_dir)
749         {
750                 return -(fa->is_dir - fb->is_dir);
751         }
752         return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
753 }
754
755 #ifndef _MSC_VER
756 #ifndef _TINYDIR_USE_READDIR
757 /*
758 The following authored by Ben Hutchings <ben@decadent.org.uk>
759 from https://womble.decadent.org.uk/readdir_r-advisory.html
760 */
761 /* Calculate the required buffer size (in bytes) for directory      *
762 * entries read from the given directory handle.  Return -1 if this  *
763 * this cannot be done.                                              *
764 *                                                                   *
765 * This code does not trust values of NAME_MAX that are less than    *
766 * 255, since some systems (including at least HP-UX) incorrectly    *
767 * define it to be a smaller value.                                  */
768 _TINYDIR_FUNC
769 size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
770 {
771         long name_max;
772         size_t name_end;
773         /* parameter may be unused */
774         (void)dirp;
775
776 #if defined _TINYDIR_USE_FPATHCONF
777         name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
778         if (name_max == -1)
779 #if defined(NAME_MAX)
780                 name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
781 #else
782                 return (size_t)(-1);
783 #endif
784 #elif defined(NAME_MAX)
785         name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
786 #else
787 #error "buffer size for readdir_r cannot be determined"
788 #endif
789         name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
790         return (name_end > sizeof(struct _tinydir_dirent) ?
791                 name_end : sizeof(struct _tinydir_dirent));
792 }
793 #endif
794 #endif
795
796 #ifdef __cplusplus
797 }
798 #endif
799
800 # if defined (_MSC_VER)
801 # pragma warning(pop)
802 # endif
803
804 #endif