| ... | @@ -94,105 +94,6 @@ static clock_serv_t macos_monotonic_clock; | ... | @@ -94,105 +94,6 @@ static clock_serv_t macos_monotonic_clock; |
| 94 | extern char **environ; | 94 | extern char **environ; |
| 95 | #endif | 95 | #endif |
| 96 | | 96 | |
| 97 | #if defined(ZIG_OS_POSIX) | | |
| 98 | static void populate_termination(Termination *term, int status) { | | |
| 99 | if (WIFEXITED(status)) { | | |
| 100 | term->how = TerminationIdClean; | | |
| 101 | term->code = WEXITSTATUS(status); | | |
| 102 | } else if (WIFSIGNALED(status)) { | | |
| 103 | term->how = TerminationIdSignaled; | | |
| 104 | term->code = WTERMSIG(status); | | |
| 105 | } else if (WIFSTOPPED(status)) { | | |
| 106 | term->how = TerminationIdStopped; | | |
| 107 | term->code = WSTOPSIG(status); | | |
| 108 | } else { | | |
| 109 | term->how = TerminationIdUnknown; | | |
| 110 | term->code = status; | | |
| 111 | } | | |
| 112 | } | | |
| 113 | | | |
| 114 | static void os_spawn_process_posix(ZigList<const char *> &args, Termination *term) { | | |
| 115 | const char **argv = heap::c_allocator.allocate<const char *>(args.length + 1); | | |
| 116 | for (size_t i = 0; i < args.length; i += 1) { | | |
| 117 | argv[i] = args.at(i); | | |
| 118 | } | | |
| 119 | argv[args.length] = nullptr; | | |
| 120 | | | |
| 121 | pid_t pid; | | |
| 122 | int rc = posix_spawnp(&pid, args.at(0), nullptr, nullptr, const_cast<char *const*>(argv), environ); | | |
| 123 | if (rc != 0) { | | |
| 124 | zig_panic("unable to spawn %s: %s", args.at(0), strerror(rc)); | | |
| 125 | } | | |
| 126 | | | |
| 127 | int status; | | |
| 128 | waitpid(pid, &status, 0); | | |
| 129 | populate_termination(term, status); | | |
| 130 | } | | |
| 131 | #endif | | |
| 132 | | | |
| 133 | #if defined(ZIG_OS_WINDOWS) | | |
| 134 | | | |
| 135 | static void os_windows_create_command_line(Buf *command_line, ZigList<const char *> &args) { | | |
| 136 | buf_resize(command_line, 0); | | |
| 137 | const char *prefix = "\""; | | |
| 138 | for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) { | | |
| 139 | const char *arg = args.at(arg_i); | | |
| 140 | buf_append_str(command_line, prefix); | | |
| 141 | prefix = " \""; | | |
| 142 | size_t arg_len = strlen(arg); | | |
| 143 | for (size_t c_i = 0; c_i < arg_len; c_i += 1) { | | |
| 144 | if (arg[c_i] == '\"') { | | |
| 145 | zig_panic("TODO"); | | |
| 146 | } | | |
| 147 | buf_append_char(command_line, arg[c_i]); | | |
| 148 | } | | |
| 149 | buf_append_char(command_line, '\"'); | | |
| 150 | } | | |
| 151 | } | | |
| 152 | | | |
| 153 | static void os_spawn_process_windows(ZigList<const char *> &args, Termination *term) { | | |
| 154 | Buf command_line = BUF_INIT; | | |
| 155 | os_windows_create_command_line(&command_line, args); | | |
| 156 | | | |
| 157 | PROCESS_INFORMATION piProcInfo = {0}; | | |
| 158 | STARTUPINFOW siStartInfo = {0}; | | |
| 159 | siStartInfo.cb = sizeof(STARTUPINFOW); | | |
| 160 | | | |
| 161 | Slice<uint8_t> exe_slice = str(args.at(0)); | | |
| 162 | auto exe_utf16_slice = Slice<WCHAR>::alloc(exe_slice.len + 1); | | |
| 163 | exe_utf16_slice.ptr[utf8_to_utf16le(exe_utf16_slice.ptr, exe_slice)] = 0; | | |
| 164 | | | |
| 165 | auto command_line_utf16 = Slice<WCHAR>::alloc(buf_len(&command_line) + 1); | | |
| 166 | command_line_utf16.ptr[utf8_to_utf16le(command_line_utf16.ptr, buf_to_slice(&command_line))] = 0; | | |
| 167 | | | |
| 168 | BOOL success = CreateProcessW(exe_utf16_slice.ptr, command_line_utf16.ptr, nullptr, nullptr, TRUE, CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr, | | |
| 169 | &siStartInfo, &piProcInfo); | | |
| 170 | | | |
| 171 | if (!success) { | | |
| 172 | zig_panic("CreateProcess failed. exe: %s command_line: %s", args.at(0), buf_ptr(&command_line)); | | |
| 173 | } | | |
| 174 | | | |
| 175 | WaitForSingleObject(piProcInfo.hProcess, INFINITE); | | |
| 176 | | | |
| 177 | DWORD exit_code; | | |
| 178 | if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) { | | |
| 179 | zig_panic("GetExitCodeProcess failed"); | | |
| 180 | } | | |
| 181 | term->how = TerminationIdClean; | | |
| 182 | term->code = exit_code; | | |
| 183 | } | | |
| 184 | #endif | | |
| 185 | | | |
| 186 | void os_spawn_process(ZigList<const char *> &args, Termination *term) { | | |
| 187 | #if defined(ZIG_OS_WINDOWS) | | |
| 188 | os_spawn_process_windows(args, term); | | |
| 189 | #elif defined(ZIG_OS_POSIX) | | |
| 190 | os_spawn_process_posix(args, term); | | |
| 191 | #else | | |
| 192 | #error "missing os_spawn_process implementation" | | |
| 193 | #endif | | |
| 194 | } | | |
| 195 | | | |
| 196 | void os_path_dirname(Buf *full_path, Buf *out_dirname) { | 97 | void os_path_dirname(Buf *full_path, Buf *out_dirname) { |
| 197 | return os_path_split(full_path, out_dirname, nullptr); | 98 | return os_path_split(full_path, out_dirname, nullptr); |
| 198 | } | 99 | } |
| ... | @@ -280,71 +181,27 @@ void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) { | ... | @@ -280,71 +181,27 @@ void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) { |
| 280 | buf_append_buf(out_full_path, basename); | 181 | buf_append_buf(out_full_path, basename); |
| 281 | } | 182 | } |
| 282 | | 183 | |
| 283 | Error os_path_real(Buf *rel_path, Buf *out_abs_path) { | | |
| 284 | #if defined(ZIG_OS_WINDOWS) | | |
| 285 | PathSpace rel_path_space = slice_to_prefixed_file_w(buf_to_slice(rel_path)); | | |
| 286 | PathSpace out_abs_path_space; | | |
| 287 | | | |
| 288 | if (_wfullpath(&out_abs_path_space.data.items[0], &rel_path_space.data.items[0], PATH_MAX_WIDE) == nullptr) { | | |
| 289 | zig_panic("_wfullpath failed"); | | |
| 290 | } | | |
| 291 | utf16le_ptr_to_utf8(out_abs_path, &out_abs_path_space.data.items[0]); | | |
| 292 | return ErrorNone; | | |
| 293 | #elif defined(ZIG_OS_POSIX) | | |
| 294 | buf_resize(out_abs_path, PATH_MAX + 1); | | |
| 295 | char *result = realpath(buf_ptr(rel_path), buf_ptr(out_abs_path)); | | |
| 296 | if (!result) { | | |
| 297 | int err = errno; | | |
| 298 | if (err == EACCES) { | | |
| 299 | return ErrorAccess; | | |
| 300 | } else if (err == ENOENT) { | | |
| 301 | return ErrorFileNotFound; | | |
| 302 | } else if (err == ENOMEM) { | | |
| 303 | return ErrorNoMem; | | |
| 304 | } else { | | |
| 305 | return ErrorFileSystem; | | |
| 306 | } | | |
| 307 | } | | |
| 308 | buf_resize(out_abs_path, strlen(buf_ptr(out_abs_path))); | | |
| 309 | return ErrorNone; | | |
| 310 | #else | | |
| 311 | #error "missing os_path_real implementation" | | |
| 312 | #endif | | |
| 313 | } | | |
| 314 | | | |
| 315 | #if defined(ZIG_OS_WINDOWS) | | |
| 316 | // Ported from std/os/path.zig | | |
| 317 | static bool isAbsoluteWindows(Slice<uint8_t> path) { | | |
| 318 | if (path.ptr[0] == '/') | | |
| 319 | return true; | | |
| 320 | | | |
| 321 | if (path.ptr[0] == '\\') { | | |
| 322 | return true; | | |
| 323 | } | | |
| 324 | if (path.len < 3) { | | |
| 325 | return false; | | |
| 326 | } | | |
| 327 | if (path.ptr[1] == ':') { | | |
| 328 | if (path.ptr[2] == '/') | | |
| 329 | return true; | | |
| 330 | if (path.ptr[2] == '\\') | | |
| 331 | return true; | | |
| 332 | } | | |
| 333 | return false; | | |
| 334 | } | | |
| 335 | #endif | | |
| 336 | | | |
| 337 | bool os_path_is_absolute(Buf *path) { | | |
| 338 | #if defined(ZIG_OS_WINDOWS) | | |
| 339 | return isAbsoluteWindows(buf_to_slice(path)); | | |
| 340 | #elif defined(ZIG_OS_POSIX) | | |
| 341 | return buf_ptr(path)[0] == '/'; | | |
| 342 | #else | | |
| 343 | #error "missing os_path_is_absolute implementation" | | |
| 344 | #endif | | |
| 345 | } | | |
| 346 | | 184 | |
| 347 | #if defined(ZIG_OS_WINDOWS) | 185 | #if defined(ZIG_OS_WINDOWS)		 |
| | 186 | // Ported from std/os/path.zig		 |
| | 187 | static bool isAbsoluteWindows(Slice<uint8_t> path) {		 |
| | 188 | if (path.ptr[0] == '/')		 |
| | 189 | return true;		 |
| | 190 | |
| | 191 | if (path.ptr[0] == '\\') {		 |
| | 192 | return true;		 |
| | 193 | }		 |
| | 194 | if (path.len < 3) {		 |
| | 195 | return false;		 |
| | 196 | }		 |
| | 197 | if (path.ptr[1] == ':') {		 |
| | 198 | if (path.ptr[2] == '/')		 |
| | 199 | return true;		 |
| | 200 | if (path.ptr[2] == '\\')		 |
| | 201 | return true;		 |
| | 202 | }		 |
| | 203 | return false;		 |
| | 204 | }		 |
| 348 | | 205 | |
| 349 | enum WindowsPathKind { | 206 | enum WindowsPathKind { |
| 350 | WindowsPathKindNone, | 207 | WindowsPathKindNone, |
| ... | @@ -687,7 +544,7 @@ static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) { | ... | @@ -687,7 +544,7 @@ static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) { |
| 687 | size_t max_size = 0; | 544 | size_t max_size = 0; |
| 688 | for (size_t i = 0; i < paths_len; i += 1) { | 545 | for (size_t i = 0; i < paths_len; i += 1) { |
| 689 | Buf *p = paths_ptr[i]; | 546 | Buf *p = paths_ptr[i]; |
| 690 | if (os_path_is_absolute(p)) { | 547 | if (buf_ptr(p)[0] == '/') { |
| 691 | first_index = i; | 548 | first_index = i; |
| 692 | have_abs = true; | 549 | have_abs = true; |
| 693 | max_size = 0; | 550 | max_size = 0; |
| ... | @@ -786,256 +643,6 @@ Error os_fetch_file(FILE *f, Buf *out_buf) { | ... | @@ -786,256 +643,6 @@ Error os_fetch_file(FILE *f, Buf *out_buf) { |
| 786 | zig_unreachable(); | 643 | zig_unreachable(); |
| 787 | } | 644 | } |
| 788 | | 645 | |
| 789 | Error os_file_exists(Buf *full_path, bool *result) { | | |
| 790 | #if defined(ZIG_OS_WINDOWS) | | |
| 791 | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); | | |
| 792 | *result = GetFileAttributesW(&path_space.data.items[0]) != INVALID_FILE_ATTRIBUTES; | | |
| 793 | return ErrorNone; | | |
| 794 | #else | | |
| 795 | *result = access(buf_ptr(full_path), F_OK) != -1; | | |
| 796 | return ErrorNone; | | |
| 797 | #endif | | |
| 798 | } | | |
| 799 | | | |
| 800 | #if defined(ZIG_OS_POSIX) | | |
| 801 | static Error os_exec_process_posix(ZigList<const char *> &args, | | |
| 802 | Termination *term, Buf *out_stderr, Buf *out_stdout) | | |
| 803 | { | | |
| 804 | int stdin_pipe[2]; | | |
| 805 | int stdout_pipe[2]; | | |
| 806 | int stderr_pipe[2]; | | |
| 807 | int err_pipe[2]; | | |
| 808 | | | |
| 809 | int err; | | |
| 810 | if ((err = pipe(stdin_pipe))) | | |
| 811 | zig_panic("pipe failed"); | | |
| 812 | if ((err = pipe(stdout_pipe))) | | |
| 813 | zig_panic("pipe failed"); | | |
| 814 | if ((err = pipe(stderr_pipe))) | | |
| 815 | zig_panic("pipe failed"); | | |
| 816 | if ((err = pipe(err_pipe))) | | |
| 817 | zig_panic("pipe failed"); | | |
| 818 | | | |
| 819 | pid_t pid = fork(); | | |
| 820 | if (pid == -1) | | |
| 821 | zig_panic("fork failed: %s", strerror(errno)); | | |
| 822 | if (pid == 0) { | | |
| 823 | // child | | |
| 824 | if (dup2(stdin_pipe[0], STDIN_FILENO) == -1) | | |
| 825 | zig_panic("dup2 failed"); | | |
| 826 | | | |
| 827 | if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1) | | |
| 828 | zig_panic("dup2 failed"); | | |
| 829 | | | |
| 830 | if (dup2(stderr_pipe[1], STDERR_FILENO) == -1) | | |
| 831 | zig_panic("dup2 failed"); | | |
| 832 | | | |
| 833 | const char **argv = heap::c_allocator.allocate<const char *>(args.length + 1); | | |
| 834 | argv[args.length] = nullptr; | | |
| 835 | for (size_t i = 0; i < args.length; i += 1) { | | |
| 836 | argv[i] = args.at(i); | | |
| 837 | } | | |
| 838 | execvp(argv[0], const_cast<char * const *>(argv)); | | |
| 839 | Error report_err = ErrorUnexpected; | | |
| 840 | if (errno == ENOENT) { | | |
| 841 | report_err = ErrorFileNotFound; | | |
| 842 | } | | |
| 843 | if (write(err_pipe[1], &report_err, sizeof(Error)) == -1) { | | |
| 844 | zig_panic("write failed"); | | |
| 845 | } | | |
| 846 | exit(1); | | |
| 847 | } else { | | |
| 848 | // parent | | |
| 849 | close(stdin_pipe[0]); | | |
| 850 | close(stdin_pipe[1]); | | |
| 851 | close(stdout_pipe[1]); | | |
| 852 | close(stderr_pipe[1]); | | |
| 853 | | | |
| 854 | int status; | | |
| 855 | waitpid(pid, &status, 0); | | |
| 856 | populate_termination(term, status); | | |
| 857 | | | |
| 858 | FILE *stdout_f = fdopen(stdout_pipe[0], "rb"); | | |
| 859 | FILE *stderr_f = fdopen(stderr_pipe[0], "rb"); | | |
| 860 | Error err1 = os_fetch_file(stdout_f, out_stdout); | | |
| 861 | Error err2 = os_fetch_file(stderr_f, out_stderr); | | |
| 862 | | | |
| 863 | fclose(stdout_f); | | |
| 864 | fclose(stderr_f); | | |
| 865 | | | |
| 866 | if (err1) return err1; | | |
| 867 | if (err2) return err2; | | |
| 868 | | | |
| 869 | Error child_err = ErrorNone; | | |
| 870 | if (write(err_pipe[1], &child_err, sizeof(Error)) == -1) { | | |
| 871 | zig_panic("write failed"); | | |
| 872 | } | | |
| 873 | close(err_pipe[1]); | | |
| 874 | if (read(err_pipe[0], &child_err, sizeof(Error)) == -1) { | | |
| 875 | zig_panic("write failed"); | | |
| 876 | } | | |
| 877 | close(err_pipe[0]); | | |
| 878 | return child_err; | | |
| 879 | } | | |
| 880 | } | | |
| 881 | #endif | | |
| 882 | | | |
| 883 | #if defined(ZIG_OS_WINDOWS) | | |
| 884 | | | |
| 885 | //static void win32_panic(const char *str) { | | |
| 886 | // DWORD err = GetLastError(); | | |
| 887 | // LPSTR messageBuffer = nullptr; | | |
| 888 | // FormatMessageA( | | |
| 889 | // FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | | |
| 890 | // NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); | | |
| 891 | // zig_panic(str, messageBuffer); | | |
| 892 | // LocalFree(messageBuffer); | | |
| 893 | //} | | |
| 894 | | | |
| 895 | static Error os_exec_process_windows(ZigList<const char *> &args, | | |
| 896 | Termination *term, Buf *out_stderr, Buf *out_stdout) | | |
| 897 | { | | |
| 898 | Buf command_line = BUF_INIT; | | |
| 899 | os_windows_create_command_line(&command_line, args); | | |
| 900 | | | |
| 901 | HANDLE g_hChildStd_IN_Rd = NULL; | | |
| 902 | HANDLE g_hChildStd_IN_Wr = NULL; | | |
| 903 | HANDLE g_hChildStd_OUT_Rd = NULL; | | |
| 904 | HANDLE g_hChildStd_OUT_Wr = NULL; | | |
| 905 | HANDLE g_hChildStd_ERR_Rd = NULL; | | |
| 906 | HANDLE g_hChildStd_ERR_Wr = NULL; | | |
| 907 | | | |
| 908 | SECURITY_ATTRIBUTES saAttr; | | |
| 909 | saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); | | |
| 910 | saAttr.bInheritHandle = TRUE; | | |
| 911 | saAttr.lpSecurityDescriptor = NULL; | | |
| 912 | | | |
| 913 | if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) { | | |
| 914 | zig_panic("StdoutRd CreatePipe"); | | |
| 915 | } | | |
| 916 | | | |
| 917 | if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { | | |
| 918 | zig_panic("Stdout SetHandleInformation"); | | |
| 919 | } | | |
| 920 | | | |
| 921 | if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) { | | |
| 922 | zig_panic("stderr CreatePipe"); | | |
| 923 | } | | |
| 924 | | | |
| 925 | if (!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) { | | |
| 926 | zig_panic("stderr SetHandleInformation"); | | |
| 927 | } | | |
| 928 | | | |
| 929 | if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) { | | |
| 930 | zig_panic("Stdin CreatePipe"); | | |
| 931 | } | | |
| 932 | | | |
| 933 | if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) { | | |
| 934 | zig_panic("Stdin SetHandleInformation"); | | |
| 935 | } | | |
| 936 | | | |
| 937 | | | |
| 938 | PROCESS_INFORMATION piProcInfo = {0}; | | |
| 939 | STARTUPINFO siStartInfo = {0}; | | |
| 940 | siStartInfo.cb = sizeof(STARTUPINFO); | | |
| 941 | siStartInfo.hStdError = g_hChildStd_ERR_Wr; | | |
| 942 | siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; | | |
| 943 | siStartInfo.hStdInput = g_hChildStd_IN_Rd; | | |
| 944 | siStartInfo.dwFlags |= STARTF_USESTDHANDLES; | | |
| 945 | | | |
| 946 | const char *exe = args.at(0); | | |
| 947 | BOOL success = CreateProcess(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr, | | |
| 948 | &siStartInfo, &piProcInfo); | | |
| 949 | | | |
| 950 | if (!success) { | | |
| 951 | if (GetLastError() == ERROR_FILE_NOT_FOUND) { | | |
| 952 | CloseHandle(piProcInfo.hProcess); | | |
| 953 | CloseHandle(piProcInfo.hThread); | | |
| 954 | return ErrorFileNotFound; | | |
| 955 | } | | |
| 956 | zig_panic("CreateProcess failed. exe: %s command_line: %s", exe, buf_ptr(&command_line)); | | |
| 957 | } | | |
| 958 | | | |
| 959 | if (!CloseHandle(g_hChildStd_IN_Wr)) { | | |
| 960 | zig_panic("stdinwr closehandle"); | | |
| 961 | } | | |
| 962 | | | |
| 963 | CloseHandle(g_hChildStd_IN_Rd); | | |
| 964 | CloseHandle(g_hChildStd_ERR_Wr); | | |
| 965 | CloseHandle(g_hChildStd_OUT_Wr); | | |
| 966 | | | |
| 967 | static const size_t BUF_SIZE = 4 * 1024; | | |
| 968 | { | | |
| 969 | DWORD dwRead; | | |
| 970 | char chBuf[BUF_SIZE]; | | |
| 971 | | | |
| 972 | buf_resize(out_stdout, 0); | | |
| 973 | for (;;) { | | |
| 974 | success = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUF_SIZE, &dwRead, NULL); | | |
| 975 | if (!success || dwRead == 0) break; | | |
| 976 | | | |
| 977 | buf_append_mem(out_stdout, chBuf, dwRead); | | |
| 978 | } | | |
| 979 | CloseHandle(g_hChildStd_OUT_Rd); | | |
| 980 | } | | |
| 981 | { | | |
| 982 | DWORD dwRead; | | |
| 983 | char chBuf[BUF_SIZE]; | | |
| 984 | | | |
| 985 | buf_resize(out_stderr, 0); | | |
| 986 | for (;;) { | | |
| 987 | success = ReadFile( g_hChildStd_ERR_Rd, chBuf, BUF_SIZE, &dwRead, NULL); | | |
| 988 | if (!success || dwRead == 0) break; | | |
| 989 | | | |
| 990 | buf_append_mem(out_stderr, chBuf, dwRead); | | |
| 991 | } | | |
| 992 | CloseHandle(g_hChildStd_ERR_Rd); | | |
| 993 | } | | |
| 994 | | | |
| 995 | WaitForSingleObject(piProcInfo.hProcess, INFINITE); | | |
| 996 | | | |
| 997 | DWORD exit_code; | | |
| 998 | if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) { | | |
| 999 | zig_panic("GetExitCodeProcess failed"); | | |
| 1000 | } | | |
| 1001 | term->how = TerminationIdClean; | | |
| 1002 | term->code = exit_code; | | |
| 1003 | | | |
| 1004 | CloseHandle(piProcInfo.hProcess); | | |
| 1005 | CloseHandle(piProcInfo.hThread); | | |
| 1006 | | | |
| 1007 | return ErrorNone; | | |
| 1008 | } | | |
| 1009 | #endif | | |
| 1010 | | | |
| 1011 | Error os_execv(const char *exe, const char **argv) { | | |
| 1012 | #if defined(ZIG_OS_WINDOWS) | | |
| 1013 | return ErrorUnsupportedOperatingSystem; | | |
| 1014 | #else | | |
| 1015 | execv(exe, (char *const *)argv); | | |
| 1016 | switch (errno) { | | |
| 1017 | case ENOMEM: | | |
| 1018 | return ErrorSystemResources; | | |
| 1019 | case EIO: | | |
| 1020 | return ErrorFileSystem; | | |
| 1021 | default: | | |
| 1022 | return ErrorUnexpected; | | |
| 1023 | } | | |
| 1024 | #endif | | |
| 1025 | } | | |
| 1026 | | | |
| 1027 | Error os_exec_process(ZigList<const char *> &args, | | |
| 1028 | Termination *term, Buf *out_stderr, Buf *out_stdout) | | |
| 1029 | { | | |
| 1030 | #if defined(ZIG_OS_WINDOWS) | | |
| 1031 | return os_exec_process_windows(args, term, out_stderr, out_stdout); | | |
| 1032 | #elif defined(ZIG_OS_POSIX) | | |
| 1033 | return os_exec_process_posix(args, term, out_stderr, out_stdout); | | |
| 1034 | #else | | |
| 1035 | #error "missing os_exec_process implementation" | | |
| 1036 | #endif | | |
| 1037 | } | | |
| 1038 | | | |
| 1039 | Error os_write_file(Buf *full_path, Buf *contents) { | 646 | Error os_write_file(Buf *full_path, Buf *contents) { |
| 1040 | #if defined(ZIG_OS_WINDOWS) | 647 | #if defined(ZIG_OS_WINDOWS) |
| 1041 | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); | 648 | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); |
| ... | @@ -1074,35 +681,6 @@ static Error copy_open_files(FILE *src_f, FILE *dest_f) { | ... | @@ -1074,35 +681,6 @@ static Error copy_open_files(FILE *src_f, FILE *dest_f) { |
| 1074 | } | 681 | } |
| 1075 | } | 682 | } |
| 1076 | | 683 | |
| 1077 | Error os_dump_file(Buf *src_path, FILE *dest_file) { | | |
| 1078 | Error err; | | |
| 1079 | | | |
| 1080 | #if defined(ZIG_OS_WINDOWS) | | |
| 1081 | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); | | |
| 1082 | FILE *src_f = _wfopen(&path_space.data.items[0], L"rb"); | | |
| 1083 | #else | | |
| 1084 | FILE *src_f = fopen(buf_ptr(src_path), "rb"); | | |
| 1085 | #endif | | |
| 1086 | if (!src_f) { | | |
| 1087 | int err = errno; | | |
| 1088 | if (err == ENOENT) { | | |
| 1089 | return ErrorFileNotFound; | | |
| 1090 | } else if (err == EACCES || err == EPERM) { | | |
| 1091 | return ErrorAccess; | | |
| 1092 | } else { | | |
| 1093 | return ErrorFileSystem; | | |
| 1094 | } | | |
| 1095 | } | | |
| 1096 | copy_open_files(src_f, dest_file); | | |
| 1097 | if ((err = copy_open_files(src_f, dest_file))) { | | |
| 1098 | fclose(src_f); | | |
| 1099 | return err; | | |
| 1100 | } | | |
| 1101 | | | |
| 1102 | fclose(src_f); | | |
| 1103 | return ErrorNone; | | |
| 1104 | } | | |
| 1105 | | | |
| 1106 | #if defined(ZIG_OS_WINDOWS) | 684 | #if defined(ZIG_OS_WINDOWS) |
| 1107 | static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) { | 685 | static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) { |
| 1108 | mtime->sec = (((ULONGLONG) ft->dwHighDateTime) << 32) + ft->dwLowDateTime; | 686 | mtime->sec = (((ULONGLONG) ft->dwHighDateTime) << 32) + ft->dwLowDateTime; |
| ... | @@ -1116,88 +694,6 @@ static FILETIME windows_os_timestamp_to_filetime(OsTimeStamp mtime) { | ... | @@ -1116,88 +694,6 @@ static FILETIME windows_os_timestamp_to_filetime(OsTimeStamp mtime) { |
| 1116 | } | 694 | } |
| 1117 | #endif | 695 | #endif |
| 1118 | | 696 | |
| 1119 | static Error set_file_times(OsFile file, OsTimeStamp ts) { | | |
| 1120 | #if defined(ZIG_OS_WINDOWS) | | |
| 1121 | FILETIME ft = windows_os_timestamp_to_filetime(ts); | | |
| 1122 | if (SetFileTime(file, nullptr, &ft, &ft) == 0) { | | |
| 1123 | return ErrorUnexpected; | | |
| 1124 | } | | |
| 1125 | return ErrorNone; | | |
| 1126 | #else | | |
| 1127 | struct timespec times[2] = { | | |
| 1128 | { (time_t)ts.sec, (long)ts.nsec }, | | |
| 1129 | { (time_t)ts.sec, (long)ts.nsec }, | | |
| 1130 | }; | | |
| 1131 | if (futimens(file, times) == -1) { | | |
| 1132 | switch (errno) { | | |
| 1133 | case EBADF: | | |
| 1134 | zig_panic("futimens EBADF"); | | |
| 1135 | default: | | |
| 1136 | return ErrorUnexpected; | | |
| 1137 | } | | |
| 1138 | } | | |
| 1139 | return ErrorNone; | | |
| 1140 | #endif | | |
| 1141 | } | | |
| 1142 | | | |
| 1143 | Error os_update_file(Buf *src_path, Buf *dst_path) { | | |
| 1144 | Error err; | | |
| 1145 | | | |
| 1146 | OsFile src_file; | | |
| 1147 | OsFileAttr src_attr; | | |
| 1148 | if ((err = os_file_open_r(src_path, &src_file, &src_attr))) { | | |
| 1149 | return err; | | |
| 1150 | } | | |
| 1151 | | | |
| 1152 | OsFile dst_file; | | |
| 1153 | OsFileAttr dst_attr; | | |
| 1154 | if ((err = os_file_open_w(dst_path, &dst_file, &dst_attr, src_attr.mode))) { | | |
| 1155 | os_file_close(&src_file); | | |
| 1156 | return err; | | |
| 1157 | } | | |
| 1158 | | | |
| 1159 | if (src_attr.size == dst_attr.size && | | |
| 1160 | src_attr.mode == dst_attr.mode && | | |
| 1161 | src_attr.mtime.sec == dst_attr.mtime.sec && | | |
| 1162 | src_attr.mtime.nsec == dst_attr.mtime.nsec) | | |
| 1163 | { | | |
| 1164 | os_file_close(&src_file); | | |
| 1165 | os_file_close(&dst_file); | | |
| 1166 | return ErrorNone; | | |
| 1167 | } | | |
| 1168 | #if defined(ZIG_OS_WINDOWS) | | |
| 1169 | if (SetEndOfFile(dst_file) == 0) { | | |
| 1170 | return ErrorUnexpected; | | |
| 1171 | } | | |
| 1172 | #else | | |
| 1173 | if (ftruncate(dst_file, 0) == -1) { | | |
| 1174 | return ErrorUnexpected; | | |
| 1175 | } | | |
| 1176 | #endif | | |
| 1177 | #if defined(ZIG_OS_WINDOWS) | | |
| 1178 | FILE *src_libc_file = _fdopen(_open_osfhandle((intptr_t)src_file, _O_RDONLY), "rb"); | | |
| 1179 | FILE *dst_libc_file = _fdopen(_open_osfhandle((intptr_t)dst_file, 0), "wb"); | | |
| 1180 | #else | | |
| 1181 | FILE *src_libc_file = fdopen(src_file, "rb"); | | |
| 1182 | FILE *dst_libc_file = fdopen(dst_file, "wb"); | | |
| 1183 | #endif | | |
| 1184 | assert(src_libc_file); | | |
| 1185 | assert(dst_libc_file); | | |
| 1186 | | | |
| 1187 | if ((err = copy_open_files(src_libc_file, dst_libc_file))) { | | |
| 1188 | fclose(src_libc_file); | | |
| 1189 | fclose(dst_libc_file); | | |
| 1190 | return err; | | |
| 1191 | } | | |
| 1192 | if (fflush(dst_libc_file) == -1) { | | |
| 1193 | return ErrorUnexpected; | | |
| 1194 | } | | |
| 1195 | err = set_file_times(dst_file, src_attr.mtime); | | |
| 1196 | fclose(src_libc_file); | | |
| 1197 | fclose(dst_libc_file); | | |
| 1198 | return err; | | |
| 1199 | } | | |
| 1200 | | | |
| 1201 | Error os_copy_file(Buf *src_path, Buf *dest_path) { | 697 | Error os_copy_file(Buf *src_path, Buf *dest_path) { |
| 1202 | #if defined(ZIG_OS_WINDOWS) | 698 | #if defined(ZIG_OS_WINDOWS) |
| 1203 | PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); | 699 | PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); |
| ... | @@ -1358,14 +854,6 @@ bool os_stderr_tty(void) { | ... | @@ -1358,14 +854,6 @@ bool os_stderr_tty(void) { |
| 1358 | #endif | 854 | #endif |
| 1359 | } | 855 | } |
| 1360 | | 856 | |
| 1361 | Error os_delete_file(Buf *path) { | | |
| 1362 | if (remove(buf_ptr(path))) { | | |
| 1363 | return ErrorFileSystem; | | |
| 1364 | } else { | | |
| 1365 | return ErrorNone; | | |
| 1366 | } | | |
| 1367 | } | | |
| 1368 | | | |
| 1369 | Error os_rename(Buf *src_path, Buf *dest_path) { | 857 | Error os_rename(Buf *src_path, Buf *dest_path) { |
| 1370 | if (buf_eql_buf(src_path, dest_path)) { | 858 | if (buf_eql_buf(src_path, dest_path)) { |
| 1371 | return ErrorNone; | 859 | return ErrorNone; |
| ... | @@ -1384,30 +872,6 @@ Error os_rename(Buf *src_path, Buf *dest_path) { | ... | @@ -1384,30 +872,6 @@ Error os_rename(Buf *src_path, Buf *dest_path) { |
| 1384 | return ErrorNone; | 872 | return ErrorNone; |
| 1385 | } | 873 | } |
| 1386 | | 874 | |
| 1387 | OsTimeStamp os_timestamp_calendar(void) { | | |
| 1388 | OsTimeStamp result; | | |
| 1389 | #if defined(ZIG_OS_WINDOWS) | | |
| 1390 | FILETIME ft; | | |
| 1391 | GetSystemTimeAsFileTime(&ft); | | |
| 1392 | windows_filetime_to_os_timestamp(&ft, &result); | | |
| 1393 | #elif defined(__MACH__) | | |
| 1394 | mach_timespec_t mts; | | |
| 1395 | | | |
| 1396 | kern_return_t err = clock_get_time(macos_calendar_clock, &mts); | | |
| 1397 | assert(!err); | | |
| 1398 | | | |
| 1399 | result.sec = mts.tv_sec; | | |
| 1400 | result.nsec = mts.tv_nsec; | | |
| 1401 | #else | | |
| 1402 | struct timespec tms; | | |
| 1403 | clock_gettime(CLOCK_REALTIME, &tms); | | |
| 1404 | | | |
| 1405 | result.sec = tms.tv_sec; | | |
| 1406 | result.nsec = tms.tv_nsec; | | |
| 1407 | #endif | | |
| 1408 | return result; | | |
| 1409 | } | | |
| 1410 | | | |
| 1411 | OsTimeStamp os_timestamp_monotonic(void) { | 875 | OsTimeStamp os_timestamp_monotonic(void) { |
| 1412 | OsTimeStamp result; | 876 | OsTimeStamp result; |
| 1413 | #if defined(ZIG_OS_WINDOWS) | 877 | #if defined(ZIG_OS_WINDOWS) |
| ... | @@ -1501,49 +965,8 @@ Error os_make_dir(Buf *path) { | ... | @@ -1501,49 +965,8 @@ Error os_make_dir(Buf *path) { |
| 1501 | #endif | 965 | #endif |
| 1502 | } | 966 | } |
| 1503 | | 967 | |
| 1504 | static void init_rand() { | | |
| 1505 | #if defined(ZIG_OS_WINDOWS) | | |
| 1506 | char bytes[sizeof(unsigned)]; | | |
| 1507 | unsigned seed; | | |
| 1508 | RtlGenRandom(bytes, sizeof(unsigned)); | | |
| 1509 | memcpy(&seed, bytes, sizeof(unsigned)); | | |
| 1510 | srand(seed); | | |
| 1511 | #elif defined(ZIG_OS_LINUX) | | |
| 1512 | unsigned char *ptr_random = (unsigned char*)getauxval(AT_RANDOM); | | |
| 1513 | unsigned seed; | | |
| 1514 | memcpy(&seed, ptr_random, sizeof(seed)); | | |
| 1515 | srand(seed); | | |
| 1516 | #elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) | | |
| 1517 | unsigned seed; | | |
| 1518 | size_t len = sizeof(seed); | | |
| 1519 | int mib[2] = { CTL_KERN, KERN_ARND }; | | |
| 1520 | if (sysctl(mib, 2, &seed, &len, NULL, 0) != 0) { | | |
| 1521 | zig_panic("unable to query random data from sysctl"); | | |
| 1522 | } | | |
| 1523 | srand(seed); | | |
| 1524 | #else | | |
| 1525 | int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC); | | |
| 1526 | if (fd == -1) { | | |
| 1527 | zig_panic("unable to open /dev/urandom"); | | |
| 1528 | } | | |
| 1529 | char bytes[sizeof(unsigned)]; | | |
| 1530 | ssize_t amt_read; | | |
| 1531 | while ((amt_read = read(fd, bytes, sizeof(unsigned))) == -1) { | | |
| 1532 | if (errno == EINTR) continue; | | |
| 1533 | zig_panic("unable to read /dev/urandom"); | | |
| 1534 | } | | |
| 1535 | if (amt_read != sizeof(unsigned)) { | | |
| 1536 | zig_panic("unable to read enough bytes from /dev/urandom"); | | |
| 1537 | } | | |
| 1538 | close(fd); | | |
| 1539 | unsigned seed; | | |
| 1540 | memcpy(&seed, bytes, sizeof(unsigned)); | | |
| 1541 | srand(seed); | | |
| 1542 | #endif | | |
| 1543 | } | | |
| 1544 | | 968 | |
| 1545 | int os_init(void) { | 969 | int os_init(void) { |
| 1546 | init_rand(); | | |
| 1547 | #if defined(ZIG_OS_WINDOWS) | 970 | #if defined(ZIG_OS_WINDOWS) |
| 1548 | _setmode(fileno(stdout), _O_BINARY); | 971 | _setmode(fileno(stdout), _O_BINARY); |
| 1549 | _setmode(fileno(stderr), _O_BINARY); | 972 | _setmode(fileno(stderr), _O_BINARY); |
| ... | @@ -1580,71 +1003,6 @@ int os_init(void) { | ... | @@ -1580,71 +1003,6 @@ int os_init(void) { |
| 1580 | return 0; | 1003 | return 0; |
| 1581 | } | 1004 | } |
| 1582 | | 1005 | |
| 1583 | Error os_self_exe_path(Buf *out_path) { | | |
| 1584 | #if defined(ZIG_OS_WINDOWS) | | |
| 1585 | PathSpace path_space; | | |
| 1586 | DWORD copied_amt = GetModuleFileNameW(nullptr, &path_space.data.items[0], PATH_MAX_WIDE); | | |
| 1587 | if (copied_amt <= 0) { | | |
| 1588 | return ErrorFileNotFound; | | |
| 1589 | } | | |
| 1590 | utf16le_ptr_to_utf8(out_path, &path_space.data.items[0]); | | |
| 1591 | return ErrorNone; | | |
| 1592 | | | |
| 1593 | #elif defined(ZIG_OS_DARWIN) | | |
| 1594 | // How long is the executable's path? | | |
| 1595 | uint32_t u32_len = 0; | | |
| 1596 | int ret1 = _NSGetExecutablePath(nullptr, &u32_len); | | |
| 1597 | assert(ret1 != 0); | | |
| 1598 | | | |
| 1599 | Buf *tmp = buf_alloc_fixed(u32_len); | | |
| 1600 | | | |
| 1601 | // Fill the executable path. | | |
| 1602 | int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len); | | |
| 1603 | assert(ret2 == 0); | | |
| 1604 | | | |
| 1605 | // According to libuv project, PATH_MAX*2 works around a libc bug where | | |
| 1606 | // the resolved path is sometimes bigger than PATH_MAX. | | |
| 1607 | buf_resize(out_path, PATH_MAX*2); | | |
| 1608 | char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path)); | | |
| 1609 | if (!real_path) { | | |
| 1610 | buf_init_from_buf(out_path, tmp); | | |
| 1611 | return ErrorNone; | | |
| 1612 | } | | |
| 1613 | | | |
| 1614 | // Resize out_path for the correct length. | | |
| 1615 | buf_resize(out_path, strlen(buf_ptr(out_path))); | | |
| 1616 | | | |
| 1617 | return ErrorNone; | | |
| 1618 | #elif defined(ZIG_OS_LINUX) | | |
| 1619 | buf_resize(out_path, PATH_MAX); | | |
| 1620 | ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path)); | | |
| 1621 | if (amt == -1) { | | |
| 1622 | return ErrorUnexpected; | | |
| 1623 | } | | |
| 1624 | buf_resize(out_path, amt); | | |
| 1625 | return ErrorNone; | | |
| 1626 | #elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_DRAGONFLY) | | |
| 1627 | buf_resize(out_path, PATH_MAX); | | |
| 1628 | int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; | | |
| 1629 | size_t cb = PATH_MAX; | | |
| 1630 | if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) { | | |
| 1631 | return ErrorUnexpected; | | |
| 1632 | } | | |
| 1633 | buf_resize(out_path, cb - 1); | | |
| 1634 | return ErrorNone; | | |
| 1635 | #elif defined(ZIG_OS_NETBSD) | | |
| 1636 | buf_resize(out_path, PATH_MAX); | | |
| 1637 | int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; | | |
| 1638 | size_t cb = PATH_MAX; | | |
| 1639 | if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) { | | |
| 1640 | return ErrorUnexpected; | | |
| 1641 | } | | |
| 1642 | buf_resize(out_path, cb - 1); | | |
| 1643 | return ErrorNone; | | |
| 1644 | #endif | | |
| 1645 | return ErrorFileNotFound; | | |
| 1646 | } | | |
| 1647 | | | |
| 1648 | #define VT_RED "\x1b[31;1m" | 1006 | #define VT_RED "\x1b[31;1m" |
| 1649 | #define VT_GREEN "\x1b[32;1m" | 1007 | #define VT_GREEN "\x1b[32;1m" |
| 1650 | #define VT_CYAN "\x1b[36;1m" | 1008 | #define VT_CYAN "\x1b[36;1m" |
| ... | @@ -1954,392 +1312,3 @@ PathSpace slice_to_prefixed_file_w(Slice<uint8_t> path) { | ... | @@ -1954,392 +1312,3 @@ PathSpace slice_to_prefixed_file_w(Slice<uint8_t> path) { |
| 1954 | return path_space; | 1312 | return path_space; |
| 1955 | } | 1313 | } |
| 1956 | #endif | 1314 | #endif |
| 1957 | | | |
| 1958 | // Ported from std.os.getAppDataDir | | |
| 1959 | Error os_get_app_data_dir(Buf *out_path, const char *appname) { | | |
| 1960 | #if defined(ZIG_OS_WINDOWS) | | |
| 1961 | WCHAR *dir_path_ptr; | | |
| 1962 | switch (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &dir_path_ptr)) { | | |
| 1963 | case S_OK: | | |
| 1964 | // defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); | | |
| 1965 | utf16le_ptr_to_utf8(out_path, dir_path_ptr); | | |
| 1966 | CoTaskMemFree(dir_path_ptr); | | |
| 1967 | buf_appendf(out_path, "\\%s", appname); | | |
| 1968 | return ErrorNone; | | |
| 1969 | case E_OUTOFMEMORY: | | |
| 1970 | return ErrorNoMem; | | |
| 1971 | default: | | |
| 1972 | return ErrorUnexpected; | | |
| 1973 | } | | |
| 1974 | zig_unreachable(); | | |
| 1975 | #elif defined(ZIG_OS_DARWIN) | | |
| 1976 | const char *home_dir = getenv("HOME"); | | |
| 1977 | if (home_dir == nullptr) { | | |
| 1978 | // TODO use /etc/passwd | | |
| 1979 | return ErrorFileNotFound; | | |
| 1980 | } | | |
| 1981 | buf_resize(out_path, 0); | | |
| 1982 | buf_appendf(out_path, "%s/Library/Application Support/%s", home_dir, appname); | | |
| 1983 | return ErrorNone; | | |
| 1984 | #elif defined(ZIG_OS_POSIX) | | |
| 1985 | const char *cache_dir = getenv("XDG_CACHE_HOME"); | | |
| 1986 | if (cache_dir == nullptr) { | | |
| 1987 | cache_dir = getenv("HOME"); | | |
| 1988 | if (cache_dir == nullptr) { | | |
| 1989 | // TODO use /etc/passwd | | |
| 1990 | return ErrorFileNotFound; | | |
| 1991 | } | | |
| 1992 | if (cache_dir[0] == 0) { | | |
| 1993 | return ErrorFileNotFound; | | |
| 1994 | } | | |
| 1995 | buf_init_from_str(out_path, cache_dir); | | |
| 1996 | if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') { | | |
| 1997 | buf_append_char(out_path, '/'); | | |
| 1998 | } | | |
| 1999 | buf_appendf(out_path, ".cache/%s", appname); | | |
| 2000 | } else { | | |
| 2001 | if (cache_dir[0] == 0) { | | |
| 2002 | return ErrorFileNotFound; | | |
| 2003 | } | | |
| 2004 | buf_init_from_str(out_path, cache_dir); | | |
| 2005 | if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') { | | |
| 2006 | buf_append_char(out_path, '/'); | | |
| 2007 | } | | |
| 2008 | buf_appendf(out_path, "%s", appname); | | |
| 2009 | } | | |
| 2010 | return ErrorNone; | | |
| 2011 | #endif | | |
| 2012 | } | | |
| 2013 | | | |
| 2014 | #if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) | | |
| 2015 | static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, void *data) { | | |
| 2016 | ZigList<Buf *> *libs = reinterpret_cast< ZigList<Buf *> *>(data); | | |
| 2017 | if (info->dlpi_name[0] == '/') { | | |
| 2018 | libs->append(buf_create_from_str(info->dlpi_name)); | | |
| 2019 | } | | |
| 2020 | return 0; | | |
| 2021 | } | | |
| 2022 | #endif | | |
| 2023 | | | |
| 2024 | Error os_self_exe_shared_libs(ZigList<Buf *> &paths) { | | |
| 2025 | #if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) | | |
| 2026 | paths.resize(0); | | |
| 2027 | dl_iterate_phdr(self_exe_shared_libs_callback, &paths); | | |
| 2028 | return ErrorNone; | | |
| 2029 | #elif defined(ZIG_OS_DARWIN) | | |
| 2030 | paths.resize(0); | | |
| 2031 | uint32_t img_count = _dyld_image_count(); | | |
| 2032 | for (uint32_t i = 0; i != img_count; i += 1) { | | |
| 2033 | const char *name = _dyld_get_image_name(i); | | |
| 2034 | paths.append(buf_create_from_str(name)); | | |
| 2035 | } | | |
| 2036 | return ErrorNone; | | |
| 2037 | #elif defined(ZIG_OS_WINDOWS) | | |
| 2038 | // zig is built statically on windows, so we can return an empty list | | |
| 2039 | paths.resize(0); | | |
| 2040 | return ErrorNone; | | |
| 2041 | #else | | |
| 2042 | #error unimplemented | | |
| 2043 | #endif | | |
| 2044 | } | | |
| 2045 | | | |
| 2046 | Error os_file_open_rw(Buf *full_path, OsFile *out_file, OsFileAttr *attr, bool need_write, uint32_t mode) { | | |
| 2047 | #if defined(ZIG_OS_WINDOWS) | | |
| 2048 | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); | | |
| 2049 | HANDLE result = CreateFileW(&path_space.data.items[0], | | |
| 2050 | need_write ? (GENERIC_READ|GENERIC_WRITE) : GENERIC_READ, | | |
| 2051 | need_write ? 0 : FILE_SHARE_READ, | | |
| 2052 | nullptr, | | |
| 2053 | need_write ? OPEN_ALWAYS : OPEN_EXISTING, | | |
| 2054 | FILE_ATTRIBUTE_NORMAL, nullptr); | | |
| 2055 | | | |
| 2056 | if (result == INVALID_HANDLE_VALUE) { | | |
| 2057 | DWORD err = GetLastError(); | | |
| 2058 | switch (err) { | | |
| 2059 | case ERROR_SHARING_VIOLATION: | | |
| 2060 | return ErrorSharingViolation; | | |
| 2061 | case ERROR_ALREADY_EXISTS: | | |
| 2062 | return ErrorPathAlreadyExists; | | |
| 2063 | case ERROR_FILE_EXISTS: | | |
| 2064 | return ErrorPathAlreadyExists; | | |
| 2065 | case ERROR_FILE_NOT_FOUND: | | |
| 2066 | return ErrorFileNotFound; | | |
| 2067 | case ERROR_PATH_NOT_FOUND: | | |
| 2068 | return ErrorFileNotFound; | | |
| 2069 | case ERROR_ACCESS_DENIED: | | |
| 2070 | return ErrorAccess; | | |
| 2071 | case ERROR_PIPE_BUSY: | | |
| 2072 | return ErrorPipeBusy; | | |
| 2073 | default: | | |
| 2074 | return ErrorUnexpected; | | |
| 2075 | } | | |
| 2076 | } | | |
| 2077 | *out_file = result; | | |
| 2078 | | | |
| 2079 | if (attr != nullptr) { | | |
| 2080 | BY_HANDLE_FILE_INFORMATION file_info; | | |
| 2081 | if (!GetFileInformationByHandle(result, &file_info)) { | | |
| 2082 | CloseHandle(result); | | |
| 2083 | return ErrorUnexpected; | | |
| 2084 | } | | |
| 2085 | windows_filetime_to_os_timestamp(&file_info.ftLastWriteTime, &attr->mtime); | | |
| 2086 | attr->inode = (((uint64_t)file_info.nFileIndexHigh) << 32) | file_info.nFileIndexLow; | | |
| 2087 | attr->mode = 0; | | |
| 2088 | attr->size = (((uint64_t)file_info.nFileSizeHigh) << 32) | file_info.nFileSizeLow; | | |
| 2089 | } | | |
| 2090 | | | |
| 2091 | return ErrorNone; | | |
| 2092 | #else | | |
| 2093 | for (;;) { | | |
| 2094 | int fd = open(buf_ptr(full_path), | | |
| 2095 | need_write ? (O_RDWR|O_CLOEXEC|O_CREAT) : (O_RDONLY|O_CLOEXEC), mode); | | |
| 2096 | if (fd == -1) { | | |
| 2097 | switch (errno) { | | |
| 2098 | case EINTR: | | |
| 2099 | continue; | | |
| 2100 | case EINVAL: | | |
| 2101 | zig_unreachable(); | | |
| 2102 | case EFAULT: | | |
| 2103 | zig_unreachable(); | | |
| 2104 | case EACCES: | | |
| 2105 | case EPERM: | | |
| 2106 | return ErrorAccess; | | |
| 2107 | case EISDIR: | | |
| 2108 | return ErrorIsDir; | | |
| 2109 | case ENOENT: | | |
| 2110 | return ErrorFileNotFound; | | |
| 2111 | default: | | |
| 2112 | return ErrorFileSystem; | | |
| 2113 | } | | |
| 2114 | } | | |
| 2115 | struct stat statbuf; | | |
| 2116 | if (fstat(fd, &statbuf) == -1) { | | |
| 2117 | close(fd); | | |
| 2118 | return ErrorFileSystem; | | |
| 2119 | } | | |
| 2120 | if (S_ISDIR(statbuf.st_mode)) { | | |
| 2121 | close(fd); | | |
| 2122 | return ErrorIsDir; | | |
| 2123 | } | | |
| 2124 | *out_file = fd; | | |
| 2125 | | | |
| 2126 | if (attr != nullptr) { | | |
| 2127 | attr->inode = statbuf.st_ino; | | |
| 2128 | #if defined(ZIG_OS_DARWIN) | | |
| 2129 | attr->mtime.sec = statbuf.st_mtimespec.tv_sec; | | |
| 2130 | attr->mtime.nsec = statbuf.st_mtimespec.tv_nsec; | | |
| 2131 | #else | | |
| 2132 | attr->mtime.sec = statbuf.st_mtim.tv_sec; | | |
| 2133 | attr->mtime.nsec = statbuf.st_mtim.tv_nsec; | | |
| 2134 | #endif | | |
| 2135 | attr->mode = statbuf.st_mode; | | |
| 2136 | attr->size = statbuf.st_size; | | |
| 2137 | } | | |
| 2138 | return ErrorNone; | | |
| 2139 | } | | |
| 2140 | #endif | | |
| 2141 | } | | |
| 2142 | | | |
| 2143 | Error os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr) { | | |
| 2144 | return os_file_open_rw(full_path, out_file, attr, false, 0); | | |
| 2145 | } | | |
| 2146 | | | |
| 2147 | Error os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_t mode) { | | |
| 2148 | return os_file_open_rw(full_path, out_file, attr, true, mode); | | |
| 2149 | } | | |
| 2150 | | | |
| 2151 | Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) { | | |
| 2152 | #if defined(ZIG_OS_WINDOWS) | | |
| 2153 | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); | | |
| 2154 | for (;;) { | | |
| 2155 | HANDLE result = CreateFileW(&path_space.data.items[0], GENERIC_READ | GENERIC_WRITE, | | |
| 2156 | 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); | | |
| 2157 | | | |
| 2158 | if (result == INVALID_HANDLE_VALUE) { | | |
| 2159 | DWORD err = GetLastError(); | | |
| 2160 | switch (err) { | | |
| 2161 | case ERROR_SHARING_VIOLATION: | | |
| 2162 | // TODO wait for the lock instead of sleeping | | |
| 2163 | Sleep(10); | | |
| 2164 | continue; | | |
| 2165 | case ERROR_ALREADY_EXISTS: | | |
| 2166 | return ErrorPathAlreadyExists; | | |
| 2167 | case ERROR_FILE_EXISTS: | | |
| 2168 | return ErrorPathAlreadyExists; | | |
| 2169 | case ERROR_FILE_NOT_FOUND: | | |
| 2170 | return ErrorFileNotFound; | | |
| 2171 | case ERROR_PATH_NOT_FOUND: | | |
| 2172 | return ErrorFileNotFound; | | |
| 2173 | case ERROR_ACCESS_DENIED: | | |
| 2174 | return ErrorAccess; | | |
| 2175 | case ERROR_PIPE_BUSY: | | |
| 2176 | return ErrorPipeBusy; | | |
| 2177 | default: | | |
| 2178 | return ErrorUnexpected; | | |
| 2179 | } | | |
| 2180 | } | | |
| 2181 | *out_file = result; | | |
| 2182 | return ErrorNone; | | |
| 2183 | } | | |
| 2184 | #else | | |
| 2185 | int fd; | | |
| 2186 | for (;;) { | | |
| 2187 | fd = open(buf_ptr(full_path), O_RDWR|O_CLOEXEC|O_CREAT, 0666); | | |
| 2188 | if (fd == -1) { | | |
| 2189 | switch (errno) { | | |
| 2190 | case EINTR: | | |
| 2191 | continue; | | |
| 2192 | case EINVAL: | | |
| 2193 | zig_unreachable(); | | |
| 2194 | case EFAULT: | | |
| 2195 | zig_unreachable(); | | |
| 2196 | case EACCES: | | |
| 2197 | case EPERM: | | |
| 2198 | return ErrorAccess; | | |
| 2199 | case EISDIR: | | |
| 2200 | return ErrorIsDir; | | |
| 2201 | case ENOENT: | | |
| 2202 | return ErrorFileNotFound; | | |
| 2203 | case ENOTDIR: | | |
| 2204 | return ErrorNotDir; | | |
| 2205 | default: | | |
| 2206 | return ErrorFileSystem; | | |
| 2207 | } | | |
| 2208 | } | | |
| 2209 | break; | | |
| 2210 | } | | |
| 2211 | for (;;) { | | |
| 2212 | struct flock lock; | | |
| 2213 | lock.l_type = F_WRLCK; | | |
| 2214 | lock.l_whence = SEEK_SET; | | |
| 2215 | lock.l_start = 0; | | |
| 2216 | lock.l_len = 0; | | |
| 2217 | if (fcntl(fd, F_SETLKW, &lock) == -1) { | | |
| 2218 | switch (errno) { | | |
| 2219 | case EINTR: | | |
| 2220 | continue; | | |
| 2221 | case EBADF: | | |
| 2222 | zig_unreachable(); | | |
| 2223 | case EFAULT: | | |
| 2224 | zig_unreachable(); | | |
| 2225 | case EINVAL: | | |
| 2226 | zig_unreachable(); | | |
| 2227 | default: | | |
| 2228 | close(fd); | | |
| 2229 | return ErrorFileSystem; | | |
| 2230 | } | | |
| 2231 | } | | |
| 2232 | break; | | |
| 2233 | } | | |
| 2234 | *out_file = fd; | | |
| 2235 | return ErrorNone; | | |
| 2236 | #endif | | |
| 2237 | } | | |
| 2238 | | | |
| 2239 | Error os_file_read(OsFile file, void *ptr, size_t *len) { | | |
| 2240 | #if defined(ZIG_OS_WINDOWS) | | |
| 2241 | DWORD amt_read; | | |
| 2242 | if (ReadFile(file, ptr, *len, &amt_read, nullptr) == 0) | | |
| 2243 | return ErrorUnexpected; | | |
| 2244 | *len = amt_read; | | |
| 2245 | return ErrorNone; | | |
| 2246 | #else | | |
| 2247 | for (;;) { | | |
| 2248 | ssize_t rc = read(file, ptr, *len); | | |
| 2249 | if (rc == -1) { | | |
| 2250 | switch (errno) { | | |
| 2251 | case EINTR: | | |
| 2252 | continue; | | |
| 2253 | case EBADF: | | |
| 2254 | zig_unreachable(); | | |
| 2255 | case EFAULT: | | |
| 2256 | zig_unreachable(); | | |
| 2257 | case EISDIR: | | |
| 2258 | return ErrorIsDir; | | |
| 2259 | default: | | |
| 2260 | return ErrorFileSystem; | | |
| 2261 | } | | |
| 2262 | } | | |
| 2263 | *len = rc; | | |
| 2264 | return ErrorNone; | | |
| 2265 | } | | |
| 2266 | #endif | | |
| 2267 | } | | |
| 2268 | | | |
| 2269 | Error os_file_read_all(OsFile file, Buf *contents) { | | |
| 2270 | Error err; | | |
| 2271 | size_t index = 0; | | |
| 2272 | for (;;) { | | |
| 2273 | size_t amt = buf_len(contents) - index; | | |
| 2274 | | | |
| 2275 | if (amt < 4096) { | | |
| 2276 | buf_resize(contents, buf_len(contents) + (4096 - amt)); | | |
| 2277 | amt = buf_len(contents) - index; | | |
| 2278 | } | | |
| 2279 | | | |
| 2280 | if ((err = os_file_read(file, buf_ptr(contents) + index, &amt))) | | |
| 2281 | return err; | | |
| 2282 | | | |
| 2283 | if (amt == 0) { | | |
| 2284 | buf_resize(contents, index); | | |
| 2285 | return ErrorNone; | | |
| 2286 | } | | |
| 2287 | | | |
| 2288 | index += amt; | | |
| 2289 | } | | |
| 2290 | } | | |
| 2291 | | | |
| 2292 | Error os_file_overwrite(OsFile file, Buf *contents) { | | |
| 2293 | #if defined(ZIG_OS_WINDOWS) | | |
| 2294 | if (SetFilePointer(file, 0, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) | | |
| 2295 | return ErrorFileSystem; | | |
| 2296 | if (!SetEndOfFile(file)) | | |
| 2297 | return ErrorFileSystem; | | |
| 2298 | DWORD bytes_written; | | |
| 2299 | if (!WriteFile(file, buf_ptr(contents), buf_len(contents), &bytes_written, nullptr)) | | |
| 2300 | return ErrorFileSystem; | | |
| 2301 | return ErrorNone; | | |
| 2302 | #else | | |
| 2303 | if (lseek(file, 0, SEEK_SET) == -1) | | |
| 2304 | return ErrorUnexpectedSeekFailure; | | |
| 2305 | if (ftruncate(file, 0) == -1) | | |
| 2306 | return ErrorUnexpectedFileTruncationFailure; | | |
| 2307 | for (;;) { | | |
| 2308 | if (write(file, buf_ptr(contents), buf_len(contents)) == -1) { | | |
| 2309 | switch (errno) { | | |
| 2310 | case EINTR: | | |
| 2311 | continue; | | |
| 2312 | case EINVAL: | | |
| 2313 | zig_unreachable(); | | |
| 2314 | case EBADF: | | |
| 2315 | zig_unreachable(); | | |
| 2316 | case EFAULT: | | |
| 2317 | zig_unreachable(); | | |
| 2318 | case EDQUOT: | | |
| 2319 | return ErrorDiskQuota; | | |
| 2320 | case ENOSPC: | | |
| 2321 | return ErrorDiskSpace; | | |
| 2322 | case EFBIG: | | |
| 2323 | return ErrorFileTooBig; | | |
| 2324 | case EIO: | | |
| 2325 | return ErrorFileSystem; | | |
| 2326 | case EPERM: | | |
| 2327 | return ErrorAccess; | | |
| 2328 | default: | | |
| 2329 | return ErrorUnexpectedWriteFailure; | | |
| 2330 | } | | |
| 2331 | } | | |
| 2332 | return ErrorNone; | | |
| 2333 | } | | |
| 2334 | #endif | | |
| 2335 | } | | |
| 2336 | | | |
| 2337 | void os_file_close(OsFile *file) { | | |
| 2338 | #if defined(ZIG_OS_WINDOWS) | | |
| 2339 | CloseHandle(*file); | | |
| 2340 | *file = NULL; | | |
| 2341 | #else | | |
| 2342 | close(*file); | | |
| 2343 | *file = -1; | | |
| 2344 | #endif | | |
| 2345 | } | | |