Skip to content

Commit efcbeaa

Browse files
kbleesGit for Windows Build Agent
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 <blees@dcon.de> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent e0c3ca0 commit efcbeaa

2 files changed

Lines changed: 40 additions & 18 deletions

File tree

compat/win32/dirent.c

Lines changed: 19 additions & 11 deletions
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_REPARSE_POINT)
@@ -21,7 +27,7 @@ static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
2127
ent->d_type = DT_REG;
2228
}
2329

24-
struct dirent *readdir(DIR *dir)
30+
static struct dirent *dirent_readdir(dirent_DIR *dir)
2531
{
2632
if (!dir) {
2733
errno = EBADF; /* No set_errno for mingw */
@@ -48,7 +54,7 @@ struct dirent *readdir(DIR *dir)
4854
return &dir->dd_dir;
4955
}
5056

51-
int closedir(DIR *dir)
57+
static int dirent_closedir(dirent_DIR *dir)
5258
{
5359
if (!dir) {
5460
errno = EBADF;
@@ -60,13 +66,13 @@ int closedir(DIR *dir)
6066
return 0;
6167
}
6268

63-
DIR *opendir(const char *name)
69+
DIR *dirent_opendir(const char *name)
6470
{
6571
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
6672
WIN32_FIND_DATAW fdata;
6773
HANDLE h;
6874
int len;
69-
DIR *dir;
75+
dirent_DIR *dir;
7076

7177
/* convert name to UTF-16 and check length < MAX_PATH */
7278
if ((len = xutftowcs_path(pattern, name)) < 0)
@@ -87,9 +93,11 @@ DIR *opendir(const char *name)
8793
}
8894

8995
/* initialize DIR structure and copy first dir entry */
90-
dir = xmalloc(sizeof(DIR));
96+
dir = xmalloc(sizeof(dirent_DIR) + MAX_PATH);
97+
dir->base_dir.preaddir = (struct dirent *(*)(DIR *dir)) dirent_readdir;
98+
dir->base_dir.pclosedir = (int (*)(DIR *dir)) dirent_closedir;
9199
dir->dd_handle = h;
92100
dir->dd_stat = 0;
93101
finddata2dirent(&dir->dd_dir, &fdata);
94-
return dir;
102+
return (DIR*) dir;
95103
}

compat/win32/dirent.h

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,34 @@
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+
#define opendir git_opendir
27+
28+
/* current dirent implementation */
29+
extern DIR *(*opendir)(const char *dirname);
30+
31+
#define readdir(dir) (dir->preaddir(dir))
32+
#define closedir(dir) (dir->pclosedir(dir))
1933

2034
#endif /* DIRENT_H */

0 commit comments

Comments
 (0)