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