3 * Dirent interface for Microsoft Visual Studio
6 * Copyright (C) 2006-2012 Toni Ronkko
7 * This file is part of dirent. Dirent may be freely distributed
8 * under the MIT license. For all details and documentation, see
9 * https://github.com/tronkko/dirent
15 * Include windows.h without Windows Sockets 1.1 to prevent conflicts with
16 * Windows Sockets 2.0.
18 #ifndef WIN32_LEAN_AND_MEAN
19 # define WIN32_LEAN_AND_MEAN
29 #include <sys/types.h>
33 /* Indicates that d_type field is available in dirent structure */
34 #define _DIRENT_HAVE_D_TYPE
36 /* Indicates that d_namlen field is available in dirent structure */
37 #define _DIRENT_HAVE_D_NAMLEN
39 /* Entries missing from MSVC 6.0 */
40 #if !defined(FILE_ATTRIBUTE_DEVICE)
41 # define FILE_ATTRIBUTE_DEVICE 0x40
44 /* File type and permission flags for stat(), general mask */
46 # define S_IFMT _S_IFMT
51 # define S_IFDIR _S_IFDIR
54 /* Character device bit */
56 # define S_IFCHR _S_IFCHR
60 #if !defined(S_IFFIFO)
61 # define S_IFFIFO _S_IFFIFO
64 /* Regular file bit */
66 # define S_IFREG _S_IFREG
71 # define S_IREAD _S_IREAD
74 /* Write permission */
75 #if !defined(S_IWRITE)
76 # define S_IWRITE _S_IWRITE
79 /* Execute permission */
81 # define S_IEXEC _S_IEXEC
86 # define S_IFIFO _S_IFIFO
100 #if !defined(S_IFSOCK)
104 /* Read user permission */
105 #if !defined(S_IRUSR)
106 # define S_IRUSR S_IREAD
109 /* Write user permission */
110 #if !defined(S_IWUSR)
111 # define S_IWUSR S_IWRITE
114 /* Execute user permission */
115 #if !defined(S_IXUSR)
119 /* Read group permission */
120 #if !defined(S_IRGRP)
124 /* Write group permission */
125 #if !defined(S_IWGRP)
129 /* Execute group permission */
130 #if !defined(S_IXGRP)
134 /* Read others permission */
135 #if !defined(S_IROTH)
139 /* Write others permission */
140 #if !defined(S_IWOTH)
144 /* Execute others permission */
145 #if !defined(S_IXOTH)
149 /* Maximum length of file name */
150 #if !defined(PATH_MAX)
151 # define PATH_MAX MAX_PATH
153 #if !defined(FILENAME_MAX)
154 # define FILENAME_MAX MAX_PATH
156 #if !defined(NAME_MAX)
157 # define NAME_MAX FILENAME_MAX
160 /* File type flags for d_type */
162 #define DT_REG S_IFREG
163 #define DT_DIR S_IFDIR
164 #define DT_FIFO S_IFIFO
165 #define DT_SOCK S_IFSOCK
166 #define DT_CHR S_IFCHR
167 #define DT_BLK S_IFBLK
168 #define DT_LNK S_IFLNK
170 /* Macros for converting between st_mode and d_type */
171 #define IFTODT(mode) ((mode) & S_IFMT)
172 #define DTTOIF(type) (type)
175 * File type macros. Note that block devices, sockets and links cannot be
176 * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
177 * only defined for compatibility. These macros should always return false
180 #if !defined(S_ISFIFO)
181 # define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
183 #if !defined(S_ISDIR)
184 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
186 #if !defined(S_ISREG)
187 # define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
189 #if !defined(S_ISLNK)
190 # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
192 #if !defined(S_ISSOCK)
193 # define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
195 #if !defined(S_ISCHR)
196 # define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
198 #if !defined(S_ISBLK)
199 # define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
202 /* Return the exact length of d_namlen without zero terminator */
203 #define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
205 /* Return number of bytes needed to store d_namlen */
206 #define _D_ALLOC_NAMLEN(p) (PATH_MAX)
214 /* Wide-character version */
220 unsigned short d_reclen;
222 /* Length of name without \0 */
229 wchar_t d_name[PATH_MAX];
231 typedef struct _wdirent _wdirent;
234 /* Current directory entry */
237 /* Private file data */
238 WIN32_FIND_DATAW data;
240 /* True if data is valid */
243 /* Win32 search handle */
246 /* Initial directory name */
249 typedef struct _WDIR _WDIR;
251 static _WDIR *_wopendir (const wchar_t *dirname);
252 static struct _wdirent *_wreaddir (_WDIR *dirp);
253 static int _wclosedir (_WDIR *dirp);
254 static void _wrewinddir (_WDIR* dirp);
257 /* For compatibility with Symbian */
258 #define wdirent _wdirent
260 #define wopendir _wopendir
261 #define wreaddir _wreaddir
262 #define wclosedir _wclosedir
263 #define wrewinddir _wrewinddir
266 /* Multi-byte character versions */
272 unsigned short d_reclen;
274 /* Length of name without \0 */
281 char d_name[PATH_MAX];
283 typedef struct dirent dirent;
289 typedef struct DIR DIR;
291 static DIR *opendir (const char *dirname);
292 static struct dirent *readdir (DIR *dirp);
293 static int closedir (DIR *dirp);
294 static void rewinddir (DIR* dirp);
297 /* Internal utility functions */
298 static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp);
299 static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp);
301 static int dirent_mbstowcs_s(
302 size_t *pReturnValue,
308 static int dirent_wcstombs_s(
309 size_t *pReturnValue,
312 const wchar_t *wcstr,
315 static void dirent_set_errno (int error);
318 * Open directory stream DIRNAME for read and return a pointer to the
319 * internal working area that is used to retrieve individual directory
324 const wchar_t *dirname)
329 /* Must have directory name */
330 if (dirname == NULL || dirname[0] == '\0') {
331 dirent_set_errno (ENOENT);
335 /* Allocate new _WDIR structure */
336 dirp = (_WDIR*) malloc (sizeof (struct _WDIR));
340 /* Reset _WDIR structure */
341 dirp->handle = INVALID_HANDLE_VALUE;
345 /* Compute the length of full path plus zero terminator
347 * Note that on WinRT there's no way to convert relative paths
348 * into absolute paths, so just assume its an absolute path.
350 # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
353 n = GetFullPathNameW (dirname, 0, NULL, NULL);
356 /* Allocate room for absolute directory name and search pattern */
357 dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);
361 * Convert relative directory name to an absolute one. This
362 * allows rewinddir() to function correctly even when current
363 * working directory is changed between opendir() and rewinddir().
365 * Note that on WinRT there's no way to convert relative paths
366 * into absolute paths, so just assume its an absolute path.
368 # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
369 wcsncpy_s(dirp->patt, n+1, dirname, n);
371 n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
376 /* Append search pattern \* to the directory name */
378 if (dirp->patt < p) {
383 /* Directory ends in path separator, e.g. c:\temp\ */
388 /* Directory name doesn't end in path separator */
395 /* Open directory stream and retrieve the first entry */
396 if (dirent_first (dirp)) {
397 /* Directory stream opened successfully */
400 /* Cannot retrieve first entry */
402 dirent_set_errno (ENOENT);
406 /* Cannot retrieve full path name */
407 dirent_set_errno (ENOENT);
412 /* Cannot allocate memory for search pattern */
417 /* Cannot allocate _WDIR structure */
421 /* Clean up in case of error */
431 * Read next directory entry. The directory entry is returned in dirent
432 * structure in the d_name field. Individual directory entries returned by
433 * this function include regular files, sub-directories, pseudo-directories
434 * "." and ".." as well as volume labels, hidden files and system files.
436 static struct _wdirent*
440 WIN32_FIND_DATAW *datap;
441 struct _wdirent *entp;
443 /* Read next directory entry */
444 datap = dirent_next (dirp);
449 /* Pointer to directory entry to return */
453 * Copy file name as wide-character string. If the file name is too
454 * long to fit in to the destination buffer, then truncate file name
455 * to PATH_MAX characters and zero-terminate the buffer.
458 while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) {
459 entp->d_name[n] = datap->cFileName[n];
462 dirp->ent.d_name[n] = 0;
464 /* Length of file name excluding zero terminator */
468 attr = datap->dwFileAttributes;
469 if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
470 entp->d_type = DT_CHR;
471 } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
472 entp->d_type = DT_DIR;
474 entp->d_type = DT_REG;
477 /* Reset dummy fields */
479 entp->d_reclen = sizeof (struct _wdirent);
483 /* Last directory entry read */
492 * Close directory stream opened by opendir() function. This invalidates the
493 * DIR structure as well as any directory entry read previously by
503 /* Release search handle */
504 if (dirp->handle != INVALID_HANDLE_VALUE) {
505 FindClose (dirp->handle);
506 dirp->handle = INVALID_HANDLE_VALUE;
509 /* Release search pattern */
515 /* Release directory structure */
520 /* Invalid directory stream */
521 dirent_set_errno (EBADF);
528 * Rewind directory stream such that _wreaddir() returns the very first
536 /* Release existing search handle */
537 if (dirp->handle != INVALID_HANDLE_VALUE) {
538 FindClose (dirp->handle);
541 /* Open new search handle */
546 /* Get first directory entry (internal) */
547 static WIN32_FIND_DATAW*
551 WIN32_FIND_DATAW *datap;
553 /* Open directory and retrieve the first entry */
554 dirp->handle = FindFirstFileExW(
555 dirp->patt, FindExInfoStandard, &dirp->data,
556 FindExSearchNameMatch, NULL, 0);
557 if (dirp->handle != INVALID_HANDLE_VALUE) {
559 /* a directory entry is now waiting in memory */
565 /* Failed to re-open directory: no directory entry in memory */
573 /* Get next directory entry (internal) */
574 static WIN32_FIND_DATAW*
580 /* Get next directory entry */
581 if (dirp->cached != 0) {
583 /* A valid directory entry already in memory */
587 } else if (dirp->handle != INVALID_HANDLE_VALUE) {
589 /* Get the next directory entry from stream */
590 if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) {
594 /* The very last entry has been processed or an error occured */
595 FindClose (dirp->handle);
596 dirp->handle = INVALID_HANDLE_VALUE;
602 /* End of directory stream reached */
611 * Open directory stream using plain old C-string.
620 /* Must have directory name */
621 if (dirname == NULL || dirname[0] == '\0') {
622 dirent_set_errno (ENOENT);
626 /* Allocate memory for DIR structure */
627 dirp = (DIR*) malloc (sizeof (struct DIR));
629 wchar_t wname[PATH_MAX];
632 /* Convert directory name to wide-character string */
633 error = dirent_mbstowcs_s (&n, wname, PATH_MAX, dirname, PATH_MAX);
636 /* Open directory stream using wide-character name */
637 dirp->wdirp = _wopendir (wname);
639 /* Directory stream opened */
642 /* Failed to open directory stream */
648 * Cannot convert file name to wide-character string. This
649 * occurs if the string contains invalid multi-byte sequences or
650 * the output buffer is too small to contain the resulting
657 /* Cannot allocate DIR structure */
661 /* Clean up in case of error */
671 * Read next directory entry.
673 * When working with text consoles, please note that file names returned by
674 * readdir() are represented in the default ANSI code page while any output to
675 * console is typically formatted on another code page. Thus, non-ASCII
676 * characters in file names will not usually display correctly on console. The
677 * problem can be fixed in two ways: (1) change the character set of console
678 * to 1252 using chcp utility and use Lucida Console font, or (2) use
679 * _cprintf function when writing to console. The _cprinf() will re-encode
680 * ANSI strings to the console code page so many non-ASCII characters will
683 static struct dirent*
687 WIN32_FIND_DATAW *datap;
690 /* Read next directory entry */
691 datap = dirent_next (dirp->wdirp);
696 /* Attempt to convert file name to multi-byte string */
697 error = dirent_wcstombs_s(
698 &n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX);
701 * If the file name cannot be represented by a multi-byte string,
702 * then attempt to use old 8+3 file name. This allows traditional
703 * Unix-code to access some file names despite of unicode
704 * characters, although file names may seem unfamiliar to the user.
706 * Be ware that the code below cannot come up with a short file
707 * name unless the file system provides one. At least
708 * VirtualBox shared folders fail to do this.
710 if (error && datap->cAlternateFileName[0] != '\0') {
711 error = dirent_wcstombs_s(
712 &n, dirp->ent.d_name, PATH_MAX,
713 datap->cAlternateFileName, PATH_MAX);
719 /* Initialize directory entry for return */
722 /* Length of file name excluding zero terminator */
723 entp->d_namlen = n - 1;
725 /* File attributes */
726 attr = datap->dwFileAttributes;
727 if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
728 entp->d_type = DT_CHR;
729 } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
730 entp->d_type = DT_DIR;
732 entp->d_type = DT_REG;
735 /* Reset dummy fields */
737 entp->d_reclen = sizeof (struct dirent);
741 * Cannot convert file name to multi-byte string so construct
742 * an errornous directory entry and return that. Note that
743 * we cannot return NULL as that would stop the processing
744 * of directory entries completely.
747 entp->d_name[0] = '?';
748 entp->d_name[1] = '\0';
750 entp->d_type = DT_UNKNOWN;
756 /* No more directory entries */
764 * Close directory stream.
773 /* Close wide-character directory stream */
774 ok = _wclosedir (dirp->wdirp);
777 /* Release multi-byte character version */
782 /* Invalid directory stream */
783 dirent_set_errno (EBADF);
791 * Rewind directory stream to beginning.
797 /* Rewind wide-character string directory stream */
798 _wrewinddir (dirp->wdirp);
801 /* Convert multi-byte string to wide character string */
804 size_t *pReturnValue,
812 #if defined(_MSC_VER) && _MSC_VER >= 1400
814 /* Microsoft Visual Studio 2005 or later */
815 error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count);
819 /* Older Visual Studio or non-Microsoft compiler */
822 /* Convert to wide-character string (or count characters) */
823 n = mbstowcs (wcstr, mbstr, sizeInWords);
824 if (!wcstr || n < count) {
826 /* Zero-terminate output buffer */
827 if (wcstr && sizeInWords) {
828 if (n >= sizeInWords) {
834 /* Length of resuting multi-byte string WITH zero terminator */
836 *pReturnValue = n + 1;
844 /* Could not convert string */
854 /* Convert wide-character string to multi-byte string */
857 size_t *pReturnValue,
859 size_t sizeInBytes, /* max size of mbstr */
860 const wchar_t *wcstr,
865 #if defined(_MSC_VER) && _MSC_VER >= 1400
867 /* Microsoft Visual Studio 2005 or later */
868 error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count);
872 /* Older Visual Studio or non-Microsoft compiler */
875 /* Convert to multi-byte string (or count the number of bytes needed) */
876 n = wcstombs (mbstr, wcstr, sizeInBytes);
877 if (!mbstr || n < count) {
879 /* Zero-terminate output buffer */
880 if (mbstr && sizeInBytes) {
881 if (n >= sizeInBytes) {
887 /* Length of resulting multi-bytes string WITH zero-terminator */
889 *pReturnValue = n + 1;
897 /* Cannot convert string */
907 /* Set errno variable */
912 #if defined(_MSC_VER) && _MSC_VER >= 1400
914 /* Microsoft Visual Studio 2005 and later */
919 /* Non-Microsoft compiler or older Microsoft compiler */