2 Copyright (c) 2013-2016, tinydir authors:
6 - Andargor <andargor@yahoo.com>
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
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.
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.
36 #if ((defined _UNICODE) && !(defined UNICODE))
40 #if ((defined UNICODE) && !(defined _UNICODE))
48 # define WIN32_LEAN_AND_MEAN
51 # pragma warning(push)
52 # pragma warning (disable : 4996)
56 # include <sys/stat.h>
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
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
87 #if (defined _MSC_VER || defined __MINGW32__)
89 #define _TINYDIR_PATH_MAX MAX_PATH
90 #elif defined __linux__
91 #include <linux/limits.h>
92 #define _TINYDIR_PATH_MAX PATH_MAX
94 #define _TINYDIR_PATH_MAX 4096
98 /* extra chars for the "\\*" mask */
99 # define _TINYDIR_PATH_EXTRA 2
101 # define _TINYDIR_PATH_EXTRA 0
104 #define _TINYDIR_FILENAME_MAX 256
106 #if (defined _MSC_VER || defined __MINGW32__)
107 #define _TINYDIR_DRIVE_MAX 3
111 # define _TINYDIR_FUNC static __inline
112 #elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
113 # define _TINYDIR_FUNC static __inline__
115 # define _TINYDIR_FUNC static inline
118 /* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */
119 #ifdef TINYDIR_USE_READDIR_R
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 ||\
125 # define _TINYDIR_HAS_READDIR_R
127 #if _POSIX_C_SOURCE >= 200112L
128 # define _TINYDIR_HAS_FPATHCONF
131 #if _BSD_SOURCE || _SVID_SOURCE || \
132 (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
133 # define _TINYDIR_HAS_DIRFD
134 # include <sys/types.h>
136 #if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
138 # define _TINYDIR_USE_FPATHCONF
140 #if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
141 !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
142 # define _TINYDIR_USE_READDIR
145 /* Use readdir by default */
147 # define _TINYDIR_USE_READDIR
150 /* MINGW32 has two versions of dirent, ASCII and UNICODE*/
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
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
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)
171 #error "Either define both alloc and free or none of them!"
174 #if !defined(_TINYDIR_MALLOC)
175 #define _TINYDIR_MALLOC(_size) malloc(_size)
176 #define _TINYDIR_FREE(_ptr) free(_ptr)
177 #endif /* !defined(_TINYDIR_MALLOC) */
179 typedef struct tinydir_file
181 _tinydir_char_t path[_TINYDIR_PATH_MAX];
182 _tinydir_char_t name[_TINYDIR_FILENAME_MAX];
183 _tinydir_char_t *extension;
196 typedef struct tinydir_dir
198 _tinydir_char_t path[_TINYDIR_PATH_MAX];
202 tinydir_file *_files;
208 struct _tinydir_dirent *_e;
209 #ifndef _TINYDIR_USE_READDIR
210 struct _tinydir_dirent *_ep;
219 int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path);
221 int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path);
223 void tinydir_close(tinydir_dir *dir);
226 int tinydir_next(tinydir_dir *dir);
228 int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
230 int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
232 int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
235 int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path);
237 void _tinydir_get_ext(tinydir_file *file);
239 int _tinydir_file_cmp(const void *a, const void *b);
241 #ifndef _TINYDIR_USE_READDIR
243 size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
251 int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
254 #ifndef _TINYDIR_USE_READDIR
256 int size; /* using int size */
259 _tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
261 _tinydir_char_t *pathp;
263 if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
268 if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
270 errno = ENAMETOOLONG;
277 dir->_h = INVALID_HANDLE_VALUE;
280 #ifndef _TINYDIR_USE_READDIR
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('/')))
291 *pathp = TINYDIR_STRING('\0');
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)
302 dir->_d = _tinydir_opendir(path);
309 /* read first file */
312 #ifdef _TINYDIR_USE_READDIR
313 dir->_e = _tinydir_readdir(dir->_d);
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;
321 error = readdir_r(dir->_d, dir->_ep, &dir->_e);
322 if (error != 0) return -1;
338 int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
340 /* Count the number of files first, to pre-allocate the files array */
342 if (tinydir_open(dir, path) == -1)
346 while (dir->has_next)
349 if (tinydir_next(dir) == -1)
356 if (tinydir_open(dir, path) == -1)
362 dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
363 if (dir->_files == NULL)
367 while (dir->has_next)
369 tinydir_file *p_file;
372 p_file = &dir->_files[dir->n_files - 1];
373 if (tinydir_readfile(dir, p_file) == -1)
378 if (tinydir_next(dir) == -1)
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)
391 qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
401 void tinydir_close(tinydir_dir *dir)
408 memset(dir->path, 0, sizeof(dir->path));
411 _TINYDIR_FREE(dir->_files);
414 if (dir->_h != INVALID_HANDLE_VALUE)
418 dir->_h = INVALID_HANDLE_VALUE;
422 _tinydir_closedir(dir->_d);
426 #ifndef _TINYDIR_USE_READDIR
427 _TINYDIR_FREE(dir->_ep);
434 int tinydir_next(tinydir_dir *dir)
448 if (FindNextFile(dir->_h, &dir->_f) == 0)
450 #ifdef _TINYDIR_USE_READDIR
451 dir->_e = _tinydir_readdir(dir->_d);
453 if (dir->_ep == NULL)
457 if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
467 if (GetLastError() != ERROR_SUCCESS &&
468 GetLastError() != ERROR_NO_MORE_FILES)
481 int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
483 if (dir == NULL || file == NULL)
489 if (dir->_h == INVALID_HANDLE_VALUE)
497 if (_tinydir_strlen(dir->path) +
504 ) + 1 + _TINYDIR_PATH_EXTRA >=
507 /* the path for the file will be too long */
508 errno = ENAMETOOLONG;
517 ) >= _TINYDIR_FILENAME_MAX)
519 errno = ENAMETOOLONG;
523 _tinydir_strcpy(file->path, dir->path);
524 _tinydir_strcat(file->path, TINYDIR_STRING("/"));
525 _tinydir_strcpy(file->name,
532 _tinydir_strcat(file->path, file->name);
539 file->path, &file->_s) == -1)
544 _tinydir_get_ext(file);
548 !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
550 S_ISDIR(file->_s.st_mode);
554 !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
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) &&
562 #ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
563 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
565 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
566 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
568 S_ISREG(file->_s.st_mode);
575 int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
577 if (dir == NULL || file == NULL)
582 if (i >= dir->n_files)
588 memcpy(file, &dir->_files[i], sizeof(tinydir_file));
589 _tinydir_get_ext(file);
595 int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
597 _tinydir_char_t path[_TINYDIR_PATH_MAX];
603 if (i >= dir->n_files || !dir->_files[i].is_dir)
609 _tinydir_strcpy(path, dir->_files[i].path);
611 if (tinydir_open_sorted(dir, path) == -1)
619 /* Open a single file given its path */
621 int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
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];
635 if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
640 if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
642 errno = ENAMETOOLONG;
646 /* Get the parent path */
647 #if (defined _MSC_VER || defined __MINGW32__)
648 #if ((defined _MSC_VER) && (_MSC_VER >= 1400))
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);
664 /* _splitpath_s not work fine with only filename and widechar support */
666 if (drive_buf[0] == L'\xFEFE')
668 if (dir_name_buf[0] == L'\xFEFE')
669 dir_name_buf[0] = '\0';
677 /* Emulate the behavior of dirname by returning "." for dir name if it's
679 if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
681 _tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
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;
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);
696 /* Open the parent directory */
697 if (tinydir_open(&dir, dir_name) == -1)
702 /* Read through the parent directory and look for the file */
705 if (tinydir_readfile(&dir, file) == -1)
710 if (_tinydir_strcmp(file->name, base_name) == 0)
730 void _tinydir_get_ext(tinydir_file *file)
732 _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
735 file->extension = &(file->name[_tinydir_strlen(file->name)]);
739 file->extension = period + 1;
744 int _tinydir_file_cmp(const void *a, const void *b)
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)
750 return -(fa->is_dir - fb->is_dir);
752 return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
756 #ifndef _TINYDIR_USE_READDIR
758 The following authored by Ben Hutchings <ben@decadent.org.uk>
759 from https://womble.decadent.org.uk/readdir_r-advisory.html
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. *
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. */
769 size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
773 /* parameter may be unused */
776 #if defined _TINYDIR_USE_FPATHCONF
777 name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
779 #if defined(NAME_MAX)
780 name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
784 #elif defined(NAME_MAX)
785 name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
787 #error "buffer size for readdir_r cannot be determined"
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));
800 # if defined (_MSC_VER)
801 # pragma warning(pop)