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