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