123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- //+private
- package os2
- import win32 "core:sys/windows"
- import "base:runtime"
- _Path_Separator :: '\\'
- _Path_Separator_String :: "\\"
- _Path_List_Separator :: ';'
- _is_path_separator :: proc(c: byte) -> bool {
- return c == '\\' || c == '/'
- }
- _mkdir :: proc(name: string, perm: int) -> Error {
- TEMP_ALLOCATOR_GUARD()
- if !win32.CreateDirectoryW(_fix_long_path(name, temp_allocator()) or_return, nil) {
- return _get_platform_error()
- }
- return nil
- }
- _mkdir_all :: proc(path: string, perm: int) -> Error {
- fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) {
- if len(p) == len(`\\?\c:`) {
- if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' {
- s = concatenate({p, `\`}, file_allocator()) or_return
- allocated = true
- return
- }
- }
- return p, false, nil
- }
- TEMP_ALLOCATOR_GUARD()
- dir_stat, err := stat(path, temp_allocator())
- if err == nil {
- if dir_stat.type == .Directory {
- return nil
- }
- return .Exist
- }
- i := len(path)
- for i > 0 && is_path_separator(path[i-1]) {
- i -= 1
- }
- j := i
- for j > 0 && !is_path_separator(path[j-1]) {
- j -= 1
- }
- if j > 1 {
- new_path, allocated := fix_root_directory(path[:j-1]) or_return
- defer if allocated {
- delete(new_path, file_allocator())
- }
- mkdir_all(new_path, perm) or_return
- }
- err = mkdir(path, perm)
- if err != nil {
- new_dir_stat, err1 := lstat(path, temp_allocator())
- if err1 == nil && new_dir_stat.type == .Directory {
- return nil
- }
- return err
- }
- return nil
- }
- _remove_all :: proc(path: string) -> Error {
- if path == "" {
- return nil
- }
- err := remove(path)
- if err == nil || err == .Not_Exist {
- return nil
- }
- TEMP_ALLOCATOR_GUARD()
- dir := win32_utf8_to_wstring(path, temp_allocator()) or_return
- empty: [1]u16
- file_op := win32.SHFILEOPSTRUCTW {
- nil,
- win32.FO_DELETE,
- dir,
- &empty[0],
- win32.FOF_NOCONFIRMATION | win32.FOF_NOERRORUI | win32.FOF_SILENT,
- false,
- nil,
- &empty[0],
- }
- res := win32.SHFileOperationW(&file_op)
- if res != 0 {
- return _get_platform_error()
- }
- return nil
- }
- @private cwd_lock: win32.SRWLOCK // zero is initialized
- _get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
- win32.AcquireSRWLockExclusive(&cwd_lock)
- TEMP_ALLOCATOR_GUARD()
- sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
- dir_buf_wstr := make([]u16, sz_utf16, temp_allocator()) or_return
- sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr))
- assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL.
- win32.ReleaseSRWLockExclusive(&cwd_lock)
- return win32_utf16_to_utf8(dir_buf_wstr, allocator)
- }
- _set_working_directory :: proc(dir: string) -> (err: Error) {
- TEMP_ALLOCATOR_GUARD()
- wstr := win32_utf8_to_wstring(dir, temp_allocator()) or_return
- win32.AcquireSRWLockExclusive(&cwd_lock)
- if !win32.SetCurrentDirectoryW(wstr) {
- err = _get_platform_error()
- }
- win32.ReleaseSRWLockExclusive(&cwd_lock)
- return
- }
- can_use_long_paths: bool
- @(init)
- init_long_path_support :: proc() {
- can_use_long_paths = false
- key: win32.HKEY
- res := win32.RegOpenKeyExW(win32.HKEY_LOCAL_MACHINE, win32.L(`SYSTEM\CurrentControlSet\Control\FileSystem`), 0, win32.KEY_READ, &key)
- defer win32.RegCloseKey(key)
- if res != 0 {
- return
- }
- value: u32
- size := u32(size_of(value))
- res = win32.RegGetValueW(
- key,
- nil,
- win32.L("LongPathsEnabled"),
- win32.RRF_RT_ANY,
- nil,
- &value,
- &size,
- )
- if res != 0 {
- return
- }
- if value == 1 {
- can_use_long_paths = true
- }
- }
- @(require_results)
- _fix_long_path_slice :: proc(path: string, allocator: runtime.Allocator) -> ([]u16, runtime.Allocator_Error) {
- return win32_utf8_to_utf16(_fix_long_path_internal(path), allocator)
- }
- @(require_results)
- _fix_long_path :: proc(path: string, allocator: runtime.Allocator) -> (win32.wstring, runtime.Allocator_Error) {
- return win32_utf8_to_wstring(_fix_long_path_internal(path), allocator)
- }
- @(require_results)
- _fix_long_path_internal :: proc(path: string) -> string {
- if can_use_long_paths {
- return path
- }
- // When using win32 to create a directory, the path
- // cannot be too long that you cannot append an 8.3
- // file name, because MAX_PATH is 260, 260-12 = 248
- if len(path) < 248 {
- return path
- }
- // UNC paths do not need to be modified
- if len(path) >= 2 && path[:2] == `\\` {
- return path
- }
- if !_is_abs(path) { // relative path
- return path
- }
- TEMP_ALLOCATOR_GUARD()
- PREFIX :: `\\?`
- path_buf := make([]byte, len(PREFIX)+len(path)+1, temp_allocator())
- copy(path_buf, PREFIX)
- n := len(path)
- r, w := 0, len(PREFIX)
- for r < n {
- switch {
- case is_path_separator(path[r]):
- r += 1
- case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])):
- // \.\
- r += 1
- case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])):
- // Skip \..\ paths
- return path
- case:
- path_buf[w] = '\\'
- w += 1
- for r < n && !is_path_separator(path[r]) {
- path_buf[w] = path[r]
- r += 1
- w += 1
- }
- }
- }
- // Root directories require a trailing \
- if w == len(`\\?\c:`) {
- path_buf[w] = '\\'
- w += 1
- }
- return string(path_buf[:w])
- }
|