From 0c341123cb7cd07db56def2be23865550bdef75a Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 8 Feb 2026 12:56:00 +0100 Subject: [PATCH] More conflicts during rebase --- core/os/os2/path.odin | 28 ++-- core/os/os2/path_windows.odin | 1 + core/os/os2/stat.odin | 4 + core/os/os2/stat_js.odin | 4 + core/os/os2/stat_linux.odin | 4 + core/os/os2/stat_posix.odin | 4 + core/os/os2/stat_wasi.odin | 4 + core/path/filepath/path.odin | 186 ++++----------------------- core/path/filepath/path_js.odin | 16 --- core/path/filepath/path_unix.odin | 23 +--- core/path/filepath/path_wasi.odin | 18 +-- core/path/filepath/path_windows.odin | 72 +---------- core/time/timezone/tz_unix.odin | 12 +- 13 files changed, 75 insertions(+), 301 deletions(-) diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin index 1d3dca5c9..2c6412a8f 100644 --- a/core/os/os2/path.odin +++ b/core/os/os2/path.odin @@ -328,9 +328,9 @@ split_path :: proc(path: string) -> (dir, filename: string) { Gets the file name and extension from a path. e.g. - 'path/to/name.tar.gz' -> 'name.tar.gz' - 'path/to/name.txt' -> 'name.txt' - 'path/to/name' -> 'name' + 'path/to/name.tar.gz' -> 'name.tar.gz' + 'path/to/name.txt' -> 'name.txt' + 'path/to/name' -> 'name' Returns "." if the path is an empty string. */ @@ -352,8 +352,8 @@ Only the last dot is considered when splitting the file extension. See `short_stem`. e.g. - 'name.tar.gz' -> 'name.tar' - 'name.txt' -> 'name' + 'name.tar.gz' -> 'name.tar' + 'name.txt' -> 'name' Returns an empty string if there is no stem. e.g: '.gitignore'. Returns an empty string if there's a trailing path separator. @@ -389,8 +389,8 @@ where `long_ext` is the extension returned by `split_filename_all`. The first dot is used to split off the file extension, unlike `stem` which uses the last dot. e.g. - 'name.tar.gz' -> 'name' - 'name.txt' -> 'name' + 'name.tar.gz' -> 'name' + 'name.txt' -> 'name' Returns an empty string if there is no stem. e.g: '.gitignore'. Returns an empty string if there's a trailing path separator. @@ -412,8 +412,8 @@ Only the last dot is considered when splitting the file extension. See `long_ext`. e.g. - 'name.tar.gz' -> '.gz' - 'name.txt' -> '.txt' + 'name.tar.gz' -> '.gz' + 'name.txt' -> '.txt' Returns an empty string if there is no dot. Returns an empty string if there is a trailing path separator. @@ -435,8 +435,8 @@ The long file extension is such that `short_stem(path)` + `long_ext(path)` = `ba The first dot is used to split off the file extension, unlike `ext` which uses the last dot. e.g. - 'name.tar.gz' -> '.tar.gz' - 'name.txt' -> '.txt' + 'name.tar.gz' -> '.tar.gz' + 'name.txt' -> '.txt' Returns an empty string if there is no dot. Returns an empty string if there is a trailing path separator. @@ -616,7 +616,7 @@ Pattern syntax is: '*' matches any sequence of non-/ characters '?' matches any single non-/ character '[' ['^'] { character-range } ']' - character classification (cannot be empty) + character classification (cannot be empty) c matches character c (c != '*', '?', '\\', '[') '\\' c matches character c @@ -728,8 +728,8 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str Returns leading volume name. e.g. - "C:\foo\bar\baz" will return "C:" on Windows. - Everything else will be "". + "C:\foo\bar\baz" will return "C:" on Windows. + Everything else will be "". */ volume_name :: proc(path: string) -> string { when ODIN_OS == .Windows { diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin index f1b58c35b..275fe3e18 100644 --- a/core/os/os2/path_windows.odin +++ b/core/os/os2/path_windows.odin @@ -157,6 +157,7 @@ _get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err } } +@(private) can_use_long_paths: bool @(init) diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin index 58df45754..0a9ac4e57 100644 --- a/core/os/os2/stat.odin +++ b/core/os/os2/stat.odin @@ -111,3 +111,7 @@ modification_time_by_path :: proc(path: string) -> (time.Time, Error) { fi, err := stat(path, temp_allocator) return fi.modification_time, err } + +is_reserved_name :: proc(path: string) -> bool { + return _is_reserved_name(path) +} \ No newline at end of file diff --git a/core/os/os2/stat_js.odin b/core/os/os2/stat_js.odin index 439226490..e37864936 100644 --- a/core/os/os2/stat_js.odin +++ b/core/os/os2/stat_js.odin @@ -18,4 +18,8 @@ _lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, er _same_file :: proc(fi1, fi2: File_Info) -> bool { return fi1.fullpath == fi2.fullpath +} + +_is_reserved_name :: proc(path: string) -> bool { + return false } \ No newline at end of file diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin index 6185252cf..dc5bccb54 100644 --- a/core/os/os2/stat_linux.odin +++ b/core/os/os2/stat_linux.odin @@ -73,3 +73,7 @@ _lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, er _same_file :: proc(fi1, fi2: File_Info) -> bool { return fi1.fullpath == fi2.fullpath } + +_is_reserved_name :: proc(path: string) -> bool { + return false +} \ No newline at end of file diff --git a/core/os/os2/stat_posix.odin b/core/os/os2/stat_posix.odin index 4ed96b389..e401ffe40 100644 --- a/core/os/os2/stat_posix.odin +++ b/core/os/os2/stat_posix.odin @@ -135,3 +135,7 @@ _lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, er _same_file :: proc(fi1, fi2: File_Info) -> bool { return fi1.fullpath == fi2.fullpath } + +_is_reserved_name :: proc(path: string) -> bool { + return false +} \ No newline at end of file diff --git a/core/os/os2/stat_wasi.odin b/core/os/os2/stat_wasi.odin index bf18d8273..f15479e22 100644 --- a/core/os/os2/stat_wasi.odin +++ b/core/os/os2/stat_wasi.odin @@ -98,3 +98,7 @@ _lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, er _same_file :: proc(fi1, fi2: File_Info) -> bool { return fi1.fullpath == fi2.fullpath } + +_is_reserved_name :: proc(path: string) -> bool { + return false +} \ No newline at end of file diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin index efc1707af..db8269cc2 100644 --- a/core/path/filepath/path.odin +++ b/core/path/filepath/path.odin @@ -16,10 +16,30 @@ is_slash :: proc(c: byte) -> bool { return c == '\\' || c == '/' } +/* + In Windows, returns `true` if `path` is one of the following: + "CON", "PRN", "AUX", "NUL", + "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", + "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", + + On other platforms, returns `false`. +*/ +is_reserved_name :: os.is_reserved_name + // Splits path immediate following the last separator; separating the path into a directory and file. // If no separator is found, `dir` will be empty and `path` set to `path`. split :: os.split_path + +/* +Join all `elems` with the system's path separator and normalize the result. + +*Allocates Using Provided Allocator* + +For example, `join_path({"/home", "foo", "bar.txt"})` will result in `"/home/foo/bar.txt"`. +*/ +join :: os.join_path + /* Returns leading volume name. @@ -27,79 +47,7 @@ split :: os.split_path "C:\foo\bar\baz" will return "C:" on Windows. Everything else will be "". */ -volume_name :: proc(path: string) -> string { - return path[:volume_name_len(path)] -} - -// Returns the length of the volume name in bytes. -volume_name_len :: proc(path: string) -> int { - if ODIN_OS == .Windows { - if len(path) < 2 { - return 0 - } - - if path[1] == ':' { - switch path[0] { - case 'a'..='z', 'A'..='Z': - return 2 - } - } - - /* - See: URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx - Further allowed paths can be of the form of: - - \\server\share or \\server\share\more\path - - \\?\C:\... - - \\.\PhysicalDriveX - */ - // Any remaining kind of path has to start with two slashes. - if !is_separator(path[0]) || !is_separator(path[1]) { - return 0 - } - - // Device path. The volume name is the whole string - if len(path) >= 5 && path[2] == '.' && is_separator(path[3]) { - return len(path) - } - - // We're a UNC share `\\host\share`, file namespace `\\?\C:` or UNC in file namespace `\\?\\host\share` - prefix := 2 - - // File namespace. - if len(path) >= 5 && path[2] == '?' && is_separator(path[3]) { - if is_separator(path[4]) { - // `\\?\\` UNC path in file namespace - prefix = 5 - } - - if len(path) >= 6 && path[5] == ':' { - switch path[4] { - case 'a'..='z', 'A'..='Z': - return 6 - case: - return 0 - } - } - } - - // UNC path, minimum version of the volume is `\\h\s` for host, share. - // Can also contain an IP address in the host position. - slash_count := 0 - for i in prefix.. 0 { - slash_count += 1 - - if slash_count == 2 { - return i - } - } - } - - return len(path) - } - return 0 -} +volume_name :: os.volume_name /* Gets the file name and extension from a path. @@ -194,87 +142,7 @@ long_ext :: os.long_ext If the result of the path is an empty string, the returned path with be `"."`. */ -clean :: proc(path: string, allocator := context.allocator) -> (cleaned: string, err: runtime.Allocator_Error) #optional_allocator_error { - context.allocator = allocator - - path := path - original_path := path - vol_len := volume_name_len(path) - path = path[vol_len:] - - if path == "" { - if vol_len > 1 && original_path[1] != ':' { - s, ok := from_slash(original_path) - if !ok { - s = strings.clone(s) or_return - } - return s, nil - } - return strings.concatenate({original_path, "."}) - } - - rooted := is_separator(path[0]) - - n := len(path) - out := &Lazy_Buffer{ - s = path, - vol_and_path = original_path, - vol_len = vol_len, - } - defer lazy_buffer_destroy(out) - - r, dot_dot := 0, 0 - if rooted { - lazy_buffer_append(out, SEPARATOR) or_return - r, dot_dot = 1, 1 - } - - for r < n { - switch { - case is_separator(path[r]): - r += 1 - case path[r] == '.' && (r+1 == n || is_separator(path[r+1])): - r += 1 - case path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_separator(path[r+2])): - r += 2 - switch { - case out.w > dot_dot: - out.w -= 1 - for out.w > dot_dot && !is_separator(lazy_buffer_index(out, out.w)) { - out.w -= 1 - } - case !rooted: - if out.w > 0 { - lazy_buffer_append(out, SEPARATOR) or_return - } - lazy_buffer_append(out, '.') or_return - lazy_buffer_append(out, '.') or_return - dot_dot = out.w - } - case: - if rooted && out.w != 1 || !rooted && out.w != 0 { - lazy_buffer_append(out, SEPARATOR) or_return - } - for ; r < n && !is_separator(path[r]); r += 1 { - lazy_buffer_append(out, path[r]) or_return - } - - } - } - - if out.w == 0 { - lazy_buffer_append(out, '.') or_return - } - - s := lazy_buffer_string(out) or_return - - new_allocation: bool - cleaned, new_allocation = from_slash(s) - if new_allocation { - delete(s) - } - return -} +clean :: os.clean_path // Returns the result of replacing each forward slash `/` character in the path with the separate OS specific character. from_slash :: proc(path: string, allocator := context.allocator) -> (new_path: string, new_allocation: bool) { @@ -295,7 +163,6 @@ to_slash :: proc(path: string, allocator := context.allocator) -> (new_path: str Relative_Error :: enum { None, - Cannot_Relate, } @@ -308,8 +175,10 @@ Relative_Error :: enum { */ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (string, Relative_Error) { context.allocator = allocator - base_clean := clean(base_path, allocator) - target_clean := clean(target_path, allocator) + base_clean, base_err := clean(base_path, allocator) + if base_err != nil { return "", .Cannot_Relate} + target_clean, target_err := clean(target_path, allocator) + if target_err != nil { return "", .Cannot_Relate} defer delete(base_clean, allocator) defer delete(target_clean, allocator) @@ -390,7 +259,8 @@ dir :: proc(path: string, allocator := context.allocator) -> string { for i >= len(vol) && !is_separator(path[i]) { i -= 1 } - dir := clean(path[len(vol) : i+1]) + dir, dir_err := clean(path[len(vol) : i+1], allocator) + if dir_err != nil { return "" } defer delete(dir) if dir == "." && len(vol) > 2 { return strings.clone(vol) diff --git a/core/path/filepath/path_js.odin b/core/path/filepath/path_js.odin index 3b5ac04f5..c0c85b487 100644 --- a/core/path/filepath/path_js.odin +++ b/core/path/filepath/path_js.odin @@ -1,17 +1,12 @@ package filepath import "base:runtime" - import "core:strings" SEPARATOR :: '/' SEPARATOR_STRING :: `/` LIST_SEPARATOR :: ':' -is_reserved_name :: proc(path: string) -> bool { - return false -} - is_abs :: proc(path: string) -> bool { return strings.has_prefix(path, "/") } @@ -22,15 +17,4 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { } return path, false -} - -join :: proc(elems: []string, allocator := context.allocator) -> (joined: string, err: runtime.Allocator_Error) #optional_allocator_error { - for e, i in elems { - if e != "" { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - p := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator) or_return - return clean(p, allocator) - } - } - return "", nil } \ No newline at end of file diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin index 8bf412599..e43264961 100644 --- a/core/path/filepath/path_unix.odin +++ b/core/path/filepath/path_unix.odin @@ -1,19 +1,13 @@ #+build linux, darwin, freebsd, openbsd, netbsd, haiku package filepath -import "base:runtime" - -import "core:strings" -import "core:sys/posix" +import "core:strings" +import "core:sys/posix" SEPARATOR :: '/' SEPARATOR_STRING :: `/` LIST_SEPARATOR :: ':' -is_reserved_name :: proc(path: string) -> bool { - return false -} - is_abs :: proc(path: string) -> bool { return strings.has_prefix(path, "/") } @@ -32,15 +26,4 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { path_str := strings.clone(string(path_ptr), allocator) return path_str, true -} - -join :: proc(elems: []string, allocator := context.allocator) -> (joined: string, err: runtime.Allocator_Error) #optional_allocator_error { - for e, i in elems { - if e != "" { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - p := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator) or_return - return clean(p, allocator) - } - } - return "", nil -} +} \ No newline at end of file diff --git a/core/path/filepath/path_wasi.odin b/core/path/filepath/path_wasi.odin index 74cc6ca1e..c0c85b487 100644 --- a/core/path/filepath/path_wasi.odin +++ b/core/path/filepath/path_wasi.odin @@ -1,17 +1,12 @@ package filepath import "base:runtime" - import "core:strings" SEPARATOR :: '/' SEPARATOR_STRING :: `/` LIST_SEPARATOR :: ':' -is_reserved_name :: proc(path: string) -> bool { - return false -} - is_abs :: proc(path: string) -> bool { return strings.has_prefix(path, "/") } @@ -22,15 +17,4 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { } return path, false -} - -join :: proc(elems: []string, allocator := context.allocator) -> (joined: string, err: runtime.Allocator_Error) #optional_allocator_error { - for e, i in elems { - if e != "" { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - p := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator) or_return - return clean(p, allocator) - } - } - return "", nil -} +} \ No newline at end of file diff --git a/core/path/filepath/path_windows.odin b/core/path/filepath/path_windows.odin index 5b81d57a0..00aae8d7f 100644 --- a/core/path/filepath/path_windows.odin +++ b/core/path/filepath/path_windows.odin @@ -1,6 +1,5 @@ package filepath -import "core:strings" import "base:runtime" import os "core:os/os2" @@ -8,27 +7,8 @@ SEPARATOR :: '\\' SEPARATOR_STRING :: `\` LIST_SEPARATOR :: ';' -@(private) -reserved_names := [?]string{ - "CON", "PRN", "AUX", "NUL", - "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", - "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", -} - -is_reserved_name :: proc(path: string) -> bool { - if len(path) == 0 { - return false - } - for reserved in reserved_names { - if strings.equal_fold(path, reserved) { - return true - } - } - return false -} - is_UNC :: proc(path: string) -> bool { - return volume_name_len(path) > 2 + return len(volume_name(path)) > 2 } is_abs :: proc(path: string) -> bool { @@ -43,52 +23,4 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { } p, _ := clean(full_path, allocator) return p, true -} - -join :: proc(elems: []string, allocator := context.allocator) -> (string, runtime.Allocator_Error) #optional_allocator_error { - for e, i in elems { - if e != "" { - return join_non_empty(elems[i:], allocator) - } - } - return "", nil -} - -join_non_empty :: proc(elems: []string, allocator := context.allocator) -> (joined: string, err: runtime.Allocator_Error) { - context.allocator = allocator - - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator) - - if len(elems[0]) == 2 && elems[0][1] == ':' { - i := 1 - for ; i < len(elems); i += 1 { - if elems[i] != "" { - break - } - } - s := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator) or_return - s = strings.concatenate({elems[0], s}, context.temp_allocator) or_return - s, _ = clean(s) - return - } - - p := strings.join(elems, SEPARATOR_STRING, context.temp_allocator) or_return - p = clean(p) or_return - if !is_UNC(p) { - return p, nil - } - - head := clean(elems[0], context.temp_allocator) or_return - if is_UNC(head) { - return p, nil - } - delete(p) // It is not needed now - - tail := strings.join(elems[1:], SEPARATOR_STRING, context.temp_allocator) or_return - tail = clean(tail, context.temp_allocator) or_return - if head[len(head)-1] == SEPARATOR { - return strings.concatenate({head, tail}) - } - - return strings.concatenate({head, SEPARATOR_STRING, tail}) -} +} \ No newline at end of file diff --git a/core/time/timezone/tz_unix.odin b/core/time/timezone/tz_unix.odin index ac6a4bb50..6bab440d9 100644 --- a/core/time/timezone/tz_unix.odin +++ b/core/time/timezone/tz_unix.odin @@ -4,7 +4,6 @@ package timezone import os "core:os/os2" import "core:strings" -import "core:path/filepath" import "core:time/datetime" local_tz_name :: proc(allocator := context.allocator) -> (name: string, success: bool) { @@ -36,12 +35,12 @@ local_tz_name :: proc(allocator := context.allocator) -> (name: string, success: } // Looking for tz path (ex fmt: "UTC", "Etc/UTC" or "America/Los_Angeles") - path_dir, path_file := filepath.split(path) + path_dir, path_file := os.split_path(path) if path_dir == "" { return } - upper_path_dir, upper_path_chunk := filepath.split(path_dir[:len(path_dir)]) + upper_path_dir, upper_path_chunk := os.split_path(path_dir[:len(path_dir)]) if upper_path_dir == "" { return } @@ -51,8 +50,8 @@ local_tz_name :: proc(allocator := context.allocator) -> (name: string, success: if err != nil { return } return region_str, true } else { - region_str, err := filepath.join({upper_path_chunk, path_file}, allocator = allocator) - if err != nil { return } + region_str, region_str_err := os.join_path({upper_path_chunk, path_file}, allocator = allocator) + if region_str_err != nil { return } return region_str, true } } @@ -80,6 +79,7 @@ _region_load :: proc(_reg_str: string, allocator := context.allocator) -> (out_r delete(local_name, allocator) return nil, true } + reg_str = local_name } defer if _reg_str == "local" { delete(reg_str, allocator) } @@ -107,4 +107,4 @@ _region_load :: proc(_reg_str: string, allocator := context.allocator) -> (out_r } return nil, false -} +} \ No newline at end of file