From 258835fb4cd7338f9a0f9a758860cf1a368987dc Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sat, 21 Jul 2018 22:15:29 +0300 Subject: Implement manifest parsing/serialization --- libbutl/manifest-serializer.bash.in | 8 +-- libbutl/manifest.cxx | 114 ++++++++++++++++++++++++++++++++--- tests/manifest-parser/testscript | 107 +++++++++++++++++++++++++++----- tests/manifest-serializer/driver.in | 2 - tests/manifest-serializer/testscript | 108 ++++++++++++++++++++++++++------- 5 files changed, 282 insertions(+), 57 deletions(-) diff --git a/libbutl/manifest-serializer.bash.in b/libbutl/manifest-serializer.bash.in index 24eac65..5b91eed 100644 --- a/libbutl/manifest-serializer.bash.in +++ b/libbutl/manifest-serializer.bash.in @@ -17,13 +17,7 @@ fi # function butl_serialize_manifest () { - # @@ TODO - #"$(butl_path)/manifest" serialize - - local n v - while IFS=: read -r -d '' n v; do - printf "$n: $v\n" - done + "$(butl_path)/manifest" serialize } # Start the manifest serialization co-process setting the following "return" diff --git a/libbutl/manifest.cxx b/libbutl/manifest.cxx index 9319b71..4c85b57 100644 --- a/libbutl/manifest.cxx +++ b/libbutl/manifest.cxx @@ -2,30 +2,80 @@ // copyright : Copyright (c) 2014-2018 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file +#include // ios::failure, ios::*bit #include +#include +#include // size_t #include +#include // invalid_argument #include +#include #include #include using namespace std; using namespace butl; +// Set the binary translation mode for stdout. Throw ios::failure on error. +// Noop on POSIX. +// +// Note that it makes sense to set the binary mode for stdout not to litter +// the resulting manifest representation with the carriage return characters +// on Windows. +// +static void +stdout_binary () +{ + try + { + stdout_fdmode (fdstream_mode::binary); + } + catch (const invalid_argument&) + { + assert (false); // No reason to happen. + } +} + static int cmd_parse () { - //@@ TODO + using parser = manifest_parser; + using parsing = manifest_parsing; + using name_value = manifest_name_value; + + // Parse the manifest list and write its binary representation. + // + try + { + stdout_binary (); + + cin.exceptions (ios::badbit | ios::failbit); + cout.exceptions (ios::badbit | ios::failbit); - const char m[] = - ":1\0" - "name:foo\0" - "version:1.2.3\0" - "description:foo\nexecutable\0" - "depends:libfoo\0" - "depends:libbar"; // Last \0 will be added. + parser p (cin, "stdin"); - cout.write (m, sizeof (m)); + // Iterate over manifests in the list. + // + for (name_value nv (p.next ()); !nv.empty (); nv = p.next ()) + { + // Iterate over manifest name/values. + // + for (; !nv.empty (); nv = p.next ()) + cout << nv.name << ':' << nv.value << '\0' << flush; + } + } + catch (const parsing& e) + { + cerr << e << endl; + return 1; + } + catch (const ios::failure& e) + { + cerr << "error: unable to read from stdin or write to stdout: " << e + << endl; + return 1; + } return 0; } @@ -33,7 +83,51 @@ cmd_parse () static int cmd_serialize () { - //@@ TODO + using serializer = manifest_serializer; + using serialization = manifest_serialization; + + try + { + stdout_binary (); + + // Don't throw when failbit is set (getline() failed to extract any + // characters). + // + cin.exceptions (ios::badbit); + + cout.exceptions (ios::badbit | ios::failbit); + + serializer s (cout, "stdout"); + + for (string l; !eof (getline (cin, l, '\0')); ) + { + size_t p (l.find (':')); + + if (p == string::npos) + throw serialization (s.name (), "':' expected after name"); + + string n (l, 0, p); + string v (l, p + 1); + + // Validates the name. Expects the first pair to be the format version. + // Ends current and starts next manifest for the start-of-manifest pair. + // + s.next (n, v); + } + + s.next ("", ""); // End of manifest list. + } + catch (const serialization& e) + { + cerr << e << endl; + return 1; + } + catch (const ios::failure& e) + { + cerr << "error: unable to read from stdin or write to stdout: " << e + << endl; + return 1; + } return 0; } diff --git a/tests/manifest-parser/testscript b/tests/manifest-parser/testscript index 154b107..78f4160 100644 --- a/tests/manifest-parser/testscript +++ b/tests/manifest-parser/testscript @@ -2,24 +2,101 @@ # copyright : Copyright (c) 2014-2018 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file -$* >>EOO -1 +: stdin +: +{ + $* <>EOO + : 1 + name: foo + version: 1.2.3 + description: \ + foo + executable + \ + depends: libfoo + depends: libbar + EOI + 1 -name -foo + name + foo -version -1.2.3 + version + 1.2.3 -description -foo -executable + description + foo + executable -depends -libfoo + depends + libfoo -depends -libbar -EOO + depends + libbar + EOO +} -#@@ TODO: test reading from file instead of stdin. +: file +: +{ + cat <=file; + : 1 + name: foo + version: 1.2.3 + EOI + + $* file >>EOO + 1 + + name + foo + + version + 1.2.3 + EOO +} + +: list +: +{ + $* <>EOO + : 1 + name: foo + : + version: 1.2.3 + EOI + 1 + + name + foo + 1 + + version + 1.2.3 + EOO +} + +: version +: +{ + : absent + : + $* <'stdin:1:1: error: format version pair expected' != 0 + name: foo + EOI + + : unsupported + : + $* <- 2>'stdin:3:3: error: unsupported format version 2' != 0 + : 1 + name: foo + : 2 + version: 1.2.3 + EOI +} + +: empty +: +{ + $* <:'' >:'' +} diff --git a/tests/manifest-serializer/driver.in b/tests/manifest-serializer/driver.in index 9e619e5..4ee31a7 100644 --- a/tests/manifest-serializer/driver.in +++ b/tests/manifest-serializer/driver.in @@ -14,7 +14,6 @@ butl_manifest_serializer_start "$@" fd="$butl_manifest_serializer_ifd" while read -r n; do - printf "%s:" "$n" >&"$fd" f=true @@ -31,7 +30,6 @@ while read -r n; do printf "%s" "$v" >&"$fd" fi done - done butl_manifest_serializer_finish diff --git a/tests/manifest-serializer/testscript b/tests/manifest-serializer/testscript index af96cc0..f305342 100644 --- a/tests/manifest-serializer/testscript +++ b/tests/manifest-serializer/testscript @@ -2,34 +2,96 @@ # copyright : Copyright (c) 2014-2018 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file -$* <>EOO +: stdout +{ + $* <>EOO -1 + 1 -name -foo + name + foo -version -1.2.3 + version + 1.2.3 -description -foo -executable + description + foo + executable -depends -libfoo + depends + libfoo -depends -libbar + depends + libbar -EOI -: 1 -name: foo -version: 1.2.3 -description: foo -executable -depends: libfoo -depends: libbar -EOO + EOI + : 1 + name: foo + version: 1.2.3 + description: \ + foo + executable + \ + depends: libfoo + depends: libbar + EOO +} -#@@ TODO: test writing to file instead of stdout. +: file +{ + $* <=file; + + 1 + + name + foo + + version + 1.2.3 + EOI + + cat file >>EOE + : 1 + name: foo + version: 1.2.3 + EOE +} + +: list +{ + $* <>EOO + + 1 + + name + foo + + + 1 + + version + 1.2.3 + EOI + : 1 + name: foo + : + version: 1.2.3 + EOO +} + +: version +: +{ + : absent + : + $* <'stdout: error: format version pair expected' != 0 + name + foo + EOI +} + +: empty +: +{ + $* <:'' >:'' +} -- cgit v1.1