hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit e1d2e9f64d0234b1f14d305a707468ffbbf5e980
parent 92d83131401d02f651c3df5ea64c727c38dc333a
Author: Yasumasa Tada <ytada@spartan.dev>
Date:   Tue, 12 Apr 2022 00:10:30 +0900

glob: support NOESCAPE

Signed-off-by: Yasumasa Tada <ytada@spartan.dev>

Diffstat:
Mglob/+test.ha | 37++++++++++++++++++++++++-------------
Mglob/glob.ha | 26+++++++++++++++++---------
2 files changed, 41 insertions(+), 22 deletions(-)

diff --git a/glob/+test.ha b/glob/+test.ha @@ -1,26 +1,37 @@ use fnmatch; @test fn glob() void = { - const cases: [_]str = [ - "/u??/*in/a*", - "/?sr/[sb]in/*[[:digit:]]*", - "/*.?a", - "./*.[[:alpha:]]a", - "[[:punct:]]*", - "/", - "//", - ".", - "..", + const cases: [_](str, flags) = [ + ("/u??/*in/a*", flags::NONE), + ("/u*r/l?[bc]*/[bg]*", flags::NOSORT), + ("/?sr/[sb]in/*[[:digit:]]*", flags::NONE), + ("/h??\\e/*/.*", flags::NOSORT), + ("/\\h??e/*/.*", flags::NOSORT | flags::NOESCAPE), + ("/r*/*", flags::NOSORT), + ("/*.?a", flags::NOCHECK), + ("./*.[[:alpha:]]a", flags::NONE), + ("./\\a[bd]c", flags::NOESCAPE), + ("./\\a[be]c", flags::NONE), + ("[[:punct:]]*", flags::NONE), + ("/", flags::NONE), + ("//", flags::NONE), + (".", flags::NONE), + ("..", flags::NONE), ]; - const flags = [fnmatch::flags::PATHNAME]; for (let i = 0z; i < len(cases); i += 1) { - let gen = glob(cases[i]); + let gen = glob(cases[i].0, cases[i].1); defer globfree(&gen); for (true) match (next(&gen)) { case void => break; + case failure => + continue; case let s: str => - assert(fnmatch::fnmatch(cases[i], s, flags...)); + let bs = fnmatch::flags::PATHNAME; + if (cases[i].1 & flags::NOESCAPE != 0) { + bs |= fnmatch::flags::NOESCAPE; + }; + assert(fnmatch::fnmatch(cases[i].0, s, bs)); }; }; }; diff --git a/glob/glob.ha b/glob/glob.ha @@ -8,23 +8,25 @@ use sort; use strings; use strio; -// Not all flags are currently supported. -export type flag = enum uint { +// MARK is currently not supported. +export type flags = enum uint { NONE = 0, MARK = 1 << 1, // If the pattern does not match any pathname, the pattern string is // returned. NOCHECK = 1 << 2, + // Backslash escaping is disabled. A backslash character is treated as + // an ordinary character. NOESCAPE = 1 << 3, - // Ordinary, [[next]] sorts the matching pathnames. When this flag is - // used, the order of pathnames returned is unspecified. + // Pathname sorting is disabled. The order of pathnames returned is + // unspecified. NOSORT = 1 << 4, }; export type generator = struct { pats: strstack, matc: size, - flgs: uint, + flgs: flags, tmps: strio::dynamic_stream, }; @@ -43,10 +45,10 @@ export type failure = struct { // Returns a generator of pathnames matching a pattern. The result must be // freed using [[globfree]]. -export fn glob(pattern: str, flags: flag...) generator = { +export fn glob(pattern: str, flags: flags...) generator = { let ss = strstack_init(); strstack_push(&ss, pattern); - let bs = 0u; + let bs = flags::NONE; for (let i = 0z; i < len(flags); i += 1) { bs |= flags[i]; }; @@ -73,7 +75,7 @@ export fn next(gen: *generator) (str | void | failure) = { && len(strio::string(&gen.tmps)) == 0; return match (next_match(os::cwd, gen)) { case void => - if (init && gen.flgs & flag::NOCHECK != 0) { + if (init && gen.flgs & flags::NOCHECK != 0) { return strio::string(&gen.tmps); }; return void; @@ -112,6 +114,9 @@ fn next_match(fs: *fs::fs, gen: *generator) (str | void | failure) = { }; let flgs = fnmatch::flags::PERIOD; + if (gen.flgs & flags::NOESCAPE != 0) { + flgs |= fnmatch::flags::NOESCAPE; + }; let it = match(fs::iter(fs, if (len(dir) > 0) dir else ".")) { case let i: *fs::iterator => yield i; @@ -135,7 +140,7 @@ fn next_match(fs: *fs::fs, gen: *generator) (str | void | failure) = { }; strstack_push(&gen.pats, dir, de.name, "/", rem); }; - if (gen.flgs & flag::NOSORT == 0) { + if (gen.flgs & flags::NOSORT == 0) { strstack_sort(&gen.pats, l); }; @@ -145,6 +150,9 @@ fn next_match(fs: *fs::fs, gen: *generator) (str | void | failure) = { fn split_pattern(p: str) (size, size) = { let pos = (strings::iter(p), 0z); + // TODO: Handle '\' in brackets correctly. + // TODO: Handle escaped '/' correctly. + // p[0..dirend] is path components which contain no special // characters. let dirend = 0z;