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