Link some more of filepath to os2

This commit is contained in:
Jeroen van Rijn
2025-11-01 17:19:46 +01:00
parent 0c341123cb
commit 2e970db51d
9 changed files with 139 additions and 271 deletions

View File

@@ -11,8 +11,8 @@ import "core:strings"
collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
NO_POS :: tokenizer.Pos{}
pkg_path, pkg_path_ok := filepath.abs(path)
if !pkg_path_ok {
pkg_path, pkg_path_err := os.get_absolute_path(path, context.allocator)
if pkg_path_err != nil {
return
}
@@ -28,8 +28,8 @@ collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
pkg.fullpath = pkg_path
for match in matches {
fullpath, ok := filepath.abs(match)
if !ok {
fullpath, fullpath_err := os.get_absolute_path(match, context.allocator)
if fullpath_err != nil {
return
}

View File

@@ -227,4 +227,4 @@ walker_walk :: proc(w: ^Walker) -> (fi: File_Info, ok: bool) {
}
return info, iter_ok
}
}

View File

@@ -22,9 +22,32 @@ is_path_separator :: proc(c: byte) -> bool {
return _is_path_separator(c)
}
@(private)
is_slash :: proc(c: byte) -> bool {
return c == '\\' || c == '/'
/*
Returns the result of replacing each path separator character in the path
with the `new_sep` rune.
*Allocates Using Provided Allocator*
*/
replace_path_separators :: proc(path: string, new_sep: rune, allocator: runtime.Allocator) -> (new_path: string, err: Error) {
buf := make([]u8, len(path), allocator) or_return
i: int
for r in path {
replacement := r
if r == '/' || r == '\\' {
replacement = new_sep
}
if replacement <= rune(0x7F) {
buf[i] = u8(replacement)
i += 1
} else {
b, w := utf8.encode_rune(r)
copy(buf[i:], b[:w])
i += w
}
}
return string(buf), nil
}
mkdir :: make_directory

View File

@@ -2,7 +2,6 @@
// To process paths such as URLs that depend on forward slashes regardless of the OS, use the slashpath package.
package filepath
import "base:runtime"
import os "core:os/os2"
import "core:strings"
@@ -11,11 +10,6 @@ SEPARATOR_CHARS :: `/\`
// is_separator checks whether the byte is a valid separator character
is_separator :: os.is_path_separator
@(private)
is_slash :: proc(c: byte) -> bool {
return c == '\\' || c == '/'
}
/*
In Windows, returns `true` if `path` is one of the following:
"CON", "PRN", "AUX", "NUL",
@@ -144,22 +138,25 @@ long_ext :: os.long_ext
*/
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) {
if SEPARATOR == '/' {
return path, false
}
return strings.replace_all(path, "/", SEPARATOR_STRING, allocator)
}
/*
Returns the result of replacing each path separator character in the path
with the specific character `new_sep`.
// Returns the result of replacing each OS specific separator with a forward slash `/` character.
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)
}
*Allocates Using Provided Allocator*
*/
replace_path_separators := os.replace_path_separators
/*
Return true if `path` is an absolute path as opposed to a relative one.
*/
is_abs :: os.is_absolute_path
/*
Get the absolute path to `path` with respect to the process's current directory.
*Allocates Using Provided Allocator*
*/
abs :: os.get_absolute_path
Relative_Error :: enum {
None,
@@ -275,108 +272,4 @@ dir :: proc(path: string, allocator := context.allocator) -> string {
// An empty string returns nil. A non-empty string with no separators returns a 1-element array.
// Any empty components will be included, e.g. `a::b` will return a 3-element array, as will `::`.
// Separators within pairs of double-quotes will be ignored and stripped, e.g. `"a:b"c:d` will return []{`a:bc`, `d`}.
split_list :: proc(path: string, allocator := context.allocator) -> (list: []string, err: runtime.Allocator_Error) #optional_allocator_error {
if path == "" {
return nil, 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 + 1, allocator) or_return
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)
list[index] = path[start:]
for s0, i in list {
s, new := strings.replace_all(s0, `"`, ``, allocator)
if !new {
s = strings.clone(s, allocator) or_return
}
list[i] = s
}
return list, nil
}
/*
Lazy_Buffer is a lazily made path buffer
When it does allocate, it uses the context.allocator
*/
@(private)
Lazy_Buffer :: struct {
s: string,
b: []byte,
w: int, // write index
vol_and_path: string,
vol_len: int,
}
@(private)
lazy_buffer_index :: proc(lb: ^Lazy_Buffer, i: int) -> byte {
if lb.b != nil {
return lb.b[i]
}
return lb.s[i]
}
@(private)
lazy_buffer_append :: proc(lb: ^Lazy_Buffer, c: byte) -> (err: runtime.Allocator_Error) {
if lb.b == nil {
if lb.w < len(lb.s) && lb.s[lb.w] == c {
lb.w += 1
return
}
lb.b = make([]byte, len(lb.s)) or_return
copy(lb.b, lb.s[:lb.w])
}
lb.b[lb.w] = c
lb.w += 1
return
}
@(private)
lazy_buffer_string :: proc(lb: ^Lazy_Buffer) -> (s: string, err: runtime.Allocator_Error) {
if lb.b == nil {
return strings.clone(lb.vol_and_path[:lb.vol_len+lb.w])
}
x := lb.vol_and_path[:lb.vol_len]
y := string(lb.b[:lb.w])
z := make([]byte, len(x)+len(y)) or_return
copy(z, x)
copy(z[len(x):], y)
return string(z), nil
}
@(private)
lazy_buffer_destroy :: proc(lb: ^Lazy_Buffer) -> runtime.Allocator_Error {
err := delete(lb.b)
lb^ = {}
return err
}
split_list :: os.split_path_list

