| |
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*/ |