Skip to content

Commit 7cc868e

Browse files
kbleesdscho
authored andcommitted
Win32: 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 (i.e. similar to vtable pointers in OOP). 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. Move the fixed sized dirent.d_name buffer to the dirent-specific DIR structure, as d_name may be implementation specific (e.g. a caching implementation may just set d_name to point into the cache instead of copying the entire file name string). Signed-off-by: Karsten Blees <blees@dcon.de>
1 parent 85e5470 commit 7cc868e

File tree

2 files changed

+36
-17
lines changed

2 files changed

+36
-17
lines changed

compat/win32/dirent.c

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

3-
struct DIR {
3+
typedef struct dirent_DIR {
4+
struct DIR base_dir; /* extend base struct DIR */
45
struct dirent dd_dir; /* includes d_type */
56
HANDLE dd_handle; /* FindFirstFile handle */
67
int dd_stat; /* 0-based index */
7-
};
8+
char dd_name[MAX_PATH * 3]; /* file name (* 3 for UTF-8 conversion) */
9+
} dirent_DIR;
10+
11+
DIR *(*opendir)(const char *dirname) = dirent_opendir;
812

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

1418
/* Set file type, based on WIN32_FIND_DATA */
1519
if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
@@ -18,7 +22,7 @@ static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
1822
ent->d_type = DT_REG;
1923
}
2024

21-
struct dirent *readdir(DIR *dir)
25+
static struct dirent *dirent_readdir(dirent_DIR *dir)
2226
{
2327
if (!dir) {
2428
errno = EBADF; /* No set_errno for mingw */
@@ -45,7 +49,7 @@ struct dirent *readdir(DIR *dir)
4549
return &dir->dd_dir;
4650
}
4751

48-
int closedir(DIR *dir)
52+
static int dirent_closedir(dirent_DIR *dir)
4953
{
5054
if (!dir) {
5155
errno = EBADF;
@@ -57,13 +61,13 @@ int closedir(DIR *dir)
5761
return 0;
5862
}
5963

60-
DIR *opendir(const char *name)
64+
DIR *dirent_opendir(const char *name)
6165
{
6266
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
6367
WIN32_FIND_DATAW fdata;
6468
HANDLE h;
6569
int len;
66-
DIR *dir;
70+
dirent_DIR *dir;
6771

6872
/* convert name to UTF-16 and check length < MAX_PATH */
6973
if ((len = xutftowcs_path(pattern, name)) < 0)
@@ -84,9 +88,12 @@ DIR *opendir(const char *name)
8488
}
8589

8690
/* initialize DIR structure and copy first dir entry */
87-
dir = xmalloc(sizeof(DIR));
91+
dir = xmalloc(sizeof(dirent_DIR));
92+
dir->base_dir.preaddir = (struct dirent *(*)(DIR *dir)) dirent_readdir;
93+
dir->base_dir.pclosedir = (int (*)(DIR *dir)) dirent_closedir;
94+
dir->dd_dir.d_name = dir->dd_name;
8895
dir->dd_handle = h;
8996
dir->dd_stat = 0;
9097
finddata2dirent(&dir->dd_dir, &fdata);
91-
return dir;
98+
return (DIR*) dir;
9299
}

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; /* 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)