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