aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2022-02-16 17:15:46 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2022-02-16 22:26:38 +0300
commit7863221ec4af93d9c57644be170aa49ca23c8f7a (patch)
tree04efeb92dfee241cb4b1f4db9c71ccaf77a2c8d4
parent042de06bcc281832d1eb03c06b85fdca78b76012 (diff)
Invent quoting modes for to_stream(name)
-rw-r--r--libbuild2/adhoc-rule-buildscript.cxx2
-rw-r--r--libbuild2/build/script/parser.cxx2
-rw-r--r--libbuild2/config/operation.cxx6
-rw-r--r--libbuild2/functions-builtin.cxx2
-rw-r--r--libbuild2/name.cxx55
-rw-r--r--libbuild2/name.hxx28
-rw-r--r--libbuild2/name.test.cxx16
-rw-r--r--libbuild2/parser.cxx2
-rw-r--r--libbuild2/script/parser.cxx6
-rw-r--r--libbuild2/test/script/parser.cxx2
10 files changed, 86 insertions, 35 deletions
diff --git a/libbuild2/adhoc-rule-buildscript.cxx b/libbuild2/adhoc-rule-buildscript.cxx
index c64dbfb..7174296 100644
--- a/libbuild2/adhoc-rule-buildscript.cxx
+++ b/libbuild2/adhoc-rule-buildscript.cxx
@@ -183,7 +183,7 @@ namespace build2
{
os << " [";
os << "diag=";
- to_stream (os, name (*script.diag_name), true /* quote */, '@');
+ to_stream (os, name (*script.diag_name), quote_mode::normal, '@');
os << ']';
}
}
diff --git a/libbuild2/build/script/parser.cxx b/libbuild2/build/script/parser.cxx
index dd6fa2d..cba4b88 100644
--- a/libbuild2/build/script/parser.cxx
+++ b/libbuild2/build/script/parser.cxx
@@ -1439,7 +1439,7 @@ namespace build2
{
diag_record dr (fail (l));
dr << "depdb dyndep: invalid string value ";
- to_stream (dr.os, n, true /* quote */);
+ to_stream (dr.os, n, quote_mode::normal);
}
}
}
diff --git a/libbuild2/config/operation.cxx b/libbuild2/config/operation.cxx
index 8ceb4d4..2fb0423 100644
--- a/libbuild2/config/operation.cxx
+++ b/libbuild2/config/operation.cxx
@@ -42,7 +42,7 @@ namespace build2
ofs << "# Created automatically by the config module." << endl
<< "#" << endl
<< "src_root = ";
- to_stream (ofs, name (src_root), true /* quote */, '@');
+ to_stream (ofs, name (src_root), quote_mode::normal, '@');
ofs << endl;
ofs.close ();
@@ -71,7 +71,7 @@ namespace build2
ofs << "# Created automatically by the config module." << endl
<< "#" << endl
<< "out_root = ";
- to_stream (ofs, name (out_root), true /* quote */, '@');
+ to_stream (ofs, name (out_root), quote_mode::normal, '@');
ofs << endl;
ofs.close ();
@@ -539,7 +539,7 @@ namespace build2
if (!p.first.empty ())
{
os << ' ';
- to_stream (os, p.first, true /* quote */, '@');
+ to_stream (os, p.first, quote_mode::normal, '@');
}
os << endl;
diff --git a/libbuild2/functions-builtin.cxx b/libbuild2/functions-builtin.cxx
index c013c3b..93f2d0f 100644
--- a/libbuild2/functions-builtin.cxx
+++ b/libbuild2/functions-builtin.cxx
@@ -94,7 +94,7 @@ namespace build2
ostringstream os;
to_stream (os,
v->as<names> (),
- true /* quote */,
+ quote_mode::normal,
'@' /* pair */,
escape && convert<bool> (move (*escape)));
return os.str ();
diff --git a/libbuild2/name.cxx b/libbuild2/name.cxx
index 1081b5c..a285459 100644
--- a/libbuild2/name.cxx
+++ b/libbuild2/name.cxx
@@ -80,15 +80,21 @@ 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)
{
using pattern_type = name::pattern_type;
- auto write_string = [&os, quote, pair, escape] (
+ auto write_string = [&os, q, pair, escape] (
const string& v,
optional<pattern_type> pat = nullopt,
bool curly = false)
{
+ // We don't expect the effective quoting mode to be specified for the
+ // name patterns or names that need curly braces in their
+ // representations.
+ //
+ assert (q != quote_mode::effective || (!pat && !curly));
+
// 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
@@ -97,7 +103,7 @@ namespace build2
// escape leading `+` in the curly braces which is also recognized as a
// path pattern.
//
- char sc[] = {
+ char nsc[] = {
'{', '}', '[', ']', '$', '(', ')', // Token endings.
' ', '\t', '\n', '#', // Spaces.
'\\', '"', // Escaping and quoting.
@@ -114,6 +120,26 @@ namespace build2
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)
@@ -124,7 +150,7 @@ namespace build2
}
}
- if (quote && v.find ('\'') != string::npos)
+ 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.
@@ -148,8 +174,10 @@ namespace build2
// pattern character but not vice-verse. See the parsing logic for
// details.
//
- else if (quote && (v.find_first_of (sc) != string::npos ||
- (!pat && v.find_first_of (pc) != string::npos)))
+ 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 << '\'';
@@ -164,8 +192,9 @@ namespace build2
// details). So we escape it both if it's not a pattern or is a path
// pattern.
//
- else if (quote && ((!pat || *pat == pattern_type::path) &&
- ((v[0] == '+' && curly) || rc (v))))
+ else if (q == quote_mode::normal &&
+ (!pat || *pat == pattern_type::path) &&
+ ((v[0] == '+' && curly) || rc (v)))
{
if (escape) os << '\\';
os << '\\' << v;
@@ -176,12 +205,12 @@ namespace build2
uint16_t dv (stream_verb (os).path); // Directory verbosity.
- auto write_dir = [&os, quote, &write_string, dv] (
+ auto write_dir = [&os, q, &write_string, dv] (
const dir_path& d,
optional<pattern_type> pat = nullopt,
bool curly = false)
{
- if (quote)
+ if (q != quote_mode::none)
write_string (dv < 1 ? diag_relative (d) : d.representation (),
pat,
curly);
@@ -194,7 +223,7 @@ namespace build2
// 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)
@@ -255,7 +284,7 @@ namespace build2
ostream&
to_stream (ostream& os,
const names_view& ns,
- bool quote,
+ quote_mode q,
char pair,
bool escape)
{
@@ -263,7 +292,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;
diff --git a/libbuild2/name.hxx b/libbuild2/name.hxx
index 1dd5a9f..795cd5c 100644
--- a/libbuild2/name.hxx
+++ b/libbuild2/name.hxx
@@ -184,8 +184,8 @@ namespace build2
to_name (string);
// Serialize the name to the stream. If requested, the name components
- // containing special characters are quoted and/or escaped. The special
- // characters are:
+ // containing special characters are quoted and/or escaped. In the normal
+ // quoting mode the special characters are:
//
// {}[]$() \t\n#\"'%
//
@@ -199,8 +199,14 @@ namespace build2
//
// As well as leading `+` if in the curly braces.
//
+ // In the effective quoting mode the special characters are:
+ //
+ // $( \t\n#"'
+ //
+ // As well as `\` if followed by any of the above characters or itself.
+ //
// If the pair argument is not '\0', then it is added to the above special
- // characters set. If the quote character is present in the component then
+ // characters sets. If the quote character is present in the component then
// it is double quoted rather than single quoted. In this case the following
// characters are escaped:
//
@@ -213,15 +219,23 @@ namespace build2
// Note that in the quoted mode empty unqualified name is printed as '',
// not {}.
//
+ enum class quote_mode
+ {
+ none,
+ normal,
+ effective
+ };
+
LIBBUILD2_SYMEXPORT ostream&
to_stream (ostream&,
const name&,
- bool quote,
+ quote_mode,
char pair = '\0',
bool escape = false);
inline ostream&
- operator<< (ostream& os, const name& n) {return to_stream (os, n, false);}
+ operator<< (ostream& os, const name& n) {
+ return to_stream (os, n, quote_mode::none);}
// Vector of names.
//
@@ -240,13 +254,13 @@ namespace build2
LIBBUILD2_SYMEXPORT ostream&
to_stream (ostream&,
const names_view&,
- bool quote,
+ quote_mode,
char pair = '\0',
bool escape = false);
inline ostream&
operator<< (ostream& os, const names_view& ns) {
- return to_stream (os, ns, false);}
+ return to_stream (os, ns, quote_mode::none);}
inline ostream&
operator<< (ostream& os, const names& ns) {return os << names_view (ns);}
diff --git a/libbuild2/name.test.cxx b/libbuild2/name.test.cxx
index 80b830e..c404503 100644
--- a/libbuild2/name.test.cxx
+++ b/libbuild2/name.test.cxx
@@ -46,7 +46,7 @@ namespace build2
// Test stream representation.
//
{
- auto ts = [] (const name& n, bool quote = true)
+ auto ts = [] (const name& n, quote_mode quote = quote_mode::normal)
{
ostringstream os;
stream_verb (os, stream_verbosity (0, 1));
@@ -54,8 +54,8 @@ namespace build2
return os.str ();
};
- assert (ts (name ()) == "''");
- assert (ts (name (), false) == "{}");
+ assert (ts (name ()) == "''");
+ assert (ts (name (), quote_mode::none) == "{}");
assert (ts (name ("foo")) == "foo");
@@ -70,10 +70,18 @@ namespace build2
assert (ts (name (dir ("bar/"), "dir", "foo")) == "bar/dir{foo}");
assert (ts (name (dir ("bar/baz/"), "dir", "foo")) == "bar/baz/dir{foo}");
- // Quoting.
+ // Normal quoting.
//
assert (ts (name (dir ("bar baz/"), "dir", "foo fox")) == "'bar baz/'dir{'foo fox'}");
+ // Effective quoting.
+ //
+ assert (ts (name ("bar\\baz"), quote_mode::effective) == "bar\\baz");
+ assert (ts (name ("bar[baz]"), quote_mode::effective) == "bar[baz]");
+ assert (ts (name ("bar$baz"), quote_mode::effective) == "'bar$baz'");
+ assert (ts (name ("bar\\\\baz"), quote_mode::effective) == "'bar\\\\baz'");
+ assert (ts (name ("bar\\$baz"), quote_mode::effective) == "'bar\\$baz'");
+
// Relative logic.
//
#ifndef _WIN32
diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx
index 9fbcd2b..bec3230 100644
--- a/libbuild2/parser.cxx
+++ b/libbuild2/parser.cxx
@@ -42,7 +42,7 @@ namespace build2
{
o << '=';
names storage;
- to_stream (o, reverse (a.value, storage), true /* quote */, '@');
+ to_stream (o, reverse (a.value, storage), quote_mode::normal, '@');
}
return o;
diff --git a/libbuild2/script/parser.cxx b/libbuild2/script/parser.cxx
index 7722002..82eb9c8 100644
--- a/libbuild2/script/parser.cxx
+++ b/libbuild2/script/parser.cxx
@@ -1115,7 +1115,7 @@ namespace build2
{
diag_record dr (fail (l));
dr << "invalid string value ";
- to_stream (dr.os, n, true /* quote */);
+ to_stream (dr.os, n, quote_mode::normal);
}
// If it is a quoted chunk, then we add the word as is.
@@ -1351,7 +1351,7 @@ namespace build2
{
diag_record dr (fail (l));
dr << "invalid string value ";
- to_stream (dr.os, n, true /* quote */);
+ to_stream (dr.os, n, quote_mode::normal);
}
}
@@ -1537,7 +1537,7 @@ namespace build2
diag_record dr;
dr << fail (l) << "expected exit status instead of ";
- to_stream (dr.os, ns, true /* quote */);
+ to_stream (dr.os, ns, quote_mode::normal);
dr << info << "exit status is an unsigned integer less than 256";
}
diff --git a/libbuild2/test/script/parser.cxx b/libbuild2/test/script/parser.cxx
index 9e92f3b..bf04a30 100644
--- a/libbuild2/test/script/parser.cxx
+++ b/libbuild2/test/script/parser.cxx
@@ -1057,7 +1057,7 @@ namespace build2
diag_record dr (fail (dl));
dr << "invalid testscript include path ";
- to_stream (dr.os, n, true); // Quote.
+ to_stream (dr.os, n, quote_mode::normal);
}
}