Add file globbing

This commit is contained in:
Bruce Hill 2024-10-29 14:36:49 -04:00
parent e3c1dd2df5
commit 7cd67dd7f3
5 changed files with 70 additions and 1 deletions

View File

@ -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:**

View File

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

View File

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

View File

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

View File

@ -133,3 +133,5 @@ func main():
>> (/foo/bar/baz) ++ (./.././qux/./../quux)
= (/foo/bar/quux)
!! Globbing:
>> (./*.tm):glob()