diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-10-29 14:36:49 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-10-29 14:36:49 -0400 |
| commit | 7cd67dd7f3ebf38a2a65c6756090936f9a1b3b03 (patch) | |
| tree | 6407b1b0836ea0d474b4822ef888cb6c6eaba7ae | |
| parent | e3c1dd2df5a593829a4d5864f8ff7ea4582da55c (diff) | |
Add file globbing
| -rw-r--r-- | docs/paths.md | 47 | ||||
| -rw-r--r-- | environment.c | 1 | ||||
| -rw-r--r-- | stdlib/paths.c | 20 | ||||
| -rw-r--r-- | stdlib/paths.h | 1 | ||||
| -rw-r--r-- | test/paths.tm | 2 |
5 files changed, 70 insertions, 1 deletions
diff --git a/docs/paths.md b/docs/paths.md index 8fb427b2..86ec2f26 100644 --- a/docs/paths.md +++ b/docs/paths.md @@ -288,6 +288,53 @@ A list of file paths. --- +### `glob` + +**Description:** +Perform a globbing operation and return an array of matching paths. Some glob +specific details: + +- The paths "." and ".." are *not* included in any globbing results. +- Files or directories that begin with "." will not match `*`, but will match `.*`. +- Globs do support `{a,b}` syntax for matching files that match any of several + choices of patterns. +- The shell-style syntax `**` for matching subdirectories is not supported. + +**Signature:** +```tomo +func glob(path: Path -> [Path]) +``` + +**Parameters:** + +- `path`: The path of the directory which may contain special globbing characters + like `*`, `?`, or `{...}` + +**Returns:** +A list of file paths that match the glob. + +**Example:** +```tomo +# Current directory includes: foo.txt, baz.txt, qux.jpg, .hidden +>> (./*):glob() += [(./foo.txt), (./baz.txt), (./qux.jpg)] + +>> (./*.txt):glob() += [(./foo.txt), (./baz.txt)] + +>> (./*.{txt,jpg}):glob() += [(./foo.txt), (./baz.txt), (./qux.jpg)] + +>> (./.*):glob() += [(./.hidden)] + +# Globs with no matches return an empty array: +>> (./*.xxx):glob() += [] +``` + +--- + ### `is_directory` **Description:** diff --git a/environment.c b/environment.c index ffc23df1..7f8d71c2 100644 --- a/environment.c +++ b/environment.c @@ -302,6 +302,7 @@ env_t *new_compilation_unit(CORD libname) {"exists", "Path$exists", "func(path:Path -> Bool)"}, {"extension", "Path$extension", "func(path:Path, full=yes -> Text)"}, {"files", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, + {"glob", "Path$glob", "func(path:Path -> [Path])"}, {"is_directory", "Path$is_directory", "func(path:Path, follow_symlinks=yes -> Bool)"}, {"is_file", "Path$is_file", "func(path:Path, follow_symlinks=yes -> Bool)"}, {"is_pipe", "Path$is_pipe", "func(path:Path, follow_symlinks=yes -> Bool)"}, diff --git a/stdlib/paths.c b/stdlib/paths.c index e8a1e1f6..8f6e1359 100644 --- a/stdlib/paths.c +++ b/stdlib/paths.c @@ -3,6 +3,7 @@ #include <errno.h> #include <fcntl.h> #include <gc.h> +#include <glob.h> #include <stdbool.h> #include <stdint.h> #include <string.h> @@ -535,6 +536,24 @@ public OptionalClosure_t Path$by_line(Path_t path) return (Closure_t){.fn=(void*)_next_line, .userdata=wrapper}; } +public Array_t Path$glob(Path_t path) +{ + glob_t glob_result; + int status = glob(Text$as_c_string(path), GLOB_BRACE | GLOB_TILDE | GLOB_TILDE_CHECK, NULL, &glob_result); + if (status != 0 && status != GLOB_NOMATCH) + fail("Failed to perform globbing"); + + Array_t glob_files = {}; + for (size_t i = 0; i < glob_result.gl_pathc; i++) { + size_t len = strlen(glob_result.gl_pathv[i]); + if ((len >= 2 && glob_result.gl_pathv[i][len-1] == '.' && glob_result.gl_pathv[i][len-2] == '/') + || (len >= 2 && glob_result.gl_pathv[i][len-1] == '.' && glob_result.gl_pathv[i][len-2] == '.' && glob_result.gl_pathv[i][len-3] == '/')) + continue; + Array$insert(&glob_files, (Text_t[1]){Text$from_str(glob_result.gl_pathv[i])}, I(0), sizeof(Text_t)); + } + return glob_files; +} + public const TypeInfo_t Path$info = { .size=sizeof(Path_t), .align=__alignof__(Path_t), @@ -542,5 +561,4 @@ public const TypeInfo_t Path$info = { .TextInfo={.lang="Path"}, }; - // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/paths.h b/stdlib/paths.h index 39089f54..551467e9 100644 --- a/stdlib/paths.h +++ b/stdlib/paths.h @@ -47,6 +47,7 @@ Path_t Path$parent(Path_t path); Text_t Path$base_name(Path_t path); Text_t Path$extension(Path_t path, bool full); Closure_t Path$by_line(Path_t path); +Array_t Path$glob(Path_t path); #define Path$hash Text$hash #define Path$compare Text$compare diff --git a/test/paths.tm b/test/paths.tm index 92a14640..4330e79f 100644 --- a/test/paths.tm +++ b/test/paths.tm @@ -133,3 +133,5 @@ func main(): >> (/foo/bar/baz) ++ (./.././qux/./../quux) = (/foo/bar/quux) + !! Globbing: + >> (./*.tm):glob() |
