aboutsummaryrefslogtreecommitdiffstats
path: root/util/folder/dirent_win.c
blob: 8eed4801c7a5ce3ca5b1d2935210f8712f636e7b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <util/system/defaults.h>

#ifdef _win_

    #include <stdio.h>
    #include "dirent_win.h"

    #if defined(_MSC_VER) && (_MSC_VER < 1900)
void __cdecl _dosmaperr(unsigned long);

static void SetErrno() {
    _dosmaperr(GetLastError());
}
    #else
void __cdecl __acrt_errno_map_os_error(unsigned long const oserrno);

static void SetErrno() {
    __acrt_errno_map_os_error(GetLastError());
}
    #endif

struct DIR* opendir(const char* dirname) {
    struct DIR* dir = (struct DIR*)malloc(sizeof(struct DIR));
    if (!dir) {
        return NULL;
    }
    dir->sh = INVALID_HANDLE_VALUE;
    dir->fff_templ = NULL;
    dir->file_no = 0;
    dir->readdir_buf = NULL;

    int len = strlen(dirname);
    // Remove trailing slashes
    while (len && (dirname[len - 1] == '\\' || dirname[len - 1] == '/')) {
        --len;
    }
    int len_converted = MultiByteToWideChar(CP_UTF8, 0, dirname, len, 0, 0);
    if (len_converted == 0) {
        closedir(dir);
        return NULL;
    }
    dir->fff_templ = (WCHAR*)malloc((len_converted + 5) * sizeof(WCHAR));
    if (!dir->fff_templ) {
        closedir(dir);
        return NULL;
    }
    MultiByteToWideChar(CP_UTF8, 0, dirname, len, dir->fff_templ, len_converted);

    WCHAR append[] = {'\\', '*', '.', '*', 0};
    memcpy(dir->fff_templ + len_converted, append, sizeof(append));
    dir->sh = FindFirstFileW(dir->fff_templ, &dir->wfd);
    if (dir->sh == INVALID_HANDLE_VALUE) {
        SetErrno();
        closedir(dir);
        return NULL;
    }

    return dir;
}

int closedir(struct DIR* dir) {
    if (dir->sh != INVALID_HANDLE_VALUE) {
        FindClose(dir->sh);
    }
    free(dir->fff_templ);
    free(dir->readdir_buf);
    free(dir);
    return 0;
}

int readdir_r(struct DIR* dir, struct dirent* entry, struct dirent** result) {
    if (!FindNextFileW(dir->sh, &dir->wfd)) {
        int err = GetLastError();
        *result = 0;
        if (err == ERROR_NO_MORE_FILES) {
            SetLastError(0);
            return 0;
        } else {
            return err;
        }
    }
    entry->d_fileno = dir->file_no++;
    entry->d_reclen = sizeof(struct dirent);
    if (dir->wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
        (dir->wfd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT || dir->wfd.dwReserved0 == IO_REPARSE_TAG_SYMLINK))
    {
        entry->d_type = DT_LNK;
    } else if (dir->wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
        entry->d_type = DT_DIR;
    } else {
        entry->d_type = DT_REG;
    }
    int len = lstrlenW(dir->wfd.cFileName);
    int conv_len = WideCharToMultiByte(CP_UTF8, 0, dir->wfd.cFileName, len, 0, 0, 0, 0);
    if (conv_len == 0) {
        return -1;
    }
    if (conv_len > sizeof(entry->d_name) - 1) {
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return ERROR_INSUFFICIENT_BUFFER;
    }
    entry->d_namlen = conv_len;
    WideCharToMultiByte(CP_UTF8, 0, dir->wfd.cFileName, len, entry->d_name, conv_len, 0, 0);
    entry->d_name[conv_len] = 0;
    *result = entry;
    return 0;
}

struct dirent* readdir(struct DIR* dir) {
    struct dirent* res;
    if (!dir->readdir_buf) {
        dir->readdir_buf = (struct dirent*)malloc(sizeof(struct dirent));
        if (dir->readdir_buf == 0) {
            return 0;
        }
    }
    readdir_r(dir, dir->readdir_buf, &res);
    return res;
}

void rewinddir(struct DIR* dir) {
    FindClose(dir->sh);
    dir->sh = FindFirstFileW(dir->fff_templ, &dir->wfd);
    dir->file_no = 0;
}

#endif // _win_