2 * dirent.h - dirent API for Microsoft Visual Studio
4 * Copyright (C) 2006-2012 Toni Ronkko
6 * SPDX-License-Identifier: MIT
7 * See LICENSES/README.md for more information.
9 * Version 1.13, Dec 12 2012, Toni Ronkko
10 * Use traditional 8+3 file name if the name cannot be represented in the
11 * default ANSI code page. Now compiles again with MSVC 6.0. Thanks to
12 * Konstantin Khomoutov for testing.
14 * Version 1.12.1, Oct 1 2012, Toni Ronkko
15 * Bug fix: renamed wide-character DIR structure _wDIR to _WDIR (with
16 * capital W) in order to maintain compatibility with MingW.
18 * Version 1.12, Sep 30 2012, Toni Ronkko
19 * Define PATH_MAX and NAME_MAX. Added wide-character variants _wDIR,
20 * _wdirent, _wopendir(), _wreaddir(), _wclosedir() and _wrewinddir().
21 * Thanks to Edgar Buerkle and Jan Nijtmans for ideas and code.
23 * Do not include windows.h. This allows dirent.h to be integrated more
24 * easily into programs using winsock. Thanks to Fernando Azaldegui.
26 * Version 1.11, Mar 15, 2011, Toni Ronkko
27 * Defined FILE_ATTRIBUTE_DEVICE for MSVC 6.0.
29 * Version 1.10, Aug 11, 2010, Toni Ronkko
30 * Added d_type and d_namlen fields to dirent structure. The former is
31 * especially useful for determining whether directory entry represents a
32 * file or a directory. For more information, see
33 * http://www.delorie.com/gnu/docs/glibc/libc_270.html
35 * Improved conformance to the standards. For example, errno is now set
36 * properly on failure and assert() is never used. Thanks to Peter Brockam
39 * Fixed a bug in rewinddir(): when using relative directory names, change
40 * of working directory no longer causes rewinddir() to fail.
42 * Version 1.9, Dec 15, 2009, John Cunningham
43 * Added rewinddir member function
45 * Version 1.8, Jan 18, 2008, Toni Ronkko
46 * Using FindFirstFileA and WIN32_FIND_DATAA to avoid converting string
47 * between multi-byte and unicode representations. This makes the
48 * code simpler and also allows the code to be compiled under MingW. Thanks
49 * to Azriel Fasten for the suggestion.
51 * Mar 4, 2007, Toni Ronkko
52 * Bug fix: due to the strncpy_s() function this file only compiled in
53 * Visual Studio 2005. Using the new string functions only when the
54 * compiler version allows.
56 * Nov 2, 2006, Toni Ronkko
57 * Major update: removed support for Watcom C, MS-DOS and Turbo C to
58 * simplify the file, updated the code to compile cleanly on Visual
59 * Studio 2005 with both unicode and multi-byte character strings,
60 * removed rewinddir() as it had a bug.
62 * Aug 20, 2006, Toni Ronkko
63 * Removed all remarks about MSVC 1.0, which is antiqued now. Simplified
64 * comments by removing SGML tags.
66 * May 14 2002, Toni Ronkko
67 * Embedded the function definitions directly to the header so that no
68 * source modules need to be included in the Visual Studio project. Removed
69 * all the dependencies to other projects so that this header file can be
72 * May 28 1998, Toni Ronkko
78 #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86)
89 #include <sys/types.h>
93 /* Indicates that d_type field is available in dirent structure */
94 #define _DIRENT_HAVE_D_TYPE
96 /* Indicates that d_namlen field is available in dirent structure */
97 #define _DIRENT_HAVE_D_NAMLEN
99 /* Entries missing from MSVC 6.0 */
100 #if !defined(FILE_ATTRIBUTE_DEVICE)
101 # define FILE_ATTRIBUTE_DEVICE 0x40
104 /* File type and permission flags for stat() */
106 # define S_IFMT _S_IFMT /* File type mask */
108 #if !defined(S_IFDIR)
109 # define S_IFDIR _S_IFDIR /* Directory */
111 #if !defined(S_IFCHR)
112 # define S_IFCHR _S_IFCHR /* Character device */
114 #if !defined(S_IFFIFO)
115 # define S_IFFIFO _S_IFFIFO /* Pipe */
117 #if !defined(S_IFREG)
118 # define S_IFREG _S_IFREG /* Regular file */
120 #if !defined(S_IREAD)
121 # define S_IREAD _S_IREAD /* Read permission */
123 #if !defined(S_IWRITE)
124 # define S_IWRITE _S_IWRITE /* Write permission */
126 #if !defined(S_IEXEC)
127 # define S_IEXEC _S_IEXEC /* Execute permission */
129 #if !defined(S_IFIFO)
130 # define S_IFIFO _S_IFIFO /* Pipe */
132 #if !defined(S_IFBLK)
133 # define S_IFBLK 0 /* Block device */
135 #if !defined(S_IFLNK)
136 # define S_IFLNK 0 /* Link */
138 #if !defined(S_IFSOCK)
139 # define S_IFSOCK 0 /* Socket */
142 #if defined(_MSC_VER)
143 # define S_IRUSR S_IREAD /* Read user */
144 # define S_IWUSR S_IWRITE /* Write user */
145 # define S_IXUSR 0 /* Execute user */
146 # define S_IRGRP 0 /* Read group */
147 # define S_IWGRP 0 /* Write group */
148 # define S_IXGRP 0 /* Execute group */
149 # define S_IROTH 0 /* Read others */
150 # define S_IWOTH 0 /* Write others */
151 # define S_IXOTH 0 /* Execute others */
154 /* Maximum length of file name */
155 #if !defined(PATH_MAX)
156 # define PATH_MAX MAX_PATH
158 #if !defined(FILENAME_MAX)
159 # define FILENAME_MAX MAX_PATH
161 #if !defined(NAME_MAX)
162 # define NAME_MAX FILENAME_MAX
165 /* File type flags for d_type */
167 #define DT_REG S_IFREG
168 #define DT_DIR S_IFDIR
169 #define DT_FIFO S_IFIFO
170 #define DT_SOCK S_IFSOCK
171 #define DT_CHR S_IFCHR
172 #define DT_BLK S_IFBLK
174 /* Macros for converting between st_mode and d_type */
175 #define IFTODT(mode) ((mode) & S_IFMT)
176 #define DTTOIF(type) (type)
179 * File type macros. Note that block devices, sockets and links cannot be
180 * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
181 * only defined for compatibility. These macros should always return false
184 #define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
185 #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
186 #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
187 #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
188 #define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
189 #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
190 #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
192 /* Return the exact length of d_namlen without zero terminator */
193 #define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
195 /* Return number of bytes needed to store d_namlen */
196 #define _D_ALLOC_NAMLEN(p) (PATH_MAX + 1)
204 /* Wide-character version */
206 long d_ino
; /* Always zero */
207 unsigned short d_reclen
; /* Structure size */
208 size_t d_namlen
; /* Length of name without \0 */
209 int d_type
; /* File type */
210 wchar_t d_name
[PATH_MAX
+ 1]; /* File name */
212 typedef struct _wdirent _wdirent
;
215 struct _wdirent ent
; /* Current directory entry */
216 WIN32_FIND_DATAW data
; /* Private file data */
217 int cached
; /* True if data is valid */
218 HANDLE handle
; /* Win32 search handle */
219 wchar_t *patt
; /* Initial directory name */
221 typedef struct _WDIR _WDIR
;
223 static _WDIR
*_wopendir (const wchar_t *dirname
);
224 static struct _wdirent
*_wreaddir (_WDIR
*dirp
);
225 static int _wclosedir (_WDIR
*dirp
);
226 static void _wrewinddir (_WDIR
* dirp
);
229 /* For compatibility with Symbian */
230 #define wdirent _wdirent
232 #define wopendir _wopendir
233 #define wreaddir _wreaddir
234 #define wclosedir _wclosedir
235 #define wrewinddir _wrewinddir
238 /* Multi-byte character versions */
240 long d_ino
; /* Always zero */
241 unsigned short d_reclen
; /* Structure size */
242 size_t d_namlen
; /* Length of name without \0 */
243 int d_type
; /* File type */
244 char d_name
[PATH_MAX
+ 1]; /* File name */
246 typedef struct dirent dirent
;
252 typedef struct DIR DIR;
254 static DIR *opendir (const char *dirname
);
255 static struct dirent
*readdir (DIR *dirp
);
256 static int closedir (DIR *dirp
);
257 static void rewinddir (DIR* dirp
);
260 /* Internal utility functions */
261 static WIN32_FIND_DATAW
*dirent_first (_WDIR
*dirp
);
262 static WIN32_FIND_DATAW
*dirent_next (_WDIR
*dirp
);
264 static int dirent_mbstowcs_s(
265 size_t *pReturnValue
,
271 static int dirent_wcstombs_s(
272 size_t *pReturnValue
,
275 const wchar_t *wcstr
,
278 static void dirent_set_errno (int error
);
281 * Open directory stream DIRNAME for read and return a pointer to the
282 * internal working area that is used to retrieve individual directory
287 const wchar_t *dirname
)
292 /* Must have directory name */
293 if (dirname
== NULL
|| dirname
[0] == '\0') {
294 dirent_set_errno (ENOENT
);
298 /* Allocate new _WDIR structure */
299 dirp
= (_WDIR
*) malloc (sizeof (struct _WDIR
));
303 /* Reset _WDIR structure */
304 dirp
->handle
= INVALID_HANDLE_VALUE
;
308 /* Compute the length of full path plus zero terminator */
309 n
= GetFullPathNameW (dirname
, 0, NULL
, NULL
);
311 /* Allocate room for absolute directory name and search pattern */
312 dirp
->patt
= (wchar_t*) malloc (sizeof (wchar_t) * n
+ 16);
316 * Convert relative directory name to an absolute one. This
317 * allows rewinddir() to function correctly even when current
318 * working directory is changed between opendir() and rewinddir().
320 n
= GetFullPathNameW (dirname
, n
, dirp
->patt
, NULL
);
324 /* Append search pattern \* to the directory name */
326 if (dirp
->patt
< p
) {
331 /* Directory ends in path separator, e.g. c:\temp\ */
336 /* Directory name doesn't end in path separator */
343 /* Open directory stream and retrieve the first entry */
344 if (dirent_first (dirp
)) {
345 /* Directory stream opened successfully */
348 /* Cannot retrieve first entry */
350 dirent_set_errno (ENOENT
);
354 /* Cannot retrieve full path name */
355 dirent_set_errno (ENOENT
);
360 /* Cannot allocate memory for search pattern */
365 /* Cannot allocate _WDIR structure */
369 /* Clean up in case of error */
379 * Read next directory entry. The directory entry is returned in dirent
380 * structure in the d_name field. Individual directory entries returned by
381 * this function include regular files, sub-directories, pseudo-directories
382 * "." and ".." as well as volume labels, hidden files and system files.
384 static struct _wdirent
*
388 WIN32_FIND_DATAW
*datap
;
389 struct _wdirent
*entp
;
391 /* Read next directory entry */
392 datap
= dirent_next (dirp
);
397 /* Pointer to directory entry to return */
401 * Copy file name as wide-character string. If the file name is too
402 * long to fit in to the destination buffer, then truncate file name
403 * to PATH_MAX characters and zero-terminate the buffer.
406 while (n
< PATH_MAX
&& datap
->cFileName
[n
] != 0) {
407 entp
->d_name
[n
] = datap
->cFileName
[n
];
410 dirp
->ent
.d_name
[n
] = 0;
412 /* Length of file name excluding zero terminator */
416 attr
= datap
->dwFileAttributes
;
417 if ((attr
& FILE_ATTRIBUTE_DEVICE
) != 0) {
418 entp
->d_type
= DT_CHR
;
419 } else if ((attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0) {
420 entp
->d_type
= DT_DIR
;
422 entp
->d_type
= DT_REG
;
425 /* Reset dummy fields */
427 entp
->d_reclen
= sizeof (struct _wdirent
);
431 /* Last directory entry read */
440 * Close directory stream opened by opendir() function. This invalidates the
441 * DIR structure as well as any directory entry read previously by
451 /* Release search handle */
452 if (dirp
->handle
!= INVALID_HANDLE_VALUE
) {
453 FindClose (dirp
->handle
);
454 dirp
->handle
= INVALID_HANDLE_VALUE
;
457 /* Release search pattern */
463 /* Release directory structure */
468 /* Invalid directory stream */
469 dirent_set_errno (EBADF
);
476 * Rewind directory stream such that _wreaddir() returns the very first
484 /* Release existing search handle */
485 if (dirp
->handle
!= INVALID_HANDLE_VALUE
) {
486 FindClose (dirp
->handle
);
489 /* Open new search handle */
494 /* Get first directory entry (internal) */
495 static WIN32_FIND_DATAW
*
499 WIN32_FIND_DATAW
*datap
;
501 /* Open directory and retrieve the first entry */
502 dirp
->handle
= FindFirstFileW (dirp
->patt
, &dirp
->data
);
503 if (dirp
->handle
!= INVALID_HANDLE_VALUE
) {
505 /* a directory entry is now waiting in memory */
511 /* Failed to re-open directory: no directory entry in memory */
519 /* Get next directory entry (internal) */
520 static WIN32_FIND_DATAW
*
526 /* Get next directory entry */
527 if (dirp
->cached
!= 0) {
529 /* A valid directory entry already in memory */
533 } else if (dirp
->handle
!= INVALID_HANDLE_VALUE
) {
535 /* Get the next directory entry from stream */
536 if (FindNextFileW (dirp
->handle
, &dirp
->data
) != FALSE
) {
540 /* The very last entry has been processed or an error occurred */
541 FindClose (dirp
->handle
);
542 dirp
->handle
= INVALID_HANDLE_VALUE
;
548 /* End of directory stream reached */
557 * Open directory stream using plain old C-string.
566 /* Must have directory name */
567 if (dirname
== NULL
|| dirname
[0] == '\0') {
568 dirent_set_errno (ENOENT
);
572 /* Allocate memory for DIR structure */
573 dirp
= (DIR*) malloc (sizeof (struct DIR));
575 wchar_t wname
[PATH_MAX
+ 1];
578 /* Convert directory name to wide-character string */
579 error
= dirent_mbstowcs_s(
580 &n
, wname
, PATH_MAX
+ 1, dirname
, PATH_MAX
);
583 /* Open directory stream using wide-character name */
584 dirp
->wdirp
= _wopendir (wname
);
586 /* Directory stream opened */
589 /* Failed to open directory stream */
595 * Cannot convert file name to wide-character string. This
596 * occurs if the string contains invalid multi-byte sequences or
597 * the output buffer is too small to contain the resulting
604 /* Cannot allocate DIR structure */
608 /* Clean up in case of error */
618 * Read next directory entry.
620 * When working with text consoles, please note that file names returned by
621 * readdir() are represented in the default ANSI code page while any output to
622 * console is typically formatted on another code page. Thus, non-ASCII
623 * characters in file names will not usually display correctly on console. The
624 * problem can be fixed in two ways: (1) change the character set of console
625 * to 1252 using chcp utility and use Lucida Console font, or (2) use
626 * _cprintf function when writing to console. The _cprinf() will re-encode
627 * ANSI strings to the console code page so many non-ASCII characters will
630 static struct dirent
*
634 WIN32_FIND_DATAW
*datap
;
637 /* Read next directory entry */
638 datap
= dirent_next (dirp
->wdirp
);
643 /* Attempt to convert file name to multi-byte string */
644 error
= dirent_wcstombs_s(
645 &n
, dirp
->ent
.d_name
, MAX_PATH
+ 1, datap
->cFileName
, MAX_PATH
);
648 * If the file name cannot be represented by a multi-byte string,
649 * then attempt to use old 8+3 file name. This allows traditional
650 * Unix-code to access some file names despite of unicode
651 * characters, although file names may seem unfamiliar to the user.
653 * Be ware that the code below cannot come up with a short file
654 * name unless the file system provides one. At least
655 * VirtualBox shared folders fail to do this.
657 if (error
&& datap
->cAlternateFileName
[0] != '\0') {
658 error
= dirent_wcstombs_s(
659 &n
, dirp
->ent
.d_name
, MAX_PATH
+ 1, datap
->cAlternateFileName
,
660 sizeof (datap
->cAlternateFileName
) /
661 sizeof (datap
->cAlternateFileName
[0]));
667 /* Initialize directory entry for return */
670 /* Length of file name excluding zero terminator */
671 entp
->d_namlen
= n
- 1;
673 /* File attributes */
674 attr
= datap
->dwFileAttributes
;
675 if ((attr
& FILE_ATTRIBUTE_DEVICE
) != 0) {
676 entp
->d_type
= DT_CHR
;
677 } else if ((attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0) {
678 entp
->d_type
= DT_DIR
;
680 entp
->d_type
= DT_REG
;
683 /* Reset dummy fields */
685 entp
->d_reclen
= sizeof (struct dirent
);
689 * Cannot convert file name to multi-byte string so construct
690 * an erroneous directory entry and return that. Note that
691 * we cannot return NULL as that would stop the processing
692 * of directory entries completely.
695 entp
->d_name
[0] = '?';
696 entp
->d_name
[1] = '\0';
698 entp
->d_type
= DT_UNKNOWN
;
704 /* No more directory entries */
712 * Close directory stream.
721 /* Close wide-character directory stream */
722 ok
= _wclosedir (dirp
->wdirp
);
725 /* Release multi-byte character version */
730 /* Invalid directory stream */
731 dirent_set_errno (EBADF
);
739 * Rewind directory stream to beginning.
745 /* Rewind wide-character string directory stream */
746 _wrewinddir (dirp
->wdirp
);
749 /* Convert multi-byte string to wide character string */
752 size_t *pReturnValue
,
760 #if defined(_MSC_VER) && _MSC_VER >= 1400
762 /* Microsoft Visual Studio 2005 or later */
763 error
= mbstowcs_s (pReturnValue
, wcstr
, sizeInWords
, mbstr
, count
);
767 /* Older Visual Studio or non-Microsoft compiler */
770 /* Convert to wide-character string */
771 n
= mbstowcs (wcstr
, mbstr
, count
);
772 if (n
< sizeInWords
) {
774 /* Zero-terminate output buffer */
779 /* Length of resulting multi-byte string WITH zero terminator */
781 *pReturnValue
= n
+ 1;
789 /* Could not convert string */
799 /* Convert wide-character string to multi-byte string */
802 size_t *pReturnValue
,
805 const wchar_t *wcstr
,
810 #if defined(_MSC_VER) && _MSC_VER >= 1400
812 /* Microsoft Visual Studio 2005 or later */
813 error
= wcstombs_s (pReturnValue
, mbstr
, sizeInBytes
, wcstr
, count
);
817 /* Older Visual Studio or non-Microsoft compiler */
820 /* Convert to multi-byte string */
821 n
= wcstombs (mbstr
, wcstr
, count
);
822 if (n
< sizeInBytes
) {
824 /* Zero-terminate output buffer */
829 /* Length of resulting multi-bytes string WITH zero-terminator */
831 *pReturnValue
= n
+ 1;
839 /* Cannot convert string */
849 /* Set errno variable */
854 #if defined(_MSC_VER)
856 /* Microsoft Visual Studio */
861 /* Non-Microsoft compiler */