path_windows.odin 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. //+private
  2. package os2
  3. import win32 "core:sys/windows"
  4. import "base:runtime"
  5. import "core:strings"
  6. _Path_Separator :: '\\'
  7. _Path_List_Separator :: ';'
  8. _is_path_separator :: proc(c: byte) -> bool {
  9. return c == '\\' || c == '/'
  10. }
  11. _mkdir :: proc(name: string, perm: File_Mode) -> Error {
  12. if !win32.CreateDirectoryW(_fix_long_path(name), nil) {
  13. return _get_platform_error()
  14. }
  15. return nil
  16. }
  17. _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
  18. fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) {
  19. if len(p) == len(`\\?\c:`) {
  20. if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' {
  21. s = strings.concatenate({p, `\`}, _file_allocator()) or_return
  22. allocated = true
  23. return
  24. }
  25. }
  26. return p, false, nil
  27. }
  28. _TEMP_ALLOCATOR_GUARD()
  29. dir, err := stat(path, _temp_allocator())
  30. if err == nil {
  31. if dir.is_directory {
  32. return nil
  33. }
  34. return .Exist
  35. }
  36. i := len(path)
  37. for i > 0 && is_path_separator(path[i-1]) {
  38. i -= 1
  39. }
  40. j := i
  41. for j > 0 && !is_path_separator(path[j-1]) {
  42. j -= 1
  43. }
  44. if j > 1 {
  45. new_path, allocated := fix_root_directory(path[:j-1]) or_return
  46. defer if allocated {
  47. delete(new_path, _file_allocator())
  48. }
  49. mkdir_all(new_path, perm) or_return
  50. }
  51. err = mkdir(path, perm)
  52. if err != nil {
  53. dir1, err1 := lstat(path, _temp_allocator())
  54. if err1 == nil && dir1.is_directory {
  55. return nil
  56. }
  57. return err
  58. }
  59. return nil
  60. }
  61. _remove_all :: proc(path: string) -> Error {
  62. // TODO(bill): _remove_all for windows
  63. return nil
  64. }
  65. _getwd :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
  66. // TODO(bill)
  67. return "", nil
  68. }
  69. _setwd :: proc(dir: string) -> (err: Error) {
  70. // TODO(bill)
  71. return nil
  72. }
  73. can_use_long_paths: bool
  74. @(init)
  75. init_long_path_support :: proc() {
  76. // TODO(bill): init_long_path_support
  77. // ADD THIS SHIT
  78. // registry_path := win32.L(`Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled`)
  79. can_use_long_paths = false
  80. }
  81. _fix_long_path_slice :: proc(path: string) -> []u16 {
  82. return win32.utf8_to_utf16(_fix_long_path_internal(path))
  83. }
  84. _fix_long_path :: proc(path: string) -> win32.wstring {
  85. return win32.utf8_to_wstring(_fix_long_path_internal(path))
  86. }
  87. _fix_long_path_internal :: proc(path: string) -> string {
  88. if can_use_long_paths {
  89. return path
  90. }
  91. // When using win32 to create a directory, the path
  92. // cannot be too long that you cannot append an 8.3
  93. // file name, because MAX_PATH is 260, 260-12 = 248
  94. if len(path) < 248 {
  95. return path
  96. }
  97. // UNC paths do not need to be modified
  98. if len(path) >= 2 && path[:2] == `\\` {
  99. return path
  100. }
  101. if !_is_abs(path) { // relative path
  102. return path
  103. }
  104. _TEMP_ALLOCATOR_GUARD()
  105. PREFIX :: `\\?`
  106. path_buf := make([]byte, len(PREFIX)+len(path)+1, _temp_allocator())
  107. copy(path_buf, PREFIX)
  108. n := len(path)
  109. r, w := 0, len(PREFIX)
  110. for r < n {
  111. switch {
  112. case is_path_separator(path[r]):
  113. r += 1
  114. case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])):
  115. // \.\
  116. r += 1
  117. case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])):
  118. // Skip \..\ paths
  119. return path
  120. case:
  121. path_buf[w] = '\\'
  122. w += 1
  123. for r < n && !is_path_separator(path[r]) {
  124. path_buf[w] = path[r]
  125. r += 1
  126. w += 1
  127. }
  128. }
  129. }
  130. // Root directories require a trailing \
  131. if w == len(`\\?\c:`) {
  132. path_buf[w] = '\\'
  133. w += 1
  134. }
  135. return string(path_buf[:w])
  136. }