xref: /dpdk/lib/eal/windows/include/dirent.h (revision 0ae7844fcdad143ce4833f4fc8d886740b3f482d)
1 /* SPDX-License-Identifier: MIT
2  * Dirent interface for Microsoft Visual Studio
3  * Version 1.21
4  * Copyright (C) 2006-2012 Toni Ronkko
5  * https://github.com/tronkko/dirent
6  */
7 
8 #ifndef DIRENT_H
9 #define DIRENT_H
10 
11 /*
12  * Include windows.h without Windows Sockets 1.1 to prevent conflicts with
13  * Windows Sockets 2.0.
14  */
15 #ifndef WIN32_LEAN_AND_MEAN
16 #   define WIN32_LEAN_AND_MEAN
17 #endif
18 
19 #include <windows.h>
20 
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <wchar.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <malloc.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <errno.h>
30 
31 /* Maximum length of file name */
32 #if !defined(PATH_MAX)
33 #   define PATH_MAX MAX_PATH
34 #endif
35 
36 /* File type flags for d_type */
37 #define DT_UNKNOWN 0
38 #define DT_REG S_IFREG
39 #define DT_DIR S_IFDIR
40 #define DT_CHR S_IFCHR
41 
42 /*
43  * File type macros.  Note that block devices, sockets and links cannot be
44  * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
45  * only defined for compatibility.  These macros should always return false
46  * on Windows.
47  */
48 #if !defined(S_ISDIR)
49 #   define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
50 #endif
51 #if !defined(S_ISREG)
52 #   define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
53 #endif
54 
55 /* Wide-character version */
56 struct _wdirent {
57 	/* Always zero */
58 	long d_ino;
59 
60 	/* Structure size */
61 	unsigned short d_reclen;
62 
63 	/* Length of name without \0 */
64 	size_t d_namlen;
65 
66 	/* File type */
67 	int d_type;
68 
69 	/* File name */
70 	wchar_t d_name[PATH_MAX];
71 };
72 typedef struct _wdirent _wdirent;
73 
74 struct _WDIR {
75 	/* Current directory entry */
76 	struct _wdirent ent;
77 
78 	/* Private file data */
79 	WIN32_FIND_DATAW data;
80 
81 	/* True if data is valid */
82 	int cached;
83 
84 	/* Win32 search handle */
85 	HANDLE handle;
86 
87 	/* Initial directory name */
88 	wchar_t *patt;
89 };
90 typedef struct _WDIR _WDIR;
91 
92 static _WDIR *_wopendir(const wchar_t *dirname);
93 static int _wclosedir(_WDIR *dirp);
94 
95 /* For compatibility with Symbian */
96 #define wdirent _wdirent
97 #define WDIR _WDIR
98 #define wopendir _wopendir
99 #define wclosedir _wclosedir
100 
101 /* Multi-byte character versions */
102 struct dirent {
103 	/* Always zero */
104 	long d_ino;
105 
106 	/* Structure size */
107 	unsigned short d_reclen;
108 
109 	/* Length of name without \0 */
110 	size_t d_namlen;
111 
112 	/* File type */
113 	int d_type;
114 
115 	/* File name */
116 	char d_name[PATH_MAX];
117 };
118 typedef struct dirent dirent;
119 
120 struct DIR {
121 	struct dirent ent;
122 	struct _WDIR *wdirp;
123 };
124 typedef struct DIR DIR;
125 
126 static DIR *opendir(const char *dirname);
127 static struct dirent *readdir(DIR *dirp);
128 static int closedir(DIR *dirp);
129 
130 /* Internal utility functions */
131 static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp);
132 static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp);
133 
134 static int dirent_mbstowcs_s(
135 	size_t *pReturnValue,
136 	wchar_t *wcstr,
137 	size_t sizeInWords,
138 	const char *mbstr,
139 	size_t count);
140 
141 static int dirent_wcstombs_s(
142 	size_t *pReturnValue,
143 	char *mbstr,
144 	size_t sizeInBytes,
145 	const wchar_t *wcstr,
146 	size_t count);
147 
148 static void dirent_set_errno(int error);
149 
150 /*
151  * Open directory stream DIRNAME for read and return a pointer to the
152  * internal working area that is used to retrieve individual directory
153  * entries.
154  */
155 static _WDIR*
_wopendir(const wchar_t * dirname)156 _wopendir(const wchar_t *dirname)
157 {
158 	_WDIR *dirp = NULL;
159 	int error;
160 
161 	/* Must have directory name */
162 	if (dirname == NULL || dirname[0] == '\0') {
163 		dirent_set_errno(ENOENT);
164 		return NULL;
165 	}
166 
167 	/* Allocate new _WDIR structure */
168 	dirp = (_WDIR *)malloc(sizeof(struct _WDIR));
169 	if (dirp != NULL) {
170 		DWORD n;
171 
172 		/* Reset _WDIR structure */
173 		dirp->handle = INVALID_HANDLE_VALUE;
174 		dirp->patt = NULL;
175 		dirp->cached = 0;
176 
177 		/* Compute the length of full path plus zero terminator
178 		 *
179 		 * Note that on WinRT there's no way to convert relative paths
180 		 * into absolute paths, so just assume its an absolute path.
181 		 */
182 	#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
183 		n = wcslen(dirname);
184 	#else
185 		n = GetFullPathNameW(dirname, 0, NULL, NULL);
186 	#endif
187 
188 		/* Allocate room for absolute directory name and search
189 		 * pattern
190 		 */
191 		dirp->patt = (wchar_t *)malloc(sizeof(wchar_t) * n + 16);
192 		if (dirp->patt) {
193 			/* Convert relative directory name to an
194 			 * absolute one. This allows rewinddir() to
195 			 * function correctly even when  current working
196 			 * directory is changed between opendir()
197 			 * and rewinddir().
198 			 *
199 			 * Note that on WinRT there's no way to convert
200 			 * relative paths into absolute paths, so just
201 			 * assume its an absolute path.
202 			 */
203 			#if defined(WINAPI_FAMILY) &&                   \
204 				(WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
205 			wcsncpy_s(dirp->patt, n + 1, dirname, n);
206 			#else
207 			n = GetFullPathNameW(dirname, n, dirp->patt, NULL);
208 			#endif
209 			if (n > 0) {
210 				wchar_t *p;
211 
212 				/* Append search pattern \* to the directory
213 				 * name
214 				 */
215 				p = dirp->patt + n;
216 				if (dirp->patt < p) {
217 					switch (p[-1]) {
218 					case '\\':
219 					case '/':
220 					case ':':
221 					/* Directory ends in path separator,
222 					 * e.g.c:\temp\
223 					 */
224 						/*NOP*/;
225 						break;
226 
227 					default:
228 					/* Directory name doesn't end in path
229 					 * separator
230 					 */
231 						*p++ = '\\';
232 					}
233 				}
234 				*p++ = '*';
235 				*p = '\0';
236 
237 				/* Open directory stream and retrieve the first
238 				 * entry
239 				 */
240 				if (dirent_first(dirp)) {
241 				/* Directory stream opened successfully */
242 					error = 0;
243 				} else {
244 					/* Cannot retrieve first entry */
245 					error = 1;
246 					dirent_set_errno(ENOENT);
247 				}
248 
249 			} else {
250 				/* Cannot retrieve full path name */
251 				dirent_set_errno(ENOENT);
252 				error = 1;
253 			}
254 
255 		} else {
256 			/* Cannot allocate memory for search pattern */
257 			error = 1;
258 		}
259 
260 	} else {
261 		/* Cannot allocate _WDIR structure */
262 		error = 1;
263 	}
264 
265 	/* Clean up in case of error */
266 	if (error  &&  dirp) {
267 		_wclosedir(dirp);
268 		dirp = NULL;
269 	}
270 
271 	return dirp;
272 }
273 
274 /*
275  * Close directory stream opened by opendir() function.
276  * This invalidates the DIR structure as well as any directory
277  * entry read previously by _wreaddir().
278  */
279 static int
_wclosedir(_WDIR * dirp)280 _wclosedir(_WDIR *dirp)
281 {
282 	int ok;
283 	if (dirp) {
284 
285 		/* Release search handle */
286 		if (dirp->handle != INVALID_HANDLE_VALUE) {
287 			FindClose(dirp->handle);
288 			dirp->handle = INVALID_HANDLE_VALUE;
289 		}
290 
291 		/* Release search pattern */
292 		if (dirp->patt) {
293 			free(dirp->patt);
294 			dirp->patt = NULL;
295 		}
296 
297 		/* Release directory structure */
298 		free(dirp);
299 		ok = /*success*/0;
300 
301 	} else {
302 		/* Invalid directory stream */
303 		dirent_set_errno(EBADF);
304 		ok = /*failure*/-1;
305 	}
306 	return ok;
307 }
308 
309 /* Get first directory entry (internal) */
310 static WIN32_FIND_DATAW*
dirent_first(_WDIR * dirp)311 dirent_first(_WDIR *dirp)
312 {
313 	WIN32_FIND_DATAW *datap;
314 
315 	/* Open directory and retrieve the first entry */
316 	dirp->handle = FindFirstFileExW(
317 		dirp->patt, FindExInfoStandard, &dirp->data,
318 		FindExSearchNameMatch, NULL, 0);
319 	if (dirp->handle != INVALID_HANDLE_VALUE) {
320 
321 		/* a directory entry is now waiting in memory */
322 		datap = &dirp->data;
323 		dirp->cached = 1;
324 
325 	} else {
326 
327 		/* Failed to re-open directory: no directory entry in memory */
328 		dirp->cached = 0;
329 		datap = NULL;
330 
331 	}
332 	return datap;
333 }
334 
335 /* Get next directory entry (internal) */
336 static WIN32_FIND_DATAW*
dirent_next(_WDIR * dirp)337 dirent_next(_WDIR *dirp)
338 {
339 	WIN32_FIND_DATAW *p;
340 
341 	/* Get next directory entry */
342 	if (dirp->cached != 0) {
343 
344 		/* A valid directory entry already in memory */
345 		p = &dirp->data;
346 		dirp->cached = 0;
347 
348 	} else if (dirp->handle != INVALID_HANDLE_VALUE) {
349 
350 		/* Get the next directory entry from stream */
351 		if (FindNextFileW(dirp->handle, &dirp->data) != FALSE) {
352 			/* Got a file */
353 			p = &dirp->data;
354 		} else {
355 			/* The very last entry has been processed
356 			 *or an error occurred
357 			 */
358 			FindClose(dirp->handle);
359 			dirp->handle = INVALID_HANDLE_VALUE;
360 			p = NULL;
361 		}
362 
363 	} else {
364 
365 		/* End of directory stream reached */
366 		p = NULL;
367 
368 	}
369 
370 	return p;
371 }
372 
373 /*
374  * Open directory stream using plain old C-string.
375  */
376 static DIR*
opendir(const char * dirname)377 opendir(const char *dirname)
378 {
379 	struct DIR *dirp;
380 	int error;
381 
382 	/* Must have directory name */
383 	if (dirname == NULL || dirname[0] == '\0') {
384 		dirent_set_errno(ENOENT);
385 		return NULL;
386 	}
387 
388 	/* Allocate memory for DIR structure */
389 	dirp = (DIR *)malloc(sizeof(struct DIR));
390 	if (dirp) {
391 		wchar_t wname[PATH_MAX];
392 		size_t n;
393 
394 		/* Convert directory name to wide-character string */
395 		error = dirent_mbstowcs_s(&n, wname, PATH_MAX,
396 			dirname, PATH_MAX);
397 		if (!error) {
398 
399 			/* Open directory stream using wide-character name */
400 			dirp->wdirp = _wopendir(wname);
401 			if (dirp->wdirp) {
402 				/* Directory stream opened */
403 				error = 0;
404 			} else {
405 				/* Failed to open directory stream */
406 				error = 1;
407 			}
408 
409 		} else {
410 			/*
411 			 * Cannot convert file name to wide-character string.
412 			 * This occurs if the string contains invalid multi-byte
413 			 * sequences or the output buffer is too small to
414 			 * contain the resulting string.
415 			 */
416 			error = 1;
417 		}
418 
419 	} else {
420 		/* Cannot allocate DIR structure */
421 		error = 1;
422 	}
423 
424 	/* Clean up in case of error */
425 	if (error  &&  dirp) {
426 		free(dirp);
427 		dirp = NULL;
428 	}
429 
430 	return dirp;
431 }
432 
433 /*
434  * Read next directory entry.
435  *
436  * When working with text consoles, please note that file names
437  * returned by readdir() are represented in the default ANSI code
438  * page while any output toconsole is typically formatted on another
439  * code page. Thus, non-ASCII characters in file names will not usually
440  * display correctly on console. The problem can be fixed in two ways:
441  * (1) change the character set of console to 1252 using chcp utility
442  * and use Lucida Console font, or (2) use _cprintf function when
443  * writing to console. The _cprintf() will re-encode ANSI strings to the
444  * console code page so many non-ASCII characters will display correctly.
445  */
446 static struct dirent*
readdir(DIR * dirp)447 readdir(DIR *dirp)
448 {
449 	WIN32_FIND_DATAW *datap;
450 	struct dirent *entp;
451 
452 	/* Read next directory entry */
453 	datap = dirent_next(dirp->wdirp);
454 	if (datap) {
455 		size_t n;
456 		int error;
457 
458 		/* Attempt to convert file name to multi-byte string */
459 		error = dirent_wcstombs_s(&n, dirp->ent.d_name,
460 			PATH_MAX, datap->cFileName, PATH_MAX);
461 
462 		/*
463 		 * If the file name cannot be represented by a multi-byte
464 		 * string, then attempt to use old 8+3 file name.
465 		 * This allows traditional Unix-code to access some file
466 		 * names despite of unicode characters, although file names
467 		 * may seem unfamiliar to the user.
468 		 *
469 		 * Be ware that the code below cannot come up with a short
470 		 * file name unless the file system provides one.  At least
471 		 * VirtualBox shared folders fail to do this.
472 		 */
473 		if (error  &&  datap->cAlternateFileName[0] != '\0') {
474 			error = dirent_wcstombs_s(
475 				&n, dirp->ent.d_name, PATH_MAX,
476 				datap->cAlternateFileName, PATH_MAX);
477 		}
478 
479 		if (!error) {
480 			DWORD attr;
481 
482 			/* Initialize directory entry for return */
483 			entp = &dirp->ent;
484 
485 			/* Length of file name excluding zero terminator */
486 			entp->d_namlen = n - 1;
487 
488 			/* File attributes */
489 			attr = datap->dwFileAttributes;
490 			if ((attr & FILE_ATTRIBUTE_DEVICE) != 0)
491 				entp->d_type = DT_CHR;
492 			else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
493 				entp->d_type = DT_DIR;
494 			else
495 				entp->d_type = DT_REG;
496 
497 			/* Reset dummy fields */
498 			entp->d_ino = 0;
499 			entp->d_reclen = sizeof(struct dirent);
500 
501 		} else {
502 			/*
503 			 * Cannot convert file name to multi-byte string so
504 			 * construct an erroneous directory entry and return
505 			 * that. Note that we cannot return NULL as that would
506 			 * stop the processing of directory entries completely.
507 			 */
508 			entp = &dirp->ent;
509 			entp->d_name[0] = '?';
510 			entp->d_name[1] = '\0';
511 			entp->d_namlen = 1;
512 			entp->d_type = DT_UNKNOWN;
513 			entp->d_ino = 0;
514 			entp->d_reclen = 0;
515 		}
516 
517 	} else {
518 		/* No more directory entries */
519 		entp = NULL;
520 	}
521 
522 	return entp;
523 }
524 
525 /*
526  * Close directory stream.
527  */
528 static int
closedir(DIR * dirp)529 closedir(DIR *dirp)
530 {
531 	int ok;
532 	if (dirp) {
533 
534 		/* Close wide-character directory stream */
535 		ok = _wclosedir(dirp->wdirp);
536 		dirp->wdirp = NULL;
537 
538 		/* Release multi-byte character version */
539 		free(dirp);
540 
541 	} else {
542 
543 		/* Invalid directory stream */
544 		dirent_set_errno(EBADF);
545 		ok = /*failure*/-1;
546 
547 	}
548 	return ok;
549 }
550 
551 /* Convert multi-byte string to wide character string */
552 static int
dirent_mbstowcs_s(size_t * pReturnValue,wchar_t * wcstr,size_t sizeInWords,const char * mbstr,size_t count)553 dirent_mbstowcs_s(
554 	size_t *pReturnValue,
555 	wchar_t *wcstr,
556 	size_t sizeInWords,
557 	const char *mbstr,
558 	size_t count)
559 {
560 	int error;
561 
562 	#if defined(_MSC_VER)  &&  _MSC_VER >= 1400
563 	/* Microsoft Visual Studio 2005 or later */
564 	error = mbstowcs_s(pReturnValue, wcstr,
565 	sizeInWords, mbstr, count);
566 	#else
567 
568 	/* Older Visual Studio or non-Microsoft compiler */
569 	size_t n;
570 
571 	/* Convert to wide-character string (or count characters) */
572 	n = mbstowcs(wcstr, mbstr, sizeInWords);
573 	if (!wcstr || n < count) {
574 
575 		/* Zero-terminate output buffer */
576 		if (wcstr  &&  sizeInWords) {
577 			if (n >= sizeInWords)
578 				n = sizeInWords - 1;
579 			wcstr[n] = 0;
580 		}
581 
582 		/* Length of resulting multi-byte string WITH zero
583 		 *terminator
584 		 */
585 		if (pReturnValue)
586 			*pReturnValue = n + 1;
587 
588 		/* Success */
589 		error = 0;
590 
591 	} else {
592 
593 		/* Could not convert string */
594 		error = 1;
595 
596 	}
597 	#endif
598 
599 	return error;
600 }
601 
602 /* Convert wide-character string to multi-byte string */
603 static int
dirent_wcstombs_s(size_t * pReturnValue,char * mbstr,size_t sizeInBytes,const wchar_t * wcstr,size_t count)604 dirent_wcstombs_s(
605 	size_t *pReturnValue,
606 	char *mbstr,
607 	size_t sizeInBytes, /* max size of mbstr */
608 	const wchar_t *wcstr,
609 	size_t count)
610 {
611 	int error;
612 
613 	#if defined(_MSC_VER)  &&  _MSC_VER >= 1400
614 	/* Microsoft Visual Studio 2005 or later */
615 	error = wcstombs_s(pReturnValue, mbstr, sizeInBytes, wcstr, count);
616 	#else
617 	/* Older Visual Studio or non-Microsoft compiler */
618 	size_t n;
619 
620 	/* Convert to multi-byte string
621 	 * (or count the number of bytes needed)
622 	 */
623 	n = wcstombs(mbstr, wcstr, sizeInBytes);
624 	if (!mbstr || n < count) {
625 		/* Zero-terminate output buffer */
626 		if (mbstr  &&  sizeInBytes) {
627 			if (n >= sizeInBytes)
628 				n = sizeInBytes - 1;
629 			mbstr[n] = '\0';
630 		}
631 		/* Length of resulting multi-bytes string WITH
632 		 *zero-terminator
633 		 */
634 		if (pReturnValue)
635 			*pReturnValue = n + 1;
636 		/* Success */
637 		error = 0;
638 	} else {
639 		/* Cannot convert string */
640 		error = 1;
641 	}
642 	#endif
643 
644 	return error;
645 }
646 
647 /* Set errno variable */
648 static void
dirent_set_errno(int error)649 dirent_set_errno(int error)
650 {
651 #if defined(_MSC_VER)  &&  _MSC_VER >= 1400
652 	/* Microsoft Visual Studio 2005 and later */
653 	_set_errno(error);
654 #else
655 
656 	/* Non-Microsoft compiler or older Microsoft compiler */
657 	errno = error;
658 #endif
659 }
660 
661 #endif /*DIRENT_H*/
662