From a3dad2118fb3925ef4f9baa90cea0dfd44ca93c6 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 25 Nov 2016 11:19:40 +0200 Subject: Allow here-document end marker to be wholly quoted --- build2/test/script/parser.cxx | 41 +++++++++++++++++++++++++++++++++++++++-- build2/test/script/script.cxx | 11 +++++++---- 2 files changed, 46 insertions(+), 6 deletions(-) (limited to 'build2') diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx index a116873..7655ba9 100644 --- a/build2/test/script/parser.cxx +++ b/build2/test/script/parser.cxx @@ -1652,8 +1652,45 @@ namespace build2 // next (t, tt); - if (tt != type::word || t.qtype != quote_type::unquoted) - fail (l) << "expected here-document end marker"; + // We require the end marker to be an unquoted or completely + // quoted word. The complete quoting becomes important for + // cases like foo"$bar" (where we will see word 'foo'). + // + // For good measure we could have also required it to be + // separated from the following token, but out grammar + // allows one to write >>EOO;. The problematic sequence + // would be >>FOO$bar -- on reparse it will be expanded + // as a single word. + // + if (tt != type::word) + fail (t) << "expected here-document end marker"; + + peek (); + const token& p (peeked ()); + if (!p.separated) + { + switch (p.type) + { + case type::dollar: + case type::lparen: + fail (p) << "here-document end marker must be literal"; + } + } + + quote_type qt (t.qtype); + switch (qt) + { + case quote_type::unquoted: + qt = quote_type::single; // Treat as single-quoted. + break; + case quote_type::single: + case quote_type::double_: + if (t.qcomp) + break; + // Fall through. + case quote_type::mixed: + fail (t) << "partially-quoted here-document end marker"; + } hd.push_back (here_doc {0, 0, 0, move (t.value), nn}); break; diff --git a/build2/test/script/script.cxx b/build2/test/script/script.cxx index b128077..8fb8115 100644 --- a/build2/test/script/script.cxx +++ b/build2/test/script/script.cxx @@ -38,14 +38,16 @@ namespace build2 } // Quote if empty or contains spaces or any of the special characters. + // Note that we use single quotes since double quotes still allow + // expansion. // - // @@ What if it contains quotes, escapes? + // @@ What if it contains single quotes? // static void to_stream_q (ostream& o, const string& s) { - if (s.empty () || s.find_first_of (" |&<>=") != string::npos) - o << '"' << s << '"'; + if (s.empty () || s.find_first_of (" |&<>=\\\"") != string::npos) + o << '\'' << s << '\''; else o << s; }; @@ -100,7 +102,8 @@ namespace build2 // Add another '>' or '<'. Note that here end marker never // needs to be quoted. // - o << d << (nl ? "" : ":") << r.doc.end; + o << d << (nl ? "" : ":"); + to_stream_q (o, r.doc.end); break; } case redirect_type::file: -- cgit v1.1