diff --git a/fios.h b/fios.h new file mode 100644 --- /dev/null +++ b/fios.h @@ -0,0 +1,38 @@ +/* $Id$ */ + +#ifndef FIOS_H +#define FIOS_H + +/* Implementation of opendir/readdir/closedir for Windows */ +#if defined(WIN32) +#include +typedef struct DIR DIR; + +typedef struct dirent { // XXX - only d_name implemented + char *d_name; /* name of found file */ + /* little hack which will point to parent DIR struct which will + * save us a call to GetFileAttributes if we want information + * about the file (for example in function fio_bla */ + DIR *dir; +} dirent; + +struct DIR { + HANDLE hFind; + /* the dirent returned by readdir. + * note: having only one global instance is not possible because + * multiple independent opendir/readdir sequences must be supported. */ + dirent ent; + WIN32_FIND_DATA fd; + /* since opendir calls FindFirstFile, we need a means of telling the + * first call to readdir that we already have a file. + * that's the case iff this is true */ + bool at_first_entry; +}; + +DIR *opendir(const char *path); +struct dirent *readdir(DIR *d); +int closedir(DIR *d); + +#endif /* defined(WIN32) */ + +#endif /* FIOS_H */ diff --git a/win32.c b/win32.c --- a/win32.c +++ b/win32.c @@ -18,7 +18,11 @@ #include #include "variables.h" #include "win32.h" +#include "fios.h" // opendir/readdir/closedir #include +#include +#include +#include static bool _has_console; @@ -602,6 +606,107 @@ static void Win32InitializeExceptions(vo } #endif /* _MSC_VER */ +/* Code below for windows version of opendir/readdir/closedir copied and + * modified from Jan Wassenberg's GPL implementation posted over at + * http://www.gamedev.net/community/forums/topic.asp?topic_id=364584&whichpage=1� */ + +/* suballocator - satisfies most requests with a reusable static instance. + * this avoids hundreds of alloc/free which would fragment the heap. + * To guarantee thread-safety, we fall back to malloc if the instance is + * already in use (it's important to avoid suprises since this is such a + * low-level routine). */ +static DIR _global_dir; +static bool _global_dir_is_in_use = false; + +static inline DIR *dir_calloc(void) +{ + DIR *d; + + if (_global_dir_is_in_use) { + d = calloc(1, sizeof(DIR)); + } else { + _global_dir_is_in_use = true; + d = &_global_dir; + memset(d, 0, sizeof(DIR)); + } + return d; +} + +static inline void dir_free(DIR *d) +{ + if (d == &_global_dir) { + _global_dir_is_in_use = false; + } else { + free(d); + } +} + +DIR *opendir(const char *path) +{ + char search_path[MAX_PATH]; + DIR *d; + UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box + DWORD fa = GetFileAttributes(path); + + /* not a directory or path not found */ + if (fa == INVALID_FILE_ATTRIBUTES || (fa & FILE_ATTRIBUTE_DIRECTORY) == 0) { + errno = ENOENT; + return NULL; + } + + d = dir_calloc(); + if (d == NULL) { + errno = ENOMEM; + return NULL; + } + + /* build search path for FindFirstFile */ + snprintf(search_path, lengthof(search_path), "%s" PATHSEP "*", path); + d->hFind = FindFirstFile(search_path, &d->fd); + SetErrorMode(sem); // restore previous setting + + if (d->hFind == INVALID_HANDLE_VALUE) { + /* not an error - the directory is just empty */ + if (GetLastError() == ERROR_NO_MORE_FILES) return NULL; + dir_free(d); + } + + d->ent.dir = d; + d->at_first_entry = true; + return d; +} + +struct dirent *readdir(DIR *d) +{ + DWORD prev_err = GetLastError(); // avoid polluting last error + + if (d->at_first_entry) { + /* the directory was empty when opened */ + if (d->hFind == INVALID_HANDLE_VALUE) return NULL; + d->at_first_entry = false; + goto already_have_file; + } + + /* Go until the end of directory or until a valid entry was found */ + if (!FindNextFile(d->hFind, &d->fd)) { // determine cause and bail + if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err); + return NULL; + } + +already_have_file: + /* This entry has passed all checks; return information about it. + * (note: d_name is a pointer; see struct dirent definition) */ + d->ent.d_name = d->fd.cFileName; + return &d->ent; +} + +int closedir(DIR *d) +{ + FindClose(d->hFind); + dir_free(d); + return 0; +} + static char *_fios_path; static char *_fios_save_path; static char *_fios_scn_path;