aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/name.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/name.cxx')
-rw-r--r--libbuild2/name.cxx145
1 files changed, 126 insertions, 19 deletions
diff --git a/libbuild2/name.cxx b/libbuild2/name.cxx
index d956a99..6c48bb3 100644
--- a/libbuild2/name.cxx
+++ b/libbuild2/name.cxx
@@ -12,9 +12,30 @@ namespace build2
const name empty_name;
const names empty_names;
+ void name::
+ canonicalize ()
+ {
+ // We cannot assume the name part is a valid filesystem name so we will
+ // have to do the splitting manually.
+ //
+ size_t p (path_traits::rfind_separator (value));
+
+ if (p != string::npos)
+ {
+ if (p + 1 == value.size ())
+ throw invalid_argument ("empty value");
+
+ dir /= dir_path (value, p != 0 ? p : 1); // Special case: "/".
+
+ value.erase (0, p + 1);
+ }
+ }
+
string
to_string (const name& n)
{
+ assert (!n.pattern);
+
string r;
// Note: similar to to_stream() below.
@@ -59,20 +80,76 @@ namespace build2
}
ostream&
- to_stream (ostream& os, const name& n, bool quote, char pair, bool escape)
+ to_stream (ostream& os, const name& n, quote_mode q, char pair, bool escape)
{
- auto write_string = [quote, pair, escape, &os](const string& v)
+ using pattern_type = name::pattern_type;
+
+ auto write_string = [&os, q, pair, escape] (
+ const string& v,
+ optional<pattern_type> pat = nullopt,
+ bool curly = false)
{
- char sc[] = {
+ // We don't expect the effective quoting mode to be specified for the
+ // name patterns.
+ //
+ assert (q != quote_mode::effective || !pat);
+
+ // Special characters, path pattern characters, and regex pattern
+ // characters. The latter only need to be quoted in the first position
+ // and if followed by a non-alphanumeric delimiter. If that's the only
+ // special character, then we handle it with escaping rather than
+ // quoting (see the parsing logic for rationale). Additionally, we
+ // escape leading `+` in the curly braces which is also recognized as a
+ // path pattern.
+ //
+ char nsc[] = {
'{', '}', '[', ']', '$', '(', ')', // Token endings.
' ', '\t', '\n', '#', // Spaces.
'\\', '"', // Escaping and quoting.
'%', // Project name separator.
- '*', '?', // Wildcard characters.
pair, // Pair separator, if any.
'\0'};
- if (quote && v.find ('\'') != string::npos)
+ char pc[] = {
+ '*', '?', // Path wildcard characters.
+ '\0'};
+
+ auto rc = [] (const string& v)
+ {
+ return (v[0] == '~' || v[0] == '^') && v[1] != '\0' && !alnum (v[1]);
+ };
+
+ char esc[] = {
+ '{', '}', '$', '(', // Token endings.
+ ' ', '\t', '\n', '#', // Spaces.
+ '"', // Quoting.
+ pair, // Pair separator, if any.
+ '\0'};
+
+ auto ec = [&esc] (const string& v)
+ {
+ for (size_t i (0); i < v.size (); ++i)
+ {
+ char c (v[i]);
+
+ if (strchr (esc, c) != nullptr || (c == '\\' && v[i + 1] == '\\'))
+ return true;
+ }
+
+ return false;
+ };
+
+ if (pat)
+ {
+ switch (*pat)
+ {
+ case pattern_type::path: break;
+ case pattern_type::regex_pattern: os << '~'; break;
+ case pattern_type::regex_substitution: os << '^'; break;
+ }
+ }
+
+ if (q != quote_mode::none && v.find ('\'') != string::npos)
{
// Quote the string with the double quotes rather than with the single
// one. Escape some of the special characters.
@@ -91,7 +168,15 @@ namespace build2
if (escape) os << '\\';
os << '"';
}
- else if (quote && v.find_first_of (sc) != string::npos)
+ //
+ // Note that a regex pattern does not need to worry about special path
+ // pattern character but not vice-verse. See the parsing logic for
+ // details.
+ //
+ else if ((q == quote_mode::normal &&
+ (v.find_first_of (nsc) != string::npos ||
+ (!pat && v.find_first_of (pc) != string::npos))) ||
+ (q == quote_mode::effective && ec (v)))
{
if (escape) os << '\\';
os << '\'';
@@ -101,26 +186,43 @@ namespace build2
if (escape) os << '\\';
os << '\'';
}
+ // Note that currently we do not preserve a leading `+` as a pattern
+ // unless it has other wildcard characters (see the parsing code for
+ // details). So we escape it both if it's not a pattern or is a path
+ // pattern.
+ //
+ else if (q == quote_mode::normal &&
+ (!pat || *pat == pattern_type::path) &&
+ ((v[0] == '+' && curly) || rc (v)))
+ {
+ if (escape) os << '\\';
+ os << '\\' << v;
+ }
else
os << v;
};
uint16_t dv (stream_verb (os).path); // Directory verbosity.
- auto write_dir = [dv, quote, &os, &write_string] (const dir_path& d)
+ auto write_dir = [&os, q, &write_string, dv] (
+ const dir_path& d,
+ optional<pattern_type> pat = nullopt,
+ bool curly = false)
{
- if (quote)
- write_string (dv < 1 ? diag_relative (d) : d.representation ());
+ if (q != quote_mode::none)
+ write_string (dv < 1 ? diag_relative (d) : d.representation (),
+ pat,
+ curly);
else
os << d;
};
- // Note: similar to to_string() below.
+ // Note: similar to to_string() above.
//
// If quoted then print empty name as '' rather than {}.
//
- if (quote && n.empty ())
+ if (q != quote_mode::none && n.empty ())
return os << (escape ? "\\'\\'" : "''");
if (n.proj)
@@ -147,7 +249,8 @@ namespace build2
if (!pd.empty ())
write_dir (pd);
- if (t || (!d && !v))
+ bool curly;
+ if ((curly = t || (!d && !v)))
{
if (t)
write_string (n.type);
@@ -156,18 +259,22 @@ namespace build2
}
if (v)
- write_string (n.value);
+ write_string (n.value, n.pattern, curly);
else if (d)
{
+ // A directory pattern cannot be regex.
+ //
+ assert (!n.pattern || *n.pattern == pattern_type::path);
+
if (rd.empty ())
- write_string (dir_path (".").representation ());
+ write_string (dir_path (".").representation (), nullopt, curly);
else if (!pd.empty ())
- write_string (rd.leaf ().representation ());
+ write_string (rd.leaf ().representation (), n.pattern, curly);
else
- write_dir (rd);
+ write_dir (rd, n.pattern, curly);
}
- if (t || (!d && !v))
+ if (curly)
os << '}';
return os;
@@ -176,7 +283,7 @@ namespace build2
ostream&
to_stream (ostream& os,
const names_view& ns,
- bool quote,
+ quote_mode q,
char pair,
bool escape)
{
@@ -184,7 +291,7 @@ namespace build2
{
const name& n (*i);
++i;
- to_stream (os, n, quote, pair, escape);
+ to_stream (os, n, q, pair, escape);
if (n.pair)
os << n.pair;