int handle_ext_vars(struct JsonnetVm *vm, PyObject *ext_vars) { if (ext_vars == NULL) return 1; PyObject *key, *val; Py_ssize_t pos = 0; while (PyDict_Next(ext_vars, &pos, &key, &val)) { const char *key_ = PyString_AsString(key); if (key_ == NULL) { jsonnet_destroy(vm); return 0; } const char *val_ = PyString_AsString(val); if (val_ == NULL) { jsonnet_destroy(vm); return 0; } jsonnet_ext_var(vm, key_, val_); } return 1; }
/** 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; }
/** 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; }
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; }