diff --git a/src/dirent/dirent-win32.h b/src/dirent/dirent-win32.h index a61f7b6a..cf3fe567 100644 --- a/src/dirent/dirent-win32.h +++ b/src/dirent/dirent-win32.h @@ -23,16 +23,19 @@ * OTHER DEALINGS IN THE SOFTWARE. * * + * Version 1.13, Dec 12 2012, Toni Ronkko + * Use traditional 8+3 file name if the name cannot be represented in the + * default ANSI code page. Now compiles again with MSVC 6.0. Thanks to + * Konstantin Khomoutov for testing. + * * Version 1.12.1, Oct 1 2012, Toni Ronkko * Bug fix: renamed wide-character DIR structure _wDIR to _WDIR (with * capital W) in order to maintain compatibility with MingW. * * Version 1.12, Sep 30 2012, Toni Ronkko - * Define PATH_MAX and NAME_MAX. - * - * Added wide-character variants _wDIR, _wdirent, _wopendir(), - * _wreaddir(), _wclosedir() and _wrewinddir(). Thanks to Edgar Buerkle - * and Jan Nijtmans for ideas and code. + * Define PATH_MAX and NAME_MAX. Added wide-character variants _wDIR, + * _wdirent, _wopendir(), _wreaddir(), _wclosedir() and _wrewinddir(). + * Thanks to Edgar Buerkle and Jan Nijtmans for ideas and code. * * Do not include windows.h. This allows dirent.h to be integrated more * easily into programs using winsock. Thanks to Fernando Azaldegui. @@ -90,44 +93,68 @@ #define DIRENT_H #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86) -#define _X86_ +# define _X86_ #endif #include #include #include #include #include -#include #include #include +#include #include #include #include -/* Windows 8 wide-character string functions */ -#if (_WIN32_WINNT >= 0x0602) -# include -#endif +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN /* Entries missing from MSVC 6.0 */ #if !defined(FILE_ATTRIBUTE_DEVICE) -# define FILE_ATTRIBUTE_DEVICE 0x40 +# define FILE_ATTRIBUTE_DEVICE 0x40 #endif /* File type and permission flags for stat() */ -#if defined(_MSC_VER) && !defined(S_IREAD) +#if !defined(S_IFMT) # define S_IFMT _S_IFMT /* File type mask */ +#endif +#if !defined(S_IFDIR) # define S_IFDIR _S_IFDIR /* Directory */ +#endif +#if !defined(S_IFCHR) # define S_IFCHR _S_IFCHR /* Character device */ +#endif +#if !defined(S_IFFIFO) # define S_IFFIFO _S_IFFIFO /* Pipe */ +#endif +#if !defined(S_IFREG) # define S_IFREG _S_IFREG /* Regular file */ +#endif +#if !defined(S_IREAD) # define S_IREAD _S_IREAD /* Read permission */ +#endif +#if !defined(S_IWRITE) # define S_IWRITE _S_IWRITE /* Write permission */ +#endif +#if !defined(S_IEXEC) # define S_IEXEC _S_IEXEC /* Execute permission */ #endif -#define S_IFBLK 0 /* Block device */ -#define S_IFLNK 0 /* Link */ -#define S_IFSOCK 0 /* Socket */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO /* Pipe */ +#endif +#if !defined(S_IFBLK) +# define S_IFBLK 0 /* Block device */ +#endif +#if !defined(S_IFLNK) +# define S_IFLNK 0 /* Link */ +#endif +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 /* Socket */ +#endif #if defined(_MSC_VER) # define S_IRUSR S_IREAD /* Read user */ @@ -141,14 +168,22 @@ # define S_IXOTH 0 /* Execute others */ #endif -/* Indicates that d_type field is available in dirent structure */ -#define _DIRENT_HAVE_D_TYPE +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif /* File type flags for d_type */ #define DT_UNKNOWN 0 #define DT_REG S_IFREG #define DT_DIR S_IFDIR -#define DT_FIFO S_IFFIFO +#define DT_FIFO S_IFIFO #define DT_SOCK S_IFSOCK #define DT_CHR S_IFCHR #define DT_BLK S_IFBLK @@ -163,7 +198,7 @@ * only defined for compatibility. These macros should always return false * on Windows. */ -#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO) +#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) @@ -171,29 +206,19 @@ #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) -/* For compatiblity with Unix */ -#if !defined(PATH_MAX) -# define PATH_MAX MAX_PATH -#endif -#if !defined(FILENAME_MAX) -# define FILENAME_MAX MAX_PATH -#endif -#if !defined(NAME_MAX) -# define NAME_MAX FILENAME_MAX -#endif +/* Return the exact length of d_namlen without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return number of bytes needed to store d_namlen */ +#define _D_ALLOC_NAMLEN(p) (PATH_MAX + 1) -/* Set errno variable */ -#if defined(_MSC_VER) -#define DIRENT_SET_ERRNO(x) _set_errno (x) -#else -#define DIRENT_SET_ERRNO(x) (errno = (x)) -#endif #ifdef __cplusplus extern "C" { #endif -/* Wide-character versions */ + +/* Wide-character version */ struct _wdirent { long d_ino; /* Always zero */ unsigned short d_reclen; /* Structure size */ @@ -205,7 +230,7 @@ typedef struct _wdirent _wdirent; struct _WDIR { struct _wdirent ent; /* Current directory entry */ - WIN32_FIND_DATAW find_data; /* Private file data */ + WIN32_FIND_DATAW data; /* Private file data */ int cached; /* True if data is valid */ HANDLE handle; /* Win32 search handle */ wchar_t *patt; /* Initial directory name */ @@ -217,6 +242,7 @@ static struct _wdirent *_wreaddir (_WDIR *dirp); static int _wclosedir (_WDIR *dirp); static void _wrewinddir (_WDIR* dirp); + /* For compatibility with Symbian */ #define wdirent _wdirent #define WDIR _WDIR @@ -248,6 +274,26 @@ static int closedir (DIR *dirp); static void rewinddir (DIR* dirp); +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + /* * Open directory stream DIRNAME for read and return a pointer to the * internal working area that is used to retrieve individual directory @@ -258,7 +304,13 @@ _wopendir( const wchar_t *dirname) { _WDIR *dirp = NULL; - int error = 0; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } /* Allocate new _WDIR structure */ dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); @@ -268,17 +320,18 @@ _wopendir( /* Reset _WDIR structure */ dirp->handle = INVALID_HANDLE_VALUE; dirp->patt = NULL; + dirp->cached = 0; /* Compute the length of full path plus zero terminator */ n = GetFullPathNameW (dirname, 0, NULL, NULL); - /* Allocate room for full path and search patterns */ + /* Allocate room for absolute directory name and search pattern */ dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); if (dirp->patt) { /* * Convert relative directory name to an absolute one. This - * allows rewinddir() to function correctly when the current + * allows rewinddir() to function correctly even when current * working directory is changed between opendir() and rewinddir(). */ n = GetFullPathNameW (dirname, n, dirp->patt, NULL); @@ -305,21 +358,18 @@ _wopendir( *p = '\0'; /* Open directory stream and retrieve the first entry */ - dirp->handle = FindFirstFileW (dirp->patt, &dirp->find_data); - if (dirp->handle != INVALID_HANDLE_VALUE) { - - /* Directory entry is now waiting in memory */ - dirp->cached = 1; - + if (dirent_first (dirp)) { + /* Directory stream opened successfully */ + error = 0; } else { - /* Search pattern is not a directory name? */ - DIRENT_SET_ERRNO (ENOENT); + /* Cannot retrieve first entry */ error = 1; + dirent_set_errno (ENOENT); } } else { - /* Cannot convert directory name to wide character string */ - DIRENT_SET_ERRNO (ENOENT); + /* Cannot retrieve full path name */ + dirent_set_errno (ENOENT); error = 1; } @@ -352,66 +402,55 @@ static struct _wdirent* _wreaddir( _WDIR *dirp) { - DWORD attr; - errno_t error; + WIN32_FIND_DATAW *datap; + struct _wdirent *entp; - /* Get next directory entry */ - if (dirp->cached != 0) { - /* A valid directory entry already in memory */ - dirp->cached = 0; - } else { - /* Get the next directory entry from stream */ - if (dirp->handle == INVALID_HANDLE_VALUE) { - return NULL; + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* Pointer to directory entry to return */ + entp = &dirp->ent; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entp->d_name[n] = datap->cFileName[n]; + n++; } - if (FindNextFileW (dirp->handle, &dirp->find_data) == FALSE) { - /* The very last entry has been processed or an error occured */ - FindClose (dirp->handle); - dirp->handle = INVALID_HANDLE_VALUE; - return NULL; - } - } + dirp->ent.d_name[n] = 0; - /* Copy file name as a wide-character string */ - error = wcsncpy_s( - dirp->ent.d_name, /* Destination string */ - PATH_MAX, /* Size of dest in words */ - dirp->find_data.cFileName, /* Source string */ - PATH_MAX + 1); /* Max # of chars to copy */ - if (!error) { + /* Length of file name excluding zero terminator */ + entp->d_namlen = n; - /* Compute the length of name */ - dirp->ent.d_namlen = wcsnlen (dirp->ent.d_name, PATH_MAX); - - /* Determine file type */ - attr = dirp->find_data.dwFileAttributes; + /* File type */ + attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { - dirp->ent.d_type = DT_CHR; + entp->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { - dirp->ent.d_type = DT_DIR; + entp->d_type = DT_DIR; } else { - dirp->ent.d_type = DT_REG; + entp->d_type = DT_REG; } /* Reset dummy fields */ - dirp->ent.d_ino = 0; - dirp->ent.d_reclen = sizeof (dirp->ent); + entp->d_ino = 0; + entp->d_reclen = sizeof (struct _wdirent); } else { - /* - * Cannot copy file name from find_data to ent. Construct a - * dummy _wdirent structure to pass error to caller. - */ - dirp->ent.d_name[0] = '?'; - dirp->ent.d_name[1] = '\0'; - dirp->ent.d_namlen = 1; - dirp->ent.d_type = DT_UNKNOWN; - dirp->ent.d_ino = 0; - dirp->ent.d_reclen = 0; + /* Last directory entry read */ + entp = NULL; + } - return &dirp->ent; + return entp; } /* @@ -444,7 +483,7 @@ _wclosedir( } else { /* Invalid directory stream */ - DIRENT_SET_ERRNO (EBADF); + dirent_set_errno (EBADF); ok = /*failure*/-1; } return ok; @@ -458,24 +497,79 @@ static void _wrewinddir( _WDIR* dirp) { - if (dirp != NULL) { - /* release search handle */ + if (dirp) { + /* Release existing search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose (dirp->handle); } - /* Open new search handle and retrieve the first directory entry */ - dirp->handle = FindFirstFileW (dirp->patt, &dirp->find_data); - if (dirp->handle != INVALID_HANDLE_VALUE) { - /* a directory entry is now waiting in memory */ - dirp->cached = 1; - } else { - /* Failed to re-open directory: no directory entry in memory */ - dirp->cached = 0; - } + /* Open new search handle */ + dirent_first (dirp); } } +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileW (dirp->patt, &dirp->data); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to re-open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + } + return datap; +} + +/* Get next directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occured */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + /* * Open directory stream using plain old C-string. */ @@ -484,54 +578,30 @@ opendir( const char *dirname) { struct DIR *dirp; - errno_t error = 0; + int error; /* Must have directory name */ - if (dirname == NULL) { - DIRENT_SET_ERRNO (ENOENT); + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); return NULL; } - /* Allocate memory for multi-byte string directory structures */ + /* Allocate memory for DIR structure */ dirp = (DIR*) malloc (sizeof (struct DIR)); if (dirp) { wchar_t wname[PATH_MAX + 1]; size_t n; - /* - * Convert directory name to wide-character string. - * - * Be ware of the return schemantics of MultiByteToWideChar() -- - * the function basically returns the number of characters written to - * output buffer or zero if the conversion fails. However, the - * function does not necessarily zero-terminate the output - * buffer and may return 0xFFFD if the string contains invalid - * characters! - */ - n = MultiByteToWideChar( - CP_ACP, /* Input code page */ - MB_PRECOMPOSED, /* Conversion flags */ - dirname, /* Input string */ - -1, /* Length of input string */ - wname, /* Output buffer */ - PATH_MAX); /* Size of output buffer */ - if (n > 0 && n < PATH_MAX) { + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX); + if (!error) { - /* Zero-terminate output buffer */ - wname[n] = '\0'; - - /* Open directory stream with wide-character string file name */ + /* Open directory stream using wide-character name */ dirp->wdirp = _wopendir (wname); if (dirp->wdirp) { - - /* Initialize directory structure */ - dirp->ent.d_name[0] = '\0'; - dirp->ent.d_namlen = 0; - dirp->ent.d_type = 0; - dirp->ent.d_ino = 0; - dirp->ent.d_reclen = 0; - - + /* Directory stream opened */ + error = 0; } else { /* Failed to open directory stream */ error = 1; @@ -564,91 +634,95 @@ opendir( /* * Read next directory entry. * - * When working with console, please note that file names returned by - * readdir() are represented in the default ANSI code page while the - * console typically runs on another code page. Thus, non-ASCII characters - * will not usually display correctly. The problem can be fixed in two ways: - * (1) change the character set of console to 1252 using chcp utility and use - * Lucida Console font, or (2) always use _cprintf function when writing to - * console. The _cprinf() will re-encode ANSI strings to the console code - * page so non-ASCII characters will display correcly. + * When working with text consoles, please note that file names returned by + * readdir() are represented in the default ANSI code page while any output to + * console is typically formatted on another code page. Thus, non-ASCII + * characters in file names will not usually display correctly on console. The + * problem can be fixed in two ways: (1) change the character set of console + * to 1252 using chcp utility and use Lucida Console font, or (2) use + * _cprintf function when writing to console. The _cprinf() will re-encode + * ANSI strings to the console code page so many non-ASCII characters will + * display correcly. */ static struct dirent* readdir( DIR *dirp) { - struct dirent *p; - struct _wdirent *wp; + WIN32_FIND_DATAW *datap; + struct dirent *entp; - /* Read next directory entry using wide-character string functions */ - wp = _wreaddir (dirp->wdirp); - if (wp) { + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, dirp->ent.d_name, MAX_PATH + 1, datap->cFileName, MAX_PATH); /* - * Convert file name to multi-byte string. + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. * - * Be ware of the return schemantics of WideCharToMultiByte() -- - * the function basically returns the number of bytes - * written to output buffer or zero if the conversion fails. - * However, the function does not necessarily zero-terminate the - * buffer and it may even return 0xFFFD the string contains - * invalid characters! + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. */ - n = WideCharToMultiByte( - CP_ACP, /* Output code page */ - 0, /* Conversion flags */ - wp->d_name, /* Input string */ - wp->d_namlen, /* Length of input string */ - dirp->ent.d_name, /* Output buffer */ - PATH_MAX, /* Size of output buffer */ - NULL, /* Replacement character */ - NULL); /* If chars were replaced */ - if (n > 0 && n < PATH_MAX) { + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, dirp->ent.d_name, MAX_PATH + 1, datap->cAlternateFileName, + sizeof (datap->cAlternateFileName) / + sizeof (datap->cAlternateFileName[0])); + } - /* Zero-terminate buffer */ - dirp->ent.d_name[n] = '\0'; + if (!error) { + DWORD attr; /* Initialize directory entry for return */ - p = &dirp->ent; + entp = &dirp->ent; - /* Compute length */ - p->d_namlen = strnlen (dirp->ent.d_name, PATH_MAX); + /* Length of file name excluding zero terminator */ + entp->d_namlen = n - 1; - /* Copy file attributes */ - p->d_type = wp->d_type; + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entp->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entp->d_type = DT_DIR; + } else { + entp->d_type = DT_REG; + } /* Reset dummy fields */ - p->d_ino = 0; - p->d_reclen = sizeof (dirp->ent); - + entp->d_ino = 0; + entp->d_reclen = sizeof (struct dirent); } else { - /* * Cannot convert file name to multi-byte string so construct * an errornous directory entry and return that. Note that * we cannot return NULL as that would stop the processing * of directory entries completely. */ - p = &dirp->ent; - p->d_name[0] = '?'; - p->d_name[1] = '\0'; - p->d_namlen = 1; - p->d_type = DT_UNKNOWN; - p->d_ino = 0; - p->d_reclen = 0; - + entp = &dirp->ent; + entp->d_name[0] = '?'; + entp->d_name[1] = '\0'; + entp->d_namlen = 1; + entp->d_type = DT_UNKNOWN; + entp->d_ino = 0; + entp->d_reclen = 0; } } else { - - /* End of directory stream */ - p = NULL; - + /* No more directory entries */ + entp = NULL; } - return p; + return entp; } /* @@ -669,9 +743,11 @@ closedir( free (dirp); } else { + /* Invalid directory stream */ - DIRENT_SET_ERRNO (EBADF); + dirent_set_errno (EBADF); ok = /*failure*/-1; + } return ok; } @@ -687,6 +763,124 @@ rewinddir( _wrewinddir (dirp->wdirp); } +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string */ + n = mbstowcs (wcstr, mbstr, count); + if (n < sizeInWords) { + + /* Zero-terminate output buffer */ + if (wcstr) { + wcstr[n] = 0; + } + + /* Length of resuting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string */ + n = wcstombs (mbstr, wcstr, count); + if (n < sizeInBytes) { + + /* Zero-terminate output buffer */ + if (mbstr) { + mbstr[n] = '\0'; + } + + /* Lenght of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) + + /* Microsoft Visual Studio */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler */ + errno = error; + +#endif +} + #ifdef __cplusplus }