eal: move OS-specific sub-directories
[dpdk.git] / lib / librte_eal / windows / include / dirent.h
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*
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
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*
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*
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*
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 _cprinf() will re-encode ANSI strings to the
444  * console code page so many non-ASCII characters will display correctly.
445  */
446 static struct dirent*
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
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
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 resuting 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
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
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 #ifdef __cplusplus
662 }
663 #endif
664 #endif /*DIRENT_H*/