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