path_windows.odin 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. //+private
  2. package os2
  3. import win32 "core:sys/windows"
  4. import "base:runtime"
  5. _Path_Separator :: '\\'
  6. _Path_Separator_String :: "\\"
  7. _Path_List_Separator :: ';'
  8. _is_path_separator :: proc(c: byte) -> bool {
  9. return c == '\\' || c == '/'
  10. }
  11. _mkdir :: proc(name: string, perm: int) -> Error {
  12. TEMP_ALLOCATOR_GUARD()
  13. if !win32.CreateDirectoryW(_fix_long_path(name, temp_allocator()) or_return, nil) {
  14. return _get_platform_error()
  15. }
  16. return nil
  17. }
  18. _mkdir_all :: proc(path: string, perm: int) -> Error {
  19. fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) {
  20. if len(p) == len(`\\?\c:`) {
  21. if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' {
  22. s = concatenate({p, `\`}, file_allocator()) or_return
  23. allocated = true
  24. return
  25. }
  26. }
  27. return p, false, nil
  28. }
  29. TEMP_ALLOCATOR_GUARD()
  30. dir_stat, err := stat(path, temp_allocator())
  31. if err == nil {
  32. if dir_stat.type == .Directory {
  33. return nil
  34. }
  35. return .Exist
  36. }
  37. i := len(path)
  38. for i > 0 && is_path_separator(path[i-1]) {
  39. i -= 1
  40. }
  41. j := i
  42. for j > 0 && !is_path_separator(path[j-1]) {
  43. j -= 1
  44. }
  45. if j > 1 {
  46. new_path, allocated := fix_root_directory(path[:j-1]) or_return
  47. defer if allocated {
  48. delete(new_path, file_allocator())
  49. }
  50. mkdir_all(new_path, perm) or_return
  51. }
  52. err = mkdir(path, perm)
  53. if err != nil {
  54. new_dir_stat, err1 := lstat(path, temp_allocator())
  55. if err1 == nil && new_dir_stat.type == .Directory {
  56. return nil
  57. }
  58. return err
  59. }
  60. return nil
  61. }
  62. _remove_all :: proc(path: string) -> Error {
  63. if path == "" {
  64. return nil
  65. }
  66. err := remove(path)
  67. if err == nil || err == .Not_Exist {
  68. return nil
  69. }
  70. TEMP_ALLOCATOR_GUARD()
  71. dir := win32_utf8_to_wstring(path, temp_allocator()) or_return
  72. empty: [1]u16
  73. file_op := win32.SHFILEOPSTRUCTW {
  74. nil,
  75. win32.FO_DELETE,
  76. dir,
  77. &empty[0],
  78. win32.FOF_NOCONFIRMATION | win32.FOF_NOERRORUI | win32.FOF_SILENT,
  79. false,
  80. nil,
  81. &empty[0],
  82. }
  83. res := win32.SHFileOperationW(&file_op)
  84. if res != 0 {
  85. return _get_platform_error()
  86. }
  87. return nil
  88. }
  89. @private cwd_lock: win32.SRWLOCK // zero is initialized
  90. _get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
  91. win32.AcquireSRWLockExclusive(&cwd_lock)
  92. TEMP_ALLOCATOR_GUARD()
  93. sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
  94. dir_buf_wstr := make([]u16, sz_utf16, temp_allocator()) or_return
  95. sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr))
  96. assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL.
  97. win32.ReleaseSRWLockExclusive(&cwd_lock)
  98. return win32_utf16_to_utf8(dir_buf_wstr, allocator)
  99. }
  100. _set_working_directory :: proc(dir: string) -> (err: Error) {
  101. TEMP_ALLOCATOR_GUARD()
  102. wstr := win32_utf8_to_wstring(dir, temp_allocator()) or_return
  103. win32.AcquireSRWLockExclusive(&cwd_lock)
  104. if !win32.SetCurrentDirectoryW(wstr) {
  105. err = _get_platform_error()
  106. }
  107. win32.ReleaseSRWLockExclusive(&cwd_lock)
  108. return
  109. }
  110. can_use_long_paths: bool
  111. @(init)
  112. init_long_path_support :: proc() {
  113. can_use_long_paths = false
  114. key: win32.HKEY
  115. res := win32.RegOpenKeyExW(win32.HKEY_LOCAL_MACHINE, win32.L(`SYSTEM\CurrentControlSet\Control\FileSystem`), 0, win32.KEY_READ, &key)
  116. defer win32.RegCloseKey(key)
  117. if res != 0 {
  118. return
  119. }
  120. value: u32
  121. size := u32(size_of(value))
  122. res = win32.RegGetValueW(
  123. key,
  124. nil,
  125. win32.L("LongPathsEnabled"),
  126. win32.RRF_RT_ANY,
  127. nil,
  128. &value,
  129. &size,
  130. )
  131. if res != 0 {
  132. return
  133. }
  134. if value == 1 {
  135. can_use_long_paths = true
  136. }
  137. }
  138. @(require_results)
  139. _fix_long_path_slice :: proc(path: string, allocator: runtime.Allocator) -> ([]u16, runtime.Allocator_Error) {
  140. return win32_utf8_to_utf16(_fix_long_path_internal(path), allocator)
  141. }
  142. @(require_results)
  143. _fix_long_path :: proc(path: string, allocator: runtime.Allocator) -> (win32.wstring, runtime.Allocator_Error) {
  144. return win32_utf8_to_wstring(_fix_long_path_internal(path), allocator)
  145. }
  146. @(require_results)
  147. _fix_long_path_internal :: proc(path: string) -> string {
  148. if can_use_long_paths {
  149. return path
  150. }
  151. // When using win32 to create a directory, the path
  152. // cannot be too long that you cannot append an 8.3
  153. // file name, because MAX_PATH is 260, 260-12 = 248
  154. if len(path) < 248 {
  155. return path
  156. }
  157. // UNC paths do not need to be modified
  158. if len(path) >= 2 && path[:2] == `\\` {
  159. return path
  160. }
  161. if !_is_abs(path) { // relative path
  162. return path
  163. }
  164. TEMP_ALLOCATOR_GUARD()
  165. PREFIX :: `\\?`
  166. path_buf := make([]byte, len(PREFIX)+len(path)+1, temp_allocator())
  167. copy(path_buf, PREFIX)
  168. n := len(path)
  169. r, w := 0, len(PREFIX)
  170. for r < n {
  171. switch {
  172. case is_path_separator(path[r]):
  173. r += 1
  174. case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])):
  175. // \.\
  176. r += 1
  177. case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])):
  178. // Skip \..\ paths
  179. return path
  180. case:
  181. path_buf[w] = '\\'
  182. w += 1
  183. for r < n && !is_path_separator(path[r]) {
  184. path_buf[w] = path[r]
  185. r += 1
  186. w += 1
  187. }
  188. }
  189. }
  190. // Root directories require a trailing \
  191. if w == len(`\\?\c:`) {
  192. path_buf[w] = '\\'
  193. w += 1
  194. }
  195. return string(path_buf[:w])
  196. }