xref: /aosp_15_r20/external/sandboxed-api/contrib/jsonnet/jsonnet.patch (revision ec63e07ab9515d95e79c211197c445ef84cefa6a)
1*ec63e07aSXin Li--- jsonnet.cpp	2020-09-09 12:15:33.687539042 +0000
2*ec63e07aSXin Li+++ write_helper.cpp	2020-09-25 15:38:37.317147682 +0000
3*ec63e07aSXin Li@@ -14,559 +14,125 @@
4*ec63e07aSXin Li limitations under the License.
5*ec63e07aSXin Li */
6*ec63e07aSXin Li
7*ec63e07aSXin Li-#include <cassert>
8*ec63e07aSXin Li-#include <cstdlib>
9*ec63e07aSXin Li-#include <cstring>
10*ec63e07aSXin Li+// We need two functions defined in jsonnet.cpp file, used for writing output
11*ec63e07aSXin Li+// (multiple files and yaml streams) -- with minor changes (e.x. return type).
12*ec63e07aSXin Li
13*ec63e07aSXin Li-#include <exception>
14*ec63e07aSXin Li #include <fstream>
15*ec63e07aSXin Li #include <iostream>
16*ec63e07aSXin Li-#include <list>
17*ec63e07aSXin Li #include <map>
18*ec63e07aSXin Li-#include <sstream>
19*ec63e07aSXin Li-#include <string>
20*ec63e07aSXin Li #include <vector>
21*ec63e07aSXin Li
22*ec63e07aSXin Li-#include "utils.h"
23*ec63e07aSXin Li+#include "contrib/jsonnet/jsonnet_helper.h"
24*ec63e07aSXin Li
25*ec63e07aSXin Li-extern "C" {
26*ec63e07aSXin Li-#include <libjsonnet.h>
27*ec63e07aSXin Li-}
28*ec63e07aSXin Li-
29*ec63e07aSXin Li-#ifdef _WIN32
30*ec63e07aSXin Li-const char PATH_SEP = ';';
31*ec63e07aSXin Li-#else
32*ec63e07aSXin Li-const char PATH_SEP = ':';
33*ec63e07aSXin Li-#endif
34*ec63e07aSXin Li-
35*ec63e07aSXin Li-void version(std::ostream &o)
36*ec63e07aSXin Li-{
37*ec63e07aSXin Li-    o << "Jsonnet commandline interpreter " << jsonnet_version() << std::endl;
38*ec63e07aSXin Li-}
39*ec63e07aSXin Li-
40*ec63e07aSXin Li-void usage(std::ostream &o)
41*ec63e07aSXin Li-{
42*ec63e07aSXin Li-    version(o);
43*ec63e07aSXin Li-    o << "\n";
44*ec63e07aSXin Li-    o << "jsonnet {<option>} <filename>\n";
45*ec63e07aSXin Li-    o << "\n";
46*ec63e07aSXin Li-    o << "Available options:\n";
47*ec63e07aSXin Li-    o << "  -h / --help             This message\n";
48*ec63e07aSXin Li-    o << "  -e / --exec             Treat filename as code\n";
49*ec63e07aSXin Li-    o << "  -J / --jpath <dir>      Specify an additional library search dir (right-most wins)\n";
50*ec63e07aSXin Li-    o << "  -o / --output-file <file> Write to the output file rather than stdout\n";
51*ec63e07aSXin Li-    o << "  -m / --multi <dir>      Write multiple files to the directory, list files on stdout\n";
52*ec63e07aSXin Li-    o << "  -y / --yaml-stream      Write output as a YAML stream of JSON documents\n";
53*ec63e07aSXin Li-    o << "  -S / --string           Expect a string, manifest as plain text\n";
54*ec63e07aSXin Li-    o << "  -s / --max-stack <n>    Number of allowed stack frames\n";
55*ec63e07aSXin Li-    o << "  -t / --max-trace <n>    Max length of stack trace before cropping\n";
56*ec63e07aSXin Li-    o << "  --gc-min-objects <n>    Do not run garbage collector until this many\n";
57*ec63e07aSXin Li-    o << "  --gc-growth-trigger <n> Run garbage collector after this amount of object growth\n";
58*ec63e07aSXin Li-    o << "  --version               Print version\n";
59*ec63e07aSXin Li-    o << "Available options for specifying values of 'external' variables:\n";
60*ec63e07aSXin Li-    o << "Provide the value as a string:\n";
61*ec63e07aSXin Li-    o << "  -V / --ext-str <var>[=<val>]     If <val> is omitted, get from environment var <var>\n";
62*ec63e07aSXin Li-    o << "       --ext-str-file <var>=<file> Read the string from the file\n";
63*ec63e07aSXin Li-    o << "Provide a value as Jsonnet code:\n";
64*ec63e07aSXin Li-    o << "  --ext-code <var>[=<code>]    If <code> is omitted, get from environment var <var>\n";
65*ec63e07aSXin Li-    o << "  --ext-code-file <var>=<file> Read the code from the file\n";
66*ec63e07aSXin Li-    o << "Available options for specifying values of 'top-level arguments':\n";
67*ec63e07aSXin Li-    o << "Provide the value as a string:\n";
68*ec63e07aSXin Li-    o << "  -A / --tla-str <var>[=<val>]     If <val> is omitted, get from environment var <var>\n";
69*ec63e07aSXin Li-    o << "       --tla-str-file <var>=<file> Read the string from the file\n";
70*ec63e07aSXin Li-    o << "Provide a value as Jsonnet code:\n";
71*ec63e07aSXin Li-    o << "  --tla-code <var>[=<code>]    If <code> is omitted, get from environment var <var>\n";
72*ec63e07aSXin Li-    o << "  --tla-code-file <var>=<file> Read the code from the file\n";
73*ec63e07aSXin Li-    o << "Environment variables:\n";
74*ec63e07aSXin Li-    o << "JSONNET_PATH is a colon (semicolon on Windows) separated list of directories added\n";
75*ec63e07aSXin Li-    o << "in reverse order before the paths specified by --jpath (i.e. left-most wins)\n";
76*ec63e07aSXin Li-    o << "E.g. JSONNET_PATH=a:b jsonnet -J c -J d is equivalent to:\n";
77*ec63e07aSXin Li-    o << "JSONNET_PATH=d:c:a:b jsonnet\n";
78*ec63e07aSXin Li-    o << "jsonnet -J b -J a -J c -J d\n";
79*ec63e07aSXin Li-    o << "\n";
80*ec63e07aSXin Li-    o << "In all cases:\n";
81*ec63e07aSXin Li-    o << "<filename> can be - (stdin)\n";
82*ec63e07aSXin Li-    o << "Multichar options are expanded e.g. -abc becomes -a -b -c.\n";
83*ec63e07aSXin Li-    o << "The -- option suppresses option processing for subsequent arguments.\n";
84*ec63e07aSXin Li-    o << "Note that since filenames and jsonnet programs can begin with -, it is advised to\n";
85*ec63e07aSXin Li-    o << "use -- if the argument is unknown, e.g. jsonnet -- \"$FILENAME\".";
86*ec63e07aSXin Li-    o << std::endl;
87*ec63e07aSXin Li-}
88*ec63e07aSXin Li-
89*ec63e07aSXin Li-/** Class for representing configuration read from command line flags.  */
90*ec63e07aSXin Li-struct JsonnetConfig {
91*ec63e07aSXin Li-    std::vector<std::string> inputFiles;
92*ec63e07aSXin Li-    std::string outputFile;
93*ec63e07aSXin Li-    bool filenameIsCode;
94*ec63e07aSXin Li-
95*ec63e07aSXin Li-    // EVAL flags
96*ec63e07aSXin Li-    bool evalMulti;
97*ec63e07aSXin Li-    bool evalStream;
98*ec63e07aSXin Li-    std::string evalMultiOutputDir;
99*ec63e07aSXin Li-
100*ec63e07aSXin Li-    JsonnetConfig()
101*ec63e07aSXin Li-        : filenameIsCode(false),
102*ec63e07aSXin Li-          evalMulti(false),
103*ec63e07aSXin Li-          evalStream(false)
104*ec63e07aSXin Li-    {
105*ec63e07aSXin Li-    }
106*ec63e07aSXin Li-};
107*ec63e07aSXin Li-
108*ec63e07aSXin Li-bool get_var_val(const std::string &var_val, std::string &var, std::string &val)
109*ec63e07aSXin Li-{
110*ec63e07aSXin Li-    size_t eq_pos = var_val.find_first_of('=', 0);
111*ec63e07aSXin Li-    if (eq_pos == std::string::npos) {
112*ec63e07aSXin Li-        var = var_val;
113*ec63e07aSXin Li-        const char *val_cstr = ::getenv(var.c_str());
114*ec63e07aSXin Li-        if (val_cstr == nullptr) {
115*ec63e07aSXin Li-            std::cerr << "ERROR: environment variable " << var << " was undefined." << std::endl;
116*ec63e07aSXin Li-            return false;
117*ec63e07aSXin Li-        }
118*ec63e07aSXin Li-        val = val_cstr;
119*ec63e07aSXin Li-    } else {
120*ec63e07aSXin Li-        var = var_val.substr(0, eq_pos);
121*ec63e07aSXin Li-        val = var_val.substr(eq_pos + 1, std::string::npos);
122*ec63e07aSXin Li-    }
123*ec63e07aSXin Li-    return true;
124*ec63e07aSXin Li-}
125*ec63e07aSXin Li-
126*ec63e07aSXin Li-bool get_var_file(const std::string &var_file, const std::string &imp, std::string &var, std::string &val)
127*ec63e07aSXin Li-{
128*ec63e07aSXin Li-    size_t eq_pos = var_file.find_first_of('=', 0);
129*ec63e07aSXin Li-    if (eq_pos == std::string::npos) {
130*ec63e07aSXin Li-        std::cerr << "ERROR: argument not in form <var>=<file> \"" << var_file << "\"."
131*ec63e07aSXin Li-                  << std::endl;
132*ec63e07aSXin Li-        return false;
133*ec63e07aSXin Li-    }
134*ec63e07aSXin Li-    var = var_file.substr(0, eq_pos);
135*ec63e07aSXin Li-    const std::string path = var_file.substr(eq_pos + 1, std::string::npos);
136*ec63e07aSXin Li-
137*ec63e07aSXin Li-    size_t b, e;
138*ec63e07aSXin Li-    val.erase().append(imp).append(" @'");
139*ec63e07aSXin Li-    // duplicate all the single quotes in @path to make a quoted string
140*ec63e07aSXin Li-    for (b = 0; (e = path.find("'", b)) != std::string::npos; b = e + 1) {
141*ec63e07aSXin Li-        val.append(path.substr(b, e - b + 1)).push_back('\'');
142*ec63e07aSXin Li+/** Writes output files for multiple file output */
143*ec63e07aSXin Li+bool write_multi_output_files(char *output, const std::string &output_dir, bool show_output_file_names) {
144*ec63e07aSXin Li+  // If multiple file output is used, then iterate over each string from
145*ec63e07aSXin Li+  // the sequence of strings returned by jsonnet_evaluate_snippet_multi,
146*ec63e07aSXin Li+  // construct pairs of filename and content, and write each output file.
147*ec63e07aSXin Li+  std::map<std::string, std::string> r;
148*ec63e07aSXin Li+  for (const char *c = output; *c != '\0';) {
149*ec63e07aSXin Li+    const char *filename = c;
150*ec63e07aSXin Li+    const char *c2 = c;
151*ec63e07aSXin Li+    while (*c2 != '\0') ++c2;
152*ec63e07aSXin Li+    ++c2;
153*ec63e07aSXin Li+    const char *json = c2;
154*ec63e07aSXin Li+    while (*c2 != '\0') ++c2;
155*ec63e07aSXin Li+    ++c2;
156*ec63e07aSXin Li+    c = c2;
157*ec63e07aSXin Li+    r[filename] = json;
158*ec63e07aSXin Li+  }
159*ec63e07aSXin Li+
160*ec63e07aSXin Li+  std::ostream *o;
161*ec63e07aSXin Li+  std::ofstream f;
162*ec63e07aSXin Li+
163*ec63e07aSXin Li+  o = &std::cout;
164*ec63e07aSXin Li+
165*ec63e07aSXin Li+  for (const auto &pair : r) {
166*ec63e07aSXin Li+    const std::string &new_content = pair.second;
167*ec63e07aSXin Li+    const std::string &filename = output_dir + pair.first;
168*ec63e07aSXin Li+    if (show_output_file_names) {
169*ec63e07aSXin Li+      (*o) << filename << std::endl;
170*ec63e07aSXin Li     }
171*ec63e07aSXin Li-    val.append(path.substr(b)).push_back('\'');
172*ec63e07aSXin Li-
173*ec63e07aSXin Li-    return true;
174*ec63e07aSXin Li-}
175*ec63e07aSXin Li-
176*ec63e07aSXin Li-enum ArgStatus {
177*ec63e07aSXin Li-    ARG_CONTINUE,
178*ec63e07aSXin Li-    ARG_SUCCESS,
179*ec63e07aSXin Li-    ARG_FAILURE,
180*ec63e07aSXin Li-};
181*ec63e07aSXin Li-
182*ec63e07aSXin Li-/** Parse the command line arguments, configuring the Jsonnet VM context and
183*ec63e07aSXin Li- * populating the JsonnetConfig.
184*ec63e07aSXin Li- */
185*ec63e07aSXin Li-static ArgStatus process_args(int argc, const char **argv, JsonnetConfig *config, JsonnetVm *vm)
186*ec63e07aSXin Li-{
187*ec63e07aSXin Li-    auto args = simplify_args(argc, argv);
188*ec63e07aSXin Li-    std::vector<std::string> remaining_args;
189*ec63e07aSXin Li-
190*ec63e07aSXin Li-    unsigned i = 0;
191*ec63e07aSXin Li-
192*ec63e07aSXin Li-    for (; i < args.size(); ++i) {
193*ec63e07aSXin Li-        const std::string &arg = args[i];
194*ec63e07aSXin Li-        if (arg == "-h" || arg == "--help") {
195*ec63e07aSXin Li-            usage(std::cout);
196*ec63e07aSXin Li-            return ARG_SUCCESS;
197*ec63e07aSXin Li-        } else if (arg == "-v" || arg == "--version") {
198*ec63e07aSXin Li-            version(std::cout);
199*ec63e07aSXin Li-            return ARG_SUCCESS;
200*ec63e07aSXin Li-        } else if (arg == "-e" || arg == "--exec") {
201*ec63e07aSXin Li-            config->filenameIsCode = true;
202*ec63e07aSXin Li-        } else if (arg == "-o" || arg == "--output-file") {
203*ec63e07aSXin Li-            std::string output_file = next_arg(i, args);
204*ec63e07aSXin Li-            if (output_file.length() == 0) {
205*ec63e07aSXin Li-                std::cerr << "ERROR: -o argument was empty string" << std::endl;
206*ec63e07aSXin Li-                return ARG_FAILURE;
207*ec63e07aSXin Li-            }
208*ec63e07aSXin Li-            config->outputFile = output_file;
209*ec63e07aSXin Li-        } else if (arg == "--") {
210*ec63e07aSXin Li-            // All subsequent args are not options.
211*ec63e07aSXin Li-            while ((++i) < args.size())
212*ec63e07aSXin Li-                remaining_args.push_back(args[i]);
213*ec63e07aSXin Li-            break;
214*ec63e07aSXin Li-        } else if (arg == "-s" || arg == "--max-stack") {
215*ec63e07aSXin Li-            long l = strtol_check(next_arg(i, args));
216*ec63e07aSXin Li-            if (l < 1) {
217*ec63e07aSXin Li-                std::cerr << "ERROR: invalid --max-stack value: " << l << std::endl;
218*ec63e07aSXin Li-                return ARG_FAILURE;
219*ec63e07aSXin Li-            }
220*ec63e07aSXin Li-            jsonnet_max_stack(vm, l);
221*ec63e07aSXin Li-        } else if (arg == "-J" || arg == "--jpath") {
222*ec63e07aSXin Li-            std::string dir = next_arg(i, args);
223*ec63e07aSXin Li-            if (dir.length() == 0) {
224*ec63e07aSXin Li-                std::cerr << "ERROR: -J argument was empty string" << std::endl;
225*ec63e07aSXin Li-                return ARG_FAILURE;
226*ec63e07aSXin Li-            }
227*ec63e07aSXin Li-            if (dir[dir.length() - 1] != '/') {
228*ec63e07aSXin Li-                dir += '/';
229*ec63e07aSXin Li-            }
230*ec63e07aSXin Li-            jsonnet_jpath_add(vm, dir.c_str());
231*ec63e07aSXin Li-        } else if (arg == "-V" || arg == "--ext-str") {
232*ec63e07aSXin Li-            std::string var, val;
233*ec63e07aSXin Li-            if (!get_var_val(next_arg(i, args), var, val))
234*ec63e07aSXin Li-                return ARG_FAILURE;
235*ec63e07aSXin Li-            jsonnet_ext_var(vm, var.c_str(), val.c_str());
236*ec63e07aSXin Li-        } else if (arg == "-E" || arg == "--var" || arg == "--env") {
237*ec63e07aSXin Li-            // TODO(dcunnin): Delete this in a future release.
238*ec63e07aSXin Li-            std::cerr << "WARNING: jsonnet eval -E, --var and --env are deprecated,"
239*ec63e07aSXin Li-                        << " please use -V or --ext-str." << std::endl;
240*ec63e07aSXin Li-            std::string var, val;
241*ec63e07aSXin Li-            if (!get_var_val(next_arg(i, args), var, val))
242*ec63e07aSXin Li-                return ARG_FAILURE;
243*ec63e07aSXin Li-            jsonnet_ext_var(vm, var.c_str(), val.c_str());
244*ec63e07aSXin Li-        } else if (arg == "--ext-str-file") {
245*ec63e07aSXin Li-            std::string var, val;
246*ec63e07aSXin Li-            if (!get_var_file(next_arg(i, args), "importstr", var, val))
247*ec63e07aSXin Li-                return ARG_FAILURE;
248*ec63e07aSXin Li-            jsonnet_ext_code(vm, var.c_str(), val.c_str());
249*ec63e07aSXin Li-        } else if (arg == "-F" || arg == "--file") {
250*ec63e07aSXin Li-            // TODO(dcunnin): Delete this in a future release.
251*ec63e07aSXin Li-            std::cerr << "WARNING: jsonnet eval -F and --file are deprecated,"
252*ec63e07aSXin Li-                        << " please use --ext-str-file." << std::endl;
253*ec63e07aSXin Li-            std::string var, val;
254*ec63e07aSXin Li-            if (!get_var_file(next_arg(i, args), "importstr", var, val))
255*ec63e07aSXin Li-                return ARG_FAILURE;
256*ec63e07aSXin Li-            jsonnet_ext_code(vm, var.c_str(), val.c_str());
257*ec63e07aSXin Li-        } else if (arg == "--ext-code") {
258*ec63e07aSXin Li-            std::string var, val;
259*ec63e07aSXin Li-            if (!get_var_val(next_arg(i, args), var, val))
260*ec63e07aSXin Li-                return ARG_FAILURE;
261*ec63e07aSXin Li-            jsonnet_ext_code(vm, var.c_str(), val.c_str());
262*ec63e07aSXin Li-        } else if (arg == "--code-var" || arg == "--code-env") {
263*ec63e07aSXin Li-            // TODO(dcunnin): Delete this in a future release.
264*ec63e07aSXin Li-            std::cerr << "WARNING: jsonnet eval --code-var and --code-env are deprecated,"
265*ec63e07aSXin Li-                        << " please use --ext-code." << std::endl;
266*ec63e07aSXin Li-            std::string var, val;
267*ec63e07aSXin Li-            if (!get_var_val(next_arg(i, args), var, val))
268*ec63e07aSXin Li-                return ARG_FAILURE;
269*ec63e07aSXin Li-            jsonnet_ext_code(vm, var.c_str(), val.c_str());
270*ec63e07aSXin Li-        } else if (arg == "--ext-code-file") {
271*ec63e07aSXin Li-            std::string var, val;
272*ec63e07aSXin Li-            if (!get_var_file(next_arg(i, args), "import", var, val))
273*ec63e07aSXin Li-                return ARG_FAILURE;
274*ec63e07aSXin Li-            jsonnet_ext_code(vm, var.c_str(), val.c_str());
275*ec63e07aSXin Li-        } else if (arg == "--code-file") {
276*ec63e07aSXin Li-            // TODO(dcunnin): Delete this in a future release.
277*ec63e07aSXin Li-            std::cerr << "WARNING: jsonnet eval --code-file is deprecated,"
278*ec63e07aSXin Li-                        << " please use --ext-code-file." << std::endl;
279*ec63e07aSXin Li-            std::string var, val;
280*ec63e07aSXin Li-            if (!get_var_file(next_arg(i, args), "import", var, val))
281*ec63e07aSXin Li-                return ARG_FAILURE;
282*ec63e07aSXin Li-            jsonnet_ext_code(vm, var.c_str(), val.c_str());
283*ec63e07aSXin Li-        } else if (arg == "-A" || arg == "--tla-str") {
284*ec63e07aSXin Li-            std::string var, val;
285*ec63e07aSXin Li-            if (!get_var_val(next_arg(i, args), var, val))
286*ec63e07aSXin Li-                return ARG_FAILURE;
287*ec63e07aSXin Li-            jsonnet_tla_var(vm, var.c_str(), val.c_str());
288*ec63e07aSXin Li-        } else if (arg == "--tla-str-file") {
289*ec63e07aSXin Li-            std::string var, val;
290*ec63e07aSXin Li-            if (!get_var_file(next_arg(i, args), "importstr", var, val))
291*ec63e07aSXin Li-                return ARG_FAILURE;
292*ec63e07aSXin Li-            jsonnet_tla_code(vm, var.c_str(), val.c_str());
293*ec63e07aSXin Li-        } else if (arg == "--tla-code") {
294*ec63e07aSXin Li-            std::string var, val;
295*ec63e07aSXin Li-            if (!get_var_val(next_arg(i, args), var, val))
296*ec63e07aSXin Li-                return ARG_FAILURE;
297*ec63e07aSXin Li-            jsonnet_tla_code(vm, var.c_str(), val.c_str());
298*ec63e07aSXin Li-        } else if (arg == "--tla-code-file") {
299*ec63e07aSXin Li-            std::string var, val;
300*ec63e07aSXin Li-            if (!get_var_file(next_arg(i, args), "import", var, val))
301*ec63e07aSXin Li-                return ARG_FAILURE;
302*ec63e07aSXin Li-            jsonnet_tla_code(vm, var.c_str(), val.c_str());
303*ec63e07aSXin Li-
304*ec63e07aSXin Li-        } else if (arg == "--gc-min-objects") {
305*ec63e07aSXin Li-            long l = strtol_check(next_arg(i, args));
306*ec63e07aSXin Li-            if (l < 0) {
307*ec63e07aSXin Li-                std::cerr << "ERROR: invalid --gc-min-objects value: " << l << std::endl;
308*ec63e07aSXin Li-                return ARG_FAILURE;
309*ec63e07aSXin Li-            }
310*ec63e07aSXin Li-            jsonnet_gc_min_objects(vm, l);
311*ec63e07aSXin Li-        } else if (arg == "-t" || arg == "--max-trace") {
312*ec63e07aSXin Li-            long l = strtol_check(next_arg(i, args));
313*ec63e07aSXin Li-            if (l < 0) {
314*ec63e07aSXin Li-                std::cerr << "ERROR: invalid --max-trace value: " << l << std::endl;
315*ec63e07aSXin Li-                return ARG_FAILURE;
316*ec63e07aSXin Li-            }
317*ec63e07aSXin Li-            jsonnet_max_trace(vm, l);
318*ec63e07aSXin Li-        } else if (arg == "--gc-growth-trigger") {
319*ec63e07aSXin Li-            std::string num = next_arg(i, args);
320*ec63e07aSXin Li-            char *ep;
321*ec63e07aSXin Li-            double v = std::strtod(num.c_str(), &ep);
322*ec63e07aSXin Li-            if (*ep != '\0' || num.length() == 0) {
323*ec63e07aSXin Li-                std::cerr << "ERROR: invalid number \"" << num << "\"" << std::endl;
324*ec63e07aSXin Li-                return ARG_FAILURE;
325*ec63e07aSXin Li-            }
326*ec63e07aSXin Li-            if (v < 0) {
327*ec63e07aSXin Li-                std::cerr << "ERROR: invalid --gc-growth-trigger \"" << num << "\""
328*ec63e07aSXin Li-                            << std::endl;
329*ec63e07aSXin Li-                return ARG_FAILURE;
330*ec63e07aSXin Li-            }
331*ec63e07aSXin Li-            jsonnet_gc_growth_trigger(vm, v);
332*ec63e07aSXin Li-        } else if (arg == "-m" || arg == "--multi") {
333*ec63e07aSXin Li-            config->evalMulti = true;
334*ec63e07aSXin Li-            std::string output_dir = next_arg(i, args);
335*ec63e07aSXin Li-            if (output_dir.length() == 0) {
336*ec63e07aSXin Li-                std::cerr << "ERROR: -m argument was empty string" << std::endl;
337*ec63e07aSXin Li-                return ARG_FAILURE;
338*ec63e07aSXin Li-            }
339*ec63e07aSXin Li-            if (output_dir[output_dir.length() - 1] != '/') {
340*ec63e07aSXin Li-                output_dir += '/';
341*ec63e07aSXin Li-            }
342*ec63e07aSXin Li-            config->evalMultiOutputDir = output_dir;
343*ec63e07aSXin Li-        } else if (arg == "-y" || arg == "--yaml-stream") {
344*ec63e07aSXin Li-            config->evalStream = true;
345*ec63e07aSXin Li-        } else if (arg == "-S" || arg == "--string") {
346*ec63e07aSXin Li-            jsonnet_string_output(vm, 1);
347*ec63e07aSXin Li-        } else if (arg.length() > 1 && arg[0] == '-') {
348*ec63e07aSXin Li-            std::cerr << "ERROR: unrecognized argument: " << arg << std::endl;
349*ec63e07aSXin Li-            return ARG_FAILURE;
350*ec63e07aSXin Li-        } else {
351*ec63e07aSXin Li-            remaining_args.push_back(args[i]);
352*ec63e07aSXin Li+    {
353*ec63e07aSXin Li+      std::ifstream exists(filename.c_str());
354*ec63e07aSXin Li+      if (exists.good()) {
355*ec63e07aSXin Li+        std::string existing_content;
356*ec63e07aSXin Li+        existing_content.assign(std::istreambuf_iterator<char>(exists),
357*ec63e07aSXin Li+                                std::istreambuf_iterator<char>());
358*ec63e07aSXin Li+        if (existing_content == new_content) {
359*ec63e07aSXin Li+          // Do not bump the timestamp on the file if its content is
360*ec63e07aSXin Li+          // the same. This may trigger other tools (e.g. make) to do
361*ec63e07aSXin Li+          // unnecessary work.
362*ec63e07aSXin Li+          continue;
363*ec63e07aSXin Li         }
364*ec63e07aSXin Li+      }
365*ec63e07aSXin Li     }
366*ec63e07aSXin Li-
367*ec63e07aSXin Li-    const char *want = config->filenameIsCode ? "code" : "filename";
368*ec63e07aSXin Li-    if (remaining_args.size() == 0) {
369*ec63e07aSXin Li-        std::cerr << "ERROR: must give " << want << "\n" << std::endl;
370*ec63e07aSXin Li-        usage(std::cerr);
371*ec63e07aSXin Li-        return ARG_FAILURE;
372*ec63e07aSXin Li-    }
373*ec63e07aSXin Li-
374*ec63e07aSXin Li-    if (remaining_args.size() > 1) {
375*ec63e07aSXin Li-        std::string filename = remaining_args[0];
376*ec63e07aSXin Li-        std::cerr << "ERROR: only one " << want << " is allowed\n" << std::endl;
377*ec63e07aSXin Li-        return ARG_FAILURE;
378*ec63e07aSXin Li-    }
379*ec63e07aSXin Li-    config->inputFiles = remaining_args;
380*ec63e07aSXin Li-    return ARG_CONTINUE;
381*ec63e07aSXin Li-}
382*ec63e07aSXin Li-
383*ec63e07aSXin Li-/** Writes output files for multiple file output */
384*ec63e07aSXin Li-static bool write_multi_output_files(JsonnetVm *vm, char *output, const std::string &output_dir,
385*ec63e07aSXin Li-                                     const std::string &output_file)
386*ec63e07aSXin Li-{
387*ec63e07aSXin Li-    // If multiple file output is used, then iterate over each string from
388*ec63e07aSXin Li-    // the sequence of strings returned by jsonnet_evaluate_snippet_multi,
389*ec63e07aSXin Li-    // construct pairs of filename and content, and write each output file.
390*ec63e07aSXin Li-    std::map<std::string, std::string> r;
391*ec63e07aSXin Li-    for (const char *c = output; *c != '\0';) {
392*ec63e07aSXin Li-        const char *filename = c;
393*ec63e07aSXin Li-        const char *c2 = c;
394*ec63e07aSXin Li-        while (*c2 != '\0')
395*ec63e07aSXin Li-            ++c2;
396*ec63e07aSXin Li-        ++c2;
397*ec63e07aSXin Li-        const char *json = c2;
398*ec63e07aSXin Li-        while (*c2 != '\0')
399*ec63e07aSXin Li-            ++c2;
400*ec63e07aSXin Li-        ++c2;
401*ec63e07aSXin Li-        c = c2;
402*ec63e07aSXin Li-        r[filename] = json;
403*ec63e07aSXin Li-    }
404*ec63e07aSXin Li-    jsonnet_realloc(vm, output, 0);
405*ec63e07aSXin Li-
406*ec63e07aSXin Li-    std::ostream *o;
407*ec63e07aSXin Li     std::ofstream f;
408*ec63e07aSXin Li-
409*ec63e07aSXin Li-    if (output_file.empty()) {
410*ec63e07aSXin Li-        o = &std::cout;
411*ec63e07aSXin Li-    } else {
412*ec63e07aSXin Li-        f.open(output_file.c_str());
413*ec63e07aSXin Li-        if (!f.good()) {
414*ec63e07aSXin Li-            std::string msg = "Writing to output file: " + output_file;
415*ec63e07aSXin Li-            perror(msg.c_str());
416*ec63e07aSXin Li-            return false;
417*ec63e07aSXin Li-        }
418*ec63e07aSXin Li-        o = &f;
419*ec63e07aSXin Li+    f.open(filename.c_str());
420*ec63e07aSXin Li+    if (!f.good()) {
421*ec63e07aSXin Li+      std::string msg = "Opening output file: " + filename;
422*ec63e07aSXin Li+      perror(msg.c_str());
423*ec63e07aSXin Li+      return false;
424*ec63e07aSXin Li+    }
425*ec63e07aSXin Li+    f << new_content;
426*ec63e07aSXin Li+    f.close();
427*ec63e07aSXin Li+    if (!f.good()) {
428*ec63e07aSXin Li+      std::string msg = "Writing to output file: " + filename;
429*ec63e07aSXin Li+      perror(msg.c_str());
430*ec63e07aSXin Li+      return false;
431*ec63e07aSXin Li     }
432*ec63e07aSXin Li+  }
433*ec63e07aSXin Li
434*ec63e07aSXin Li-    for (const auto &pair : r) {
435*ec63e07aSXin Li-        const std::string &new_content = pair.second;
436*ec63e07aSXin Li-        const std::string &filename = output_dir + pair.first;
437*ec63e07aSXin Li-        (*o) << filename << std::endl;
438*ec63e07aSXin Li-        {
439*ec63e07aSXin Li-            std::ifstream exists(filename.c_str());
440*ec63e07aSXin Li-            if (exists.good()) {
441*ec63e07aSXin Li-                std::string existing_content;
442*ec63e07aSXin Li-                existing_content.assign(std::istreambuf_iterator<char>(exists),
443*ec63e07aSXin Li-                                        std::istreambuf_iterator<char>());
444*ec63e07aSXin Li-                if (existing_content == new_content) {
445*ec63e07aSXin Li-                    // Do not bump the timestamp on the file if its content is
446*ec63e07aSXin Li-                    // the same. This may trigger other tools (e.g. make) to do
447*ec63e07aSXin Li-                    // unnecessary work.
448*ec63e07aSXin Li-                    continue;
449*ec63e07aSXin Li-                }
450*ec63e07aSXin Li-            }
451*ec63e07aSXin Li-        }
452*ec63e07aSXin Li-        std::ofstream f;
453*ec63e07aSXin Li-        f.open(filename.c_str());
454*ec63e07aSXin Li-        if (!f.good()) {
455*ec63e07aSXin Li-            std::string msg = "Opening output file: " + filename;
456*ec63e07aSXin Li-            perror(msg.c_str());
457*ec63e07aSXin Li-            return false;
458*ec63e07aSXin Li-        }
459*ec63e07aSXin Li-        f << new_content;
460*ec63e07aSXin Li-        f.close();
461*ec63e07aSXin Li-        if (!f.good()) {
462*ec63e07aSXin Li-            std::string msg = "Writing to output file: " + filename;
463*ec63e07aSXin Li-            perror(msg.c_str());
464*ec63e07aSXin Li-            return false;
465*ec63e07aSXin Li-        }
466*ec63e07aSXin Li-    }
467*ec63e07aSXin Li+  std::cout.flush();
468*ec63e07aSXin Li
469*ec63e07aSXin Li-    if (output_file.empty()) {
470*ec63e07aSXin Li-        std::cout.flush();
471*ec63e07aSXin Li-    } else {
472*ec63e07aSXin Li-        f.close();
473*ec63e07aSXin Li-        if (!f.good()) {
474*ec63e07aSXin Li-            std::string msg = "Writing to output file: " + output_file;
475*ec63e07aSXin Li-            perror(msg.c_str());
476*ec63e07aSXin Li-            return false;
477*ec63e07aSXin Li-        }
478*ec63e07aSXin Li-    }
479*ec63e07aSXin Li-    return true;
480*ec63e07aSXin Li+  return true;
481*ec63e07aSXin Li }
482*ec63e07aSXin Li
483*ec63e07aSXin Li /** Writes output files for YAML stream output */
484*ec63e07aSXin Li-static bool write_output_stream(JsonnetVm *vm, char *output, const std::string &output_file)
485*ec63e07aSXin Li-{
486*ec63e07aSXin Li-    std::ostream *o;
487*ec63e07aSXin Li-    std::ofstream f;
488*ec63e07aSXin Li-
489*ec63e07aSXin Li-    if (output_file.empty()) {
490*ec63e07aSXin Li-        o = &std::cout;
491*ec63e07aSXin Li-    } else {
492*ec63e07aSXin Li-        f.open(output_file.c_str());
493*ec63e07aSXin Li-        if (!f.good()) {
494*ec63e07aSXin Li-            std::string msg = "Writing to output file: " + output_file;
495*ec63e07aSXin Li-            perror(msg.c_str());
496*ec63e07aSXin Li-            return false;
497*ec63e07aSXin Li-        }
498*ec63e07aSXin Li-        o = &f;
499*ec63e07aSXin Li-    }
500*ec63e07aSXin Li-
501*ec63e07aSXin Li-    // If YAML stream output is used, then iterate over each string from
502*ec63e07aSXin Li-    // the sequence of strings returned by jsonnet_evaluate_snippet_stream,
503*ec63e07aSXin Li-    // and add the --- and ... as defined by the YAML spec.
504*ec63e07aSXin Li-    std::vector<std::string> r;
505*ec63e07aSXin Li-    for (const char *c = output; *c != '\0';) {
506*ec63e07aSXin Li-        const char *json = c;
507*ec63e07aSXin Li-        while (*c != '\0')
508*ec63e07aSXin Li-            ++c;
509*ec63e07aSXin Li-        ++c;
510*ec63e07aSXin Li-        r.emplace_back(json);
511*ec63e07aSXin Li-    }
512*ec63e07aSXin Li-    jsonnet_realloc(vm, output, 0);
513*ec63e07aSXin Li-    for (const auto &str : r) {
514*ec63e07aSXin Li-        (*o) << "---\n";
515*ec63e07aSXin Li-        (*o) << str;
516*ec63e07aSXin Li-    }
517*ec63e07aSXin Li-    if (r.size() > 0)
518*ec63e07aSXin Li-        (*o) << "...\n";
519*ec63e07aSXin Li-    o->flush();
520*ec63e07aSXin Li-
521*ec63e07aSXin Li-    if (output_file.empty()) {
522*ec63e07aSXin Li-        std::cout.flush();
523*ec63e07aSXin Li-    } else {
524*ec63e07aSXin Li-        f.close();
525*ec63e07aSXin Li-        if (!f.good()) {
526*ec63e07aSXin Li-            std::string msg = "Writing to output file: " + output_file;
527*ec63e07aSXin Li-            perror(msg.c_str());
528*ec63e07aSXin Li-            return false;
529*ec63e07aSXin Li-        }
530*ec63e07aSXin Li+bool write_output_stream(char *output, const std::string &output_file) {
531*ec63e07aSXin Li+  std::ostream *o;
532*ec63e07aSXin Li+  std::ofstream f;
533*ec63e07aSXin Li+
534*ec63e07aSXin Li+  if (output_file.empty()) {
535*ec63e07aSXin Li+    o = &std::cout;
536*ec63e07aSXin Li+  } else {
537*ec63e07aSXin Li+    f.open(output_file.c_str());
538*ec63e07aSXin Li+    if (!f.good()) {
539*ec63e07aSXin Li+      std::string msg = "Writing to output file: " + output_file;
540*ec63e07aSXin Li+      perror(msg.c_str());
541*ec63e07aSXin Li+      return false;
542*ec63e07aSXin Li+    }
543*ec63e07aSXin Li+    o = &f;
544*ec63e07aSXin Li+  }
545*ec63e07aSXin Li+
546*ec63e07aSXin Li+  // If YAML stream output is used, then iterate over each string from
547*ec63e07aSXin Li+  // the sequence of strings returned by jsonnet_evaluate_snippet_stream,
548*ec63e07aSXin Li+  // and add the --- and ... as defined by the YAML spec.
549*ec63e07aSXin Li+  std::vector<std::string> r;
550*ec63e07aSXin Li+  for (const char *c = output; *c != '\0';) {
551*ec63e07aSXin Li+    const char *json = c;
552*ec63e07aSXin Li+    while (*c != '\0') ++c;
553*ec63e07aSXin Li+    ++c;
554*ec63e07aSXin Li+    r.emplace_back(json);
555*ec63e07aSXin Li+  }
556*ec63e07aSXin Li+
557*ec63e07aSXin Li+  for (const auto &str : r) {
558*ec63e07aSXin Li+    (*o) << "---\n";
559*ec63e07aSXin Li+    (*o) << str;
560*ec63e07aSXin Li+  }
561*ec63e07aSXin Li+  if (r.size() > 0) (*o) << "...\n";
562*ec63e07aSXin Li+  o->flush();
563*ec63e07aSXin Li+
564*ec63e07aSXin Li+  if (output_file.empty()) {
565*ec63e07aSXin Li+    std::cout.flush();
566*ec63e07aSXin Li+  } else {
567*ec63e07aSXin Li+    f.close();
568*ec63e07aSXin Li+    if (!f.good()) {
569*ec63e07aSXin Li+      std::string msg = "Writing to output file: " + output_file;
570*ec63e07aSXin Li+      perror(msg.c_str());
571*ec63e07aSXin Li+      return false;
572*ec63e07aSXin Li     }
573*ec63e07aSXin Li+  }
574*ec63e07aSXin Li
575*ec63e07aSXin Li-    return true;
576*ec63e07aSXin Li-}
577*ec63e07aSXin Li-
578*ec63e07aSXin Li-int main(int argc, const char **argv)
579*ec63e07aSXin Li-{
580*ec63e07aSXin Li-    try {
581*ec63e07aSXin Li-        JsonnetVm *vm = jsonnet_make();
582*ec63e07aSXin Li-        JsonnetConfig config;
583*ec63e07aSXin Li-        if (const char *jsonnet_path_env = getenv("JSONNET_PATH")) {
584*ec63e07aSXin Li-            std::list<std::string> jpath;
585*ec63e07aSXin Li-            std::istringstream iss(jsonnet_path_env);
586*ec63e07aSXin Li-            std::string path;
587*ec63e07aSXin Li-            while (std::getline(iss, path, PATH_SEP)) {
588*ec63e07aSXin Li-                jpath.push_front(path);
589*ec63e07aSXin Li-            }
590*ec63e07aSXin Li-            for (const std::string &path : jpath) {
591*ec63e07aSXin Li-                jsonnet_jpath_add(vm, path.c_str());
592*ec63e07aSXin Li-            }
593*ec63e07aSXin Li-        }
594*ec63e07aSXin Li-        ArgStatus arg_status = process_args(argc, argv, &config, vm);
595*ec63e07aSXin Li-        if (arg_status != ARG_CONTINUE) {
596*ec63e07aSXin Li-            jsonnet_destroy(vm);
597*ec63e07aSXin Li-            return arg_status == ARG_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE;
598*ec63e07aSXin Li-        }
599*ec63e07aSXin Li-
600*ec63e07aSXin Li-        // Evaluate input Jsonnet and handle any errors from Jsonnet VM.
601*ec63e07aSXin Li-        int error;
602*ec63e07aSXin Li-        char *output;
603*ec63e07aSXin Li-        assert(config.inputFiles.size() == 1);
604*ec63e07aSXin Li-
605*ec63e07aSXin Li-        // Read input file.
606*ec63e07aSXin Li-        std::string input;
607*ec63e07aSXin Li-        if (!read_input(config.filenameIsCode, &config.inputFiles[0], &input)) {
608*ec63e07aSXin Li-            jsonnet_destroy(vm);
609*ec63e07aSXin Li-            return EXIT_FAILURE;
610*ec63e07aSXin Li-        }
611*ec63e07aSXin Li-
612*ec63e07aSXin Li-        if (config.evalMulti) {
613*ec63e07aSXin Li-            output = jsonnet_evaluate_snippet_multi(
614*ec63e07aSXin Li-                vm, config.inputFiles[0].c_str(), input.c_str(), &error);
615*ec63e07aSXin Li-        } else if (config.evalStream) {
616*ec63e07aSXin Li-            output = jsonnet_evaluate_snippet_stream(
617*ec63e07aSXin Li-                vm, config.inputFiles[0].c_str(), input.c_str(), &error);
618*ec63e07aSXin Li-        } else {
619*ec63e07aSXin Li-            output = jsonnet_evaluate_snippet(
620*ec63e07aSXin Li-                vm, config.inputFiles[0].c_str(), input.c_str(), &error);
621*ec63e07aSXin Li-        }
622*ec63e07aSXin Li-
623*ec63e07aSXin Li-        if (error) {
624*ec63e07aSXin Li-            std::cerr << output;
625*ec63e07aSXin Li-            jsonnet_realloc(vm, output, 0);
626*ec63e07aSXin Li-            jsonnet_destroy(vm);
627*ec63e07aSXin Li-            return EXIT_FAILURE;
628*ec63e07aSXin Li-        }
629*ec63e07aSXin Li-
630*ec63e07aSXin Li-        // Write output JSON.
631*ec63e07aSXin Li-        if (config.evalMulti) {
632*ec63e07aSXin Li-            if (!write_multi_output_files(
633*ec63e07aSXin Li-                    vm, output, config.evalMultiOutputDir, config.outputFile)) {
634*ec63e07aSXin Li-                jsonnet_destroy(vm);
635*ec63e07aSXin Li-                return EXIT_FAILURE;
636*ec63e07aSXin Li-            }
637*ec63e07aSXin Li-        } else if (config.evalStream) {
638*ec63e07aSXin Li-            if (!write_output_stream(vm, output, config.outputFile)) {
639*ec63e07aSXin Li-                jsonnet_destroy(vm);
640*ec63e07aSXin Li-                return EXIT_FAILURE;
641*ec63e07aSXin Li-            }
642*ec63e07aSXin Li-        } else {
643*ec63e07aSXin Li-            bool successful = write_output_file(output, config.outputFile);
644*ec63e07aSXin Li-            jsonnet_realloc(vm, output, 0);
645*ec63e07aSXin Li-            if (!successful) {
646*ec63e07aSXin Li-                jsonnet_destroy(vm);
647*ec63e07aSXin Li-                return EXIT_FAILURE;
648*ec63e07aSXin Li-            }
649*ec63e07aSXin Li-        }
650*ec63e07aSXin Li-
651*ec63e07aSXin Li-        jsonnet_destroy(vm);
652*ec63e07aSXin Li-        return EXIT_SUCCESS;
653*ec63e07aSXin Li-
654*ec63e07aSXin Li-    } catch (const std::bad_alloc &) {
655*ec63e07aSXin Li-        // Avoid further allocation attempts
656*ec63e07aSXin Li-        fputs("Internal out-of-memory error (please report this)\n", stderr);
657*ec63e07aSXin Li-    } catch (const std::exception &e) {
658*ec63e07aSXin Li-        std::cerr << "Internal error (please report this): " << e.what() << std::endl;
659*ec63e07aSXin Li-    } catch (...) {
660*ec63e07aSXin Li-        std::cerr << "An unknown exception occurred (please report this)." << std::endl;
661*ec63e07aSXin Li-    }
662*ec63e07aSXin Li-    return EXIT_FAILURE;
663*ec63e07aSXin Li+  return true;
664*ec63e07aSXin Li }
665