2 * dirent.h - dirent API for Microsoft Visual Studio
4 * Copyright (C) 2006-2012 Toni Ronkko
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * ``Software''), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
26 * Version 1.13, Dec 12 2012, Toni Ronkko
27 * Use traditional 8+3 file name if the name cannot be represented in the
28 * default ANSI code page. Now compiles again with MSVC 6.0. Thanks to
29 * Konstantin Khomoutov for testing.
31 * Version 1.12.1, Oct 1 2012, Toni Ronkko
32 * Bug fix: renamed wide-character DIR structure _wDIR to _WDIR (with
33 * capital W) in order to maintain compatibility with MingW.
35 * Version 1.12, Sep 30 2012, Toni Ronkko
36 * Define PATH_MAX and NAME_MAX. Added wide-character variants _wDIR,
37 * _wdirent, _wopendir(), _wreaddir(), _wclosedir() and _wrewinddir().
38 * Thanks to Edgar Buerkle and Jan Nijtmans for ideas and code.
40 * Do not include windows.h. This allows dirent.h to be integrated more
41 * easily into programs using winsock. Thanks to Fernando Azaldegui.
43 * Version 1.11, Mar 15, 2011, Toni Ronkko
44 * Defined FILE_ATTRIBUTE_DEVICE for MSVC 6.0.
46 * Version 1.10, Aug 11, 2010, Toni Ronkko
47 * Added d_type and d_namlen fields to dirent structure. The former is
48 * especially useful for determining whether directory entry represents a
49 * file or a directory. For more information, see
50 * http://www.delorie.com/gnu/docs/glibc/libc_270.html
52 * Improved conformance to the standards. For example, errno is now set
53 * properly on failure and assert() is never used. Thanks to Peter Brockam
56 * Fixed a bug in rewinddir(): when using relative directory names, change
57 * of working directory no longer causes rewinddir() to fail.
59 * Version 1.9, Dec 15, 2009, John Cunningham
60 * Added rewinddir member function
62 * Version 1.8, Jan 18, 2008, Toni Ronkko
63 * Using FindFirstFileA and WIN32_FIND_DATAA to avoid converting string
64 * between multi-byte and unicode representations. This makes the
65 * code simpler and also allows the code to be compiled under MingW. Thanks
66 * to Azriel Fasten for the suggestion.
68 * Mar 4, 2007, Toni Ronkko
69 * Bug fix: due to the strncpy_s() function this file only compiled in
70 * Visual Studio 2005. Using the new string functions only when the
71 * compiler version allows.
73 * Nov 2, 2006, Toni Ronkko
74 * Major update: removed support for Watcom C, MS-DOS and Turbo C to
75 * simplify the file, updated the code to compile cleanly on Visual
76 * Studio 2005 with both unicode and multi-byte character strings,
77 * removed rewinddir() as it had a bug.
79 * Aug 20, 2006, Toni Ronkko
80 * Removed all remarks about MSVC 1.0, which is antiqued now. Simplified
81 * comments by removing SGML tags.
83 * May 14 2002, Toni Ronkko
84 * Embedded the function definitions directly to the header so that no
85 * source modules need to be included in the Visual Studio project. Removed
86 * all the dependencies to other projects so that this header file can be
89 * May 28 1998, Toni Ronkko
91 *****************************************************************************/
95 #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86)
106 #include <sys/types.h>
107 #include <sys/stat.h>
110 /* Indicates that d_type field is available in dirent structure */
111 #define _DIRENT_HAVE_D_TYPE
113 /* Indicates that d_namlen field is available in dirent structure */
114 #define _DIRENT_HAVE_D_NAMLEN
116 /* Entries missing from MSVC 6.0 */
117 #if !defined(FILE_ATTRIBUTE_DEVICE)
118 # define FILE_ATTRIBUTE_DEVICE 0x40
121 /* File type and permission flags for stat() */
123 # define S_IFMT _S_IFMT /* File type mask */
125 #if !defined(S_IFDIR)
126 # define S_IFDIR _S_IFDIR /* Directory */
128 #if !defined(S_IFCHR)
129 # define S_IFCHR _S_IFCHR /* Character device */
131 #if !defined(S_IFFIFO)
132 # define S_IFFIFO _S_IFFIFO /* Pipe */
134 #if !defined(S_IFREG)
135 # define S_IFREG _S_IFREG /* Regular file */
137 #if !defined(S_IREAD)
138 # define S_IREAD _S_IREAD /* Read permission */
140 #if !defined(S_IWRITE)
141 # define S_IWRITE _S_IWRITE /* Write permission */
143 #if !defined(S_IEXEC)
144 # define S_IEXEC _S_IEXEC /* Execute permission */
146 #if !defined(S_IFIFO)
147 # define S_IFIFO _S_IFIFO /* Pipe */
149 #if !defined(S_IFBLK)
150 # define S_IFBLK 0 /* Block device */
152 #if !defined(S_IFLNK)
153 # define S_IFLNK 0 /* Link */
155 #if !defined(S_IFSOCK)
156 # define S_IFSOCK 0 /* Socket */
159 #if defined(_MSC_VER)
160 # define S_IRUSR S_IREAD /* Read user */
161 # define S_IWUSR S_IWRITE /* Write user */
162 # define S_IXUSR 0 /* Execute user */
163 # define S_IRGRP 0 /* Read group */
164 # define S_IWGRP 0 /* Write group */
165 # define S_IXGRP 0 /* Execute group */
166 # define S_IROTH 0 /* Read others */
167 # define S_IWOTH 0 /* Write others */
168 # define S_IXOTH 0 /* Execute others */
171 /* Maximum length of file name */
172 #if !defined(PATH_MAX)
173 # define PATH_MAX MAX_PATH
175 #if !defined(FILENAME_MAX)
176 # define FILENAME_MAX MAX_PATH
178 #if !defined(NAME_MAX)
179 # define NAME_MAX FILENAME_MAX
182 /* File type flags for d_type */
184 #define DT_REG S_IFREG
185 #define DT_DIR S_IFDIR
186 #define DT_FIFO S_IFIFO
187 #define DT_SOCK S_IFSOCK
188 #define DT_CHR S_IFCHR
189 #define DT_BLK S_IFBLK
191 /* Macros for converting between st_mode and d_type */
192 #define IFTODT(mode) ((mode) & S_IFMT)
193 #define DTTOIF(type) (type)
196 * File type macros. Note that block devices, sockets and links cannot be
197 * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
198 * only defined for compatibility. These macros should always return false
201 #define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
202 #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
203 #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
204 #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
205 #define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
206 #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
207 #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
209 /* Return the exact length of d_namlen without zero terminator */
210 #define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
212 /* Return number of bytes needed to store d_namlen */
213 #define _D_ALLOC_NAMLEN(p) (PATH_MAX + 1)
221 /* Wide-character version */
223 long d_ino; /* Always zero */
224 unsigned short d_reclen; /* Structure size */
225 size_t d_namlen; /* Length of name without \0 */
226 int d_type; /* File type */
227 wchar_t d_name[PATH_MAX + 1]; /* File name */
229 typedef struct _wdirent _wdirent;
232 struct _wdirent ent; /* Current directory entry */
233 WIN32_FIND_DATAW data; /* Private file data */
234 int cached; /* True if data is valid */
235 HANDLE handle; /* Win32 search handle */
236 wchar_t *patt; /* Initial directory name */
238 typedef struct _WDIR _WDIR;
240 static _WDIR *_wopendir (const wchar_t *dirname);
241 static struct _wdirent *_wreaddir (_WDIR *dirp);
242 static int _wclosedir (_WDIR *dirp);
243 static void _wrewinddir (_WDIR* dirp);
246 /* For compatibility with Symbian */
247 #define wdirent _wdirent
249 #define wopendir _wopendir
250 #define wreaddir _wreaddir
251 #define wclosedir _wclosedir
252 #define wrewinddir _wrewinddir
255 /* Multi-byte character versions */
257 long d_ino; /* Always zero */
258 unsigned short d_reclen; /* Structure size */
259 size_t d_namlen; /* Length of name without \0 */
260 int d_type; /* File type */
261 char d_name[PATH_MAX + 1]; /* File name */
263 typedef struct dirent dirent;
269 typedef struct DIR DIR;
271 static DIR *opendir (const char *dirname);
272 static struct dirent *readdir (DIR *dirp);
273 static int closedir (DIR *dirp);
274 static void rewinddir (DIR* dirp);
277 /* Internal utility functions */
278 static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp);
279 static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp);
281 static int dirent_mbstowcs_s(
282 size_t *pReturnValue,
288 static int dirent_wcstombs_s(
289 size_t *pReturnValue,
292 const wchar_t *wcstr,
295 static void dirent_set_errno (int error);
298 * Open directory stream DIRNAME for read and return a pointer to the
299 * internal working area that is used to retrieve individual directory
304 const wchar_t *dirname)
309 /* Must have directory name */
310 if (dirname == NULL || dirname[0] == '\0') {
311 dirent_set_errno (ENOENT);
315 /* Allocate new _WDIR structure */
316 dirp = (_WDIR*) malloc (sizeof (struct _WDIR));
320 /* Reset _WDIR structure */
321 dirp->handle = INVALID_HANDLE_VALUE;
325 /* Compute the length of full path plus zero terminator */
326 n = GetFullPathNameW (dirname, 0, NULL, NULL);
328 /* Allocate room for absolute directory name and search pattern */
329 dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);
333 * Convert relative directory name to an absolute one. This
334 * allows rewinddir() to function correctly even when current
335 * working directory is changed between opendir() and rewinddir().
337 n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
341 /* Append search pattern \* to the directory name */
343 if (dirp->patt < p) {
348 /* Directory ends in path separator, e.g. c:\temp\ */
353 /* Directory name doesn't end in path separator */
360 /* Open directory stream and retrieve the first entry */
361 if (dirent_first (dirp)) {
362 /* Directory stream opened successfully */
365 /* Cannot retrieve first entry */
367 dirent_set_errno (ENOENT);
371 /* Cannot retrieve full path name */
372 dirent_set_errno (ENOENT);
377 /* Cannot allocate memory for search pattern */
382 /* Cannot allocate _WDIR structure */
386 /* Clean up in case of error */
396 * Read next directory entry. The directory entry is returned in dirent
397 * structure in the d_name field. Individual directory entries returned by
398 * this function include regular files, sub-directories, pseudo-directories
399 * "." and ".." as well as volume labels, hidden files and system files.
401 static struct _wdirent*
405 WIN32_FIND_DATAW *datap;
406 struct _wdirent *entp;
408 /* Read next directory entry */
409 datap = dirent_next (dirp);
414 /* Pointer to directory entry to return */
418 * Copy file name as wide-character string. If the file name is too
419 * long to fit in to the destination buffer, then truncate file name
420 * to PATH_MAX characters and zero-terminate the buffer.
423 while (n < PATH_MAX && datap->cFileName[n] != 0) {
424 entp->d_name[n] = datap->cFileName[n];
427 dirp->ent.d_name[n] = 0;
429 /* Length of file name excluding zero terminator */
433 attr = datap->dwFileAttributes;
434 if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
435 entp->d_type = DT_CHR;
436 } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
437 entp->d_type = DT_DIR;
439 entp->d_type = DT_REG;
442 /* Reset dummy fields */
444 entp->d_reclen = sizeof (struct _wdirent);
448 /* Last directory entry read */
457 * Close directory stream opened by opendir() function. This invalidates the
458 * DIR structure as well as any directory entry read previously by
468 /* Release search handle */
469 if (dirp->handle != INVALID_HANDLE_VALUE) {
470 FindClose (dirp->handle);
471 dirp->handle = INVALID_HANDLE_VALUE;
474 /* Release search pattern */
480 /* Release directory structure */
485 /* Invalid directory stream */
486 dirent_set_errno (EBADF);
493 * Rewind directory stream such that _wreaddir() returns the very first
501 /* Release existing search handle */
502 if (dirp->handle != INVALID_HANDLE_VALUE) {
503 FindClose (dirp->handle);
506 /* Open new search handle */
511 /* Get first directory entry (internal) */
512 static WIN32_FIND_DATAW*
516 WIN32_FIND_DATAW *datap;
518 /* Open directory and retrieve the first entry */
519 dirp->handle = FindFirstFileW (dirp->patt, &dirp->data);
520 if (dirp->handle != INVALID_HANDLE_VALUE) {
522 /* a directory entry is now waiting in memory */
528 /* Failed to re-open directory: no directory entry in memory */
536 /* Get next directory entry (internal) */
537 static WIN32_FIND_DATAW*
543 /* Get next directory entry */
544 if (dirp->cached != 0) {
546 /* A valid directory entry already in memory */
550 } else if (dirp->handle != INVALID_HANDLE_VALUE) {
552 /* Get the next directory entry from stream */
553 if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) {
557 /* The very last entry has been processed or an error occured */
558 FindClose (dirp->handle);
559 dirp->handle = INVALID_HANDLE_VALUE;
565 /* End of directory stream reached */
574 * Open directory stream using plain old C-string.
583 /* Must have directory name */
584 if (dirname == NULL || dirname[0] == '\0') {
585 dirent_set_errno (ENOENT);
589 /* Allocate memory for DIR structure */
590 dirp = (DIR*) malloc (sizeof (struct DIR));
592 wchar_t wname[PATH_MAX + 1];
595 /* Convert directory name to wide-character string */
596 error = dirent_mbstowcs_s(
597 &n, wname, PATH_MAX + 1, dirname, PATH_MAX);
600 /* Open directory stream using wide-character name */
601 dirp->wdirp = _wopendir (wname);
603 /* Directory stream opened */
606 /* Failed to open directory stream */
612 * Cannot convert file name to wide-character string. This
613 * occurs if the string contains invalid multi-byte sequences or
614 * the output buffer is too small to contain the resulting
621 /* Cannot allocate DIR structure */
625 /* Clean up in case of error */
635 * Read next directory entry.
637 * When working with text consoles, please note that file names returned by
638 * readdir() are represented in the default ANSI code page while any output to
639 * console is typically formatted on another code page. Thus, non-ASCII
640 * characters in file names will not usually display correctly on console. The
641 * problem can be fixed in two ways: (1) change the character set of console
642 * to 1252 using chcp utility and use Lucida Console font, or (2) use
643 * _cprintf function when writing to console. The _cprinf() will re-encode
644 * ANSI strings to the console code page so many non-ASCII characters will
647 static struct dirent*
651 WIN32_FIND_DATAW *datap;
654 /* Read next directory entry */
655 datap = dirent_next (dirp->wdirp);
660 /* Attempt to convert file name to multi-byte string */
661 error = dirent_wcstombs_s(
662 &n, dirp->ent.d_name, MAX_PATH + 1, datap->cFileName, MAX_PATH);
665 * If the file name cannot be represented by a multi-byte string,
666 * then attempt to use old 8+3 file name. This allows traditional
667 * Unix-code to access some file names despite of unicode
668 * characters, although file names may seem unfamiliar to the user.
670 * Be ware that the code below cannot come up with a short file
671 * name unless the file system provides one. At least
672 * VirtualBox shared folders fail to do this.
674 if (error && datap->cAlternateFileName[0] != '\0') {
675 error = dirent_wcstombs_s(
676 &n, dirp->ent.d_name, MAX_PATH + 1, datap->cAlternateFileName,
677 sizeof (datap->cAlternateFileName) /
678 sizeof (datap->cAlternateFileName[0]));
684 /* Initialize directory entry for return */
687 /* Length of file name excluding zero terminator */
688 entp->d_namlen = n - 1;
690 /* File attributes */
691 attr = datap->dwFileAttributes;
692 if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
693 entp->d_type = DT_CHR;
694 } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
695 entp->d_type = DT_DIR;
697 entp->d_type = DT_REG;
700 /* Reset dummy fields */
702 entp->d_reclen = sizeof (struct dirent);
706 * Cannot convert file name to multi-byte string so construct
707 * an errornous directory entry and return that. Note that
708 * we cannot return NULL as that would stop the processing
709 * of directory entries completely.
712 entp->d_name[0] = '?';
713 entp->d_name[1] = '\0';
715 entp->d_type = DT_UNKNOWN;
721 /* No more directory entries */
729 * Close directory stream.
738 /* Close wide-character directory stream */
739 ok = _wclosedir (dirp->wdirp);
742 /* Release multi-byte character version */
747 /* Invalid directory stream */
748 dirent_set_errno (EBADF);
756 * Rewind directory stream to beginning.
762 /* Rewind wide-character string directory stream */
763 _wrewinddir (dirp->wdirp);
766 /* Convert multi-byte string to wide character string */
769 size_t *pReturnValue,
777 #if defined(_MSC_VER) && _MSC_VER >= 1400
779 /* Microsoft Visual Studio 2005 or later */
780 error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count);
784 /* Older Visual Studio or non-Microsoft compiler */
787 /* Convert to wide-character string */
788 n = mbstowcs (wcstr, mbstr, count);
789 if (n < sizeInWords) {
791 /* Zero-terminate output buffer */
796 /* Length of resuting multi-byte string WITH zero terminator */
798 *pReturnValue = n + 1;
806 /* Could not convert string */
816 /* Convert wide-character string to multi-byte string */
819 size_t *pReturnValue,
822 const wchar_t *wcstr,
827 #if defined(_MSC_VER) && _MSC_VER >= 1400
829 /* Microsoft Visual Studio 2005 or later */
830 error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count);
834 /* Older Visual Studio or non-Microsoft compiler */
837 /* Convert to multi-byte string */
838 n = wcstombs (mbstr, wcstr, count);
839 if (n < sizeInBytes) {
841 /* Zero-terminate output buffer */
846 /* Lenght of resulting multi-bytes string WITH zero-terminator */
848 *pReturnValue = n + 1;
856 /* Cannot convert string */
866 /* Set errno variable */
871 #if defined(_MSC_VER)
873 /* Microsoft Visual Studio */
878 /* Non-Microsoft compiler */