]> Creatis software - cpPlugins.git/blob - lib/cpPlugins/OS/tinydir.h
...
[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 cpPlugins__OS__TINYDIR_H
30 #define cpPlugins__OS__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 // cpPlugins__OS__TINYDIR_H
805
806 // eof - $RCSfile$