diff options
| author | gingerBill <bill@gingerbill.org> | 2020-09-26 16:02:03 +0100 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2020-09-26 16:02:03 +0100 |
| commit | 840af6825aa5a728bb7fab3be24edff34aa3d571 (patch) | |
| tree | 209fb558042e30cf1ea3d090c3a14ca3f320326e /core/path | |
| parent | 3ccaf47566f31d22daf3493f8fdcaea4fa825748 (diff) | |
Update packages os, path, and filepath
Diffstat (limited to 'core/path')
| -rw-r--r-- | core/path/filepath/path.odin | 160 | ||||
| -rw-r--r-- | core/path/filepath/path_unix.odin | 1 | ||||
| -rw-r--r-- | core/path/filepath/path_windows.odin | 106 | ||||
| -rw-r--r-- | core/path/path_unix.odin | 83 | ||||
| -rw-r--r-- | core/path/path_windows.odin | 124 |
5 files changed, 258 insertions, 216 deletions
diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin index c15ff110a..f2dbedf1f 100644 --- a/core/path/filepath/path.odin +++ b/core/path/filepath/path.odin @@ -42,6 +42,7 @@ volume_name_len :: proc(path: string) -> int { } } + // URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx if l := len(path); l >= 5 && is_slash(path[0]) && is_slash(path[1]) && !is_slash(path[2]) && path[2] != '.' { for n := 3; n < l-1; n += 1 { @@ -65,6 +66,31 @@ volume_name_len :: proc(path: string) -> int { return 0; } +base :: proc(path: string) -> string { + if path == "" { + return "."; + } + + path := path; + for len(path) > 0 && is_separator(path[len(path)-1]) { + path = path[:len(path)-1]; + } + + path = path[volume_name_len(path):]; + + i := len(path)-1; + for i >= 0 && !is_separator(path[i]) { + i -= 1; + } + if i >= 0 { + path = path[i+1:]; + } + if path == "" { + return SEPARATOR_STRING; + } + return path; +} + clean :: proc(path: string, allocator := context.allocator) -> string { context.allocator = allocator; @@ -76,7 +102,11 @@ clean :: proc(path: string, allocator := context.allocator) -> string { if path == "" { if vol_len > 1 && original_path[1] != ':' { - return from_slash(original_path); + s, ok := from_slash(original_path); + if !ok { + s = strings.clone(s); + } + return s; } return strings.concatenate({original_path, "."}); } @@ -134,19 +164,133 @@ clean :: proc(path: string, allocator := context.allocator) -> string { } s := lazy_buffer_string(out); - cleaned := from_slash(s); + cleaned, new_allocation := from_slash(s); + if new_allocation { + delete(s); + } return cleaned; } -from_slash :: proc(path: string, allocator := context.allocator) -> string { +from_slash :: proc(path: string, allocator := context.allocator) -> (new_path: string, new_allocation: bool) { if SEPARATOR == '/' { - return path; + return path, false; + } + return strings.replace_all(path, "/", SEPARATOR_STRING, allocator); +} + +to_slash :: proc(path: string, allocator := context.allocator) -> (new_path: string, new_allocation: bool) { + if SEPARATOR == '/' { + return path, false; + } + return strings.replace_all(path, SEPARATOR_STRING, "/", allocator); +} + +ext :: proc(path: string) -> string { + for i := len(path)-1; i >= 0 && !is_separator(path[i]); i -= 1 { + if path[i] == '.' { + return path[i:]; + } + } + return ""; +} + + +Relative_Error :: enum { + None, + + Cannot_Relate, +} + +rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (string, Relative_Error) { + context.allocator = allocator; + base_vol, target_vol := volume_name(base_path), volume_name(target_path); + base, target := clean(base_path), clean(target_path); + + delete_target := true; + defer { + if delete_target { + delete(target); + } + delete(base); + } + + if strings.equal_fold(target, base) { + return strings.clone("."), .None; + } + + base = base[len(base_vol):]; + target = target[len(target_vol):]; + if base == "." { + base = ""; + } + + base_slashed := len(base) > 0 && base[0] == SEPARATOR; + target_slashed := len(target) > 0 && target[0] == SEPARATOR; + if base_slashed != target_slashed || !strings.equal_fold(base_vol, target_vol) { + return "", .Cannot_Relate; + } + + bl, tl := len(base), len(target); + b0, bi, t0, ti: int; + for { + for bi < bl && base[bi] != SEPARATOR { + bi += 1; + } + for ti < tl && target[ti] != SEPARATOR { + ti += 1; + } + if !strings.equal_fold(target[t0:ti], base[t0:ti]) { + break; + } + if bi < bl { + bi += 1; + } + if ti < tl { + ti += 1; + } + b0, t0 = bi, ti; + } + + if base[b0:bi] == ".." { + return "", .Cannot_Relate; + } + + if b0 != bl { + seps := strings.count(base[b0:bl], SEPARATOR_STRING); + size := 2 + seps*3; + if tl != t0 { + size += 1 + tl - t0; + } + buf := make([]byte, size); + n := copy(buf, ".."); + for i in 0..<seps { + buf[n] = SEPARATOR; + copy(buf[n+1:], ".."); + n += 3; + } + if t0 != tl { + buf[n] = SEPARATOR; + copy(buf[n+1:], target[t0:]); + } + return string(buf), .None; + } + + delete_target = false; + return target[t0:], .None; +} + +dir :: proc(path: string, allocator := context.allocator) -> string { + vol := volume_name(path); + i := len(path) - 1; + for i >= len(vol) && is_separator(path[i]) { + i -= 1; } - s, ok := strings.replace_all(path, "/", SEPARATOR_STRING, allocator); - if !ok { - s = strings.clone(s, allocator); + dir := clean(path[len(vol) : i+1], allocator); + defer delete(dir, allocator); + if dir == "." && len(vol) > 2 { + return strings.clone(vol); } - return s; + return strings.concatenate({vol, dir}); } diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin index 7ffe6291e..c9d0e28f1 100644 --- a/core/path/filepath/path_unix.odin +++ b/core/path/filepath/path_unix.odin @@ -3,3 +3,4 @@ package filepath SEPARATOR :: '/'; SEPARATOR_STRING :: `/`; +LIST_SEPARATOR :: ':'; diff --git a/core/path/filepath/path_windows.odin b/core/path/filepath/path_windows.odin index 604859ca8..e413133f8 100644 --- a/core/path/filepath/path_windows.odin +++ b/core/path/filepath/path_windows.odin @@ -1,10 +1,12 @@ package filepath import "core:strings" +import "core:os" +import win32 "core:sys/windows" SEPARATOR :: '\\'; SEPARATOR_STRING :: `\`; - +LIST_SEPARATOR :: ';'; reserved_names := []string{ "CON", "PRN", "AUX", "NUL", @@ -28,6 +30,108 @@ is_UNC :: proc(path: string) -> bool { return volume_name_len(path) > 2; } + +is_abs :: proc(path: string) -> bool { + if is_reserved_name(path) { + return true; + } + l := volume_name_len(path); + if l == 0 { + return false; + } + + path := path[l:]; + if path == "" { + return false; + } + return is_slash(path[0]); +} + + +@(private) +full_path :: proc(name: string, allocator := context.allocator) -> (path: string, err: os.Errno) { + name := name; + if name == "" { + name = "."; + } + p := win32.utf8_to_utf16(name, context.temp_allocator); + defer delete(p); + buf := make([dynamic]u16, 100, allocator); + for { + n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil); + if n == 0 { + delete(buf); + return "", os.Errno(win32.GetLastError()); + } + if n <= u32(len(buf)) { + return win32.utf16_to_utf8(buf[:n]), os.ERROR_NONE; + } + resize(&buf, len(buf)*2); + } +} + + + + +abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { + full_path, err := full_path(path, context.temp_allocator); + if err != 0 { + return "", false; + } + return clean(full_path, allocator), true; +} + +split_list :: proc(path: string, allocator := context.allocator) -> []string { + if path == "" { + return nil; + } + + start: int; + quote: bool; + + start, quote = 0, false; + count := 0; + + for i := 0; i < len(path); i += 1 { + c := path[i]; + switch { + case c == '"': + quote = !quote; + case c == LIST_SEPARATOR && !quote: + count += 1; + } + } + + start, quote = 0, false; + list := make([]string, count, allocator); + index := 0; + for i := 0; i < len(path); i += 1 { + c := path[i]; + switch { + case c == '"': + quote = !quote; + case c == LIST_SEPARATOR && !quote: + list[index] = path[start:i]; + index += 1; + start = i + 1; + } + } + assert(index == count); + + for s, i in list { + s, new := strings.replace_all(s, `"`, ``, allocator); + if !new { + s = strings.clone(s, allocator); + } + list[i] = s; + } + + return list; +} + + + + join :: proc(elems: ..string, allocator := context.allocator) -> string { for e, i in elems { if e != "" { diff --git a/core/path/path_unix.odin b/core/path/path_unix.odin deleted file mode 100644 index b380d8187..000000000 --- a/core/path/path_unix.odin +++ /dev/null @@ -1,83 +0,0 @@ -//+build linux, darwin, freebsd -package path - -foreign import libc "system:c" - -import "core:os" -import "core:strings" - - -MAX :: 4096; // @note(bp): apparently PATH_MAX is bullshit - -OS_SEPARATOR :: '/'; -OS_SEPARATOR_STRING :: "/"; - -OS_SEPARATORS :: `/`; -OS_SEPARATORS_ARRAY :: []string{`/`}; - -@(private) -null_term :: proc(str: string) -> string { - for c, i in str { - if c == '\x00' { - return str[:i]; - } - } - return str; -} - - -full :: proc(path: string, allocator := context.temp_allocator) -> string { - cpath := strings.clone_to_cstring(path, context.temp_allocator); - - foreign libc { - realpath :: proc(path: cstring, resolved_path: ^u8) -> cstring ---; - } - - buf := make([dynamic]u8, MAX, MAX, allocator); - - cstr := realpath(cpath, &buf[0]); - for cstr == nil && os.get_last_error() == int(os.ENAMETOOLONG) { - resize(&buf, len(buf) + MAX); - cstr = realpath(cpath, &buf[0]); - } - - return null_term(string(buf[:])); -} - -current :: proc(allocator := context.temp_allocator) -> string { - foreign libc{ - getcwd :: proc(buf: ^u8, size: int) -> cstring ---; - } - - buf := make([dynamic]u8, MAX, MAX, allocator); - - cstr := getcwd(&buf[0], len(buf)); - for cstr == nil && os.get_last_error() == int(os.ENAMETOOLONG) { - resize(&buf, len(buf) + MAX); - cstr = getcwd(&buf[0], len(buf)); - } - - return null_term(string(buf[:])); -} - - -exists :: proc(path: string) -> bool { - if _, err := os.stat(path); err != 0 { - return true; - } - return false; -} - -is_dir :: proc(path: string) -> bool { - if stat, err := os.stat(path); err != 0 { - return os.S_ISDIR(u32(stat.mode)); - } - return false; -} - -is_file :: proc(path: string) -> bool { - if stat, err := os.stat(path); err != 0 { - return os.S_ISREG(u32(stat.mode)); - } - return false; -} diff --git a/core/path/path_windows.odin b/core/path/path_windows.odin deleted file mode 100644 index bc0360880..000000000 --- a/core/path/path_windows.odin +++ /dev/null @@ -1,124 +0,0 @@ -package path
-
-import "core:strings"
-import win32 "core:sys/windows"
-
-
-OS_SEPARATOR :: '\\';
-OS_SEPARATOR_STRING :: "\\";
-
-OS_SEPARATORS :: `/\`;
-OS_SEPARATORS_ARRAY :: []string{`/`, `\`};
-
-@(private)
-null_term :: proc "contextless" (str: string) -> string {
- for c, i in str {
- if c == '\x00' {
- return str[:i];
- }
- }
- return str;
-}
-
-
-long :: proc(path: string, allocator := context.temp_allocator) -> string {
- c_path := win32.utf8_to_wstring(path, context.temp_allocator);
- length := win32.GetLongPathNameW(c_path, nil, 0);
-
- if length == 0 {
- return "";
- }
-
- buf := make([]u16, length, context.temp_allocator);
-
- win32.GetLongPathNameW(c_path, win32.LPCWSTR(&buf[0]), length);
-
- res := win32.utf16_to_utf8(buf[:length], allocator);
-
- return null_term(res);
-}
-
-short :: proc(path: string, allocator := context.temp_allocator) -> string {
- c_path := win32.utf8_to_wstring(path, context.temp_allocator);
- length := win32.GetShortPathNameW(c_path, nil, 0);
-
- if length == 0 {
- return "";
- }
-
- buf := make([]u16, length, context.temp_allocator);
-
- win32.GetShortPathNameW(c_path, win32.LPCWSTR(&buf[0]), length);
-
- res := win32.utf16_to_utf8(buf[:length], allocator);
-
- return null_term(res);
-}
-
-full :: proc(path: string, allocator := context.temp_allocator) -> string {
- c_path := win32.utf8_to_wstring(path, context.temp_allocator);
- length := win32.GetFullPathNameW(c_path, 0, nil, nil);
-
- if length == 0 {
- return "";
- }
-
- buf := make([]u16, length, context.temp_allocator);
-
- win32.GetFullPathNameW(c_path, length, win32.LPCWSTR(&buf[0]), nil);
-
- res := win32.utf16_to_utf8(buf[:length], allocator);
-
- return null_term(res);
-}
-
-current :: proc(allocator := context.temp_allocator) -> string {
- length := win32.GetCurrentDirectoryW(0, nil);
-
- if length == 0 {
- return "";
- }
-
- buf := make([]u16, length, context.temp_allocator);
-
- win32.GetCurrentDirectoryW(length, win32.LPCWSTR(&buf[0]));
-
- res := win32.utf16_to_utf8(buf[:length], allocator);
-
- return strings.trim_null(res);
-}
-
-
-exists :: proc(path: string) -> bool {
- c_path := win32.utf8_to_wstring(path, context.temp_allocator);
- attribs := win32.GetFileAttributesW(c_path);
-
- return i32(attribs) != win32.INVALID_FILE_ATTRIBUTES;
-}
-
-is_dir :: proc(path: string) -> bool {
- c_path := win32.utf8_to_wstring(path, context.temp_allocator);
- attribs := win32.GetFileAttributesW(c_path);
-
- return (i32(attribs) != win32.INVALID_FILE_ATTRIBUTES) && (attribs & win32.FILE_ATTRIBUTE_DIRECTORY == win32.FILE_ATTRIBUTE_DIRECTORY);
-}
-
-is_file :: proc(path: string) -> bool {
- c_path := win32.utf8_to_wstring(path, context.temp_allocator);
- attribs := win32.GetFileAttributesW(c_path);
-
- return (i32(attribs) != win32.INVALID_FILE_ATTRIBUTES) && (attribs & win32.FILE_ATTRIBUTE_DIRECTORY != win32.FILE_ATTRIBUTE_DIRECTORY);
-}
-
-
-drive :: proc(path: string, new := false, allocator := context.allocator) -> string {
- if len(path) >= 3 {
- letter := path[:2];
-
- if path[1] == ':' && (path[2] == '\\' || path[2] == '/') {
- return new ? strings.clone(path[:2], allocator) : path[:2];
- }
- }
-
- return "";
-}
|