diff --git a/core/odin/parser/parse_files.odin b/core/odin/parser/parse_files.odin index 5e7bee923..93c282d35 100644 --- a/core/odin/parser/parse_files.odin +++ b/core/odin/parser/parse_files.odin @@ -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 } diff --git a/core/os/os2/dir_walker.odin b/core/os/os2/dir_walker.odin index 0af751f31..ba5342cf8 100644 --- a/core/os/os2/dir_walker.odin +++ b/core/os/os2/dir_walker.odin @@ -227,4 +227,4 @@ walker_walk :: proc(w: ^Walker) -> (fi: File_Info, ok: bool) { } return info, iter_ok -} +} \ No newline at end of file diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin index 2c6412a8f..ac18b7562 100644 --- a/core/os/os2/path.odin +++ b/core/os/os2/path.odin @@ -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 diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin index db8269cc2..2f2f7996c 100644 --- a/core/path/filepath/path.odin +++ b/core/path/filepath/path.odin @@ -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 -} \ No newline at end of file +split_list :: os.split_path_list \ No newline at end of file diff --git a/core/path/filepath/path_js.odin b/core/path/filepath/path_js.odin index c0c85b487..e2657cb3e 100644 --- a/core/path/filepath/path_js.odin +++ b/core/path/filepath/path_js.odin @@ -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 -} \ No newline at end of file +LIST_SEPARATOR :: ':' \ No newline at end of file diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin index e43264961..2e1b1419e 100644 --- a/core/path/filepath/path_unix.odin +++ b/core/path/filepath/path_unix.odin @@ -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 -} \ No newline at end of file +LIST_SEPARATOR :: ':' \ No newline at end of file diff --git a/core/path/filepath/path_wasi.odin b/core/path/filepath/path_wasi.odin index c0c85b487..e2657cb3e 100644 --- a/core/path/filepath/path_wasi.odin +++ b/core/path/filepath/path_wasi.odin @@ -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 -} \ No newline at end of file +LIST_SEPARATOR :: ':' \ No newline at end of file diff --git a/core/path/filepath/path_windows.odin b/core/path/filepath/path_windows.odin index 00aae8d7f..9b8e92bbd 100644 --- a/core/path/filepath/path_windows.odin +++ b/core/path/filepath/path_windows.odin @@ -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 } \ No newline at end of file diff --git a/core/path/filepath/walk.odin b/core/path/filepath/walk.odin index 845ba06a0..00b063bc7 100644 --- a/core/path/filepath/walk.odin +++ b/core/path/filepath/walk.odin @@ -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 \ No newline at end of file