View File

@@ -1,20 +1,5 @@
package filepath
import "base:runtime"
import "core:strings"
SEPARATOR :: '/'
SEPARATOR_STRING :: `/`
LIST_SEPARATOR :: ':'
is_abs :: proc(path: string) -> bool {
return strings.has_prefix(path, "/")
}
abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
if is_abs(path) {
return strings.clone(string(path), allocator), true
}
return path, false
}
LIST_SEPARATOR :: ':'

View File

@@ -1,29 +1,6 @@
#+build linux, darwin, freebsd, openbsd, netbsd, haiku
package filepath
import "core:strings"
import "core:sys/posix"
SEPARATOR :: '/'
SEPARATOR_STRING :: `/`
LIST_SEPARATOR :: ':'
is_abs :: proc(path: string) -> bool {
return strings.has_prefix(path, "/")
}
abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
rel := path
if rel == "" {
rel = "."
}
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
path_ptr := posix.realpath(rel_cstr, nil)
if path_ptr == nil {
return "", posix.errno() == nil
}
defer posix.free(path_ptr)
path_str := strings.clone(string(path_ptr), allocator)
return path_str, true
}
LIST_SEPARATOR :: ':'

View File

@@ -1,20 +1,5 @@
package filepath
import "base:runtime"
import "core:strings"
SEPARATOR :: '/'
SEPARATOR_STRING :: `/`
LIST_SEPARATOR :: ':'
is_abs :: proc(path: string) -> bool {
return strings.has_prefix(path, "/")
}
abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
if is_abs(path) {
return strings.clone(string(path), allocator), true
}
return path, false
}
LIST_SEPARATOR :: ':'

View File

