ocran 1.3.18 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.txt +309 -292
- data/LICENSE.txt +22 -22
- data/README.md +549 -533
- data/exe/ocran +5 -5
- data/ext/extconf.rb +15 -0
- data/lib/ocran/build_constants.rb +16 -16
- data/lib/ocran/build_facade.rb +17 -17
- data/lib/ocran/build_helper.rb +110 -105
- data/lib/ocran/command_output.rb +22 -22
- data/lib/ocran/dir_builder.rb +162 -0
- data/lib/ocran/direction.rb +636 -458
- data/lib/ocran/file_path_set.rb +69 -69
- data/lib/ocran/gem_spec_queryable.rb +172 -172
- data/lib/ocran/host_config_helper.rb +57 -44
- data/lib/ocran/inno_setup_script_builder.rb +111 -111
- data/lib/ocran/launcher_batch_builder.rb +85 -85
- data/lib/ocran/library_detector.rb +61 -61
- data/lib/ocran/library_detector_posix.rb +55 -0
- data/lib/ocran/option.rb +323 -273
- data/lib/ocran/refine_pathname.rb +104 -104
- data/lib/ocran/runner.rb +115 -105
- data/lib/ocran/runtime_environment.rb +46 -46
- data/lib/ocran/stub_builder.rb +298 -264
- data/lib/ocran/version.rb +5 -5
- data/lib/ocran/windows_command_escaping.rb +15 -15
- data/lib/ocran.rb +7 -7
- data/share/ocran/lzma.exe +0 -0
- data/src/Makefile +75 -0
- data/src/edicon.c +161 -0
- data/src/error.c +100 -0
- data/src/error.h +66 -0
- data/src/inst_dir.c +334 -0
- data/src/inst_dir.h +157 -0
- data/src/lzma/7zTypes.h +529 -0
- data/src/lzma/Compiler.h +43 -0
- data/src/lzma/LzmaDec.c +1363 -0
- data/src/lzma/LzmaDec.h +236 -0
- data/src/lzma/Precomp.h +10 -0
- data/src/script_info.c +246 -0
- data/src/script_info.h +7 -0
- data/src/stub.c +133 -0
- data/src/stub.manifest +29 -0
- data/src/stub.rc +3 -0
- data/src/system_utils.c +1002 -0
- data/src/system_utils.h +209 -0
- data/src/system_utils_posix.c +500 -0
- data/src/unpack.c +574 -0
- data/src/unpack.h +85 -0
- data/src/vit-ruby.ico +0 -0
- metadata +52 -16
- data/share/ocran/edicon.exe +0 -0
- data/share/ocran/stub.exe +0 -0
- data/share/ocran/stubw.exe +0 -0
data/src/system_utils.c
ADDED
|
@@ -0,0 +1,1002 @@
|
|
|
1
|
+
#include <windows.h>
|
|
2
|
+
#include <ntstatus.h>
|
|
3
|
+
#include <ntdef.h>
|
|
4
|
+
#include <bcrypt.h>
|
|
5
|
+
#include <stdbool.h>
|
|
6
|
+
#include "error.h"
|
|
7
|
+
#include "system_utils.h"
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* Returns true if `path` is a “clean” relative path:
|
|
11
|
+
* - not empty
|
|
12
|
+
* - does not start with a path separator
|
|
13
|
+
* - on Windows, no drive-letter spec (e.g. "C:\")
|
|
14
|
+
* - no empty segments ("//")
|
|
15
|
+
* - no "." or ".." segments
|
|
16
|
+
*/
|
|
17
|
+
bool IsCleanRelativePath(const char *path)
|
|
18
|
+
{
|
|
19
|
+
if (!path || !*path) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#ifdef _WIN32
|
|
24
|
+
/* Forbid Windows drive specification (e.g. "C:\") */
|
|
25
|
+
if (((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z'))
|
|
26
|
+
&& path[1] == ':'
|
|
27
|
+
&& is_path_separator(path[2])) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
#endif
|
|
31
|
+
|
|
32
|
+
/* Forbid absolute path (leading '/' or '\') */
|
|
33
|
+
if (is_path_separator(*path)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Validate each path segment */
|
|
38
|
+
const char *p = path;
|
|
39
|
+
while (*p) {
|
|
40
|
+
const char *start = p;
|
|
41
|
+
|
|
42
|
+
/* Advance until next separator or end-of-string */
|
|
43
|
+
while (*p && !is_path_separator(*p)) {
|
|
44
|
+
p++;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
size_t len = p - start;
|
|
48
|
+
|
|
49
|
+
/* Reject empty, "." or ".." segments */
|
|
50
|
+
if (len == 0
|
|
51
|
+
|| (len == 1 && start[0] == '.')
|
|
52
|
+
|| (len == 2 && start[0] == '.' && start[1] == '.')) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Skip over the separator */
|
|
57
|
+
if (*p) {
|
|
58
|
+
p++;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Combines two file path components into a single path, handling path separators.
|
|
66
|
+
char *JoinPath(const char *p1, const char *p2)
|
|
67
|
+
{
|
|
68
|
+
if (p1 == NULL || *p1 == '\0') {
|
|
69
|
+
APP_ERROR("p1 is null or empty");
|
|
70
|
+
return NULL;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (p2 == NULL || *p2 == '\0') {
|
|
74
|
+
APP_ERROR("p2 is null or empty");
|
|
75
|
+
return NULL;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
size_t p1_len = strlen(p1);
|
|
79
|
+
if (is_path_separator(p1[p1_len - 1])) { p1_len--; }
|
|
80
|
+
|
|
81
|
+
size_t p2_len = strlen(p2);
|
|
82
|
+
const char *p2_start = p2;
|
|
83
|
+
if (is_path_separator(*p2_start)) { p2_start++; p2_len--; }
|
|
84
|
+
|
|
85
|
+
size_t joined_len = p1_len + 1 + p2_len;
|
|
86
|
+
char *joined_path = calloc(1, joined_len + 1);
|
|
87
|
+
if (!joined_path) {
|
|
88
|
+
APP_ERROR("Failed to allocate buffer for join path");
|
|
89
|
+
return NULL;
|
|
90
|
+
}
|
|
91
|
+
memcpy(joined_path, p1, p1_len);
|
|
92
|
+
joined_path[p1_len] = PATH_SEPARATOR;
|
|
93
|
+
memcpy(joined_path + p1_len + 1, p2_start, p2_len);
|
|
94
|
+
joined_path[joined_len] = '\0';
|
|
95
|
+
|
|
96
|
+
return joined_path;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
char *GetParentPath(const char *path)
|
|
100
|
+
{
|
|
101
|
+
if (!path) {
|
|
102
|
+
APP_ERROR("path is NULL");
|
|
103
|
+
return NULL;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
size_t len = strlen(path);
|
|
107
|
+
size_t i = len;
|
|
108
|
+
|
|
109
|
+
/* Skip any trailing separators */
|
|
110
|
+
while (i > 0 && is_path_separator(path[i])) {
|
|
111
|
+
i--;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Skip the last segment’s characters */
|
|
115
|
+
while (i > 0 && !is_path_separator(path[i])) {
|
|
116
|
+
i--;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* i==0 ⇒ empty parent */
|
|
120
|
+
|
|
121
|
+
char *out = malloc(i + 1);
|
|
122
|
+
if (!out) {
|
|
123
|
+
APP_ERROR("Memory allocation failed for parent path");
|
|
124
|
+
return NULL;
|
|
125
|
+
}
|
|
126
|
+
memcpy(out, path, i);
|
|
127
|
+
out[i] = '\0';
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Converts a NULL-terminated UTF-16 string to a malloc-allocated UTF-8 string.
|
|
133
|
+
*
|
|
134
|
+
* @param utf16 Pointer to a NULL-terminated UTF-16 (wchar_t*) input string.
|
|
135
|
+
* @return malloc-allocated UTF-8 string on success (caller must free()),
|
|
136
|
+
* or NULL on failure (error logged via APP_ERROR).
|
|
137
|
+
*/
|
|
138
|
+
static char *utf16_to_utf8(const wchar_t *utf16)
|
|
139
|
+
{
|
|
140
|
+
int utf8_size = WideCharToMultiByte(
|
|
141
|
+
CP_UTF8,
|
|
142
|
+
WC_ERR_INVALID_CHARS,
|
|
143
|
+
utf16, -1, NULL, 0, NULL, NULL
|
|
144
|
+
);
|
|
145
|
+
if (utf8_size == 0) {
|
|
146
|
+
DWORD err = GetLastError();
|
|
147
|
+
APP_ERROR(
|
|
148
|
+
"Failed to calculate buffer size for UTF-8 conversion, Error=%lu",
|
|
149
|
+
err
|
|
150
|
+
);
|
|
151
|
+
return NULL;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
char *utf8 = calloc((size_t)utf8_size, sizeof(*utf8));
|
|
155
|
+
if (!utf8) {
|
|
156
|
+
APP_ERROR("Memory allocation failed for UTF-8 conversion");
|
|
157
|
+
return NULL;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
int written = WideCharToMultiByte(
|
|
161
|
+
CP_UTF8,
|
|
162
|
+
WC_ERR_INVALID_CHARS,
|
|
163
|
+
utf16, -1, utf8, utf8_size, NULL, NULL
|
|
164
|
+
);
|
|
165
|
+
if (written == 0) {
|
|
166
|
+
DWORD err = GetLastError();
|
|
167
|
+
APP_ERROR("Failed to convert UTF-16 to UTF-8, Error=%lu", err);
|
|
168
|
+
free(utf8);
|
|
169
|
+
return NULL;
|
|
170
|
+
}
|
|
171
|
+
return utf8;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Converts a NULL-terminated UTF-8 string to a malloc-allocated UTF-16 string.
|
|
176
|
+
*
|
|
177
|
+
* @param utf8 Pointer to a NULL-terminated UTF-8 (char*) input string.
|
|
178
|
+
* @return malloc-allocated UTF-16 string on success (caller must free()),
|
|
179
|
+
* or NULL on failure (error logged via APP_ERROR).
|
|
180
|
+
*/
|
|
181
|
+
static wchar_t *utf8_to_utf16(const char *utf8)
|
|
182
|
+
{
|
|
183
|
+
if (!utf8) {
|
|
184
|
+
APP_ERROR("utf8 is NULL");
|
|
185
|
+
return NULL;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
int utf16_size = MultiByteToWideChar(
|
|
189
|
+
CP_UTF8,
|
|
190
|
+
MB_ERR_INVALID_CHARS,
|
|
191
|
+
utf8, -1, NULL, 0
|
|
192
|
+
);
|
|
193
|
+
if (utf16_size == 0) {
|
|
194
|
+
DWORD err = GetLastError();
|
|
195
|
+
APP_ERROR(
|
|
196
|
+
"Failed to calculate buffer size for UTF-16 conversion, Error=%lu",
|
|
197
|
+
err
|
|
198
|
+
);
|
|
199
|
+
return NULL;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
wchar_t *utf16 = calloc((size_t)utf16_size, sizeof(*utf16));
|
|
203
|
+
if (!utf16) {
|
|
204
|
+
APP_ERROR("Memory allocation failed for UTF-16 conversion");
|
|
205
|
+
return NULL;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
int written = MultiByteToWideChar(
|
|
209
|
+
CP_UTF8,
|
|
210
|
+
MB_ERR_INVALID_CHARS,
|
|
211
|
+
utf8, -1, utf16, utf16_size
|
|
212
|
+
);
|
|
213
|
+
if (written == 0) {
|
|
214
|
+
DWORD err = GetLastError();
|
|
215
|
+
APP_ERROR("Failed to convert UTF-8 to UTF-16, Error=%lu", err);
|
|
216
|
+
free(utf16);
|
|
217
|
+
return NULL;
|
|
218
|
+
}
|
|
219
|
+
return utf16;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Recursively creates a directory and all its parent directories.
|
|
223
|
+
bool CreateDirectoriesRecursively(const char *dir)
|
|
224
|
+
{
|
|
225
|
+
if (!dir || !*dir) {
|
|
226
|
+
APP_ERROR("dir is NULL or empty");
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
bool result = false;
|
|
231
|
+
wchar_t *wpath = NULL;
|
|
232
|
+
|
|
233
|
+
size_t path_len = strlen(dir);
|
|
234
|
+
char *path = malloc(path_len + 1);
|
|
235
|
+
if (!path) {
|
|
236
|
+
APP_ERROR("Memory allocation failed for path");
|
|
237
|
+
|
|
238
|
+
goto cleanup;
|
|
239
|
+
}
|
|
240
|
+
memcpy(path, dir, path_len);
|
|
241
|
+
path[path_len] = '\0';
|
|
242
|
+
|
|
243
|
+
char *p = path + path_len;
|
|
244
|
+
do {
|
|
245
|
+
// Convert to UTF-16 for API call
|
|
246
|
+
wpath = utf8_to_utf16(path);
|
|
247
|
+
if (!wpath) {
|
|
248
|
+
APP_ERROR("Failed to convert path to UTF-16");
|
|
249
|
+
goto cleanup;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
DWORD path_attr = GetFileAttributesW(wpath);
|
|
253
|
+
if (path_attr != INVALID_FILE_ATTRIBUTES) {
|
|
254
|
+
if (path_attr & FILE_ATTRIBUTE_DIRECTORY) {
|
|
255
|
+
free(wpath);
|
|
256
|
+
wpath = NULL;
|
|
257
|
+
break;
|
|
258
|
+
} else {
|
|
259
|
+
APP_ERROR("Directory name conflicts with a file(%s)", path);
|
|
260
|
+
goto cleanup;
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
DWORD err = GetLastError();
|
|
264
|
+
if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
|
|
265
|
+
// continue;
|
|
266
|
+
} else {
|
|
267
|
+
APP_ERROR("Cannot access the directory, Error=%lu", err);
|
|
268
|
+
goto cleanup;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
free(wpath);
|
|
273
|
+
wpath = NULL;
|
|
274
|
+
|
|
275
|
+
while (p > path && !is_path_separator(*p)) {
|
|
276
|
+
p--;
|
|
277
|
+
}
|
|
278
|
+
*p = '\0';
|
|
279
|
+
} while (p >= path);
|
|
280
|
+
|
|
281
|
+
char *end = path + path_len;
|
|
282
|
+
for (; p < end; p++) {
|
|
283
|
+
if (*p) continue;
|
|
284
|
+
|
|
285
|
+
*p = PATH_SEPARATOR;
|
|
286
|
+
|
|
287
|
+
// Convert to UTF-16 for API call
|
|
288
|
+
wpath = utf8_to_utf16(path);
|
|
289
|
+
if (!wpath) {
|
|
290
|
+
APP_ERROR("Failed to convert path to UTF-16");
|
|
291
|
+
goto cleanup;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (!CreateDirectoryW(wpath, NULL)) {
|
|
295
|
+
DWORD err = GetLastError();
|
|
296
|
+
APP_ERROR("Failed to create directory '%s', Error=%lu", path, err);
|
|
297
|
+
goto cleanup;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
free(wpath);
|
|
301
|
+
wpath = NULL;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
result = true;
|
|
305
|
+
|
|
306
|
+
cleanup:
|
|
307
|
+
if (wpath) {
|
|
308
|
+
free(wpath);
|
|
309
|
+
}
|
|
310
|
+
if (path) {
|
|
311
|
+
free(path);
|
|
312
|
+
}
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Deletes a directory and all its contents recursively.
|
|
317
|
+
bool DeleteRecursively(const char *path)
|
|
318
|
+
{
|
|
319
|
+
if (!path || !*path) {
|
|
320
|
+
APP_ERROR("path is NULL or empty");
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
char *findPath = JoinPath(path, "*");
|
|
325
|
+
if (!findPath) {
|
|
326
|
+
APP_ERROR("Failed to build find path for deletion");
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
wchar_t *wfindPath = utf8_to_utf16(findPath);
|
|
331
|
+
free(findPath);
|
|
332
|
+
if (!wfindPath) {
|
|
333
|
+
APP_ERROR("Failed to convert find path to UTF-16");
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
WIN32_FIND_DATAW findData;
|
|
338
|
+
HANDLE handle = FindFirstFileW(wfindPath, &findData);
|
|
339
|
+
free(wfindPath);
|
|
340
|
+
|
|
341
|
+
if (handle != INVALID_HANDLE_VALUE) {
|
|
342
|
+
do {
|
|
343
|
+
const wchar_t *wname = findData.cFileName;
|
|
344
|
+
if ( wname[0]==L'.' && (!wname[1] || (wname[1]==L'.' && !wname[2])) ) {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Convert filename from UTF-16 to UTF-8
|
|
349
|
+
char *name = utf16_to_utf8(wname);
|
|
350
|
+
if (!name) {
|
|
351
|
+
APP_ERROR("Failed to convert filename to UTF-8");
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
char *subPath = JoinPath(path, name);
|
|
356
|
+
free(name);
|
|
357
|
+
if (!subPath) {
|
|
358
|
+
APP_ERROR("Failed to build delete file path");
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
wchar_t *wsubPath = utf8_to_utf16(subPath);
|
|
363
|
+
if (!wsubPath) {
|
|
364
|
+
APP_ERROR("Failed to convert subpath to UTF-16");
|
|
365
|
+
free(subPath);
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
370
|
+
DeleteRecursively(subPath);
|
|
371
|
+
} else if (!DeleteFileW(wsubPath)) {
|
|
372
|
+
DWORD err = GetLastError();
|
|
373
|
+
APP_ERROR("Failed to delete file, Error=%lu", err);
|
|
374
|
+
MoveFileExW(wsubPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
free(wsubPath);
|
|
378
|
+
free(subPath);
|
|
379
|
+
} while (FindNextFileW(handle, &findData));
|
|
380
|
+
FindClose(handle);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
wchar_t *wpath = utf8_to_utf16(path);
|
|
384
|
+
if (!wpath) {
|
|
385
|
+
APP_ERROR("Failed to convert path to UTF-16");
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (!RemoveDirectoryW(wpath)) {
|
|
390
|
+
DWORD err = GetLastError();
|
|
391
|
+
APP_ERROR("Failed to delete directory, Error=%lu", err);
|
|
392
|
+
MoveFileExW(wpath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
|
|
393
|
+
free(wpath);
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
free(wpath);
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
static bool generate_unique_name(char *buffer, size_t buffer_size)
|
|
401
|
+
{
|
|
402
|
+
char base32[] = "0123456789ABCDEF"
|
|
403
|
+
"GHIJKLMNOPQRSTUV"
|
|
404
|
+
;
|
|
405
|
+
|
|
406
|
+
NTSTATUS b = BCryptGenRandom(
|
|
407
|
+
0,
|
|
408
|
+
(PUCHAR)buffer,
|
|
409
|
+
buffer_size,
|
|
410
|
+
BCRYPT_USE_SYSTEM_PREFERRED_RNG
|
|
411
|
+
);
|
|
412
|
+
if (!NT_SUCCESS(b)) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
for (size_t i = 0; i < buffer_size; i++) {
|
|
417
|
+
buffer[i] = base32[buffer[i] & 0x1F];
|
|
418
|
+
}
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Defines the maximum number of attempts to create a unique directory.
|
|
423
|
+
#define MAX_RETRY_CREATE_UNIQUE_DIR 20U
|
|
424
|
+
|
|
425
|
+
// Generates a unique directory within a specified base path using a prefix.
|
|
426
|
+
char *CreateUniqueDirectory(char *tmpl)
|
|
427
|
+
{
|
|
428
|
+
if (!tmpl || !*tmpl) {
|
|
429
|
+
APP_ERROR("template is NULL or empty");
|
|
430
|
+
return NULL;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
size_t tmpl_len = strlen(tmpl);
|
|
434
|
+
char *tmpl_end = tmpl + tmpl_len;
|
|
435
|
+
char *x_str = tmpl_end;
|
|
436
|
+
size_t x_len = 0;
|
|
437
|
+
while (x_str > tmpl && *--x_str == 'X') {
|
|
438
|
+
x_len++;
|
|
439
|
+
}
|
|
440
|
+
if (x_len < 6) {
|
|
441
|
+
APP_ERROR("Template must end with at least six 'X's");
|
|
442
|
+
return NULL;
|
|
443
|
+
}
|
|
444
|
+
char *x_head = tmpl_end - x_len;
|
|
445
|
+
|
|
446
|
+
for (size_t retry = 0; retry < MAX_RETRY_CREATE_UNIQUE_DIR; retry++) {
|
|
447
|
+
if (!generate_unique_name(x_head, x_len)) {
|
|
448
|
+
APP_ERROR("Failed to construct a unique directory path");
|
|
449
|
+
return NULL;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
wchar_t *wtmpl = utf8_to_utf16(tmpl);
|
|
453
|
+
if (!wtmpl) {
|
|
454
|
+
APP_ERROR("Failed to convert template path to UTF-16");
|
|
455
|
+
return NULL;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
BOOL created = CreateDirectoryW(wtmpl, NULL);
|
|
459
|
+
DWORD err = GetLastError();
|
|
460
|
+
free(wtmpl);
|
|
461
|
+
|
|
462
|
+
if (created) {
|
|
463
|
+
return tmpl;
|
|
464
|
+
}
|
|
465
|
+
if (err != ERROR_ALREADY_EXISTS) {
|
|
466
|
+
APP_ERROR("Failed to create a unique directory, Error=%lu", err);
|
|
467
|
+
return NULL;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
APP_ERROR(
|
|
472
|
+
"Failed to create a unique directory after %u retries",
|
|
473
|
+
MAX_RETRY_CREATE_UNIQUE_DIR
|
|
474
|
+
);
|
|
475
|
+
return NULL;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Maximum path length in Windows (32,767 chars).
|
|
479
|
+
#define MAX_LONG_PATH 32767U
|
|
480
|
+
|
|
481
|
+
// Retrieves the full path to the executable file of the current process
|
|
482
|
+
char *GetImagePath(void)
|
|
483
|
+
{
|
|
484
|
+
char *image_path = NULL;
|
|
485
|
+
|
|
486
|
+
wchar_t *wimage_path = calloc(MAX_LONG_PATH, sizeof(*wimage_path));
|
|
487
|
+
if (!wimage_path) {
|
|
488
|
+
APP_ERROR("Memory allocation failed for image path");
|
|
489
|
+
|
|
490
|
+
goto cleanup;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
DWORD copied = GetModuleFileNameW(NULL, wimage_path, MAX_LONG_PATH);
|
|
494
|
+
if (copied == 0) {
|
|
495
|
+
DWORD err = GetLastError();
|
|
496
|
+
APP_ERROR("GetModuleFileNameW failed, Error=%lu", err);
|
|
497
|
+
|
|
498
|
+
goto cleanup;
|
|
499
|
+
}
|
|
500
|
+
if (copied == MAX_LONG_PATH
|
|
501
|
+
&& GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
|
502
|
+
APP_ERROR("Image path truncated; buffer too small");
|
|
503
|
+
|
|
504
|
+
goto cleanup;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
image_path = utf16_to_utf8(wimage_path);
|
|
508
|
+
if (!image_path) {
|
|
509
|
+
APP_ERROR("Failed to convert image path to UTF-8");
|
|
510
|
+
|
|
511
|
+
goto cleanup;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
cleanup:
|
|
515
|
+
if (wimage_path) {
|
|
516
|
+
free(wimage_path);
|
|
517
|
+
}
|
|
518
|
+
return image_path;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Retrieves the path to the temporary directory for the current user.
|
|
522
|
+
char *GetTempDirectoryPath(void)
|
|
523
|
+
{
|
|
524
|
+
wchar_t *wtemp_dir = calloc(MAX_PATH, sizeof(*wtemp_dir));
|
|
525
|
+
if (!wtemp_dir) {
|
|
526
|
+
APP_ERROR("Memory allocation failed for temp directory");
|
|
527
|
+
return NULL;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (!GetTempPathW(MAX_PATH, wtemp_dir)) {
|
|
531
|
+
DWORD err = GetLastError();
|
|
532
|
+
APP_ERROR("Failed to get temp path, Error=%lu", err);
|
|
533
|
+
free(wtemp_dir);
|
|
534
|
+
return NULL;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
char *temp_dir = utf16_to_utf8(wtemp_dir);
|
|
538
|
+
free(wtemp_dir);
|
|
539
|
+
if (!temp_dir) {
|
|
540
|
+
APP_ERROR("Failed to convert temp path to UTF-8");
|
|
541
|
+
return NULL;
|
|
542
|
+
}
|
|
543
|
+
return temp_dir;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
bool ExportFile(const char *path, const void *buffer, size_t buffer_size)
|
|
547
|
+
{
|
|
548
|
+
bool result = false;
|
|
549
|
+
char *parent = NULL;
|
|
550
|
+
wchar_t *wpath = NULL;
|
|
551
|
+
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
552
|
+
DWORD written = 0;
|
|
553
|
+
|
|
554
|
+
if (buffer_size > MAXDWORD) {
|
|
555
|
+
APP_ERROR(
|
|
556
|
+
"ExportFile: Write length %zu exceeds maximum DWORD",
|
|
557
|
+
buffer_size
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
goto cleanup;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
parent = GetParentPath(path);
|
|
564
|
+
if (!parent) {
|
|
565
|
+
APP_ERROR("Failed to get parent path");
|
|
566
|
+
|
|
567
|
+
goto cleanup;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (!CreateDirectoriesRecursively(parent)) {
|
|
571
|
+
APP_ERROR("ExportFile: Failed to create parent directory for %s", path);
|
|
572
|
+
|
|
573
|
+
goto cleanup;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Convert UTF-8 path to UTF-16 for proper multibyte support
|
|
577
|
+
wpath = utf8_to_utf16(path);
|
|
578
|
+
if (!wpath) {
|
|
579
|
+
APP_ERROR("ExportFile: Failed to convert path to UTF-16");
|
|
580
|
+
goto cleanup;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
hFile = CreateFileW(
|
|
584
|
+
wpath, // file path (UTF-16)
|
|
585
|
+
GENERIC_WRITE, // write-only access (like O_WRONLY)
|
|
586
|
+
0, // no sharing (exclusive)
|
|
587
|
+
NULL, // default security
|
|
588
|
+
CREATE_ALWAYS, // create or overwrite (O_CREAT|O_TRUNC)
|
|
589
|
+
FILE_ATTRIBUTE_NORMAL, // normal file (no special flags)
|
|
590
|
+
NULL // no template file
|
|
591
|
+
);
|
|
592
|
+
if (hFile == INVALID_HANDLE_VALUE) {
|
|
593
|
+
DWORD err = GetLastError();
|
|
594
|
+
APP_ERROR("ExportFile: CreateFileW failed, Error=%u", err);
|
|
595
|
+
|
|
596
|
+
goto cleanup;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (!WriteFile(hFile, buffer, (DWORD)buffer_size, &written, NULL)) {
|
|
600
|
+
DWORD err = GetLastError();
|
|
601
|
+
APP_ERROR("ExportFile: WriteFile failed, Error=%u", err);
|
|
602
|
+
|
|
603
|
+
goto cleanup;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (written != (DWORD)buffer_size) {
|
|
607
|
+
APP_ERROR(
|
|
608
|
+
"ExportFile: Write size mismatch, expected %zu, wrote %u",
|
|
609
|
+
buffer_size, written
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
goto cleanup;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
result = true;
|
|
616
|
+
|
|
617
|
+
cleanup:
|
|
618
|
+
if (parent) {
|
|
619
|
+
free(parent);
|
|
620
|
+
}
|
|
621
|
+
if (wpath) {
|
|
622
|
+
free(wpath);
|
|
623
|
+
}
|
|
624
|
+
if (hFile != INVALID_HANDLE_VALUE) {
|
|
625
|
+
CloseHandle(hFile);
|
|
626
|
+
}
|
|
627
|
+
return result;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
struct MemoryMap {
|
|
631
|
+
void *base; // Base address of the mapping
|
|
632
|
+
size_t size; // Length of the mapping
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
MemoryMap *CreateMemoryMap(const char *path)
|
|
636
|
+
{
|
|
637
|
+
if (!path) {
|
|
638
|
+
APP_ERROR("CreateMemoryMap: path is NULL");
|
|
639
|
+
return NULL;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
MemoryMap *map = malloc(sizeof(*map));
|
|
643
|
+
if (!map) {
|
|
644
|
+
APP_ERROR("CreateMemoryMap: Memory allocation failed for map");
|
|
645
|
+
return NULL;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
649
|
+
HANDLE hMapping = NULL; // section object handle for mapping
|
|
650
|
+
LPVOID base = NULL;
|
|
651
|
+
wchar_t *wpath = NULL;
|
|
652
|
+
|
|
653
|
+
/*
|
|
654
|
+
* Open the target file for memory mapping:
|
|
655
|
+
* - Read-only access; write and delete operations are denied.
|
|
656
|
+
* - Allows other processes to open the file for read-only access.
|
|
657
|
+
* - Fails if the file does not exist.
|
|
658
|
+
* - Sets the file's attribute to read-only.
|
|
659
|
+
* - Hints OS to optimize for sequential access after initial random read.
|
|
660
|
+
*/
|
|
661
|
+
|
|
662
|
+
// Convert UTF-8 path to UTF-16 for proper multibyte support
|
|
663
|
+
wpath = utf8_to_utf16(path);
|
|
664
|
+
if (!wpath) {
|
|
665
|
+
APP_ERROR("CreateMemoryMap: Failed to convert path to UTF-16");
|
|
666
|
+
goto cleanup;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
hFile = CreateFileW(
|
|
670
|
+
wpath, // path to existing file (UTF-16)
|
|
671
|
+
GENERIC_READ, // read-only access
|
|
672
|
+
FILE_SHARE_READ, // share read, deny write/delete
|
|
673
|
+
NULL, // default security
|
|
674
|
+
OPEN_EXISTING, // open only if file exists
|
|
675
|
+
FILE_ATTRIBUTE_READONLY // read-only attribute
|
|
676
|
+
| FILE_FLAG_SEQUENTIAL_SCAN, // then optimize for sequential access
|
|
677
|
+
NULL // no template file
|
|
678
|
+
);
|
|
679
|
+
if (hFile == INVALID_HANDLE_VALUE) {
|
|
680
|
+
DWORD err = GetLastError();
|
|
681
|
+
APP_ERROR(
|
|
682
|
+
"CreateMemoryMap: CreateFileW(\"%s\") failed, Error=%lu",
|
|
683
|
+
path, err
|
|
684
|
+
);
|
|
685
|
+
|
|
686
|
+
goto cleanup;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
LARGE_INTEGER fileSize;
|
|
690
|
+
if (!GetFileSizeEx(hFile, &fileSize)) {
|
|
691
|
+
DWORD err = GetLastError();
|
|
692
|
+
APP_ERROR(
|
|
693
|
+
"CreateMemoryMap: GetFileSizeEx failed, Error=%lu",
|
|
694
|
+
err
|
|
695
|
+
);
|
|
696
|
+
|
|
697
|
+
goto cleanup;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/*
|
|
701
|
+
* Verify that the file size obtained at runtime does not exceed the
|
|
702
|
+
* maximum value representable by size_t on this platform. This check
|
|
703
|
+
* prevents an overflow when casting the 64-bit file size to size_t,
|
|
704
|
+
* which may be 32 bits on some environments.
|
|
705
|
+
*/
|
|
706
|
+
|
|
707
|
+
if (fileSize.QuadPart < 0
|
|
708
|
+
|| (ULONGLONG)fileSize.QuadPart > (ULONGLONG)SIZE_MAX) {
|
|
709
|
+
APP_ERROR(
|
|
710
|
+
"CreateMemoryMap: file too large (%lld bytes)",
|
|
711
|
+
fileSize.QuadPart
|
|
712
|
+
);
|
|
713
|
+
|
|
714
|
+
goto cleanup;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
map->size = (size_t)fileSize.QuadPart;
|
|
718
|
+
|
|
719
|
+
hMapping = CreateFileMapping(
|
|
720
|
+
hFile, // read-only file handle
|
|
721
|
+
NULL, // default security attributes
|
|
722
|
+
PAGE_READONLY, // read-only mapping protection
|
|
723
|
+
0, // max size high 32-bit (0 = full file)
|
|
724
|
+
0, // max size low 32-bit (0 = full file)
|
|
725
|
+
NULL // unnamed mapping (private)
|
|
726
|
+
);
|
|
727
|
+
if (!hMapping) {
|
|
728
|
+
DWORD err = GetLastError();
|
|
729
|
+
APP_ERROR(
|
|
730
|
+
"CreateMemoryMap: CreateFileMapping(hFile) failed, Error=%lu",
|
|
731
|
+
err
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
goto cleanup;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
CloseHandle(hFile);
|
|
738
|
+
hFile = INVALID_HANDLE_VALUE;
|
|
739
|
+
|
|
740
|
+
base = MapViewOfFile(
|
|
741
|
+
hMapping, // handle returned by CreateFileMapping
|
|
742
|
+
FILE_MAP_READ, // read-only access to mapped view
|
|
743
|
+
0, // file offset high 32 bits (start at 0)
|
|
744
|
+
0, // file offset low 32 bits (start at 0)
|
|
745
|
+
0 // number of bytes to map (0 = entire file)
|
|
746
|
+
);
|
|
747
|
+
if (!base) {
|
|
748
|
+
DWORD err = GetLastError();
|
|
749
|
+
APP_ERROR(
|
|
750
|
+
"CreateMemoryMap: MapViewOfFile(hMapping) failed, Error=%lu",
|
|
751
|
+
err
|
|
752
|
+
);
|
|
753
|
+
|
|
754
|
+
goto cleanup;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
CloseHandle(hMapping);
|
|
758
|
+
hMapping = NULL;
|
|
759
|
+
|
|
760
|
+
map->base = base;
|
|
761
|
+
if (wpath) {
|
|
762
|
+
free(wpath);
|
|
763
|
+
}
|
|
764
|
+
return map;
|
|
765
|
+
|
|
766
|
+
cleanup:
|
|
767
|
+
if (base) {
|
|
768
|
+
UnmapViewOfFile(base);
|
|
769
|
+
}
|
|
770
|
+
if (hMapping) {
|
|
771
|
+
CloseHandle(hMapping);
|
|
772
|
+
}
|
|
773
|
+
if (hFile != INVALID_HANDLE_VALUE) {
|
|
774
|
+
CloseHandle(hFile);
|
|
775
|
+
}
|
|
776
|
+
if (wpath) {
|
|
777
|
+
free(wpath);
|
|
778
|
+
}
|
|
779
|
+
free(map);
|
|
780
|
+
return NULL;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
void DestroyMemoryMap(MemoryMap *map)
|
|
784
|
+
{
|
|
785
|
+
if (!map) {
|
|
786
|
+
APP_ERROR("DestroyMemoryMap: map is NULL");
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
if (map->base) {
|
|
791
|
+
if (!UnmapViewOfFile(map->base)) {
|
|
792
|
+
DWORD err = GetLastError();
|
|
793
|
+
APP_ERROR(
|
|
794
|
+
"DestroyMemoryMap: UnmapViewOfFile failed, Error=%lu",
|
|
795
|
+
err
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
map->base = NULL;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
free(map);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
void *GetMemoryMapBase(const MemoryMap *map)
|
|
805
|
+
{
|
|
806
|
+
if (!map) {
|
|
807
|
+
APP_ERROR("GetMemoryMapBase: map is NULL");
|
|
808
|
+
return NULL;
|
|
809
|
+
}
|
|
810
|
+
return map->base;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
size_t GetMemoryMapSize(const MemoryMap *map)
|
|
814
|
+
{
|
|
815
|
+
if (!map) {
|
|
816
|
+
APP_ERROR("GetMemoryMapSize: map is NULL");
|
|
817
|
+
return 0;
|
|
818
|
+
}
|
|
819
|
+
return map->size;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* @brief Handle console control events in the parent process.
|
|
824
|
+
*
|
|
825
|
+
* This handler ignores all console control events (Ctrl+C, Ctrl+Break, etc.)
|
|
826
|
+
* in the parent process so it can complete cleanup without interruption.
|
|
827
|
+
* Child processes (e.g., Ruby) receive these events and exit quickly,
|
|
828
|
+
* allowing the parent to perform final cleanup tasks.
|
|
829
|
+
*
|
|
830
|
+
* @param dwCtrlType The type of console control event received.
|
|
831
|
+
* @return TRUE to indicate the event was handled and should be ignored.
|
|
832
|
+
*/
|
|
833
|
+
static BOOL WINAPI ConsoleHandleRoutine(DWORD dwCtrlType)
|
|
834
|
+
{
|
|
835
|
+
return TRUE;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
bool InitializeSignalHandling(void)
|
|
839
|
+
{
|
|
840
|
+
if (!SetConsoleCtrlHandler(ConsoleHandleRoutine, TRUE)) {
|
|
841
|
+
DWORD err = GetLastError();
|
|
842
|
+
APP_ERROR("Failed to set console control handler, Error=%lu", err);
|
|
843
|
+
return false;
|
|
844
|
+
}
|
|
845
|
+
return true;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
bool SetEnvVar(const char *name, const char *value)
|
|
849
|
+
{
|
|
850
|
+
if (!name) {
|
|
851
|
+
APP_ERROR("name is NULL");
|
|
852
|
+
return false;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if (!SetEnvironmentVariable(name, value)) {
|
|
856
|
+
DWORD err = GetLastError();
|
|
857
|
+
APP_ERROR("Failed to set environment variable, Error=%lu", err);
|
|
858
|
+
return false;
|
|
859
|
+
}
|
|
860
|
+
return true;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
static size_t quoted_arg(char *quoted, const char* arg)
|
|
864
|
+
{
|
|
865
|
+
size_t count = 0;
|
|
866
|
+
|
|
867
|
+
count++;
|
|
868
|
+
if (quoted) {
|
|
869
|
+
*quoted++ = '"';
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
for (const char *p = arg; *p; p++) {
|
|
873
|
+
switch (*p) {
|
|
874
|
+
case '\\': {
|
|
875
|
+
size_t trail = 1;
|
|
876
|
+
while (*++p == '\\') {
|
|
877
|
+
trail++;
|
|
878
|
+
}
|
|
879
|
+
if (*p == '"' || *p == '\0') {
|
|
880
|
+
trail *= 2;
|
|
881
|
+
}
|
|
882
|
+
count += trail;
|
|
883
|
+
if (quoted) {
|
|
884
|
+
while (trail--) *quoted++ = '\\';
|
|
885
|
+
}
|
|
886
|
+
p--;
|
|
887
|
+
break;
|
|
888
|
+
}
|
|
889
|
+
case '"':
|
|
890
|
+
count += 2;
|
|
891
|
+
if (quoted) {
|
|
892
|
+
*quoted++ = '\\';
|
|
893
|
+
*quoted++ = '"';
|
|
894
|
+
}
|
|
895
|
+
break;
|
|
896
|
+
default:
|
|
897
|
+
count++;
|
|
898
|
+
if (quoted) {
|
|
899
|
+
*quoted++ = *p;
|
|
900
|
+
}
|
|
901
|
+
break;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
count++;
|
|
906
|
+
if (quoted) {
|
|
907
|
+
*quoted++ = '"';
|
|
908
|
+
*quoted = '\0';
|
|
909
|
+
}
|
|
910
|
+
return count;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
static size_t quoted_args(char *args, char *argv[])
|
|
914
|
+
{
|
|
915
|
+
size_t args_len = 0;
|
|
916
|
+
|
|
917
|
+
for (char **p = argv; *p; p++) {
|
|
918
|
+
if (args_len > 0) {
|
|
919
|
+
if (args) {
|
|
920
|
+
args[args_len] = ' ';
|
|
921
|
+
}
|
|
922
|
+
args_len++;
|
|
923
|
+
}
|
|
924
|
+
if (args) {
|
|
925
|
+
args_len += quoted_arg(&args[args_len], *p);
|
|
926
|
+
} else {
|
|
927
|
+
args_len += quoted_arg(NULL, *p);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
if (args) {
|
|
932
|
+
args[args_len] = '\0';
|
|
933
|
+
}
|
|
934
|
+
return args_len;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
bool CreateAndWaitForProcess(const char *app_name, char *argv[], int *exit_code)
|
|
938
|
+
{
|
|
939
|
+
PROCESS_INFORMATION pi = { 0 };
|
|
940
|
+
STARTUPINFOW si = { .cb = sizeof(si) };
|
|
941
|
+
bool result = false;
|
|
942
|
+
char *cmd_line = NULL;
|
|
943
|
+
wchar_t *wapp_name = NULL;
|
|
944
|
+
wchar_t *wcmd_line = NULL;
|
|
945
|
+
|
|
946
|
+
cmd_line = calloc(quoted_args(NULL, argv) + 1, sizeof(*cmd_line));
|
|
947
|
+
if (!cmd_line) {
|
|
948
|
+
APP_ERROR("Failed to build command line for CreateProcessW()");
|
|
949
|
+
goto cleanup;
|
|
950
|
+
}
|
|
951
|
+
quoted_args(cmd_line, argv);
|
|
952
|
+
|
|
953
|
+
DEBUG("ApplicationName=%s", app_name);
|
|
954
|
+
DEBUG("CommandLine=%s", cmd_line);
|
|
955
|
+
|
|
956
|
+
wapp_name = utf8_to_utf16(app_name);
|
|
957
|
+
if (!wapp_name) {
|
|
958
|
+
APP_ERROR("Failed to convert application name to UTF-16");
|
|
959
|
+
goto cleanup;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
wcmd_line = utf8_to_utf16(cmd_line);
|
|
963
|
+
if (!wcmd_line) {
|
|
964
|
+
APP_ERROR("Failed to convert command line to UTF-16");
|
|
965
|
+
goto cleanup;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
if (!CreateProcessW(wapp_name, wcmd_line, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
|
|
969
|
+
APP_ERROR("Failed to create process (%lu)", GetLastError());
|
|
970
|
+
goto cleanup;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0) {
|
|
974
|
+
APP_ERROR("Failed to wait script process (%lu)", GetLastError());
|
|
975
|
+
goto cleanup;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (!GetExitCodeProcess(pi.hProcess, (LPDWORD)exit_code)) {
|
|
979
|
+
APP_ERROR("Failed to get exit status (%lu)", GetLastError());
|
|
980
|
+
goto cleanup;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
result = true;
|
|
984
|
+
|
|
985
|
+
cleanup:
|
|
986
|
+
if (cmd_line) {
|
|
987
|
+
free(cmd_line);
|
|
988
|
+
}
|
|
989
|
+
if (wapp_name) {
|
|
990
|
+
free(wapp_name);
|
|
991
|
+
}
|
|
992
|
+
if (wcmd_line) {
|
|
993
|
+
free(wcmd_line);
|
|
994
|
+
}
|
|
995
|
+
if (pi.hProcess && pi.hProcess != INVALID_HANDLE_VALUE) {
|
|
996
|
+
CloseHandle(pi.hProcess);
|
|
997
|
+
}
|
|
998
|
+
if (pi.hThread && pi.hThread != INVALID_HANDLE_VALUE) {
|
|
999
|
+
CloseHandle(pi.hThread);
|
|
1000
|
+
}
|
|
1001
|
+
return result;
|
|
1002
|
+
}
|