1 /* 2 Copyright (c) 2013-2021, tinydir authors: 3 - Cong Xu 4 - Lautis Sun 5 - Baudouin Feildel 6 - Andargor <[email protected]> 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 #elif defined(__cplusplus) 129 # define _TINYDIR_FUNC static inline 130 #elif defined(__GNUC__) 131 /* Suppress unused function warning */ 132 # define _TINYDIR_FUNC __attribute__((unused)) static 133 #else 134 # define _TINYDIR_FUNC static 135 #endif 136 137 /* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */ 138 #ifdef TINYDIR_USE_READDIR_R 139 140 /* readdir_r is a POSIX-only function, and may not be available under various 141 * environments/settings, e.g. MinGW. Use readdir fallback */ 142 #if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\ 143 _POSIX_SOURCE 144 # define _TINYDIR_HAS_READDIR_R 145 #endif 146 #if _POSIX_C_SOURCE >= 200112L 147 # define _TINYDIR_HAS_FPATHCONF 148 # include <unistd.h> 149 #endif 150 #if _BSD_SOURCE || _SVID_SOURCE || \ 151 (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700) 152 # define _TINYDIR_HAS_DIRFD 153 # include <sys/types.h> 154 #endif 155 #if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\ 156 defined _PC_NAME_MAX 157 # define _TINYDIR_USE_FPATHCONF 158 #endif 159 #if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\ 160 !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX) 161 # define _TINYDIR_USE_READDIR 162 #endif 163 164 /* Use readdir by default */ 165 #else 166 # define _TINYDIR_USE_READDIR 167 #endif 168 169 /* MINGW32 has two versions of dirent, ASCII and UNICODE*/ 170 #ifndef _MSC_VER 171 #if (defined __MINGW32__) && (defined _UNICODE) 172 #define _TINYDIR_DIR _WDIR 173 #define _tinydir_dirent _wdirent 174 #define _tinydir_opendir _wopendir 175 #define _tinydir_readdir _wreaddir 176 #define _tinydir_closedir _wclosedir 177 #else 178 #define _TINYDIR_DIR DIR 179 #define _tinydir_dirent dirent 180 #define _tinydir_opendir opendir 181 #define _tinydir_readdir readdir 182 #define _tinydir_closedir closedir 183 #endif 184 #endif 185 186 /* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */ 187 #if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE) 188 #elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE) 189 #else 190 #error "Either define both alloc and free or none of them!" 191 #endif 192 193 #if !defined(_TINYDIR_MALLOC) 194 #define _TINYDIR_MALLOC(_size) malloc(_size) 195 #define _TINYDIR_FREE(_ptr) free(_ptr) 196 #endif /* !defined(_TINYDIR_MALLOC) */ 197 198 typedef struct tinydir_file 199 { 200 _tinydir_char_t path[_TINYDIR_PATH_MAX]; 201 _tinydir_char_t name[_TINYDIR_FILENAME_MAX]; 202 _tinydir_char_t *extension; 203 int is_dir; 204 int is_reg; 205 206 #ifndef _MSC_VER 207 #ifdef __MINGW32__ 208 struct _stat _s; 209 #else 210 struct stat _s; 211 #endif 212 #endif 213 } tinydir_file; 214 215 typedef struct tinydir_dir 216 { 217 _tinydir_char_t path[_TINYDIR_PATH_MAX]; 218 int has_next; 219 size_t n_files; 220 221 tinydir_file *_files; 222 #ifdef _MSC_VER 223 HANDLE _h; 224 WIN32_FIND_DATA _f; 225 #else 226 _TINYDIR_DIR *_d; 227 struct _tinydir_dirent *_e; 228 #ifndef _TINYDIR_USE_READDIR 229 struct _tinydir_dirent *_ep; 230 #endif 231 #endif 232 } tinydir_dir; 233 234 235 /* declarations */ 236 237 _TINYDIR_FUNC 238 int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path); 239 _TINYDIR_FUNC 240 int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path); 241 _TINYDIR_FUNC 242 void tinydir_close(tinydir_dir *dir); 243 244 _TINYDIR_FUNC 245 int tinydir_next(tinydir_dir *dir); 246 _TINYDIR_FUNC 247 int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file); 248 _TINYDIR_FUNC 249 int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i); 250 _TINYDIR_FUNC 251 int tinydir_open_subdir_n(tinydir_dir *dir, size_t i); 252 253 _TINYDIR_FUNC 254 int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path); 255 _TINYDIR_FUNC 256 void _tinydir_get_ext(tinydir_file *file); 257 _TINYDIR_FUNC 258 int _tinydir_file_cmp(const void *a, const void *b); 259 #ifndef _MSC_VER 260 #ifndef _TINYDIR_USE_READDIR 261 _TINYDIR_FUNC 262 size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp); 263 #endif 264 #endif 265 266 267 /* definitions*/ 268 269 _TINYDIR_FUNC 270 int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path) 271 { 272 #ifndef _MSC_VER 273 #ifndef _TINYDIR_USE_READDIR 274 int error; 275 int size; /* using int size */ 276 #endif 277 #else 278 _tinydir_char_t path_buf[_TINYDIR_PATH_MAX]; 279 #endif 280 _tinydir_char_t *pathp; 281 282 if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0) 283 { 284 errno = EINVAL; 285 return -1; 286 } 287 if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) 288 { 289 errno = ENAMETOOLONG; 290 return -1; 291 } 292 293 /* initialise dir */ 294 dir->_files = NULL; 295 #ifdef _MSC_VER 296 dir->_h = INVALID_HANDLE_VALUE; 297 #else 298 dir->_d = NULL; 299 #ifndef _TINYDIR_USE_READDIR 300 dir->_ep = NULL; 301 #endif 302 #endif 303 tinydir_close(dir); 304 305 _tinydir_strcpy(dir->path, path); 306 /* Remove trailing slashes */ 307 pathp = &dir->path[_tinydir_strlen(dir->path) - 1]; 308 while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/'))) 309 { 310 *pathp = TINYDIR_STRING('\0'); 311 pathp++; 312 } 313 #ifdef _MSC_VER 314 _tinydir_strcpy(path_buf, dir->path); 315 _tinydir_strcat(path_buf, TINYDIR_STRING("\\*")); 316 #if (defined WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP) 317 dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0); 318 #else 319 dir->_h = FindFirstFile(path_buf, &dir->_f); 320 #endif 321 if (dir->_h == INVALID_HANDLE_VALUE) 322 { 323 errno = ENOENT; 324 #else 325 dir->_d = _tinydir_opendir(path); 326 if (dir->_d == NULL) 327 { 328 #endif 329 goto bail; 330 } 331 332 /* read first file */ 333 dir->has_next = 1; 334 #ifndef _MSC_VER 335 #ifdef _TINYDIR_USE_READDIR 336 dir->_e = _tinydir_readdir(dir->_d); 337 #else 338 /* allocate dirent buffer for readdir_r */ 339 size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */ 340 if (size == -1) return -1; 341 dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size); 342 if (dir->_ep == NULL) return -1; 343 344 error = readdir_r(dir->_d, dir->_ep, &dir->_e); 345 if (error != 0) return -1; 346 #endif 347 if (dir->_e == NULL) 348 { 349 dir->has_next = 0; 350 } 351 #endif 352 353 return 0; 354 355 bail: 356 tinydir_close(dir); 357 return -1; 358 } 359 360 _TINYDIR_FUNC 361 int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path) 362 { 363 /* Count the number of files first, to pre-allocate the files array */ 364 size_t n_files = 0; 365 if (tinydir_open(dir, path) == -1) 366 { 367 return -1; 368 } 369 while (dir->has_next) 370 { 371 n_files++; 372 if (tinydir_next(dir) == -1) 373 { 374 goto bail; 375 } 376 } 377 tinydir_close(dir); 378 379 if (n_files == 0 || tinydir_open(dir, path) == -1) 380 { 381 return -1; 382 } 383 384 dir->n_files = 0; 385 dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files); 386 if (dir->_files == NULL) 387 { 388 goto bail; 389 } 390 while (dir->has_next) 391 { 392 tinydir_file *p_file; 393 dir->n_files++; 394 395 p_file = &dir->_files[dir->n_files - 1]; 396 if (tinydir_readfile(dir, p_file) == -1) 397 { 398 goto bail; 399 } 400 401 if (tinydir_next(dir) == -1) 402 { 403 goto bail; 404 } 405 406 /* Just in case the number of files has changed between the first and 407 second reads, terminate without writing into unallocated memory */ 408 if (dir->n_files == n_files) 409 { 410 break; 411 } 412 } 413 414 qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp); 415 416 return 0; 417 418 bail: 419 tinydir_close(dir); 420 return -1; 421 } 422 423 _TINYDIR_FUNC 424 void tinydir_close(tinydir_dir *dir) 425 { 426 if (dir == NULL) 427 { 428 return; 429 } 430 431 memset(dir->path, 0, sizeof(dir->path)); 432 dir->has_next = 0; 433 dir->n_files = 0; 434 _TINYDIR_FREE(dir->_files); 435 dir->_files = NULL; 436 #ifdef _MSC_VER 437 if (dir->_h != INVALID_HANDLE_VALUE) 438 { 439 FindClose(dir->_h); 440 } 441 dir->_h = INVALID_HANDLE_VALUE; 442 #else 443 if (dir->_d) 444 { 445 _tinydir_closedir(dir->_d); 446 } 447 dir->_d = NULL; 448 dir->_e = NULL; 449 #ifndef _TINYDIR_USE_READDIR 450 _TINYDIR_FREE(dir->_ep); 451 dir->_ep = NULL; 452 #endif 453 #endif 454 } 455 456 _TINYDIR_FUNC 457 int tinydir_next(tinydir_dir *dir) 458 { 459 if (dir == NULL) 460 { 461 errno = EINVAL; 462 return -1; 463 } 464 if (!dir->has_next) 465 { 466 errno = ENOENT; 467 return -1; 468 } 469 470 #ifdef _MSC_VER 471 if (FindNextFile(dir->_h, &dir->_f) == 0) 472 #else 473 #ifdef _TINYDIR_USE_READDIR 474 dir->_e = _tinydir_readdir(dir->_d); 475 #else 476 if (dir->_ep == NULL) 477 { 478 return -1; 479 } 480 if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0) 481 { 482 return -1; 483 } 484 #endif 485 if (dir->_e == NULL) 486 #endif 487 { 488 dir->has_next = 0; 489 #ifdef _MSC_VER 490 if (GetLastError() != ERROR_SUCCESS && 491 GetLastError() != ERROR_NO_MORE_FILES) 492 { 493 tinydir_close(dir); 494 errno = EIO; 495 return -1; 496 } 497 #endif 498 } 499 500 return 0; 501 } 502 503 _TINYDIR_FUNC 504 int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file) 505 { 506 const _tinydir_char_t *filename; 507 if (dir == NULL || file == NULL) 508 { 509 errno = EINVAL; 510 return -1; 511 } 512 #ifdef _MSC_VER 513 if (dir->_h == INVALID_HANDLE_VALUE) 514 #else 515 if (dir->_e == NULL) 516 #endif 517 { 518 errno = ENOENT; 519 return -1; 520 } 521 filename = 522 #ifdef _MSC_VER 523 dir->_f.cFileName; 524 #else 525 dir->_e->d_name; 526 #endif 527 if (_tinydir_strlen(dir->path) + 528 _tinydir_strlen(filename) + 1 + _TINYDIR_PATH_EXTRA >= 529 _TINYDIR_PATH_MAX) 530 { 531 /* the path for the file will be too long */ 532 errno = ENAMETOOLONG; 533 return -1; 534 } 535 if (_tinydir_strlen(filename) >= _TINYDIR_FILENAME_MAX) 536 { 537 errno = ENAMETOOLONG; 538 return -1; 539 } 540 541 _tinydir_strcpy(file->path, dir->path); 542 if (_tinydir_strcmp(dir->path, TINYDIR_STRING("/")) != 0) 543 _tinydir_strcat(file->path, TINYDIR_STRING("/")); 544 _tinydir_strcpy(file->name, filename); 545 _tinydir_strcat(file->path, filename); 546 #ifndef _MSC_VER 547 #ifdef __MINGW32__ 548 if (_tstat( 549 #elif (defined _BSD_SOURCE) || (defined _DEFAULT_SOURCE) \ 550 || ((defined _XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) \ 551 || ((defined _POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) \ 552 || ((defined __APPLE__) && (defined __MACH__)) \ 553 || (defined BSD) 554 if (lstat( 555 #else 556 if (stat( 557 #endif 558 file->path, &file->_s) == -1) 559 { 560 return -1; 561 } 562 #endif 563 _tinydir_get_ext(file); 564 565 file->is_dir = 566 #ifdef _MSC_VER 567 !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); 568 #else 569 S_ISDIR(file->_s.st_mode); 570 #endif 571 file->is_reg = 572 #ifdef _MSC_VER 573 !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || 574 ( 575 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) && 576 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && 577 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && 578 #ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM 579 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) && 580 #endif 581 #ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA 582 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) && 583 #endif 584 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) && 585 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY)); 586 #else 587 S_ISREG(file->_s.st_mode); 588 #endif 589 590 return 0; 591 } 592 593 _TINYDIR_FUNC 594 int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i) 595 { 596 if (dir == NULL || file == NULL) 597 { 598 errno = EINVAL; 599 return -1; 600 } 601 if (i >= dir->n_files) 602 { 603 errno = ENOENT; 604 return -1; 605 } 606 607 memcpy(file, &dir->_files[i], sizeof(tinydir_file)); 608 _tinydir_get_ext(file); 609 610 return 0; 611 } 612 613 _TINYDIR_FUNC 614 int tinydir_open_subdir_n(tinydir_dir *dir, size_t i) 615 { 616 _tinydir_char_t path[_TINYDIR_PATH_MAX]; 617 if (dir == NULL) 618 { 619 errno = EINVAL; 620 return -1; 621 } 622 if (i >= dir->n_files || !dir->_files[i].is_dir) 623 { 624 errno = ENOENT; 625 return -1; 626 } 627 628 _tinydir_strcpy(path, dir->_files[i].path); 629 tinydir_close(dir); 630 if (tinydir_open_sorted(dir, path) == -1) 631 { 632 return -1; 633 } 634 635 return 0; 636 } 637 638 /* Open a single file given its path */ 639 _TINYDIR_FUNC 640 int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path) 641 { 642 tinydir_dir dir; 643 int result = 0; 644 int found = 0; 645 _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX]; 646 _tinydir_char_t file_name_buf[_TINYDIR_PATH_MAX]; 647 _tinydir_char_t *dir_name; 648 _tinydir_char_t *base_name; 649 #if (defined _MSC_VER || defined __MINGW32__) 650 _tinydir_char_t drive_buf[_TINYDIR_PATH_MAX]; 651 _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX]; 652 #endif 653 654 if (file == NULL || path == NULL || _tinydir_strlen(path) == 0) 655 { 656 errno = EINVAL; 657 return -1; 658 } 659 if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) 660 { 661 errno = ENAMETOOLONG; 662 return -1; 663 } 664 665 /* Get the parent path */ 666 #if (defined _MSC_VER || defined __MINGW32__) 667 #if ((defined _MSC_VER) && (_MSC_VER >= 1400)) 668 errno = _tsplitpath_s( 669 path, 670 drive_buf, _TINYDIR_DRIVE_MAX, 671 dir_name_buf, _TINYDIR_FILENAME_MAX, 672 file_name_buf, _TINYDIR_FILENAME_MAX, 673 ext_buf, _TINYDIR_FILENAME_MAX); 674 #else 675 _tsplitpath( 676 path, 677 drive_buf, 678 dir_name_buf, 679 file_name_buf, 680 ext_buf); 681 #endif 682 683 if (errno) 684 { 685 return -1; 686 } 687 688 /* _splitpath_s not work fine with only filename and widechar support */ 689 #ifdef _UNICODE 690 if (drive_buf[0] == L'\xFEFE') 691 drive_buf[0] = '\0'; 692 if (dir_name_buf[0] == L'\xFEFE') 693 dir_name_buf[0] = '\0'; 694 #endif 695 696 /* Emulate the behavior of dirname by returning "." for dir name if it's 697 empty */ 698 if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0') 699 { 700 _tinydir_strcpy(dir_name_buf, TINYDIR_STRING(".")); 701 } 702 /* Concatenate the drive letter and dir name to form full dir name */ 703 _tinydir_strcat(drive_buf, dir_name_buf); 704 dir_name = drive_buf; 705 /* Concatenate the file name and extension to form base name */ 706 _tinydir_strcat(file_name_buf, ext_buf); 707 base_name = file_name_buf; 708 #else 709 _tinydir_strcpy(dir_name_buf, path); 710 dir_name = dirname(dir_name_buf); 711 _tinydir_strcpy(file_name_buf, path); 712 base_name = basename(file_name_buf); 713 #endif 714 715 /* Special case: if the path is a root dir, open the parent dir as the file */ 716 #if (defined _MSC_VER || defined __MINGW32__) 717 if (_tinydir_strlen(base_name) == 0) 718 #else 719 if ((_tinydir_strcmp(base_name, TINYDIR_STRING("/"))) == 0) 720 #endif 721 { 722 memset(file, 0, sizeof * file); 723 file->is_dir = 1; 724 file->is_reg = 0; 725 _tinydir_strcpy(file->path, dir_name); 726 file->extension = file->path + _tinydir_strlen(file->path); 727 return 0; 728 } 729 730 /* Open the parent directory */ 731 if (tinydir_open(&dir, dir_name) == -1) 732 { 733 return -1; 734 } 735 736 /* Read through the parent directory and look for the file */ 737 while (dir.has_next) 738 { 739 if (tinydir_readfile(&dir, file) == -1) 740 { 741 result = -1; 742 goto bail; 743 } 744 if (_tinydir_strcmp(file->name, base_name) == 0) 745 { 746 /* File found */ 747 found = 1; 748 break; 749 } 750 tinydir_next(&dir); 751 } 752 if (!found) 753 { 754 result = -1; 755 errno = ENOENT; 756 } 757 758 bail: 759 tinydir_close(&dir); 760 return result; 761 } 762 763 _TINYDIR_FUNC 764 void _tinydir_get_ext(tinydir_file *file) 765 { 766 _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.')); 767 if (period == NULL) 768 { 769 file->extension = &(file->name[_tinydir_strlen(file->name)]); 770 } 771 else 772 { 773 file->extension = period + 1; 774 } 775 } 776 777 _TINYDIR_FUNC 778 int _tinydir_file_cmp(const void *a, const void *b) 779 { 780 const tinydir_file *fa = (const tinydir_file *)a; 781 const tinydir_file *fb = (const tinydir_file *)b; 782 if (fa->is_dir != fb->is_dir) 783 { 784 return -(fa->is_dir - fb->is_dir); 785 } 786 return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX); 787 } 788 789 #ifndef _MSC_VER 790 #ifndef _TINYDIR_USE_READDIR 791 /* 792 The following authored by Ben Hutchings <[email protected]> 793 from https://womble.decadent.org.uk/readdir_r-advisory.html 794 */ 795 /* Calculate the required buffer size (in bytes) for directory * 796 * entries read from the given directory handle. Return -1 if this * 797 * this cannot be done. * 798 * * 799 * This code does not trust values of NAME_MAX that are less than * 800 * 255, since some systems (including at least HP-UX) incorrectly * 801 * define it to be a smaller value. */ 802 _TINYDIR_FUNC 803 size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp) 804 { 805 long name_max; 806 size_t name_end; 807 /* parameter may be unused */ 808 (void)dirp; 809 810 #if defined _TINYDIR_USE_FPATHCONF 811 name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX); 812 if (name_max == -1) 813 #if defined(NAME_MAX) 814 name_max = (NAME_MAX > 255) ? NAME_MAX : 255; 815 #else 816 return (size_t)(-1); 817 #endif 818 #elif defined(NAME_MAX) 819 name_max = (NAME_MAX > 255) ? NAME_MAX : 255; 820 #else 821 #error "buffer size for readdir_r cannot be determined" 822 #endif 823 name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1; 824 return (name_end > sizeof(struct _tinydir_dirent) ? 825 name_end : sizeof(struct _tinydir_dirent)); 826 } 827 #endif 828 #endif 829 830 #ifdef __cplusplus 831 } 832 #endif 833 834 # if defined (_MSC_VER) 835 # pragma warning(pop) 836 # endif 837 838 #endif 839