mingw: make the dirent implementation pluggable

Emulating the POSIX `dirent` API on Windows via
`FindFirstFile()`/`FindNextFile()` is pretty staightforward, however,
most of the information provided in the `WIN32_FIND_DATA` structure is
thrown away in the process. A more sophisticated implementation may
cache this data, e.g. for later reuse in calls to `lstat()`.

Make the `dirent` implementation pluggable so that it can be switched at
runtime, e.g. based on a config option.

Define a base DIR structure with pointers to `readdir()`/`closedir()`
that match the `opendir()` implementation (similar to vtable pointers in
Object-Oriented Programming). Define `readdir()`/`closedir()` so that
they call the function pointers in the `DIR` structure. This allows to
choose the `opendir()` implementation on a call-by-call basis.

Make the fixed-size `dirent.d_name` buffer a flex array, as `d_name` may
be implementation specific (e.g. a caching implementation may allocate a
`struct dirent` with _just_ the size needed to hold the `d_name` in
question).

Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Karsten Blees 2013-09-08 14:18:40 +02:00 коммит произвёл Matthew John Cheetham
Родитель 33319f0b8d
Коммит 5e72cadde8
2 изменённых файлов: 38 добавлений и 18 удалений

Просмотреть файл

@ -1,15 +1,21 @@
#include "../../git-compat-util.h"
struct DIR {
struct dirent dd_dir; /* includes d_type */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
typedef struct dirent_DIR {
struct DIR base_dir; /* extend base struct DIR */
HANDLE dd_handle; /* FindFirstFile handle */
int dd_stat; /* 0-based index */
};
struct dirent dd_dir; /* includes d_type */
} dirent_DIR;
#pragma GCC diagnostic pop
DIR *(*opendir)(const char *dirname) = dirent_opendir;
static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
{
/* convert UTF-16 name to UTF-8 */
xwcstoutf(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
/* convert UTF-16 name to UTF-8 (d_name points to dirent_DIR.dd_name) */
xwcstoutf(ent->d_name, fdata->cFileName, MAX_PATH * 3);
/* Set file type, based on WIN32_FIND_DATA */
if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
@ -18,7 +24,7 @@ static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
ent->d_type = DT_REG;
}
struct dirent *readdir(DIR *dir)
static struct dirent *dirent_readdir(dirent_DIR *dir)
{
if (!dir) {
errno = EBADF; /* No set_errno for mingw */
@ -45,7 +51,7 @@ struct dirent *readdir(DIR *dir)
return &dir->dd_dir;
}
int closedir(DIR *dir)
static int dirent_closedir(dirent_DIR *dir)
{
if (!dir) {
errno = EBADF;
@ -57,13 +63,13 @@ int closedir(DIR *dir)
return 0;
}
DIR *opendir(const char *name)
DIR *dirent_opendir(const char *name)
{
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
WIN32_FIND_DATAW fdata;
HANDLE h;
int len;
DIR *dir;
dirent_DIR *dir;
/* convert name to UTF-16 and check length < MAX_PATH */
if ((len = xutftowcs_path(pattern, name)) < 0)
@ -84,9 +90,11 @@ DIR *opendir(const char *name)
}
/* initialize DIR structure and copy first dir entry */
dir = xmalloc(sizeof(DIR));
dir = xmalloc(sizeof(dirent_DIR) + MAX_PATH);
dir->base_dir.preaddir = (struct dirent *(*)(DIR *dir)) dirent_readdir;
dir->base_dir.pclosedir = (int (*)(DIR *dir)) dirent_closedir;
dir->dd_handle = h;
dir->dd_stat = 0;
finddata2dirent(&dir->dd_dir, &fdata);
return dir;
return (DIR*) dir;
}

Просмотреть файл

@ -1,8 +1,6 @@
#ifndef DIRENT_H
#define DIRENT_H
typedef struct DIR DIR;
#define DT_UNKNOWN 0
#define DT_DIR 1
#define DT_REG 2
@ -10,11 +8,25 @@ typedef struct DIR DIR;
struct dirent {
unsigned char d_type; /* file type to prevent lstat after readdir */
char d_name[MAX_PATH * 3]; /* file name (* 3 for UTF-8 conversion) */
char d_name[FLEX_ARRAY]; /* file name */
};
DIR *opendir(const char *dirname);
struct dirent *readdir(DIR *dir);
int closedir(DIR *dir);
/*
* Base DIR structure, contains pointers to readdir/closedir implementations so
* that opendir may choose a concrete implementation on a call-by-call basis.
*/
typedef struct DIR {
struct dirent *(*preaddir)(struct DIR *dir);
int (*pclosedir)(struct DIR *dir);
} DIR;
/* default dirent implementation */
extern DIR *dirent_opendir(const char *dirname);
/* current dirent implementation */
extern DIR *(*opendir)(const char *dirname);
#define readdir(dir) (dir->preaddir(dir))
#define closedir(dir) (dir->pclosedir(dir))
#endif /* DIRENT_H */