@@ -91,7 +91,7 @@ void usage(std::ostream &o)
91
91
o << " Available options for specifying values of 'external' variables:\n " ;
92
92
o << " Provide the value as a string:\n " ;
93
93
o << " -V / --ext-str <var>[=<val>] If <val> is omitted, get from environment var <var>\n " ;
94
- o << " --ext-str-file <var>=<file> Read the string from the file\n " ;
94
+ o << " --ext-str-file <var>=<file> Read the string from the file(s) \n " ;
95
95
o << " Provide a value as Jsonnet code:\n " ;
96
96
o << " --ext-code <var>[=<code>] If <code> is omitted, get from environment var <var>\n " ;
97
97
o << " --ext-code-file <var>=<file> Read the code from the file\n " ;
@@ -110,7 +110,7 @@ void usage(std::ostream &o)
110
110
o << " -h / --help This message\n " ;
111
111
o << " -e / --exec Treat filename as code\n " ;
112
112
o << " -o / --output-file <file> Write to the output file rather than stdout\n " ;
113
- o << " -i / --in-place Update the Jsonnet file in place. Same as -o <filename>\n " ;
113
+ o << " -i / --in-place Update the Jsonnet file(s) in place. Same as -o <filename>\n " ;
114
114
o << " --test Exit with failure if reformatting changed the file.\n " ;
115
115
o << " -n / --indent <n> Number of spaces to indent by (default 0, means no change)\n " ;
116
116
o << " --max-blank-lines <n> Max vertical spacing, 0 means no change (default 2)\n " ;
@@ -121,6 +121,7 @@ void usage(std::ostream &o)
121
121
o << " --[no-]pad-objects { x: 1, x: 2 } instead of {x: 1, y: 2} (on by default)\n " ;
122
122
o << " --debug-desugaring Unparse the desugared AST without executing it\n " ;
123
123
o << " --version Print version\n " ;
124
+ o << " Note: Multiple filenames can be provided at once when using -i or --test options.\n " ;
124
125
o << " \n " ;
125
126
o << " In all cases:\n " ;
126
127
o << " <filename> can be - (stdin)\n " ;
@@ -152,7 +153,7 @@ enum Command {
152
153
/* * Class for representing configuration read from command line flags. */
153
154
struct JsonnetConfig {
154
155
Command cmd;
155
- std::string inputFile ;
156
+ std::vector<std:: string> inputFiles ;
156
157
std::string outputFile;
157
158
bool filenameIsCode;
158
159
@@ -455,44 +456,53 @@ static bool process_args(int argc,
455
456
return false ;
456
457
}
457
458
458
- std::string filename = remaining_args[0 ];
459
- if (remaining_args.size () > 1 ) {
460
- std::cerr << " ERROR: Already specified " << want
461
- << " as \" " << filename << " \"\n "
462
- << std::endl;
463
- usage (std::cerr);
459
+ bool multiple_files_allowed = config->cmd == FMT &&
460
+ (config->fmtTest || config->fmtInPlace );
461
+ if (!multiple_files_allowed) {
462
+ std::string filename = remaining_args[0 ];
463
+ if (remaining_args.size () > 1 ) {
464
+ std::cerr << " ERROR: Already specified " << want
465
+ << " as \" " << filename << " \"\n "
466
+ << std::endl;
467
+ usage (std::cerr);
468
+ return false ;
469
+ }
470
+ }
471
+ config->inputFiles = remaining_args;
472
+ return true ;
473
+ }
474
+
475
+ static bool read_input_contents (std::string filename, std::string *input) {
476
+ std::ifstream f;
477
+ f.open (filename);
478
+ if (!f.good ()) {
479
+ std::string msg = " Opening input file: " + filename;
480
+ perror (msg.c_str ());
481
+ return false ;
482
+ }
483
+ input->assign (std::istreambuf_iterator<char >(f),
484
+ std::istreambuf_iterator<char >());
485
+ if (!f.good ()) {
486
+ std::string msg = " Reading input file: " + filename;
487
+ perror (msg.c_str ());
464
488
return false ;
465
489
}
466
- config->inputFile = filename;
467
490
return true ;
468
491
}
469
492
470
493
/* * Reads Jsonnet code from the input file or stdin into the input buffer. */
471
- static bool read_input (JsonnetConfig* config, std::string* input) {
494
+ static bool read_input (JsonnetConfig* config, int index, std::string* input) {
472
495
if (config->filenameIsCode ) {
473
- *input = config->inputFile ;
474
- config->inputFile = " <cmdline>" ;
496
+ *input = config->inputFiles [ index ] ;
497
+ config->inputFiles [ index ] = " <cmdline>" ; // side effect!
475
498
} else {
476
- // Input file "-" tell Jsonnet to read stdin.
477
- if (config->inputFile == " -" ) {
478
- config->inputFile = " <stdin>" ;
499
+ // Input file "-" tells Jsonnet to read stdin.
500
+ if (config->inputFiles [ index ] == " -" ) {
501
+ config->inputFiles [ index ] = " <stdin>" ; // side effect!
479
502
input->assign (std::istreambuf_iterator<char >(std::cin),
480
503
std::istreambuf_iterator<char >());
481
504
} else {
482
- std::ifstream f;
483
- f.open (config->inputFile .c_str ());
484
- if (!f.good ()) {
485
- std::string msg = " Opening input file: " + config->inputFile ;
486
- perror (msg.c_str ());
487
- return false ;
488
- }
489
- input->assign (std::istreambuf_iterator<char >(f),
490
- std::istreambuf_iterator<char >());
491
- if (!f.good ()) {
492
- std::string msg = " Reading input file: " + config->inputFile ;
493
- perror (msg.c_str ());
494
- return false ;
495
- }
505
+ return read_input_contents (config->inputFiles [index ], input);
496
506
}
497
507
}
498
508
return true ;
@@ -618,27 +628,29 @@ int main(int argc, const char **argv)
618
628
return EXIT_FAILURE;
619
629
}
620
630
621
- // Read input files.
622
- std::string input;
623
- if (!read_input (&config, &input)) {
624
- jsonnet_destroy (vm);
625
- return EXIT_FAILURE;
626
- }
627
-
628
631
// Evaluate input Jsonnet and handle any errors from Jsonnet VM.
629
632
int error;
630
633
char *output;
631
634
switch (config.cmd ) {
632
635
case EVAL: {
636
+ assert (config.inputFiles .size () == 1 );
637
+
638
+ // Read input file.
639
+ std::string input;
640
+ if (!read_input (&config, 0 , &input)) {
641
+ jsonnet_destroy (vm);
642
+ return EXIT_FAILURE;
643
+ }
644
+
633
645
if (config.evalMulti ) {
634
646
output = jsonnet_evaluate_snippet_multi (
635
- vm, config.inputFile .c_str (), input.c_str (), &error);
647
+ vm, config.inputFiles [ 0 ] .c_str (), input.c_str (), &error);
636
648
} else if (config.evalStream ) {
637
649
output = jsonnet_evaluate_snippet_stream (
638
- vm, config.inputFile .c_str (), input.c_str (), &error);
650
+ vm, config.inputFiles [ 0 ] .c_str (), input.c_str (), &error);
639
651
} else {
640
652
output = jsonnet_evaluate_snippet (
641
- vm, config.inputFile .c_str (), input.c_str (), &error);
653
+ vm, config.inputFiles [ 0 ] .c_str (), input.c_str (), &error);
642
654
}
643
655
644
656
if (error) {
@@ -673,38 +685,76 @@ int main(int argc, const char **argv)
673
685
674
686
case FMT: {
675
687
std::string output_file = config.outputFile ;
676
- if (config.fmtInPlace ) {
677
- if (config.inputFile == " -" ) {
678
- std::cerr << " ERROR: Cannot use --in-place with stdin" << std::endl;
679
- jsonnet_destroy (vm);
680
- return EXIT_FAILURE;
688
+
689
+ if (config.fmtInPlace || config.fmtTest ) {
690
+ assert (config.inputFiles .size () >= 1 );
691
+ for (std::string inputFile: config.inputFiles ) {
692
+ if (config.fmtInPlace ) {
693
+ output_file = inputFile;
694
+
695
+ if (inputFile == " -" ) {
696
+ std::cerr << " ERROR: Cannot use --in-place with stdin" << std::endl;
697
+ jsonnet_destroy (vm);
698
+ return EXIT_FAILURE;
699
+ }
700
+ if (config.filenameIsCode ) {
701
+ std::cerr << " ERROR: Cannot use --in-place with --exec" << std::endl;
702
+ jsonnet_destroy (vm);
703
+ return EXIT_FAILURE;
704
+ }
705
+ }
706
+
707
+ std::string input;
708
+ if (!read_input_contents (inputFile, &input)) {
709
+ jsonnet_destroy (vm);
710
+ return EXIT_FAILURE;
711
+ }
712
+
713
+ output = jsonnet_fmt_snippet (vm, inputFile.c_str (), input.c_str (), &error);
714
+
715
+ if (error) {
716
+ std::cerr << output;
717
+ std::cerr.flush ();
718
+ jsonnet_realloc (vm, output, 0 );
719
+ jsonnet_destroy (vm);
720
+ return EXIT_FAILURE;
721
+ }
722
+
723
+ if (config.fmtTest ) {
724
+ // Check the output matches the input.
725
+ bool ok = output == input;
726
+ jsonnet_realloc (vm, output, 0 );
727
+ jsonnet_destroy (vm);
728
+ return ok ? EXIT_SUCCESS : 2 ;
729
+ } else {
730
+ // Write output Jsonnet.
731
+ bool successful = write_output_file (output, output_file);
732
+ jsonnet_realloc (vm, output, 0 );
733
+ if (!successful) {
734
+ jsonnet_destroy (vm);
735
+ return EXIT_FAILURE;
736
+ }
737
+ }
681
738
}
682
- if (config.filenameIsCode ) {
683
- std::cerr << " ERROR: Cannot use --in-place with --exec" << std::endl;
739
+ } else {
740
+ assert (config.inputFiles .size () == 1 );
741
+ // Read input file.
742
+ std::string input;
743
+ if (!read_input (&config, 0 , &input)) {
684
744
jsonnet_destroy (vm);
685
745
return EXIT_FAILURE;
686
746
}
687
- output_file = config.inputFile ;
688
- }
689
747
690
- output = jsonnet_fmt_snippet (vm, config.inputFile .c_str (), input.c_str (), &error);
748
+ output = jsonnet_fmt_snippet (vm, config.inputFiles [ 0 ] .c_str (), input.c_str (), &error);
691
749
692
- if (error) {
693
- std::cerr << output;
694
- std::cerr.flush ();
695
- jsonnet_realloc (vm, output, 0 );
696
- jsonnet_destroy (vm);
697
- return EXIT_FAILURE;
698
- }
699
-
700
- if (config.fmtTest ) {
701
- // Check the output matches the input.
702
- bool ok = output == input;
703
- jsonnet_realloc (vm, output, 0 );
704
- jsonnet_destroy (vm);
705
- return ok ? EXIT_SUCCESS : 2 ;
750
+ if (error) {
751
+ std::cerr << output;
752
+ std::cerr.flush ();
753
+ jsonnet_realloc (vm, output, 0 );
754
+ jsonnet_destroy (vm);
755
+ return EXIT_FAILURE;
756
+ }
706
757
707
- } else {
708
758
// Write output Jsonnet.
709
759
bool successful = write_output_file (output, output_file);
710
760
jsonnet_realloc (vm, output, 0 );
0 commit comments