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