Skip to content

Commit b0bae7f

Browse files
committed
Merge branch 'sk/mingw-dirent'
* sk/mingw-dirent: Win32 dirent: improve dirent implementation Win32 dirent: clarify #include directives Win32 dirent: change FILENAME_MAX to MAX_PATH Win32 dirent: remove unused dirent.d_reclen member Win32 dirent: remove unused dirent.d_ino member
2 parents 641830c + d8890ce commit b0bae7f

File tree

3 files changed

+59
-67
lines changed

3 files changed

+59
-67
lines changed

compat/win32/dirent.c

Lines changed: 55 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,91 @@
1-
#include "../git-compat-util.h"
2-
#include "dirent.h"
1+
#include "../../git-compat-util.h"
32

43
struct DIR {
54
struct dirent dd_dir; /* includes d_type */
65
HANDLE dd_handle; /* FindFirstFile handle */
76
int dd_stat; /* 0-based index */
8-
char dd_name[1]; /* extend struct */
97
};
108

9+
static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata)
10+
{
11+
/* copy file name from WIN32_FIND_DATA to dirent */
12+
memcpy(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
13+
14+
/* Set file type, based on WIN32_FIND_DATA */
15+
if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
16+
ent->d_type = DT_DIR;
17+
else
18+
ent->d_type = DT_REG;
19+
}
20+
1121
DIR *opendir(const char *name)
1222
{
13-
DWORD attrs = GetFileAttributesA(name);
23+
char pattern[MAX_PATH];
24+
WIN32_FIND_DATAA fdata;
25+
HANDLE h;
1426
int len;
15-
DIR *p;
27+
DIR *dir;
1628

17-
/* check for valid path */
18-
if (attrs == INVALID_FILE_ATTRIBUTES) {
19-
errno = ENOENT;
29+
/* check that name is not NULL */
30+
if (!name) {
31+
errno = EINVAL;
2032
return NULL;
2133
}
22-
23-
/* check if it's a directory */
24-
if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
25-
errno = ENOTDIR;
26-
return NULL;
27-
}
28-
2934
/* check that the pattern won't be too long for FindFirstFileA */
3035
len = strlen(name);
31-
if (is_dir_sep(name[len - 1]))
32-
len--;
3336
if (len + 2 >= MAX_PATH) {
3437
errno = ENAMETOOLONG;
3538
return NULL;
3639
}
37-
38-
p = malloc(sizeof(DIR) + len + 2);
39-
if (!p)
40+
/* copy name to temp buffer */
41+
memcpy(pattern, name, len + 1);
42+
43+
/* append optional '/' and wildcard '*' */
44+
if (len && !is_dir_sep(pattern[len - 1]))
45+
pattern[len++] = '/';
46+
pattern[len++] = '*';
47+
pattern[len] = 0;
48+
49+
/* open find handle */
50+
h = FindFirstFileA(pattern, &fdata);
51+
if (h == INVALID_HANDLE_VALUE) {
52+
DWORD err = GetLastError();
53+
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
4054
return NULL;
55+
}
4156

42-
memset(p, 0, sizeof(DIR) + len + 2);
43-
strcpy(p->dd_name, name);
44-
p->dd_name[len] = '/';
45-
p->dd_name[len+1] = '*';
46-
47-
p->dd_handle = INVALID_HANDLE_VALUE;
48-
return p;
57+
/* initialize DIR structure and copy first dir entry */
58+
dir = xmalloc(sizeof(DIR));
59+
dir->dd_handle = h;
60+
dir->dd_stat = 0;
61+
finddata2dirent(&dir->dd_dir, &fdata);
62+
return dir;
4963
}
5064

5165
struct dirent *readdir(DIR *dir)
5266
{
53-
WIN32_FIND_DATAA buf;
54-
HANDLE handle;
55-
56-
if (!dir || !dir->dd_handle) {
67+
if (!dir) {
5768
errno = EBADF; /* No set_errno for mingw */
5869
return NULL;
5970
}
6071

61-
if (dir->dd_handle == INVALID_HANDLE_VALUE && dir->dd_stat == 0) {
62-
DWORD lasterr;
63-
handle = FindFirstFileA(dir->dd_name, &buf);
64-
lasterr = GetLastError();
65-
dir->dd_handle = handle;
66-
if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
67-
errno = err_win_to_posix(lasterr);
72+
/* if first entry, dirent has already been set up by opendir */
73+
if (dir->dd_stat) {
74+
/* get next entry and convert from WIN32_FIND_DATA to dirent */
75+
WIN32_FIND_DATAA fdata;
76+
if (FindNextFileA(dir->dd_handle, &fdata)) {
77+
finddata2dirent(&dir->dd_dir, &fdata);
78+
} else {
79+
DWORD lasterr = GetLastError();
80+
/* POSIX says you shouldn't set errno when readdir can't
81+
find any more files; so, if another error we leave it set. */
82+
if (lasterr != ERROR_NO_MORE_FILES)
83+
errno = err_win_to_posix(lasterr);
6884
return NULL;
6985
}
70-
} else if (dir->dd_handle == INVALID_HANDLE_VALUE) {
71-
return NULL;
72-
} else if (!FindNextFileA(dir->dd_handle, &buf)) {
73-
DWORD lasterr = GetLastError();
74-
FindClose(dir->dd_handle);
75-
dir->dd_handle = INVALID_HANDLE_VALUE;
76-
/* POSIX says you shouldn't set errno when readdir can't
77-
find any more files; so, if another error we leave it set. */
78-
if (lasterr != ERROR_NO_MORE_FILES)
79-
errno = err_win_to_posix(lasterr);
80-
return NULL;
8186
}
8287

83-
/* We get here if `buf' contains valid data. */
84-
strcpy(dir->dd_dir.d_name, buf.cFileName);
8588
++dir->dd_stat;
86-
87-
/* Set file type, based on WIN32_FIND_DATA */
88-
dir->dd_dir.d_type = 0;
89-
if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
90-
dir->dd_dir.d_type |= DT_DIR;
91-
else
92-
dir->dd_dir.d_type |= DT_REG;
93-
9489
return &dir->dd_dir;
9590
}
9691

@@ -101,8 +96,7 @@ int closedir(DIR *dir)
10196
return -1;
10297
}
10398

104-
if (dir->dd_handle != INVALID_HANDLE_VALUE)
105-
FindClose(dir->dd_handle);
99+
FindClose(dir->dd_handle);
106100
free(dir);
107101
return 0;
108102
}

compat/win32/dirent.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,8 @@ typedef struct DIR DIR;
99
#define DT_LNK 3
1010

1111
struct dirent {
12-
long d_ino; /* Always zero. */
13-
char d_name[FILENAME_MAX]; /* File name. */
14-
union {
15-
unsigned short d_reclen; /* Always zero. */
16-
unsigned char d_type; /* Reimplementation adds this */
17-
};
12+
unsigned char d_type; /* file type to prevent lstat after readdir */
13+
char d_name[MAX_PATH]; /* file name */
1814
};
1915

2016
DIR *opendir(const char *dirname);

config.mak.uname

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ ifeq ($(uname_S),Windows)
354354
NO_POSIX_GOODIES = UnfortunatelyYes
355355
NATIVE_CRLF = YesPlease
356356
DEFAULT_HELP_FORMAT = html
357+
NO_D_INO_IN_DIRENT = YesPlease
357358

358359
CC = compat/vcbuild/scripts/clink.pl
359360
AR = compat/vcbuild/scripts/lib.pl
@@ -503,6 +504,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
503504
NO_INET_NTOP = YesPlease
504505
NO_POSIX_GOODIES = UnfortunatelyYes
505506
DEFAULT_HELP_FORMAT = html
507+
NO_D_INO_IN_DIRENT = YesPlease
506508
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -D_USE_32BIT_TIME_T -DNOGDI -Icompat -Icompat/win32
507509
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
508510
COMPAT_OBJS += compat/mingw.o compat/winansi.o \

0 commit comments

Comments
 (0)