Merge branch 'rs/fix-alt-odb-path-comparison'
[git] / compat / win32 / dirent.c
1 #include "../../git-compat-util.h"
2
3 struct DIR {
4         struct dirent dd_dir; /* includes d_type */
5         HANDLE dd_handle;     /* FindFirstFile handle */
6         int dd_stat;          /* 0-based index */
7 };
8
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
21 DIR *opendir(const char *name)
22 {
23         char pattern[MAX_PATH];
24         WIN32_FIND_DATAA fdata;
25         HANDLE h;
26         int len;
27         DIR *dir;
28
29         /* check that name is not NULL */
30         if (!name) {
31                 errno = EINVAL;
32                 return NULL;
33         }
34         /* check that the pattern won't be too long for FindFirstFileA */
35         len = strlen(name);
36         if (len + 2 >= MAX_PATH) {
37                 errno = ENAMETOOLONG;
38                 return NULL;
39         }
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);
54                 return NULL;
55         }
56
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;
63 }
64
65 struct dirent *readdir(DIR *dir)
66 {
67         if (!dir) {
68                 errno = EBADF; /* No set_errno for mingw */
69                 return NULL;
70         }
71
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);
84                         return NULL;
85                 }
86         }
87
88         ++dir->dd_stat;
89         return &dir->dd_dir;
90 }
91
92 int closedir(DIR *dir)
93 {
94         if (!dir) {
95                 errno = EBADF;
96                 return -1;
97         }
98
99         FindClose(dir->dd_handle);
100         free(dir);
101         return 0;
102 }