Skip to content

Commit 4788ca0

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 57089b3 commit 4788ca0

File tree

2 files changed

+35
-18
lines changed

2 files changed

+35
-18
lines changed

compat/win32/dirent.c

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
#include "../../git-compat-util.h"
22

3-
struct DIR {
4-
struct dirent dd_dir; /* includes d_type */
3+
typedef struct dirent_DIR {
4+
struct DIR base_dir; /* extend base struct DIR */
55
HANDLE dd_handle; /* FindFirstFile handle */
66
int dd_stat; /* 0-based index */
7-
};
7+
struct dirent dd_dir; /* includes d_type */
8+
} dirent_DIR;
9+
10+
DIR *(*opendir)(const char *dirname) = dirent_opendir;
811

912
static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
1013
{
11-
/* convert UTF-16 name to UTF-8 */
12-
xwcstoutf(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
14+
/* convert UTF-16 name to UTF-8 (d_name points to dirent_DIR.dd_name) */
15+
xwcstoutf(ent->d_name, fdata->cFileName, MAX_PATH * 3);
1316

1417
/* Set file type, based on WIN32_FIND_DATA */
1518
if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
@@ -18,7 +21,7 @@ static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
1821
ent->d_type = DT_REG;
1922
}
2023

21-
struct dirent *readdir(DIR *dir)
24+
static struct dirent *dirent_readdir(dirent_DIR *dir)
2225
{
2326
if (!dir) {
2427
errno = EBADF; /* No set_errno for mingw */
@@ -45,7 +48,7 @@ struct dirent *readdir(DIR *dir)
4548
return &dir->dd_dir;
4649
}
4750

48-
int closedir(DIR *dir)
51+
static int dirent_closedir(dirent_DIR *dir)
4952
{
5053
if (!dir) {
5154
errno = EBADF;
@@ -57,13 +60,13 @@ int closedir(DIR *dir)
5760
return 0;
5861
}
5962

60-
DIR *opendir(const char *name)
63+
DIR *dirent_opendir(const char *name)
6164
{
6265
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
6366
WIN32_FIND_DATAW fdata;
6467
HANDLE h;
6568
int len;
66-
DIR *dir;
69+
dirent_DIR *dir;
6770

6871
/* convert name to UTF-16 and check length < MAX_PATH */
6972
if ((len = xutftowcs_path(pattern, name)) < 0)
@@ -84,9 +87,11 @@ DIR *opendir(const char *name)
8487
}
8588

8689
/* initialize DIR structure and copy first dir entry */
87-
dir = xmalloc(sizeof(DIR));
90+
dir = xmalloc(sizeof(dirent_DIR) + MAX_PATH);
91+
dir->base_dir.preaddir = (struct dirent *(*)(DIR *dir)) dirent_readdir;
92+
dir->base_dir.pclosedir = (int (*)(DIR *dir)) dirent_closedir;
8893
dir->dd_handle = h;
8994
dir->dd_stat = 0;
9095
finddata2dirent(&dir->dd_dir, &fdata);
91-
return dir;
96+
return (DIR*) dir;
9297
}

compat/win32/dirent.h

Lines changed: 19 additions & 7 deletions
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)