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 <stdio.h>
22 #include <stdarg.h>
23 #include <wchar.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <malloc.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <errno.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 void *, const void *));
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 void *,const void *))875 scandir(
876     const char *dirname,
877     struct dirent ***namelist,
878     int (*filter)(const struct dirent*),
879     int (*compare)(const void*, const void*))
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*), compare);
968                     break;
969 
970                 }
971 
972             } else {
973                 /* Error reading directory entry */
974                 result = /*Error*/ -1;
975                 break;
976             }
977 
978         }
979 
980     } else {
981         /* Cannot open directory */
982         result = /*Error*/ -1;
983     }
984 
985     /* Release temporary directory entry */
986     if (tmp) {
987         free (tmp);
988     }
989 
990     /* Release allocated memory on error */
991     if (result < 0) {
992         for (i = 0; i < size; i++) {
993             free (files[i]);
994         }
995         free (files);
996         files = NULL;
997     }
998 
999     /* Close directory stream */
1000     if (dir) {
1001         closedir (dir);
1002     }
1003 
1004     /* Pass pointer table to caller */
1005     if (namelist) {
1006         *namelist = files;
1007     }
1008     return result;
1009 }
1010 
1011 /* Alphabetical sorting */
1012 static int
alphasort(const struct dirent ** a,const struct dirent ** b)1013 alphasort(
1014     const struct dirent **a, const struct dirent **b)
1015 {
1016     return strcoll ((*a)->d_name, (*b)->d_name);
1017 }
1018 
1019 /* Sort versions */
1020 static int
versionsort(const struct dirent ** a,const struct dirent ** b)1021 versionsort(
1022     const struct dirent **a, const struct dirent **b)
1023 {
1024     /* FIXME: implement strverscmp and use that */
1025     return alphasort (a, b);
1026 }
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 
1040 #if defined(_MSC_VER)  &&  _MSC_VER >= 1400
1041 
1042     /* Microsoft Visual Studio 2005 or later */
1043     error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count);
1044 
1045 #else
1046 
1047     /* Older Visual Studio or non-Microsoft compiler */
1048     size_t n;
1049 
1050     /* Convert to wide-character string (or count characters) */
1051     n = mbstowcs (wcstr, mbstr, sizeInWords);
1052     if (!wcstr  ||  n < count) {
1053 
1054         /* Zero-terminate output buffer */
1055         if (wcstr  &&  sizeInWords) {
1056             if (n >= sizeInWords) {
1057                 n = sizeInWords - 1;
1058             }
1059             wcstr[n] = 0;
1060         }
1061 
1062         /* Length of resulting multi-byte string WITH zero terminator */
1063         if (pReturnValue) {
1064             *pReturnValue = n + 1;
1065         }
1066 
1067         /* Success */
1068         error = 0;
1069 
1070     } else {
1071 
1072         /* Could not convert string */
1073         error = 1;
1074 
1075     }
1076 
1077 #endif
1078 
1079     return error;
1080 }
1081 
1082 /* Convert wide-character string to multi-byte string */
1083 static int
dirent_wcstombs_s(size_t * pReturnValue,char * mbstr,size_t sizeInBytes,const wchar_t * wcstr,size_t count)1084 dirent_wcstombs_s(
1085     size_t *pReturnValue,
1086     char *mbstr,
1087     size_t sizeInBytes, /* max size of mbstr */
1088     const wchar_t *wcstr,
1089     size_t count)
1090 {
1091     int error;
1092 
1093 #if defined(_MSC_VER)  &&  _MSC_VER >= 1400
1094 
1095     /* Microsoft Visual Studio 2005 or later */
1096     error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count);
1097 
1098 #else
1099 
1100     /* Older Visual Studio or non-Microsoft compiler */
1101     size_t n;
1102 
1103     /* Convert to multi-byte string (or count the number of bytes needed) */
1104     n = wcstombs (mbstr, wcstr, sizeInBytes);
1105     if (!mbstr  ||  n < count) {
1106 
1107         /* Zero-terminate output buffer */
1108         if (mbstr  &&  sizeInBytes) {
1109             if (n >= sizeInBytes) {
1110                 n = sizeInBytes - 1;
1111             }
1112             mbstr[n] = '\0';
1113         }
1114 
1115         /* Length of resulting multi-bytes string WITH zero-terminator */
1116         if (pReturnValue) {
1117             *pReturnValue = n + 1;
1118         }
1119 
1120         /* Success */
1121         error = 0;
1122 
1123     } else {
1124 
1125         /* Cannot convert string */
1126         error = 1;
1127 
1128     }
1129 
1130 #endif
1131 
1132     return error;
1133 }
1134 
1135 /* Set errno variable */
1136 static void
dirent_set_errno(int error)1137 dirent_set_errno(
1138     int error)
1139 {
1140 #if defined(_MSC_VER)  &&  _MSC_VER >= 1400
1141 
1142     /* Microsoft Visual Studio 2005 and later */
1143     _set_errno (error);
1144 
1145 #else
1146 
1147     /* Non-Microsoft compiler or older Microsoft compiler */
1148     errno = error;
1149 
1150 #endif
1151 }
1152 
1153 
1154 #ifdef __cplusplus
1155 }
1156 #endif
1157 #endif /*DIRENT_H*/
1158 
1159