예제 #1
0
static PyObject* evaluate_snippet(PyObject* self, PyObject* args, PyObject *keywds)
{
    const char *filename, *src;
    char *out;
    unsigned max_stack = 500, gc_min_objects = 1000, max_trace = 20;
    double gc_growth_trigger = 2;
    int error;
    PyObject *ext_vars = NULL, *import_callback = NULL;
    struct JsonnetVm *vm;
    static char *kwlist[] = {"filename", "src", "max_stack", "gc_min_objects", "gc_growth_trigger", "ext_vars", "max_trace", "import_callback", NULL};

    (void) self;

    if (!PyArg_ParseTupleAndKeywords(args, keywds, "ss|IIdOIO", kwlist,
                                     &filename, &src,
                                     &max_stack, &gc_min_objects, &gc_growth_trigger, &ext_vars,
                                     &max_trace, &import_callback)) {
        return NULL;
    }

    vm = jsonnet_make();
    jsonnet_max_stack(vm, max_stack);
    jsonnet_gc_min_objects(vm, gc_min_objects);
    jsonnet_max_trace(vm, max_trace);
    jsonnet_gc_growth_trigger(vm, gc_growth_trigger);
    if (!handle_ext_vars(vm, ext_vars)) {
        return NULL;
    }
    struct ImportCtx ctx = { vm, import_callback };
    if (!handle_import_callback(&ctx, import_callback)) {
        return NULL;
    }

    out = jsonnet_evaluate_snippet(vm, filename, src, &error);
    return handle_result(vm, out, error);
}
예제 #2
0
/** Parse the command line arguments, configuring the Jsonnet VM context and
 * populating the JsonnetConfig.
 */
