imagex-win32.c: Fix comments
[wimlib] / programs / imagex-win32.c
1 /* Windows-specific code for wimlib-imagex.  */
2
3 #ifndef __WIN32__
4 #  error "This file contains Windows code"
5 #endif
6
7 #include "imagex-win32.h"
8 #include <assert.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <io.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <windows.h>
15
16 /* Replacement for glob() in Windows native builds that operates on wide
17  * characters.  */
18 int
19 win32_wglob(const wchar_t *pattern, int flags,
20             int (*errfunc)(const wchar_t *epath, int eerrno),
21             glob_t *pglob)
22 {
23         WIN32_FIND_DATAW dat;
24         DWORD err;
25         HANDLE hFind;
26         int ret;
27         size_t nspaces;
28
29         const wchar_t *backslash, *end_slash;
30         size_t prefix_len;
31
32         backslash = wcsrchr(pattern, L'\\');
33         end_slash = wcsrchr(pattern, L'/');
34
35         if (backslash > end_slash)
36                 end_slash = backslash;
37
38         if (end_slash)
39                 prefix_len = end_slash - pattern + 1;
40         else
41                 prefix_len = 0;
42
43         /* This function does not support all functionality of the POSIX glob(),
44          * so make sure the parameters are consistent with supported
45          * functionality. */
46         assert(errfunc == NULL);
47         assert((flags & GLOB_ERR) == GLOB_ERR);
48         assert((flags & ~(GLOB_NOSORT | GLOB_ERR)) == 0);
49
50         hFind = FindFirstFileW(pattern, &dat);
51         if (hFind == INVALID_HANDLE_VALUE) {
52                 err = GetLastError();
53                 if (err == ERROR_FILE_NOT_FOUND) {
54                         errno = 0;
55                         return GLOB_NOMATCH;
56                 } else {
57                         /* The other possible error codes for FindFirstFile()
58                          * are undocumented. */
59                         errno = EIO;
60                         return GLOB_ABORTED;
61                 }
62         }
63         pglob->gl_pathc = 0;
64         pglob->gl_pathv = NULL;
65         nspaces = 0;
66         do {
67                 wchar_t *path;
68                 if (pglob->gl_pathc == nspaces) {
69                         size_t new_nspaces;
70                         wchar_t **pathv;
71
72                         new_nspaces = nspaces * 2 + 1;  
73                         pathv = realloc(pglob->gl_pathv,
74                                         new_nspaces * sizeof(pglob->gl_pathv[0]));
75                         if (!pathv)
76                                 goto oom;
77                         pglob->gl_pathv = pathv;
78                         nspaces = new_nspaces;
79                 }
80                 size_t filename_len = wcslen(dat.cFileName);
81                 size_t len_needed = prefix_len + filename_len;
82
83                 path = malloc((len_needed + 1) * sizeof(wchar_t));
84                 if (!path)
85                         goto oom;
86
87                 wmemcpy(path, pattern, prefix_len);
88                 wmemcpy(path + prefix_len, dat.cFileName, filename_len + 1);
89                 pglob->gl_pathv[pglob->gl_pathc++] = path;
90         } while (FindNextFileW(hFind, &dat));
91         err = GetLastError();
92         CloseHandle(hFind);
93         if (err == ERROR_NO_MORE_FILES) {
94                 errno = 0;
95                 return 0;
96         } else {
97                 /* Other possible error codes for FindNextFile() are
98                  * undocumented */
99                 errno = EIO;
100                 ret = GLOB_ABORTED;
101                 goto fail_globfree;
102         }
103 oom:
104         CloseHandle(hFind);
105         errno = ENOMEM;
106         ret = GLOB_NOSPACE;
107 fail_globfree:
108         globfree(pglob);
109         return ret;
110 }
111
112 void
113 globfree(glob_t *pglob)
114 {
115         size_t i;
116         for (i = 0; i < pglob->gl_pathc; i++)
117                 free(pglob->gl_pathv[i]);
118         free(pglob->gl_pathv);
119 }
120
121 /* Convert a string from the "current Windows codepage" to UTF-16LE.  */
122 wchar_t *
123 win32_mbs_to_wcs(const char *mbs, size_t mbs_nbytes, size_t *num_wchars_ret)
124 {
125         if (mbs_nbytes > INT_MAX) {
126                 fwprintf(stderr, L"ERROR: too much data (%zu bytes)!\n",
127                          mbs_nbytes);
128                 return NULL;
129         }
130         if (mbs_nbytes == 0) {
131                 *num_wchars_ret = 0;
132                 return (wchar_t*)mbs;
133         }
134         int len = MultiByteToWideChar(CP_ACP,
135                                       MB_ERR_INVALID_CHARS,
136                                       mbs,
137                                       mbs_nbytes,
138                                       NULL,
139                                       0);
140         if (len <= 0)
141                 goto out_invalid;
142         wchar_t *wcs = malloc(len * sizeof(wchar_t));
143         if (!wcs) {
144                 fwprintf(stderr, L"ERROR: out of memory!\n");
145                 return NULL;
146         }
147         int len2 = MultiByteToWideChar(CP_ACP,
148                                        MB_ERR_INVALID_CHARS,
149                                        mbs,
150                                        mbs_nbytes,
151                                        wcs,
152                                        len);
153         if (len2 != len) {
154                 free(wcs);
155                 goto out_invalid;
156         }
157         *num_wchars_ret = len;
158         return wcs;
159 out_invalid:
160         fwprintf(stderr,
161 L"ERROR: Invalid multi-byte string in the text file you provided as input!\n"
162 L"       Maybe try converting your text file to UTF-16LE?\n"
163         );
164         return NULL;
165 }
166
167 static inline bool
168 is_path_separator(wchar_t c)
169 {
170         return c == L'/' || c == L'\\';
171 }
172
173 /* basename() (modifying, trailing-slash stripping version) for wide-character
174  * strings.  Understands both forward and backward slashes.  */
175 wchar_t *
176 win32_wbasename(wchar_t *path)
177 {
178         wchar_t *p = wcschr(path, L'\0');
179
180         p--;
181         while (p >= path && is_path_separator(*p))
182                 *p-- = '\0';
183         while (p >= path && !is_path_separator(*p))
184                 p--;
185         p++;
186         return p;
187 }
188
189 /* Set a file descriptor to binary mode.  */
190 void set_fd_to_binary_mode(int fd)
191 {
192         _setmode(fd, _O_BINARY);
193 }