]> granicus.if.org Git - jq/commitdiff
Refactor handling of inputs in main() (fix #667)
authorNicolas Williams <nico@cryptonector.com>
Sat, 27 Dec 2014 23:58:47 +0000 (17:58 -0600)
committerNicolas Williams <nico@cryptonector.com>
Fri, 13 Feb 2015 21:55:31 +0000 (15:55 -0600)
Much of this could be in libjq.  Eventually all of the work of reading
from files and looping over `jq_next()` should move into libjq, with
`main()` mostly doing all the command-line option processing.

main.c

diff --git a/main.c b/main.c
index 7a91f038dfdaf417f015adf9315f4d501e0df735..6321cabe05fad93445a1860e7d22b15369ae7d20 100644 (file)
--- a/main.c
+++ b/main.c
@@ -140,56 +140,98 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts) {
   return ret;
 }
 
-// XXX Move all this into the next_input_state struct
-FILE* current_input;
-const char** input_filenames = NULL;
-int ninput_files;
-int next_input_idx;
-static int read_more(char* buf, size_t size) {
-  if (!current_input || feof(current_input)) {
-    if (current_input) {
-      if (current_input == stdin) {
+// XXX Move this and related functions into libjq (with a better name), into util.[ch] say
+struct next_input_state {
+  FILE* current_input;
+  const char** input_filenames;
+  int alloced;
+  int ninput_files;
+  int next_input_idx;
+  int open_failures;
+  struct jv_parser *parser;
+  jv slurped;
+  char buf[4096];
+};
+typedef struct next_input_state *next_input_state;
+
+static void input_state_free(next_input_state *state) {
+  next_input_state old_state = *state;
+  *state = NULL;
+  if (old_state == NULL)
+    return;
+  if (old_state->parser != NULL)
+    jv_parser_free(old_state->parser);
+  jv_mem_free(old_state->input_filenames);
+  jv_free(old_state->slurped);
+  jv_mem_free(old_state);
+}
+
+static int input_state_init(next_input_state *state, int max_inputs) {
+  next_input_state new_state = jv_mem_alloc(sizeof(*new_state));
+  new_state->next_input_idx = 0;
+  new_state->open_failures = 0;
+  new_state->ninput_files = 0;
+  new_state->current_input = NULL;
+  new_state->parser = NULL;         // initialized when we know the flags
+  new_state->slurped = jv_invalid();
+  new_state->buf[0] = 0;
+
+  // XXX a jv_mem_calloc() would be nice
+  assert(max_inputs > 0 && (uintmax_t)max_inputs * sizeof(const char*) < SIZE_MAX);
+  new_state->input_filenames = jv_mem_alloc(sizeof(const char*) * max_inputs);
+  new_state->alloced = max_inputs;
+  for (; max_inputs > 0; max_inputs--) 
+    new_state->input_filenames[max_inputs - 1] = NULL;
+  *state = new_state;
+  return 0;
+}
+
+static void input_state_add_input(next_input_state state, const char *input) {
+  assert(state->ninput_files < state->alloced);
+  state->input_filenames[state->ninput_files++] = input;
+}
+
+static int input_state_read_more(next_input_state state) {
+  if (!state->current_input || feof(state->current_input)) {
+    if (state->current_input) {
+      if (state->current_input == stdin) {
         clearerr(stdin); // perhaps we can read again; anyways, we don't fclose(stdin)
       } else {
-        fclose(current_input);
+        fclose(state->current_input);
       }
-      current_input = 0;
+      state->current_input = NULL;
     }
-    if (next_input_idx < ninput_files) {
-      if (!strcmp(input_filenames[next_input_idx], "-")) {
-        current_input = stdin;
+    if (state->next_input_idx < state->ninput_files) {
+      if (!strcmp(state->input_filenames[state->next_input_idx], "-")) {
+        state->current_input = stdin;
       } else {
-        current_input = fopen(input_filenames[next_input_idx], "r");
-        if (!current_input)
-          fprintf(stderr, "%s: %s: %s\n", progname, input_filenames[next_input_idx], strerror(errno));
+        state->current_input = fopen(state->input_filenames[state->next_input_idx], "r");
+        if (!state->current_input) {
+          fprintf(stderr, "%s: Error: could not open %s: %s\n", progname, state->input_filenames[state->next_input_idx], strerror(errno));
+          state->open_failures++;
+        }
       }
-      next_input_idx++;
+      state->next_input_idx++;
     }
   }
 
-  buf[0] = 0;
-  if (current_input) {
-    if (!fgets(buf, size, current_input))
-      buf[0] = 0;
+  state->buf[0] = 0;
+  if (state->current_input) {
+    if (!fgets(state->buf, sizeof(state->buf), state->current_input))
+      state->buf[0] = 0;
   }
-  return next_input_idx == ninput_files && (!current_input || feof(current_input));
+  return state->next_input_idx == state->ninput_files && (!state->current_input || feof(state->current_input));
 }
 
-struct next_input_state {
-  struct jv_parser *parser;
-  jv slurped;
-  char buf[4096];
-};
-
 // Blocks to read one more input from stdin and/or given files
 // When slurping, it returns just one value
-static jv next_input(jq_state *jq, void *data) {
-  struct next_input_state *state = data;
+static jv input_state_next_input(jq_state *jq, void *data) {
+  next_input_state state = data;
   int is_last = 0;
   jv value = jv_invalid(); // need more input
   do {
     if (options & RAW_INPUT) {
-      is_last = read_more(state->buf, sizeof(state->buf));
+      is_last = input_state_read_more(state);
       if (state->buf[0] == '\0')
         continue;
       int len = strlen(state->buf); // Raw input doesn't support NULs
@@ -210,7 +252,7 @@ static jv next_input(jq_state *jq, void *data) {
       }
     } else {
       if (jv_parser_remaining(state->parser) == 0) {
-        is_last = read_more(state->buf, sizeof(state->buf));
+        is_last = input_state_read_more(state);
         jv_parser_set_buf(state->parser, state->buf, strlen(state->buf), !is_last); // NULs also not supported here
       }
       value = jv_parser_next(state->parser);
@@ -227,6 +269,7 @@ static jv next_input(jq_state *jq, void *data) {
   } while (!is_last);
   return value;
 }
+// XXX End of stuff to move into utils
 
 static void debug_cb(jq_state *jq, void *data, jv input) {
   int dumpopts = *(int *)data;
@@ -251,8 +294,10 @@ int main(int argc, char* argv[]) {
   }
 
   const char* program = 0;
-  input_filenames = jv_mem_alloc(sizeof(const char*) * argc);
-  ninput_files = 0;
+
+  next_input_state input_state;
+  input_state_init(&input_state, argc);
+
   int further_args_are_files = 0;
   int jq_flags = 0;
   size_t short_opts = 0;
@@ -260,13 +305,13 @@ int main(int argc, char* argv[]) {
   jv lib_search_paths = jv_null();
   for (int i=1; i<argc; i++, short_opts = 0) {
     if (further_args_are_files) {
-      input_filenames[ninput_files++] = argv[i];
+      input_state_add_input(input_state, argv[i]);
     } else if (!strcmp(argv[i], "--")) {
       if (!program) usage(2);
       further_args_are_files = 1;
     } else if (!isoptish(argv[i])) {
       if (program) {
-        input_filenames[ninput_files++] = argv[i];
+        input_state_add_input(input_state, argv[i]);
       } else {
         program = argv[i];
       }
@@ -489,11 +534,11 @@ int main(int argc, char* argv[]) {
 
   if (!program) usage(2);
   if ((options & IN_PLACE)) {
-    if (ninput_files == 0) usage(2);
-    if (strcmp(input_filenames[0], "-") == 0) usage(2);
-    size_t tlen = strlen(input_filenames[0]) + 7;
+    if (input_state->ninput_files == 0) usage(2);
+    if (strcmp(input_state->input_filenames[0], "-") == 0) usage(2);
+    size_t tlen = strlen(input_state->input_filenames[0]) + 7;
     t = jv_mem_alloc(tlen);
-    int n = snprintf(t, tlen,"%sXXXXXX", input_filenames[0]);
+    int n = snprintf(t, tlen,"%sXXXXXX", input_state->input_filenames[0]);
     assert(n > 0 && (size_t)n < tlen);
     if (mkstemp(t) == -1) {
       fprintf(stderr, "Error: %s creating temporary file", strerror(errno));
@@ -504,7 +549,6 @@ int main(int argc, char* argv[]) {
       exit(3);
     }
   }
-  if (ninput_files == 0) current_input = stdin;
 
   if ((options & PROVIDE_NULL) && (options & (RAW_INPUT | SLURP))) {
     fprintf(stderr, "%s: --null-input cannot be used with --raw-input or --slurp\n", progname);
@@ -544,22 +588,20 @@ int main(int argc, char* argv[]) {
     printf("\n");
   }
 
-  // XXX Refactor this and input_filenames[] and related setup into a
-  // function to setup struct next_input_state.
   if ((options & SEQ))
     parser_flags |= JV_PARSE_SEQ;
 
-  struct next_input_state input_state;
-  input_state.parser = jv_parser_new(parser_flags);
+  if (input_state->ninput_files == 0) input_state->current_input = stdin;
+  input_state->parser = jv_parser_new(parser_flags);
   if ((options & RAW_INPUT) && (options & SLURP))
-    input_state.slurped = jv_string("");
+    input_state->slurped = jv_string("");
   else if ((options & SLURP))
-    input_state.slurped = jv_array();
+    input_state->slurped = jv_array();
   else
-    input_state.slurped = jv_invalid();
+    input_state->slurped = jv_invalid();
 
   // Let jq program read from inputs
-  jq_set_input_cb(jq, next_input, &input_state);
+  jq_set_input_cb(jq, input_state_next_input, input_state);
 
   jq_set_debug_cb(jq, debug_cb, &dumpopts);
 
@@ -567,7 +609,8 @@ int main(int argc, char* argv[]) {
     ret = process(jq, jv_null(), jq_flags, dumpopts);
   } else {
     jv value;
-    while (jv_is_valid((value = next_input(jq, &input_state))) || jv_invalid_has_msg(jv_copy(value))) {
+    while (input_state->open_failures == 0 &&
+           (jv_is_valid((value = input_state_next_input(jq, input_state))) || jv_invalid_has_msg(jv_copy(value)))) {
       if (jv_is_valid(value)) {
         ret = process(jq, value, jq_flags, dumpopts);
         continue;
@@ -586,13 +629,13 @@ int main(int argc, char* argv[]) {
       jv_free(msg);
     }
     if (options & SLURP) {
-      ret = process(jq, input_state.slurped, jq_flags, dumpopts);
-      input_state.slurped = jv_invalid();
+      ret = process(jq, input_state->slurped, jq_flags, dumpopts);
+      input_state->slurped = jv_invalid();
     }
   }
 
-  jv_free(input_state.slurped);
-  jv_parser_free(input_state.parser);
+  if (ret == 0 && input_state->open_failures != 0)
+    ret = 2;
 
   if (ret != 0)
     goto out;
@@ -608,14 +651,15 @@ int main(int argc, char* argv[]) {
       fprintf(stderr, "Error: %s opening /dev/null\n", strerror(errno));
       exit(3);
     }
-    if (rename(t, input_filenames[0]) == -1) {
+    assert(input_state->ninput_files > 0 && !strcmp(input_state->input_filenames[0], "-"));
+    if (rename(t, input_state->input_filenames[0]) == -1) {
       fprintf(stderr, "Error: %s renaming temporary file\n", strerror(errno));
       exit(3);
     }
     jv_mem_free(t);
   }
 out:
-  jv_mem_free(input_filenames);
+  input_state_free(&input_state);
   jq_teardown(&jq);
   if (ret >= 10 && (options & EXIT_STATUS))
     return ret - 10;