@@ -1,26 +1,9 @@
package filepath
import "base:runtime"
import os "core:os/os2"
SEPARATOR :: '\\'
SEPARATOR_STRING :: `\`
LIST_SEPARATOR :: ';'
is_UNC :: proc(path: string) -> bool {
return len(volume_name(path)) > 2
}
is_abs :: proc(path: string) -> bool {
return os.is_absolute_path(path)
}
abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)
full_path, err := os.get_absolute_path(path, context.temp_allocator)
if err != nil {
return "", false
}
p, _ := clean(full_path, allocator)
return p, true
}

View File

@@ -3,81 +3,103 @@
package filepath
import os "core:os/os2"
import "core:slice"
// Walk_Proc is the type of the procedure called for each file or directory visited by 'walk'
// The 'path' parameter contains the parameter to walk as a prefix (this is the same as info.fullpath except on 'root')
// The 'info' parameter is the os.File_Info for the named path
//
// If there was a problem walking to the file or directory named by path, the incoming error will describe the problem
// and the procedure can decide how to handle that error (and walk will not descend into that directory)
// In the case of an error, the info argument will be 0
// If an error is returned, processing stops
// The sole exception is if 'skip_dir' is returned as true:
// when 'skip_dir' is invoked on a directory. 'walk' skips directory contents
// when 'skip_dir' is invoked on a non-directory. 'walk' skips the remaining files in the containing directory
Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Error, user_data: rawptr) -> (err: os.Error, skip_dir: bool)
Walker :: os.Walker
// walk walks the file tree rooted at 'root', calling 'walk_proc' for each file or directory in the tree, including 'root'
// All errors that happen visiting files and directories are filtered by walk_proc
// The files are walked in lexical order to make the output deterministic
// NOTE: Walking large directories can be inefficient due to the lexical sort
// NOTE: walk does not follow symbolic links
// NOTE: os.File_Info uses the 'context.temp_allocator' to allocate, and will delete when it is done
walk :: proc(root: string, walk_proc: Walk_Proc, user_data: rawptr) -> os.Error {
info, err := os.lstat(root, context.temp_allocator)
defer os.file_info_delete(info, context.temp_allocator)
/*
Initializes a walker, either using a path or a file pointer to a directory the walker will start at.
skip_dir: bool
if err != nil {
err, skip_dir = walk_proc(info, err, user_data)
} else {
err, skip_dir = _walk(info, walk_proc, user_data)
}
return nil if skip_dir else err
}
You are allowed to repeatedly call this to reuse it for later walks.
For an example on how to use the walker, see `walker_walk`.
*/
walker_init :: os.walker_init
@(private)
_walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (err: os.Error, skip_dir: bool) {
if info.type != .Directory {
if info.fullpath == "" && info.name == "" {
// ignore empty things
return
}
return walk_proc(info, nil, user_data)
}
/*
Creates a walker, either using a path or a file pointer to a directory the walker will start at.
fis: []os.File_Info
err1: os.Error
fis, err = read_dir(info.fullpath, context.temp_allocator)
defer os.file_info_slice_delete(fis, context.temp_allocator)
For an example on how to use the walker, see `walker_walk`.
*/
walker_create :: os.walker_create
err1, skip_dir = walk_proc(info, err, user_data)
if err != nil || err1 != nil || skip_dir {
err = err1
return
}
/*
Returns the last error that occurred during the walker's operations.
for fi in fis {
err, skip_dir = _walk(fi, walk_proc, user_data)
if err != nil || skip_dir {
if fi.type != .Directory || !skip_dir {
return
Can be called while iterating, or only at the end to check if anything failed.
*/
walker_error :: os.walker_error
walker_destroy :: os.walker_destroy
// Marks the current directory to be skipped (not entered into).
walker_skip_dir :: os.walker_skip_dir
/*
Returns the next file info in the iterator, files are iterated in breadth-first order.
If an error occurred opening a directory, you may get zero'd info struct and
`walker_error` will return the error.
Example:
package main
import "core:fmt"
import "core:strings"
import os "core:os/os2"
main :: proc() {
w := os.walker_create("core")
defer os.walker_destroy(&w)
for info in os.walker_walk(&w) {
// Optionally break on the first error:
// _ = walker_error(&w) or_break
// Or, handle error as we go:
if path, err := os.walker_error(&w); err != nil {
fmt.eprintfln("failed walking %s: %s", path, err)
continue
}
// Or, do not handle errors during iteration, and just check the error at the end.
// Skip a directory:
if strings.has_suffix(info.fullpath, ".git") {
os.walker_skip_dir(&w)
continue
}
fmt.printfln("%#v", info)
}
// Handle error if one happened during iteration at the end:
if path, err := os.walker_error(&w); err != nil {
fmt.eprintfln("failed walking %s: %v", path, err)
}
}
*/
walker_walk :: os.walker_walk
return
}
/*
Reads the file `f` (assuming it is a directory) and returns the unsorted directory entries.
This returns up to `n` entries OR all of them if `n <= 0`.
*/
read_directory :: os.read_directory
@(private)
read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> (fis: []os.File_Info, err: os.Error) {
f := os.open(dir_name, os.O_RDONLY) or_return
defer os.close(f)
fis = os.read_dir(f, -1, allocator) or_return
slice.sort_by(fis, proc(a, b: os.File_Info) -> bool {
return a.name < b.name
})
return
}
/*
Reads the file `f` (assuming it is a directory) and returns all of the unsorted directory entries.
*/
read_all_directory :: os.read_all_directory
/*
Reads the named directory by path (assuming it is a directory) and returns the unsorted directory entries.
This returns up to `n` entries OR all of them if `n <= 0`.
*/
read_directory_by_path :: os.read_directory_by_path
/*
Reads the named directory by path (assuming it is a directory) and returns all of the unsorted directory entries.
*/
read_all_directory_by_path :: os.read_all_directory_by_path