diff --git a/src/libs/karm-base/rune.h b/src/libs/karm-base/rune.h index 2d97453ef5..f0ac4742c9 100644 --- a/src/libs/karm-base/rune.h +++ b/src/libs/karm-base/rune.h @@ -450,7 +450,7 @@ usize transcodeLen(Cursor input) { while (input.rem()) { Rune r; - if (Source::decode(r, input)) { + if (Source::decodeUnit(r, input)) { result += 1; } } diff --git a/src/libs/karm-io/glob.cpp b/src/libs/karm-io/glob.cpp new file mode 100644 index 0000000000..ef4f12c2d2 --- /dev/null +++ b/src/libs/karm-io/glob.cpp @@ -0,0 +1,100 @@ +#include "glob.h" + +namespace Karm::Io { + +bool _matchWildCard(SScan &glob, SScan &in); + +bool _matchGroupe(SScan &glob, Rune curr) { + bool neg = glob.skip('^'); + + while (!glob.ended() && + !glob.skip(']')) { + Rune rs = glob.next(); + Rune re = rs; + + if (glob.peek() == '-' && glob.peek(1) != ']') { + re = glob.peek(1); + glob.next(2); + } + + if (re < rs) + std::swap(rs, re); + + if (rs <= curr && curr <= re) { + while (!glob.ended() && + !glob.skip(']')) { + glob.next(); + } + + return !neg; + } + } + + return neg; +} + +bool _matchWildCard(SScan &glob, SScan &in) { + for (usize n = in.rem(); n > 0; n--) { + SScan g = glob, s = in; + s.next(n); + if (matchGlob(g, s)) { + // We found a match, commit the changes + glob = g, in = s; + return true; + } + } + + return matchGlob(glob, in); +} + +bool matchGlob(SScan &glob, SScan &in) { + while (!glob.ended() && !in.ended()) { + auto op = glob.next(); + + switch (op) { + case '?': + if (in.ended()) + return false; + in.next(); + break; + + case '[': + if (!_matchGroupe(glob, in.next())) + return false; + break; + + case '*': + if (!_matchWildCard(glob, in)) + return false; + break; + + case '\\': + if (in.ended()) + return false; + + if (op != in.next()) + return false; + + break; + + default: + if (in.ended()) + return false; + + if (op != in.next()) + return false; + + break; + } + } + + return glob.ended() && in.ended(); +} + +bool matchGlob(Str glob, Str str) { + SScan globScan(glob); + SScan strScan(str); + return matchGlob(globScan, strScan); +} + +} // namespace Karm::Io diff --git a/src/libs/karm-io/glob.h b/src/libs/karm-io/glob.h new file mode 100644 index 0000000000..73f2593ff6 --- /dev/null +++ b/src/libs/karm-io/glob.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace Karm::Io { + +// Match a glob pattern against a string. +// The glob pattern can contain the following special characters: +// - '?' matches any single character. +// - '*' matches any sequence of characters. +// - '[' matches any character in the group. +// - '[^' matches any character not in the group. +// - ']' closes the group. +// - '-' specifies a range in the group. +// - '^' negates the group. +bool matchGlob(SScan &glob, SScan &in); + +// Match a string against a glob pattern. +bool matchGlob(Str glob, Str str); + +} // namespace Karm::Io diff --git a/src/libs/karm-io/tests/test-glob.cpp b/src/libs/karm-io/tests/test-glob.cpp new file mode 100644 index 0000000000..454b35d4d4 --- /dev/null +++ b/src/libs/karm-io/tests/test-glob.cpp @@ -0,0 +1,51 @@ +#include +#include + +namespace Karm::Io::Tests { + +test$(globMatch) { + expect$(matchGlob("", "")); + expect$(matchGlob("a", "a")); + expect$(not matchGlob("a", "")); + expect$(not matchGlob("", "a")); + expect$(matchGlob("abcABC123", "abcABC123")); + expect$(not matchGlob("ABCabc123", "abcABC123")); + expect$(matchGlob("?", "a")); + expect$(matchGlob("?", "b")); + expect$(matchGlob("?", "1")); + expect$(matchGlob("?", "8")); + expect$(not matchGlob("?", "")); + expect$(matchGlob("abcABC*", "abcABC123")); + expect$(matchGlob("abc*123", "abcABC123")); + expect$(matchGlob("abc*123", "abc123")); + expect$(matchGlob("*ABC123", "abcABC123")); + expect$(not matchGlob("abcABC*", "ABCabc123")); + expect$(not matchGlob("abc*123", "ABCabc123")); + expect$(not matchGlob("abc*123", "abcABCXYZ")); + expect$(not matchGlob("*ABC123", "ABCabc123")); + expect$(matchGlob("[abc]", "a")); + expect$(matchGlob("[abc]", "b")); + expect$(matchGlob("[abc]", "c")); + expect$(not matchGlob("[abc]", "1")); + expect$(not matchGlob("[abc]", "2")); + expect$(not matchGlob("[abc]", "3")); + expect$(matchGlob("[a-z]", "a")); + expect$(matchGlob("[a-z]", "z")); + expect$(not matchGlob("[a-z]", "1")); + expect$(not matchGlob("[a-z]", "9")); + expect$(not matchGlob("[^a-z]", "a")); + expect$(not matchGlob("[^a-z]", "z")); + expect$(matchGlob("[^a-z]", "1")); + expect$(matchGlob("[^a-z]", "9")); + expect$(matchGlob("[a-z0-9]", "a")); + expect$(matchGlob("[a-z0-9]", "k")); + expect$(matchGlob("[a-z0-9]", "1")); + expect$(matchGlob("[a-z0-9]", "7")); + expect$(matchGlob("[a-z0-9]", "a")); + expect$(not matchGlob("[a-z0-9]", "A")); + expect$(not matchGlob("[a-z0-9]", "K")); + + return Ok(); +} + +} // namespace Karm::Io::Tests