1 /*
2  * Dirent interface for Microsoft Visual Studio
3  *
4  * Copyright (C) 2006-2012 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
8  */
9 #ifndef DIRENT_H
10 #define DIRENT_H
11 
12 /*
13  * Include windows.h without Windows Sockets 1.1 to prevent conflicts with
14  * Windows Sockets 2.0.
15  */
16 #ifndef WIN32_LEAN_AND_MEAN
17 #   define WIN32_LEAN_AND_MEAN
18 #endif
19 #include <windows.h>
20 
21 #include <errno.h>
22 #include <malloc.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <wchar.h>
30 
31 /* Indicates that d_type field is available in dirent structure */
32 #define _DIRENT_HAVE_D_TYPE
33 
34 /* Indicates that d_namlen field is available in dirent structure */
35 #define _DIRENT_HAVE_D_NAMLEN
36 
37 /* Entries missing from MSVC 6.0 */
38 #if !defined(FILE_ATTRIBUTE_DEVICE)
39 #   define FILE_ATTRIBUTE_DEVICE 0x40
40 #endif
41 
42 /* File type and permission flags for stat(), general mask */
43 #if !defined(S_IFMT)
44 #   define S_IFMT _S_IFMT
45 #endif
46 
47 /* Directory bit */
48 #if !defined(S_IFDIR)
49 #   define S_IFDIR _S_IFDIR
50 #endif
51 
52 /* Character device bit */
53 #if !defined(S_IFCHR)
54 #   define S_IFCHR _S_IFCHR
55 #endif
56 
57 /* Pipe bit */
58 #if !defined(S_IFFIFO)
59 #   define S_IFFIFO _S_IFFIFO
60 #endif
61 
62 /* Regular file bit */
63 #if !defined(S_IFREG)
64 #   define S_IFREG _S_IFREG
65 #endif
66 
67 /* Read permission */
68 #if !defined(S_IREAD)
69 #   define S_IREAD _S_IREAD
70 #endif
71 
72 /* Write permission */
73 #if !defined(S_IWRITE)
74 #   define S_IWRITE _S_IWRITE
75 #endif
76 
77 /* Execute permission */
78 #if !defined(S_IEXEC)
79 #   define S_IEXEC _S_IEXEC
80 #endif
81 
82 /* Pipe */
83 #if !defined(S_IFIFO)
84 #   define S_IFIFO _S_IFIFO
85 #endif
86 
87 /* Block device */
88 #if !defined(S_IFBLK)
89 #   define S_IFBLK 0
90 #endif
91 
92 /* Link */
93 #if !defined(S_IFLNK)
94 #   define S_IFLNK 0
95 #endif
96 
97 /* Socket */
98 #if !defined(S_IFSOCK)
99 #   define S_IFSOCK 0
100 #endif
101 
102 /* Read user permission */
103 #if !defined(S_IRUSR)
104 #   define S_IRUSR S_IREAD
105 #endif
106 
107 /* Write user permission */
108 #if !defined(S_IWUSR)
109 #   define S_IWUSR S_IWRITE
110 #endif
111 
112 /* Execute user permission */
113 #if !defined(S_IXUSR)
114 #   define S_IXUSR 0
115 #endif
116 
117 /* Read group permission */
118 #if !defined(S_IRGRP)
119 #   define S_IRGRP 0
120 #endif
121 
122 /* Write group permission */
123 #if !defined(S_IWGRP)
124 #   define S_IWGRP 0
125 #endif
126 
127 /* Execute group permission */
128 #if !defined(S_IXGRP)
129 #   define S_IXGRP 0
130 #endif
131 
132 /* Read others permission */
133 #if !defined(S_IROTH)
134 #   define S_IROTH 0
135 #endif
136 
137 /* Write others permission */
138 #if !defined(S_IWOTH)
139 #   define S_IWOTH 0
140 #endif
141 
142 /* Execute others permission */
143 #if !defined(S_IXOTH)
144 #   define S_IXOTH 0
145 #endif
146 
147 /* Maximum length of file name */
148 #if !defined(PATH_MAX)
149 #   define PATH_MAX MAX_PATH
150 #endif
151 #if !defined(FILENAME_MAX)
152 #   define FILENAME_MAX MAX_PATH
153 #endif
154 #if !defined(NAME_MAX)
155 #   define NAME_MAX FILENAME_MAX
156 #endif
157 
158 /* File type flags for d_type */
159 #define DT_UNKNOWN 0
160 #define DT_REG S_IFREG
161 #define DT_DIR S_IFDIR
162 #define DT_FIFO S_IFIFO
163 #define DT_SOCK S_IFSOCK
164 #define DT_CHR S_IFCHR
165 #define DT_BLK S_IFBLK
166 #define DT_LNK S_IFLNK
167 
168 /* Macros for converting between st_mode and d_type */
169 #define IFTODT(mode) ((mode) & S_IFMT)
170 #define DTTOIF(type) (type)
171 
172 /*
173  * File type macros.  Note that block devices, sockets and links cannot be
174  * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
175  * only defined for compatibility.  These macros should always return false
176  * on Windows.
177  */
178 #if !defined(S_ISFIFO)
179 #   define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
180 #endif
181 #if !defined(S_ISDIR)
182 #   define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
183 #endif
184 #if !defined(S_ISREG)
185 #   define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
186 #endif
187 #if !defined(S_ISLNK)
188 #   define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
189 #endif
190 #if !defined(S_ISSOCK)
191 #   define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
192 #endif
193 #if !defined(S_ISCHR)
194 #   define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
195 #endif
196 #if !defined(S_ISBLK)
197 #   define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
198 #endif
199 
200 /* Return the exact length of the file name without zero terminator */
201 #define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
202 
203 /* Return the maximum size of a file name */
204 #define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1)
205 
206 
207 #ifdef __cplusplus
208 extern "C" {
209 #endif
210 
211 
212 /* Wide-character version */
213 struct _wdirent {
214     /* Always zero */
215     long d_ino;
216 
217     /* File position within stream */
218     long d_off;
219 
220     /* Structure size */
221     unsigned short d_reclen;
222 
223     /* Length of name without \0 */
224     size_t d_namlen;
225 
226     /* File type */
227     int d_type;
228 
229     /* File name */
230     wchar_t d_name[PATH_MAX+1];
231 };
232 typedef struct _wdirent _wdirent;
233 
234 struct _WDIR {
235     /* Current directory entry */
236     struct _wdirent ent;
237 
238     /* Private file data */
239     WIN32_FIND_DATAW data;
240 
241     /* True if data is valid */
242     int cached;
243 
244     /* Win32 search handle */
245     HANDLE handle;
246 
247     /* Initial directory name */
248     wchar_t *patt;
249 };
250 typedef struct _WDIR _WDIR;
251 
252 /* Multi-byte character version */
253 struct dirent {
254     /* Always zero */
255     long d_ino;
256 
257     /* File position within stream */
258     long d_off;
259 
260     /* Structure size */
261     unsigned short d_reclen;
262 
263     /* Length of name without \0 */
264     size_t d_namlen;
265 
266     /* File type */
267     int d_type;
268 
269     /* File name */
270     char d_name[PATH_MAX+1];
271 };
272 typedef struct dirent dirent;
273 
274 struct DIR {
275     struct dirent ent;
276     struct _WDIR *wdirp;
277 };
278 typedef struct DIR DIR;
279 
280 
281 /* Dirent functions */
282 static DIR *opendir (const char *dirname);
283 static _WDIR *_wopendir (const wchar_t *dirname);
284 
285 static struct dirent *readdir (DIR *dirp);
286 static struct _wdirent *_wreaddir (_WDIR *dirp);
287 
288 static int readdir_r(
289     DIR *dirp, struct dirent *entry, struct dirent **result);
290 static int _wreaddir_r(
291     _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result);
292 
293 static int closedir (DIR *dirp);
294 static int _wclosedir (_WDIR *dirp);
295 
296 static void rewinddir (DIR* dirp);
297 static void _wrewinddir (_WDIR* dirp);
298 
299 static int scandir (const char *dirname, struct dirent ***namelist,
300     int (*filter)(const struct dirent*),
301     int (*compare)(const struct dirent**, const struct dirent**));
302 
303 static int alphasort (const struct dirent **a, const struct dirent **b);
304 
305 static int versionsort (const struct dirent **a, const struct dirent **b);
306 
307 
308 /* For compatibility with Symbian */
309 #define wdirent _wdirent
310 #define WDIR _WDIR
311 #define wopendir _wopendir
312 #define wreaddir _wreaddir
313 #define wclosedir _wclosedir
314 #define wrewinddir _wrewinddir
315 
316 
317 /* Internal utility functions */
318 static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp);
319 static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp);
320 
321 static int dirent_mbstowcs_s(
322     size_t *pReturnValue,
323     wchar_t *wcstr,
324     size_t sizeInWords,
325     const char *mbstr,
326     size_t count);
327 
328 static int dirent_wcstombs_s(
329     size_t *pReturnValue,
330     char *mbstr,
331     size_t sizeInBytes,
332     const wchar_t *wcstr,
333     size_t count);
334 
335 static void dirent_set_errno (int error);
336 
337 
338 /*
339  * Open directory stream DIRNAME for read and return a pointer to the
340  * internal working area that is used to retrieve individual directory
341  * entries.
342  */
343 static _WDIR*
_wopendir(const wchar_t * dirname)344 _wopendir(
345     const wchar_t *dirname)
346 {
347     _WDIR *dirp = NULL;
348     int error;
349 
350     /* Must have directory name */
351     if (dirname == NULL  ||  dirname[0] == '\0') {
352         dirent_set_errno (ENOENT);
353         return NULL;
354     }
355 
356     /* Allocate new _WDIR structure */
357     dirp = (_WDIR*) malloc (sizeof (struct _WDIR));
358     if (dirp != NULL) {
359         DWORD n;
360 
361         /* Reset _WDIR structure */
362         dirp->handle = INVALID_HANDLE_VALUE;
363         dirp->patt = NULL;
364         dirp->cached = 0;
365 
366         /* Compute the length of full path plus zero terminator
367          *
368          * Note that on WinRT there's no way to convert relative paths
369          * into absolute paths, so just assume it is an absolute path.
370          */
371 #       if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
372             n = wcslen(dirname);
373 #       else
374             n = GetFullPathNameW (dirname, 0, NULL, NULL);
375 #       endif
376 
377         /* Allocate room for absolute directory name and search pattern */
378         dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);
379         if (dirp->patt) {
380 
381             /*
382              * Convert relative directory name to an absolute one.  This
383              * allows rewinddir() to function correctly even when current
384              * working directory is changed between opendir() and rewinddir().
385              *
386              * Note that on WinRT there's no way to convert relative paths
387              * into absolute paths, so just assume it is an absolute path.
388              */
389 #           if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
390                 wcsncpy_s(dirp->patt, n+1, dirname, n);
391 #           else
392                 n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
393 #           endif
394             if (n > 0) {
395                 wchar_t *p;
396 
397                 /* Append search pattern \* to the directory name */
398                 p = dirp->patt + n;
399                 if (dirp->patt < p) {
400                     switch (p[-1]) {
401                     case '\\':
402                     case '/':
403                     case ':':
404                         /* Directory ends in path separator, e.g. c:\temp\ */
405                         /*NOP*/;
406                         break;
407 
408                     default:
409                         /* Directory name doesn't end in path separator */
410                         *p++ = '\\';
411                     }
412                 }
413                 *p++ = '*';
414                 *p = '\0';
415 
416                 /* Open directory stream and retrieve the first entry */
417                 if (dirent_first (dirp)) {
418                     /* Directory stream opened successfully */
419                     error = 0;
420                 } else {
421                     /* Cannot retrieve first entry */
422                     error = 1;
423                     dirent_set_errno (ENOENT);
424                 }
425 
426             } else {
427                 /* Cannot retrieve full path name */
428                 dirent_set_errno (ENOENT);
429                 error = 1;
430             }
431 
432         } else {
433             /* Cannot allocate memory for search pattern */
434             error = 1;
435         }
436 
437     } else {
438         /* Cannot allocate _WDIR structure */
439         error = 1;
440     }
441 
442     /* Clean up in case of error */
443     if (error  &&  dirp) {
444         _wclosedir (dirp);
445         dirp = NULL;
446     }
447 
448     return dirp;
449 }
450 
451 /*
452  * Read next directory entry.
453  *
454  * Returns pointer to static directory entry which may be overwritten by
455  * subsequent calls to _wreaddir().
456  */
457 static struct _wdirent*
_wreaddir(_WDIR * dirp)458 _wreaddir(
459     _WDIR *dirp)
460 {
461     struct _wdirent *entry;
462 
463     /*
464      * Read directory entry to buffer.  We can safely ignore the return value
465      * as entry will be set to NULL in case of error.
466      */
467     (void) _wreaddir_r (dirp, &dirp->ent, &entry);
468 
469     /* Return pointer to statically allocated directory entry */
470     return entry;
471 }
472 
473 /*
474  * Read next directory entry.
475  *
476  * Returns zero on success.  If end of directory stream is reached, then sets
477  * result to NULL and returns zero.
478  */
479 static int
_wreaddir_r(_WDIR * dirp,struct _wdirent * entry,struct _wdirent ** result)480 _wreaddir_r(
481     _WDIR *dirp,
482     struct _wdirent *entry,
483     struct _wdirent **result)
484 {
485     WIN32_FIND_DATAW *datap;
486 
487     /* Read next directory entry */
488     datap = dirent_next (dirp);
489     if (datap) {
490         size_t n;
491         DWORD attr;
492 
493         /*
494          * Copy file name as wide-character string.  If the file name is too
495          * long to fit in to the destination buffer, then truncate file name
496          * to PATH_MAX characters and zero-terminate the buffer.
497          */
498         n = 0;
499         while (n < PATH_MAX  &&  datap->cFileName[n] != 0) {
500             entry->d_name[n] = datap->cFileName[n];
501             n++;
502         }
503         entry->d_name[n] = 0;
504 
505         /* Length of file name excluding zero terminator */
506         entry->d_namlen = n;
507 
508         /* File type */
509         attr = datap->dwFileAttributes;
510         if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
511             entry->d_type = DT_CHR;
512         } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
513             entry->d_type = DT_DIR;
514         } else {
515             entry->d_type = DT_REG;
516         }
517 
518         /* Reset dummy fields */
519         entry->d_ino = 0;
520         entry->d_off = 0;
521         entry->d_reclen = sizeof (struct _wdirent);
522 
523         /* Set result address */
524         *result = entry;
525 
526     } else {
527 
528         /* Return NULL to indicate end of directory */
529         *result = NULL;
530 
531     }
532 
533     return /*OK*/0;
534 }
535 
536 /*
537  * Close directory stream opened by opendir() function.  This invalidates the
538  * DIR structure as well as any directory entry read previously by
539  * _wreaddir().
540  */
541 static int
_wclosedir(_WDIR * dirp)542 _wclosedir(
543     _WDIR *dirp)
544 {
545     int ok;
546     if (dirp) {
547 
548         /* Release search handle */
549         if (dirp->handle != INVALID_HANDLE_VALUE) {
550             FindClose (dirp->handle);
551             dirp->handle = INVALID_HANDLE_VALUE;
552         }
553 
554         /* Release search pattern */
555         if (dirp->patt) {
556             free (dirp->patt);
557             dirp->patt = NULL;
558         }
559 
560         /* Release directory structure */
561         free (dirp);
562         ok = /*success*/0;
563 
564     } else {
565 
566         /* Invalid directory stream */
567         dirent_set_errno (EBADF);
568         ok = /*failure*/-1;
569 
570     }
571     return ok;
572 }
573 
574 /*
575  * Rewind directory stream such that _wreaddir() returns the very first
576  * file name again.
577  */
578 static void
_wrewinddir(_WDIR * dirp)579 _wrewinddir(
580     _WDIR* dirp)
581 {
582     if (dirp) {
583         /* Release existing search handle */
584         if (dirp->handle != INVALID_HANDLE_VALUE) {
585             FindClose (dirp->handle);
586         }
587 
588         /* Open new search handle */
589         dirent_first (dirp);
590     }
591 }
592 
593 /* Get first directory entry (internal) */
594 static WIN32_FIND_DATAW*
dirent_first(_WDIR * dirp)595 dirent_first(
596     _WDIR *dirp)
597 {
598     WIN32_FIND_DATAW *datap;
599 
600     /* Open directory and retrieve the first entry */
601     dirp->handle = FindFirstFileExW(
602         dirp->patt, FindExInfoStandard, &dirp->data,
603         FindExSearchNameMatch, NULL, 0);
604     if (dirp->handle != INVALID_HANDLE_VALUE) {
605 
606         /* a directory entry is now waiting in memory */
607         datap = &dirp->data;
608         dirp->cached = 1;
609 
610     } else {
611 
612         /* Failed to re-open directory: no directory entry in memory */
613         dirp->cached = 0;
614         datap = NULL;
615 
616     }
617     return datap;
618 }
619 
620 /*
621  * Get next directory entry (internal).
622  *
623  * Returns
624  */
625 static WIN32_FIND_DATAW*
dirent_next(_WDIR * dirp)626 dirent_next(
627     _WDIR *dirp)
628 {
629     WIN32_FIND_DATAW *p;
630 
631     /* Get next directory entry */
632     if (dirp->cached != 0) {
633 
634         /* A valid directory entry already in memory */
635         p = &dirp->data;
636         dirp->cached = 0;
637 
638     } else if (dirp->handle != INVALID_HANDLE_VALUE) {
639 
640         /* Get the next directory entry from stream */
641         if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) {
642             /* Got a file */
643             p = &dirp->data;
644         } else {
645             /* The very last entry has been processed or an error occurred */
646             FindClose (dirp->handle);
647             dirp->handle = INVALID_HANDLE_VALUE;
648             p = NULL;
649         }
650 
651     } else {
652 
653         /* End of directory stream reached */
654         p = NULL;
655 
656     }
657 
658     return p;
659 }
660 
661 /*
662  * Open directory stream using plain old C-string.
663  */
664 static DIR*
opendir(const char * dirname)665 opendir(
666     const char *dirname)
667 {
668     struct DIR *dirp;
669     int error;
670 
671     /* Must have directory name */
672     if (dirname == NULL  ||  dirname[0] == '\0') {
673         dirent_set_errno (ENOENT);
674         return NULL;
675     }
676 
677     /* Allocate memory for DIR structure */
678     dirp = (DIR*) malloc (sizeof (struct DIR));
679     if (dirp) {
680         wchar_t wname[PATH_MAX + 1];
681         size_t n;
682 
683         /* Convert directory name to wide-character string */
684         error = dirent_mbstowcs_s(
685             &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1);
686         if (!error) {
687 
688             /* Open directory stream using wide-character name */
689             dirp->wdirp = _wopendir (wname);
690             if (dirp->wdirp) {
691                 /* Directory stream opened */
692                 error = 0;
693             } else {
694                 /* Failed to open directory stream */
695                 error = 1;
696             }
697 
698         } else {
699             /*
700              * Cannot convert file name to wide-character string.  This
701              * occurs if the string contains invalid multi-byte sequences or
702              * the output buffer is too small to contain the resulting
703              * string.
704              */
705             error = 1;
706         }
707 
708     } else {
709         /* Cannot allocate DIR structure */
710         error = 1;
711     }
712 
713     /* Clean up in case of error */
714     if (error  &&  dirp) {
715         free (dirp);
716         dirp = NULL;
717     }
718 
719     return dirp;
720 }
721 
722 /*
723  * Read next directory entry.
724  */
725 static struct dirent*
readdir(DIR * dirp)726 readdir(
727     DIR *dirp)
728 {
729     struct dirent *entry;
730 
731     /*
732      * Read directory entry to buffer.  We can safely ignore the return value
733      * as entry will be set to NULL in case of error.
734      */
735     (void) readdir_r (dirp, &dirp->ent, &entry);
736 
737     /* Return pointer to statically allocated directory entry */
738     return entry;
739 }
740 
741 /*
742  * Read next directory entry into called-allocated buffer.
743  *
744  * Returns zero on success.  If the end of directory stream is reached, then
745  * sets result to NULL and returns zero.
746  */
747 static int
readdir_r(DIR * dirp,struct dirent * entry,struct dirent ** result)748 readdir_r(
749     DIR *dirp,
750     struct dirent *entry,
751     struct dirent **result)
752 {
753     WIN32_FIND_DATAW *datap;
754 
755     /* Read next directory entry */
756     datap = dirent_next (dirp->wdirp);
757     if (datap) {
758         size_t n;
759         int error;
760 
761         /* Attempt to convert file name to multi-byte string */
762         error = dirent_wcstombs_s(
763             &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1);
764 
765         /*
766          * If the file name cannot be represented by a multi-byte string,
767          * then attempt to use old 8+3 file name.  This allows traditional
768          * Unix-code to access some file names despite of unicode
769          * characters, although file names may seem unfamiliar to the user.
770          *
771          * Be ware that the code below cannot come up with a short file
772          * name unless the file system provides one.  At least
773          * VirtualBox shared folders fail to do this.
774          */
775         if (error  &&  datap->cAlternateFileName[0] != '\0') {
776             error = dirent_wcstombs_s(
777                 &n, entry->d_name, PATH_MAX + 1,
778                 datap->cAlternateFileName, PATH_MAX + 1);
779         }
780 
781         if (!error) {
782             DWORD attr;
783 
784             /* Length of file name excluding zero terminator */
785             entry->d_namlen = n - 1;
786 
787             /* File attributes */
788             attr = datap->dwFileAttributes;
789             if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
790                 entry->d_type = DT_CHR;
791             } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
792                 entry->d_type = DT_DIR;
793             } else {
794                 entry->d_type = DT_REG;
795             }
796 
797             /* Reset dummy fields */
798             entry->d_ino = 0;
799             entry->d_off = 0;
800             entry->d_reclen = sizeof (struct dirent);
801 
802         } else {
803 
804             /*
805              * Cannot convert file name to multi-byte string so construct
806              * an erroneous directory entry and return that.  Note that
807              * we cannot return NULL as that would stop the processing
808              * of directory entries completely.
809              */
810             entry->d_name[0] = '?';
811             entry->d_name[1] = '\0';
812             entry->d_namlen = 1;
813             entry->d_type = DT_UNKNOWN;
814             entry->d_ino = 0;
815             entry->d_off = -1;
816             entry->d_reclen = 0;
817 
818         }
819 
820         /* Return pointer to directory entry */
821         *result = entry;
822 
823     } else {
824 
825         /* No more directory entries */
826         *result = NULL;
827 
828     }
829 
830     return /*OK*/0;
831 }
832 
833 /*
834  * Close directory stream.
835  */
836 static int
closedir(DIR * dirp)837 closedir(
838     DIR *dirp)
839 {
840     int ok;
841     if (dirp) {
842 
843         /* Close wide-character directory stream */
844         ok = _wclosedir (dirp->wdirp);
845         dirp->wdirp = NULL;
846 
847         /* Release multi-byte character version */
848         free (dirp);
849 
850     } else {
851 
852         /* Invalid directory stream */
853         dirent_set_errno (EBADF);
854         ok = /*failure*/-1;
855 
856     }
857     return ok;
858 }
859 
860 /*
861  * Rewind directory stream to beginning.
862  */
863 static void
rewinddir(DIR * dirp)864 rewinddir(
865     DIR* dirp)
866 {
867     /* Rewind wide-character string directory stream */
868     _wrewinddir (dirp->wdirp);
869 }
870 
871 /*
872  * Scan directory for entries.
873  */
874 static int
scandir(const char * dirname,struct dirent *** namelist,int (* filter)(const struct dirent *),int (* compare)(const struct dirent **,const struct dirent **))875 scandir(
876     const char *dirname,
877     struct dirent ***namelist,
878     int (*filter)(const struct dirent*),
879     int (*compare)(const struct dirent**, const struct dirent**))
880 {
881     struct dirent **files = NULL;
882     size_t size = 0;
883     size_t allocated = 0;
884     const size_t init_size = 1;
885     DIR *dir = NULL;
886     struct dirent *entry;
887     struct dirent *tmp = NULL;
888     size_t i;
889     int result = 0;
890 
891     /* Open directory stream */
892     dir = opendir (dirname);
893     if (dir) {
894 
895         /* Read directory entries to memory */
896         while (1) {
897 
898             /* Enlarge pointer table to make room for another pointer */
899             if (size >= allocated) {
900                 void *p;
901                 size_t num_entries;
902 
903                 /* Compute number of entries in the enlarged pointer table */
904                 if (size < init_size) {
905                     /* Allocate initial pointer table */
906                     num_entries = init_size;
907                 } else {
908                     /* Double the size */
909                     num_entries = size * 2;
910                 }
911 
912                 /* Allocate first pointer table or enlarge existing table */
913                 p = realloc (files, sizeof (void*) * num_entries);
914                 if (p != NULL) {
915                     /* Got the memory */
916                     files = (dirent**) p;
917                     allocated = num_entries;
918                 } else {
919                     /* Out of memory */
920                     result = -1;
921                     break;
922                 }
923 
924             }
925 
926             /* Allocate room for temporary directory entry */
927             if (tmp == NULL) {
928                 tmp = (struct dirent*) malloc (sizeof (struct dirent));
929                 if (tmp == NULL) {
930                     /* Cannot allocate temporary directory entry */
931                     result = -1;
932                     break;
933                 }
934             }
935 
936             /* Read directory entry to temporary area */
937             if (readdir_r (dir, tmp, &entry) == /*OK*/0) {
938 
939                 /* Did we get an entry? */
940                 if (entry != NULL) {
941                     int pass;
942 
943                     /* Determine whether to include the entry in result */
944                     if (filter) {
945                         /* Let the filter function decide */
946                         pass = filter (tmp);
947                     } else {
948                         /* No filter function, include everything */
949                         pass = 1;
950                     }
951 
952                     if (pass) {
953                         /* Store the temporary entry to pointer table */
954                         files[size++] = tmp;
955                         tmp = NULL;
956 
957                         /* Keep up with the number of files */
958                         result++;
959                     }
960 
961                 } else {
962 
963                     /*
964                      * End of directory stream reached => sort entries and
965                      * exit.
966                      */
967                     qsort (files, size, sizeof (void*),
968                         (int (*) (const void*, const void*)) compare);
969                     break;
970 
971                 }
972 
973             } else {
974                 /* Error reading directory entry */
975                 result = /*Error*/ -1;
976                 break;
977             }
978 
979         }
980 
981     } else {
982         /* Cannot open directory */
983         result = /*Error*/ -1;
984     }
985 
986     /* Release temporary directory entry */
987     if (tmp) {
988         free (tmp);
989     }
990 
991     /* Release allocated memory on error */
992     if (result < 0) {
993         for (i = 0; i < size; i++) {
994             free (files[i]);
995         }
996         free (files);
997         files = NULL;
998     }
999 
1000     /* Close directory stream */
1001     if (dir) {
1002         closedir (dir);
1003     }
1004 
1005     /* Pass pointer table to caller */
1006     if (namelist) {
1007         *namelist = files;
1008     }
1009     return result;
1010 }
1011 
1012 /* Alphabetical sorting */
1013 static int
alphasort(const struct dirent ** a,const struct dirent ** b)1014 alphasort(
1015     const struct dirent **a, const struct dirent **b)
1016 {
1017     return strcoll ((*a)->d_name, (*b)->d_name);
1018 }
1019 
1020 /* Sort versions */
1021 static int
versionsort(const struct dirent ** a,const struct dirent ** b)1022 versionsort(
1023     const struct dirent **a, const struct dirent **b)
1024 {
1025     /* FIXME: implement strverscmp and use that */
1026     return alphasort (a, b);
1027 }
1028 
1029 /* Convert multi-byte string to wide character string */
1030 static int
dirent_mbstowcs_s(size_t * pReturnValue,wchar_t * wcstr,size_t sizeInWords,const char * mbstr,size_t count)1031 dirent_mbstowcs_s(
1032     size_t *pReturnValue,
1033     wchar_t *wcstr,
1034     size_t sizeInWords,
1035     const char *mbstr,
1036     size_t count)
1037 {
1038     int error;
1039     int n;
1040     size_t len;
1041     UINT cp;
1042     DWORD flags;
1043 
1044     /* Determine code page for multi-byte string */
1045     if (AreFileApisANSI ()) {
1046         /* Default ANSI code page */
1047         cp = GetACP ();
1048     } else {
1049         /* Default OEM code page */
1050         cp = GetOEMCP ();
1051     }
1052 
1053     /*
1054      * Determine flags based on the character set.  For more information,
1055      * please see https://docs.microsoft.com/fi-fi/windows/desktop/api/stringapiset/nf-stringapiset-multibytetowidechar
1056      */
1057     switch (cp) {
1058     case 42:
1059     case 50220:
1060     case 50221:
1061     case 50222:
1062     case 50225:
1063     case 50227:
1064     case 50229:
1065     case 57002:
1066     case 57003:
1067     case 57004:
1068     case 57005:
1069     case 57006:
1070     case 57007:
1071     case 57008:
1072     case 57009:
1073     case 57010:
1074     case 57011:
1075     case 65000:
1076         /* MultiByteToWideChar does not support MB_ERR_INVALID_CHARS */
1077         flags = 0;
1078         break;
1079 
1080     default:
1081         /*
1082          * Ask MultiByteToWideChar to return an error if a multi-byte
1083          * character cannot be converted to a wide-character.
1084          */
1085         flags = MB_ERR_INVALID_CHARS;
1086     }
1087 
1088     /* Compute the length of input string without zero-terminator */
1089     len = 0;
1090     while (mbstr[len] != '\0'  &&  len < count) {
1091         len++;
1092     }
1093 
1094     /* Convert to wide-character string */
1095     n = MultiByteToWideChar(
1096         /* Source code page */ cp,
1097         /* Flags */ flags,
1098         /* Pointer to string to convert */ mbstr,
1099         /* Size of multi-byte string */ (int) len,
1100         /* Pointer to output buffer */ wcstr,
1101         /* Size of output buffer */ (int)sizeInWords - 1
1102     );
1103 
1104     /* Ensure that output buffer is zero-terminated */
1105     wcstr[n] = '\0';
1106 
1107     /* Return length of wide-character string with zero-terminator */
1108     *pReturnValue = (size_t) (n + 1);
1109 
1110     /* Return zero if conversion succeeded */
1111     if (n > 0) {
1112         error = 0;
1113     } else {
1114         error = 1;
1115     }
1116     return error;
1117 }
1118 
1119 /* Convert wide-character string to multi-byte string */
1120 static int
dirent_wcstombs_s(size_t * pReturnValue,char * mbstr,size_t sizeInBytes,const wchar_t * wcstr,size_t count)1121 dirent_wcstombs_s(
1122     size_t *pReturnValue,
1123     char *mbstr,
1124     size_t sizeInBytes, /* max size of mbstr */
1125     const wchar_t *wcstr,
1126     size_t count)
1127 {
1128     int n;
1129     int error;
1130     UINT cp;
1131     size_t len;
1132     BOOL flag = 0;
1133     LPBOOL pflag;
1134 
1135     /* Determine code page for multi-byte string */
1136     if (AreFileApisANSI ()) {
1137         /* Default ANSI code page */
1138         cp = GetACP ();
1139     } else {
1140         /* Default OEM code page */
1141         cp = GetOEMCP ();
1142     }
1143 
1144     /* Compute the length of input string without zero-terminator */
1145     len = 0;
1146     while (wcstr[len] != '\0'  &&  len < count) {
1147         len++;
1148     }
1149 
1150     /*
1151      * Determine if we can ask WideCharToMultiByte to return information on
1152      * broken characters.  For more information, please see
1153      * https://docs.microsoft.com/en-us/windows/desktop/api/stringapiset/nf-stringapiset-widechartomultibyte
1154      */
1155     switch (cp) {
1156     case CP_UTF7:
1157     case CP_UTF8:
1158         /*
1159          * WideCharToMultiByte fails if we request information on default
1160          * characters.  This is just a nuisance but doesn't affect the
1161          * conversion: if Windows is configured to use UTF-8, then the default
1162          * character should not be needed anyway.
1163          */
1164         pflag = NULL;
1165         break;
1166 
1167     default:
1168         /*
1169          * Request that WideCharToMultiByte sets the flag if it uses the
1170          * default character.
1171          */
1172         pflag = &flag;
1173     }
1174 
1175     /* Convert wide-character string to multi-byte character string */
1176     n = WideCharToMultiByte(
1177         /* Target code page */ cp,
1178         /* Flags */ 0,
1179         /* Pointer to unicode string */ wcstr,
1180         /* Length of unicode string */ (int) len,
1181         /* Pointer to output buffer */ mbstr,
1182         /* Size of output buffer */ (int)sizeInBytes - 1,
1183         /* Default character */ NULL,
1184         /* Whether default character was used or not */ pflag
1185     );
1186 
1187     /* Ensure that output buffer is zero-terminated */
1188     mbstr[n] = '\0';
1189 
1190     /* Return length of multi-byte string with zero-terminator */
1191     *pReturnValue = (size_t) (n + 1);
1192 
1193     /* Return zero if conversion succeeded without using default characters */
1194     if (n > 0  &&  flag == 0) {
1195         error = 0;
1196     } else {
1197         error = 1;
1198     }
1199     return error;
1200 }
1201 
1202 /* Set errno variable */
1203 static void
dirent_set_errno(int error)1204 dirent_set_errno(
1205     int error)
1206 {
1207 #if defined(_MSC_VER)  &&  _MSC_VER >= 1400
1208 
1209     /* Microsoft Visual Studio 2005 and later */
1210     _set_errno (error);
1211 
1212 #else
1213 
1214     /* Non-Microsoft compiler or older Microsoft compiler */
1215     errno = error;
1216 
1217 #endif
1218 }
1219 
1220 
1221 #ifdef __cplusplus
1222 }
1223 #endif
1224 #endif /*DIRENT_H*/
1225