Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2015 Andrew Kelley
3 : : *
4 : : * This file is part of zig, which is MIT licensed.
5 : : * See http://opensource.org/licenses/MIT
6 : : */
7 : :
8 : : #include "os.hpp"
9 : : #include "util.hpp"
10 : : #include "error.hpp"
11 : :
12 : : #if defined(_WIN32)
13 : :
14 : : #if !defined(NOMINMAX)
15 : : #define NOMINMAX
16 : : #endif
17 : :
18 : : #if !defined(VC_EXTRALEAN)
19 : : #define VC_EXTRALEAN
20 : : #endif
21 : :
22 : : #if !defined(WIN32_LEAN_AND_MEAN)
23 : : #define WIN32_LEAN_AND_MEAN
24 : : #endif
25 : :
26 : : #if !defined(_WIN32_WINNT)
27 : : #define _WIN32_WINNT 0x600
28 : : #endif
29 : :
30 : : #if !defined(NTDDI_VERSION)
31 : : #define NTDDI_VERSION 0x06000000
32 : : #endif
33 : :
34 : : #include <windows.h>
35 : : #include <shlobj.h>
36 : : #include <io.h>
37 : : #include <fcntl.h>
38 : : #include <ntsecapi.h>
39 : :
40 : : typedef SSIZE_T ssize_t;
41 : : #else
42 : : #define ZIG_OS_POSIX
43 : :
44 : : #include <unistd.h>
45 : : #include <sys/types.h>
46 : : #include <sys/stat.h>
47 : : #include <sys/wait.h>
48 : : #include <sys/resource.h>
49 : : #include <fcntl.h>
50 : : #include <limits.h>
51 : : #include <spawn.h>
52 : :
53 : : #endif
54 : :
55 : : #if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD)
56 : : #include <link.h>
57 : : #endif
58 : :
59 : : #if defined(ZIG_OS_LINUX)
60 : : #include <sys/auxv.h>
61 : : #endif
62 : :
63 : : #if defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD)
64 : : #include <sys/sysctl.h>
65 : : #endif
66 : :
67 : : #if defined(__MACH__)
68 : : #include <mach/clock.h>
69 : : #include <mach/mach.h>
70 : : #include <mach-o/dyld.h>
71 : : #endif
72 : :
73 : : #if defined(ZIG_OS_WINDOWS)
74 : : static uint64_t windows_perf_freq;
75 : : #elif defined(__MACH__)
76 : : static clock_serv_t macos_calendar_clock;
77 : : static clock_serv_t macos_monotonic_clock;
78 : : #endif
79 : :
80 : : #include <stdlib.h>
81 : : #include <errno.h>
82 : : #include <time.h>
83 : :
84 : : // Apple doesn't provide the environ global variable
85 : : #if defined(__APPLE__) && !defined(environ)
86 : : #include <crt_externs.h>
87 : : #define environ (*_NSGetEnviron())
88 : : #elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD)
89 : : extern char **environ;
90 : : #endif
91 : :
92 : : #if defined(ZIG_OS_POSIX)
93 : 45 : static void populate_termination(Termination *term, int status) {
94 [ + - ]: 45 : if (WIFEXITED(status)) {
95 : 45 : term->how = TerminationIdClean;
96 : 45 : term->code = WEXITSTATUS(status);
97 [ # # ]: 0 : } else if (WIFSIGNALED(status)) {
98 : 0 : term->how = TerminationIdSignaled;
99 : 0 : term->code = WTERMSIG(status);
100 [ # # ]: 0 : } else if (WIFSTOPPED(status)) {
101 : 0 : term->how = TerminationIdStopped;
102 : 0 : term->code = WSTOPSIG(status);
103 : : } else {
104 : 0 : term->how = TerminationIdUnknown;
105 : 0 : term->code = status;
106 : : }
107 : 45 : }
108 : :
109 : 45 : static void os_spawn_process_posix(ZigList<const char *> &args, Termination *term) {
110 : 45 : const char **argv = allocate<const char *>(args.length + 1);
111 [ + + ]: 2178 : for (size_t i = 0; i < args.length; i += 1) {
112 : 2133 : argv[i] = args.at(i);
113 : : }
114 : 45 : argv[args.length] = nullptr;
115 : :
116 : : pid_t pid;
117 : 45 : int rc = posix_spawnp(&pid, args.at(0), nullptr, nullptr, const_cast<char *const*>(argv), environ);
118 [ - + ]: 45 : if (rc != 0) {
119 : 0 : zig_panic("posix_spawn failed: %s", strerror(rc));
120 : : }
121 : :
122 : : int status;
123 : 45 : waitpid(pid, &status, 0);
124 : 45 : populate_termination(term, status);
125 : 45 : }
126 : : #endif
127 : :
128 : : #if defined(ZIG_OS_WINDOWS)
129 : :
130 : : static void os_windows_create_command_line(Buf *command_line, ZigList<const char *> &args) {
131 : : buf_resize(command_line, 0);
132 : : const char *prefix = "\"";
133 : : for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) {
134 : : const char *arg = args.at(arg_i);
135 : : buf_append_str(command_line, prefix);
136 : : prefix = " \"";
137 : : size_t arg_len = strlen(arg);
138 : : for (size_t c_i = 0; c_i < arg_len; c_i += 1) {
139 : : if (arg[c_i] == '\"') {
140 : : zig_panic("TODO");
141 : : }
142 : : buf_append_char(command_line, arg[c_i]);
143 : : }
144 : : buf_append_char(command_line, '\"');
145 : : }
146 : : }
147 : :
148 : : static void os_spawn_process_windows(ZigList<const char *> &args, Termination *term) {
149 : : Buf command_line = BUF_INIT;
150 : : os_windows_create_command_line(&command_line, args);
151 : :
152 : : PROCESS_INFORMATION piProcInfo = {0};
153 : : STARTUPINFO siStartInfo = {0};
154 : : siStartInfo.cb = sizeof(STARTUPINFO);
155 : :
156 : : const char *exe = args.at(0);
157 : : BOOL success = CreateProcessA(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr,
158 : : &siStartInfo, &piProcInfo);
159 : :
160 : : if (!success) {
161 : : zig_panic("CreateProcess failed. exe: %s command_line: %s", exe, buf_ptr(&command_line));
162 : : }
163 : :
164 : : WaitForSingleObject(piProcInfo.hProcess, INFINITE);
165 : :
166 : : DWORD exit_code;
167 : : if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) {
168 : : zig_panic("GetExitCodeProcess failed");
169 : : }
170 : : term->how = TerminationIdClean;
171 : : term->code = exit_code;
172 : : }
173 : : #endif
174 : :
175 : 45 : void os_spawn_process(ZigList<const char *> &args, Termination *term) {
176 : : #if defined(ZIG_OS_WINDOWS)
177 : : os_spawn_process_windows(args, term);
178 : : #elif defined(ZIG_OS_POSIX)
179 : 45 : os_spawn_process_posix(args, term);
180 : : #else
181 : : #error "missing os_spawn_process implementation"
182 : : #endif
183 : 45 : }
184 : :
185 : 2416 : void os_path_dirname(Buf *full_path, Buf *out_dirname) {
186 : 2416 : return os_path_split(full_path, out_dirname, nullptr);
187 : : }
188 : :
189 : 62498 : bool os_is_sep(uint8_t c) {
190 : : #if defined(ZIG_OS_WINDOWS)
191 : : return c == '\\' || c == '/';
192 : : #else
193 : 62498 : return c == '/';
194 : : #endif
195 : : }
196 : :
197 : 4313 : void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) {
198 : 4313 : size_t len = buf_len(full_path);
199 [ + - ]: 4313 : if (len != 0) {
200 : 4313 : size_t last_index = len - 1;
201 : 4313 : char last_char = buf_ptr(full_path)[last_index];
202 [ - + ]: 4313 : if (os_is_sep(last_char)) {
203 [ # # ]: 0 : if (last_index == 0) {
204 [ # # ]: 0 : if (out_dirname) buf_init_from_mem(out_dirname, &last_char, 1);
205 [ # # ]: 0 : if (out_basename) buf_init_from_str(out_basename, "");
206 : 4313 : return;
207 : : }
208 : 0 : last_index -= 1;
209 : : }
210 : 4313 : for (size_t i = last_index;;) {
211 : 51503 : uint8_t c = buf_ptr(full_path)[i];
212 [ + + ]: 51503 : if (os_is_sep(c)) {
213 [ + + ]: 4313 : if (out_dirname) {
214 [ + - ]: 4259 : buf_init_from_mem(out_dirname, buf_ptr(full_path), (i == 0) ? 1 : i);
215 : : }
216 [ + + ]: 4313 : if (out_basename) {
217 : 1897 : buf_init_from_mem(out_basename, buf_ptr(full_path) + i + 1, buf_len(full_path) - (i + 1));
218 : : }
219 : 4313 : return;
220 : : }
221 [ - + ]: 47190 : if (i == 0) break;
222 : 47190 : i -= 1;
223 : 47190 : }
224 : : }
225 [ # # ]: 0 : if (out_dirname) buf_init_from_mem(out_dirname, ".", 1);
226 [ # # ]: 0 : if (out_basename) buf_init_from_buf(out_basename, full_path);
227 : : }
228 : :
229 : 3662 : void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname) {
230 [ - + ]: 3662 : if (buf_len(full_path) == 0) {
231 [ # # ]: 0 : if (out_basename) buf_init_from_str(out_basename, "");
232 [ # # ]: 0 : if (out_extname) buf_init_from_str(out_extname, "");
233 : 0 : return;
234 : : }
235 : 3662 : size_t i = buf_len(full_path) - 1;
236 : : while (true) {
237 [ + + ]: 14552 : if (buf_ptr(full_path)[i] == '.') {
238 [ + - ]: 3662 : if (out_basename) {
239 : 3662 : buf_resize(out_basename, 0);
240 : 3662 : buf_append_mem(out_basename, buf_ptr(full_path), i);
241 : : }
242 : :
243 [ - + ]: 3662 : if (out_extname) {
244 : 0 : buf_resize(out_extname, 0);
245 : 0 : buf_append_mem(out_extname, buf_ptr(full_path) + i, buf_len(full_path) - i);
246 : : }
247 : 3662 : return;
248 : : }
249 : :
250 [ - + ]: 10890 : if (i == 0) {
251 [ # # ]: 0 : if (out_basename) buf_init_from_buf(out_basename, full_path);
252 [ # # ]: 0 : if (out_extname) buf_init_from_str(out_extname, "");
253 : 0 : return;
254 : : }
255 : 10890 : i -= 1;
256 : : }
257 : : }
258 : :
259 : 6271 : void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) {
260 [ - + ]: 6271 : if (buf_len(dirname) == 0) {
261 : 0 : buf_init_from_buf(out_full_path, basename);
262 : 0 : return;
263 : : }
264 : :
265 : 6271 : buf_init_from_buf(out_full_path, dirname);
266 : 6271 : uint8_t c = *(buf_ptr(out_full_path) + buf_len(out_full_path) - 1);
267 [ + - ]: 6271 : if (!os_is_sep(c))
268 : 6271 : buf_append_char(out_full_path, ZIG_OS_SEP_CHAR);
269 : 6271 : buf_append_buf(out_full_path, basename);
270 : : }
271 : :
272 : 0 : Error os_path_real(Buf *rel_path, Buf *out_abs_path) {
273 : : #if defined(ZIG_OS_WINDOWS)
274 : : buf_resize(out_abs_path, 4096);
275 : : if (_fullpath(buf_ptr(out_abs_path), buf_ptr(rel_path), buf_len(out_abs_path)) == nullptr) {
276 : : zig_panic("_fullpath failed");
277 : : }
278 : : buf_resize(out_abs_path, strlen(buf_ptr(out_abs_path)));
279 : : return ErrorNone;
280 : : #elif defined(ZIG_OS_POSIX)
281 : 0 : buf_resize(out_abs_path, PATH_MAX + 1);
282 : 0 : char *result = realpath(buf_ptr(rel_path), buf_ptr(out_abs_path));
283 [ # # ]: 0 : if (!result) {
284 : 0 : int err = errno;
285 [ # # ]: 0 : if (err == EACCES) {
286 : 0 : return ErrorAccess;
287 [ # # ]: 0 : } else if (err == ENOENT) {
288 : 0 : return ErrorFileNotFound;
289 [ # # ]: 0 : } else if (err == ENOMEM) {
290 : 0 : return ErrorNoMem;
291 : : } else {
292 : 0 : return ErrorFileSystem;
293 : : }
294 : : }
295 : 0 : buf_resize(out_abs_path, strlen(buf_ptr(out_abs_path)));
296 : 0 : return ErrorNone;
297 : : #else
298 : : #error "missing os_path_real implementation"
299 : : #endif
300 : : }
301 : :
302 : : #if defined(ZIG_OS_WINDOWS)
303 : : // Ported from std/os/path.zig
304 : : static bool isAbsoluteWindows(Slice<uint8_t> path) {
305 : : if (path.ptr[0] == '/')
306 : : return true;
307 : :
308 : : if (path.ptr[0] == '\\') {
309 : : return true;
310 : : }
311 : : if (path.len < 3) {
312 : : return false;
313 : : }
314 : : if (path.ptr[1] == ':') {
315 : : if (path.ptr[2] == '/')
316 : : return true;
317 : : if (path.ptr[2] == '\\')
318 : : return true;
319 : : }
320 : : return false;
321 : : }
322 : : #endif
323 : :
324 : 9177 : bool os_path_is_absolute(Buf *path) {
325 : : #if defined(ZIG_OS_WINDOWS)
326 : : return isAbsoluteWindows(buf_to_slice(path));
327 : : #elif defined(ZIG_OS_POSIX)
328 : 9177 : return buf_ptr(path)[0] == '/';
329 : : #else
330 : : #error "missing os_path_is_absolute implementation"
331 : : #endif
332 : : }
333 : :
334 : : #if defined(ZIG_OS_WINDOWS)
335 : :
336 : : enum WindowsPathKind {
337 : : WindowsPathKindNone,
338 : : WindowsPathKindDrive,
339 : : WindowsPathKindNetworkShare,
340 : : };
341 : :
342 : : struct WindowsPath {
343 : : Slice<uint8_t> disk_designator;
344 : : WindowsPathKind kind;
345 : : bool is_abs;
346 : : };
347 : :
348 : :
349 : : // Ported from std/os/path.zig
350 : : static WindowsPath windowsParsePath(Slice<uint8_t> path) {
351 : : if (path.len >= 2 && path.ptr[1] == ':') {
352 : : return WindowsPath{
353 : : path.slice(0, 2),
354 : : WindowsPathKindDrive,
355 : : isAbsoluteWindows(path),
356 : : };
357 : : }
358 : : if (path.len >= 1 && (path.ptr[0] == '/' || path.ptr[0] == '\\') &&
359 : : (path.len == 1 || (path.ptr[1] != '/' && path.ptr[1] != '\\')))
360 : : {
361 : : return WindowsPath{
362 : : path.slice(0, 0),
363 : : WindowsPathKindNone,
364 : : true,
365 : : };
366 : : }
367 : : WindowsPath relative_path = {
368 : : str(""),
369 : : WindowsPathKindNone,
370 : : false,
371 : : };
372 : : if (path.len < strlen("//a/b")) {
373 : : return relative_path;
374 : : }
375 : :
376 : : {
377 : : if (memStartsWith(path, str("//"))) {
378 : : if (path.ptr[2] == '/') {
379 : : return relative_path;
380 : : }
381 : :
382 : : SplitIterator it = memSplit(path, str("/"));
383 : : {
384 : : Optional<Slice<uint8_t>> opt_component = SplitIterator_next(&it);
385 : : if (!opt_component.is_some) return relative_path;
386 : : }
387 : : {
388 : : Optional<Slice<uint8_t>> opt_component = SplitIterator_next(&it);
389 : : if (!opt_component.is_some) return relative_path;
390 : : }
391 : : return WindowsPath{
392 : : path.slice(0, it.index),
393 : : WindowsPathKindNetworkShare,
394 : : isAbsoluteWindows(path),
395 : : };
396 : : }
397 : : }
398 : : {
399 : : if (memStartsWith(path, str("\\\\"))) {
400 : : if (path.ptr[2] == '\\') {
401 : : return relative_path;
402 : : }
403 : :
404 : : SplitIterator it = memSplit(path, str("\\"));
405 : : {
406 : : Optional<Slice<uint8_t>> opt_component = SplitIterator_next(&it);
407 : : if (!opt_component.is_some) return relative_path;
408 : : }
409 : : {
410 : : Optional<Slice<uint8_t>> opt_component = SplitIterator_next(&it);
411 : : if (!opt_component.is_some) return relative_path;
412 : : }
413 : : return WindowsPath{
414 : : path.slice(0, it.index),
415 : : WindowsPathKindNetworkShare,
416 : : isAbsoluteWindows(path),
417 : : };
418 : : }
419 : : }
420 : : return relative_path;
421 : : }
422 : :
423 : : // Ported from std/os/path.zig
424 : : static uint8_t asciiUpper(uint8_t byte) {
425 : : if (byte >= 'a' && byte <= 'z') {
426 : : return 'A' + (byte - 'a');
427 : : }
428 : : return byte;
429 : : }
430 : :
431 : : // Ported from std/os/path.zig
432 : : static bool asciiEqlIgnoreCase(Slice<uint8_t> s1, Slice<uint8_t> s2) {
433 : : if (s1.len != s2.len)
434 : : return false;
435 : : for (size_t i = 0; i < s1.len; i += 1) {
436 : : if (asciiUpper(s1.ptr[i]) != asciiUpper(s2.ptr[i]))
437 : : return false;
438 : : }
439 : : return true;
440 : : }
441 : :
442 : : // Ported from std/os/path.zig
443 : : static bool compareDiskDesignators(WindowsPathKind kind, Slice<uint8_t> p1, Slice<uint8_t> p2) {
444 : : switch (kind) {
445 : : case WindowsPathKindNone:
446 : : assert(p1.len == 0);
447 : : assert(p2.len == 0);
448 : : return true;
449 : : case WindowsPathKindDrive:
450 : : return asciiUpper(p1.ptr[0]) == asciiUpper(p2.ptr[0]);
451 : : case WindowsPathKindNetworkShare:
452 : : uint8_t sep1 = p1.ptr[0];
453 : : uint8_t sep2 = p2.ptr[0];
454 : :
455 : : SplitIterator it1 = memSplit(p1, {&sep1, 1});
456 : : SplitIterator it2 = memSplit(p2, {&sep2, 1});
457 : :
458 : : // TODO ASCII is wrong, we actually need full unicode support to compare paths.
459 : : return asciiEqlIgnoreCase(SplitIterator_next(&it1).value, SplitIterator_next(&it2).value) &&
460 : : asciiEqlIgnoreCase(SplitIterator_next(&it1).value, SplitIterator_next(&it2).value);
461 : : }
462 : : zig_unreachable();
463 : : }
464 : :
465 : : // Ported from std/os/path.zig
466 : : static Buf os_path_resolve_windows(Buf **paths_ptr, size_t paths_len) {
467 : : if (paths_len == 0) {
468 : : Buf cwd = BUF_INIT;
469 : : int err;
470 : : if ((err = os_get_cwd(&cwd))) {
471 : : zig_panic("get cwd failed");
472 : : }
473 : : return cwd;
474 : : }
475 : :
476 : : // determine which disk designator we will result with, if any
477 : : char result_drive_buf[3] = {'_', ':', '\0'}; // 0 needed for strlen later
478 : : Slice<uint8_t> result_disk_designator = str("");
479 : : WindowsPathKind have_drive_kind = WindowsPathKindNone;
480 : : bool have_abs_path = false;
481 : : size_t first_index = 0;
482 : : size_t max_size = 0;
483 : : for (size_t i = 0; i < paths_len; i += 1) {
484 : : Slice<uint8_t> p = buf_to_slice(paths_ptr[i]);
485 : : WindowsPath parsed = windowsParsePath(p);
486 : : if (parsed.is_abs) {
487 : : have_abs_path = true;
488 : : first_index = i;
489 : : max_size = result_disk_designator.len;
490 : : }
491 : : switch (parsed.kind) {
492 : : case WindowsPathKindDrive:
493 : : result_drive_buf[0] = asciiUpper(parsed.disk_designator.ptr[0]);
494 : : result_disk_designator = str(result_drive_buf);
495 : : have_drive_kind = WindowsPathKindDrive;
496 : : break;
497 : : case WindowsPathKindNetworkShare:
498 : : result_disk_designator = parsed.disk_designator;
499 : : have_drive_kind = WindowsPathKindNetworkShare;
500 : : break;
501 : : case WindowsPathKindNone:
502 : : break;
503 : : }
504 : : max_size += p.len + 1;
505 : : }
506 : :
507 : : // if we will result with a disk designator, loop again to determine
508 : : // which is the last time the disk designator is absolutely specified, if any
509 : : // and count up the max bytes for paths related to this disk designator
510 : : if (have_drive_kind != WindowsPathKindNone) {
511 : : have_abs_path = false;
512 : : first_index = 0;
513 : : max_size = result_disk_designator.len;
514 : : bool correct_disk_designator = false;
515 : :
516 : : for (size_t i = 0; i < paths_len; i += 1) {
517 : : Slice<uint8_t> p = buf_to_slice(paths_ptr[i]);
518 : : WindowsPath parsed = windowsParsePath(p);
519 : : if (parsed.kind != WindowsPathKindNone) {
520 : : if (parsed.kind == have_drive_kind) {
521 : : correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator);
522 : : } else {
523 : : continue;
524 : : }
525 : : }
526 : : if (!correct_disk_designator) {
527 : : continue;
528 : : }
529 : : if (parsed.is_abs) {
530 : : first_index = i;
531 : : max_size = result_disk_designator.len;
532 : : have_abs_path = true;
533 : : }
534 : : max_size += p.len + 1;
535 : : }
536 : : }
537 : :
538 : : // Allocate result and fill in the disk designator, calling getCwd if we have to.
539 : : Slice<uint8_t> result;
540 : : size_t result_index = 0;
541 : :
542 : : if (have_abs_path) {
543 : : switch (have_drive_kind) {
544 : : case WindowsPathKindDrive: {
545 : : result = Slice<uint8_t>::alloc(max_size);
546 : :
547 : : memCopy(result, result_disk_designator);
548 : : result_index += result_disk_designator.len;
549 : : break;
550 : : }
551 : : case WindowsPathKindNetworkShare: {
552 : : result = Slice<uint8_t>::alloc(max_size);
553 : : SplitIterator it = memSplit(buf_to_slice(paths_ptr[first_index]), str("/\\"));
554 : : Slice<uint8_t> server_name = SplitIterator_next(&it).value;
555 : : Slice<uint8_t> other_name = SplitIterator_next(&it).value;
556 : :
557 : : result.ptr[result_index] = '\\';
558 : : result_index += 1;
559 : : result.ptr[result_index] = '\\';
560 : : result_index += 1;
561 : : memCopy(result.sliceFrom(result_index), server_name);
562 : : result_index += server_name.len;
563 : : result.ptr[result_index] = '\\';
564 : : result_index += 1;
565 : : memCopy(result.sliceFrom(result_index), other_name);
566 : : result_index += other_name.len;
567 : :
568 : : result_disk_designator = result.slice(0, result_index);
569 : : break;
570 : : }
571 : : case WindowsPathKindNone: {
572 : : Buf cwd = BUF_INIT;
573 : : int err;
574 : : if ((err = os_get_cwd(&cwd))) {
575 : : zig_panic("get cwd failed");
576 : : }
577 : : WindowsPath parsed_cwd = windowsParsePath(buf_to_slice(&cwd));
578 : : result = Slice<uint8_t>::alloc(max_size + parsed_cwd.disk_designator.len + 1);
579 : : memCopy(result, parsed_cwd.disk_designator);
580 : : result_index += parsed_cwd.disk_designator.len;
581 : : result_disk_designator = result.slice(0, parsed_cwd.disk_designator.len);
582 : : if (parsed_cwd.kind == WindowsPathKindDrive) {
583 : : result.ptr[0] = asciiUpper(result.ptr[0]);
584 : : }
585 : : have_drive_kind = parsed_cwd.kind;
586 : : break;
587 : : }
588 : : }
589 : : } else {
590 : : // TODO call get cwd for the result_disk_designator instead of the global one
591 : : Buf cwd = BUF_INIT;
592 : : int err;
593 : : if ((err = os_get_cwd(&cwd))) {
594 : : zig_panic("get cwd failed");
595 : : }
596 : : result = Slice<uint8_t>::alloc(max_size + buf_len(&cwd) + 1);
597 : :
598 : : memCopy(result, buf_to_slice(&cwd));
599 : : result_index += buf_len(&cwd);
600 : : WindowsPath parsed_cwd = windowsParsePath(result.slice(0, result_index));
601 : : result_disk_designator = parsed_cwd.disk_designator;
602 : : if (parsed_cwd.kind == WindowsPathKindDrive) {
603 : : result.ptr[0] = asciiUpper(result.ptr[0]);
604 : : }
605 : : have_drive_kind = parsed_cwd.kind;
606 : : }
607 : :
608 : : // Now we know the disk designator to use, if any, and what kind it is. And our result
609 : : // is big enough to append all the paths to.
610 : : bool correct_disk_designator = true;
611 : : for (size_t i = 0; i < paths_len; i += 1) {
612 : : Slice<uint8_t> p = buf_to_slice(paths_ptr[i]);
613 : : WindowsPath parsed = windowsParsePath(p);
614 : :
615 : : if (parsed.kind != WindowsPathKindNone) {
616 : : if (parsed.kind == have_drive_kind) {
617 : : correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator);
618 : : } else {
619 : : continue;
620 : : }
621 : : }
622 : : if (!correct_disk_designator) {
623 : : continue;
624 : : }
625 : : SplitIterator it = memSplit(p.sliceFrom(parsed.disk_designator.len), str("/\\"));
626 : : while (true) {
627 : : Optional<Slice<uint8_t>> opt_component = SplitIterator_next(&it);
628 : : if (!opt_component.is_some) break;
629 : : Slice<uint8_t> component = opt_component.value;
630 : : if (memEql(component, str("."))) {
631 : : continue;
632 : : } else if (memEql(component, str(".."))) {
633 : : while (true) {
634 : : if (result_index == 0 || result_index == result_disk_designator.len)
635 : : break;
636 : : result_index -= 1;
637 : : if (result.ptr[result_index] == '\\' || result.ptr[result_index] == '/')
638 : : break;
639 : : }
640 : : } else {
641 : : result.ptr[result_index] = '\\';
642 : : result_index += 1;
643 : : memCopy(result.sliceFrom(result_index), component);
644 : : result_index += component.len;
645 : : }
646 : : }
647 : : }
648 : :
649 : : if (result_index == result_disk_designator.len) {
650 : : result.ptr[result_index] = '\\';
651 : : result_index += 1;
652 : : }
653 : :
654 : : Buf return_value = BUF_INIT;
655 : : buf_init_from_mem(&return_value, (char *)result.ptr, result_index);
656 : : return return_value;
657 : : }
658 : : #endif
659 : :
660 : : #if defined(ZIG_OS_POSIX)
661 : : // Ported from std/os/path.zig
662 : 9177 : static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) {
663 [ - + ]: 9177 : if (paths_len == 0) {
664 : 0 : Buf cwd = BUF_INIT;
665 : : int err;
666 [ # # ]: 0 : if ((err = os_get_cwd(&cwd))) {
667 : 0 : zig_panic("get cwd failed");
668 : : }
669 : 0 : return cwd;
670 : : }
671 : :
672 : 9177 : size_t first_index = 0;
673 : 9177 : bool have_abs = false;
674 : 9177 : size_t max_size = 0;
675 [ + + ]: 18354 : for (size_t i = 0; i < paths_len; i += 1) {
676 : 9177 : Buf *p = paths_ptr[i];
677 [ + + ]: 9177 : if (os_path_is_absolute(p)) {
678 : 9175 : first_index = i;
679 : 9175 : have_abs = true;
680 : 9175 : max_size = 0;
681 : : }
682 : 9177 : max_size += buf_len(p) + 1;
683 : : }
684 : :
685 : : uint8_t *result_ptr;
686 : : size_t result_len;
687 : 9177 : size_t result_index = 0;
688 : :
689 [ + + ]: 9177 : if (have_abs) {
690 : 9175 : result_len = max_size;
691 : 9175 : result_ptr = allocate_nonzero<uint8_t>(result_len);
692 : : } else {
693 : 2 : Buf cwd = BUF_INIT;
694 : : int err;
695 [ - + ]: 2 : if ((err = os_get_cwd(&cwd))) {
696 : 0 : zig_panic("get cwd failed");
697 : : }
698 : 2 : result_len = max_size + buf_len(&cwd) + 1;
699 : 2 : result_ptr = allocate_nonzero<uint8_t>(result_len);
700 : 2 : memcpy(result_ptr, buf_ptr(&cwd), buf_len(&cwd));
701 : 2 : result_index += buf_len(&cwd);
702 : : }
703 : :
704 [ + + ]: 18354 : for (size_t i = first_index; i < paths_len; i += 1) {
705 : 9177 : Buf *p = paths_ptr[i];
706 : 9177 : SplitIterator it = memSplit(buf_to_slice(p), str("/"));
707 : : while (true) {
708 : 88685 : Optional<Slice<uint8_t>> opt_component = SplitIterator_next(&it);
709 [ + + ]: 88685 : if (!opt_component.is_some) break;
710 : 79508 : Slice<uint8_t> component = opt_component.value;
711 : :
712 [ + + ]: 79508 : if (memEql<uint8_t>(component, str("."))) {
713 : 2 : continue;
714 [ + + ]: 79506 : } else if (memEql<uint8_t>(component, str(".."))) {
715 : : while (true) {
716 [ - + ]: 1050 : if (result_index == 0)
717 : 0 : break;
718 : 858 : result_index -= 1;
719 [ + + ]: 858 : if (result_ptr[result_index] == '/')
720 : 192 : break;
721 : : }
722 : : } else {
723 : 79314 : result_ptr[result_index] = '/';
724 : 79314 : result_index += 1;
725 : 79314 : memcpy(result_ptr + result_index, component.ptr, component.len);
726 : 79506 : result_index += component.len;
727 : : }
728 : 79508 : }
729 : : }
730 : :
731 [ - + ]: 9177 : if (result_index == 0) {
732 : 0 : result_ptr[0] = '/';
733 : 0 : result_index += 1;
734 : : }
735 : :
736 : 9177 : Buf return_value = BUF_INIT;
737 : 9177 : buf_init_from_mem(&return_value, (char *)result_ptr, result_index);
738 : 9177 : return return_value;
739 : : }
740 : : #endif
741 : :
742 : : // Ported from std/os/path.zig
743 : 9177 : Buf os_path_resolve(Buf **paths_ptr, size_t paths_len) {
744 : : #if defined(ZIG_OS_WINDOWS)
745 : : return os_path_resolve_windows(paths_ptr, paths_len);
746 : : #elif defined(ZIG_OS_POSIX)
747 : 9177 : return os_path_resolve_posix(paths_ptr, paths_len);
748 : : #else
749 : : #error "missing os_path_resolve implementation"
750 : : #endif
751 : : }
752 : :
753 : 69 : Error os_fetch_file(FILE *f, Buf *out_buf) {
754 : : static const ssize_t buf_size = 0x2000;
755 : 69 : buf_resize(out_buf, buf_size);
756 : 69 : ssize_t actual_buf_len = 0;
757 : :
758 : : for (;;) {
759 : 201 : size_t amt_read = fread(buf_ptr(out_buf) + actual_buf_len, 1, buf_size, f);
760 : 201 : actual_buf_len += amt_read;
761 : :
762 [ + + ]: 201 : if (amt_read != buf_size) {
763 [ + - ]: 69 : if (feof(f)) {
764 : 69 : buf_resize(out_buf, actual_buf_len);
765 : 69 : return ErrorNone;
766 : : } else {
767 : 0 : return ErrorFileSystem;
768 : : }
769 : : }
770 : :
771 : 132 : buf_resize(out_buf, actual_buf_len + buf_size);
772 : 132 : }
773 : : zig_unreachable();
774 : : }
775 : :
776 : 45 : Error os_file_exists(Buf *full_path, bool *result) {
777 : : #if defined(ZIG_OS_WINDOWS)
778 : : *result = GetFileAttributes(buf_ptr(full_path)) != INVALID_FILE_ATTRIBUTES;
779 : : return ErrorNone;
780 : : #else
781 : 45 : *result = access(buf_ptr(full_path), F_OK) != -1;
782 : 45 : return ErrorNone;
783 : : #endif
784 : : }
785 : :
786 : : #if defined(ZIG_OS_POSIX)
787 : 0 : static Error os_exec_process_posix(ZigList<const char *> &args,
788 : : Termination *term, Buf *out_stderr, Buf *out_stdout)
789 : : {
790 : : int stdin_pipe[2];
791 : : int stdout_pipe[2];
792 : : int stderr_pipe[2];
793 : : int err_pipe[2];
794 : :
795 : : int err;
796 [ # # ]: 0 : if ((err = pipe(stdin_pipe)))
797 : 0 : zig_panic("pipe failed");
798 [ # # ]: 0 : if ((err = pipe(stdout_pipe)))
799 : 0 : zig_panic("pipe failed");
800 [ # # ]: 0 : if ((err = pipe(stderr_pipe)))
801 : 0 : zig_panic("pipe failed");
802 [ # # ]: 0 : if ((err = pipe(err_pipe)))
803 : 0 : zig_panic("pipe failed");
804 : :
805 : 0 : pid_t pid = fork();
806 [ # # ]: 0 : if (pid == -1)
807 : 0 : zig_panic("fork failed: %s", strerror(errno));
808 [ # # ]: 0 : if (pid == 0) {
809 : : // child
810 [ # # ]: 0 : if (dup2(stdin_pipe[0], STDIN_FILENO) == -1)
811 : 0 : zig_panic("dup2 failed");
812 : :
813 [ # # ]: 0 : if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1)
814 : 0 : zig_panic("dup2 failed");
815 : :
816 [ # # ]: 0 : if (dup2(stderr_pipe[1], STDERR_FILENO) == -1)
817 : 0 : zig_panic("dup2 failed");
818 : :
819 : 0 : const char **argv = allocate<const char *>(args.length + 1);
820 : 0 : argv[args.length] = nullptr;
821 [ # # ]: 0 : for (size_t i = 0; i < args.length; i += 1) {
822 : 0 : argv[i] = args.at(i);
823 : : }
824 : 0 : execvp(argv[0], const_cast<char * const *>(argv));
825 : 0 : Error report_err = ErrorUnexpected;
826 [ # # ]: 0 : if (errno == ENOENT) {
827 : 0 : report_err = ErrorFileNotFound;
828 : : }
829 : 0 : write(err_pipe[1], &report_err, sizeof(Error));
830 : 0 : exit(1);
831 : : } else {
832 : : // parent
833 : 0 : close(stdin_pipe[0]);
834 : 0 : close(stdin_pipe[1]);
835 : 0 : close(stdout_pipe[1]);
836 : 0 : close(stderr_pipe[1]);
837 : :
838 : : int status;
839 : 0 : waitpid(pid, &status, 0);
840 : 0 : populate_termination(term, status);
841 : :
842 : 0 : FILE *stdout_f = fdopen(stdout_pipe[0], "rb");
843 : 0 : FILE *stderr_f = fdopen(stderr_pipe[0], "rb");
844 : 0 : Error err1 = os_fetch_file(stdout_f, out_stdout);
845 : 0 : Error err2 = os_fetch_file(stderr_f, out_stderr);
846 : :
847 : 0 : fclose(stdout_f);
848 : 0 : fclose(stderr_f);
849 : :
850 [ # # ]: 0 : if (err1) return err1;
851 [ # # ]: 0 : if (err2) return err2;
852 : :
853 : 0 : Error child_err = ErrorNone;
854 : 0 : write(err_pipe[1], &child_err, sizeof(Error));
855 : 0 : close(err_pipe[1]);
856 : 0 : read(err_pipe[0], &child_err, sizeof(Error));
857 : 0 : close(err_pipe[0]);
858 : 0 : return child_err;
859 : : }
860 : : }
861 : : #endif
862 : :
863 : : #if defined(ZIG_OS_WINDOWS)
864 : :
865 : : //static void win32_panic(const char *str) {
866 : : // DWORD err = GetLastError();
867 : : // LPSTR messageBuffer = nullptr;
868 : : // FormatMessageA(
869 : : // FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
870 : : // NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
871 : : // zig_panic(str, messageBuffer);
872 : : // LocalFree(messageBuffer);
873 : : //}
874 : :
875 : : static Error os_exec_process_windows(ZigList<const char *> &args,
876 : : Termination *term, Buf *out_stderr, Buf *out_stdout)
877 : : {
878 : : Buf command_line = BUF_INIT;
879 : : os_windows_create_command_line(&command_line, args);
880 : :
881 : : HANDLE g_hChildStd_IN_Rd = NULL;
882 : : HANDLE g_hChildStd_IN_Wr = NULL;
883 : : HANDLE g_hChildStd_OUT_Rd = NULL;
884 : : HANDLE g_hChildStd_OUT_Wr = NULL;
885 : : HANDLE g_hChildStd_ERR_Rd = NULL;
886 : : HANDLE g_hChildStd_ERR_Wr = NULL;
887 : :
888 : : SECURITY_ATTRIBUTES saAttr;
889 : : saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
890 : : saAttr.bInheritHandle = TRUE;
891 : : saAttr.lpSecurityDescriptor = NULL;
892 : :
893 : : if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) {
894 : : zig_panic("StdoutRd CreatePipe");
895 : : }
896 : :
897 : : if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
898 : : zig_panic("Stdout SetHandleInformation");
899 : : }
900 : :
901 : : if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) {
902 : : zig_panic("stderr CreatePipe");
903 : : }
904 : :
905 : : if (!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) {
906 : : zig_panic("stderr SetHandleInformation");
907 : : }
908 : :
909 : : if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) {
910 : : zig_panic("Stdin CreatePipe");
911 : : }
912 : :
913 : : if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) {
914 : : zig_panic("Stdin SetHandleInformation");
915 : : }
916 : :
917 : :
918 : : PROCESS_INFORMATION piProcInfo = {0};
919 : : STARTUPINFO siStartInfo = {0};
920 : : siStartInfo.cb = sizeof(STARTUPINFO);
921 : : siStartInfo.hStdError = g_hChildStd_ERR_Wr;
922 : : siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
923 : : siStartInfo.hStdInput = g_hChildStd_IN_Rd;
924 : : siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
925 : :
926 : : const char *exe = args.at(0);
927 : : BOOL success = CreateProcess(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr,
928 : : &siStartInfo, &piProcInfo);
929 : :
930 : : if (!success) {
931 : : if (GetLastError() == ERROR_FILE_NOT_FOUND) {
932 : : CloseHandle(piProcInfo.hProcess);
933 : : CloseHandle(piProcInfo.hThread);
934 : : return ErrorFileNotFound;
935 : : }
936 : : zig_panic("CreateProcess failed. exe: %s command_line: %s", exe, buf_ptr(&command_line));
937 : : }
938 : :
939 : : if (!CloseHandle(g_hChildStd_IN_Wr)) {
940 : : zig_panic("stdinwr closehandle");
941 : : }
942 : :
943 : : CloseHandle(g_hChildStd_IN_Rd);
944 : : CloseHandle(g_hChildStd_ERR_Wr);
945 : : CloseHandle(g_hChildStd_OUT_Wr);
946 : :
947 : : static const size_t BUF_SIZE = 4 * 1024;
948 : : {
949 : : DWORD dwRead;
950 : : char chBuf[BUF_SIZE];
951 : :
952 : : buf_resize(out_stdout, 0);
953 : : for (;;) {
954 : : success = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUF_SIZE, &dwRead, NULL);
955 : : if (!success || dwRead == 0) break;
956 : :
957 : : buf_append_mem(out_stdout, chBuf, dwRead);
958 : : }
959 : : CloseHandle(g_hChildStd_OUT_Rd);
960 : : }
961 : : {
962 : : DWORD dwRead;
963 : : char chBuf[BUF_SIZE];
964 : :
965 : : buf_resize(out_stderr, 0);
966 : : for (;;) {
967 : : success = ReadFile( g_hChildStd_ERR_Rd, chBuf, BUF_SIZE, &dwRead, NULL);
968 : : if (!success || dwRead == 0) break;
969 : :
970 : : buf_append_mem(out_stderr, chBuf, dwRead);
971 : : }
972 : : CloseHandle(g_hChildStd_ERR_Rd);
973 : : }
974 : :
975 : : WaitForSingleObject(piProcInfo.hProcess, INFINITE);
976 : :
977 : : DWORD exit_code;
978 : : if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) {
979 : : zig_panic("GetExitCodeProcess failed");
980 : : }
981 : : term->how = TerminationIdClean;
982 : : term->code = exit_code;
983 : :
984 : : CloseHandle(piProcInfo.hProcess);
985 : : CloseHandle(piProcInfo.hThread);
986 : :
987 : : return ErrorNone;
988 : : }
989 : : #endif
990 : :
991 : 0 : Error os_execv(const char *exe, const char **argv) {
992 : : #if defined(ZIG_OS_WINDOWS)
993 : : return ErrorUnsupportedOperatingSystem;
994 : : #else
995 : 0 : execv(exe, (char *const *)argv);
996 [ # # # ]: 0 : switch (errno) {
997 : 0 : case ENOMEM:
998 : 0 : return ErrorSystemResources;
999 : 0 : case EIO:
1000 : 0 : return ErrorFileSystem;
1001 : 0 : default:
1002 : 0 : return ErrorUnexpected;
1003 : : }
1004 : : #endif
1005 : : }
1006 : :
1007 : 0 : Error os_exec_process(ZigList<const char *> &args,
1008 : : Termination *term, Buf *out_stderr, Buf *out_stdout)
1009 : : {
1010 : : #if defined(ZIG_OS_WINDOWS)
1011 : : return os_exec_process_windows(args, term, out_stderr, out_stdout);
1012 : : #elif defined(ZIG_OS_POSIX)
1013 : 0 : return os_exec_process_posix(args, term, out_stderr, out_stdout);
1014 : : #else
1015 : : #error "missing os_exec_process implementation"
1016 : : #endif
1017 : : }
1018 : :
1019 : 24 : Error os_write_file(Buf *full_path, Buf *contents) {
1020 : 24 : FILE *f = fopen(buf_ptr(full_path), "wb");
1021 [ - + ]: 24 : if (!f) {
1022 : 0 : zig_panic("os_write_file failed for %s", buf_ptr(full_path));
1023 : : }
1024 : 24 : size_t amt_written = fwrite(buf_ptr(contents), 1, buf_len(contents), f);
1025 [ - + ]: 24 : if (amt_written != (size_t)buf_len(contents))
1026 : 0 : zig_panic("write failed: %s", strerror(errno));
1027 [ - + ]: 24 : if (fclose(f))
1028 : 0 : zig_panic("close failed");
1029 : 24 : return ErrorNone;
1030 : : }
1031 : :
1032 : 0 : Error os_copy_file(Buf *src_path, Buf *dest_path) {
1033 : 0 : FILE *src_f = fopen(buf_ptr(src_path), "rb");
1034 [ # # ]: 0 : if (!src_f) {
1035 : 0 : int err = errno;
1036 [ # # ]: 0 : if (err == ENOENT) {
1037 : 0 : return ErrorFileNotFound;
1038 [ # # ][ # # ]: 0 : } else if (err == EACCES || err == EPERM) {
1039 : 0 : return ErrorAccess;
1040 : : } else {
1041 : 0 : return ErrorFileSystem;
1042 : : }
1043 : : }
1044 : 0 : FILE *dest_f = fopen(buf_ptr(dest_path), "wb");
1045 [ # # ]: 0 : if (!dest_f) {
1046 : 0 : int err = errno;
1047 [ # # ]: 0 : if (err == ENOENT) {
1048 : 0 : fclose(src_f);
1049 : 0 : return ErrorFileNotFound;
1050 [ # # ][ # # ]: 0 : } else if (err == EACCES || err == EPERM) {
1051 : 0 : fclose(src_f);
1052 : 0 : return ErrorAccess;
1053 : : } else {
1054 : 0 : fclose(src_f);
1055 : 0 : return ErrorFileSystem;
1056 : : }
1057 : : }
1058 : :
1059 : : static const size_t buf_size = 2048;
1060 : : char buf[buf_size];
1061 : : for (;;) {
1062 : 0 : size_t amt_read = fread(buf, 1, buf_size, src_f);
1063 [ # # ]: 0 : if (amt_read != buf_size) {
1064 [ # # ]: 0 : if (ferror(src_f)) {
1065 : 0 : fclose(src_f);
1066 : 0 : fclose(dest_f);
1067 : 0 : return ErrorFileSystem;
1068 : : }
1069 : : }
1070 : 0 : size_t amt_written = fwrite(buf, 1, amt_read, dest_f);
1071 [ # # ]: 0 : if (amt_written != amt_read) {
1072 : 0 : fclose(src_f);
1073 : 0 : fclose(dest_f);
1074 : 0 : return ErrorFileSystem;
1075 : : }
1076 [ # # ]: 0 : if (feof(src_f)) {
1077 : 0 : fclose(src_f);
1078 : 0 : fclose(dest_f);
1079 : 0 : return ErrorNone;
1080 : : }
1081 : 0 : }
1082 : : }
1083 : :
1084 : 69 : Error os_fetch_file_path(Buf *full_path, Buf *out_contents) {
1085 : 69 : FILE *f = fopen(buf_ptr(full_path), "rb");
1086 [ - + ]: 69 : if (!f) {
1087 [ # # # # : 0 : switch (errno) {
# # ]
1088 : 0 : case EACCES:
1089 : 0 : return ErrorAccess;
1090 : 0 : case EINTR:
1091 : 0 : return ErrorInterrupted;
1092 : 0 : case EINVAL:
1093 : 0 : return ErrorInvalidFilename;
1094 : 0 : case ENFILE:
1095 : : case ENOMEM:
1096 : 0 : return ErrorSystemResources;
1097 : 0 : case ENOENT:
1098 : 0 : return ErrorFileNotFound;
1099 : 0 : default:
1100 : 0 : return ErrorFileSystem;
1101 : : }
1102 : : }
1103 : 69 : Error result = os_fetch_file(f, out_contents);
1104 : 69 : fclose(f);
1105 : 69 : return result;
1106 : : }
1107 : :
1108 : 2 : Error os_get_cwd(Buf *out_cwd) {
1109 : : #if defined(ZIG_OS_WINDOWS)
1110 : : char buf[4096];
1111 : : if (GetCurrentDirectory(4096, buf) == 0) {
1112 : : zig_panic("GetCurrentDirectory failed");
1113 : : }
1114 : : buf_init_from_str(out_cwd, buf);
1115 : : return ErrorNone;
1116 : : #elif defined(ZIG_OS_POSIX)
1117 : : char buf[PATH_MAX];
1118 : 2 : char *res = getcwd(buf, PATH_MAX);
1119 [ - + ]: 2 : if (res == nullptr) {
1120 : 0 : zig_panic("unable to get cwd: %s", strerror(errno));
1121 : : }
1122 : 2 : buf_init_from_str(out_cwd, res);
1123 : 2 : return ErrorNone;
1124 : : #else
1125 : : #error "missing os_get_cwd implementation"
1126 : : #endif
1127 : : }
1128 : :
1129 : : #if defined(ZIG_OS_WINDOWS)
1130 : : #define is_wprefix(s, prefix) \
1131 : : (wcsncmp((s), (prefix), sizeof(prefix) / sizeof(WCHAR) - 1) == 0)
1132 : : static bool is_stderr_cyg_pty(void) {
1133 : : HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
1134 : : if (stderr_handle == INVALID_HANDLE_VALUE)
1135 : : return false;
1136 : :
1137 : : int size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH;
1138 : : FILE_NAME_INFO *nameinfo;
1139 : : WCHAR *p = NULL;
1140 : :
1141 : : // Cygwin/msys's pty is a pipe.
1142 : : if (GetFileType(stderr_handle) != FILE_TYPE_PIPE) {
1143 : : return 0;
1144 : : }
1145 : : nameinfo = (FILE_NAME_INFO *)allocate<char>(size);
1146 : : if (nameinfo == NULL) {
1147 : : return 0;
1148 : : }
1149 : : // Check the name of the pipe:
1150 : : // '\{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master'
1151 : : if (GetFileInformationByHandleEx(stderr_handle, FileNameInfo, nameinfo, size)) {
1152 : : nameinfo->FileName[nameinfo->FileNameLength / sizeof(WCHAR)] = L'\0';
1153 : : p = nameinfo->FileName;
1154 : : if (is_wprefix(p, L"\\cygwin-")) { /* Cygwin */
1155 : : p += 8;
1156 : : } else if (is_wprefix(p, L"\\msys-")) { /* MSYS and MSYS2 */
1157 : : p += 6;
1158 : : } else {
1159 : : p = NULL;
1160 : : }
1161 : : if (p != NULL) {
1162 : : while (*p && isxdigit(*p)) /* Skip 16-digit hexadecimal. */
1163 : : ++p;
1164 : : if (is_wprefix(p, L"-pty")) {
1165 : : p += 4;
1166 : : } else {
1167 : : p = NULL;
1168 : : }
1169 : : }
1170 : : if (p != NULL) {
1171 : : while (*p && isdigit(*p)) /* Skip pty number. */
1172 : : ++p;
1173 : : if (is_wprefix(p, L"-from-master")) {
1174 : : //p += 12;
1175 : : } else if (is_wprefix(p, L"-to-master")) {
1176 : : //p += 10;
1177 : : } else {
1178 : : p = NULL;
1179 : : }
1180 : : }
1181 : : }
1182 : : free(nameinfo);
1183 : : return (p != NULL);
1184 : : }
1185 : : #endif
1186 : :
1187 : 0 : bool os_stderr_tty(void) {
1188 : : #if defined(ZIG_OS_WINDOWS)
1189 : : return _isatty(_fileno(stderr)) != 0 || is_stderr_cyg_pty();
1190 : : #elif defined(ZIG_OS_POSIX)
1191 : 0 : return isatty(STDERR_FILENO) != 0;
1192 : : #else
1193 : : #error "missing os_stderr_tty implementation"
1194 : : #endif
1195 : : }
1196 : :
1197 : 35 : Error os_delete_file(Buf *path) {
1198 [ - + ]: 35 : if (remove(buf_ptr(path))) {
1199 : 0 : return ErrorFileSystem;
1200 : : } else {
1201 : 35 : return ErrorNone;
1202 : : }
1203 : : }
1204 : :
1205 : 35 : Error os_rename(Buf *src_path, Buf *dest_path) {
1206 [ - + ]: 35 : if (buf_eql_buf(src_path, dest_path)) {
1207 : 0 : return ErrorNone;
1208 : : }
1209 : : #if defined(ZIG_OS_WINDOWS)
1210 : : if (!MoveFileExA(buf_ptr(src_path), buf_ptr(dest_path), MOVEFILE_REPLACE_EXISTING)) {
1211 : : return ErrorFileSystem;
1212 : : }
1213 : : #else
1214 [ - + ]: 35 : if (rename(buf_ptr(src_path), buf_ptr(dest_path)) == -1) {
1215 : 0 : return ErrorFileSystem;
1216 : : }
1217 : : #endif
1218 : 35 : return ErrorNone;
1219 : : }
1220 : :
1221 : : #if defined(ZIG_OS_WINDOWS)
1222 : : static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) {
1223 : : mtime->sec = (((ULONGLONG) ft->dwHighDateTime) << 32) + ft->dwLowDateTime;
1224 : : mtime->nsec = 0;
1225 : : }
1226 : : #endif
1227 : :
1228 : 2952 : OsTimeStamp os_timestamp_calendar(void) {
1229 : : OsTimeStamp result;
1230 : : #if defined(ZIG_OS_WINDOWS)
1231 : : FILETIME ft;
1232 : : GetSystemTimeAsFileTime(&ft);
1233 : : windows_filetime_to_os_timestamp(&ft, &result);
1234 : : #elif defined(__MACH__)
1235 : : mach_timespec_t mts;
1236 : :
1237 : : kern_return_t err = clock_get_time(macos_calendar_clock, &mts);
1238 : : assert(!err);
1239 : :
1240 : : result.sec = mts.tv_sec;
1241 : : result.nsec = mts.tv_nsec;
1242 : : #else
1243 : : struct timespec tms;
1244 : 2952 : clock_gettime(CLOCK_REALTIME, &tms);
1245 : :
1246 : 2952 : result.sec = tms.tv_sec;
1247 : 2952 : result.nsec = tms.tv_nsec;
1248 : : #endif
1249 : 2952 : return result;
1250 : : }
1251 : :
1252 : 310 : OsTimeStamp os_timestamp_monotonic(void) {
1253 : : OsTimeStamp result;
1254 : : #if defined(ZIG_OS_WINDOWS)
1255 : : uint64_t counts;
1256 : : QueryPerformanceCounter((LARGE_INTEGER*)&counts);
1257 : : result.sec = counts / windows_perf_freq;
1258 : : result.nsec = (counts % windows_perf_freq) * 1000000000u / windows_perf_freq;
1259 : : #elif defined(__MACH__)
1260 : : mach_timespec_t mts;
1261 : :
1262 : : kern_return_t err = clock_get_time(macos_monotonic_clock, &mts);
1263 : : assert(!err);
1264 : :
1265 : : result.sec = mts.tv_sec;
1266 : : result.nsec = mts.tv_nsec;
1267 : : #else
1268 : : struct timespec tms;
1269 : 310 : clock_gettime(CLOCK_MONOTONIC, &tms);
1270 : :
1271 : 310 : result.sec = tms.tv_sec;
1272 : 310 : result.nsec = tms.tv_nsec;
1273 : : #endif
1274 : 310 : return result;
1275 : : }
1276 : :
1277 : 279 : Error os_make_path(Buf *path) {
1278 : 279 : Buf resolved_path = os_path_resolve(&path, 1);
1279 : :
1280 : 279 : size_t end_index = buf_len(&resolved_path);
1281 : : Error err;
1282 : : while (true) {
1283 [ + + ]: 570 : if ((err = os_make_dir(buf_slice(&resolved_path, 0, end_index)))) {
1284 [ + + ]: 194 : if (err == ErrorPathAlreadyExists) {
1285 [ + - ]: 188 : if (end_index == buf_len(&resolved_path))
1286 : 188 : return ErrorNone;
1287 [ + - ]: 6 : } else if (err == ErrorFileNotFound) {
1288 : : // march end_index backward until next path component
1289 : : while (true) {
1290 : 202 : end_index -= 1;
1291 [ + + ]: 208 : if (os_is_sep(buf_ptr(&resolved_path)[end_index]))
1292 : 6 : break;
1293 : : }
1294 : 6 : continue;
1295 : : } else {
1296 : 0 : return err;
1297 : : }
1298 : : }
1299 [ + + ]: 97 : if (end_index == buf_len(&resolved_path))
1300 : 91 : return ErrorNone;
1301 : : // march end_index forward until next path component
1302 : : while (true) {
1303 : 202 : end_index += 1;
1304 [ + + ][ + + ]: 208 : if (end_index == buf_len(&resolved_path) || os_is_sep(buf_ptr(&resolved_path)[end_index]))
[ + + ]
1305 : 6 : break;
1306 : : }
1307 : : }
1308 : : return ErrorNone;
1309 : : }
1310 : :
1311 : 291 : Error os_make_dir(Buf *path) {
1312 : : #if defined(ZIG_OS_WINDOWS)
1313 : : if (!CreateDirectory(buf_ptr(path), NULL)) {
1314 : : if (GetLastError() == ERROR_ALREADY_EXISTS)
1315 : : return ErrorPathAlreadyExists;
1316 : : if (GetLastError() == ERROR_PATH_NOT_FOUND)
1317 : : return ErrorFileNotFound;
1318 : : if (GetLastError() == ERROR_ACCESS_DENIED)
1319 : : return ErrorAccess;
1320 : : return ErrorUnexpected;
1321 : : }
1322 : : return ErrorNone;
1323 : : #else
1324 [ + + ]: 291 : if (mkdir(buf_ptr(path), 0755) == -1) {
1325 [ + + ]: 194 : if (errno == EEXIST)
1326 : 188 : return ErrorPathAlreadyExists;
1327 [ + - ]: 6 : if (errno == ENOENT)
1328 : 6 : return ErrorFileNotFound;
1329 [ # # ]: 0 : if (errno == EACCES)
1330 : 0 : return ErrorAccess;
1331 : 0 : return ErrorUnexpected;
1332 : : }
1333 : 97 : return ErrorNone;
1334 : : #endif
1335 : : }
1336 : :
1337 : 18 : static void init_rand() {
1338 : : #if defined(ZIG_OS_WINDOWS)
1339 : : char bytes[sizeof(unsigned)];
1340 : : unsigned seed;
1341 : : RtlGenRandom(bytes, sizeof(unsigned));
1342 : : memcpy(&seed, bytes, sizeof(unsigned));
1343 : : srand(seed);
1344 : : #elif defined(ZIG_OS_LINUX)
1345 : 18 : srand(*((unsigned*)getauxval(AT_RANDOM)));
1346 : : #else
1347 : : int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC);
1348 : : if (fd == -1) {
1349 : : zig_panic("unable to open /dev/urandom");
1350 : : }
1351 : : char bytes[sizeof(unsigned)];
1352 : : ssize_t amt_read;
1353 : : while ((amt_read = read(fd, bytes, sizeof(unsigned))) == -1) {
1354 : : if (errno == EINTR) continue;
1355 : : zig_panic("unable to read /dev/urandom");
1356 : : }
1357 : : if (amt_read != sizeof(unsigned)) {
1358 : : zig_panic("unable to read enough bytes from /dev/urandom");
1359 : : }
1360 : : close(fd);
1361 : : unsigned seed;
1362 : : memcpy(&seed, bytes, sizeof(unsigned));
1363 : : srand(seed);
1364 : : #endif
1365 : 18 : }
1366 : :
1367 : 18 : int os_init(void) {
1368 : 18 : init_rand();
1369 : : #if defined(ZIG_OS_WINDOWS)
1370 : : _setmode(fileno(stdout), _O_BINARY);
1371 : : _setmode(fileno(stderr), _O_BINARY);
1372 : : if (!QueryPerformanceFrequency((LARGE_INTEGER*)&windows_perf_freq)) {
1373 : : return ErrorSystemResources;
1374 : : }
1375 : : #elif defined(__MACH__)
1376 : : host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &macos_monotonic_clock);
1377 : : host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &macos_calendar_clock);
1378 : : #endif
1379 : : #if defined(ZIG_OS_POSIX)
1380 : : // Raise the open file descriptor limit.
1381 : : // Code lifted from node.js
1382 : : struct rlimit lim;
1383 [ + + ][ + + ]: 18 : if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) {
[ + - ]
1384 : : // Do a binary search for the limit.
1385 : 2 : rlim_t min = lim.rlim_cur;
1386 : 2 : rlim_t max = 1 << 20;
1387 : : // But if there's a defined upper bound, don't search, just set it.
1388 [ + - ]: 2 : if (lim.rlim_max != RLIM_INFINITY) {
1389 : 2 : min = lim.rlim_max;
1390 : 2 : max = lim.rlim_max;
1391 : : }
1392 : 2 : do {
1393 : 2 : lim.rlim_cur = min + (max - min) / 2;
1394 [ - + ]: 2 : if (setrlimit(RLIMIT_NOFILE, &lim)) {
1395 : 0 : max = lim.rlim_cur;
1396 : : } else {
1397 : 2 : min = lim.rlim_cur;
1398 : : }
1399 [ - + ]: 2 : } while (min + 1 < max);
1400 : : }
1401 : : #endif
1402 : 18 : return 0;
1403 : : }
1404 : :
1405 : 78 : Error os_self_exe_path(Buf *out_path) {
1406 : : #if defined(ZIG_OS_WINDOWS)
1407 : : buf_resize(out_path, 256);
1408 : : for (;;) {
1409 : : DWORD copied_amt = GetModuleFileName(nullptr, buf_ptr(out_path), buf_len(out_path));
1410 : : if (copied_amt <= 0) {
1411 : : return ErrorFileNotFound;
1412 : : }
1413 : : if (copied_amt < buf_len(out_path)) {
1414 : : buf_resize(out_path, copied_amt);
1415 : : return ErrorNone;
1416 : : }
1417 : : buf_resize(out_path, buf_len(out_path) * 2);
1418 : : }
1419 : :
1420 : : #elif defined(ZIG_OS_DARWIN)
1421 : : // How long is the executable's path?
1422 : : uint32_t u32_len = 0;
1423 : : int ret1 = _NSGetExecutablePath(nullptr, &u32_len);
1424 : : assert(ret1 != 0);
1425 : :
1426 : : Buf *tmp = buf_alloc_fixed(u32_len);
1427 : :
1428 : : // Fill the executable path.
1429 : : int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len);
1430 : : assert(ret2 == 0);
1431 : :
1432 : : // According to libuv project, PATH_MAX*2 works around a libc bug where
1433 : : // the resolved path is sometimes bigger than PATH_MAX.
1434 : : buf_resize(out_path, PATH_MAX*2);
1435 : : char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path));
1436 : : if (!real_path) {
1437 : : buf_init_from_buf(out_path, tmp);
1438 : : return ErrorNone;
1439 : : }
1440 : :
1441 : : // Resize out_path for the correct length.
1442 : : buf_resize(out_path, strlen(buf_ptr(out_path)));
1443 : :
1444 : : return ErrorNone;
1445 : : #elif defined(ZIG_OS_LINUX)
1446 : 78 : buf_resize(out_path, PATH_MAX);
1447 : 78 : ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path));
1448 [ - + ]: 78 : if (amt == -1) {
1449 : 0 : return ErrorUnexpected;
1450 : : }
1451 : 78 : buf_resize(out_path, amt);
1452 : 78 : return ErrorNone;
1453 : : #elif defined(ZIG_OS_FREEBSD)
1454 : : buf_resize(out_path, PATH_MAX);
1455 : : int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
1456 : : size_t cb = PATH_MAX;
1457 : : if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) {
1458 : : return ErrorUnexpected;
1459 : : }
1460 : : buf_resize(out_path, cb - 1);
1461 : : return ErrorNone;
1462 : : #elif defined(ZIG_OS_NETBSD)
1463 : : buf_resize(out_path, PATH_MAX);
1464 : : int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
1465 : : size_t cb = PATH_MAX;
1466 : : if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) {
1467 : : return ErrorUnexpected;
1468 : : }
1469 : : buf_resize(out_path, cb - 1);
1470 : : return ErrorNone;
1471 : : #endif
1472 : : return ErrorFileNotFound;
1473 : : }
1474 : :
1475 : : #define VT_RED "\x1b[31;1m"
1476 : : #define VT_GREEN "\x1b[32;1m"
1477 : : #define VT_CYAN "\x1b[36;1m"
1478 : : #define VT_WHITE "\x1b[37;1m"
1479 : : #define VT_BOLD "\x1b[0;1m"
1480 : : #define VT_RESET "\x1b[0m"
1481 : :
1482 : 0 : static void set_color_posix(TermColor color) {
1483 [ # # # # : 0 : switch (color) {
# # # ]
1484 : 0 : case TermColorRed:
1485 : 0 : fprintf(stderr, VT_RED);
1486 : 0 : break;
1487 : 0 : case TermColorGreen:
1488 : 0 : fprintf(stderr, VT_GREEN);
1489 : 0 : break;
1490 : 0 : case TermColorCyan:
1491 : 0 : fprintf(stderr, VT_CYAN);
1492 : 0 : break;
1493 : 0 : case TermColorWhite:
1494 : 0 : fprintf(stderr, VT_WHITE);
1495 : 0 : break;
1496 : 0 : case TermColorBold:
1497 : 0 : fprintf(stderr, VT_BOLD);
1498 : 0 : break;
1499 : 0 : case TermColorReset:
1500 : 0 : fprintf(stderr, VT_RESET);
1501 : 0 : break;
1502 : : }
1503 : 0 : }
1504 : :
1505 : :
1506 : : #if defined(ZIG_OS_WINDOWS)
1507 : : bool got_orig_console_attrs = false;
1508 : : WORD original_console_attributes = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE;
1509 : : #endif
1510 : :
1511 : 0 : void os_stderr_set_color(TermColor color) {
1512 : : #if defined(ZIG_OS_WINDOWS)
1513 : : if (is_stderr_cyg_pty()) {
1514 : : set_color_posix(color);
1515 : : return;
1516 : : }
1517 : : HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
1518 : : if (stderr_handle == INVALID_HANDLE_VALUE)
1519 : : zig_panic("unable to get stderr handle");
1520 : : fflush(stderr);
1521 : :
1522 : : if (!got_orig_console_attrs) {
1523 : : got_orig_console_attrs = true;
1524 : : CONSOLE_SCREEN_BUFFER_INFO info;
1525 : : if (GetConsoleScreenBufferInfo(stderr_handle, &info)) {
1526 : : original_console_attributes = info.wAttributes;
1527 : : }
1528 : : }
1529 : :
1530 : : switch (color) {
1531 : : case TermColorRed:
1532 : : SetConsoleTextAttribute(stderr_handle, FOREGROUND_RED|FOREGROUND_INTENSITY);
1533 : : break;
1534 : : case TermColorGreen:
1535 : : SetConsoleTextAttribute(stderr_handle, FOREGROUND_GREEN|FOREGROUND_INTENSITY);
1536 : : break;
1537 : : case TermColorCyan:
1538 : : SetConsoleTextAttribute(stderr_handle, FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
1539 : : break;
1540 : : case TermColorWhite:
1541 : : case TermColorBold:
1542 : : SetConsoleTextAttribute(stderr_handle,
1543 : : FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
1544 : : break;
1545 : : case TermColorReset:
1546 : : SetConsoleTextAttribute(stderr_handle, original_console_attributes);
1547 : : break;
1548 : : }
1549 : : #else
1550 : 0 : set_color_posix(color);
1551 : : #endif
1552 : 0 : }
1553 : :
1554 : 0 : Error os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {
1555 : : #if defined(ZIG_OS_WINDOWS)
1556 : : buf_resize(output_buf, 0);
1557 : : buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr);
1558 : : switch (platform_type) {
1559 : : case ZigLLVM_x86:
1560 : : buf_append_str(output_buf, "x86\\");
1561 : : break;
1562 : : case ZigLLVM_x86_64:
1563 : : buf_append_str(output_buf, "x64\\");
1564 : : break;
1565 : : case ZigLLVM_arm:
1566 : : buf_append_str(output_buf, "arm\\");
1567 : : break;
1568 : : default:
1569 : : zig_panic("Attempted to use vcruntime for non-supported platform.");
1570 : : }
1571 : : Buf* tmp_buf = buf_alloc();
1572 : : buf_init_from_buf(tmp_buf, output_buf);
1573 : : buf_append_str(tmp_buf, "ucrt.lib");
1574 : : if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
1575 : : return ErrorNone;
1576 : : }
1577 : : else {
1578 : : buf_resize(output_buf, 0);
1579 : : return ErrorFileNotFound;
1580 : : }
1581 : : #else
1582 : 0 : return ErrorFileNotFound;
1583 : : #endif
1584 : : }
1585 : :
1586 : 0 : Error os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) {
1587 : : #if defined(ZIG_OS_WINDOWS)
1588 : : buf_resize(output_buf, 0);
1589 : : buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr);
1590 : : if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) {
1591 : : return ErrorNone;
1592 : : }
1593 : : else {
1594 : : buf_resize(output_buf, 0);
1595 : : return ErrorFileNotFound;
1596 : : }
1597 : : #else
1598 : 0 : return ErrorFileNotFound;
1599 : : #endif
1600 : : }
1601 : :
1602 : 0 : Error os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {
1603 : : #if defined(ZIG_OS_WINDOWS)
1604 : : {
1605 : : buf_resize(output_buf, 0);
1606 : : buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr);
1607 : : switch (platform_type) {
1608 : : case ZigLLVM_x86:
1609 : : buf_append_str(output_buf, "x86\\");
1610 : : break;
1611 : : case ZigLLVM_x86_64:
1612 : : buf_append_str(output_buf, "x64\\");
1613 : : break;
1614 : : case ZigLLVM_arm:
1615 : : buf_append_str(output_buf, "arm\\");
1616 : : break;
1617 : : default:
1618 : : zig_panic("Attempted to use vcruntime for non-supported platform.");
1619 : : }
1620 : : Buf* tmp_buf = buf_alloc();
1621 : : buf_init_from_buf(tmp_buf, output_buf);
1622 : : buf_append_str(tmp_buf, "kernel32.lib");
1623 : : if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
1624 : : return ErrorNone;
1625 : : }
1626 : : }
1627 : : {
1628 : : buf_resize(output_buf, 0);
1629 : : buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr);
1630 : : switch (platform_type) {
1631 : : case ZigLLVM_x86:
1632 : : buf_append_str(output_buf, "x86\\");
1633 : : break;
1634 : : case ZigLLVM_x86_64:
1635 : : buf_append_str(output_buf, "x64\\");
1636 : : break;
1637 : : case ZigLLVM_arm:
1638 : : buf_append_str(output_buf, "arm\\");
1639 : : break;
1640 : : default:
1641 : : zig_panic("Attempted to use vcruntime for non-supported platform.");
1642 : : }
1643 : : Buf* tmp_buf = buf_alloc();
1644 : : buf_init_from_buf(tmp_buf, output_buf);
1645 : : buf_append_str(tmp_buf, "kernel32.lib");
1646 : : if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
1647 : : return ErrorNone;
1648 : : }
1649 : : }
1650 : : return ErrorFileNotFound;
1651 : : #else
1652 : 0 : return ErrorFileNotFound;
1653 : : #endif
1654 : : }
1655 : :
1656 : : #if defined(ZIG_OS_WINDOWS)
1657 : : // Ported from std/unicode.zig
1658 : : struct Utf16LeIterator {
1659 : : uint8_t *bytes;
1660 : : size_t i;
1661 : : };
1662 : :
1663 : : // Ported from std/unicode.zig
1664 : : static Utf16LeIterator Utf16LeIterator_init(WCHAR *ptr) {
1665 : : return {(uint8_t*)ptr, 0};
1666 : : }
1667 : :
1668 : : // Ported from std/unicode.zig
1669 : : static Optional<uint32_t> Utf16LeIterator_nextCodepoint(Utf16LeIterator *it) {
1670 : : if (it->bytes[it->i] == 0 && it->bytes[it->i + 1] == 0)
1671 : : return {};
1672 : : uint32_t c0 = ((uint32_t)it->bytes[it->i]) | (((uint32_t)it->bytes[it->i + 1]) << 8);
1673 : : if ((c0 & ~((uint32_t)0x03ff)) == 0xd800) {
1674 : : // surrogate pair
1675 : : it->i += 2;
1676 : : assert(it->bytes[it->i] != 0 || it->bytes[it->i + 1] != 0);
1677 : : uint32_t c1 = ((uint32_t)it->bytes[it->i]) | (((uint32_t)it->bytes[it->i + 1]) << 8);
1678 : : assert((c1 & ~((uint32_t)0x03ff)) == 0xdc00);
1679 : : it->i += 2;
1680 : : return Optional<uint32_t>::some(0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff)));
1681 : : } else {
1682 : : assert((c0 & ~((uint32_t)0x03ff)) != 0xdc00);
1683 : : it->i += 2;
1684 : : return Optional<uint32_t>::some(c0);
1685 : : }
1686 : : }
1687 : :
1688 : : // Ported from std/unicode.zig
1689 : : static uint8_t utf8CodepointSequenceLength(uint32_t c) {
1690 : : if (c < 0x80) return 1;
1691 : : if (c < 0x800) return 2;
1692 : : if (c < 0x10000) return 3;
1693 : : if (c < 0x110000) return 4;
1694 : : zig_unreachable();
1695 : : }
1696 : :
1697 : : // Ported from std/unicode.zig
1698 : : static size_t utf8Encode(uint32_t c, Slice<uint8_t> out) {
1699 : : size_t length = utf8CodepointSequenceLength(c);
1700 : : assert(out.len >= length);
1701 : : switch (length) {
1702 : : // The pattern for each is the same
1703 : : // - Increasing the initial shift by 6 each time
1704 : : // - Each time after the first shorten the shifted
1705 : : // value to a max of 0b111111 (63)
1706 : : case 1:
1707 : : out.ptr[0] = c; // Can just do 0 + codepoint for initial range
1708 : : break;
1709 : : case 2:
1710 : : out.ptr[0] = 0b11000000 | (c >> 6);
1711 : : out.ptr[1] = 0b10000000 | (c & 0b111111);
1712 : : break;
1713 : : case 3:
1714 : : assert(!(0xd800 <= c && c <= 0xdfff));
1715 : : out.ptr[0] = 0b11100000 | (c >> 12);
1716 : : out.ptr[1] = 0b10000000 | ((c >> 6) & 0b111111);
1717 : : out.ptr[2] = 0b10000000 | (c & 0b111111);
1718 : : break;
1719 : : case 4:
1720 : : out.ptr[0] = 0b11110000 | (c >> 18);
1721 : : out.ptr[1] = 0b10000000 | ((c >> 12) & 0b111111);
1722 : : out.ptr[2] = 0b10000000 | ((c >> 6) & 0b111111);
1723 : : out.ptr[3] = 0b10000000 | (c & 0b111111);
1724 : : break;
1725 : : default:
1726 : : zig_unreachable();
1727 : : }
1728 : : return length;
1729 : : }
1730 : :
1731 : : // Ported from std.unicode.utf16leToUtf8Alloc
1732 : : static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le) {
1733 : : // optimistically guess that it will all be ascii.
1734 : : buf_resize(out, 0);
1735 : : size_t out_index = 0;
1736 : : Utf16LeIterator it = Utf16LeIterator_init(utf16le);
1737 : : for (;;) {
1738 : : Optional<uint32_t> opt_codepoint = Utf16LeIterator_nextCodepoint(&it);
1739 : : if (!opt_codepoint.is_some) break;
1740 : : uint32_t codepoint = opt_codepoint.value;
1741 : :
1742 : : size_t utf8_len = utf8CodepointSequenceLength(codepoint);
1743 : : buf_resize(out, buf_len(out) + utf8_len);
1744 : : utf8Encode(codepoint, {(uint8_t*)buf_ptr(out)+out_index, buf_len(out)-out_index});
1745 : : out_index += utf8_len;
1746 : : }
1747 : : }
1748 : : #endif
1749 : :
1750 : : // Ported from std.os.getAppDataDir
1751 : 18 : Error os_get_app_data_dir(Buf *out_path, const char *appname) {
1752 : : #if defined(ZIG_OS_WINDOWS)
1753 : : WCHAR *dir_path_ptr;
1754 : : switch (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &dir_path_ptr)) {
1755 : : case S_OK:
1756 : : // defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr));
1757 : : utf16le_ptr_to_utf8(out_path, dir_path_ptr);
1758 : : CoTaskMemFree(dir_path_ptr);
1759 : : buf_appendf(out_path, "\\%s", appname);
1760 : : return ErrorNone;
1761 : : case E_OUTOFMEMORY:
1762 : : return ErrorNoMem;
1763 : : default:
1764 : : return ErrorUnexpected;
1765 : : }
1766 : : zig_unreachable();
1767 : : #elif defined(ZIG_OS_DARWIN)
1768 : : const char *home_dir = getenv("HOME");
1769 : : if (home_dir == nullptr) {
1770 : : // TODO use /etc/passwd
1771 : : return ErrorFileNotFound;
1772 : : }
1773 : : buf_resize(out_path, 0);
1774 : : buf_appendf(out_path, "%s/Library/Application Support/%s", home_dir, appname);
1775 : : return ErrorNone;
1776 : : #elif defined(ZIG_OS_POSIX)
1777 : 18 : const char *home_dir = getenv("HOME");
1778 [ - + ]: 18 : if (home_dir == nullptr) {
1779 : : // TODO use /etc/passwd
1780 : 0 : return ErrorFileNotFound;
1781 : : }
1782 [ - + ]: 18 : if (home_dir[0] == 0) {
1783 : 0 : return ErrorFileNotFound;
1784 : : }
1785 : 18 : buf_init_from_str(out_path, home_dir);
1786 [ + - ]: 18 : if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') {
1787 : 18 : buf_append_char(out_path, '/');
1788 : : }
1789 : 18 : buf_appendf(out_path, ".local/share/%s", appname);
1790 : 18 : return ErrorNone;
1791 : : #endif
1792 : : }
1793 : :
1794 : :
1795 : : #if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD)
1796 : 230 : static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, void *data) {
1797 : 230 : ZigList<Buf *> *libs = reinterpret_cast< ZigList<Buf *> *>(data);
1798 [ + + ]: 230 : if (info->dlpi_name[0] == '/') {
1799 : 184 : libs->append(buf_create_from_str(info->dlpi_name));
1800 : : }
1801 : 230 : return 0;
1802 : : }
1803 : : #endif
1804 : :
1805 : 23 : Error os_self_exe_shared_libs(ZigList<Buf *> &paths) {
1806 : : #if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD)
1807 : 23 : paths.resize(0);
1808 : 23 : dl_iterate_phdr(self_exe_shared_libs_callback, &paths);
1809 : 23 : return ErrorNone;
1810 : : #elif defined(ZIG_OS_DARWIN)
1811 : : paths.resize(0);
1812 : : uint32_t img_count = _dyld_image_count();
1813 : : for (uint32_t i = 0; i != img_count; i += 1) {
1814 : : const char *name = _dyld_get_image_name(i);
1815 : : paths.append(buf_create_from_str(name));
1816 : : }
1817 : : return ErrorNone;
1818 : : #elif defined(ZIG_OS_WINDOWS)
1819 : : // zig is built statically on windows, so we can return an empty list
1820 : : paths.resize(0);
1821 : : return ErrorNone;
1822 : : #else
1823 : : #error unimplemented
1824 : : #endif
1825 : : }
1826 : :
1827 : 5454 : Error os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr) {
1828 : : #if defined(ZIG_OS_WINDOWS)
1829 : : // TODO use CreateFileW
1830 : : HANDLE result = CreateFileA(buf_ptr(full_path), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
1831 : :
1832 : : if (result == INVALID_HANDLE_VALUE) {
1833 : : DWORD err = GetLastError();
1834 : : switch (err) {
1835 : : case ERROR_SHARING_VIOLATION:
1836 : : return ErrorSharingViolation;
1837 : : case ERROR_ALREADY_EXISTS:
1838 : : return ErrorPathAlreadyExists;
1839 : : case ERROR_FILE_EXISTS:
1840 : : return ErrorPathAlreadyExists;
1841 : : case ERROR_FILE_NOT_FOUND:
1842 : : return ErrorFileNotFound;
1843 : : case ERROR_PATH_NOT_FOUND:
1844 : : return ErrorFileNotFound;
1845 : : case ERROR_ACCESS_DENIED:
1846 : : return ErrorAccess;
1847 : : case ERROR_PIPE_BUSY:
1848 : : return ErrorPipeBusy;
1849 : : default:
1850 : : return ErrorUnexpected;
1851 : : }
1852 : : }
1853 : : *out_file = result;
1854 : :
1855 : : if (attr != nullptr) {
1856 : : BY_HANDLE_FILE_INFORMATION file_info;
1857 : : if (!GetFileInformationByHandle(result, &file_info)) {
1858 : : CloseHandle(result);
1859 : : return ErrorUnexpected;
1860 : : }
1861 : : windows_filetime_to_os_timestamp(&file_info.ftLastWriteTime, &attr->mtime);
1862 : : attr->inode = (((uint64_t)file_info.nFileIndexHigh) << 32) | file_info.nFileIndexLow;
1863 : : }
1864 : :
1865 : : return ErrorNone;
1866 : : #else
1867 : : for (;;) {
1868 : 5454 : int fd = open(buf_ptr(full_path), O_RDONLY|O_CLOEXEC);
1869 [ - + ]: 5454 : if (fd == -1) {
1870 [ # # # # : 0 : switch (errno) {
# # # ]
1871 : 0 : case EINTR:
1872 : 0 : continue;
1873 : 0 : case EINVAL:
1874 : 0 : zig_unreachable();
1875 : 0 : case EFAULT:
1876 : 0 : zig_unreachable();
1877 : 0 : case EACCES:
1878 : 5454 : return ErrorAccess;
1879 : 0 : case EISDIR:
1880 : 0 : return ErrorIsDir;
1881 : 0 : case ENOENT:
1882 : 0 : return ErrorFileNotFound;
1883 : 0 : default:
1884 : 0 : return ErrorFileSystem;
1885 : : }
1886 : : }
1887 : : struct stat statbuf;
1888 [ - + ]: 5454 : if (fstat(fd, &statbuf) == -1) {
1889 : 0 : close(fd);
1890 : 0 : return ErrorFileSystem;
1891 : : }
1892 [ - + ]: 5454 : if (S_ISDIR(statbuf.st_mode)) {
1893 : 0 : close(fd);
1894 : 0 : return ErrorIsDir;
1895 : : }
1896 : 5454 : *out_file = fd;
1897 : :
1898 [ + - ]: 5454 : if (attr != nullptr) {
1899 : 5454 : attr->inode = statbuf.st_ino;
1900 : : #if defined(ZIG_OS_DARWIN)
1901 : : attr->mtime.sec = statbuf.st_mtimespec.tv_sec;
1902 : : attr->mtime.nsec = statbuf.st_mtimespec.tv_nsec;
1903 : : #else
1904 : 5454 : attr->mtime.sec = statbuf.st_mtim.tv_sec;
1905 : 5454 : attr->mtime.nsec = statbuf.st_mtim.tv_nsec;
1906 : : #endif
1907 : : }
1908 : 5454 : return ErrorNone;
1909 : 0 : }
1910 : : #endif
1911 : : }
1912 : :
1913 : 148 : Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) {
1914 : : #if defined(ZIG_OS_WINDOWS)
1915 : : for (;;) {
1916 : : HANDLE result = CreateFileA(buf_ptr(full_path), GENERIC_READ | GENERIC_WRITE,
1917 : : 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
1918 : :
1919 : : if (result == INVALID_HANDLE_VALUE) {
1920 : : DWORD err = GetLastError();
1921 : : switch (err) {
1922 : : case ERROR_SHARING_VIOLATION:
1923 : : // TODO wait for the lock instead of sleeping
1924 : : Sleep(10);
1925 : : continue;
1926 : : case ERROR_ALREADY_EXISTS:
1927 : : return ErrorPathAlreadyExists;
1928 : : case ERROR_FILE_EXISTS:
1929 : : return ErrorPathAlreadyExists;
1930 : : case ERROR_FILE_NOT_FOUND:
1931 : : return ErrorFileNotFound;
1932 : : case ERROR_PATH_NOT_FOUND:
1933 : : return ErrorFileNotFound;
1934 : : case ERROR_ACCESS_DENIED:
1935 : : return ErrorAccess;
1936 : : case ERROR_PIPE_BUSY:
1937 : : return ErrorPipeBusy;
1938 : : default:
1939 : : return ErrorUnexpected;
1940 : : }
1941 : : }
1942 : : *out_file = result;
1943 : : return ErrorNone;
1944 : : }
1945 : : #else
1946 : : int fd;
1947 : : for (;;) {
1948 : 148 : fd = open(buf_ptr(full_path), O_RDWR|O_CLOEXEC|O_CREAT, 0666);
1949 [ - + ]: 148 : if (fd == -1) {
1950 [ # # # # : 0 : switch (errno) {
# # # # ]
1951 : 0 : case EINTR:
1952 : 0 : continue;
1953 : 0 : case EINVAL:
1954 : 0 : zig_unreachable();
1955 : 0 : case EFAULT:
1956 : 0 : zig_unreachable();
1957 : 0 : case EACCES:
1958 : 0 : return ErrorAccess;
1959 : 0 : case EISDIR:
1960 : 0 : return ErrorIsDir;
1961 : 0 : case ENOENT:
1962 : 0 : return ErrorFileNotFound;
1963 : 0 : case ENOTDIR:
1964 : 0 : return ErrorNotDir;
1965 : 0 : default:
1966 : 0 : return ErrorFileSystem;
1967 : : }
1968 : : }
1969 : 148 : break;
1970 : : }
1971 : : for (;;) {
1972 : : struct flock lock;
1973 : 148 : lock.l_type = F_WRLCK;
1974 : 148 : lock.l_whence = SEEK_SET;
1975 : 148 : lock.l_start = 0;
1976 : 148 : lock.l_len = 0;
1977 [ - + ]: 148 : if (fcntl(fd, F_SETLKW, &lock) == -1) {
1978 [ # # # # : 0 : switch (errno) {
# ]
1979 : 0 : case EINTR:
1980 : 0 : continue;
1981 : 0 : case EBADF:
1982 : 0 : zig_unreachable();
1983 : 0 : case EFAULT:
1984 : 0 : zig_unreachable();
1985 : 0 : case EINVAL:
1986 : 0 : zig_unreachable();
1987 : 0 : default:
1988 : 0 : close(fd);
1989 : 0 : return ErrorFileSystem;
1990 : : }
1991 : : }
1992 : 148 : break;
1993 : 0 : }
1994 : 148 : *out_file = fd;
1995 : 148 : return ErrorNone;
1996 : : #endif
1997 : : }
1998 : :
1999 : 65385 : Error os_file_read(OsFile file, void *ptr, size_t *len) {
2000 : : #if defined(ZIG_OS_WINDOWS)
2001 : : DWORD amt_read;
2002 : : if (ReadFile(file, ptr, *len, &amt_read, nullptr) == 0)
2003 : : return ErrorUnexpected;
2004 : : *len = amt_read;
2005 : : return ErrorNone;
2006 : : #else
2007 : : for (;;) {
2008 : 65385 : ssize_t rc = read(file, ptr, *len);
2009 [ - + ]: 65385 : if (rc == -1) {
2010 [ # # # # : 0 : switch (errno) {
# ]
2011 : 0 : case EINTR:
2012 : 0 : continue;
2013 : 0 : case EBADF:
2014 : 0 : zig_unreachable();
2015 : 0 : case EFAULT:
2016 : 0 : zig_unreachable();
2017 : 0 : case EISDIR:
2018 : 0 : return ErrorIsDir;
2019 : 0 : default:
2020 : 0 : return ErrorFileSystem;
2021 : : }
2022 : : }
2023 : 65385 : *len = rc;
2024 : 65385 : return ErrorNone;
2025 : 0 : }
2026 : : #endif
2027 : : }
2028 : :
2029 : 148 : Error os_file_read_all(OsFile file, Buf *contents) {
2030 : : Error err;
2031 : 148 : size_t index = 0;
2032 : : for (;;) {
2033 : 296 : size_t amt = buf_len(contents) - index;
2034 : :
2035 [ + - ]: 296 : if (amt < 4096) {
2036 : 296 : buf_resize(contents, buf_len(contents) + (4096 - amt));
2037 : 296 : amt = buf_len(contents) - index;
2038 : : }
2039 : :
2040 [ - + ]: 296 : if ((err = os_file_read(file, buf_ptr(contents) + index, &amt)))
2041 : 148 : return err;
2042 : :
2043 [ + + ]: 296 : if (amt == 0) {
2044 : 148 : buf_resize(contents, index);
2045 : 148 : return ErrorNone;
2046 : : }
2047 : :
2048 : 148 : index += amt;
2049 : 148 : }
2050 : : }
2051 : :
2052 : 74 : Error os_file_overwrite(OsFile file, Buf *contents) {
2053 : : #if defined(ZIG_OS_WINDOWS)
2054 : : if (SetFilePointer(file, 0, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
2055 : : return ErrorFileSystem;
2056 : : if (!SetEndOfFile(file))
2057 : : return ErrorFileSystem;
2058 : : DWORD bytes_written;
2059 : : if (!WriteFile(file, buf_ptr(contents), buf_len(contents), &bytes_written, nullptr))
2060 : : return ErrorFileSystem;
2061 : : return ErrorNone;
2062 : : #else
2063 [ - + ]: 74 : if (lseek(file, 0, SEEK_SET) == -1)
2064 : 0 : return ErrorUnexpectedSeekFailure;
2065 [ - + ]: 74 : if (ftruncate(file, 0) == -1)
2066 : 0 : return ErrorUnexpectedFileTruncationFailure;
2067 : : for (;;) {
2068 [ - + ]: 74 : if (write(file, buf_ptr(contents), buf_len(contents)) == -1) {
2069 [ # # # # : 0 : switch (errno) {
# # # # #
# ]
2070 : 0 : case EINTR:
2071 : 0 : continue;
2072 : 0 : case EINVAL:
2073 : 0 : zig_unreachable();
2074 : 0 : case EBADF:
2075 : 0 : zig_unreachable();
2076 : 0 : case EFAULT:
2077 : 0 : zig_unreachable();
2078 : 0 : case EDQUOT:
2079 : 0 : return ErrorDiskQuota;
2080 : 0 : case ENOSPC:
2081 : 0 : return ErrorDiskSpace;
2082 : 0 : case EFBIG:
2083 : 0 : return ErrorFileTooBig;
2084 : 0 : case EIO:
2085 : 0 : return ErrorFileSystem;
2086 : 0 : case EPERM:
2087 : 0 : return ErrorAccess;
2088 : 0 : default:
2089 : 0 : return ErrorUnexpectedWriteFailure;
2090 : : }
2091 : : }
2092 : 74 : return ErrorNone;
2093 : : }
2094 : : #endif
2095 : : }
2096 : :
2097 : 5602 : void os_file_close(OsFile *file) {
2098 : : #if defined(ZIG_OS_WINDOWS)
2099 : : CloseHandle(*file);
2100 : : *file = NULL;
2101 : : #else
2102 : 5602 : close(*file);
2103 : 5602 : *file = -1;
2104 : : #endif
2105 : 5602 : }
2106 : :
2107 : : #ifdef ZIG_OS_LINUX
2108 : : const char *possible_ld_names[] = {
2109 : : #if defined(ZIG_ARCH_X86_64)
2110 : : "ld-linux-x86-64.so.2",
2111 : : "ld-musl-x86_64.so.1",
2112 : : #elif defined(ZIG_ARCH_ARM64)
2113 : : "ld-linux-aarch64.so.1",
2114 : : "ld-musl-aarch64.so.1",
2115 : : #elif defined(ZIG_ARCH_ARM)
2116 : : "ld-linux-armhf.so.3",
2117 : : "ld-musl-armhf.so.1",
2118 : : "ld-linux.so.3",
2119 : : "ld-musl-arm.so.1",
2120 : : #endif
2121 : : NULL,
2122 : : };
2123 : : #endif
|