1 /*
2  * dirent.h - dirent API for Microsoft Visual Studio
3  *
4  * Copyright (C) 2006-2012 Toni Ronkko
5  *
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:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
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.
24  *
25  *
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.
30  *
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.
34  *
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.
39  *
40  * Do not include windows.h.  This allows dirent.h to be integrated more
41  * easily into programs using winsock.  Thanks to Fernando Azaldegui.
42  *
43  * Version 1.11, Mar 15, 2011, Toni Ronkko
44  * Defined FILE_ATTRIBUTE_DEVICE for MSVC 6.0.
45  *
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
51  *
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
54  * for suggestions.
55  *
56  * Fixed a bug in rewinddir(): when using relative directory names, change
57  * of working directory no longer causes rewinddir() to fail.
58  *
59  * Version 1.9, Dec 15, 2009, John Cunningham
60  * Added rewinddir member function
61  *
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.
67  *
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.
72  *
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.
78  *
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.
82  *
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
87  * used independently.
88  *
89  * May 28 1998, Toni Ronkko
90  * First version.
91  *****************************************************************************/
92 #pragma once
93 #ifndef DIRENT_H
94 #define DIRENT_H
95 
96 #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86)
97 #   define _X86_
98 #endif
99 #include <stdio.h>
100 #include <stdarg.h>
101 #include <windef.h>
102 #include <winbase.h>
103 #include <wchar.h>
104 #include <string.h>
105 #include <stdlib.h>
106 #include <malloc.h>
107 #include <sys/types.h>
108 #include <sys/stat.h>
109 #include <errno.h>
110 
111 /* Indicates that d_type field is available in dirent structure */
112 #define _DIRENT_HAVE_D_TYPE
113 
114 /* Indicates that d_namlen field is available in dirent structure */
115 #define _DIRENT_HAVE_D_NAMLEN
116 
117 /* Entries missing from MSVC 6.0 */
118 #if !defined(FILE_ATTRIBUTE_DEVICE)
119 #   define FILE_ATTRIBUTE_DEVICE 0x40
120 #endif
121 
122 /* File type and permission flags for stat() */
123 #if !defined(S_IFMT)
124 #   define S_IFMT   _S_IFMT                     /* File type mask */
125 #endif
126 #if !defined(S_IFDIR)
127 #   define S_IFDIR  _S_IFDIR                    /* Directory */
128 #endif
129 #if !defined(S_IFCHR)
130 #   define S_IFCHR  _S_IFCHR                    /* Character device */
131 #endif
132 #if !defined(S_IFFIFO)
133 #   define S_IFFIFO _S_IFFIFO                   /* Pipe */
134 #endif
135 #if !defined(S_IFREG)
136 #   define S_IFREG  _S_IFREG                    /* Regular file */
137 #endif
138 #if !defined(S_IREAD)
139 #   define S_IREAD  _S_IREAD                    /* Read permission */
140 #endif
141 #if !defined(S_IWRITE)
142 #   define S_IWRITE _S_IWRITE                   /* Write permission */
143 #endif
144 #if !defined(S_IEXEC)
145 #   define S_IEXEC  _S_IEXEC                    /* Execute permission */
146 #endif
147 #if !defined(S_IFIFO)
148 #   define S_IFIFO _S_IFIFO                     /* Pipe */
149 #endif
150 #if !defined(S_IFBLK)
151 #   define S_IFBLK   0                          /* Block device */
152 #endif
153 #if !defined(S_IFLNK)
154 #   define S_IFLNK   0                          /* Link */
155 #endif
156 #if !defined(S_IFSOCK)
157 #   define S_IFSOCK  0                          /* Socket */
158 #endif
159 
160 #if defined(_MSC_VER)
161 #   define S_IRUSR  S_IREAD                     /* Read user */
162 #   define S_IWUSR  S_IWRITE                    /* Write user */
163 #   define S_IXUSR  0                           /* Execute user */
164 #   define S_IRGRP  0                           /* Read group */
165 #   define S_IWGRP  0                           /* Write group */
166 #   define S_IXGRP  0                           /* Execute group */
167 #   define S_IROTH  0                           /* Read others */
168 #   define S_IWOTH  0                           /* Write others */
169 #   define S_IXOTH  0                           /* Execute others */
170 #endif
171 
172 /* Maximum length of file name */
173 #if !defined(PATH_MAX)
174 #   define PATH_MAX MAX_PATH
175 #endif
176 #if !defined(FILENAME_MAX)
177 #   define FILENAME_MAX MAX_PATH
178 #endif
179 #if !defined(NAME_MAX)
180 #   define NAME_MAX FILENAME_MAX
181 #endif
182 
183 /* File type flags for d_type */
184 #define DT_UNKNOWN  0
185 #define DT_REG      S_IFREG
186 #define DT_DIR      S_IFDIR
187 #define DT_FIFO     S_IFIFO
188 #define DT_SOCK     S_IFSOCK
189 #define DT_CHR      S_IFCHR
190 #define DT_BLK      S_IFBLK
191 #define DT_LNK      S_IFLNK
192 
193 /* Macros for converting between st_mode and d_type */
194 #define IFTODT(mode) ((mode) & S_IFMT)
195 #define DTTOIF(type) (type)
196 
197 /*
198  * File type macros.  Note that block devices, sockets and links cannot be
199  * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
200  * only defined for compatibility.  These macros should always return false
201  * on Windows.
202  */
203 #define	S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
204 #ifndef S_ISDIR
205 #define	S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)
206 #endif
207 #define	S_ISREG(mode)  (((mode) & S_IFMT) == S_IFREG)
208 #define	S_ISLNK(mode)  (((mode) & S_IFMT) == S_IFLNK)
209 #define	S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
210 #define	S_ISCHR(mode)  (((mode) & S_IFMT) == S_IFCHR)
211 #define	S_ISBLK(mode)  (((mode) & S_IFMT) == S_IFBLK)
212 
213 /* Return the exact length of d_namlen without zero terminator */
214 #define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
215 
216 /* Return number of bytes needed to store d_namlen */
217 #define _D_ALLOC_NAMLEN(p) (PATH_MAX + 1)
218 
219 
220 #ifdef __cplusplus
221 extern "C" {
222 #endif
223 
224 
225 /* Wide-character version */
226 struct _wdirent {
227     long d_ino;                                 /* Always zero */
228     unsigned short d_reclen;                    /* Structure size */
229     size_t d_namlen;                            /* Length of name without \0 */
230     int d_type;                                 /* File type */
231     wchar_t d_name[PATH_MAX + 1];               /* File name */
232 };
233 typedef struct _wdirent _wdirent;
234 
235 struct _WDIR {
236     struct _wdirent ent;                        /* Current directory entry */
237     WIN32_FIND_DATAW data;                      /* Private file data */
238     int cached;                                 /* True if data is valid */
239     HANDLE handle;                              /* Win32 search handle */
240     wchar_t *patt;                              /* Initial directory name */
241 };
242 typedef struct _WDIR _WDIR;
243 
244 static _WDIR *_wopendir (const wchar_t *dirname);
245 static struct _wdirent *_wreaddir (_WDIR *dirp);
246 static int _wclosedir (_WDIR *dirp);
247 static void _wrewinddir (_WDIR* dirp);
248 
249 
250 /* For compatibility with Symbian */
251 #define wdirent _wdirent
252 #define WDIR _WDIR
253 #define wopendir _wopendir
254 #define wreaddir _wreaddir
255 #define wclosedir _wclosedir
256 #define wrewinddir _wrewinddir
257 
258 
259 /* Multi-byte character versions */
260 struct dirent {
261     long d_ino;                                 /* Always zero */
262     unsigned short d_reclen;                    /* Structure size */
263     size_t d_namlen;                            /* Length of name without \0 */
264     int d_type;                                 /* File type */
265     char d_name[PATH_MAX + 1];                  /* File name */
266 };
267 typedef struct dirent dirent;
268 
269 struct DIR {
270     struct dirent ent;
271     struct _WDIR *wdirp;
272 };
273 typedef struct DIR DIR;
274 
275 static DIR *opendir (const char *dirname);
276 static struct dirent *readdir (DIR *dirp);
277 static int closedir (DIR *dirp);
278 static void rewinddir (DIR* dirp);
279 
280 
281 /* Internal utility functions */
282 static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp);
283 static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp);
284 
285 static int dirent_mbstowcs_s(
286     size_t *pReturnValue,
287     wchar_t *wcstr,
288     size_t sizeInWords,
289     const char *mbstr,
290     size_t count);
291 
292 static int dirent_wcstombs_s(
293     size_t *pReturnValue,
294     char *mbstr,
295     size_t sizeInBytes,
296     const wchar_t *wcstr,
297     size_t count);
298 
299 static void dirent_set_errno (int error);
300 
301 /*
302  * Open directory stream DIRNAME for read and return a pointer to the
303  * internal working area that is used to retrieve individual directory
304  * entries.
305  */
306 static _WDIR*
_wopendir(const wchar_t * dirname)307 _wopendir(
308     const wchar_t *dirname)
309 {
310     _WDIR *dirp = NULL;
311     int error;
312 
313     /* Must have directory name */
314     if (dirname == NULL  ||  dirname[0] == '\0') {
315         dirent_set_errno (ENOENT);
316         return NULL;
317     }
318 
319     /* Allocate new _WDIR structure */
320     dirp = (_WDIR*) malloc (sizeof (struct _WDIR));
321     if (dirp != NULL) {
322         DWORD n;
323 
324         /* Reset _WDIR structure */
325         dirp->handle = INVALID_HANDLE_VALUE;
326         dirp->patt = NULL;
327         dirp->cached = 0;
328 
329         /* Compute the length of full path plus zero terminator */
330         n = GetFullPathNameW (dirname, 0, NULL, NULL);
331 
332         /* Allocate room for absolute directory name and search pattern */
333         dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);
334         if (dirp->patt) {
335 
336             /*
337              * Convert relative directory name to an absolute one.  This
338              * allows rewinddir() to function correctly even when current
339              * working directory is changed between opendir() and rewinddir().
340              */
341             n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
342             if (n > 0) {
343                 wchar_t *p;
344 
345                 /* Append search pattern \* to the directory name */
346                 p = dirp->patt + n;
347                 if (dirp->patt < p) {
348                     switch (p[-1]) {
349                     case '\\':
350                     case '/':
351                     case ':':
352                         /* Directory ends in path separator, e.g. c:\temp\ */
353                         /*NOP*/;
354                         break;
355 
356                     default:
357                         /* Directory name doesn't end in path separator */
358                         *p++ = '\\';
359                     }
360                 }
361                 *p++ = '*';
362                 *p = '\0';
363 
364                 /* Open directory stream and retrieve the first entry */
365                 if (dirent_first (dirp)) {
366                     /* Directory stream opened successfully */
367                     error = 0;
368                 } else {
369                     /* Cannot retrieve first entry */
370                     error = 1;
371                     dirent_set_errno (ENOENT);
372                 }
373 
374             } else {
375                 /* Cannot retrieve full path name */
376                 dirent_set_errno (ENOENT);
377                 error = 1;
378             }
379 
380         } else {
381             /* Cannot allocate memory for search pattern */
382             error = 1;
383         }
384 
385     } else {
386         /* Cannot allocate _WDIR structure */
387         error = 1;
388     }
389 
390     /* Clean up in case of error */
391     if (error  &&  dirp) {
392         _wclosedir (dirp);
393         dirp = NULL;
394     }
395 
396     return dirp;
397 }
398 
399 /*
400  * Read next directory entry.  The directory entry is returned in dirent
401  * structure in the d_name field.  Individual directory entries returned by
402  * this function include regular files, sub-directories, pseudo-directories
403  * "." and ".." as well as volume labels, hidden files and system files.
404  */
405 //static struct _wdirent*
406 //_wreaddir(
407 //    _WDIR *dirp)
408 //{
409 //    WIN32_FIND_DATAW *datap;
410 //    struct _wdirent *entp;
411 //
412 //    /* Read next directory entry */
413 //    datap = dirent_next (dirp);
414 //    if (datap) {
415 //        size_t n;
416 //        DWORD attr;
417 //
418 //        /* Pointer to directory entry to return */
419 //        entp = &dirp->ent;
420 //
421 //        /*
422 //         * Copy file name as wide-character string.  If the file name is too
423 //         * long to fit in to the destination buffer, then truncate file name
424 //         * to PATH_MAX characters and zero-terminate the buffer.
425 //         */
426 //        n = 0;
427 //        while (n < PATH_MAX  &&  datap->cFileName[n] != 0) {
428 //            entp->d_name[n] = datap->cFileName[n];
429 //            n++;
430 //        }
431 //        dirp->ent.d_name[n] = 0;
432 //
433 //        /* Length of file name excluding zero terminator */
434 //        entp->d_namlen = n;
435 //
436 //        /* File type */
437 //        attr = datap->dwFileAttributes;
438 //        if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
439 //            entp->d_type = DT_CHR;
440 //        } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
441 //            entp->d_type = DT_DIR;
442 //        } else {
443 //            entp->d_type = DT_REG;
444 //        }
445 //
446 //        /* Reset dummy fields */
447 //        entp->d_ino = 0;
448 //        entp->d_reclen = sizeof (struct _wdirent);
449 //
450 //    } else {
451 //
452 //        /* Last directory entry read */
453 //        entp = NULL;
454 //
455 //    }
456 //
457 //    return entp;
458 //}
459 //
460 /*
461  * Close directory stream opened by opendir() function.  This invalidates the
462  * DIR structure as well as any directory entry read previously by
463  * _wreaddir().
464  */
465 static int
_wclosedir(_WDIR * dirp)466 _wclosedir(
467     _WDIR *dirp)
468 {
469     int ok;
470     if (dirp) {
471 
472         /* Release search handle */
473         if (dirp->handle != INVALID_HANDLE_VALUE) {
474             FindClose (dirp->handle);
475             dirp->handle = INVALID_HANDLE_VALUE;
476         }
477 
478         /* Release search pattern */
479         if (dirp->patt) {
480             free (dirp->patt);
481             dirp->patt = NULL;
482         }
483 
484         /* Release directory structure */
485         free (dirp);
486         ok = /*success*/0;
487 
488     } else {
489         /* Invalid directory stream */
490         dirent_set_errno (EBADF);
491         ok = /*failure*/-1;
492     }
493     return ok;
494 }
495 
496 /*
497  * Rewind directory stream such that _wreaddir() returns the very first
498  * file name again.
499  */
500 static void
_wrewinddir(_WDIR * dirp)501 _wrewinddir(
502     _WDIR* dirp)
503 {
504     if (dirp) {
505         /* Release existing search handle */
506         if (dirp->handle != INVALID_HANDLE_VALUE) {
507             FindClose (dirp->handle);
508         }
509 
510         /* Open new search handle */
511         dirent_first (dirp);
512     }
513 }
514 
515 /* Get first directory entry (internal) */
516 static WIN32_FIND_DATAW*
dirent_first(_WDIR * dirp)517 dirent_first(
518     _WDIR *dirp)
519 {
520     WIN32_FIND_DATAW *datap;
521 
522     /* Open directory and retrieve the first entry */
523     dirp->handle = FindFirstFileW (dirp->patt, &dirp->data);
524     if (dirp->handle != INVALID_HANDLE_VALUE) {
525 
526         /* a directory entry is now waiting in memory */
527         datap = &dirp->data;
528         dirp->cached = 1;
529 
530     } else {
531 
532         /* Failed to re-open directory: no directory entry in memory */
533         dirp->cached = 0;
534         datap = NULL;
535 
536     }
537     return datap;
538 }
539 
540 /* Get next directory entry (internal) */
541 static WIN32_FIND_DATAW*
dirent_next(_WDIR * dirp)542 dirent_next(
543     _WDIR *dirp)
544 {
545     WIN32_FIND_DATAW *p;
546 
547     /* Get next directory entry */
548     if (dirp->cached != 0) {
549 
550         /* A valid directory entry already in memory */
551         p = &dirp->data;
552         dirp->cached = 0;
553 
554     } else if (dirp->handle != INVALID_HANDLE_VALUE) {
555 
556         /* Get the next directory entry from stream */
557         if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) {
558             /* Got a file */
559             p = &dirp->data;
560         } else {
561             /* The very last entry has been processed or an error occured */
562             FindClose (dirp->handle);
563             dirp->handle = INVALID_HANDLE_VALUE;
564             p = NULL;
565         }
566 
567     } else {
568 
569         /* End of directory stream reached */
570         p = NULL;
571 
572     }
573 
574     return p;
575 }
576 
577 /*
578  * Open directory stream using plain old C-string.
579  */
580 static DIR*
opendir(const char * dirname)581 opendir(
582     const char *dirname)
583 {
584     struct DIR *dirp;
585     int error;
586 
587     /* Must have directory name */
588     if (dirname == NULL  ||  dirname[0] == '\0') {
589         dirent_set_errno (ENOENT);
590         return NULL;
591     }
592 
593     /* Allocate memory for DIR structure */
594     dirp = (DIR*) malloc (sizeof (struct DIR));
595     if (dirp) {
596         wchar_t wname[PATH_MAX + 1];
597         size_t n;
598 
599         /* Convert directory name to wide-character string */
600         error = dirent_mbstowcs_s(
601             &n, wname, PATH_MAX + 1, dirname, PATH_MAX);
602         if (!error) {
603 
604             /* Open directory stream using wide-character name */
605             dirp->wdirp = _wopendir (wname);
606             if (dirp->wdirp) {
607                 /* Directory stream opened */
608                 error = 0;
609             } else {
610                 /* Failed to open directory stream */
611                 error = 1;
612             }
613 
614         } else {
615             /*
616              * Cannot convert file name to wide-character string.  This
617              * occurs if the string contains invalid multi-byte sequences or
618              * the output buffer is too small to contain the resulting
619              * string.
620              */
621             error = 1;
622         }
623 
624     } else {
625         /* Cannot allocate DIR structure */
626         error = 1;
627     }
628 
629     /* Clean up in case of error */
630     if (error  &&  dirp) {
631         free (dirp);
632         dirp = NULL;
633     }
634 
635     return dirp;
636 }
637 
638 /*
639  * Read next directory entry.
640  *
641  * When working with text consoles, please note that file names returned by
642  * readdir() are represented in the default ANSI code page while any output to
643  * console is typically formatted on another code page.  Thus, non-ASCII
644  * characters in file names will not usually display correctly on console.  The
645  * problem can be fixed in two ways: (1) change the character set of console
646  * to 1252 using chcp utility and use Lucida Console font, or (2) use
647  * _cprintf function when writing to console.  The _cprinf() will re-encode
648  * ANSI strings to the console code page so many non-ASCII characters will
649  * display correcly.
650  */
651 static struct dirent*
readdir(DIR * dirp)652 readdir(
653     DIR *dirp)
654 {
655     WIN32_FIND_DATAW *datap;
656     struct dirent *entp;
657 
658     /* Read next directory entry */
659     datap = dirent_next (dirp->wdirp);
660     if (datap) {
661         size_t n;
662         int error;
663 
664         /* Attempt to convert file name to multi-byte string */
665         error = dirent_wcstombs_s(
666             &n, dirp->ent.d_name, MAX_PATH + 1, datap->cFileName, MAX_PATH);
667 
668         /*
669          * If the file name cannot be represented by a multi-byte string,
670          * then attempt to use old 8+3 file name.  This allows traditional
671          * Unix-code to access some file names despite of unicode
672          * characters, although file names may seem unfamiliar to the user.
673          *
674          * Be ware that the code below cannot come up with a short file
675          * name unless the file system provides one.  At least
676          * VirtualBox shared folders fail to do this.
677          */
678         if (error  &&  datap->cAlternateFileName[0] != '\0') {
679             error = dirent_wcstombs_s(
680                 &n, dirp->ent.d_name, MAX_PATH + 1, datap->cAlternateFileName,
681                 sizeof (datap->cAlternateFileName) /
682                     sizeof (datap->cAlternateFileName[0]));
683         }
684 
685         if (!error) {
686             DWORD attr;
687 
688             /* Initialize directory entry for return */
689             entp = &dirp->ent;
690 
691             /* Length of file name excluding zero terminator */
692             entp->d_namlen = n - 1;
693 
694             /* File attributes */
695             attr = datap->dwFileAttributes;
696             if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
697                 entp->d_type = DT_CHR;
698             } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
699                 entp->d_type = DT_DIR;
700             } else {
701                 entp->d_type = DT_REG;
702             }
703 
704             /* Reset dummy fields */
705             entp->d_ino = 0;
706             entp->d_reclen = sizeof (struct dirent);
707 
708         } else {
709             /*
710              * Cannot convert file name to multi-byte string so construct
711              * an errornous directory entry and return that.  Note that
712              * we cannot return NULL as that would stop the processing
713              * of directory entries completely.
714              */
715             entp = &dirp->ent;
716             entp->d_name[0] = '?';
717             entp->d_name[1] = '\0';
718             entp->d_namlen = 1;
719             entp->d_type = DT_UNKNOWN;
720             entp->d_ino = 0;
721             entp->d_reclen = 0;
722         }
723 
724     } else {
725         /* No more directory entries */
726         entp = NULL;
727     }
728 
729     return entp;
730 }
731 
732 /*
733  * Close directory stream.
734  */
735 static int
closedir(DIR * dirp)736 closedir(
737     DIR *dirp)
738 {
739     int ok;
740     if (dirp) {
741 
742         /* Close wide-character directory stream */
743         ok = _wclosedir (dirp->wdirp);
744         dirp->wdirp = NULL;
745 
746         /* Release multi-byte character version */
747         free (dirp);
748 
749     } else {
750 
751         /* Invalid directory stream */
752         dirent_set_errno (EBADF);
753         ok = /*failure*/-1;
754 
755     }
756     return ok;
757 }
758 
759 /*
760  * Rewind directory stream to beginning.
761  */
762 static void
rewinddir(DIR * dirp)763 rewinddir(
764     DIR* dirp)
765 {
766     /* Rewind wide-character string directory stream */
767     _wrewinddir (dirp->wdirp);
768 }
769 
770 /* Convert multi-byte string to wide character string */
771 static int
dirent_mbstowcs_s(size_t * pReturnValue,wchar_t * wcstr,size_t sizeInWords,const char * mbstr,size_t count)772 dirent_mbstowcs_s(
773     size_t *pReturnValue,
774     wchar_t *wcstr,
775     size_t sizeInWords,
776     const char *mbstr,
777     size_t count)
778 {
779     int error;
780 
781 #if defined(_MSC_VER)  &&  _MSC_VER >= 1400
782 
783     /* Microsoft Visual Studio 2005 or later */
784     error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count);
785 
786 #else
787 
788     /* Older Visual Studio or non-Microsoft compiler */
789     size_t n;
790 
791     /* Convert to wide-character string */
792     n = mbstowcs (wcstr, mbstr, count);
793     if (n < sizeInWords) {
794 
795         /* Zero-terminate output buffer */
796         if (wcstr) {
797             wcstr[n] = 0;
798         }
799 
800         /* Length of resuting multi-byte string WITH zero terminator */
801         if (pReturnValue) {
802             *pReturnValue = n + 1;
803         }
804 
805         /* Success */
806         error = 0;
807 
808     } else {
809 
810         /* Could not convert string */
811         error = 1;
812 
813     }
814 
815 #endif
816 
817     return error;
818 }
819 
820 /* Convert wide-character string to multi-byte string */
821 static int
dirent_wcstombs_s(size_t * pReturnValue,char * mbstr,size_t sizeInBytes,const wchar_t * wcstr,size_t count)822 dirent_wcstombs_s(
823     size_t *pReturnValue,
824     char *mbstr,
825     size_t sizeInBytes,
826     const wchar_t *wcstr,
827     size_t count)
828 {
829     int error;
830 
831 #if defined(_MSC_VER)  &&  _MSC_VER >= 1400
832 
833     /* Microsoft Visual Studio 2005 or later */
834     error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count);
835 
836 #else
837 
838     /* Older Visual Studio or non-Microsoft compiler */
839     size_t n;
840 
841     /* Convert to multi-byte string */
842     n = wcstombs (mbstr, wcstr, count);
843     if (n < sizeInBytes) {
844 
845         /* Zero-terminate output buffer */
846         if (mbstr) {
847             mbstr[n] = '\0';
848         }
849 
850         /* Lenght of resulting multi-bytes string WITH zero-terminator */
851         if (pReturnValue) {
852             *pReturnValue = n + 1;
853         }
854 
855         /* Success */
856         error = 0;
857 
858     } else {
859 
860         /* Cannot convert string */
861         error = 1;
862 
863     }
864 
865 #endif
866 
867     return error;
868 }
869 
870 /* Set errno variable */
871 static void
dirent_set_errno(int error)872 dirent_set_errno(
873     int error)
874 {
875 #if defined(_MSC_VER)
876 
877     /* Microsoft Visual Studio */
878     _set_errno (error);
879 
880 #else
881 
882     /* Non-Microsoft compiler */
883     errno = error;
884 
885 #endif
886 }
887 
888 
889 #ifdef __cplusplus
890 }
891 #endif
892 #endif /*DIRENT_H*/
893 
894