hare

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

commit fbf2c44024959c81f02489e099828a2ff2be373a
parent eafd7c548cca799d906622679992cb3f1e5d6fc8
Author: Yasumasa Tada <ytada@spartan.dev>
Date:   Sat, 16 Apr 2022 21:31:42 +0900

glob: support MARK

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

Diffstat:
Mglob/+test.ha | 14+++++++++++---
Mglob/glob.ha | 48+++++++++++++++++++++++++++++++++++++-----------
2 files changed, 48 insertions(+), 14 deletions(-)

diff --git a/glob/+test.ha b/glob/+test.ha @@ -1,11 +1,12 @@ use fnmatch; +use strings; @test fn glob() void = { 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), + ("/?sr/[sb]in/*[[:digit:]]*", flags::MARK), + ("/h??\\e/*/.*", flags::NOSORT | flags::MARK), ("/\\h??e/*/.*", flags::NOSORT | flags::NOESCAPE), ("/r*/*", flags::NOSORT), ("/*.?a", flags::NOCHECK), @@ -31,7 +32,14 @@ use fnmatch; if (cases[i].1 & flags::NOESCAPE != 0) { bs |= fnmatch::flags::NOESCAPE; }; - assert(fnmatch::fnmatch(cases[i].0, s, bs)); + assert(fnmatch::fnmatch(cases[i].0, s, bs) + || cases[i].1 & flags::MARK != 0 + && fnmatch::fnmatch( + cases[i].0, + strings::rtrim(s, '/'), + bs + ) + ); }; }; }; diff --git a/glob/glob.ha b/glob/glob.ha @@ -8,9 +8,11 @@ use sort; use strings; use strio; -// MARK is currently not supported. +// Flags used to control the behavior of [[next]]. export type flags = enum uint { NONE = 0, + // Slash appending is enabled. A slash character is appended to each + // pathname that is a directory that matches the pattern. MARK = 1 << 1, // If the pattern does not match any pathname, the pattern string is // returned. @@ -47,7 +49,7 @@ export type failure = !struct { // freed using [[globfree]]. export fn glob(pattern: str, flags: flags...) generator = { let ss = strstack_init(); - strstack_push(&ss, pattern); + strio::concat(strstack_push(&ss), pattern)!; let bs = flags::NONE; for (let i = 0z; i < len(flags); i += 1) { bs |= flags[i]; @@ -118,14 +120,15 @@ fn next_match(fs: *fs::fs, gen: *generator) (str | void | failure) = { flgs |= fnmatch::flags::NOESCAPE; }; let it = match(fs::iter(fs, if (len(dir) > 0) dir else ".")) { - case let i: *fs::iterator => - yield i; case let e: fs::error => return failure { path = dir, error = e, }; + case let i: *fs::iterator => + yield i; }; + defer fs::finish(it); for (true) match (fs::next(it)) { case void => break; @@ -133,12 +136,33 @@ fn next_match(fs: *fs::fs, gen: *generator) (str | void | failure) = { if (!fnmatch::fnmatch(pat, de.name, flgs)) { continue; }; - if (len(rem) == 0) { - strstack_push(&gen.pats, dir, de.name); - gen.matc += 1; + let b = strstack_push(&gen.pats); + if (len(rem) > 0) { + strio::concat(b, dir, de.name, "/", rem)!; continue; }; - strstack_push(&gen.pats, dir, de.name, "/", rem); + strio::concat(b, dir, de.name)!; + if (gen.flgs & flags::MARK != 0) { + let m = fs::isdir(de.ftype); + // POSIX does not specify the behavior when a pathname + // that matches the pattern is a symlink to a + // directory. But in major implementation a slash + // character is appended in this case. + if (fs::islink(de.ftype)) { + match (fs::realpath(fs, strio::string(b))) { + case let r: str => + match (fs::stat(fs, r)) { + case let s: fs::filestat => + m = fs::isdir(s.mode); + }; + }; + }; + if (m) { + strio::concat(b, "/")!; + }; + }; + gen.matc += 1; + }; if (gen.flgs & flags::NOSORT == 0) { strstack_sort(&gen.pats, l); @@ -204,17 +228,19 @@ fn strstack_free(ss: *strstack) void = { for (let i = 0z; i < len(ss.bufv); i += 1) { io::close(&ss.bufv[i]); }; + free(ss.bufv); }; fn strstack_size(ss: *strstack) size = ss.bufc; -fn strstack_push(ss: *strstack, strs: str...) void = { +fn strstack_push(ss: *strstack) *strio::dynamic_stream = { if (ss.bufc == len(ss.bufv)) { append(ss.bufv, strio::dynamic()); }; - strio::reset(&ss.bufv[ss.bufc]); - strio::concat(&ss.bufv[ss.bufc], strs...)!; + let b = &ss.bufv[ss.bufc]; + strio::reset(b); ss.bufc += 1; + return b; }; fn strstack_pop(ss: *strstack) (str | void) = {