123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- #+private
- package os2
- import "base:runtime"
- import "core:strings"
- import win32 "core:sys/windows"
- _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 := 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 := 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 := 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,
- cstring16(&empty[0]),
- win32.FOF_NOCONFIRMATION | win32.FOF_NOERRORUI | win32.FOF_SILENT,
- false,
- nil,
- cstring16(&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 := TEMP_ALLOCATOR_GUARD({ allocator })
- 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 := 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
- }
- _get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
- temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
- buf := make([dynamic]u16, 512, temp_allocator) or_return
- for {
- ret := win32.GetModuleFileNameW(nil, raw_data(buf), win32.DWORD(len(buf)))
- if ret == 0 {
- err = _get_platform_error()
- return
- }
- if ret == win32.DWORD(len(buf)) && win32.GetLastError() == win32.ERROR_INSUFFICIENT_BUFFER {
- resize(&buf, len(buf)*2) or_return
- continue
- }
- return win32_utf16_to_utf8(buf[:ret], allocator)
- }
- }
- can_use_long_paths: bool
- @(init)
- init_long_path_support :: proc "contextless" () {
- 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_absolute_path(path) { // relative path
- return path
- }
- temp_allocator := 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])
- }
- _are_paths_identical :: strings.equal_fold
- _clean_path_handle_start :: proc(path: string, buffer: []u8) -> (rooted: bool, start: int) {
- // Preserve rooted paths.
- start = _volume_name_len(path)
- if start > 0 {
- rooted = true
- if len(path) > start && _is_path_separator(path[start]) {
- // Take `C:` to `C:\`.
- start += 1
- }
- copy(buffer, path[:start])
- for n in 0..<start {
- if _is_path_separator(buffer[n]) {
- buffer[n] = _Path_Separator
- }
- }
- }
- return
- }
- _is_absolute_path :: proc(path: string) -> bool {
- if _is_reserved_name(path) {
- return true
- }
- l := _volume_name_len(path)
- if l == 0 {
- return false
- }
- path := path
- path = path[l:]
- if path == "" {
- return false
- }
- return _is_path_separator(path[0])
- }
- _get_absolute_path :: proc(path: string, allocator: runtime.Allocator) -> (absolute_path: string, err: Error) {
- rel := path
- if rel == "" {
- rel = "."
- }
- temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
- rel_utf16 := win32.utf8_to_utf16(rel, temp_allocator)
- n := win32.GetFullPathNameW(cstring16(raw_data(rel_utf16)), 0, nil, nil)
- if n == 0 {
- return "", Platform_Error(win32.GetLastError())
- }
- buf := make([]u16, n, temp_allocator) or_return
- n = win32.GetFullPathNameW(cstring16(raw_data(rel_utf16)), u32(n), cstring16(raw_data(buf)), nil)
- if n == 0 {
- return "", Platform_Error(win32.GetLastError())
- }
- return win32.utf16_to_utf8(buf, allocator)
- }
- _get_relative_path_handle_start :: proc(base, target: string) -> bool {
- base_root := base[:_volume_name_len(base)]
- target_root := target[:_volume_name_len(target)]
- return strings.equal_fold(base_root, target_root)
- }
- _get_common_path_len :: proc(base, target: string) -> int {
- i := 0
- end := min(len(base), len(target))
- for j in 0..=end {
- if j == end || _is_path_separator(base[j]) {
- if strings.equal_fold(base[i:j], target[i:j]) {
- i = j
- } else {
- break
- }
- }
- }
- return i
- }
- _split_path :: proc(path: string) -> (dir, file: string) {
- vol_len := _volume_name_len(path)
- i := len(path) - 1
- for i >= vol_len && !_is_path_separator(path[i]) {
- i -= 1
- }
- if i == vol_len {
- return path[:i+1], path[i+1:]
- } else if i > vol_len {
- return path[:i], path[i+1:]
- }
- return "", path
- }
|