dirent.h头文件
1 /* 2 * Dirent interface for Microsoft Visual Studio 3 * 4 * Copyright (C) 1998-2019 Toni Ronkko 5 * This file is part of dirent. Dirent may be freely distributed 6 * under the MIT license. For all details and documentation, see 7 * https://github.com/tronkko/dirent 8 */ 9 #ifndef DIRENT_H 10 #define DIRENT_H 11 12 /* Hide warnings about unreferenced local functions */ 13 #if defined(__clang__) 14 # pragma clang diagnostic ignored "-Wunused-function" 15 #elif defined(_MSC_VER) 16 # pragma warning(disable:4505) 17 #elif defined(__GNUC__) 18 # pragma GCC diagnostic ignored "-Wunused-function" 19 #endif 20 21 /* 22 * Include windows.h without Windows Sockets 1.1 to prevent conflicts with 23 * Windows Sockets 2.0. 24 */ 25 #ifndef WIN32_LEAN_AND_MEAN 26 # define WIN32_LEAN_AND_MEAN 27 #endif 28 #include29 30 #include 31 #include 32 #include 33 #include <string.h> 34 #include 35 #include <malloc.h> 36 #include 37 #include 38 #include 39 40 /* Indicates that d_type field is available in dirent structure */ 41 #define _DIRENT_HAVE_D_TYPE 42 43 /* Indicates that d_namlen field is available in dirent structure */ 44 #define _DIRENT_HAVE_D_NAMLEN 45 46 /* Entries missing from MSVC 6.0 */ 47 #if !defined(FILE_ATTRIBUTE_DEVICE) 48 # define FILE_ATTRIBUTE_DEVICE 0x40 49 #endif 50 51 /* File type and permission flags for stat(), general mask */ 52 #if !defined(S_IFMT) 53 # define S_IFMT _S_IFMT 54 #endif 55 56 /* Directory bit */ 57 #if !defined(S_IFDIR) 58 # define S_IFDIR _S_IFDIR 59 #endif 60 61 /* Character device bit */ 62 #if !defined(S_IFCHR) 63 # define S_IFCHR _S_IFCHR 64 #endif 65 66 /* Pipe bit */ 67 #if !defined(S_IFFIFO) 68 # define S_IFFIFO _S_IFFIFO 69 #endif 70 71 /* Regular file bit */ 72 #if !defined(S_IFREG) 73 # define S_IFREG _S_IFREG 74 #endif 75 76 /* Read permission */ 77 #if !defined(S_IREAD) 78 # define S_IREAD _S_IREAD 79 #endif 80 81 /* Write permission */ 82 #if !defined(S_IWRITE) 83 # define S_IWRITE _S_IWRITE 84 #endif 85 86 /* Execute permission */ 87 #if !defined(S_IEXEC) 88 # define S_IEXEC _S_IEXEC 89 #endif 90 91 /* Pipe */ 92 #if !defined(S_IFIFO) 93 # define S_IFIFO _S_IFIFO 94 #endif 95 96 /* Block device */ 97 #if !defined(S_IFBLK) 98 # define S_IFBLK 0 99 #endif 100 101 /* Link */ 102 #if !defined(S_IFLNK) 103 # define S_IFLNK 0 104 #endif 105 106 /* Socket */ 107 #if !defined(S_IFSOCK) 108 # define S_IFSOCK 0 109 #endif 110 111 /* Read user permission */ 112 #if !defined(S_IRUSR) 113 # define S_IRUSR S_IREAD 114 #endif 115 116 /* Write user permission */ 117 #if !defined(S_IWUSR) 118 # define S_IWUSR S_IWRITE 119 #endif 120 121 /* Execute user permission */ 122 #if !defined(S_IXUSR) 123 # define S_IXUSR 0 124 #endif 125 126 /* Read group permission */ 127 #if !defined(S_IRGRP) 128 # define S_IRGRP 0 129 #endif 130 131 /* Write group permission */ 132 #if !defined(S_IWGRP) 133 # define S_IWGRP 0 134 #endif 135 136 /* Execute group permission */ 137 #if !defined(S_IXGRP) 138 # define S_IXGRP 0 139 #endif 140 141 /* Read others permission */ 142 #if !defined(S_IROTH) 143 # define S_IROTH 0 144 #endif 145 146 /* Write others permission */ 147 #if !defined(S_IWOTH) 148 # define S_IWOTH 0 149 #endif 150 151 /* Execute others permission */ 152 #if !defined(S_IXOTH) 153 # define S_IXOTH 0 154 #endif 155 156 /* Maximum length of file name */ 157 #if !defined(PATH_MAX) 158 # define PATH_MAX MAX_PATH 159 #endif 160 #if !defined(FILENAME_MAX) 161 # define FILENAME_MAX MAX_PATH 162 #endif 163 #if !defined(NAME_MAX) 164 # define NAME_MAX FILENAME_MAX 165 #endif 166 167 /* File type flags for d_type */ 168 #define DT_UNKNOWN 0 169 #define DT_REG S_IFREG 170 #define DT_DIR S_IFDIR 171 #define DT_FIFO S_IFIFO 172 #define DT_SOCK S_IFSOCK 173 #define DT_CHR S_IFCHR 174 #define DT_BLK S_IFBLK 175 #define DT_LNK S_IFLNK 176 177 /* Macros for converting between st_mode and d_type */ 178 #define IFTODT(mode) ((mode) & S_IFMT) 179 #define DTTOIF(type) (type) 180 181 /* 182 * File type macros. Note that block devices, sockets and links cannot be 183 * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are 184 * only defined for compatibility. These macros should always return false 185 * on Windows. 186 */ 187 #if !defined(S_ISFIFO) 188 # define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) 189 #endif 190 #if !defined(S_ISDIR) 191 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) 192 #endif 193 #if !defined(S_ISREG) 194 # define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) 195 #endif 196 #if !defined(S_ISLNK) 197 # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) 198 #endif 199 #if !defined(S_ISSOCK) 200 # define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) 201 #endif 202 #if !defined(S_ISCHR) 203 # define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) 204 #endif 205 #if !defined(S_ISBLK) 206 # define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) 207 #endif 208 209 /* Return the exact length of the file name without zero terminator */ 210 #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) 211 212 /* Return the maximum size of a file name */ 213 #define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) 214 215 216 #ifdef __cplusplus 217 extern "C" { 218 #endif 219 220 221 /* Wide-character version */ 222 struct _wdirent { 223 /* Always zero */ 224 long d_ino; 225 226 /* File position within stream */ 227 long d_off; 228 229 /* Structure size */ 230 unsigned short d_reclen; 231 232 /* Length of name without \0 */ 233 size_t d_namlen; 234 235 /* File type */ 236 int d_type; 237 238 /* File name */ 239 wchar_t d_name[PATH_MAX+1]; 240 }; 241 typedef struct _wdirent _wdirent; 242 243 struct _WDIR { 244 /* Current directory entry */ 245 struct _wdirent ent; 246 247 /* Private file data */ 248 WIN32_FIND_DATAW data; 249 250 /* True if data is valid */ 251 int cached; 252 253 /* Win32 search handle */ 254 HANDLE handle; 255 256 /* Initial directory name */ 257 wchar_t *patt; 258 }; 259 typedef struct _WDIR _WDIR; 260 261 /* Multi-byte character version */ 262 struct dirent { 263 /* Always zero */ 264 long d_ino; 265 266 /* File position within stream */ 267 long d_off; 268 269 /* Structure size */ 270 unsigned short d_reclen; 271 272 /* Length of name without \0 */ 273 size_t d_namlen; 274 275 /* File type */ 276 int d_type; 277 278 /* File name */ 279 char d_name[PATH_MAX+1]; 280 }; 281 typedef struct dirent dirent; 282 283 struct DIR { 284 struct dirent ent; 285 struct _WDIR *wdirp; 286 }; 287 typedef struct DIR DIR; 288 289 290 /* Dirent functions */ 291 static DIR *opendir (const char *dirname); 292 static _WDIR *_wopendir (const wchar_t *dirname); 293 294 static struct dirent *readdir (DIR *dirp); 295 static struct _wdirent *_wreaddir (_WDIR *dirp); 296 297 static int readdir_r( 298 DIR *dirp, struct dirent *entry, struct dirent **result); 299 static int _wreaddir_r( 300 _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); 301 302 static int closedir (DIR *dirp); 303 static int _wclosedir (_WDIR *dirp); 304 305 static void rewinddir (DIR* dirp); 306 static void _wrewinddir (_WDIR* dirp); 307 308 static int scandir (const char *dirname, struct dirent ***namelist, 309 int (*filter)(const struct dirent*), 310 int (*compare)(const struct dirent**, const struct dirent**)); 311 312 static int alphasort (const struct dirent **a, const struct dirent **b); 313 314 static int versionsort (const struct dirent **a, const struct dirent **b); 315 316 317 /* For compatibility with Symbian */ 318 #define wdirent _wdirent 319 #define WDIR _WDIR 320 #define wopendir _wopendir 321 #define wreaddir _wreaddir 322 #define wclosedir _wclosedir 323 #define wrewinddir _wrewinddir 324 325 326 /* Internal utility functions */ 327 static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); 328 static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); 329 330 static int dirent_mbstowcs_s( 331 size_t *pReturnValue, 332 wchar_t *wcstr, 333 size_t sizeInWords, 334 const char *mbstr, 335 size_t count); 336 337 static int dirent_wcstombs_s( 338 size_t *pReturnValue, 339 char *mbstr, 340 size_t sizeInBytes, 341 const wchar_t *wcstr, 342 size_t count); 343 344 static void dirent_set_errno (int error); 345 346 347 /* 348 * Open directory stream DIRNAME for read and return a pointer to the 349 * internal working area that is used to retrieve individual directory 350 * entries. 351 */ 352 static _WDIR* 353 _wopendir( 354 const wchar_t *dirname) 355 { 356 _WDIR *dirp; 357 DWORD n; 358 wchar_t *p; 359 360 /* Must have directory name */ 361 if (dirname == NULL || dirname[0] == '\0') { 362 dirent_set_errno (ENOENT); 363 return NULL; 364 } 365 366 /* Allocate new _WDIR structure */ 367 dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); 368 if (!dirp) { 369 return NULL; 370 } 371 372 /* Reset _WDIR structure */ 373 dirp->handle = INVALID_HANDLE_VALUE; 374 dirp->patt = NULL; 375 dirp->cached = 0; 376 377 /* 378 * Compute the length of full path plus zero terminator 379 * 380 * Note that on WinRT there's no way to convert relative paths 381 * into absolute paths, so just assume it is an absolute path. 382 */ 383 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) 384 /* Desktop */ 385 n = GetFullPathNameW (dirname, 0, NULL, NULL); 386 #else 387 /* WinRT */ 388 n = wcslen (dirname); 389 #endif 390 391 /* Allocate room for absolute directory name and search pattern */ 392 dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); 393 if (dirp->patt == NULL) { 394 goto exit_closedir; 395 } 396 397 /* 398 * Convert relative directory name to an absolute one. This 399 * allows rewinddir() to function correctly even when current 400 * working directory is changed between opendir() and rewinddir(). 401 * 402 * Note that on WinRT there's no way to convert relative paths 403 * into absolute paths, so just assume it is an absolute path. 404 */ 405 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) 406 /* Desktop */ 407 n = GetFullPathNameW (dirname, n, dirp->patt, NULL); 408 if (n <= 0) { 409 goto exit_closedir; 410 } 411 #else 412 /* WinRT */ 413 wcsncpy_s (dirp->patt, n+1, dirname, n); 414 #endif 415 416 /* Append search pattern \* to the directory name */ 417 p = dirp->patt + n; 418 switch (p[-1]) { 419 case '\\': 420 case '/': 421 case ':': 422 /* Directory ends in path separator, e.g. c:\temp\ */ 423 /*NOP*/; 424 break; 425 426 default: 427 /* Directory name doesn't end in path separator */ 428 *p++ = '\\'; 429 } 430 *p++ = '*'; 431 *p = '\0'; 432 433 /* Open directory stream and retrieve the first entry */ 434 if (!dirent_first (dirp)) { 435 goto exit_closedir; 436 } 437 438 /* Success */ 439 return dirp; 440 441 /* Failure */ 442 exit_closedir: 443 _wclosedir (dirp); 444 return NULL; 445 } 446 447 /* 448 * Read next directory entry. 449 * 450 * Returns pointer to static directory entry which may be overwritten by 451 * subsequent calls to _wreaddir(). 452 */ 453 static struct _wdirent* 454 _wreaddir( 455 _WDIR *dirp) 456 { 457 struct _wdirent *entry; 458 459 /* 460 * Read directory entry to buffer. We can safely ignore the return value 461 * as entry will be set to NULL in case of error. 462 */ 463 (void) _wreaddir_r (dirp, &dirp->ent, &entry); 464 465 /* Return pointer to statically allocated directory entry */ 466 return entry; 467 } 468 469 /* 470 * Read next directory entry. 471 * 472 * Returns zero on success. If end of directory stream is reached, then sets 473 * result to NULL and returns zero. 474 */ 475 static int 476 _wreaddir_r( 477 _WDIR *dirp, 478 struct _wdirent *entry, 479 struct _wdirent **result) 480 { 481 WIN32_FIND_DATAW *datap; 482 483 /* Read next directory entry */ 484 datap = dirent_next (dirp); 485 if (datap) { 486 size_t n; 487 DWORD attr; 488 489 /* 490 * Copy file name as wide-character string. If the file name is too 491 * long to fit in to the destination buffer, then truncate file name 492 * to PATH_MAX characters and zero-terminate the buffer. 493 */ 494 n = 0; 495 while (n < PATH_MAX && datap->cFileName[n] != 0) { 496 entry->d_name[n] = datap->cFileName[n]; 497 n++; 498 } 499 entry->d_name[n] = 0; 500 501 /* Length of file name excluding zero terminator */ 502 entry->d_namlen = n; 503 504 /* File type */ 505 attr = datap->dwFileAttributes; 506 if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { 507 entry->d_type = DT_CHR; 508 } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { 509 entry->d_type = DT_DIR; 510 } else { 511 entry->d_type = DT_REG; 512 } 513 514 /* Reset dummy fields */ 515 entry->d_ino = 0; 516 entry->d_off = 0; 517 entry->d_reclen = sizeof (struct _wdirent); 518 519 /* Set result address */ 520 *result = entry; 521 522 } else { 523 524 /* Return NULL to indicate end of directory */ 525 *result = NULL; 526 527 } 528 529 return /*OK*/0; 530 } 531 532 /* 533 * Close directory stream opened by opendir() function. This invalidates the 534 * DIR structure as well as any directory entry read previously by 535 * _wreaddir(). 536 */ 537 static int 538 _wclosedir( 539 _WDIR *dirp) 540 { 541 int ok; 542 if (dirp) { 543 544 /* Release search handle */ 545 if (dirp->handle != INVALID_HANDLE_VALUE) { 546 FindClose (dirp->handle); 547 } 548 549 /* Release search pattern */ 550 free (dirp->patt); 551 552 /* Release directory structure */ 553 free (dirp); 554 ok = /*success*/0; 555 556 } else { 557 558 /* Invalid directory stream */ 559 dirent_set_errno (EBADF); 560 ok = /*failure*/-1; 561 562 } 563 return ok; 564 } 565 566 /* 567 * Rewind directory stream such that _wreaddir() returns the very first 568 * file name again. 569 */ 570 static void 571 _wrewinddir( 572 _WDIR* dirp) 573 { 574 if (dirp) { 575 /* Release existing search handle */ 576 if (dirp->handle != INVALID_HANDLE_VALUE) { 577 FindClose (dirp->handle); 578 } 579 580 /* Open new search handle */ 581 dirent_first (dirp); 582 } 583 } 584 585 /* Get first directory entry (internal) */ 586 static WIN32_FIND_DATAW* 587 dirent_first( 588 _WDIR *dirp) 589 { 590 WIN32_FIND_DATAW *datap; 591 DWORD error; 592 593 /* Open directory and retrieve the first entry */ 594 dirp->handle = FindFirstFileExW( 595 dirp->patt, FindExInfoStandard, &dirp->data, 596 FindExSearchNameMatch, NULL, 0); 597 if (dirp->handle != INVALID_HANDLE_VALUE) { 598 599 /* a directory entry is now waiting in memory */ 600 datap = &dirp->data; 601 dirp->cached = 1; 602 603 } else { 604 605 /* Failed to open directory: no directory entry in memory */ 606 dirp->cached = 0; 607 datap = NULL; 608 609 /* Set error code */ 610 error = GetLastError (); 611 switch (error) { 612 case ERROR_ACCESS_DENIED: 613 /* No read access to directory */ 614 dirent_set_errno (EACCES); 615 break; 616 617 case ERROR_DIRECTORY: 618 /* Directory name is invalid */ 619 dirent_set_errno (ENOTDIR); 620 break; 621 622 case ERROR_PATH_NOT_FOUND: 623 default: 624 /* Cannot find the file */ 625 dirent_set_errno (ENOENT); 626 } 627 628 } 629 return datap; 630 } 631 632 /* 633 * Get next directory entry (internal). 634 * 635 * Returns 636 */ 637 static WIN32_FIND_DATAW* 638 dirent_next( 639 _WDIR *dirp) 640 { 641 WIN32_FIND_DATAW *p; 642 643 /* Get next directory entry */ 644 if (dirp->cached != 0) { 645 646 /* A valid directory entry already in memory */ 647 p = &dirp->data; 648 dirp->cached = 0; 649 650 } else if (dirp->handle != INVALID_HANDLE_VALUE) { 651 652 /* Get the next directory entry from stream */ 653 if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { 654 /* Got a file */ 655 p = &dirp->data; 656 } else { 657 /* The very last entry has been processed or an error occurred */ 658 FindClose (dirp->handle); 659 dirp->handle = INVALID_HANDLE_VALUE; 660 p = NULL; 661 } 662 663 } else { 664 665 /* End of directory stream reached */ 666 p = NULL; 667 668 } 669 670 return p; 671 } 672 673 /* 674 * Open directory stream using plain old C-string. 675 */ 676 static DIR* 677 opendir( 678 const char *dirname) 679 { 680 struct DIR *dirp; 681 682 /* Must have directory name */ 683 if (dirname == NULL || dirname[0] == '\0') { 684 dirent_set_errno (ENOENT); 685 return NULL; 686 } 687 688 /* Allocate memory for DIR structure */ 689 dirp = (DIR*) malloc (sizeof (struct DIR)); 690 if (!dirp) { 691 return NULL; 692 } 693 { 694 int error; 695 wchar_t wname[PATH_MAX + 1]; 696 size_t n; 697 698 /* Convert directory name to wide-character string */ 699 error = dirent_mbstowcs_s( 700 &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); 701 if (error) { 702 /* 703 * Cannot convert file name to wide-character string. This 704 * occurs if the string contains invalid multi-byte sequences or 705 * the output buffer is too small to contain the resulting 706 * string. 707 */ 708 goto exit_free; 709 } 710 711 712 /* Open directory stream using wide-character name */ 713 dirp->wdirp = _wopendir (wname); 714 if (!dirp->wdirp) { 715 goto exit_free; 716 } 717 718 } 719 720 /* Success */ 721 return dirp; 722 723 /* Failure */ 724 exit_free: 725 free (dirp); 726 return NULL; 727 } 728 729 /* 730 * Read next directory entry. 731 */ 732 static struct dirent* 733 readdir( 734 DIR *dirp) 735 { 736 struct dirent *entry; 737 738 /* 739 * Read directory entry to buffer. We can safely ignore the return value 740 * as entry will be set to NULL in case of error. 741 */ 742 (void) readdir_r (dirp, &dirp->ent, &entry); 743 744 /* Return pointer to statically allocated directory entry */ 745 return entry; 746 } 747 748 /* 749 * Read next directory entry into called-allocated buffer. 750 * 751 * Returns zero on success. If the end of directory stream is reached, then 752 * sets result to NULL and returns zero. 753 */ 754 static int 755 readdir_r( 756 DIR *dirp, 757 struct dirent *entry, 758 struct dirent **result) 759 { 760 WIN32_FIND_DATAW *datap; 761 762 /* Read next directory entry */ 763 datap = dirent_next (dirp->wdirp); 764 if (datap) { 765 size_t n; 766 int error; 767 768 /* Attempt to convert file name to multi-byte string */ 769 error = dirent_wcstombs_s( 770 &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); 771 772 /* 773 * If the file name cannot be represented by a multi-byte string, 774 * then attempt to use old 8+3 file name. This allows traditional 775 * Unix-code to access some file names despite of unicode 776 * characters, although file names may seem unfamiliar to the user. 777 * 778 * Be ware that the code below cannot come up with a short file 779 * name unless the file system provides one. At least 780 * VirtualBox shared folders fail to do this. 781 */ 782 if (error && datap->cAlternateFileName[0] != '\0') { 783 error = dirent_wcstombs_s( 784 &n, entry->d_name, PATH_MAX + 1, 785 datap->cAlternateFileName, PATH_MAX + 1); 786 } 787 788 if (!error) { 789 DWORD attr; 790 791 /* Length of file name excluding zero terminator */ 792 entry->d_namlen = n - 1; 793 794 /* File attributes */ 795 attr = datap->dwFileAttributes; 796 if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { 797 entry->d_type = DT_CHR; 798 } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { 799 entry->d_type = DT_DIR; 800 } else { 801 entry->d_type = DT_REG; 802 } 803 804 /* Reset dummy fields */ 805 entry->d_ino = 0; 806 entry->d_off = 0; 807 entry->d_reclen = sizeof (struct dirent); 808 809 } else { 810 811 /* 812 * Cannot convert file name to multi-byte string so construct 813 * an erroneous directory entry and return that. Note that 814 * we cannot return NULL as that would stop the processing 815 * of directory entries completely. 816 */ 817 entry->d_name[0] = '?'; 818 entry->d_name[1] = '\0'; 819 entry->d_namlen = 1; 820 entry->d_type = DT_UNKNOWN; 821 entry->d_ino = 0; 822 entry->d_off = -1; 823 entry->d_reclen = 0; 824 825 } 826 827 /* Return pointer to directory entry */ 828 *result = entry; 829 830 } else { 831 832 /* No more directory entries */ 833 *result = NULL; 834 835 } 836 837 return /*OK*/0; 838 } 839 840 /* 841 * Close directory stream. 842 */ 843 static int 844 closedir( 845 DIR *dirp) 846 { 847 int ok; 848 if (dirp) { 849 850 /* Close wide-character directory stream */ 851 ok = _wclosedir (dirp->wdirp); 852 dirp->wdirp = NULL; 853 854 /* Release multi-byte character version */ 855 free (dirp); 856 857 } else { 858 859 /* Invalid directory stream */ 860 dirent_set_errno (EBADF); 861 ok = /*failure*/-1; 862 863 } 864 return ok; 865 } 866 867 /* 868 * Rewind directory stream to beginning. 869 */ 870 static void 871 rewinddir( 872 DIR* dirp) 873 { 874 /* Rewind wide-character string directory stream */ 875 _wrewinddir (dirp->wdirp); 876 } 877 878 /* 879 * Scan directory for entries. 880 */ 881 static int 882 scandir( 883 const char *dirname, 884 struct dirent ***namelist, 885 int (*filter)(const struct dirent*), 886 int (*compare)(const struct dirent**, const struct dirent**)) 887 { 888 struct dirent **files = NULL; 889 size_t size = 0; 890 size_t allocated = 0; 891 const size_t init_size = 1; 892 DIR *dir = NULL; 893 struct dirent *entry; 894 struct dirent *tmp = NULL; 895 size_t i; 896 int result = 0; 897 898 /* Open directory stream */ 899 dir = opendir (dirname); 900 if (dir) { 901 902 /* Read directory entries to memory */ 903 while (1) { 904 905 /* Enlarge pointer table to make room for another pointer */ 906 if (size >= allocated) { 907 void *p; 908 size_t num_entries; 909 910 /* Compute number of entries in the enlarged pointer table */ 911 if (size < init_size) { 912 /* Allocate initial pointer table */ 913 num_entries = init_size; 914 } else { 915 /* Double the size */ 916 num_entries = size * 2; 917 } 918 919 /* Allocate first pointer table or enlarge existing table */ 920 p = realloc (files, sizeof (void*) * num_entries); 921 if (p != NULL) { 922 /* Got the memory */ 923 files = (dirent**) p; 924 allocated = num_entries; 925 } else { 926 /* Out of memory */ 927 result = -1; 928 break; 929 } 930 931 } 932 933 /* Allocate room for temporary directory entry */ 934 if (tmp == NULL) { 935 tmp = (struct dirent*) malloc (sizeof (struct dirent)); 936 if (tmp == NULL) { 937 /* Cannot allocate temporary directory entry */ 938 result = -1; 939 break; 940 } 941 } 942 943 /* Read directory entry to temporary area */ 944 if (readdir_r (dir, tmp, &entry) == /*OK*/0) { 945 946 /* Did we get an entry? */ 947 if (entry != NULL) { 948 int pass; 949 950 /* Determine whether to include the entry in result */ 951 if (filter) { 952 /* Let the filter function decide */ 953 pass = filter (tmp); 954 } else { 955 /* No filter function, include everything */ 956 pass = 1; 957 } 958 959 if (pass) { 960 /* Store the temporary entry to pointer table */ 961 files[size++] = tmp; 962 tmp = NULL; 963 964 /* Keep up with the number of files */ 965 result++; 966 } 967 968 } else { 969 970 /* 971 * End of directory stream reached => sort entries and 972 * exit. 973 */ 974 qsort (files, size, sizeof (void*), 975 (int (*) (const void*, const void*)) compare); 976 break; 977 978 } 979 980 } else { 981 /* Error reading directory entry */ 982 result = /*Error*/ -1; 983 break; 984 } 985 986 } 987 988 } else { 989 /* Cannot open directory */ 990 result = /*Error*/ -1; 991 } 992 993 /* Release temporary directory entry */ 994 free (tmp); 995 996 /* Release allocated memory on error */ 997 if (result < 0) { 998 for (i = 0; i < size; i++) { 999 free (files[i]); 1000 } 1001 free (files); 1002 files = NULL; 1003 } 1004 1005 /* Close directory stream */ 1006 if (dir) { 1007 closedir (dir); 1008 } 1009 1010 /* Pass pointer table to caller */ 1011 if (namelist) { 1012 *namelist = files; 1013 } 1014 return result; 1015 } 1016 1017 /* Alphabetical sorting */ 1018 static int 1019 alphasort( 1020 const struct dirent **a, const struct dirent **b) 1021 { 1022 return strcoll ((*a)->d_name, (*b)->d_name); 1023 } 1024 1025 /* Sort versions */ 1026 static int 1027 versionsort( 1028 const struct dirent **a, const struct dirent **b) 1029 { 1030 /* FIXME: implement strverscmp and use that */ 1031 return alphasort (a, b); 1032 } 1033 1034 /* Convert multi-byte string to wide character string */ 1035 static int 1036 dirent_mbstowcs_s( 1037 size_t *pReturnValue, 1038 wchar_t *wcstr, 1039 size_t sizeInWords, 1040 const char *mbstr, 1041 size_t count) 1042 { 1043 int error; 1044 1045 #if defined(_MSC_VER) && _MSC_VER >= 1400 1046 1047 /* Microsoft Visual Studio 2005 or later */ 1048 error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); 1049 1050 #else 1051 1052 /* Older Visual Studio or non-Microsoft compiler */ 1053 size_t n; 1054 1055 /* Convert to wide-character string (or count characters) */ 1056 n = mbstowcs (wcstr, mbstr, sizeInWords); 1057 if (!wcstr || n < count) { 1058 1059 /* Zero-terminate output buffer */ 1060 if (wcstr && sizeInWords) { 1061 if (n >= sizeInWords) { 1062 n = sizeInWords - 1; 1063 } 1064 wcstr[n] = 0; 1065 } 1066 1067 /* Length of resulting multi-byte string WITH zero terminator */ 1068 if (pReturnValue) { 1069 *pReturnValue = n + 1; 1070 } 1071 1072 /* Success */ 1073 error = 0; 1074 1075 } else { 1076 1077 /* Could not convert string */ 1078 error = 1; 1079 1080 } 1081 1082 #endif 1083 return error; 1084 } 1085 1086 /* Convert wide-character string to multi-byte string */ 1087 static int 1088 dirent_wcstombs_s( 1089 size_t *pReturnValue, 1090 char *mbstr, 1091 size_t sizeInBytes, /* max size of mbstr */ 1092 const wchar_t *wcstr, 1093 size_t count) 1094 { 1095 int error; 1096 1097 #if defined(_MSC_VER) && _MSC_VER >= 1400 1098 1099 /* Microsoft Visual Studio 2005 or later */ 1100 error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); 1101 1102 #else 1103 1104 /* Older Visual Studio or non-Microsoft compiler */ 1105 size_t n; 1106 1107 /* Convert to multi-byte string (or count the number of bytes needed) */ 1108 n = wcstombs (mbstr, wcstr, sizeInBytes); 1109 if (!mbstr || n < count) { 1110 1111 /* Zero-terminate output buffer */ 1112 if (mbstr && sizeInBytes) { 1113 if (n >= sizeInBytes) { 1114 n = sizeInBytes - 1; 1115 } 1116 mbstr[n] = '\0'; 1117 } 1118 1119 /* Length of resulting multi-bytes string WITH zero-terminator */ 1120 if (pReturnValue) { 1121 *pReturnValue = n + 1; 1122 } 1123 1124 /* Success */ 1125 error = 0; 1126 1127 } else { 1128 1129 /* Cannot convert string */ 1130 error = 1; 1131 1132 } 1133 1134 #endif 1135 return error; 1136 } 1137 1138 /* Set errno variable */ 1139 static void 1140 dirent_set_errno( 1141 int error) 1142 { 1143 #if defined(_MSC_VER) && _MSC_VER >= 1400 1144 1145 /* Microsoft Visual Studio 2005 and later */ 1146 _set_errno (error); 1147 1148 #else 1149 1150 /* Non-Microsoft compiler or older Microsoft compiler */ 1151 errno = error; 1152 1153 #endif 1154 } 1155 1156 1157 #ifdef __cplusplus 1158 } 1159 #endif 1160 #endif /*DIRENT_H*/