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