/*1* Dirent interface for Microsoft Visual Studio2* Version 1.213*4* Copyright (C) 2006-2012 Toni Ronkko5* This file is part of dirent. Dirent may be freely distributed6* under the MIT license. For all details and documentation, see7* https://github.com/tronkko/dirent8*/9#ifndef DIRENT_H10#define DIRENT_H1112/*13* Define architecture flags so we don't need to include windows.h.14* Avoiding windows.h makes it simpler to use windows sockets in conjunction15* with dirent.h.16*/17#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86)18# define _X86_19#endif20#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_AMD64)21#define _AMD64_22#endif2324#include <stdio.h>25#include <stdarg.h>26#include <windef.h>27#include <winbase.h>28#include <wchar.h>29#include <string.h>30#include <stdlib.h>31#include <malloc.h>32#include <sys/types.h>33#include <sys/stat.h>34#include <errno.h>3536/* Indicates that d_type field is available in dirent structure */37#define _DIRENT_HAVE_D_TYPE3839/* Indicates that d_namlen field is available in dirent structure */40#define _DIRENT_HAVE_D_NAMLEN4142/* Entries missing from MSVC 6.0 */43#if !defined(FILE_ATTRIBUTE_DEVICE)44# define FILE_ATTRIBUTE_DEVICE 0x4045#endif4647/* File type and permission flags for stat(), general mask */48#if !defined(S_IFMT)49# define S_IFMT _S_IFMT50#endif5152/* Directory bit */53#if !defined(S_IFDIR)54# define S_IFDIR _S_IFDIR55#endif5657/* Character device bit */58#if !defined(S_IFCHR)59# define S_IFCHR _S_IFCHR60#endif6162/* Pipe bit */63#if !defined(S_IFFIFO)64# define S_IFFIFO _S_IFFIFO65#endif6667/* Regular file bit */68#if !defined(S_IFREG)69# define S_IFREG _S_IFREG70#endif7172/* Read permission */73#if !defined(S_IREAD)74# define S_IREAD _S_IREAD75#endif7677/* Write permission */78#if !defined(S_IWRITE)79# define S_IWRITE _S_IWRITE80#endif8182/* Execute permission */83#if !defined(S_IEXEC)84# define S_IEXEC _S_IEXEC85#endif8687/* Pipe */88#if !defined(S_IFIFO)89# define S_IFIFO _S_IFIFO90#endif9192/* Block device */93#if !defined(S_IFBLK)94# define S_IFBLK 095#endif9697/* Link */98#if !defined(S_IFLNK)99# define S_IFLNK 0100#endif101102/* Socket */103#if !defined(S_IFSOCK)104# define S_IFSOCK 0105#endif106107/* Read user permission */108#if !defined(S_IRUSR)109# define S_IRUSR S_IREAD110#endif111112/* Write user permission */113#if !defined(S_IWUSR)114# define S_IWUSR S_IWRITE115#endif116117/* Execute user permission */118#if !defined(S_IXUSR)119# define S_IXUSR 0120#endif121122/* Read group permission */123#if !defined(S_IRGRP)124# define S_IRGRP 0125#endif126127/* Write group permission */128#if !defined(S_IWGRP)129# define S_IWGRP 0130#endif131132/* Execute group permission */133#if !defined(S_IXGRP)134# define S_IXGRP 0135#endif136137/* Read others permission */138#if !defined(S_IROTH)139# define S_IROTH 0140#endif141142/* Write others permission */143#if !defined(S_IWOTH)144# define S_IWOTH 0145#endif146147/* Execute others permission */148#if !defined(S_IXOTH)149# define S_IXOTH 0150#endif151152/* Maximum length of file name */153#if !defined(PATH_MAX)154# define PATH_MAX MAX_PATH155#endif156#if !defined(FILENAME_MAX)157# define FILENAME_MAX MAX_PATH158#endif159#if !defined(NAME_MAX)160# define NAME_MAX FILENAME_MAX161#endif162163/* File type flags for d_type */164#define DT_UNKNOWN 0165#define DT_REG S_IFREG166#define DT_DIR S_IFDIR167#define DT_FIFO S_IFIFO168#define DT_SOCK S_IFSOCK169#define DT_CHR S_IFCHR170#define DT_BLK S_IFBLK171#define DT_LNK S_IFLNK172173/* Macros for converting between st_mode and d_type */174#define IFTODT(mode) ((mode) & S_IFMT)175#define DTTOIF(type) (type)176177/*178* File type macros. Note that block devices, sockets and links cannot be179* distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are180* only defined for compatibility. These macros should always return false181* on Windows.182*/183#if !defined(S_ISFIFO)184# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)185#endif186#if !defined(S_ISDIR)187# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)188#endif189#if !defined(S_ISREG)190# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)191#endif192#if !defined(S_ISLNK)193# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)194#endif195#if !defined(S_ISSOCK)196# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)197#endif198#if !defined(S_ISCHR)199# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)200#endif201#if !defined(S_ISBLK)202# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)203#endif204205/* Return the exact length of d_namlen without zero terminator */206#define _D_EXACT_NAMLEN(p) ((p)->d_namlen)207208/* Return number of bytes needed to store d_namlen */209#define _D_ALLOC_NAMLEN(p) (PATH_MAX)210211212#ifdef __cplusplus213extern "C" {214#endif215216217/* Wide-character version */218struct _wdirent {219/* Always zero */220long d_ino;221222/* Structure size */223unsigned short d_reclen;224225/* Length of name without \0 */226size_t d_namlen;227228/* File type */229int d_type;230231/* File name */232wchar_t d_name[PATH_MAX];233};234typedef struct _wdirent _wdirent;235236struct _WDIR {237/* Current directory entry */238struct _wdirent ent;239240/* Private file data */241WIN32_FIND_DATAW data;242243/* True if data is valid */244int cached;245246/* Win32 search handle */247HANDLE handle;248249/* Initial directory name */250wchar_t *patt;251};252typedef struct _WDIR _WDIR;253254static _WDIR *_wopendir (const wchar_t *dirname);255static struct _wdirent *_wreaddir (_WDIR *dirp);256static int _wclosedir (_WDIR *dirp);257static void _wrewinddir (_WDIR* dirp);258259260/* For compatibility with Symbian */261#define wdirent _wdirent262#define WDIR _WDIR263#define wopendir _wopendir264#define wreaddir _wreaddir265#define wclosedir _wclosedir266#define wrewinddir _wrewinddir267268269/* Multi-byte character versions */270struct dirent {271/* Always zero */272long d_ino;273274/* Structure size */275unsigned short d_reclen;276277/* Length of name without \0 */278size_t d_namlen;279280/* File type */281int d_type;282283/* File name */284char d_name[PATH_MAX];285};286typedef struct dirent dirent;287288struct DIR {289struct dirent ent;290struct _WDIR *wdirp;291};292typedef struct DIR DIR;293294static DIR *opendir (const char *dirname);295static struct dirent *readdir (DIR *dirp);296static int closedir (DIR *dirp);297static void rewinddir (DIR* dirp);298299300/* Internal utility functions */301static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp);302static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp);303304static int dirent_mbstowcs_s(305size_t *pReturnValue,306wchar_t *wcstr,307size_t sizeInWords,308const char *mbstr,309size_t count);310311static int dirent_wcstombs_s(312size_t *pReturnValue,313char *mbstr,314size_t sizeInBytes,315const wchar_t *wcstr,316size_t count);317318static void dirent_set_errno (int error);319320/*321* Open directory stream DIRNAME for read and return a pointer to the322* internal working area that is used to retrieve individual directory323* entries.324*/325static _WDIR*326_wopendir(327const wchar_t *dirname)328{329_WDIR *dirp = NULL;330int error;331332/* Must have directory name */333if (dirname == NULL || dirname[0] == '\0') {334dirent_set_errno (ENOENT);335return NULL;336}337338/* Allocate new _WDIR structure */339dirp = (_WDIR*) malloc (sizeof (struct _WDIR));340if (dirp != NULL) {341DWORD n;342343/* Reset _WDIR structure */344dirp->handle = INVALID_HANDLE_VALUE;345dirp->patt = NULL;346dirp->cached = 0;347348/* Compute the length of full path plus zero terminator */349n = GetFullPathNameW (dirname, 0, NULL, NULL);350351/* Allocate room for absolute directory name and search pattern */352dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);353if (dirp->patt) {354355/*356* Convert relative directory name to an absolute one. This357* allows rewinddir() to function correctly even when current358* working directory is changed between opendir() and rewinddir().359*/360n = GetFullPathNameW (dirname, n, dirp->patt, NULL);361if (n > 0) {362wchar_t *p;363364/* Append search pattern \* to the directory name */365p = dirp->patt + n;366if (dirp->patt < p) {367switch (p[-1]) {368case '\\':369case '/':370case ':':371/* Directory ends in path separator, e.g. c:\temp\ */372/*NOP*/;373break;374375default:376/* Directory name doesn't end in path separator */377*p++ = '\\';378}379}380*p++ = '*';381*p = '\0';382383/* Open directory stream and retrieve the first entry */384if (dirent_first (dirp)) {385/* Directory stream opened successfully */386error = 0;387} else {388/* Cannot retrieve first entry */389error = 1;390dirent_set_errno (ENOENT);391}392393} else {394/* Cannot retrieve full path name */395dirent_set_errno (ENOENT);396error = 1;397}398399} else {400/* Cannot allocate memory for search pattern */401error = 1;402}403404} else {405/* Cannot allocate _WDIR structure */406error = 1;407}408409/* Clean up in case of error */410if (error && dirp) {411_wclosedir (dirp);412dirp = NULL;413}414415return dirp;416}417418/*419* Read next directory entry. The directory entry is returned in dirent420* structure in the d_name field. Individual directory entries returned by421* this function include regular files, sub-directories, pseudo-directories422* "." and ".." as well as volume labels, hidden files and system files.423*/424static struct _wdirent*425_wreaddir(426_WDIR *dirp)427{428WIN32_FIND_DATAW *datap;429struct _wdirent *entp;430431/* Read next directory entry */432datap = dirent_next (dirp);433if (datap) {434size_t n;435DWORD attr;436437/* Pointer to directory entry to return */438entp = &dirp->ent;439440/*441* Copy file name as wide-character string. If the file name is too442* long to fit in to the destination buffer, then truncate file name443* to PATH_MAX characters and zero-terminate the buffer.444*/445n = 0;446while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) {447entp->d_name[n] = datap->cFileName[n];448n++;449}450dirp->ent.d_name[n] = 0;451452/* Length of file name excluding zero terminator */453entp->d_namlen = n;454455/* File type */456attr = datap->dwFileAttributes;457if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {458entp->d_type = DT_CHR;459} else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {460entp->d_type = DT_DIR;461} else {462entp->d_type = DT_REG;463}464465/* Reset dummy fields */466entp->d_ino = 0;467entp->d_reclen = sizeof (struct _wdirent);468469} else {470471/* Last directory entry read */472entp = NULL;473474}475476return entp;477}478479/*480* Close directory stream opened by opendir() function. This invalidates the481* DIR structure as well as any directory entry read previously by482* _wreaddir().483*/484static int485_wclosedir(486_WDIR *dirp)487{488int ok;489if (dirp) {490491/* Release search handle */492if (dirp->handle != INVALID_HANDLE_VALUE) {493FindClose (dirp->handle);494dirp->handle = INVALID_HANDLE_VALUE;495}496497/* Release search pattern */498if (dirp->patt) {499free (dirp->patt);500dirp->patt = NULL;501}502503/* Release directory structure */504free (dirp);505ok = /*success*/0;506507} else {508/* Invalid directory stream */509dirent_set_errno (EBADF);510ok = /*failure*/-1;511}512return ok;513}514515/*516* Rewind directory stream such that _wreaddir() returns the very first517* file name again.518*/519static void520_wrewinddir(521_WDIR* dirp)522{523if (dirp) {524/* Release existing search handle */525if (dirp->handle != INVALID_HANDLE_VALUE) {526FindClose (dirp->handle);527}528529/* Open new search handle */530dirent_first (dirp);531}532}533534/* Get first directory entry (internal) */535static WIN32_FIND_DATAW*536dirent_first(537_WDIR *dirp)538{539WIN32_FIND_DATAW *datap;540541/* Open directory and retrieve the first entry */542dirp->handle = FindFirstFileW (dirp->patt, &dirp->data);543if (dirp->handle != INVALID_HANDLE_VALUE) {544545/* a directory entry is now waiting in memory */546datap = &dirp->data;547dirp->cached = 1;548549} else {550551/* Failed to re-open directory: no directory entry in memory */552dirp->cached = 0;553datap = NULL;554555}556return datap;557}558559/* Get next directory entry (internal) */560static WIN32_FIND_DATAW*561dirent_next(562_WDIR *dirp)563{564WIN32_FIND_DATAW *p;565566/* Get next directory entry */567if (dirp->cached != 0) {568569/* A valid directory entry already in memory */570p = &dirp->data;571dirp->cached = 0;572573} else if (dirp->handle != INVALID_HANDLE_VALUE) {574575/* Get the next directory entry from stream */576if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) {577/* Got a file */578p = &dirp->data;579} else {580/* The very last entry has been processed or an error occured */581FindClose (dirp->handle);582dirp->handle = INVALID_HANDLE_VALUE;583p = NULL;584}585586} else {587588/* End of directory stream reached */589p = NULL;590591}592593return p;594}595596/*597* Open directory stream using plain old C-string.598*/599static DIR*600opendir(601const char *dirname)602{603struct DIR *dirp;604int error;605606/* Must have directory name */607if (dirname == NULL || dirname[0] == '\0') {608dirent_set_errno (ENOENT);609return NULL;610}611612/* Allocate memory for DIR structure */613dirp = (DIR*) malloc (sizeof (struct DIR));614if (dirp) {615wchar_t wname[PATH_MAX];616size_t n;617618/* Convert directory name to wide-character string */619error = dirent_mbstowcs_s (&n, wname, PATH_MAX, dirname, PATH_MAX);620if (!error) {621622/* Open directory stream using wide-character name */623dirp->wdirp = _wopendir (wname);624if (dirp->wdirp) {625/* Directory stream opened */626error = 0;627} else {628/* Failed to open directory stream */629error = 1;630}631632} else {633/*634* Cannot convert file name to wide-character string. This635* occurs if the string contains invalid multi-byte sequences or636* the output buffer is too small to contain the resulting637* string.638*/639error = 1;640}641642} else {643/* Cannot allocate DIR structure */644error = 1;645}646647/* Clean up in case of error */648if (error && dirp) {649free (dirp);650dirp = NULL;651}652653return dirp;654}655656/*657* Read next directory entry.658*659* When working with text consoles, please note that file names returned by660* readdir() are represented in the default ANSI code page while any output to661* console is typically formatted on another code page. Thus, non-ASCII662* characters in file names will not usually display correctly on console. The663* problem can be fixed in two ways: (1) change the character set of console664* to 1252 using chcp utility and use Lucida Console font, or (2) use665* _cprintf function when writing to console. The _cprinf() will re-encode666* ANSI strings to the console code page so many non-ASCII characters will667* display correcly.668*/669static struct dirent*670readdir(671DIR *dirp)672{673WIN32_FIND_DATAW *datap;674struct dirent *entp;675676/* Read next directory entry */677datap = dirent_next (dirp->wdirp);678if (datap) {679size_t n;680int error;681682/* Attempt to convert file name to multi-byte string */683error = dirent_wcstombs_s(684&n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX);685686/*687* If the file name cannot be represented by a multi-byte string,688* then attempt to use old 8+3 file name. This allows traditional689* Unix-code to access some file names despite of unicode690* characters, although file names may seem unfamiliar to the user.691*692* Be ware that the code below cannot come up with a short file693* name unless the file system provides one. At least694* VirtualBox shared folders fail to do this.695*/696if (error && datap->cAlternateFileName[0] != '\0') {697error = dirent_wcstombs_s(698&n, dirp->ent.d_name, PATH_MAX,699datap->cAlternateFileName, PATH_MAX);700}701702if (!error) {703DWORD attr;704705/* Initialize directory entry for return */706entp = &dirp->ent;707708/* Length of file name excluding zero terminator */709entp->d_namlen = n - 1;710711/* File attributes */712attr = datap->dwFileAttributes;713if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {714entp->d_type = DT_CHR;715} else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {716entp->d_type = DT_DIR;717} else {718entp->d_type = DT_REG;719}720721/* Reset dummy fields */722entp->d_ino = 0;723entp->d_reclen = sizeof (struct dirent);724725} else {726/*727* Cannot convert file name to multi-byte string so construct728* an errornous directory entry and return that. Note that729* we cannot return NULL as that would stop the processing730* of directory entries completely.731*/732entp = &dirp->ent;733entp->d_name[0] = '?';734entp->d_name[1] = '\0';735entp->d_namlen = 1;736entp->d_type = DT_UNKNOWN;737entp->d_ino = 0;738entp->d_reclen = 0;739}740741} else {742/* No more directory entries */743entp = NULL;744}745746return entp;747}748749/*750* Close directory stream.751*/752static int753closedir(754DIR *dirp)755{756int ok;757if (dirp) {758759/* Close wide-character directory stream */760ok = _wclosedir (dirp->wdirp);761dirp->wdirp = NULL;762763/* Release multi-byte character version */764free (dirp);765766} else {767768/* Invalid directory stream */769dirent_set_errno (EBADF);770ok = /*failure*/-1;771772}773return ok;774}775776/*777* Rewind directory stream to beginning.778*/779static void780rewinddir(781DIR* dirp)782{783/* Rewind wide-character string directory stream */784_wrewinddir (dirp->wdirp);785}786787/* Convert multi-byte string to wide character string */788static int789dirent_mbstowcs_s(790size_t *pReturnValue,791wchar_t *wcstr,792size_t sizeInWords,793const char *mbstr,794size_t count)795{796int error;797798#if defined(_MSC_VER) && _MSC_VER >= 1400799800/* Microsoft Visual Studio 2005 or later */801error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count);802803#else804805/* Older Visual Studio or non-Microsoft compiler */806size_t n;807808/* Convert to wide-character string (or count characters) */809n = mbstowcs (wcstr, mbstr, sizeInWords);810if (!wcstr || n < count) {811812/* Zero-terminate output buffer */813if (wcstr && sizeInWords) {814if (n >= sizeInWords) {815n = sizeInWords - 1;816}817wcstr[n] = 0;818}819820/* Length of resuting multi-byte string WITH zero terminator */821if (pReturnValue) {822*pReturnValue = n + 1;823}824825/* Success */826error = 0;827828} else {829830/* Could not convert string */831error = 1;832833}834835#endif836837return error;838}839840/* Convert wide-character string to multi-byte string */841static int842dirent_wcstombs_s(843size_t *pReturnValue,844char *mbstr,845size_t sizeInBytes, /* max size of mbstr */846const wchar_t *wcstr,847size_t count)848{849int error;850851#if defined(_MSC_VER) && _MSC_VER >= 1400852853/* Microsoft Visual Studio 2005 or later */854error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count);855856#else857858/* Older Visual Studio or non-Microsoft compiler */859size_t n;860861/* Convert to multi-byte string (or count the number of bytes needed) */862n = wcstombs (mbstr, wcstr, sizeInBytes);863if (!mbstr || n < count) {864865/* Zero-terminate output buffer */866if (mbstr && sizeInBytes) {867if (n >= sizeInBytes) {868n = sizeInBytes - 1;869}870mbstr[n] = '\0';871}872873/* Lenght of resulting multi-bytes string WITH zero-terminator */874if (pReturnValue) {875*pReturnValue = n + 1;876}877878/* Success */879error = 0;880881} else {882883/* Cannot convert string */884error = 1;885886}887888#endif889890return error;891}892893/* Set errno variable */894static void895dirent_set_errno(896int error)897{898#if defined(_MSC_VER) && _MSC_VER >= 1400899900/* Microsoft Visual Studio 2005 and later */901_set_errno (error);902903#else904905/* Non-Microsoft compiler or older Microsoft compiler */906errno = error;907908#endif909}910911912#ifdef __cplusplus913}914#endif915#endif /*DIRENT_H*/916917918919