Merge branch 'rs/refresh-beyond-symlink'
[gitweb.git] / compat / win32 / dirent.c
index fac7f2504731c8494a63a41dc3ba035a081c472c..52420ec7d4dad7b8e984bd7f5a36330f1fa88e24 100644 (file)
@@ -4,92 +4,78 @@ struct DIR {
        struct dirent dd_dir; /* includes d_type */
        HANDLE dd_handle;     /* FindFirstFile handle */
        int dd_stat;          /* 0-based index */
-       char dd_name[1];      /* extend struct */
 };
 
+static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
+{
+       /* convert UTF-16 name to UTF-8 */
+       xwcstoutf(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
+
+       /* Set file type, based on WIN32_FIND_DATA */
+       if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+               ent->d_type = DT_DIR;
+       else
+               ent->d_type = DT_REG;
+}
+
 DIR *opendir(const char *name)
 {
-       DWORD attrs = GetFileAttributesA(name);
+       wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
+       WIN32_FIND_DATAW fdata;
+       HANDLE h;
        int len;
-       DIR *p;
+       DIR *dir;
 
-       /* check for valid path */
-       if (attrs == INVALID_FILE_ATTRIBUTES) {
-               errno = ENOENT;
+       /* convert name to UTF-16 and check length < MAX_PATH */
+       if ((len = xutftowcs_path(pattern, name)) < 0)
                return NULL;
-       }
 
-       /* check if it's a directory */
-       if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
-               errno = ENOTDIR;
+       /* append optional '/' and wildcard '*' */
+       if (len && !is_dir_sep(pattern[len - 1]))
+               pattern[len++] = '/';
+       pattern[len++] = '*';
+       pattern[len] = 0;
+
+       /* open find handle */
+       h = FindFirstFileW(pattern, &fdata);
+       if (h == INVALID_HANDLE_VALUE) {
+               DWORD err = GetLastError();
+               errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
                return NULL;
        }
 
-       /* check that the pattern won't be too long for FindFirstFileA */
-       len = strlen(name);
-       if (is_dir_sep(name[len - 1]))
-               len--;
-       if (len + 2 >= MAX_PATH) {
-               errno = ENAMETOOLONG;
-               return NULL;
-       }
-
-       p = malloc(sizeof(DIR) + len + 2);
-       if (!p)
-               return NULL;
-
-       memset(p, 0, sizeof(DIR) + len + 2);
-       strcpy(p->dd_name, name);
-       p->dd_name[len] = '/';
-       p->dd_name[len+1] = '*';
-
-       p->dd_handle = INVALID_HANDLE_VALUE;
-       return p;
+       /* initialize DIR structure and copy first dir entry */
+       dir = xmalloc(sizeof(DIR));
+       dir->dd_handle = h;
+       dir->dd_stat = 0;
+       finddata2dirent(&dir->dd_dir, &fdata);
+       return dir;
 }
 
 struct dirent *readdir(DIR *dir)
 {
-       WIN32_FIND_DATAA buf;
-       HANDLE handle;
-
-       if (!dir || !dir->dd_handle) {
+       if (!dir) {
                errno = EBADF; /* No set_errno for mingw */
                return NULL;
        }
 
-       if (dir->dd_handle == INVALID_HANDLE_VALUE && dir->dd_stat == 0) {
-               DWORD lasterr;
-               handle = FindFirstFileA(dir->dd_name, &buf);
-               lasterr = GetLastError();
-               dir->dd_handle = handle;
-               if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
-                       errno = err_win_to_posix(lasterr);
+       /* if first entry, dirent has already been set up by opendir */
+       if (dir->dd_stat) {
+               /* get next entry and convert from WIN32_FIND_DATA to dirent */
+               WIN32_FIND_DATAW fdata;
+               if (FindNextFileW(dir->dd_handle, &fdata)) {
+                       finddata2dirent(&dir->dd_dir, &fdata);
+               } else {
+                       DWORD lasterr = GetLastError();
+                       /* POSIX says you shouldn't set errno when readdir can't
+                          find any more files; so, if another error we leave it set. */
+                       if (lasterr != ERROR_NO_MORE_FILES)
+                               errno = err_win_to_posix(lasterr);
                        return NULL;
                }
-       } else if (dir->dd_handle == INVALID_HANDLE_VALUE) {
-               return NULL;
-       } else if (!FindNextFileA(dir->dd_handle, &buf)) {
-               DWORD lasterr = GetLastError();
-               FindClose(dir->dd_handle);
-               dir->dd_handle = INVALID_HANDLE_VALUE;
-               /* POSIX says you shouldn't set errno when readdir can't
-                  find any more files; so, if another error we leave it set. */
-               if (lasterr != ERROR_NO_MORE_FILES)
-                       errno = err_win_to_posix(lasterr);
-               return NULL;
        }
 
-       /* We get here if `buf' contains valid data.  */
-       strcpy(dir->dd_dir.d_name, buf.cFileName);
        ++dir->dd_stat;
-
-       /* Set file type, based on WIN32_FIND_DATA */
-       dir->dd_dir.d_type = 0;
-       if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-               dir->dd_dir.d_type |= DT_DIR;
-       else
-               dir->dd_dir.d_type |= DT_REG;
-
        return &dir->dd_dir;
 }
 
@@ -100,8 +86,7 @@ int closedir(DIR *dir)
                return -1;
        }
 
-       if (dir->dd_handle != INVALID_HANDLE_VALUE)
-               FindClose(dir->dd_handle);
+       FindClose(dir->dd_handle);
        free(dir);
        return 0;
 }