1/* 2Copyright (c) 2013-2017, tinydir authors: 3- Cong Xu 4- Lautis Sun 5- Baudouin Feildel 6- Andargor <andargor@yahoo.com> 7All rights reserved. 8 9Redistribution and use in source and binary forms, with or without 10modification, are permitted provided that the following conditions are met: 11 121. Redistributions of source code must retain the above copyright notice, this 13 list of conditions and the following disclaimer. 142. 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 18THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25ON 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 27SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28*/ 29#ifndef TINYDIR_H 30#define TINYDIR_H 31 32#ifdef __cplusplus 33extern "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 179typedef 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 196typedef 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 219int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path); 220_TINYDIR_FUNC 221int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path); 222_TINYDIR_FUNC 223void tinydir_close(tinydir_dir *dir); 224 225_TINYDIR_FUNC 226int tinydir_next(tinydir_dir *dir); 227_TINYDIR_FUNC 228int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file); 229_TINYDIR_FUNC 230int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i); 231_TINYDIR_FUNC 232int tinydir_open_subdir_n(tinydir_dir *dir, size_t i); 233 234_TINYDIR_FUNC 235int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path); 236_TINYDIR_FUNC 237void _tinydir_get_ext(tinydir_file *file); 238_TINYDIR_FUNC 239int _tinydir_file_cmp(const void *a, const void *b); 240#ifndef _MSC_VER 241#ifndef _TINYDIR_USE_READDIR 242_TINYDIR_FUNC 243size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp); 244#endif 245#endif 246 247 248/* definitions*/ 249 250_TINYDIR_FUNC 251int 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 336bail: 337 tinydir_close(dir); 338 return -1; 339} 340 341_TINYDIR_FUNC 342int 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 399bail: 400 tinydir_close(dir); 401 return -1; 402} 403 404_TINYDIR_FUNC 405void 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 438int 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 485int 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 579int 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 599int 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 625int 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 728bail: 729 tinydir_close(&dir); 730 return result; 731} 732 733_TINYDIR_FUNC 734void _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 748int _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/* 762The following authored by Ben Hutchings <ben@decadent.org.uk> 763from 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 773size_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 809