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