2 * Dirent interface for Microsoft Visual Studio
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
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"
22 * Include windows.h without Windows Sockets 1.1 to prevent conflicts with
23 * Windows Sockets 2.0.
25 #ifndef WIN32_LEAN_AND_MEAN
26 # define WIN32_LEAN_AND_MEAN
36 #include <sys/types.h>
41 /* Indicates that d_type field is available in dirent structure */
42 #define _DIRENT_HAVE_D_TYPE
44 /* Indicates that d_namlen field is available in dirent structure */
45 #define _DIRENT_HAVE_D_NAMLEN
47 /* Entries missing from MSVC 6.0 */
48 #if !defined(FILE_ATTRIBUTE_DEVICE)
49 # define FILE_ATTRIBUTE_DEVICE 0x40
52 /* File type and permission flags for stat(), general mask */
54 # define S_IFMT _S_IFMT
59 # define S_IFDIR _S_IFDIR
62 /* Character device bit */
64 # define S_IFCHR _S_IFCHR
68 #if !defined(S_IFFIFO)
69 # define S_IFFIFO _S_IFFIFO
72 /* Regular file bit */
74 # define S_IFREG _S_IFREG
79 # define S_IREAD _S_IREAD
82 /* Write permission */
83 #if !defined(S_IWRITE)
84 # define S_IWRITE _S_IWRITE
87 /* Execute permission */
89 # define S_IEXEC _S_IEXEC
94 # define S_IFIFO _S_IFIFO
103 #if !defined(S_IFLNK)
108 #if !defined(S_IFSOCK)
112 /* Read user permission */
113 #if !defined(S_IRUSR)
114 # define S_IRUSR S_IREAD
117 /* Write user permission */
118 #if !defined(S_IWUSR)
119 # define S_IWUSR S_IWRITE
122 /* Execute user permission */
123 #if !defined(S_IXUSR)
127 /* Read group permission */
128 #if !defined(S_IRGRP)
132 /* Write group permission */
133 #if !defined(S_IWGRP)
137 /* Execute group permission */
138 #if !defined(S_IXGRP)
142 /* Read others permission */
143 #if !defined(S_IROTH)
147 /* Write others permission */
148 #if !defined(S_IWOTH)
152 /* Execute others permission */
153 #if !defined(S_IXOTH)
157 /* Maximum length of file name */
158 #if !defined(PATH_MAX)
159 # define PATH_MAX MAX_PATH
161 #if !defined(FILENAME_MAX)
162 # define FILENAME_MAX MAX_PATH
164 #if !defined(NAME_MAX)
165 # define NAME_MAX FILENAME_MAX
168 /* File type flags for d_type */
170 #define DT_REG S_IFREG
171 #define DT_DIR S_IFDIR
172 #define DT_FIFO S_IFIFO
173 #define DT_SOCK S_IFSOCK
174 #define DT_CHR S_IFCHR
175 #define DT_BLK S_IFBLK
176 #define DT_LNK S_IFLNK
178 /* Macros for converting between st_mode and d_type */
179 #define IFTODT(mode) ((mode) & S_IFMT)
180 #define DTTOIF(type) (type)
183 * File type macros. Note that block devices, sockets and links cannot be
184 * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
185 * only defined for compatibility. These macros should always return false
188 #if !defined(S_ISFIFO)
189 # define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
191 #if !defined(S_ISDIR)
192 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
194 #if !defined(S_ISREG)
195 # define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
197 #if !defined(S_ISLNK)
198 # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
200 #if !defined(S_ISSOCK)
201 # define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
203 #if !defined(S_ISCHR)
204 # define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
206 #if !defined(S_ISBLK)
207 # define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
210 /* Return the exact length of the file name without zero terminator */
211 #define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
213 /* Return the maximum size of a file name */
214 #define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1)
222 /* Wide-character version */
227 /* File position within stream */
231 unsigned short d_reclen;
233 /* Length of name without \0 */
240 wchar_t d_name[PATH_MAX + 1];
242 typedef struct _wdirent _wdirent;
245 /* Current directory entry */
248 /* Private file data */
249 WIN32_FIND_DATAW data;
251 /* True if data is valid */
254 /* Win32 search handle */
257 /* Initial directory name */
260 typedef struct _WDIR _WDIR;
262 /* Multi-byte character version */
267 /* File position within stream */
271 unsigned short d_reclen;
273 /* Length of name without \0 */
280 char d_name[PATH_MAX + 1];
282 typedef struct dirent dirent;
288 typedef struct DIR DIR;
291 /* Dirent functions */
292 static DIR *opendir(const char *dirname);
293 static _WDIR *_wopendir(const wchar_t *dirname);
295 static struct dirent *readdir(DIR *dirp);
296 static struct _wdirent *_wreaddir(_WDIR *dirp);
298 static int readdir_r(
299 DIR *dirp, struct dirent *entry, struct dirent **result);
300 static int _wreaddir_r(
301 _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result);
303 static int closedir(DIR *dirp);
304 static int _wclosedir(_WDIR *dirp);
306 static void rewinddir(DIR *dirp);
307 static void _wrewinddir(_WDIR *dirp);
309 static int scandir(const char *dirname, struct dirent ***namelist,
310 int (*filter)(const struct dirent *),
311 int (*compare)(const struct dirent **, const struct dirent **));
313 static int alphasort(const struct dirent **a, const struct dirent **b);
315 static int versionsort(const struct dirent **a, const struct dirent **b);
317 static int strverscmp(const char *a, const char *b);
319 /* For compatibility with Symbian */
320 #define wdirent _wdirent
322 #define wopendir _wopendir
323 #define wreaddir _wreaddir
324 #define wclosedir _wclosedir
325 #define wrewinddir _wrewinddir
327 /* Compatibility with older Microsoft compilers and non-Microsoft compilers */
328 #if !defined(_MSC_VER) || _MSC_VER < 1400
329 # define wcstombs_s dirent_wcstombs_s
330 # define mbstowcs_s dirent_mbstowcs_s
333 /* Optimize dirent_set_errno() away on modern Microsoft compilers */
334 #if defined(_MSC_VER) && _MSC_VER >= 1400
335 # define dirent_set_errno _set_errno
339 /* Internal utility functions */
340 static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp);
341 static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp);
343 #if !defined(_MSC_VER) || _MSC_VER < 1400
344 static int dirent_mbstowcs_s(
345 size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords,
346 const char *mbstr, size_t count);
349 #if !defined(_MSC_VER) || _MSC_VER < 1400
350 static int dirent_wcstombs_s(
351 size_t *pReturnValue, char *mbstr, size_t sizeInBytes,
352 const wchar_t *wcstr, size_t count);
355 #if !defined(_MSC_VER) || _MSC_VER < 1400
356 static void dirent_set_errno(int error);
361 * Open directory stream DIRNAME for read and return a pointer to the
362 * internal working area that is used to retrieve individual directory
365 static _WDIR *_wopendir(const wchar_t *dirname) {
368 /* Must have directory name */
369 if(dirname == NULL || dirname[0] == '\0') {
370 dirent_set_errno(ENOENT);
374 /* Allocate new _WDIR structure */
375 _WDIR *dirp = (_WDIR *) malloc(sizeof(struct _WDIR));
381 /* Reset _WDIR structure */
382 dirp->handle = INVALID_HANDLE_VALUE;
387 * Compute the length of full path plus zero terminator
389 * Note that on WinRT there's no way to convert relative paths
390 * into absolute paths, so just assume it is an absolute path.
392 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
394 DWORD n = GetFullPathNameW(dirname, 0, NULL, NULL);
397 size_t n = wcslen(dirname);
400 /* Allocate room for absolute directory name and search pattern */
401 dirp->patt = (wchar_t *) malloc(sizeof(wchar_t) * n + 16);
403 if(dirp->patt == NULL) {
408 * Convert relative directory name to an absolute one. This
409 * allows rewinddir() to function correctly even when current
410 * working directory is changed between opendir() and rewinddir().
412 * Note that on WinRT there's no way to convert relative paths
413 * into absolute paths, so just assume it is an absolute path.
415 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
417 n = GetFullPathNameW(dirname, n, dirp->patt, NULL);
425 wcsncpy_s(dirp->patt, n + 1, dirname, n);
428 /* Append search pattern \* to the directory name */
435 /* Directory ends in path separator, e.g. c:\temp\ */
441 /* Directory name doesn't end in path separator */
448 /* Open directory stream and retrieve the first entry */
449 if(!dirent_first(dirp)) {
463 * Read next directory entry.
465 * Returns pointer to static directory entry which may be overwritten by
466 * subsequent calls to _wreaddir().
468 static struct _wdirent *_wreaddir(_WDIR *dirp) {
470 * Read directory entry to buffer. We can safely ignore the return
471 * value as entry will be set to NULL in case of error.
473 struct _wdirent *entry;
474 (void) _wreaddir_r(dirp, &dirp->ent, &entry);
476 /* Return pointer to statically allocated directory entry */
481 * Read next directory entry.
483 * Returns zero on success. If end of directory stream is reached, then sets
484 * result to NULL and returns zero.
486 static int _wreaddir_r(
487 _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result) {
488 /* Read next directory entry */
489 WIN32_FIND_DATAW *datap = dirent_next(dirp);
492 /* Return NULL to indicate end of directory */
498 * Copy file name as wide-character string. If the file name is too
499 * long to fit in to the destination buffer, then truncate file name
500 * to PATH_MAX characters and zero-terminate the buffer.
504 while(n < PATH_MAX && datap->cFileName[n] != 0) {
505 entry->d_name[n] = datap->cFileName[n];
509 entry->d_name[n] = 0;
511 /* Length of file name excluding zero terminator */
515 DWORD attr = datap->dwFileAttributes;
517 if((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
518 entry->d_type = DT_CHR;
519 } else if((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
520 entry->d_type = DT_DIR;
522 entry->d_type = DT_REG;
525 /* Reset dummy fields */
528 entry->d_reclen = sizeof(struct _wdirent);
530 /* Set result address */
536 * Close directory stream opened by opendir() function. This invalidates the
537 * DIR structure as well as any directory entry read previously by
540 static int _wclosedir(_WDIR *dirp) {
542 dirent_set_errno(EBADF);
543 return /*failure*/ -1;
546 /* Release search handle */
547 if(dirp->handle != INVALID_HANDLE_VALUE) {
548 FindClose(dirp->handle);
551 /* Release search pattern */
554 /* Release directory structure */
560 * Rewind directory stream such that _wreaddir() returns the very first
563 static void _wrewinddir(_WDIR *dirp) {
568 /* Release existing search handle */
569 if(dirp->handle != INVALID_HANDLE_VALUE) {
570 FindClose(dirp->handle);
573 /* Open new search handle */
577 /* Get first directory entry */
578 static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp) {
583 /* Open directory and retrieve the first entry */
584 dirp->handle = FindFirstFileExW(
585 dirp->patt, FindExInfoStandard, &dirp->data,
586 FindExSearchNameMatch, NULL, 0);
588 if(dirp->handle == INVALID_HANDLE_VALUE) {
592 /* A directory entry is now waiting in memory */
597 /* Failed to open directory: no directory entry in memory */
601 DWORD errorcode = GetLastError();
604 case ERROR_ACCESS_DENIED:
605 /* No read access to directory */
606 dirent_set_errno(EACCES);
609 case ERROR_DIRECTORY:
610 /* Directory name is invalid */
611 dirent_set_errno(ENOTDIR);
614 case ERROR_PATH_NOT_FOUND:
616 /* Cannot find the file */
617 dirent_set_errno(ENOENT);
623 /* Get next directory entry */
624 static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp) {
625 /* Is the next directory entry already in cache? */
627 /* Yes, a valid directory entry found in memory */
632 /* No directory entry in cache */
633 if(dirp->handle == INVALID_HANDLE_VALUE) {
637 /* Read the next directory entry from stream */
638 if(FindNextFileW(dirp->handle, &dirp->data) == FALSE) {
647 FindClose(dirp->handle);
648 dirp->handle = INVALID_HANDLE_VALUE;
652 /* Open directory stream using plain old C-string */
653 static DIR *opendir(const char *dirname) {
654 /* Must have directory name */
655 if(dirname == NULL || dirname[0] == '\0') {
656 dirent_set_errno(ENOENT);
660 /* Allocate memory for DIR structure */
661 struct DIR *dirp = (DIR *) malloc(sizeof(struct DIR));
667 /* Convert directory name to wide-character string */
668 wchar_t wname[PATH_MAX + 1];
670 int error = mbstowcs_s(&n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1);
676 /* Open directory stream using wide-character name */
677 dirp->wdirp = _wopendir(wname);
692 /* Read next directory entry */
693 static struct dirent *readdir(DIR *dirp) {
695 * Read directory entry to buffer. We can safely ignore the return
696 * value as entry will be set to NULL in case of error.
698 struct dirent *entry;
699 (void) readdir_r(dirp, &dirp->ent, &entry);
701 /* Return pointer to statically allocated directory entry */
706 * Read next directory entry into called-allocated buffer.
708 * Returns zero on success. If the end of directory stream is reached, then
709 * sets result to NULL and returns zero.
711 static int readdir_r(
712 DIR *dirp, struct dirent *entry, struct dirent **result) {
713 /* Read next directory entry */
714 WIN32_FIND_DATAW *datap = dirent_next(dirp->wdirp);
717 /* No more directory entries */
722 /* Attempt to convert file name to multi-byte string */
724 int error = wcstombs_s(
725 &n, entry->d_name, PATH_MAX + 1,
726 datap->cFileName, PATH_MAX + 1);
729 * If the file name cannot be represented by a multi-byte string, then
730 * attempt to use old 8+3 file name. This allows the program to
731 * access files although file names may seem unfamiliar to the user.
733 * Be ware that the code below cannot come up with a short file name
734 * unless the file system provides one. At least VirtualBox shared
735 * folders fail to do this.
737 if(error && datap->cAlternateFileName[0] != '\0') {
739 &n, entry->d_name, PATH_MAX + 1,
740 datap->cAlternateFileName, PATH_MAX + 1);
744 /* Length of file name excluding zero terminator */
745 entry->d_namlen = n - 1;
747 /* File attributes */
748 DWORD attr = datap->dwFileAttributes;
750 if((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
751 entry->d_type = DT_CHR;
752 } else if((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
753 entry->d_type = DT_DIR;
755 entry->d_type = DT_REG;
758 /* Reset dummy fields */
761 entry->d_reclen = sizeof(struct dirent);
764 * Cannot convert file name to multi-byte string so construct
765 * an erroneous directory entry and return that. Note that
766 * we cannot return NULL as that would stop the processing
767 * of directory entries completely.
769 entry->d_name[0] = '?';
770 entry->d_name[1] = '\0';
772 entry->d_type = DT_UNKNOWN;
778 /* Return pointer to directory entry */
783 /* Close directory stream */
784 static int closedir(DIR *dirp) {
791 /* Close wide-character directory stream */
792 ok = _wclosedir(dirp->wdirp);
795 /* Release multi-byte character version */
800 /* Invalid directory stream */
801 dirent_set_errno(EBADF);
802 return /*failure*/ -1;
805 /* Rewind directory stream to beginning */
806 static void rewinddir(DIR *dirp) {
811 /* Rewind wide-character string directory stream */
812 _wrewinddir(dirp->wdirp);
815 /* Scan directory for entries */
817 const char *dirname, struct dirent ***namelist,
818 int (*filter)(const struct dirent *),
819 int (*compare)(const struct dirent **, const struct dirent **)) {
822 /* Open directory stream */
823 DIR *dir = opendir(dirname);
826 /* Cannot open directory */
830 /* Read directory entries to memory */
831 struct dirent *tmp = NULL;
832 struct dirent **files = NULL;
834 size_t allocated = 0;
837 /* Allocate room for a temporary directory entry */
839 tmp = (struct dirent *) malloc(sizeof(struct dirent));
846 /* Read directory entry to temporary area */
847 struct dirent *entry;
849 if(readdir_r(dir, tmp, &entry) != /*OK*/0) {
853 /* Stop if we already read the last directory entry */
858 /* Determine whether to include the entry in results */
859 if(filter && !filter(tmp)) {
863 /* Enlarge pointer table to make room for another pointer */
864 if(size >= allocated) {
865 /* Compute number of entries in the new table */
866 size_t num_entries = size * 2 + 16;
868 /* Allocate new pointer table or enlarge existing */
869 void *p = realloc(files, sizeof(void *) * num_entries);
876 files = (dirent **) p;
877 allocated = num_entries;
880 /* Store the temporary entry to ptr table */
887 /* Release allocated file entries */
888 for(size_t i = 0; i < size; i++) {
892 /* Release the pointer table */
896 /* Exit with error code */
897 result = /*error*/ -1;
901 /* Sort directory entries */
902 qsort(files, size, sizeof(void *),
903 (int (*)(const void *, const void *)) compare);
905 /* Pass pointer table to caller */
910 /* Return the number of directory entries read */
914 /* Release temporary directory entry, if we had one */
917 /* Close directory stream */
922 /* Alphabetical sorting */
923 static int alphasort(const struct dirent **a, const struct dirent **b) {
924 return strcoll((*a)->d_name, (*b)->d_name);
928 static int versionsort(const struct dirent **a, const struct dirent **b) {
929 return strverscmp((*a)->d_name, (*b)->d_name);
932 /* Compare strings */
933 static int strverscmp(const char *a, const char *b) {
937 /* Find first difference */
938 while(a[i] == b[i]) {
947 /* Count backwards and find the leftmost digit */
950 while(j > 0 && isdigit(a[j - 1])) {
954 /* Determine mode of comparison */
955 if(a[j] == '0' || b[j] == '0') {
956 /* Find the next non-zero digit */
957 while(a[j] == '0' && a[j] == b[j]) {
961 /* String with more digits is smaller, e.g 002 < 01 */
966 } else if(isdigit(b[j])) {
969 } else if(isdigit(a[j]) && isdigit(b[j])) {
970 /* Numeric comparison */
974 /* Compute number of digits in each string */
975 while(isdigit(a[k1])) {
979 while(isdigit(b[k2])) {
983 /* Number with more digits is bigger, e.g 999 < 1000 */
991 /* Alphabetical comparison */
992 return (int)((unsigned char) a[i]) - ((unsigned char) b[i]);
995 /* Convert multi-byte string to wide character string */
996 #if !defined(_MSC_VER) || _MSC_VER < 1400
997 static int dirent_mbstowcs_s(
998 size_t *pReturnValue, wchar_t *wcstr,
999 size_t sizeInWords, const char *mbstr, size_t count) {
1000 /* Older Visual Studio or non-Microsoft compiler */
1001 size_t n = mbstowcs(wcstr, mbstr, sizeInWords);
1003 if(wcstr && n >= count) {
1007 /* Zero-terminate output buffer */
1008 if(wcstr && sizeInWords) {
1009 if(n >= sizeInWords) {
1010 n = sizeInWords - 1;
1016 /* Length of multi-byte string with zero terminator */
1018 *pReturnValue = n + 1;
1026 /* Convert wide-character string to multi-byte string */
1027 #if !defined(_MSC_VER) || _MSC_VER < 1400
1028 static int dirent_wcstombs_s(
1029 size_t *pReturnValue, char *mbstr,
1030 size_t sizeInBytes, const wchar_t *wcstr, size_t count) {
1031 /* Older Visual Studio or non-Microsoft compiler */
1032 size_t n = wcstombs(mbstr, wcstr, sizeInBytes);
1034 if(mbstr && n >= count) {
1038 /* Zero-terminate output buffer */
1039 if(mbstr && sizeInBytes) {
1040 if(n >= sizeInBytes) {
1041 n = sizeInBytes - 1;
1047 /* Length of resulting multi-bytes string WITH zero-terminator */
1049 *pReturnValue = n + 1;
1057 /* Set errno variable */
1058 #if !defined(_MSC_VER) || _MSC_VER < 1400
1059 static void dirent_set_errno(int error) {
1060 /* Non-Microsoft compiler or older Microsoft compiler */