Skip to content

Commit b280eef

Browse files
kbleesdscho
authored andcommitted
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 <[email protected]> Signed-off-by: Johannes Schindelin <[email protected]>
1 parent e3e1ce1 commit b280eef

File tree

2 files changed

+38
-18
lines changed

2 files changed

+38
-18
lines changed

compat/win32/dirent.c

+19-11
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
#include "../../git-compat-util.h"
22

3-
struct DIR {
4-
struct dirent dd_dir; /* includes d_type */
3+
#pragma GCC diagnostic push
4+
#pragma GCC diagnostic ignored "-Wpedantic"
5+
typedef struct dirent_DIR {
6+
struct DIR base_dir; /* extend base struct DIR */
57
HANDLE dd_handle; /* FindFirstFile handle */
68
int dd_stat; /* 0-based index */
7-
};
9+
struct dirent dd_dir; /* includes d_type */
10+
} dirent_DIR;
11+
#pragma GCC diagnostic pop
12+
13+
DIR *(*opendir)(const char *dirname) = dirent_opendir;
814

915
static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
1016
{
11-
/* convert UTF-16 name to UTF-8 */
12-
xwcstoutf(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
17+
/* convert UTF-16 name to UTF-8 (d_name points to dirent_DIR.dd_name) */
18+
xwcstoutf(ent->d_name, fdata->cFileName, MAX_PATH * 3);
1319

1420
/* Set file type, based on WIN32_FIND_DATA */
1521
if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
@@ -18,7 +24,7 @@ static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
1824
ent->d_type = DT_REG;
1925
}
2026

21-
struct dirent *readdir(DIR *dir)
27+
static struct dirent *dirent_readdir(dirent_DIR *dir)
2228
{
2329
if (!dir) {
2430
errno = EBADF; /* No set_errno for mingw */
@@ -45,7 +51,7 @@ struct dirent *readdir(DIR *dir)
4551
return &dir->dd_dir;
4652
}
4753

48-
int closedir(DIR *dir)
54+
static int dirent_closedir(dirent_DIR *dir)
4955
{
5056
if (!dir) {
5157
errno = EBADF;
@@ -57,13 +63,13 @@ int closedir(DIR *dir)
5763
return 0;
5864
}
5965

60-
DIR *opendir(const char *name)
66+
DIR *dirent_opendir(const char *name)
6167
{
6268
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
6369
WIN32_FIND_DATAW fdata;
6470
HANDLE h;
6571
int len;
66-
DIR *dir;
72+
dirent_DIR *dir;
6773

6874
/* convert name to UTF-16 and check length < MAX_PATH */
6975
if ((len = xutftowcs_path(pattern, name)) < 0)
@@ -84,9 +90,11 @@ DIR *opendir(const char *name)
8490
}
8591

8692
/* initialize DIR structure and copy first dir entry */
87-
dir = xmalloc(sizeof(DIR));
93+
dir = xmalloc(sizeof(dirent_DIR) + MAX_PATH);
94+
dir->base_dir.preaddir = (struct dirent *(*)(DIR *dir)) dirent_readdir;
95+
dir->base_dir.pclosedir = (int (*)(DIR *dir)) dirent_closedir;
8896
dir->dd_handle = h;
8997
dir->dd_stat = 0;
9098
finddata2dirent(&dir->dd_dir, &fdata);
91-
return dir;
99+
return (DIR*) dir;
92100
}

compat/win32/dirent.h

+19-7
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
11
#ifndef DIRENT_H
22
#define DIRENT_H
33

4-
typedef struct DIR DIR;
5-
64
#define DT_UNKNOWN 0
75
#define DT_DIR 1
86
#define DT_REG 2
97
#define DT_LNK 3
108

119
struct dirent {
12-
unsigned char d_type; /* file type to prevent lstat after readdir */
13-
char d_name[MAX_PATH * 3]; /* file name (* 3 for UTF-8 conversion) */
10+
unsigned char d_type; /* file type to prevent lstat after readdir */
11+
char d_name[FLEX_ARRAY]; /* file name */
1412
};
1513

16-
DIR *opendir(const char *dirname);
17-
struct dirent *readdir(DIR *dir);
18-
int closedir(DIR *dir);
14+
/*
15+
* Base DIR structure, contains pointers to readdir/closedir implementations so
16+
* that opendir may choose a concrete implementation on a call-by-call basis.
17+
*/
18+
typedef struct DIR {
19+
struct dirent *(*preaddir)(struct DIR *dir);
20+
int (*pclosedir)(struct DIR *dir);
21+
} DIR;
22+
23+
/* default dirent implementation */
24+
extern DIR *dirent_opendir(const char *dirname);
25+
26+
/* current dirent implementation */
27+
extern DIR *(*opendir)(const char *dirname);
28+
29+
#define readdir(dir) (dir->preaddir(dir))
30+
#define closedir(dir) (dir->pclosedir(dir))
1931

2032
#endif /* DIRENT_H */

0 commit comments

Comments
 (0)