From a17e517e079c33bcb4d6dea94f6c441a5eb2e33a Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 19 Oct 2016 16:41:43 +0200 Subject: Generalize and move test printing code to script --- build2/test/script/parser.cxx | 3 ++ build2/test/script/runner.cxx | 75 --------------------------------- build2/test/script/script | 18 ++++++++ build2/test/script/script.cxx | 97 +++++++++++++++++++++++++++++++++++++++++++ build2/test/script/script.ixx | 39 +++++++++++++++++ 5 files changed, 157 insertions(+), 75 deletions(-) create mode 100644 build2/test/script/script.ixx diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx index 9655778..a9a179b 100644 --- a/build2/test/script/parser.cxx +++ b/build2/test/script/parser.cxx @@ -514,6 +514,9 @@ namespace build2 // interesting characters (operators plus quotes/escapes), // then no need to re-lex. // + // NOTE: updated quoting (script.cxx:to_stream_q()) if adding + // any new characters. + // if (q || s.find_first_of ("|&<>\'\"\\") == string::npos) add_word (move (s), l); else diff --git a/build2/test/script/runner.cxx b/build2/test/script/runner.cxx index 25383cf..3a5eb76 100644 --- a/build2/test/script/runner.cxx +++ b/build2/test/script/runner.cxx @@ -12,81 +12,6 @@ namespace build2 { namespace script { - static ostream& - operator<< (ostream& o, const test& t) - { - auto print_string = [&o] (const string& s) - { - // Quote if empty or contains spaces. - // - if (s.empty () || s.find (' ') != string::npos) - o << '"' << s << '"'; - else - o << s; - }; - - auto print_redirect = [&o, &print_string] (const redirect& r, - const char* prefix) - { - o << ' ' << prefix; - - size_t n (string::traits_type::length (prefix)); - assert (n > 0); - - switch (r.type) - { - case redirect_type::null: o << '!'; break; - case redirect_type::here_string: print_string (r.value); break; - case redirect_type::here_document: - { - o << prefix[n - 1]; // Add another '>' or '<'. - print_string (r.here_end); - break; - } - default: assert (false); - } - }; - - auto print_heredoc = [&o] (const redirect& r) - { - // Here-document value always ends with a newline. - // - o << endl << r.value << r.here_end; - }; - - print_string (t.program.string ()); - - for (const auto& a: t.arguments) - { - o << ' '; - print_string (a); - } - - if (t.in.type != redirect_type::none) - print_redirect (t.in, "<"); - - if (t.out.type != redirect_type::none) - print_redirect (t.out, ">"); - - if (t.err.type != redirect_type::none) - print_redirect (t.err, "2>"); - - if (t.exit.comparison != exit_comparison::eq || t.exit.status != 0) - o << (t.exit.comparison == exit_comparison::eq ? " == " : " != ") - << (int)t.exit.status; - - if (t.in.type == redirect_type::here_document) - print_heredoc (t.in); - - if (t.out.type == redirect_type::here_document) - print_heredoc (t.out); - - if (t.err.type == redirect_type::here_document) - print_heredoc (t.err); - - return o; - } - static void print_test (diag_record& r, const test& t) { diff --git a/build2/test/script/script b/build2/test/script/script index e3afbca..04b964f 100644 --- a/build2/test/script/script +++ b/build2/test/script/script @@ -59,6 +59,19 @@ namespace build2 redirect err; }; + enum class command_to_stream: uint16_t + { + header = 0x01, + here_doc = 0x02, // Note: printed on a new line. + all = header | here_doc + }; + + void + to_stream (ostream&, const command&, command_to_stream); + + ostream& + operator<< (ostream&, const command&); + enum class exit_comparison {eq, ne}; struct command_exit @@ -91,6 +104,9 @@ namespace build2 command_exit exit; }; + ostream& + operator<< (ostream&, const test&); + class scope { public: @@ -162,4 +178,6 @@ namespace build2 } } +#include + #endif // BUILD2_TEST_SCRIPT_SCRIPT diff --git a/build2/test/script/script.cxx b/build2/test/script/script.cxx index 2be023d..1fb100d 100644 --- a/build2/test/script/script.cxx +++ b/build2/test/script/script.cxx @@ -14,6 +14,103 @@ namespace build2 { namespace script { + // Quote if empty or contains spaces or any of the special characters. + // + // @@ What if it contains quotes, escapes? + // + static void + to_stream_q (ostream& o, const string& s) + { + if (s.empty () || s.find_first_of (" |&<>=") != string::npos) + o << '"' << s << '"'; + else + o << s; + }; + + void + to_stream (ostream& o, const command& c, command_to_stream m) + { + auto print_redirect = [&o] (const redirect& r, const char* prefix) + { + o << ' ' << prefix; + + size_t n (string::traits_type::length (prefix)); + assert (n > 0); + + switch (r.type) + { + case redirect_type::none: assert (false); break; + case redirect_type::null: o << '!'; break; + case redirect_type::here_string: to_stream_q (o, r.value); break; + case redirect_type::here_document: + { + // Add another '>' or '<'. Note that here end marker never + // needs to be quoted. + // + o << prefix[n - 1] << r.here_end; + break; + } + } + }; + + auto print_doc = [&o] (const redirect& r) + { + // Here-document value always ends with a newline. + // + o << endl << r.value << r.here_end; + }; + + if ((m & command_to_stream::header) == command_to_stream::header) + { + // Program. + // + to_stream_q (o, c.program.string ()); + + // Arguments. + // + for (const string& a: c.arguments) + { + o << ' '; + to_stream_q (o, a); + } + + // Redirects. + // + if (c.in.type != redirect_type::none) print_redirect (c.in, "<"); + if (c.out.type != redirect_type::none) print_redirect (c.out, ">"); + if (c.err.type != redirect_type::none) print_redirect (c.err, "2>"); + } + + if ((m & command_to_stream::here_doc) == command_to_stream::here_doc) + { + // Here-documents. + // + if (c.in.type == redirect_type::here_document) print_doc (c.in); + if (c.out.type == redirect_type::here_document) print_doc (c.out); + if (c.err.type == redirect_type::here_document) print_doc (c.err); + } + } + + ostream& + operator<< (ostream& o, const test& t) + { + to_stream (o, t, command_to_stream::header); + + if (t.exit.comparison != exit_comparison::eq || t.exit.status != 0) + { + switch (t.exit.comparison) + { + case exit_comparison::eq: o << " == "; break; + case exit_comparison::ne: o << " != "; break; + } + + o << static_cast (t.exit.status); + } + + to_stream (o, t, command_to_stream::here_doc); + return o; + } + script:: script (target& tt, testscript& st) : test_target (tt), script_target (st), diff --git a/build2/test/script/script.ixx b/build2/test/script/script.ixx new file mode 100644 index 0000000..2e215da --- /dev/null +++ b/build2/test/script/script.ixx @@ -0,0 +1,39 @@ +// file : build2/test/script/script.ixx -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +namespace build2 +{ + namespace test + { + namespace script + { + inline command_to_stream + operator&= (command_to_stream& x, command_to_stream y) + { + return x = static_cast ( + static_cast (x) & static_cast (y)); + } + + inline command_to_stream + operator|= (command_to_stream& x, command_to_stream y) + { + return x = static_cast ( + static_cast (x) | static_cast (y)); + } + + inline command_to_stream + operator& (command_to_stream x, command_to_stream y) {return x &= y;} + + inline command_to_stream + operator| (command_to_stream x, command_to_stream y) {return x |= y;} + + inline ostream& + operator<< (ostream& o, const command& c) + { + to_stream (o, c, command_to_stream::all); + return o; + } + } + } +} -- cgit v1.1