file_windows.odin 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. //+private
  2. package os2
  3. import "base:runtime"
  4. import "core:io"
  5. import "core:mem"
  6. import "core:sync"
  7. import "core:time"
  8. import "core:unicode/utf16"
  9. import win32 "core:sys/windows"
  10. INVALID_HANDLE :: ~uintptr(0)
  11. S_IWRITE :: 0o200
  12. _ERROR_BAD_NETPATH :: 53
  13. MAX_RW :: 1<<30
  14. File_Impl_Kind :: enum u8 {
  15. File,
  16. Console,
  17. Pipe,
  18. }
  19. File_Impl :: struct {
  20. file: File,
  21. fd: rawptr,
  22. name: string,
  23. wname: win32.wstring,
  24. kind: File_Impl_Kind,
  25. allocator: runtime.Allocator,
  26. rw_mutex: sync.RW_Mutex, // read write calls
  27. p_mutex: sync.Mutex, // pread pwrite calls
  28. }
  29. @(init)
  30. init_std_files :: proc() {
  31. stdin = new_file(uintptr(win32.GetStdHandle(win32.STD_INPUT_HANDLE)), "<stdin>")
  32. stdout = new_file(uintptr(win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)), "<stdout>")
  33. stderr = new_file(uintptr(win32.GetStdHandle(win32.STD_ERROR_HANDLE)), "<stderr>")
  34. }
  35. @(fini)
  36. fini_std_files :: proc() {
  37. close(stdin)
  38. close(stdout)
  39. close(stderr)
  40. }
  41. _handle :: proc(f: ^File) -> win32.HANDLE {
  42. return win32.HANDLE(_fd(f))
  43. }
  44. _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: uintptr, err: Error) {
  45. if len(name) == 0 {
  46. err = .Not_Exist
  47. return
  48. }
  49. TEMP_ALLOCATOR_GUARD()
  50. path := _fix_long_path(name, temp_allocator()) or_return
  51. access: u32
  52. switch flags & {.Read, .Write} {
  53. case {.Read}: access = win32.FILE_GENERIC_READ
  54. case {.Write}: access = win32.FILE_GENERIC_WRITE
  55. case {.Read, .Write}: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE
  56. }
  57. if .Create in flags {
  58. access |= win32.FILE_GENERIC_WRITE
  59. }
  60. if .Append in flags {
  61. access &~= win32.FILE_GENERIC_WRITE
  62. access |= win32.FILE_APPEND_DATA
  63. }
  64. share_mode := u32(win32.FILE_SHARE_READ | win32.FILE_SHARE_WRITE)
  65. sa := win32.SECURITY_ATTRIBUTES {
  66. nLength = size_of(win32.SECURITY_ATTRIBUTES),
  67. bInheritHandle = .Inheritable in flags,
  68. }
  69. create_mode: u32 = win32.OPEN_EXISTING
  70. switch {
  71. case flags & {.Create, .Excl} == {.Create, .Excl}:
  72. create_mode = win32.CREATE_NEW
  73. case flags & {.Create, .Trunc} == {.Create, .Trunc}:
  74. create_mode = win32.CREATE_ALWAYS
  75. case flags & {.Create} == {.Create}:
  76. create_mode = win32.OPEN_ALWAYS
  77. case flags & {.Trunc} == {.Trunc}:
  78. create_mode = win32.TRUNCATE_EXISTING
  79. }
  80. attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS
  81. if perm & S_IWRITE == 0 {
  82. attrs = win32.FILE_ATTRIBUTE_READONLY
  83. if create_mode == win32.CREATE_ALWAYS {
  84. // NOTE(bill): Open has just asked to create a file in read-only mode.
  85. // If the file already exists, to make it akin to a *nix open call,
  86. // the call preserves the existing permissions.
  87. h := win32.CreateFileW(path, access, share_mode, &sa, win32.TRUNCATE_EXISTING, win32.FILE_ATTRIBUTE_NORMAL, nil)
  88. if h == win32.INVALID_HANDLE {
  89. switch e := win32.GetLastError(); e {
  90. case win32.ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, win32.ERROR_PATH_NOT_FOUND:
  91. // file does not exist, create the file
  92. case 0:
  93. return uintptr(h), nil
  94. case:
  95. return 0, _get_platform_error()
  96. }
  97. }
  98. }
  99. }
  100. h := win32.CreateFileW(path, access, share_mode, &sa, create_mode, attrs, nil)
  101. if h == win32.INVALID_HANDLE {
  102. return 0, _get_platform_error()
  103. }
  104. return uintptr(h), nil
  105. }
  106. _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
  107. flags := flags if flags != nil else {.Read}
  108. handle := _open_internal(name, flags, perm) or_return
  109. return _new_file(handle, name)
  110. }
  111. _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
  112. if handle == INVALID_HANDLE {
  113. return
  114. }
  115. impl := new(File_Impl, file_allocator()) or_return
  116. defer if err != nil {
  117. free(impl, file_allocator())
  118. }
  119. impl.file.impl = impl
  120. impl.allocator = file_allocator()
  121. impl.fd = rawptr(handle)
  122. impl.name = clone_string(name, impl.allocator) or_return
  123. impl.wname = win32_utf8_to_wstring(name, impl.allocator) or_return
  124. handle := _handle(&impl.file)
  125. kind := File_Impl_Kind.File
  126. if m: u32; win32.GetConsoleMode(handle, &m) {
  127. kind = .Console
  128. }
  129. if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
  130. kind = .Pipe
  131. }
  132. impl.kind = kind
  133. impl.file.stream = {
  134. data = impl,
  135. procedure = _file_stream_proc,
  136. }
  137. impl.file.fstat = _fstat
  138. return &impl.file, nil
  139. }
  140. _fd :: proc(f: ^File) -> uintptr {
  141. if f == nil || f.impl == nil {
  142. return INVALID_HANDLE
  143. }
  144. return uintptr((^File_Impl)(f.impl).fd)
  145. }
  146. _destroy :: proc(f: ^File_Impl) -> Error {
  147. if f == nil {
  148. return nil
  149. }
  150. a := f.allocator
  151. err0 := free(f.wname, a)
  152. err1 := delete(f.name, a)
  153. err2 := free(f, a)
  154. err0 or_return
  155. err1 or_return
  156. err2 or_return
  157. return nil
  158. }
  159. _close :: proc(f: ^File_Impl) -> Error {
  160. if f == nil {
  161. return nil
  162. }
  163. if !win32.CloseHandle(win32.HANDLE(f.fd)) {
  164. return .Closed
  165. }
  166. return _destroy(f)
  167. }
  168. _name :: proc(f: ^File) -> string {
  169. return (^File_Impl)(f.impl).name if f != nil && f.impl != nil else ""
  170. }
  171. _seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
  172. handle := _handle(&f.file)
  173. if handle == win32.INVALID_HANDLE {
  174. return 0, .Invalid_File
  175. }
  176. if f.kind == .Pipe {
  177. return 0, .Invalid_File
  178. }
  179. sync.guard(&f.rw_mutex)
  180. w: u32
  181. switch whence {
  182. case .Start: w = win32.FILE_BEGIN
  183. case .Current: w = win32.FILE_CURRENT
  184. case .End: w = win32.FILE_END
  185. }
  186. hi := i32(offset>>32)
  187. lo := i32(offset)
  188. dw_ptr := win32.SetFilePointer(handle, lo, &hi, w)
  189. if dw_ptr == win32.INVALID_SET_FILE_POINTER {
  190. return 0, _get_platform_error()
  191. }
  192. return i64(hi)<<32 + i64(dw_ptr), nil
  193. }
  194. _read :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
  195. read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
  196. if len(b) == 0 {
  197. return 0, nil
  198. }
  199. // TODO(bill): should this be moved to `File_Impl` instead?
  200. BUF_SIZE :: 386
  201. buf16: [BUF_SIZE]u16
  202. buf8: [4*BUF_SIZE]u8
  203. for n < len(b) && err == nil {
  204. min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
  205. max_read := u32(min(BUF_SIZE, min_read))
  206. if max_read == 0 {
  207. break
  208. }
  209. single_read_length: u32
  210. ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
  211. if !ok {
  212. err = _get_platform_error()
  213. }
  214. buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
  215. src := buf8[:buf8_len]
  216. ctrl_z := false
  217. for i := 0; i < len(src) && n+i < len(b); i += 1 {
  218. x := src[i]
  219. if x == 0x1a { // ctrl-z
  220. ctrl_z = true
  221. break
  222. }
  223. b[n] = x
  224. n += 1
  225. }
  226. if ctrl_z || single_read_length < max_read {
  227. break
  228. }
  229. // NOTE(bill): if the last two values were a newline, then it is expected that
  230. // this is the end of the input
  231. if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" {
  232. break
  233. }
  234. }
  235. return
  236. }
  237. handle := _handle(&f.file)
  238. single_read_length: win32.DWORD
  239. total_read: int
  240. length := len(p)
  241. sync.shared_guard(&f.rw_mutex) // multiple readers
  242. if sync.guard(&f.p_mutex) {
  243. to_read := min(win32.DWORD(length), MAX_RW)
  244. ok: win32.BOOL
  245. if f.kind == .Console {
  246. n, cerr := read_console(handle, p[total_read:][:to_read])
  247. total_read += n
  248. if cerr != nil {
  249. return i64(total_read), cerr
  250. }
  251. } else {
  252. ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil)
  253. }
  254. if single_read_length > 0 && ok {
  255. total_read += int(single_read_length)
  256. } else {
  257. err = _get_platform_error()
  258. }
  259. }
  260. return i64(total_read), err
  261. }
  262. _read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (n: i64, err: Error) {
  263. pread :: proc(f: ^File_Impl, data: []byte, offset: i64) -> (n: i64, err: Error) {
  264. buf := data
  265. if len(buf) > MAX_RW {
  266. buf = buf[:MAX_RW]
  267. }
  268. curr_offset := _seek(f, offset, .Current) or_return
  269. defer _seek(f, curr_offset, .Start)
  270. o := win32.OVERLAPPED{
  271. OffsetHigh = u32(offset>>32),
  272. Offset = u32(offset),
  273. }
  274. // TODO(bill): Determine the correct behaviour for consoles
  275. h := _handle(&f.file)
  276. done: win32.DWORD
  277. if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
  278. err = _get_platform_error()
  279. done = 0
  280. }
  281. n = i64(done)
  282. return
  283. }
  284. sync.guard(&f.p_mutex)
  285. p, offset := p, offset
  286. for len(p) > 0 {
  287. m := pread(f, p, offset) or_return
  288. n += m
  289. p = p[m:]
  290. offset += i64(m)
  291. }
  292. return
  293. }
  294. _write :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
  295. if len(p) == 0 {
  296. return
  297. }
  298. single_write_length: win32.DWORD
  299. total_write: i64
  300. length := i64(len(p))
  301. handle := _handle(&f.file)
  302. sync.guard(&f.rw_mutex)
  303. for total_write < length {
  304. remaining := length - total_write
  305. to_write := win32.DWORD(min(i32(remaining), MAX_RW))
  306. e := win32.WriteFile(handle, &p[total_write], to_write, &single_write_length, nil)
  307. if single_write_length <= 0 || !e {
  308. n = i64(total_write)
  309. err = _get_platform_error()
  310. return
  311. }
  312. total_write += i64(single_write_length)
  313. }
  314. return i64(total_write), nil
  315. }
  316. _write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (n: i64, err: Error) {
  317. pwrite :: proc(f: ^File_Impl, data: []byte, offset: i64) -> (n: i64, err: Error) {
  318. buf := data
  319. if len(buf) > MAX_RW {
  320. buf = buf[:MAX_RW]
  321. }
  322. curr_offset := _seek(f, offset, .Current) or_return
  323. defer _seek(f, curr_offset, .Start)
  324. o := win32.OVERLAPPED{
  325. OffsetHigh = u32(offset>>32),
  326. Offset = u32(offset),
  327. }
  328. h := _handle(&f.file)
  329. done: win32.DWORD
  330. if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
  331. err = _get_platform_error()
  332. done = 0
  333. }
  334. n = i64(done)
  335. return
  336. }
  337. sync.guard(&f.p_mutex)
  338. p, offset := p, offset
  339. for len(p) > 0 {
  340. m := pwrite(f, p, offset) or_return
  341. n += m
  342. p = p[m:]
  343. offset += i64(m)
  344. }
  345. return
  346. }
  347. _file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
  348. length: win32.LARGE_INTEGER
  349. if f.kind == .Pipe {
  350. return 0, .No_Size
  351. }
  352. handle := _handle(&f.file)
  353. if !win32.GetFileSizeEx(handle, &length) {
  354. err = _get_platform_error()
  355. }
  356. n = i64(length)
  357. return
  358. }
  359. _sync :: proc(f: ^File) -> Error {
  360. if f != nil && f.impl != nil {
  361. return _flush((^File_Impl)(f.impl))
  362. }
  363. return nil
  364. }
  365. _flush :: proc(f: ^File_Impl) -> Error {
  366. handle := _handle(&f.file)
  367. if !win32.FlushFileBuffers(handle) {
  368. return _get_platform_error()
  369. }
  370. return nil
  371. }
  372. _truncate :: proc(f: ^File, size: i64) -> Error {
  373. if f == nil || f.impl == nil {
  374. return nil
  375. }
  376. curr_off := seek(f, 0, .Current) or_return
  377. defer seek(f, curr_off, .Start)
  378. seek(f, size, .Start) or_return
  379. handle := _handle(f)
  380. if !win32.SetEndOfFile(handle) {
  381. return _get_platform_error()
  382. }
  383. return nil
  384. }
  385. _remove :: proc(name: string) -> Error {
  386. TEMP_ALLOCATOR_GUARD()
  387. p := _fix_long_path(name, temp_allocator()) or_return
  388. err, err1: Error
  389. if !win32.DeleteFileW(p) {
  390. err = _get_platform_error()
  391. }
  392. if err == nil {
  393. return nil
  394. }
  395. if !win32.RemoveDirectoryW(p) {
  396. err1 = _get_platform_error()
  397. }
  398. if err1 == nil {
  399. return nil
  400. }
  401. if err != err1 {
  402. a := win32.GetFileAttributesW(p)
  403. if a == win32.INVALID_FILE_ATTRIBUTES {
  404. err = _get_platform_error()
  405. } else {
  406. if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
  407. err = err1
  408. } else if a & win32.FILE_ATTRIBUTE_READONLY != 0 {
  409. if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) {
  410. err = nil
  411. if !win32.DeleteFileW(p) {
  412. err = _get_platform_error()
  413. }
  414. }
  415. }
  416. }
  417. }
  418. return err
  419. }
  420. _rename :: proc(old_path, new_path: string) -> Error {
  421. TEMP_ALLOCATOR_GUARD()
  422. from := _fix_long_path(old_path, temp_allocator()) or_return
  423. to := _fix_long_path(new_path, temp_allocator()) or_return
  424. if win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
  425. return nil
  426. }
  427. return _get_platform_error()
  428. }
  429. _link :: proc(old_name, new_name: string) -> Error {
  430. TEMP_ALLOCATOR_GUARD()
  431. o := _fix_long_path(old_name, temp_allocator()) or_return
  432. n := _fix_long_path(new_name, temp_allocator()) or_return
  433. if win32.CreateHardLinkW(n, o, nil) {
  434. return nil
  435. }
  436. return _get_platform_error()
  437. }
  438. _symlink :: proc(old_name, new_name: string) -> Error {
  439. return .Unsupported
  440. }
  441. _open_sym_link :: proc(p: [^]u16) -> (handle: win32.HANDLE, err: Error) {
  442. attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS)
  443. attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
  444. handle = win32.CreateFileW(p, 0, 0, nil, win32.OPEN_EXISTING, attrs, nil)
  445. if handle == win32.INVALID_HANDLE {
  446. return nil, _get_platform_error()
  447. }
  448. return
  449. }
  450. _normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: string, err: Error) {
  451. has_prefix :: proc(p: []u16, str: string) -> bool {
  452. if len(p) < len(str) {
  453. return false
  454. }
  455. // assume ascii
  456. for i in 0..<len(str) {
  457. if p[i] != u16(str[i]) {
  458. return false
  459. }
  460. }
  461. return true
  462. }
  463. has_unc_prefix :: proc(p: []u16) -> bool {
  464. return has_prefix(p, `\??\`)
  465. }
  466. if !has_unc_prefix(p) {
  467. return win32_utf16_to_utf8(p, allocator)
  468. }
  469. ws := p[4:]
  470. switch {
  471. case len(ws) >= 2 && ws[1] == ':':
  472. return win32_utf16_to_utf8(ws, allocator)
  473. case has_prefix(ws, `UNC\`):
  474. ws[3] = '\\' // override data in buffer
  475. return win32_utf16_to_utf8(ws[3:], allocator)
  476. }
  477. handle := _open_sym_link(raw_data(p)) or_return
  478. defer win32.CloseHandle(handle)
  479. n := win32.GetFinalPathNameByHandleW(handle, nil, 0, win32.VOLUME_NAME_DOS)
  480. if n == 0 {
  481. return "", _get_platform_error()
  482. }
  483. TEMP_ALLOCATOR_GUARD()
  484. buf := make([]u16, n+1, temp_allocator())
  485. n = win32.GetFinalPathNameByHandleW(handle, raw_data(buf), u32(len(buf)), win32.VOLUME_NAME_DOS)
  486. if n == 0 {
  487. return "", _get_platform_error()
  488. }
  489. ws = buf[:n]
  490. if has_unc_prefix(ws) {
  491. ws = ws[4:]
  492. if len(ws) > 3 && has_prefix(ws, `UNC`) {
  493. ws[2] = '\\'
  494. return win32_utf16_to_utf8(ws[2:], allocator)
  495. }
  496. return win32_utf16_to_utf8(ws, allocator)
  497. }
  498. return "", .Invalid_Path
  499. }
  500. _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
  501. MAXIMUM_REPARSE_DATA_BUFFER_SIZE :: 16 * 1024
  502. @thread_local
  503. rdb_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
  504. TEMP_ALLOCATOR_GUARD()
  505. p := _fix_long_path(name, temp_allocator()) or_return
  506. handle := _open_sym_link(p) or_return
  507. defer win32.CloseHandle(handle)
  508. bytes_returned: u32
  509. if !win32.DeviceIoControl(handle, win32.FSCTL_GET_REPARSE_POINT, nil, 0, &rdb_buf[0], len(rdb_buf)-1, &bytes_returned, nil) {
  510. err = _get_platform_error()
  511. return
  512. }
  513. mem.zero_slice(rdb_buf[:min(bytes_returned+1, len(rdb_buf))])
  514. rdb := (^win32.REPARSE_DATA_BUFFER)(&rdb_buf[0])
  515. switch rdb.ReparseTag {
  516. case win32.IO_REPARSE_TAG_SYMLINK:
  517. rb := (^win32.SYMBOLIC_LINK_REPARSE_BUFFER)(&rdb.rest)
  518. pb := win32.wstring(&rb.PathBuffer)
  519. pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
  520. p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
  521. if rb.Flags & win32.SYMLINK_FLAG_RELATIVE != 0 {
  522. return win32_utf16_to_utf8(p, allocator)
  523. }
  524. return _normalize_link_path(p, allocator)
  525. case win32.IO_REPARSE_TAG_MOUNT_POINT:
  526. rb := (^win32.MOUNT_POINT_REPARSE_BUFFER)(&rdb.rest)
  527. pb := win32.wstring(&rb.PathBuffer)
  528. pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
  529. p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
  530. return _normalize_link_path(p, allocator)
  531. }
  532. // Path wasn't a symlink/junction but another reparse point kind
  533. return "", nil
  534. }
  535. _fchdir :: proc(f: ^File) -> Error {
  536. if f == nil || f.impl == nil {
  537. return nil
  538. }
  539. impl := (^File_Impl)(f.impl)
  540. if !win32.SetCurrentDirectoryW(impl.wname) {
  541. return _get_platform_error()
  542. }
  543. return nil
  544. }
  545. _fchmod :: proc(f: ^File, mode: int) -> Error {
  546. if f == nil || f.impl == nil {
  547. return nil
  548. }
  549. d: win32.BY_HANDLE_FILE_INFORMATION
  550. if !win32.GetFileInformationByHandle(_handle(f), &d) {
  551. return _get_platform_error()
  552. }
  553. attrs := d.dwFileAttributes
  554. if mode & S_IWRITE != 0 {
  555. attrs &~= win32.FILE_ATTRIBUTE_READONLY
  556. } else {
  557. attrs |= win32.FILE_ATTRIBUTE_READONLY
  558. }
  559. info: win32.FILE_BASIC_INFO
  560. info.FileAttributes = attrs
  561. if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) {
  562. return _get_platform_error()
  563. }
  564. return nil
  565. }
  566. _fchown :: proc(f: ^File, uid, gid: int) -> Error {
  567. return .Unsupported
  568. }
  569. _chdir :: proc(name: string) -> Error {
  570. TEMP_ALLOCATOR_GUARD()
  571. p := _fix_long_path(name, temp_allocator()) or_return
  572. if !win32.SetCurrentDirectoryW(p) {
  573. return _get_platform_error()
  574. }
  575. return nil
  576. }
  577. _chmod :: proc(name: string, mode: int) -> Error {
  578. f := open(name, {.Write}) or_return
  579. defer close(f)
  580. return _fchmod(f, mode)
  581. }
  582. _chown :: proc(name: string, uid, gid: int) -> Error {
  583. return .Unsupported
  584. }
  585. _lchown :: proc(name: string, uid, gid: int) -> Error {
  586. return .Unsupported
  587. }
  588. _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
  589. f := open(name, {.Write}) or_return
  590. defer close(f)
  591. return _fchtimes(f, atime, mtime)
  592. }
  593. _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
  594. if f == nil || f.impl == nil {
  595. return nil
  596. }
  597. d: win32.BY_HANDLE_FILE_INFORMATION
  598. if !win32.GetFileInformationByHandle(_handle(f), &d) {
  599. return _get_platform_error()
  600. }
  601. to_windows_time :: #force_inline proc(t: time.Time) -> win32.LARGE_INTEGER {
  602. // a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC)
  603. return win32.LARGE_INTEGER(time.time_to_unix_nano(t) * 100 + 116444736000000000)
  604. }
  605. atime, mtime := atime, mtime
  606. if time.time_to_unix_nano(atime) < time.time_to_unix_nano(mtime) {
  607. atime = mtime
  608. }
  609. info: win32.FILE_BASIC_INFO
  610. info.LastAccessTime = to_windows_time(atime)
  611. info.LastWriteTime = to_windows_time(mtime)
  612. if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) {
  613. return _get_platform_error()
  614. }
  615. return nil
  616. }
  617. _exists :: proc(path: string) -> bool {
  618. TEMP_ALLOCATOR_GUARD()
  619. wpath, _ := _fix_long_path(path, temp_allocator())
  620. attribs := win32.GetFileAttributesW(wpath)
  621. return attribs != win32.INVALID_FILE_ATTRIBUTES
  622. }
  623. @(private="package")
  624. _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
  625. f := (^File_Impl)(stream_data)
  626. ferr: Error
  627. switch mode {
  628. case .Read:
  629. n, ferr = _read(f, p)
  630. err = error_to_io_error(ferr)
  631. return
  632. case .Read_At:
  633. n, ferr = _read_at(f, p, offset)
  634. err = error_to_io_error(ferr)
  635. return
  636. case .Write:
  637. n, ferr = _write(f, p)
  638. err = error_to_io_error(ferr)
  639. return
  640. case .Write_At:
  641. n, ferr = _write_at(f, p, offset)
  642. err = error_to_io_error(ferr)
  643. return
  644. case .Seek:
  645. n, ferr = _seek(f, offset, whence)
  646. err = error_to_io_error(ferr)
  647. return
  648. case .Size:
  649. n, ferr = _file_size(f)
  650. err = error_to_io_error(ferr)
  651. return
  652. case .Flush:
  653. ferr = _flush(f)
  654. err = error_to_io_error(ferr)
  655. return
  656. case .Close, .Destroy:
  657. ferr = _close(f)
  658. err = error_to_io_error(ferr)
  659. return
  660. case .Query:
  661. return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Query})
  662. }
  663. return 0, .Empty
  664. }
  665. @(private="package", require_results)
  666. win32_utf8_to_wstring :: proc(s: string, allocator: runtime.Allocator) -> (ws: [^]u16, err: runtime.Allocator_Error) {
  667. ws = raw_data(win32_utf8_to_utf16(s, allocator) or_return)
  668. return
  669. }
  670. @(private="package", require_results)
  671. win32_utf8_to_utf16 :: proc(s: string, allocator: runtime.Allocator) -> (ws: []u16, err: runtime.Allocator_Error) {
  672. if len(s) < 1 {
  673. return
  674. }
  675. b := transmute([]byte)s
  676. cstr := raw_data(b)
  677. n := win32.MultiByteToWideChar(win32.CP_UTF8, win32.MB_ERR_INVALID_CHARS, cstr, i32(len(s)), nil, 0)
  678. if n == 0 {
  679. return nil, nil
  680. }
  681. text := make([]u16, n+1, allocator) or_return
  682. n1 := win32.MultiByteToWideChar(win32.CP_UTF8, win32.MB_ERR_INVALID_CHARS, cstr, i32(len(s)), raw_data(text), n)
  683. if n1 == 0 {
  684. delete(text, allocator)
  685. return
  686. }
  687. text[n] = 0
  688. for n >= 1 && text[n-1] == 0 {
  689. n -= 1
  690. }
  691. ws = text[:n]
  692. return
  693. }
  694. @(private="package", require_results)
  695. win32_wstring_to_utf8 :: proc(s: [^]u16, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) {
  696. if s == nil || s[0] == 0 {
  697. return "", nil
  698. }
  699. n := 0
  700. for s[n] != 0 {
  701. n += 1
  702. }
  703. return win32_utf16_to_utf8(s[:n], allocator)
  704. }
  705. @(private="package", require_results)
  706. win32_utf16_to_utf8 :: proc(s: []u16, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) {
  707. if len(s) == 0 {
  708. return
  709. }
  710. n := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, raw_data(s), i32(len(s)), nil, 0, nil, nil)
  711. if n == 0 {
  712. return
  713. }
  714. // If N < 0 the call to WideCharToMultiByte assume the wide string is null terminated
  715. // and will scan it to find the first null terminated character. The resulting string will
  716. // also be null terminated.
  717. // If N > 0 it assumes the wide string is not null terminated and the resulting string
  718. // will not be null terminated.
  719. text := make([]byte, n, allocator) or_return
  720. n1 := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, raw_data(s), i32(len(s)), raw_data(text), n, nil, nil)
  721. if n1 == 0 {
  722. delete(text, allocator)
  723. return
  724. }
  725. for i in 0..<n {
  726. if text[i] == 0 {
  727. n = i
  728. break
  729. }
  730. }
  731. res = string(text[:n])
  732. return
  733. }