From 94993c5f113b27083e586c7620eb896fe750c6d1 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 17 Mar 2025 18:59:03 -0400 Subject: Add Path:expand_home() as a publicly visible API and bugfix it --- docs/paths.md | 25 +++++++++++++++++++++++++ environment.c | 1 + stdlib/paths.c | 31 ++++++++++++++++--------------- stdlib/paths.h | 2 +- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/docs/paths.md b/docs/paths.md index 14ec7832..cb4bb055 100644 --- a/docs/paths.md +++ b/docs/paths.md @@ -50,6 +50,7 @@ intended. Paths can be created from text with slashes using - [`func children(path: Path, include_hidden=no -> [Path])`](#children) - [`func create_directory(path: Path, permissions=0o755[32] -> Void)`](#create_directory) - [`func exists(path: Path -> Bool)`](#exists) +- [`func expand_home(path: Path -> Path)`](#expand_home) - [`func extension(path: Path, full=yes -> Text)`](#extension) - [`func files(path: Path, include_hidden=no -> [Path])`](#files) - [`func from_components(components:[Text] -> Path)`](#from_components) @@ -376,6 +377,30 @@ func exists(path: Path -> Bool) --- +### `expand_home` +For home-based paths (those starting with `~`), expand the path to replace the +tilde with and absolute path to the user's `$HOME` directory. + +```tomo +func expand_home(path: Path -> Path) +``` + +- `path`: The path to expand. + +**Returns:** +If the path does not start with a `~`, then return it unmodified. Otherwise, +replace the `~` with an absolute path to the user's home directory. + +**Example:** +```tomo +>> (~/foo):expand_home() # Assume current user is 'user' += /home/user/foo +>> (/foo):expand_home() # No change += /foo +``` + +--- + ### `extension` Returns the file extension of the file at the specified path. Optionally returns the full extension. diff --git a/environment.c b/environment.c index 2fa0d504..4ffa1dd9 100644 --- a/environment.c +++ b/environment.c @@ -310,6 +310,7 @@ env_t *new_compilation_unit(CORD libname) {"concatenated_with", "Path$concat", "func(a,b:Path -> Path)"}, {"create_directory", "Path$create_directory", "func(path:Path, permissions=Int32(0o755))"}, {"exists", "Path$exists", "func(path:Path -> Bool)"}, + {"expand_home", "Path$expand_home", "func(path:Path -> Path)"}, {"extension", "Path$extension", "func(path:Path, full=yes -> Text)"}, {"files", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, {"from_components", "Path$from_components", "func(components:[Text] -> Path)"}, diff --git a/stdlib/paths.c b/stdlib/paths.c index 813c2004..bac37b27 100644 --- a/stdlib/paths.c +++ b/stdlib/paths.c @@ -98,11 +98,12 @@ public Path_t Path$from_text(Text_t text) return Path$from_str(Text$as_c_string(text)); } -static INLINE Path_t Path$_expand_home(Path_t path) +public Path_t Path$expand_home(Path_t path) { if (path.type == PATH_HOME) { Path_t pwd = Path$from_str(getenv("HOME")); - Array_t components = Array$concat(path.components, pwd.components, sizeof(Text_t)); + Array_t components = Array$concat(pwd.components, path.components, sizeof(Text_t)); + assert(components.length == path.components.length + pwd.components.length); clean_components(&components); path = (Path_t){.type=PATH_ABSOLUTE, .components=components}; } @@ -165,14 +166,14 @@ public Path_t Path$relative_to(Path_t path, Path_t relative_to) public bool Path$exists(Path_t path) { - path = Path$_expand_home(path); + path = Path$expand_home(path); struct stat sb; return (stat(Path$as_c_string(path), &sb) == 0); } static INLINE int path_stat(Path_t path, bool follow_symlinks, struct stat *sb) { - path = Path$_expand_home(path); + path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); return follow_symlinks ? stat(path_str, sb) : lstat(path_str, sb); } @@ -219,21 +220,21 @@ public bool Path$is_symlink(Path_t path) public bool Path$can_read(Path_t path) { - path = Path$_expand_home(path); + path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); return (euidaccess(path_str, R_OK) == 0); } public bool Path$can_write(Path_t path) { - path = Path$_expand_home(path); + path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); return (euidaccess(path_str, W_OK) == 0); } public bool Path$can_execute(Path_t path) { - path = Path$_expand_home(path); + path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); return (euidaccess(path_str, X_OK) == 0); } @@ -264,7 +265,7 @@ public OptionalMoment_t Path$changed(Path_t path, bool follow_symlinks) static void _write(Path_t path, Array_t bytes, int mode, int permissions) { - path = Path$_expand_home(path); + path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); int fd = open(path_str, mode, permissions); if (fd == -1) @@ -301,7 +302,7 @@ public void Path$append_bytes(Path_t path, Array_t bytes, int permissions) public OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t count) { - path = Path$_expand_home(path); + path = Path$expand_home(path); int fd = open(Path$as_c_string(path), O_RDONLY); if (fd == -1) return NONE_ARRAY; @@ -404,7 +405,7 @@ public void Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t gro public void Path$remove(Path_t path, bool ignore_missing) { - path = Path$_expand_home(path); + path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); struct stat sb; if (lstat(path_str, &sb) != 0) { @@ -425,7 +426,7 @@ public void Path$remove(Path_t path, bool ignore_missing) public void Path$create_directory(Path_t path, int permissions) { - path = Path$_expand_home(path); + path = Path$expand_home(path); const char *c_path = Path$as_c_string(path); int status = mkdir(c_path, (mode_t)permissions); if (status != 0 && errno != EEXIST) @@ -434,7 +435,7 @@ public void Path$create_directory(Path_t path, int permissions) static Array_t _filtered_children(Path_t path, bool include_hidden, mode_t filter) { - path = Path$_expand_home(path); + path = Path$expand_home(path); struct dirent *dir; Array_t children = {}; const char *path_str = Path$as_c_string(path); @@ -483,7 +484,7 @@ public Array_t Path$subdirectories(Path_t path, bool include_hidden) public Path_t Path$unique_directory(Path_t path) { - path = Path$_expand_home(path); + path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); size_t len = strlen(path_str); if (len >= PATH_MAX) fail("Path is too long: %s", path_str); @@ -498,7 +499,7 @@ public Path_t Path$unique_directory(Path_t path) public Path_t Path$write_unique_bytes(Path_t path, Array_t bytes) { - path = Path$_expand_home(path); + path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); size_t len = strlen(path_str); if (len >= PATH_MAX) fail("Path is too long: %s", path_str); @@ -638,7 +639,7 @@ static Text_t _next_line(FILE **f) public OptionalClosure_t Path$by_line(Path_t path) { - path = Path$_expand_home(path); + path = Path$expand_home(path); FILE *f = fopen(Path$as_c_string(path), "r"); if (f == NULL) diff --git a/stdlib/paths.h b/stdlib/paths.h index b971cdf5..f65bd3ca 100644 --- a/stdlib/paths.h +++ b/stdlib/paths.h @@ -16,8 +16,8 @@ const char *Path$as_c_string(Path_t path); Path_t Path$_concat(int n, Path_t items[n]); #define Path$concat(...) Path$_concat((int)sizeof((Path_t[]){__VA_ARGS__})/sizeof(Path_t), ((Path_t[]){__VA_ARGS__})) Path_t Path$resolved(Path_t path, Path_t relative_to); -Path_t Path$relative(Path_t path, Path_t relative_to); Path_t Path$relative_to(Path_t path, Path_t relative_to); +Path_t Path$expand_home(Path_t path); bool Path$exists(Path_t path); bool Path$is_file(Path_t path, bool follow_symlinks); bool Path$is_directory(Path_t path, bool follow_symlinks); -- cgit v1.2.3