From c595aac0142436f64ada4f5412b821bfcc6db008 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 19 Sep 2019 17:43:02 +0300 Subject: Adapt to bracket expressions in wildcard patterns --- libbuild2/cc/link-rule.cxx | 2 +- libbuild2/functions-filesystem.cxx | 5 +-- libbuild2/parser.cxx | 82 ++++++++++++++++++++++---------------- libbuild2/test/script/runner.cxx | 5 ++- tests/name/cross.testscript | 2 +- 5 files changed, 54 insertions(+), 42 deletions(-) diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index 2eeebda..14e5b94 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -8,7 +8,7 @@ #include // exit() #include // strlen() -#include // file_exists() +#include // file_exists(), path_search() #include #include diff --git a/libbuild2/functions-filesystem.cxx b/libbuild2/functions-filesystem.cxx index 2fcd305..d0861ba 100644 --- a/libbuild2/functions-filesystem.cxx +++ b/libbuild2/functions-filesystem.cxx @@ -93,10 +93,7 @@ namespace build2 bool rel (pattern.relative () == entry.relative () && !pattern.empty () && !entry.empty ()); - bool self (!pattern.empty () && - (*pattern.begin ()).find ("***") != string::npos); - - if (rel && !self) + if (rel && !path_pattern_self_matching (pattern)) return path_match (pattern, entry); // The start directory must be specified and be absolute. diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index a7e65dd..ffe64fb 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -7,7 +7,7 @@ #include #include // cout -#include // path_search(), path_match() +#include // path_{search,match,pattern}() #include #include @@ -21,6 +21,7 @@ #include using namespace std; +using namespace butl; namespace build2 { @@ -275,14 +276,6 @@ namespace build2 return make_pair (move (lhs), move (t)); } - // Test if a string is a wildcard pattern. - // - static inline bool - pattern (const string& s) - { - return s.find_first_of ("*?") != string::npos; - }; - bool parser:: parse_clause (token& t, type& tt, bool one) { @@ -553,7 +546,7 @@ namespace build2 // Figure out if this is a target or a target type/pattern (yeah, // it can be a mixture). // - if (pattern (n.value)) + if (path_pattern (n.value)) { if (n.pair) fail (nloc) << "out-qualified target type/pattern"; @@ -772,7 +765,7 @@ namespace build2 // Make sure not a pattern (see also the target case above and scope // below). // - if (pattern (d.string ())) + if (path_pattern (d)) fail (nloc) << "pattern in directory " << d.representation (); if (tt != type::lsbrace) @@ -820,7 +813,7 @@ namespace build2 // Make sure not a pattern (see also the target and directory cases // above). // - if (pattern (d.string ())) + if (path_pattern (d)) fail (nloc) << "pattern in directory " << d.representation (); next (t, tt); // Newline. @@ -1004,7 +997,7 @@ namespace build2 // Make sure none of our targets are patterns (maybe we will allow // quoting later). // - if (pattern (n.value)) + if (path_pattern (n.value)) fail (tloc) << "pattern in target " << n; enter_target tg (*this, @@ -3350,7 +3343,7 @@ namespace build2 auto match = [&dir, sp] (const path& pattern, const name& n) -> bool { const path& p (dir ? path_cast (n.dir) : path (n.value)); - return butl::path_match (pattern, p, *sp); + return path_match (pattern, p, *sp); }; // Append name/extension to result according to dir. Store an indication @@ -3386,6 +3379,8 @@ namespace build2 append (move (m), move (e), a); }; + // May throw invalid_path. + // auto include_pattern = [&r, &append, &include_match, sp, &l, this] (string&& p, optional&& e, @@ -3396,12 +3391,7 @@ namespace build2 // can skip checking for duplicated. This should help quite a bit in the // common cases where we have a pattern plus maybe a few exclusions. // - bool unique (false); - if (r.empty ()) - { - size_t i (p.find ("**")); - unique = (i == string::npos || p.find ("**", i + 2) == string::npos); - } + bool unique (r.empty () && path_pattern_recursive (path (p)) <= 1); function&&)> appf; if (unique) @@ -3443,7 +3433,7 @@ namespace build2 try { - butl::path_search (path (move (p)), process, *sp); + path_search (path (move (p)), process, *sp); } catch (const system_error& e) { @@ -3465,13 +3455,11 @@ namespace build2 r.erase (i); }; - auto exclude_pattern = [&r, &match] (string&& p) + auto exclude_pattern = [&r, &match] (const path& p) { - path pattern (move (p)); - for (auto i (r.begin ()); i != r.end (); ) { - if (match (pattern, *i)) + if (match (p, *i)) i = r.erase (i); else ++i; @@ -3576,10 +3564,12 @@ namespace build2 include_pattern (move (v), move (e), a); else { - if (v.find_first_of ("*?") != string::npos) - exclude_pattern (move (v)); + path p (move (v)); + + if (path_pattern (p)) + exclude_pattern (p); else - exclude_match (move (v)); + exclude_match (move (p).representation ()); // Reuse the buffer. } } catch (const invalid_path& e) @@ -4195,6 +4185,7 @@ namespace build2 continue; string val (move (t.value)); + const location loc (get_location (t)); bool quoted (t.qtype != quote_type::unquoted); // Should we accumulate? If the buffer is not empty, then we continue @@ -4266,11 +4257,11 @@ namespace build2 if (quoted) // See above. break; - fail (t) << "invalid project name '" << proj << "': " << e; + fail (loc) << "invalid project name '" << proj << "': " << e; } if (pp) - fail (t) << "nested project name " << *p1; + fail (loc) << "nested project name " << *p1; pp1 = &p1; @@ -4311,7 +4302,7 @@ namespace build2 } if (p != n && tp != nullptr && !pinc) - fail (t) << "nested type name " << val; + fail (loc) << "nested type name " << val; dir_path d1; const dir_path* dp1 (dp); @@ -4347,7 +4338,7 @@ namespace build2 } catch (const invalid_path& e) { - fail (t) << "invalid path '" << e.path << "'"; + fail (loc) << "invalid path '" << e.path << "'"; } count = parse_names_trailer ( @@ -4374,12 +4365,35 @@ namespace build2 // It should either contain a wildcard character or, in a curly // context, start with unquoted '+'. // + // Note that in the general case we need to convert it to a path prior + // to testing for being a pattern (think of b[a/r] that is not a + // pattern). If the conversion fails then this is not a path pattern. + // + auto pattern = [&val, &loc, this] () + { + // Let's optimize it a bit for the common cases. + // + if (val.find_first_of ("*?[") == string::npos) + return false; + + if (path::traits_type::find_separator (val) == string::npos) + return path_pattern (val); + + try + { + return path_pattern (path (val)); + } + catch (const invalid_path& e) + { + fail (loc) << "invalid path '" << e.path << "'" << endf; + } + }; + if (pmode != pattern_mode::ignore && !*pp1 && // Cannot be project-qualified. !quoted && // Cannot be quoted. ((dp != nullptr && dp->absolute ()) || pbase_ != nullptr) && - ((val.find_first_of ("*?") != string::npos) || - (curly && val[0] == '+'))) + (pattern () || (curly && val[0] == '+'))) { // Resolve the target if there is one. If we fail, then this is not // a pattern. diff --git a/libbuild2/test/script/runner.cxx b/libbuild2/test/script/runner.cxx index 554edd0..56e29b1 100644 --- a/libbuild2/test/script/runner.cxx +++ b/libbuild2/test/script/runner.cxx @@ -8,7 +8,8 @@ #include #include -#include // fdopen_mode, fdnull(), fddup() +#include // fdopen_mode, fdnull(), fddup() +#include // path_search(), path_pattern() #include #include @@ -800,7 +801,7 @@ namespace build2 // Remove files or directories using wildcard. // - if (p.string ().find_first_of ("?*") != string::npos) + if (path_pattern (p)) { bool removed (false); diff --git a/tests/name/cross.testscript b/tests/name/cross.testscript index 263a1d1..198f890 100644 --- a/tests/name/cross.testscript +++ b/tests/name/cross.testscript @@ -87,7 +87,7 @@ EOI EOE $* <'print {dir/foo}{fox{bar}}' 2>>EOE != 0 - :1:20: error: nested type name fox + :1:17: error: nested type name fox EOE } -- cgit v1.1