aboutsummaryrefslogtreecommitdiff
path: root/libbutl/manifest.cxx
blob: 4c85b57042e26174eee162fb8c92e6ac9b117f47 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// file      : libbutl/manifest.cxx
// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#include <ios>       // ios::failure, ios::*bit
#include <string>
#include <cassert>
#include <cstddef>   // size_t
#include <iostream>
#include <stdexcept> // invalid_argument

#include <libbutl/utility.mxx>
#include <libbutl/fdstream.mxx>
#include <libbutl/manifest-parser.mxx>
#include <libbutl/manifest-serializer.mxx>

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 ()
{
  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);

    parser p (cin, "stdin");

    // 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;
}

static int
cmd_serialize ()
{
  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;
}

int
main (int argc, char* argv[])
{
  // We should switch to CLI if we need anything more elaborate.
  //
  if (argc < 2)
  {
    cerr << "error: missing command" << endl;
    return 1;
  }

  string c (argv[1]);

  if (c == "parse")     return cmd_parse ();
  if (c == "serialize") return cmd_serialize ();

  cerr << "error: unknown command '" << c << "'" << endl;
  return 1;
}