os_windows.odin 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. // +build windows
  2. package os
  3. import win32 "core:sys/windows"
  4. import "core:intrinsics"
  5. Handle :: distinct uintptr;
  6. File_Time :: distinct u64;
  7. Errno :: distinct int;
  8. INVALID_HANDLE :: ~Handle(0);
  9. O_RDONLY :: 0x00000;
  10. O_WRONLY :: 0x00001;
  11. O_RDWR :: 0x00002;
  12. O_CREATE :: 0x00040;
  13. O_EXCL :: 0x00080;
  14. O_NOCTTY :: 0x00100;
  15. O_TRUNC :: 0x00200;
  16. O_NONBLOCK :: 0x00800;
  17. O_APPEND :: 0x00400;
  18. O_SYNC :: 0x01000;
  19. O_ASYNC :: 0x02000;
  20. O_CLOEXEC :: 0x80000;
  21. ERROR_NONE: Errno : 0;
  22. ERROR_FILE_NOT_FOUND: Errno : 2;
  23. ERROR_PATH_NOT_FOUND: Errno : 3;
  24. ERROR_ACCESS_DENIED: Errno : 5;
  25. ERROR_INVALID_HANDLE: Errno : 6;
  26. ERROR_NO_MORE_FILES: Errno : 18;
  27. ERROR_HANDLE_EOF: Errno : 38;
  28. ERROR_NETNAME_DELETED: Errno : 64;
  29. ERROR_FILE_EXISTS: Errno : 80;
  30. ERROR_BROKEN_PIPE: Errno : 109;
  31. ERROR_BUFFER_OVERFLOW: Errno : 111;
  32. ERROR_INSUFFICIENT_BUFFER: Errno : 122;
  33. ERROR_MOD_NOT_FOUND: Errno : 126;
  34. ERROR_PROC_NOT_FOUND: Errno : 127;
  35. ERROR_DIR_NOT_EMPTY: Errno : 145;
  36. ERROR_ALREADY_EXISTS: Errno : 183;
  37. ERROR_ENVVAR_NOT_FOUND: Errno : 203;
  38. ERROR_MORE_DATA: Errno : 234;
  39. ERROR_OPERATION_ABORTED: Errno : 995;
  40. ERROR_IO_PENDING: Errno : 997;
  41. ERROR_NOT_FOUND: Errno : 1168;
  42. ERROR_PRIVILEGE_NOT_HELD: Errno : 1314;
  43. WSAEACCES: Errno : 10013;
  44. WSAECONNRESET: Errno : 10054;
  45. // Windows reserves errors >= 1<<29 for application use
  46. ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
  47. // "Argv" arguments converted to Odin strings
  48. args := _alloc_command_line_arguments();
  49. is_path_separator :: proc(r: rune) -> bool {
  50. return r == '/' || r == '\\';
  51. }
  52. open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
  53. if len(path) == 0 do return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
  54. access: u32;
  55. switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
  56. case O_RDONLY: access = win32.FILE_GENERIC_READ;
  57. case O_WRONLY: access = win32.FILE_GENERIC_WRITE;
  58. case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE;
  59. }
  60. if mode&O_APPEND != 0 {
  61. access &~= win32.FILE_GENERIC_WRITE;
  62. access |= win32.FILE_APPEND_DATA;
  63. }
  64. if mode&O_CREATE != 0 {
  65. access |= win32.FILE_GENERIC_WRITE;
  66. }
  67. share_mode := u32(win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE);
  68. sa: ^win32.SECURITY_ATTRIBUTES = nil;
  69. sa_inherit := win32.SECURITY_ATTRIBUTES{nLength = size_of(win32.SECURITY_ATTRIBUTES), bInheritHandle = true};
  70. if mode&O_CLOEXEC == 0 {
  71. sa = &sa_inherit;
  72. }
  73. create_mode: u32;
  74. switch {
  75. case mode&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL):
  76. create_mode = win32.CREATE_NEW;
  77. case mode&(O_CREATE|O_TRUNC) == (O_CREATE | O_TRUNC):
  78. create_mode = win32.CREATE_ALWAYS;
  79. case mode&O_CREATE == O_CREATE:
  80. create_mode = win32.OPEN_ALWAYS;
  81. case mode&O_TRUNC == O_TRUNC:
  82. create_mode = win32.TRUNCATE_EXISTING;
  83. case:
  84. create_mode = win32.OPEN_EXISTING;
  85. }
  86. wide_path := win32.utf8_to_wstring(path);
  87. handle := Handle(win32.CreateFileW(auto_cast wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL, nil));
  88. if handle != INVALID_HANDLE do return handle, ERROR_NONE;
  89. err := Errno(win32.GetLastError());
  90. return INVALID_HANDLE, err;
  91. }
  92. close :: proc(fd: Handle) -> Errno {
  93. if !win32.CloseHandle(win32.HANDLE(fd)) {
  94. return Errno(win32.GetLastError());
  95. }
  96. return ERROR_NONE;
  97. }
  98. write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
  99. if len(data) == 0 do return 0, ERROR_NONE;
  100. single_write_length: win32.DWORD;
  101. total_write: i64;
  102. length := i64(len(data));
  103. for total_write < length {
  104. remaining := length - total_write;
  105. MAX :: 1<<31-1;
  106. to_write := win32.DWORD(min(i32(remaining), MAX));
  107. e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil);
  108. if single_write_length <= 0 || !e {
  109. err := Errno(win32.GetLastError());
  110. return int(total_write), err;
  111. }
  112. total_write += i64(single_write_length);
  113. }
  114. return int(total_write), ERROR_NONE;
  115. }
  116. read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
  117. if len(data) == 0 do return 0, ERROR_NONE;
  118. single_read_length: win32.DWORD;
  119. total_read: i64;
  120. length := i64(len(data));
  121. for total_read < length {
  122. remaining := length - total_read;
  123. MAX :: 1<<32-1;
  124. to_read := win32.DWORD(min(u32(remaining), MAX));
  125. e := win32.ReadFile(win32.HANDLE(fd), &data[total_read], to_read, &single_read_length, nil);
  126. if single_read_length <= 0 || !e {
  127. err := Errno(win32.GetLastError());
  128. return int(total_read), err;
  129. }
  130. total_read += i64(single_read_length);
  131. }
  132. return int(total_read), ERROR_NONE;
  133. }
  134. seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
  135. w: u32;
  136. switch whence {
  137. case 0: w = win32.FILE_BEGIN;
  138. case 1: w = win32.FILE_CURRENT;
  139. case 2: w = win32.FILE_END;
  140. }
  141. hi := i32(offset>>32);
  142. lo := i32(offset);
  143. ft := win32.GetFileType(win32.HANDLE(fd));
  144. if ft == win32.FILE_TYPE_PIPE do return 0, ERROR_FILE_IS_PIPE;
  145. dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w);
  146. if dw_ptr == win32.INVALID_SET_FILE_POINTER {
  147. err := Errno(win32.GetLastError());
  148. return 0, err;
  149. }
  150. return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE;
  151. }
  152. file_size :: proc(fd: Handle) -> (i64, Errno) {
  153. length: win32.LARGE_INTEGER;
  154. err: Errno;
  155. if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
  156. err = Errno(win32.GetLastError());
  157. }
  158. return i64(length), err;
  159. }
  160. // NOTE(bill): Uses startup to initialize it
  161. stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE));
  162. stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE));
  163. stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE));
  164. get_std_handle :: proc "contextless" (h: uint) -> Handle {
  165. fd := win32.GetStdHandle(win32.DWORD(h));
  166. when size_of(uintptr) == 8 {
  167. win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
  168. }
  169. return Handle(fd);
  170. }
  171. last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
  172. file_info: win32.BY_HANDLE_FILE_INFORMATION;
  173. if !win32.GetFileInformationByHandle(win32.HANDLE(fd), &file_info) {
  174. return 0, Errno(win32.GetLastError());
  175. }
  176. lo := File_Time(file_info.ftLastWriteTime.dwLowDateTime);
  177. hi := File_Time(file_info.ftLastWriteTime.dwHighDateTime);
  178. return lo | hi << 32, ERROR_NONE;
  179. }
  180. last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
  181. data: win32.WIN32_FILE_ATTRIBUTE_DATA;
  182. wide_path := win32.utf8_to_wstring(name);
  183. if !win32.GetFileAttributesExW(auto_cast wide_path, win32.GetFileExInfoStandard, &data) {
  184. return 0, Errno(win32.GetLastError());
  185. }
  186. l := File_Time(data.ftLastWriteTime.dwLowDateTime);
  187. h := File_Time(data.ftLastWriteTime.dwHighDateTime);
  188. return l | h << 32, ERROR_NONE;
  189. }
  190. heap_alloc :: proc(size: int) -> rawptr {
  191. return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size));
  192. }
  193. heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
  194. if new_size == 0 {
  195. heap_free(ptr);
  196. return nil;
  197. }
  198. if ptr == nil do return heap_alloc(new_size);
  199. return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, uint(new_size));
  200. }
  201. heap_free :: proc(ptr: rawptr) {
  202. if ptr == nil do return;
  203. win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
  204. }
  205. get_page_size :: proc() -> int {
  206. // NOTE(tetra): The page size never changes, so why do anything complicated
  207. // if we don't have to.
  208. @static page_size := -1;
  209. if page_size != -1 do return page_size;
  210. info: win32.SYSTEM_INFO;
  211. win32.GetSystemInfo(&info);
  212. page_size = int(info.dwPageSize);
  213. return page_size;
  214. }
  215. // NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName;
  216. // The current directory is stored as a global variable in the process.
  217. @private cwd_gate := false;
  218. get_current_directory :: proc(allocator := context.allocator) -> string {
  219. for intrinsics.atomic_xchg(&cwd_gate, true) {}
  220. sz_utf16 := win32.GetCurrentDirectoryW(0, nil);
  221. dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator); // the first time, it _includes_ the NUL.
  222. sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), auto_cast &dir_buf_wstr[0]);
  223. assert(int(sz_utf16)+1 == len(dir_buf_wstr)); // the second time, it _excludes_ the NUL.
  224. intrinsics.atomic_store(&cwd_gate, false);
  225. return win32.utf16_to_utf8(dir_buf_wstr, allocator);
  226. }
  227. set_current_directory :: proc(path: string) -> (err: Errno) {
  228. wstr := win32.utf8_to_wstring(path);
  229. for intrinsics.atomic_xchg(&cwd_gate, true) {}
  230. defer intrinsics.atomic_store(&cwd_gate, false);
  231. res := win32.SetCurrentDirectoryW(auto_cast wstr);
  232. if !res do return Errno(win32.GetLastError());
  233. return;
  234. }
  235. exit :: proc(code: int) -> ! {
  236. win32.ExitProcess(win32.DWORD(code));
  237. }
  238. current_thread_id :: proc "contextless" () -> int {
  239. return int(win32.GetCurrentThreadId());
  240. }
  241. _alloc_command_line_arguments :: proc() -> []string {
  242. arg_count: i32;
  243. arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), &arg_count);
  244. arg_list := make([]string, int(arg_count));
  245. for _, i in arg_list {
  246. wc_str := (^win32.wstring)(uintptr(arg_list_ptr) + size_of(win32.wstring)*uintptr(i))^;
  247. olen := win32.WideCharToMultiByte(win32.CP_UTF8, 0, wc_str, -1,
  248. nil, 0, nil, nil);
  249. buf := make([]byte, int(olen));
  250. n := win32.WideCharToMultiByte(win32.CP_UTF8, 0, wc_str, -1,
  251. raw_data(buf), olen, nil, nil);
  252. if n > 0 {
  253. n -= 1;
  254. }
  255. arg_list[i] = string(buf[:n]);
  256. }
  257. return arg_list;
  258. }
  259. get_windows_version_ansi :: proc() -> win32.OSVERSIONINFOEXW {
  260. osvi : win32.OSVERSIONINFOEXW;
  261. osvi.os_version_info_size = size_of(win32.OSVERSIONINFOEXW);
  262. win32.GetVersionExW(&osvi);
  263. return osvi;
  264. }
  265. is_windows_xp :: proc() -> bool {
  266. osvi := get_windows_version_ansi();
  267. return (osvi.major_version == 5 && osvi.minor_version == 1);
  268. }
  269. is_windows_vista :: proc() -> bool {
  270. osvi := get_windows_version_ansi();
  271. return (osvi.major_version == 6 && osvi.minor_version == 0);
  272. }
  273. is_windows_7 :: proc() -> bool {
  274. osvi := get_windows_version_ansi();
  275. return (osvi.major_version == 6 && osvi.minor_version == 1);
  276. }
  277. is_windows_8 :: proc() -> bool {
  278. osvi := get_windows_version_ansi();
  279. return (osvi.major_version == 6 && osvi.minor_version == 2);
  280. }
  281. is_windows_8_1 :: proc() -> bool {
  282. osvi := get_windows_version_ansi();
  283. return (osvi.major_version == 6 && osvi.minor_version == 3);
  284. }
  285. is_windows_10 :: proc() -> bool {
  286. osvi := get_windows_version_ansi();
  287. return (osvi.major_version == 10 && osvi.minor_version == 0);
  288. }