static bool process_args(int argc,
                         const char **argv,
                         JsonnetConfig *config,
                         JsonnetVm *vm) {
    auto args = simplify_args(argc, argv);
    std::vector<std::string> remaining_args;

    for (unsigned i=0 ; i<args.size() ; ++i) {
        const std::string &arg = args[i];
        if (arg == "-h" || arg == "--help") {
            usage(std::cout);
            return false;
        } else if (arg == "-v" || arg == "--version") {
            version(std::cout);
            return false;
        } else if (arg == "-s" || arg == "--max-stack") {
            long l = strtol_check(next_arg(i, args));
            if (l < 1) {
                std::cerr << "ERROR: Invalid --max-stack value: " << l << "\n"
                          << std::endl;
                usage(std::cerr);
                return false;
            }
            jsonnet_max_stack(vm, l);
        } else if (arg == "-J" || arg == "--jpath") {
            std::string dir = next_arg(i, args);
            if (dir.length() == 0) {
                std::cerr << "ERROR: -J argument was empty string" << std::endl;
                return false;
            }
            if (dir[dir.length() - 1] != '/') {
                dir += '/';
            }
            jsonnet_jpath_add(vm, dir.c_str());
        } else if (arg == "-E" || arg == "--env") {
            const std::string var = next_arg(i, args);
            const char *val = ::getenv(var.c_str());
            if (val == nullptr) {
                std::cerr << "ERROR: Environment variable " << var
                          << " was undefined." << std::endl;
                return false;
            }
            jsonnet_ext_var(vm, var.c_str(), val);
        } else if (arg == "-V" || arg == "--var") {
            const std::string var_val = next_arg(i, args);
            size_t eq_pos = var_val.find_first_of('=', 0);
            if (eq_pos == std::string::npos) {
                std::cerr << "ERROR: argument not in form <var>=<val> \""
                          << var_val << "\"." << std::endl;
                return false;
            }
            const std::string var = var_val.substr(0, eq_pos);
            const std::string val = var_val.substr(eq_pos + 1,
                                                   std::string::npos);
            jsonnet_ext_var(vm, var.c_str(), val.c_str());
        } else if (arg == "--code-env") {
            const std::string var = next_arg(i, args);
            const char *val = ::getenv(var.c_str());
            if (val == nullptr) {
                std::cerr << "ERROR: Environment variable " << var
                          << " was undefined." << std::endl;
                return EXIT_FAILURE;
            }
            jsonnet_ext_code(vm, var.c_str(), val);
        } else if (arg == "--code-var") {
            const std::string var_val = next_arg(i, args);
            size_t eq_pos = var_val.find_first_of('=', 0);
            if (eq_pos == std::string::npos) {
                std::cerr << "ERROR: argument not in form <var>=<val> \""
                          << var_val << "\"." << std::endl;
                return EXIT_FAILURE;
            }
            const std::string var = var_val.substr(0, eq_pos);
            const std::string val = var_val.substr(eq_pos + 1,
                                                   std::string::npos);
            jsonnet_ext_code(vm, var.c_str(), val.c_str());
        } else if (arg == "--gc-min-objects") {
            long l = strtol_check(next_arg(i, args));
            if (l < 0) {
                std::cerr << "ERROR: Invalid --gc-min-objects value: " << l
                          << std::endl;
                usage(std::cerr);
                return EXIT_FAILURE;
            }
            jsonnet_gc_min_objects(vm, l);
        } else if (arg == "-t" || arg == "--max-trace") {
            long l = strtol_check(next_arg(i, args));
            if (l < 0) {
                std::cerr << "ERROR: Invalid --max-trace value: " << l
                          << std::endl;
                usage(std::cerr);
                return EXIT_FAILURE;
            }
            jsonnet_max_trace(vm, l);
        } else if (arg == "--gc-growth-trigger") {
            const char *arg = next_arg(i,args).c_str();
            char *ep;
            double v = std::strtod(arg, &ep);
            if (*ep != '\0' || *arg == '\0') {
                std::cerr << "ERROR: Invalid number \"" << arg << "\""
                          << std::endl;
                usage(std::cerr);
                return EXIT_FAILURE;
            }
            if (v < 0) {
                std::cerr << "ERROR: Invalid --gc-growth-trigger \""
                          << arg << "\"\n" << std::endl;
                usage(std::cerr);
                return EXIT_FAILURE;
            }
            jsonnet_gc_growth_trigger(vm, v);
        } else if (arg == "-e" || arg == "--exec") {
            config->set_filename_is_code(true);
        } else if (arg == "-m" || arg == "--multi") {
            config->set_multi(true);
            std::string output_dir = next_arg(i, args);
            if (output_dir.length() == 0) {
                std::cerr << "ERROR: -m argument was empty string" << std::endl;
                return EXIT_FAILURE;
            }
            if (output_dir[output_dir.length() - 1] != '/') {
                output_dir += '/';
            }
            config->set_output_dir(output_dir);
        } else if (arg == "-o" || arg == "--output-file") {
            std::string output_file = next_arg(i, args);
            if (output_file.length() == 0) {
                std::cerr << "ERROR: -o argument was empty string" << std::endl;
                return EXIT_FAILURE;
            }
            config->set_output_file(output_file);
        } else if (arg == "-S" || arg == "--string") {
            jsonnet_string_output(vm, 1);
        } else if (arg == "--debug-lexer") {
            jsonnet_debug_lexer(vm, true);
        } else if (arg == "--debug-ast") {
            jsonnet_debug_ast(vm, true);
        } else if (arg == "--debug-desugaring") {
            jsonnet_debug_desugaring(vm, true);
        } else if (arg == "--") {
            // All subsequent args are not options.
            while ((++i) < args.size())
                remaining_args.push_back(args[i]);
            break;
        } else {
            remaining_args.push_back(args[i]);
        }
    }

    const char *want = config->filename_is_code() ? "code" : "filename";
    if (remaining_args.size() == 0) {
        std::cerr << "ERROR: Must give " << want << "\n" << std::endl;
        usage(std::cerr);
        return false;
    }

    std::string filename = remaining_args[0];
    if (remaining_args.size() > 1) {
        std::cerr << "ERROR: Already specified " << want
                  << " as \"" << filename << "\"\n"
                  << std::endl;
        usage(std::cerr);
        return false;
    }
    config->set_input_file(filename);
    return true;
}
예제 #3
0
/** Parse the command line arguments, configuring the Jsonnet VM context and
 * populating the JsonnetConfig.
 */
