|
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 /* |
|
103 * Symbolic link. Be ware that S_IFLNK value and S_ISLNK() macro are only |
|
104 * usable with dirent - they do not work with stat() function call! |
|
105 */ |
|
106 #if !defined(S_IFLNK) |
|
107 # define S_IFLNK (_S_IFDIR | _S_IFREG) |
|
108 #endif |
|
109 |
|
110 /* Socket */ |
|
111 #if !defined(S_IFSOCK) |
|
112 # define S_IFSOCK 0 |
|
113 #endif |
|
114 |
|
115 /* Read user permission */ |
|
116 #if !defined(S_IRUSR) |
|
117 # define S_IRUSR S_IREAD |
|
118 #endif |
|
119 |
|
120 /* Write user permission */ |
|
121 #if !defined(S_IWUSR) |
|
122 # define S_IWUSR S_IWRITE |
|
123 #endif |
|
124 |
|
125 /* Execute user permission */ |
|
126 #if !defined(S_IXUSR) |
|
127 # define S_IXUSR 0 |
|
128 #endif |
|
129 |
|
130 /* User full permissions */ |
|
131 #if !defined(S_IRWXU) |
|
132 # define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) |
|
133 #endif |
|
134 |
|
135 /* Read group permission */ |
|
136 #if !defined(S_IRGRP) |
|
137 # define S_IRGRP 0 |
|
138 #endif |
|
139 |
|
140 /* Write group permission */ |
|
141 #if !defined(S_IWGRP) |
|
142 # define S_IWGRP 0 |
|
143 #endif |
|
144 |
|
145 /* Execute group permission */ |
|
146 #if !defined(S_IXGRP) |
|
147 # define S_IXGRP 0 |
|
148 #endif |
|
149 |
|
150 /* Group full permissions */ |
|
151 #if !defined(S_IRWXG) |
|
152 # define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP) |
|
153 #endif |
|
154 |
|
155 /* Read others permission */ |
|
156 #if !defined(S_IROTH) |
|
157 # define S_IROTH 0 |
|
158 #endif |
|
159 |
|
160 /* Write others permission */ |
|
161 #if !defined(S_IWOTH) |
|
162 # define S_IWOTH 0 |
|
163 #endif |
|
164 |
|
165 /* Execute others permission */ |
|
166 #if !defined(S_IXOTH) |
|
167 # define S_IXOTH 0 |
|
168 #endif |
|
169 |
|
170 /* Other full permissions */ |
|
171 #if !defined(S_IRWXO) |
|
172 # define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH) |
|
173 #endif |
|
174 |
|
175 /* Maximum length of file name */ |
|
176 #if !defined(PATH_MAX) |
|
177 # define PATH_MAX MAX_PATH |
|
178 #endif |
|
179 #if !defined(FILENAME_MAX) |
|
180 # define FILENAME_MAX MAX_PATH |
|
181 #endif |
|
182 #if !defined(NAME_MAX) |
|
183 # define NAME_MAX FILENAME_MAX |
|
184 #endif |
|
185 |
|
186 /* File type flags for d_type */ |
|
187 #define DT_UNKNOWN 0 |
|
188 #define DT_REG S_IFREG |
|
189 #define DT_DIR S_IFDIR |
|
190 #define DT_FIFO S_IFIFO |
|
191 #define DT_SOCK S_IFSOCK |
|
192 #define DT_CHR S_IFCHR |
|
193 #define DT_BLK S_IFBLK |
|
194 #define DT_LNK S_IFLNK |
|
195 |
|
196 /* Macros for converting between st_mode and d_type */ |
|
197 #define IFTODT(mode) ((mode) & S_IFMT) |
|
198 #define DTTOIF(type) (type) |
|
199 |
|
200 /* |
|
201 * File type macros. Note that block devices and sockets cannot be |
|
202 * distinguished on Windows, and the macros S_ISBLK and S_ISSOCK are only |
|
203 * defined for compatibility. These macros should always return false on |
|
204 * Windows. |
|
205 */ |
|
206 #if !defined(S_ISFIFO) |
|
207 # define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) |
|
208 #endif |
|
209 #if !defined(S_ISDIR) |
|
210 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) |
|
211 #endif |
|
212 #if !defined(S_ISREG) |
|
213 # define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) |
|
214 #endif |
|
215 #if !defined(S_ISLNK) |
|
216 # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) |
|
217 #endif |
|
218 #if !defined(S_ISSOCK) |
|
219 # define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) |
|
220 #endif |
|
221 #if !defined(S_ISCHR) |
|
222 # define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) |
|
223 #endif |
|
224 #if !defined(S_ISBLK) |
|
225 # define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) |
|
226 #endif |
|
227 |
|
228 /* Return the exact length of the file name without zero terminator */ |
|
229 #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) |
|
230 |
|
231 /* Return the maximum size of a file name */ |
|
232 #define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) |
|
233 |
|
234 |
|
235 #ifdef __cplusplus |
|
236 extern "C" { |
|
237 #endif |
|
238 |
|
239 |
|
240 /* Wide-character version */ |
|
241 struct _wdirent { |
|
242 /* Always zero */ |
|
243 long d_ino; |
|
244 |
|
245 /* Position of next file in a directory stream */ |
|
246 long d_off; |
|
247 |
|
248 /* Structure size */ |
|
249 unsigned short d_reclen; |
|
250 |
|
251 /* Length of name without \0 */ |
|
252 size_t d_namlen; |
|
253 |
|
254 /* File type */ |
|
255 int d_type; |
|
256 |
|
257 /* File name */ |
|
258 wchar_t d_name[PATH_MAX+1]; |
|
259 }; |
|
260 typedef struct _wdirent _wdirent; |
|
261 |
|
262 struct _WDIR { |
|
263 /* Current directory entry */ |
|
264 struct _wdirent ent; |
|
265 |
|
266 /* Private file data */ |
|
267 WIN32_FIND_DATAW data; |
|
268 |
|
269 /* True if data is valid */ |
|
270 int cached; |
|
271 |
|
272 /* True if next entry is invalid */ |
|
273 int invalid; |
|
274 |
|
275 /* Win32 search handle */ |
|
276 HANDLE handle; |
|
277 |
|
278 /* Initial directory name */ |
|
279 wchar_t *patt; |
|
280 }; |
|
281 typedef struct _WDIR _WDIR; |
|
282 |
|
283 /* Multi-byte character version */ |
|
284 struct dirent { |
|
285 /* Always zero */ |
|
286 long d_ino; |
|
287 |
|
288 /* Position of next file in a directory stream */ |
|
289 long d_off; |
|
290 |
|
291 /* Structure size */ |
|
292 unsigned short d_reclen; |
|
293 |
|
294 /* Length of name without \0 */ |
|
295 size_t d_namlen; |
|
296 |
|
297 /* File type */ |
|
298 int d_type; |
|
299 |
|
300 /* File name */ |
|
301 char d_name[PATH_MAX+1]; |
|
302 }; |
|
303 typedef struct dirent dirent; |
|
304 |
|
305 struct DIR { |
|
306 struct dirent ent; |
|
307 struct _WDIR *wdirp; |
|
308 }; |
|
309 typedef struct DIR DIR; |
|
310 |
|
311 |
|
312 /* Dirent functions */ |
|
313 static DIR *opendir(const char *dirname); |
|
314 static _WDIR *_wopendir(const wchar_t *dirname); |
|
315 |
|
316 static struct dirent *readdir(DIR *dirp); |
|
317 static struct _wdirent *_wreaddir(_WDIR *dirp); |
|
318 |
|
319 static int readdir_r( |
|
320 DIR *dirp, struct dirent *entry, struct dirent **result); |
|
321 static int _wreaddir_r( |
|
322 _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); |
|
323 |
|
324 static int closedir(DIR *dirp); |
|
325 static int _wclosedir(_WDIR *dirp); |
|
326 |
|
327 static void rewinddir(DIR *dirp); |
|
328 static void _wrewinddir(_WDIR *dirp); |
|
329 |
|
330 static long telldir(DIR *dirp); |
|
331 static long _wtelldir(_WDIR *dirp); |
|
332 |
|
333 static void seekdir(DIR *dirp, long loc); |
|
334 static void _wseekdir(_WDIR *dirp, long loc); |
|
335 |
|
336 static int scandir(const char *dirname, struct dirent ***namelist, |
|
337 int (*filter)(const struct dirent*), |
|
338 int (*compare)(const struct dirent**, const struct dirent**)); |
|
339 |
|
340 static int alphasort(const struct dirent **a, const struct dirent **b); |
|
341 |
|
342 static int versionsort(const struct dirent **a, const struct dirent **b); |
|
343 |
|
344 static int strverscmp(const char *a, const char *b); |
|
345 |
|
346 /* For compatibility with Symbian */ |
|
347 #define wdirent _wdirent |
|
348 #define WDIR _WDIR |
|
349 #define wopendir _wopendir |
|
350 #define wreaddir _wreaddir |
|
351 #define wclosedir _wclosedir |
|
352 #define wrewinddir _wrewinddir |
|
353 #define wtelldir _wtelldir |
|
354 #define wseekdir _wseekdir |
|
355 |
|
356 /* Compatibility with older Microsoft compilers and non-Microsoft compilers */ |
|
357 #if !defined(_MSC_VER) || _MSC_VER < 1400 |
|
358 # define wcstombs_s dirent_wcstombs_s |
|
359 # define mbstowcs_s dirent_mbstowcs_s |
|
360 #endif |
|
361 |
|
362 /* Optimize dirent_set_errno() away on modern Microsoft compilers */ |
|
363 #if defined(_MSC_VER) && _MSC_VER >= 1400 |
|
364 # define dirent_set_errno _set_errno |
|
365 #endif |
|
366 |
|
367 |
|
368 /* Internal utility functions */ |
|
369 static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp); |
|
370 static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp); |
|
371 static long dirent_hash(WIN32_FIND_DATAW *datap); |
|
372 |
|
373 #if !defined(_MSC_VER) || _MSC_VER < 1400 |
|
374 static int dirent_mbstowcs_s( |
|
375 size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, |
|
376 const char *mbstr, size_t count); |
|
377 #endif |
|
378 |
|
379 #if !defined(_MSC_VER) || _MSC_VER < 1400 |
|
380 static int dirent_wcstombs_s( |
|
381 size_t *pReturnValue, char *mbstr, size_t sizeInBytes, |
|
382 const wchar_t *wcstr, size_t count); |
|
383 #endif |
|
384 |
|
385 #if !defined(_MSC_VER) || _MSC_VER < 1400 |
|
386 static void dirent_set_errno(int error); |
|
387 #endif |
|
388 |
|
389 |
|
390 /* |
|
391 * Open directory stream DIRNAME for read and return a pointer to the |
|
392 * internal working area that is used to retrieve individual directory |
|
393 * entries. |
|
394 */ |
|
395 static _WDIR * |
|
396 _wopendir(const wchar_t *dirname) |
|
397 { |
|
398 wchar_t *p; |
|
399 |
|
400 /* Must have directory name */ |
|
401 if (dirname == NULL || dirname[0] == '\0') { |
|
402 dirent_set_errno(ENOENT); |
|
403 return NULL; |
|
404 } |
|
405 |
|
406 /* Allocate new _WDIR structure */ |
|
407 _WDIR *dirp = (_WDIR*) malloc(sizeof(struct _WDIR)); |
|
408 if (!dirp) |
|
409 return NULL; |
|
410 |
|
411 /* Reset _WDIR structure */ |
|
412 dirp->handle = INVALID_HANDLE_VALUE; |
|
413 dirp->patt = NULL; |
|
414 dirp->cached = 0; |
|
415 dirp->invalid = 0; |
|
416 |
|
417 /* |
|
418 * Compute the length of full path plus zero terminator |
|
419 * |
|
420 * Note that on WinRT there's no way to convert relative paths |
|
421 * into absolute paths, so just assume it is an absolute path. |
|
422 */ |
|
423 #if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) |
|
424 /* Desktop */ |
|
425 DWORD n = GetFullPathNameW(dirname, 0, NULL, NULL); |
|
426 #else |
|
427 /* WinRT */ |
|
428 size_t n = wcslen(dirname); |
|
429 #endif |
|
430 |
|
431 /* Allocate room for absolute directory name and search pattern */ |
|
432 dirp->patt = (wchar_t*) malloc(sizeof(wchar_t) * n + 16); |
|
433 if (dirp->patt == NULL) |
|
434 goto exit_closedir; |
|
435 |
|
436 /* |
|
437 * Convert relative directory name to an absolute one. This |
|
438 * allows rewinddir() to function correctly even when current |
|
439 * working directory is changed between opendir() and rewinddir(). |
|
440 * |
|
441 * Note that on WinRT there's no way to convert relative paths |
|
442 * into absolute paths, so just assume it is an absolute path. |
|
443 */ |
|
444 #if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) |
|
445 /* Desktop */ |
|
446 n = GetFullPathNameW(dirname, n, dirp->patt, NULL); |
|
447 if (n <= 0) |
|
448 goto exit_closedir; |
|
449 #else |
|
450 /* WinRT */ |
|
451 wcsncpy_s(dirp->patt, n+1, dirname, n); |
|
452 #endif |
|
453 |
|
454 /* Append search pattern \* to the directory name */ |
|
455 p = dirp->patt + n; |
|
456 switch (p[-1]) { |
|
457 case '\\': |
|
458 case '/': |
|
459 case ':': |
|
460 /* Directory ends in path separator, e.g. c:\temp\ */ |
|
461 /*NOP*/; |
|
462 break; |
|
463 |
|
464 default: |
|
465 /* Directory name doesn't end in path separator */ |
|
466 *p++ = '\\'; |
|
467 } |
|
468 *p++ = '*'; |
|
469 *p = '\0'; |
|
470 |
|
471 /* Open directory stream and retrieve the first entry */ |
|
472 if (!dirent_first(dirp)) |
|
473 goto exit_closedir; |
|
474 |
|
475 /* Success */ |
|
476 return dirp; |
|
477 |
|
478 /* Failure */ |
|
479 exit_closedir: |
|
480 _wclosedir(dirp); |
|
481 return NULL; |
|
482 } |
|
483 |
|
484 /* |
|
485 * Read next directory entry. |
|
486 * |
|
487 * Returns pointer to static directory entry which may be overwritten by |
|
488 * subsequent calls to _wreaddir(). |
|
489 */ |
|
490 static struct _wdirent * |
|
491 _wreaddir(_WDIR *dirp) |
|
492 { |
|
493 /* |
|
494 * Read directory entry to buffer. We can safely ignore the return |
|
495 * value as entry will be set to NULL in case of error. |
|
496 */ |
|
497 struct _wdirent *entry; |
|
498 (void) _wreaddir_r(dirp, &dirp->ent, &entry); |
|
499 |
|
500 /* Return pointer to statically allocated directory entry */ |
|
501 return entry; |
|
502 } |
|
503 |
|
504 /* |
|
505 * Read next directory entry. |
|
506 * |
|
507 * Returns zero on success. If end of directory stream is reached, then sets |
|
508 * result to NULL and returns zero. |
|
509 */ |
|
510 static int |
|
511 _wreaddir_r( |
|
512 _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result) |
|
513 { |
|
514 /* Validate directory handle */ |
|
515 if (!dirp || dirp->handle == INVALID_HANDLE_VALUE || !dirp->patt) { |
|
516 dirent_set_errno(EBADF); |
|
517 *result = NULL; |
|
518 return -1; |
|
519 } |
|
520 |
|
521 /* Read next directory entry */ |
|
522 WIN32_FIND_DATAW *datap = dirent_next(dirp); |
|
523 if (!datap) { |
|
524 /* Return NULL to indicate end of directory */ |
|
525 *result = NULL; |
|
526 return /*OK*/0; |
|
527 } |
|
528 |
|
529 /* |
|
530 * Copy file name as wide-character string. If the file name is too |
|
531 * long to fit in to the destination buffer, then truncate file name |
|
532 * to PATH_MAX characters and zero-terminate the buffer. |
|
533 */ |
|
534 size_t i = 0; |
|
535 while (i < PATH_MAX && datap->cFileName[i] != 0) { |
|
536 entry->d_name[i] = datap->cFileName[i]; |
|
537 i++; |
|
538 } |
|
539 entry->d_name[i] = 0; |
|
540 |
|
541 /* Length of file name excluding zero terminator */ |
|
542 entry->d_namlen = i; |
|
543 |
|
544 /* Determine file type */ |
|
545 DWORD attr = datap->dwFileAttributes; |
|
546 if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) |
|
547 entry->d_type = DT_CHR; |
|
548 else if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) |
|
549 entry->d_type = DT_LNK; |
|
550 else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) |
|
551 entry->d_type = DT_DIR; |
|
552 else |
|
553 entry->d_type = DT_REG; |
|
554 |
|
555 /* Read the next directory entry to cache */ |
|
556 datap = dirent_next(dirp); |
|
557 if (datap) { |
|
558 /* Compute 31-bit hash of the next directory entry */ |
|
559 entry->d_off = dirent_hash(datap); |
|
560 |
|
561 /* Push the next directory entry back to cache */ |
|
562 dirp->cached = 1; |
|
563 } else { |
|
564 /* End of directory stream */ |
|
565 entry->d_off = (long) ((~0UL) >> 1); |
|
566 } |
|
567 |
|
568 /* Reset other fields */ |
|
569 entry->d_ino = 0; |
|
570 entry->d_reclen = sizeof(struct _wdirent); |
|
571 |
|
572 /* Set result address */ |
|
573 *result = entry; |
|
574 return /*OK*/0; |
|
575 } |
|
576 |
|
577 /* |
|
578 * Close directory stream opened by opendir() function. This invalidates the |
|
579 * DIR structure as well as any directory entry read previously by |
|
580 * _wreaddir(). |
|
581 */ |
|
582 static int |
|
583 _wclosedir(_WDIR *dirp) |
|
584 { |
|
585 if (!dirp) { |
|
586 dirent_set_errno(EBADF); |
|
587 return /*failure*/-1; |
|
588 } |
|
589 |
|
590 /* |
|
591 * Release search handle if we have one. Being able to handle |
|
592 * partially initialized _WDIR structure allows us to use this |
|
593 * function to handle errors occurring within _wopendir. |
|
594 */ |
|
595 if (dirp->handle != INVALID_HANDLE_VALUE) { |
|
596 FindClose(dirp->handle); |
|
597 } |
|
598 |
|
599 /* |
|
600 * Release search pattern. Note that we don't need to care if |
|
601 * dirp->patt is NULL or not: function free is guaranteed to act |
|
602 * appropriately. |
|
603 */ |
|
604 free(dirp->patt); |
|
605 |
|
606 /* Release directory structure */ |
|
607 free(dirp); |
|
608 return /*success*/0; |
|
609 } |
|
610 |
|
611 /* |
|
612 * Rewind directory stream such that _wreaddir() returns the very first |
|
613 * file name again. |
|
614 */ |
|
615 static void _wrewinddir(_WDIR* dirp) |
|
616 { |
|
617 /* Check directory pointer */ |
|
618 if (!dirp || dirp->handle == INVALID_HANDLE_VALUE || !dirp->patt) |
|
619 return; |
|
620 |
|
621 /* Release existing search handle */ |
|
622 FindClose(dirp->handle); |
|
623 |
|
624 /* Open new search handle */ |
|
625 dirent_first(dirp); |
|
626 } |
|
627 |
|
628 /* Get first directory entry */ |
|
629 static WIN32_FIND_DATAW * |
|
630 dirent_first(_WDIR *dirp) |
|
631 { |
|
632 /* Open directory and retrieve the first entry */ |
|
633 dirp->handle = FindFirstFileExW( |
|
634 dirp->patt, FindExInfoStandard, &dirp->data, |
|
635 FindExSearchNameMatch, NULL, 0); |
|
636 if (dirp->handle == INVALID_HANDLE_VALUE) |
|
637 goto error; |
|
638 |
|
639 /* A directory entry is now waiting in memory */ |
|
640 dirp->cached = 1; |
|
641 return &dirp->data; |
|
642 |
|
643 error: |
|
644 /* Failed to open directory: no directory entry in memory */ |
|
645 dirp->cached = 0; |
|
646 dirp->invalid = 1; |
|
647 |
|
648 /* Set error code */ |
|
649 DWORD errorcode = GetLastError(); |
|
650 switch (errorcode) { |
|
651 case ERROR_ACCESS_DENIED: |
|
652 /* No read access to directory */ |
|
653 dirent_set_errno(EACCES); |
|
654 break; |
|
655 |
|
656 case ERROR_DIRECTORY: |
|
657 /* Directory name is invalid */ |
|
658 dirent_set_errno(ENOTDIR); |
|
659 break; |
|
660 |
|
661 case ERROR_PATH_NOT_FOUND: |
|
662 default: |
|
663 /* Cannot find the file */ |
|
664 dirent_set_errno(ENOENT); |
|
665 } |
|
666 return NULL; |
|
667 } |
|
668 |
|
669 /* Get next directory entry */ |
|
670 static WIN32_FIND_DATAW * |
|
671 dirent_next(_WDIR *dirp) |
|
672 { |
|
673 /* Return NULL if seek position was invalid */ |
|
674 if (dirp->invalid) |
|
675 return NULL; |
|
676 |
|
677 /* Is the next directory entry already in cache? */ |
|
678 if (dirp->cached) { |
|
679 /* Yes, a valid directory entry found in memory */ |
|
680 dirp->cached = 0; |
|
681 return &dirp->data; |
|
682 } |
|
683 |
|
684 /* Read the next directory entry from stream */ |
|
685 if (FindNextFileW(dirp->handle, &dirp->data) == FALSE) { |
|
686 /* End of directory stream */ |
|
687 return NULL; |
|
688 } |
|
689 |
|
690 /* Success */ |
|
691 return &dirp->data; |
|
692 } |
|
693 |
|
694 /* |
|
695 * Compute 31-bit hash of file name. |
|
696 * |
|
697 * See djb2 at http://www.cse.yorku.ca/~oz/hash.html |
|
698 */ |
|
699 static long |
|
700 dirent_hash(WIN32_FIND_DATAW *datap) |
|
701 { |
|
702 unsigned long hash = 5381; |
|
703 unsigned long c; |
|
704 const wchar_t *p = datap->cFileName; |
|
705 const wchar_t *e = p + MAX_PATH; |
|
706 while (p != e && (c = *p++) != 0) { |
|
707 hash = (hash << 5) + hash + c; |
|
708 } |
|
709 |
|
710 return (long) (hash & ((~0UL) >> 1)); |
|
711 } |
|
712 |
|
713 /* Open directory stream using plain old C-string */ |
|
714 static DIR *opendir(const char *dirname) |
|
715 { |
|
716 /* Must have directory name */ |
|
717 if (dirname == NULL || dirname[0] == '\0') { |
|
718 dirent_set_errno(ENOENT); |
|
719 return NULL; |
|
720 } |
|
721 |
|
722 /* Allocate memory for DIR structure */ |
|
723 struct DIR *dirp = (DIR*) malloc(sizeof(struct DIR)); |
|
724 if (!dirp) |
|
725 return NULL; |
|
726 |
|
727 /* Convert directory name to wide-character string */ |
|
728 wchar_t wname[PATH_MAX + 1]; |
|
729 size_t n; |
|
730 int error = mbstowcs_s(&n, wname, PATH_MAX + 1, dirname, PATH_MAX+1); |
|
731 if (error) |
|
732 goto exit_failure; |
|
733 |
|
734 /* Open directory stream using wide-character name */ |
|
735 dirp->wdirp = _wopendir(wname); |
|
736 if (!dirp->wdirp) |
|
737 goto exit_failure; |
|
738 |
|
739 /* Success */ |
|
740 return dirp; |
|
741 |
|
742 /* Failure */ |
|
743 exit_failure: |
|
744 free(dirp); |
|
745 return NULL; |
|
746 } |
|
747 |
|
748 /* Read next directory entry */ |
|
749 static struct dirent * |
|
750 readdir(DIR *dirp) |
|
751 { |
|
752 /* |
|
753 * Read directory entry to buffer. We can safely ignore the return |
|
754 * value as entry will be set to NULL in case of error. |
|
755 */ |
|
756 struct dirent *entry; |
|
757 (void) readdir_r(dirp, &dirp->ent, &entry); |
|
758 |
|
759 /* Return pointer to statically allocated directory entry */ |
|
760 return entry; |
|
761 } |
|
762 |
|
763 /* |
|
764 * Read next directory entry into called-allocated buffer. |
|
765 * |
|
766 * Returns zero on success. If the end of directory stream is reached, then |
|
767 * sets result to NULL and returns zero. |
|
768 */ |
|
769 static int |
|
770 readdir_r( |
|
771 DIR *dirp, struct dirent *entry, struct dirent **result) |
|
772 { |
|
773 /* Read next directory entry */ |
|
774 WIN32_FIND_DATAW *datap = dirent_next(dirp->wdirp); |
|
775 if (!datap) { |
|
776 /* No more directory entries */ |
|
777 *result = NULL; |
|
778 return /*OK*/0; |
|
779 } |
|
780 |
|
781 /* Attempt to convert file name to multi-byte string */ |
|
782 size_t n; |
|
783 int error = wcstombs_s( |
|
784 &n, entry->d_name, PATH_MAX + 1, |
|
785 datap->cFileName, PATH_MAX + 1); |
|
786 |
|
787 /* |
|
788 * If the file name cannot be represented by a multi-byte string, then |
|
789 * attempt to use old 8+3 file name. This allows the program to |
|
790 * access files although file names may seem unfamiliar to the user. |
|
791 * |
|
792 * Be ware that the code below cannot come up with a short file name |
|
793 * unless the file system provides one. At least VirtualBox shared |
|
794 * folders fail to do this. |
|
795 */ |
|
796 if (error && datap->cAlternateFileName[0] != '\0') { |
|
797 error = wcstombs_s( |
|
798 &n, entry->d_name, PATH_MAX + 1, |
|
799 datap->cAlternateFileName, PATH_MAX + 1); |
|
800 } |
|
801 |
|
802 if (!error) { |
|
803 /* Length of file name excluding zero terminator */ |
|
804 entry->d_namlen = n - 1; |
|
805 |
|
806 /* Determine file type */ |
|
807 DWORD attr = datap->dwFileAttributes; |
|
808 if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) |
|
809 entry->d_type = DT_CHR; |
|
810 else if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) |
|
811 entry->d_type = DT_LNK; |
|
812 else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) |
|
813 entry->d_type = DT_DIR; |
|
814 else |
|
815 entry->d_type = DT_REG; |
|
816 |
|
817 /* Get offset of next file */ |
|
818 datap = dirent_next(dirp->wdirp); |
|
819 if (datap) { |
|
820 /* Compute 31-bit hash of the next directory entry */ |
|
821 entry->d_off = dirent_hash(datap); |
|
822 |
|
823 /* Push the next directory entry back to cache */ |
|
824 dirp->wdirp->cached = 1; |
|
825 } else { |
|
826 /* End of directory stream */ |
|
827 entry->d_off = (long) ((~0UL) >> 1); |
|
828 } |
|
829 |
|
830 /* Reset fields */ |
|
831 entry->d_ino = 0; |
|
832 entry->d_reclen = sizeof(struct dirent); |
|
833 } else { |
|
834 /* |
|
835 * Cannot convert file name to multi-byte string so construct |
|
836 * an erroneous directory entry and return that. Note that |
|
837 * we cannot return NULL as that would stop the processing |
|
838 * of directory entries completely. |
|
839 */ |
|
840 entry->d_name[0] = '?'; |
|
841 entry->d_name[1] = '\0'; |
|
842 entry->d_namlen = 1; |
|
843 entry->d_type = DT_UNKNOWN; |
|
844 entry->d_ino = 0; |
|
845 entry->d_off = -1; |
|
846 entry->d_reclen = 0; |
|
847 } |
|
848 |
|
849 /* Return pointer to directory entry */ |
|
850 *result = entry; |
|
851 return /*OK*/0; |
|
852 } |
|
853 |
|
854 /* Close directory stream */ |
|
855 static int |
|
856 closedir(DIR *dirp) |
|
857 { |
|
858 int ok; |
|
859 |
|
860 if (!dirp) |
|
861 goto exit_failure; |
|
862 |
|
863 /* Close wide-character directory stream */ |
|
864 ok = _wclosedir(dirp->wdirp); |
|
865 dirp->wdirp = NULL; |
|
866 |
|
867 /* Release multi-byte character version */ |
|
868 free(dirp); |
|
869 return ok; |
|
870 |
|
871 exit_failure: |
|
872 /* Invalid directory stream */ |
|
873 dirent_set_errno(EBADF); |
|
874 return /*failure*/-1; |
|
875 } |
|
876 |
|
877 /* Rewind directory stream to beginning */ |
|
878 static void |
|
879 rewinddir(DIR *dirp) |
|
880 { |
|
881 if (!dirp) |
|
882 return; |
|
883 |
|
884 /* Rewind wide-character string directory stream */ |
|
885 _wrewinddir(dirp->wdirp); |
|
886 } |
|
887 |
|
888 /* Get position of directory stream */ |
|
889 static long |
|
890 _wtelldir(_WDIR *dirp) |
|
891 { |
|
892 if (!dirp || dirp->handle == INVALID_HANDLE_VALUE) { |
|
893 dirent_set_errno(EBADF); |
|
894 return /*failure*/-1; |
|
895 } |
|
896 |
|
897 /* Read next file entry */ |
|
898 WIN32_FIND_DATAW *datap = dirent_next(dirp); |
|
899 if (!datap) { |
|
900 /* End of directory stream */ |
|
901 return (long) ((~0UL) >> 1); |
|
902 } |
|
903 |
|
904 /* Store file entry to cache for readdir() */ |
|
905 dirp->cached = 1; |
|
906 |
|
907 /* Return the 31-bit hash code to be used as stream position */ |
|
908 return dirent_hash(datap); |
|
909 } |
|
910 |
|
911 /* Get position of directory stream */ |
|
912 static long |
|
913 telldir(DIR *dirp) |
|
914 { |
|
915 if (!dirp) { |
|
916 dirent_set_errno(EBADF); |
|
917 return -1; |
|
918 } |
|
919 |
|
920 return _wtelldir(dirp->wdirp); |
|
921 } |
|
922 |
|
923 /* Seek directory stream to offset */ |
|
924 static void |
|
925 _wseekdir(_WDIR *dirp, long loc) |
|
926 { |
|
927 if (!dirp) |
|
928 return; |
|
929 |
|
930 /* Directory must be open */ |
|
931 if (dirp->handle == INVALID_HANDLE_VALUE) |
|
932 goto exit_failure; |
|
933 |
|
934 /* Ensure that seek position is valid */ |
|
935 if (loc < 0) |
|
936 goto exit_failure; |
|
937 |
|
938 /* Restart directory stream from the beginning */ |
|
939 FindClose(dirp->handle); |
|
940 if (!dirent_first(dirp)) |
|
941 goto exit_failure; |
|
942 |
|
943 /* Reset invalid flag so that we can read from the stream again */ |
|
944 dirp->invalid = 0; |
|
945 |
|
946 /* |
|
947 * Read directory entries from the beginning until the hash matches a |
|
948 * file name. Be ware that hash code is only 31 bits longs and |
|
949 * duplicates are possible: the hash code cannot return the position |
|
950 * with 100.00% accuracy! Moreover, the method is slow for large |
|
951 * directories. |
|
952 */ |
|
953 long hash; |
|
954 do { |
|
955 /* Read next directory entry */ |
|
956 WIN32_FIND_DATAW *datap = dirent_next(dirp); |
|
957 if (!datap) { |
|
958 /* |
|
959 * End of directory stream was reached before finding |
|
960 * the requested location. Perhaps the file in |
|
961 * question was deleted or moved out of the directory. |
|
962 */ |
|
963 goto exit_failure; |
|
964 } |
|
965 |
|
966 /* Does the file name match the hash? */ |
|
967 hash = dirent_hash(datap); |
|
968 } while (hash != loc); |
|
969 |
|
970 /* |
|
971 * File name matches the hash! Push the directory entry back to cache |
|
972 * from where next readdir() will return it. |
|
973 */ |
|
974 dirp->cached = 1; |
|
975 dirp->invalid = 0; |
|
976 return; |
|
977 |
|
978 exit_failure: |
|
979 /* Ensure that readdir will return NULL */ |
|
980 dirp->invalid = 1; |
|
981 } |
|
982 |
|
983 /* Seek directory stream to offset */ |
|
984 static void |
|
985 seekdir(DIR *dirp, long loc) |
|
986 { |
|
987 if (!dirp) |
|
988 return; |
|
989 |
|
990 _wseekdir(dirp->wdirp, loc); |
|
991 } |
|
992 |
|
993 /* Scan directory for entries */ |
|
994 static int |
|
995 scandir( |
|
996 const char *dirname, struct dirent ***namelist, |
|
997 int (*filter)(const struct dirent*), |
|
998 int (*compare)(const struct dirent**, const struct dirent**)) |
|
999 { |
|
1000 int result; |
|
1001 |
|
1002 /* Open directory stream */ |
|
1003 DIR *dir = opendir(dirname); |
|
1004 if (!dir) { |
|
1005 /* Cannot open directory */ |
|
1006 return /*Error*/ -1; |
|
1007 } |
|
1008 |
|
1009 /* Read directory entries to memory */ |
|
1010 struct dirent *tmp = NULL; |
|
1011 struct dirent **files = NULL; |
|
1012 size_t size = 0; |
|
1013 size_t allocated = 0; |
|
1014 while (1) { |
|
1015 /* Allocate room for a temporary directory entry */ |
|
1016 if (!tmp) { |
|
1017 tmp = (struct dirent*) malloc(sizeof(struct dirent)); |
|
1018 if (!tmp) |
|
1019 goto exit_failure; |
|
1020 } |
|
1021 |
|
1022 /* Read directory entry to temporary area */ |
|
1023 struct dirent *entry; |
|
1024 if (readdir_r(dir, tmp, &entry) != /*OK*/0) |
|
1025 goto exit_failure; |
|
1026 |
|
1027 /* Stop if we already read the last directory entry */ |
|
1028 if (entry == NULL) |
|
1029 goto exit_success; |
|
1030 |
|
1031 /* Determine whether to include the entry in results */ |
|
1032 if (filter && !filter(tmp)) |
|
1033 continue; |
|
1034 |
|
1035 /* Enlarge pointer table to make room for another pointer */ |
|
1036 if (size >= allocated) { |
|
1037 /* Compute number of entries in the new table */ |
|
1038 size_t num_entries = size * 2 + 16; |
|
1039 |
|
1040 /* Allocate new pointer table or enlarge existing */ |
|
1041 void *p = realloc(files, sizeof(void*) * num_entries); |
|
1042 if (!p) |
|
1043 goto exit_failure; |
|
1044 |
|
1045 /* Got the memory */ |
|
1046 files = (dirent**) p; |
|
1047 allocated = num_entries; |
|
1048 } |
|
1049 |
|
1050 /* Store the temporary entry to ptr table */ |
|
1051 files[size++] = tmp; |
|
1052 tmp = NULL; |
|
1053 } |
|
1054 |
|
1055 exit_failure: |
|
1056 /* Release allocated entries */ |
|
1057 for (size_t i = 0; i < size; i++) { |
|
1058 free(files[i]); |
|
1059 } |
|
1060 |
|
1061 /* Release the pointer table */ |
|
1062 free(files); |
|
1063 files = NULL; |
|
1064 |
|
1065 /* Exit with error code */ |
|
1066 result = /*error*/ -1; |
|
1067 goto exit_status; |
|
1068 |
|
1069 exit_success: |
|
1070 /* Sort directory entries */ |
|
1071 if (size > 1 && compare) { |
|
1072 qsort(files, size, sizeof(void*), |
|
1073 (int (*) (const void*, const void*)) compare); |
|
1074 } |
|
1075 |
|
1076 /* Pass pointer table to caller */ |
|
1077 if (namelist) |
|
1078 *namelist = files; |
|
1079 |
|
1080 /* Return the number of directory entries read */ |
|
1081 result = (int) size; |
|
1082 |
|
1083 exit_status: |
|
1084 /* Release temporary directory entry, if we had one */ |
|
1085 free(tmp); |
|
1086 |
|
1087 /* Close directory stream */ |
|
1088 closedir(dir); |
|
1089 return result; |
|
1090 } |
|
1091 |
|
1092 /* Alphabetical sorting */ |
|
1093 static int |
|
1094 alphasort(const struct dirent **a, const struct dirent **b) |
|
1095 { |
|
1096 return strcoll((*a)->d_name, (*b)->d_name); |
|
1097 } |
|
1098 |
|
1099 /* Sort versions */ |
|
1100 static int |
|
1101 versionsort(const struct dirent **a, const struct dirent **b) |
|
1102 { |
|
1103 return strverscmp((*a)->d_name, (*b)->d_name); |
|
1104 } |
|
1105 |
|
1106 /* Compare strings */ |
|
1107 static int |
|
1108 strverscmp(const char *a, const char *b) |
|
1109 { |
|
1110 size_t i = 0; |
|
1111 size_t j; |
|
1112 |
|
1113 /* Find first difference */ |
|
1114 while (a[i] == b[i]) { |
|
1115 if (a[i] == '\0') { |
|
1116 /* No difference */ |
|
1117 return 0; |
|
1118 } |
|
1119 ++i; |
|
1120 } |
|
1121 |
|
1122 /* Count backwards and find the leftmost digit */ |
|
1123 j = i; |
|
1124 while (j > 0 && isdigit(a[j-1])) { |
|
1125 --j; |
|
1126 } |
|
1127 |
|
1128 /* Determine mode of comparison */ |
|
1129 if (a[j] == '0' || b[j] == '0') { |
|
1130 /* Find the next non-zero digit */ |
|
1131 while (a[j] == '0' && a[j] == b[j]) { |
|
1132 j++; |
|
1133 } |
|
1134 |
|
1135 /* String with more digits is smaller, e.g 002 < 01 */ |
|
1136 if (isdigit(a[j])) { |
|
1137 if (!isdigit(b[j])) { |
|
1138 return -1; |
|
1139 } |
|
1140 } else if (isdigit(b[j])) { |
|
1141 return 1; |
|
1142 } |
|
1143 } else if (isdigit(a[j]) && isdigit(b[j])) { |
|
1144 /* Numeric comparison */ |
|
1145 size_t k1 = j; |
|
1146 size_t k2 = j; |
|
1147 |
|
1148 /* Compute number of digits in each string */ |
|
1149 while (isdigit(a[k1])) { |
|
1150 k1++; |
|
1151 } |
|
1152 while (isdigit(b[k2])) { |
|
1153 k2++; |
|
1154 } |
|
1155 |
|
1156 /* Number with more digits is bigger, e.g 999 < 1000 */ |
|
1157 if (k1 < k2) |
|
1158 return -1; |
|
1159 else if (k1 > k2) |
|
1160 return 1; |
|
1161 } |
|
1162 |
|
1163 /* Alphabetical comparison */ |
|
1164 return (int) ((unsigned char) a[i]) - ((unsigned char) b[i]); |
|
1165 } |
|
1166 |
|
1167 /* Convert multi-byte string to wide character string */ |
|
1168 #if !defined(_MSC_VER) || _MSC_VER < 1400 |
|
1169 static int |
|
1170 dirent_mbstowcs_s( |
|
1171 size_t *pReturnValue, wchar_t *wcstr, |
|
1172 size_t sizeInWords, const char *mbstr, size_t count) |
|
1173 { |
|
1174 /* Older Visual Studio or non-Microsoft compiler */ |
|
1175 size_t n = mbstowcs(wcstr, mbstr, sizeInWords); |
|
1176 if (wcstr && n >= count) |
|
1177 return /*error*/ 1; |
|
1178 |
|
1179 /* Zero-terminate output buffer */ |
|
1180 if (wcstr && sizeInWords) { |
|
1181 if (n >= sizeInWords) |
|
1182 n = sizeInWords - 1; |
|
1183 wcstr[n] = 0; |
|
1184 } |
|
1185 |
|
1186 /* Length of multi-byte string with zero terminator */ |
|
1187 if (pReturnValue) { |
|
1188 *pReturnValue = n + 1; |
|
1189 } |
|
1190 |
|
1191 /* Success */ |
|
1192 return 0; |
|
1193 } |
|
1194 #endif |
|
1195 |
|
1196 /* Convert wide-character string to multi-byte string */ |
|
1197 #if !defined(_MSC_VER) || _MSC_VER < 1400 |
|
1198 static int |
|
1199 dirent_wcstombs_s( |
|
1200 size_t *pReturnValue, char *mbstr, |
|
1201 size_t sizeInBytes, const wchar_t *wcstr, size_t count) |
|
1202 { |
|
1203 /* Older Visual Studio or non-Microsoft compiler */ |
|
1204 size_t n = wcstombs(mbstr, wcstr, sizeInBytes); |
|
1205 if (mbstr && n >= count) |
|
1206 return /*error*/1; |
|
1207 |
|
1208 /* Zero-terminate output buffer */ |
|
1209 if (mbstr && sizeInBytes) { |
|
1210 if (n >= sizeInBytes) { |
|
1211 n = sizeInBytes - 1; |
|
1212 } |
|
1213 mbstr[n] = '\0'; |
|
1214 } |
|
1215 |
|
1216 /* Length of resulting multi-bytes string WITH zero-terminator */ |
|
1217 if (pReturnValue) { |
|
1218 *pReturnValue = n + 1; |
|
1219 } |
|
1220 |
|
1221 /* Success */ |
|
1222 return 0; |
|
1223 } |
|
1224 #endif |
|
1225 |
|
1226 /* Set errno variable */ |
|
1227 #if !defined(_MSC_VER) || _MSC_VER < 1400 |
|
1228 static void |
|
1229 dirent_set_errno(int error) |
|
1230 { |
|
1231 /* Non-Microsoft compiler or older Microsoft compiler */ |
|
1232 errno = error; |
|
1233 } |
|
1234 #endif |
|
1235 |
|
1236 #ifdef __cplusplus |
|
1237 } |
|
1238 #endif |
|
1239 #endif /*DIRENT_H*/ |