compat / win32 / dirent.con commit setup.c: support multi-checkout repo setup (31e26eb)
   1#include "../../git-compat-util.h"
   2
   3struct DIR {
   4        struct dirent dd_dir; /* includes d_type */
   5        HANDLE dd_handle;     /* FindFirstFile handle */
   6        int dd_stat;          /* 0-based index */
   7};
   8
   9static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
  10{
  11        /* convert UTF-16 name to UTF-8 */
  12        xwcstoutf(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
  21DIR *opendir(const char *name)
  22{
  23        wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
  24        WIN32_FIND_DATAW fdata;
  25        HANDLE h;
  26        int len;
  27        DIR *dir;
  28
  29        /* convert name to UTF-16 and check length < MAX_PATH */
  30        if ((len = xutftowcs_path(pattern, name)) < 0)
  31                return NULL;
  32
  33        /* append optional '/' and wildcard '*' */
  34        if (len && !is_dir_sep(pattern[len - 1]))
  35                pattern[len++] = '/';
  36        pattern[len++] = '*';
  37        pattern[len] = 0;
  38
  39        /* open find handle */
  40        h = FindFirstFileW(pattern, &fdata);
  41        if (h == INVALID_HANDLE_VALUE) {
  42                DWORD err = GetLastError();
  43                errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
  44                return NULL;
  45        }
  46
  47        /* initialize DIR structure and copy first dir entry */
  48        dir = xmalloc(sizeof(DIR));
  49        dir->dd_handle = h;
  50        dir->dd_stat = 0;
  51        finddata2dirent(&dir->dd_dir, &fdata);
  52        return dir;
  53}
  54
  55struct dirent *readdir(DIR *dir)
  56{
  57        if (!dir) {
  58                errno = EBADF; /* No set_errno for mingw */
  59                return NULL;
  60        }
  61
  62        /* if first entry, dirent has already been set up by opendir */
  63        if (dir->dd_stat) {
  64                /* get next entry and convert from WIN32_FIND_DATA to dirent */
  65                WIN32_FIND_DATAW fdata;
  66                if (FindNextFileW(dir->dd_handle, &fdata)) {
  67                        finddata2dirent(&dir->dd_dir, &fdata);
  68                } else {
  69                        DWORD lasterr = GetLastError();
  70                        /* POSIX says you shouldn't set errno when readdir can't
  71                           find any more files; so, if another error we leave it set. */
  72                        if (lasterr != ERROR_NO_MORE_FILES)
  73                                errno = err_win_to_posix(lasterr);
  74                        return NULL;
  75                }
  76        }
  77
  78        ++dir->dd_stat;
  79        return &dir->dd_dir;
  80}
  81
  82int closedir(DIR *dir)
  83{
  84        if (!dir) {
  85                errno = EBADF;
  86                return -1;
  87        }
  88
  89        FindClose(dir->dd_handle);
  90        free(dir);
  91        return 0;
  92}