More conflicts during rebase

This commit is contained in:
Jeroen van Rijn
2026-02-08 12:56:00 +01:00
parent 5924fb448e
commit 0c341123cb
13 changed files with 75 additions and 301 deletions

View File

@@ -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..<len(path) {
// Host needs to be at least 1 character
if is_separator(path[i]) && i > 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)

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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})
}
}