static ArgStatus process_args(int argc, const char **argv, JsonnetConfig *config, JsonnetVm *vm)
{
    auto args = simplify_args(argc, argv);
    std::vector<std::string> remaining_args;

    unsigned i = 0;
    if (args.size() > 0 && args[i] == "fmt") {
        config->cmd = FMT;
        i++;
    } else if (args.size() > 0 && args[i] == "eval") {
        config->cmd = EVAL;
        i++;
    }

    for (; i < args.size(); ++i) {
        const std::string &arg = args[i];
        if (arg == "-h" || arg == "--help") {
            usage(std::cout);
            return ARG_SUCCESS;
        } else if (arg == "-v" || arg == "--version") {
            version(std::cout);
            return ARG_SUCCESS;
        } else if (arg == "-e" || arg == "--exec") {
            config->filenameIsCode = true;
        } else if (arg == "-o" || arg == "--output-file") {
            std::string output_file = next_arg(i, args);
            if (output_file.length() == 0) {
                std::cerr << "ERROR: -o argument was empty string" << std::endl;
                return ARG_FAILURE;
            }
            config->outputFile = output_file;
        } else if (arg == "--") {
            // All subsequent args are not options.
            while ((++i) < args.size())
                remaining_args.push_back(args[i]);
            break;
        } else if (config->cmd == EVAL) {
            if (arg == "-s" || arg == "--max-stack") {
                long l = strtol_check(next_arg(i, args));
                if (l < 1) {
                    std::cerr << "ERROR: Invalid --max-stack value: " << l << std::endl;
                    return ARG_FAILURE;
                }
                jsonnet_max_stack(vm, l);
            } else if (arg == "-J" || arg == "--jpath") {
                std::string dir = next_arg(i, args);
                if (dir.length() == 0) {
                    std::cerr << "ERROR: -J argument was empty string" << std::endl;
                    return ARG_FAILURE;
                }
                if (dir[dir.length() - 1] != '/') {
                    dir += '/';
                }
                jsonnet_jpath_add(vm, dir.c_str());
            } else if (arg == "-V" || arg == "--ext-str") {
                std::string var, val;
                if (!get_var_val(next_arg(i, args), var, val))
                    return ARG_FAILURE;
                jsonnet_ext_var(vm, var.c_str(), val.c_str());
            } else if (arg == "--ext-str-file") {
                std::string var, val;
                if (!get_var_file(next_arg(i, args), var, val))
                    return ARG_FAILURE;
                jsonnet_ext_var(vm, var.c_str(), val.c_str());
            } else if (arg == "--ext-code") {
                std::string var, val;
                if (!get_var_val(next_arg(i, args), var, val))
                    return ARG_FAILURE;
                jsonnet_ext_code(vm, var.c_str(), val.c_str());
            } else if (arg == "--ext-code-file") {
                std::string var, val;
                if (!get_var_file(next_arg(i, args), var, val))
                    return ARG_FAILURE;
                jsonnet_ext_code(vm, var.c_str(), val.c_str());
            } else if (arg == "-A" || arg == "--tla-str") {
                std::string var, val;
                if (!get_var_val(next_arg(i, args), var, val))
                    return ARG_FAILURE;
                jsonnet_tla_var(vm, var.c_str(), val.c_str());
            } else if (arg == "--tla-str-file") {
                std::string var, val;
                if (!get_var_file(next_arg(i, args), var, val))
                    return ARG_FAILURE;
                jsonnet_tla_var(vm, var.c_str(), val.c_str());
            } else if (arg == "--tla-code") {
                std::string var, val;
                if (!get_var_val(next_arg(i, args), var, val))
                    return ARG_FAILURE;
                jsonnet_tla_code(vm, var.c_str(), val.c_str());
            } else if (arg == "--tla-code-file") {
                std::string var, val;
                if (!get_var_file(next_arg(i, args), var, val))
                    return ARG_FAILURE;
                jsonnet_tla_code(vm, var.c_str(), val.c_str());

            } else if (arg == "--gc-min-objects") {
                long l = strtol_check(next_arg(i, args));
                if (l < 0) {
                    std::cerr << "ERROR: Invalid --gc-min-objects value: " << l << std::endl;
                    return ARG_FAILURE;
                }
                jsonnet_gc_min_objects(vm, l);
            } else if (arg == "-t" || arg == "--max-trace") {
                long l = strtol_check(next_arg(i, args));
                if (l < 0) {
                    std::cerr << "ERROR: Invalid --max-trace value: " << l << std::endl;
                    return ARG_FAILURE;
                }
                jsonnet_max_trace(vm, l);
            } else if (arg == "--gc-growth-trigger") {
                const char *arg = next_arg(i, args).c_str();
                char *ep;
                double v = std::strtod(arg, &ep);
                if (*ep != '\0' || *arg == '\0') {
                    std::cerr << "ERROR: Invalid number \"" << arg << "\"" << std::endl;
                    return ARG_FAILURE;
                }
                if (v < 0) {
                    std::cerr << "ERROR: Invalid --gc-growth-trigger \"" << arg << "\""
                              << std::endl;
                    return ARG_FAILURE;
                }
                jsonnet_gc_growth_trigger(vm, v);
            } else if (arg == "-m" || arg == "--multi") {
                config->evalMulti = true;
                std::string output_dir = next_arg(i, args);
                if (output_dir.length() == 0) {
                    std::cerr << "ERROR: -m argument was empty string" << std::endl;
                    return ARG_FAILURE;
                }
                if (output_dir[output_dir.length() - 1] != '/') {
                    output_dir += '/';
                }
                config->evalMultiOutputDir = output_dir;
            } else if (arg == "-y" || arg == "--yaml-stream") {
                config->evalStream = true;
            } else if (arg == "-S" || arg == "--string") {
                jsonnet_string_output(vm, 1);
            } else if (arg.length() > 1 && arg[0] == '-') {
                std::cerr << "ERROR: Unrecognized argument: " << arg << std::endl;
                return ARG_FAILURE;
            } else {
                remaining_args.push_back(args[i]);
            }

        } else {
            assert(config->cmd == FMT);
            if (arg == "-i" || arg == "--in-place") {
                config->fmtInPlace = true;
            } else if (arg == "--test") {
                config->fmtTest = true;
            } else if (arg == "-n" || arg == "--indent") {
                long l = strtol_check(next_arg(i, args));
                if (l < 0) {
                    std::cerr << "ERROR: Invalid --indent value: " << l << std::endl;
                    return ARG_FAILURE;
                }
                jsonnet_fmt_indent(vm, l);
            } else if (arg == "--max-blank-lines") {
                long l = strtol_check(next_arg(i, args));
                if (l < 0) {
                    std::cerr << "ERROR: Invalid --max-blank-lines value: " << l << ""
                              << std::endl;
                    return ARG_FAILURE;
                }
                jsonnet_fmt_max_blank_lines(vm, l);
            } else if (arg == "--comment-style") {
                const std::string val = next_arg(i, args);
                if (val == "h") {
                    jsonnet_fmt_comment(vm, 'h');
                } else if (val == "s") {
                    jsonnet_fmt_comment(vm, 's');
                } else if (val == "l") {
                    jsonnet_fmt_comment(vm, 'l');
                } else {
                    std::cerr << "ERROR: Invalid --comment-style value: " << val
                              << std::endl;
                    return ARG_FAILURE;
                }
            } else if (arg == "--string-style") {
                const std::string val = next_arg(i, args);
                if (val == "d") {
                    jsonnet_fmt_string(vm, 'd');
                } else if (val == "s") {
                    jsonnet_fmt_string(vm, 's');
                } else if (val == "l") {
                    jsonnet_fmt_string(vm, 'l');
                } else {
                    std::cerr << "ERROR: Invalid --string-style value: " << val
                              << std::endl;
                    return ARG_FAILURE;
                }
            } else if (arg == "--pad-arrays") {
                jsonnet_fmt_pad_arrays(vm, true);
            } else if (arg == "--no-pad-arrays") {
                jsonnet_fmt_pad_arrays(vm, false);
            } else if (arg == "--pad-objects") {
                jsonnet_fmt_pad_objects(vm, true);
            } else if (arg == "--no-pad-objects") {
                jsonnet_fmt_pad_objects(vm, false);
            } else if (arg == "--pretty-field-names") {
                jsonnet_fmt_pretty_field_names(vm, true);
            } else if (arg == "--no-pretty-field-names") {
                jsonnet_fmt_pretty_field_names(vm, false);
            } else if (arg == "--sort-imports") {
                jsonnet_fmt_sort_imports(vm, true);
            } else if (arg == "--no-sort-imports") {
                jsonnet_fmt_sort_imports(vm, false);
            } else if (arg == "--debug-desugaring") {
                jsonnet_fmt_debug_desugaring(vm, true);
            } else if (arg.length() > 1 && arg[0] == '-') {
                std::cerr << "ERROR: Unrecognized argument: " << arg << std::endl;
                return ARG_FAILURE;
            } else {
                remaining_args.push_back(args[i]);
            }
        }
    }

    const char *want = config->filenameIsCode ? "code" : "filename";
    if (remaining_args.size() == 0) {
        std::cerr << "ERROR: Must give " << want << "\n" << std::endl;
        usage(std::cerr);
        return ARG_FAILURE;
    }

    bool multiple_files_allowed = config->cmd == FMT && (config->fmtTest || config->fmtInPlace);
    if (!multiple_files_allowed) {
        if (remaining_args.size() > 1) {
            std::string filename = remaining_args[0];
            std::cerr << "ERROR: Only one " << want << " is allowed\n" << std::endl;
            return ARG_FAILURE;
        }
    }
    config->inputFiles = remaining_args;
    return ARG_CONTINUE;
}
예제 #4
0
파일: jsonnet.cpp 프로젝트: imclab/jsonnet
int main(int argc, const char **argv)
{
    JsonnetVM *vm = jsonnet_make();
    std::string filename = "-";
    bool filename_is_code = false;

    auto args = simplify_args(argc, argv);
    std::vector<std::string> remaining_args;

    for (unsigned i=0 ; i<args.size() ; ++i) {
        const std::string &arg = args[i];
        if (arg == "-h" || arg == "--help") {
            usage(std::cout);
            exit(EXIT_SUCCESS);
        } else if (arg == "-s" || arg == "--max-stack") {
            long l = strtol_check(next_arg(i, args));
            if (l < 1) {
                std::cerr << "ERROR: Invalid --max-stack value: " << l << "\n" << std::endl;
                usage(std::cerr);
                exit(EXIT_FAILURE);
            }
            jsonnet_max_stack(vm, l);
        } else if (arg == "-E" || arg == "--env") {
            const std::string &var = next_arg(i, args);
            const char *val = ::getenv(var.c_str());
            if (val == nullptr) {
                std::cerr << "ERROR: Environment variable " << var << " was undefined." << std::endl;
                exit(EXIT_FAILURE);
            }
            jsonnet_env(vm, var.c_str(), val);
        } else if (arg == "--gc-min-objects") {
            long l = strtol_check(next_arg(i, args));
            if (l < 0) {
                std::cerr << "ERROR: Invalid --gc-min-objects value: " << l << std::endl;
                usage(std::cerr);
                exit(EXIT_FAILURE);
            }
            jsonnet_gc_min_objects(vm, l);
        } else if (arg == "-t" || arg == "--max-trace") {
            long l = strtol_check(next_arg(i, args));
            if (l < 0) {
                std::cerr << "ERROR: Invalid --max-trace value: " << l << std::endl;
                usage(std::cerr);
                exit(EXIT_FAILURE);
            }
            jsonnet_max_trace(vm, l);
        } else if (arg == "--gc-growth-trigger") {
            const char *arg = next_arg(i,args).c_str();
            char *ep;
            double v = std::strtod(arg, &ep);
            if (*ep != '\0' || *arg == '\0') {
                std::cerr << "ERROR: Invalid number \"" << arg << "\"" << std::endl;
                usage(std::cerr);
                exit(EXIT_FAILURE);
            }
            if (v < 0) {
                std::cerr << "ERROR: Invalid --gc-growth-trigger \"" << arg << "\"\n" << std::endl;
                usage(std::cerr);
                exit(EXIT_FAILURE);
            }
            jsonnet_gc_growth_trigger(vm, v);
        } else if (arg == "-e" || arg == "--exec") {
            filename_is_code = true;
        } else if (arg == "--debug-ast") {
            jsonnet_debug_ast(vm, true);
        } else if (arg == "--") {
            // All subsequent args are not options.
            while ((++i) < args.size())
                remaining_args.push_back(args[i]);
            break;
        } else {
            remaining_args.push_back(args[i]);
        }
    }

    if (remaining_args.size() > 0) 
        filename = remaining_args[0];

    if (remaining_args.size() > 1) {
        std::cerr << "ERROR: Filename already specified as \"" << filename << "\"\n"
                  << std::endl;
        usage(std::cerr);
        exit(EXIT_FAILURE);
    }
    
    if (filename_is_code && remaining_args.size() == 0) {
        std::cerr << "ERROR: Must give filename when using -e, --exec\n" << std::endl;
        usage(std::cerr);
        exit(EXIT_FAILURE);
    }

    std::string input;
    if (filename_is_code) {
        input = filename;
        filename = "<cmdline>";
    } else {
        if (filename == "-") {
            filename = "<stdin>";
            input.assign(std::istreambuf_iterator<char>(std::cin),
                         std::istreambuf_iterator<char>());
        } else {
            std::ifstream f;
            f.open(filename.c_str());
            if (!f.good()) {
                std::string msg = "Opening input file: " + filename;
                perror(msg.c_str());
                return EXIT_FAILURE;
            }
            input.assign(std::istreambuf_iterator<char>(f),
                         std::istreambuf_iterator<char>());
            if (!f.good()) {
                std::string msg = "Reading input file: " + filename;
                perror(msg.c_str());
                return EXIT_FAILURE;
            }
        }
    }

    int error;
    const char *output =
        jsonnet_evaluate_snippet(vm, filename.c_str(), input.c_str(), &error);

    if (error) {
        std::cerr << output;
        std::cerr.flush();
        jsonnet_cleanup_string(vm, output);
        jsonnet_destroy(vm);
        return EXIT_FAILURE;
    }

    std::cout << output;
    std::cout.flush();
    jsonnet_cleanup_string(vm, output);
    jsonnet_destroy(vm);
    return EXIT_SUCCESS;
}
예제 #5
0
int main(int argc, const char **argv)
{
				JsonnetVM *vm = jsonnet_make();
				std::string filename = "-";
				bool filename_is_code = false;
				bool multi = false;

				auto args = simplify_args(argc, argv);
				std::vector<std::string> remaining_args;

				for (unsigned i = 0; i<args.size(); ++i) {
								const std::string &arg = args[i];
								if (arg == "-h" || arg == "--help") {
												usage(std::cout);
												exit(EXIT_SUCCESS);
								}
								else if (arg == "-s" || arg == "--max-stack") {
												long l = strtol_check(next_arg(i, args));
												if (l < 1) {
																std::cerr << "ERROR: Invalid --max-stack value: " << l << "\n" << std::endl;
																usage(std::cerr);
																exit(EXIT_FAILURE);
												}
												jsonnet_max_stack(vm, l);
								}
								else if (arg == "-E" || arg == "--env") {
												const std::string var = next_arg(i, args);
												const char *val = ::getenv(var.c_str());
												if (val == nullptr) {
																std::cerr << "ERROR: Environment variable " << var
																				<< " was undefined." << std::endl;
																exit(EXIT_FAILURE);
												}
												jsonnet_ext_var(vm, var.c_str(), val);
								}
								else if (arg == "-V" || arg == "--var") {
												const std::string var_val = next_arg(i, args);
												size_t eq_pos = var_val.find_first_of('=', 0);
												if (eq_pos == std::string::npos) {
																std::cerr << "ERROR: argument not in form <var>=<val> \""
																				<< var_val << "\"." << std::endl;
																exit(EXIT_FAILURE);
												}
												const std::string var = var_val.substr(0, eq_pos);
												const std::string val = var_val.substr(eq_pos + 1, std::string::npos);
												jsonnet_ext_var(vm, var.c_str(), val.c_str());
								}
								else if (arg == "--gc-min-objects") {
												long l = strtol_check(next_arg(i, args));
												if (l < 0) {
																std::cerr << "ERROR: Invalid --gc-min-objects value: " << l << std::endl;
																usage(std::cerr);
																exit(EXIT_FAILURE);
												}
												jsonnet_gc_min_objects(vm, l);
								}
								else if (arg == "-t" || arg == "--max-trace") {
												long l = strtol_check(next_arg(i, args));
												if (l < 0) {
																std::cerr << "ERROR: Invalid --max-trace value: " << l << std::endl;
																usage(std::cerr);
																exit(EXIT_FAILURE);
												}
												jsonnet_max_trace(vm, l);
								}
								else if (arg == "--gc-growth-trigger") {
												const char *arg = next_arg(i, args).c_str();
												char *ep;
												double v = std::strtod(arg, &ep);
												if (*ep != '\0' || *arg == '\0') {
																std::cerr << "ERROR: Invalid number \"" << arg << "\"" << std::endl;
																usage(std::cerr);
																exit(EXIT_FAILURE);
												}
												if (v < 0) {
																std::cerr << "ERROR: Invalid --gc-growth-trigger \"" << arg << "\"\n" << std::endl;
																usage(std::cerr);
																exit(EXIT_FAILURE);
												}
												jsonnet_gc_growth_trigger(vm, v);
								}
								else if (arg == "-e" || arg == "--exec") {
												filename_is_code = true;
								}
								else if (arg == "-m" || arg == "--multi") {
												multi = true;
								}
								else if (arg == "--debug-ast") {
												jsonnet_debug_ast(vm, true);
								}
								else if (arg == "--") {
												// All subsequent args are not options.
												while ((++i) < args.size())
																remaining_args.push_back(args[i]);
												break;
								}
								else {
												remaining_args.push_back(args[i]);
								}
				}

				if (remaining_args.size() > 0)
								filename = remaining_args[0];

				if (remaining_args.size() > 1) {
								std::cerr << "ERROR: Filename already specified as \"" << filename << "\"\n"
												<< std::endl;
								usage(std::cerr);
								exit(EXIT_FAILURE);
				}

				if (filename_is_code && remaining_args.size() == 0) {
								std::cerr << "ERROR: Must give filename when using -e, --exec\n" << std::endl;
								usage(std::cerr);
								exit(EXIT_FAILURE);
				}

				std::string input;
				if (filename_is_code) {
								input = filename;
								filename = "<cmdline>";
				}
				else {
								if (filename == "-") {
												filename = "<stdin>";
												input.assign(std::istreambuf_iterator<char>(std::cin),
																std::istreambuf_iterator<char>());
								}
								else {
												std::ifstream f;
												f.open(filename.c_str());
												if (!f.good()) {
																std::string msg = "Opening input file: " + filename;
																perror(msg.c_str());
																return EXIT_FAILURE;
												}
												input.assign(std::istreambuf_iterator<char>(f),
																std::istreambuf_iterator<char>());
												if (!f.good()) {
																std::string msg = "Reading input file: " + filename;
																perror(msg.c_str());
																return EXIT_FAILURE;
												}
								}
				}

				int error;
				const char *output;
				if (multi) {
								output = jsonnet_evaluate_snippet_multi(vm, filename.c_str(), input.c_str(), &error);
				}
				else {
								output = jsonnet_evaluate_snippet(vm, filename.c_str(), input.c_str(), &error);
				}

				if (error) {
								std::cerr << output;
								std::cerr.flush();
								jsonnet_cleanup_string(vm, output);
								jsonnet_destroy(vm);
								return EXIT_FAILURE;
				}

				if (multi) {
								std::map<std::string, std::string> r;
								for (const char *c = output; *c != '\0';) {
												const char *filename = c;
												const char *c2 = c;
												while (*c2 != '\0') ++c2;
												++c2;
												const char *json = c2;
												while (*c2 != '\0') ++c2;
												++c2;
												c = c2;
												r[filename] = json;
								}
								jsonnet_cleanup_string(vm, output);
								for (const auto &pair : r) {
												const std::string &new_content = pair.second;
												const std::string &filename = pair.first;
												std::cout << filename << std::endl;
												{
																std::ifstream exists(filename.c_str());
																if (exists.good()) {
																				std::string existing_content;
																				existing_content.assign(std::istreambuf_iterator<char>(exists),
																								std::istreambuf_iterator<char>());
																				if (existing_content == new_content) {
																								// Do not bump the timestamp on the file if its content is the same.
																								// This may trigger other tools (e.g. make) to do unnecessary work.
																								continue;
																				}
																}
												}
												std::ofstream f;
												f.open(filename.c_str());
												if (!f.good()) {
																std::string msg = "Opening output file: " + filename;
																perror(msg.c_str());
																jsonnet_destroy(vm);
																return EXIT_FAILURE;
												}
												f << new_content;
												f.close();
												if (!f.good()) {
																std::string msg = "Writing to output file: " + filename;
																perror(msg.c_str());
																jsonnet_destroy(vm);
																return EXIT_FAILURE;
												}
								}
								std::cout.flush();
				}
				else {
								std::cout << output;
								std::cout.flush();
								jsonnet_cleanup_string(vm, output);
				}
				jsonnet_destroy(vm);
				return EXIT_SUCCESS;
}