]> granicus.if.org Git - jq/commitdiff
Refactor moar: move parts of main.c into libjq
authorNicolas Williams <nico@cryptonector.com>
Mon, 2 Feb 2015 00:35:42 +0000 (18:35 -0600)
committerNicolas Williams <nico@cryptonector.com>
Fri, 13 Feb 2015 21:58:02 +0000 (15:58 -0600)
This adds utility functions for reading and parsing files that should be
easy to use by other apps, together with jq_start()/jq_next().

jq.h
jv.h
main.c
util.c

diff --git a/jq.h b/jq.h
index 2252fa844956dccba3d1550b83210208e4dc4a67..280e8277fdafefa87faf2972cb938e30d56d6b1d 100644 (file)
--- a/jq.h
+++ b/jq.h
@@ -37,4 +37,16 @@ jv jq_get_prog_origin(jq_state *);
 jv jq_get_lib_dirs(jq_state *);
 void jq_set_attr(jq_state *, jv, jv);
 jv jq_get_attr(jq_state *, jv);
+
+typedef struct jq_util_input_state *jq_util_input_state;
+
+jq_util_input_state jq_util_input_init(jq_err_cb, void *);
+void jq_util_input_set_parser(jq_util_input_state, jv_parser *, int);
+void jq_util_input_free(jq_util_input_state *);
+void jq_util_input_add_input(jq_util_input_state, jv);
+int jq_util_input_open_errors(jq_util_input_state);
+int jq_util_input_read_more(jq_util_input_state);
+jv jq_util_input_next_input(jq_util_input_state);
+jv jq_util_input_next_input_cb(jq_state *, void *);
+
 #endif /* !_JQ_H_ */
diff --git a/jv.h b/jv.h
index 9e7077b426c4c09a043c4b8d485cf9d11e604fe5..8ed35c14a37e9a9511ea4f97da53d0f7d259c864 100644 (file)
--- a/jv.h
+++ b/jv.h
@@ -184,12 +184,12 @@ void jv_nomem_handler(jv_nomem_handler_f, void *);
 
 jv jv_load_file(const char *, int);
 
-struct jv_parser;
-struct jv_parser* jv_parser_new(int);
-void jv_parser_set_buf(struct jv_parser*, const char*, int, int);
-int jv_parser_remaining(struct jv_parser*);
-jv jv_parser_next(struct jv_parser*);
-void jv_parser_free(struct jv_parser*);
+typedef struct jv_parser jv_parser;
+jv_parser* jv_parser_new(int);
+void jv_parser_set_buf(jv_parser*, const char*, int, int);
+int jv_parser_remaining(jv_parser*);
+jv jv_parser_next(jv_parser*);
+void jv_parser_free(jv_parser*);
 
 jv jv_get(jv, jv);
 jv jv_set(jv, jv, jv);
diff --git a/main.c b/main.c
index 6321cabe05fad93445a1860e7d22b15369ae7d20..db0472e9d27b77fcf65131c171892ae61420f8fd 100644 (file)
--- a/main.c
+++ b/main.c
@@ -140,137 +140,6 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts) {
   return ret;
 }
 
-// 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(state->current_input);
-      }
-      state->current_input = NULL;
-    }
-    if (state->next_input_idx < state->ninput_files) {
-      if (!strcmp(state->input_filenames[state->next_input_idx], "-")) {
-        state->current_input = stdin;
-      } else {
-        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++;
-        }
-      }
-      state->next_input_idx++;
-    }
-  }
-
-  state->buf[0] = 0;
-  if (state->current_input) {
-    if (!fgets(state->buf, sizeof(state->buf), state->current_input))
-      state->buf[0] = 0;
-  }
-  return state->next_input_idx == state->ninput_files && (!state->current_input || feof(state->current_input));
-}
-
-// Blocks to read one more input from stdin and/or given files
-// When slurping, it returns just one value
-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 = input_state_read_more(state);
-      if (state->buf[0] == '\0')
-        continue;
-      int len = strlen(state->buf); // Raw input doesn't support NULs
-      if (len > 0) {
-        if (options & SLURP) {
-          state->slurped = jv_string_concat(state->slurped, jv_string(state->buf));
-        } else {
-          if (!jv_is_valid(value))
-            value = jv_string("");
-          if (state->buf[len-1] == '\n') {
-            // whole line
-            state->buf[len-1] = 0;
-            return jv_string_concat(value, jv_string(state->buf));
-          }
-          value = jv_string_concat(value, jv_string(state->buf));
-          state->buf[0] = '\0';
-        }
-      }
-    } else {
-      if (jv_parser_remaining(state->parser) == 0) {
-        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);
-      if (options & SLURP) {
-        if (jv_is_valid(value)) {
-          state->slurped = jv_array_append(state->slurped, value);
-          value = jv_invalid();
-        } else if (jv_invalid_has_msg(jv_copy(value)))
-          return value;
-      } else if (jv_is_valid(value) || jv_invalid_has_msg(jv_copy(value))) {
-        return value;
-      }
-    }
-  } 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;
   jv_dumpf(JV_ARRAY(jv_string("DEBUG:"), input), stderr, dumpopts & ~(JV_PRINT_PRETTY));
@@ -295,23 +164,25 @@ int main(int argc, char* argv[]) {
 
   const char* program = 0;
 
-  next_input_state input_state;
-  input_state_init(&input_state, argc);
+  jq_util_input_state input_state = jq_util_input_init(NULL, NULL); // XXX add err_cb
 
   int further_args_are_files = 0;
   int jq_flags = 0;
   size_t short_opts = 0;
   jv program_arguments = jv_array();
   jv lib_search_paths = jv_null();
+  char *first_file = 0;
   for (int i=1; i<argc; i++, short_opts = 0) {
     if (further_args_are_files) {
-      input_state_add_input(input_state, argv[i]);
+      jq_util_input_add_input(input_state, jv_string(argv[i]));
+      first_file = first_file ? first_file : argv[i];
     } else if (!strcmp(argv[i], "--")) {
       if (!program) usage(2);
       further_args_are_files = 1;
     } else if (!isoptish(argv[i])) {
       if (program) {
-        input_state_add_input(input_state, argv[i]);
+        jq_util_input_add_input(input_state, jv_string(argv[i]));
+        first_file = first_file ? first_file : argv[i];
       } else {
         program = argv[i];
       }
@@ -534,11 +405,11 @@ int main(int argc, char* argv[]) {
 
   if (!program) usage(2);
   if ((options & IN_PLACE)) {
-    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;
+    if (first_file == 0) usage(2);
+    if (strcmp(first_file, "-") == 0) usage(2);
+    size_t tlen = strlen(first_file) + 7;
     t = jv_mem_alloc(tlen);
-    int n = snprintf(t, tlen,"%sXXXXXX", input_state->input_filenames[0]);
+    int n = snprintf(t, tlen,"%sXXXXXX", first_file);
     assert(n > 0 && (size_t)n < tlen);
     if (mkstemp(t) == -1) {
       fprintf(stderr, "Error: %s creating temporary file", strerror(errno));
@@ -591,26 +462,23 @@ int main(int argc, char* argv[]) {
   if ((options & SEQ))
     parser_flags |= JV_PARSE_SEQ;
 
-  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("");
-  else if ((options & SLURP))
-    input_state->slurped = jv_array();
-  else
-    input_state->slurped = jv_invalid();
+  if (!(options & RAW_INPUT))
+    jq_util_input_set_parser(input_state, jv_parser_new(parser_flags), (options & SLURP) ? 1 : 0);
 
   // Let jq program read from inputs
-  jq_set_input_cb(jq, input_state_next_input, input_state);
+  jq_set_input_cb(jq, jq_util_input_next_input_cb, input_state);
 
+  // Let jq program call `debug` builtin and have that go somewhere
   jq_set_debug_cb(jq, debug_cb, &dumpopts);
 
+  if (first_file == 0)
+    jq_util_input_add_input(input_state, jv_string("-"));
   if (options & PROVIDE_NULL) {
     ret = process(jq, jv_null(), jq_flags, dumpopts);
   } else {
     jv 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)))) {
+    while (jq_util_input_open_errors(input_state) == 0 &&
+           (jv_is_valid((value = jq_util_input_next_input(input_state))) || jv_invalid_has_msg(jv_copy(value)))) {
       if (jv_is_valid(value)) {
         ret = process(jq, value, jq_flags, dumpopts);
         continue;
@@ -628,13 +496,9 @@ int main(int argc, char* argv[]) {
       fprintf(stderr, "ignoring parse error: %s\n", jv_string_value(msg));
       jv_free(msg);
     }
-    if (options & SLURP) {
-      ret = process(jq, input_state->slurped, jq_flags, dumpopts);
-      input_state->slurped = jv_invalid();
-    }
   }
 
-  if (ret == 0 && input_state->open_failures != 0)
+  if (jq_util_input_open_errors(input_state) != 0 && ret == 0 && (options & EXIT_STATUS))
     ret = 2;
 
   if (ret != 0)
@@ -651,15 +515,15 @@ int main(int argc, char* argv[]) {
       fprintf(stderr, "Error: %s opening /dev/null\n", strerror(errno));
       exit(3);
     }
-    assert(input_state->ninput_files > 0 && !strcmp(input_state->input_filenames[0], "-"));
-    if (rename(t, input_state->input_filenames[0]) == -1) {
+    assert(first_file != 0 && !strcmp(first_file, "-"));
+    if (rename(t, first_file) == -1) {
       fprintf(stderr, "Error: %s renaming temporary file\n", strerror(errno));
       exit(3);
     }
     jv_mem_free(t);
   }
 out:
-  input_state_free(&input_state);
+  jq_util_input_free(&input_state);
   jq_teardown(&jq);
   if (ret >= 10 && (options & EXIT_STATUS))
     return ret - 10;
diff --git a/util.c b/util.c
index 33ee07f6ab45ec5009447313691ca70ef7dc626d..d63fd8a537ec61b486501edd04b1cd19588d4175 100644 (file)
--- a/util.c
+++ b/util.c
@@ -6,6 +6,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <assert.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <string.h>
@@ -17,7 +18,8 @@
 
 
 #include "util.h"
-#include "jv.h"
+#include "jq.h"
+#include "jv_alloc.h"
 
 #ifndef HAVE_MKSTEMP
 int mkstemp(char *template) {
@@ -143,3 +145,167 @@ not_this:
 #endif /* !HAVE_MEMMEM */
 }
 
+struct jq_util_input_state {
+  jq_err_cb err_cb;
+  void *err_cb_data;
+  jv_parser *parser;
+  FILE* current_input;
+  jv files;
+  int open_failures;
+  jv slurped;
+  char buf[4096];
+};
+
+static void fprinter(void *data, jv fname) {
+  fprintf((FILE *)data, "jq: error: Could not open file %s: %s\n", jv_string_value(fname), strerror(errno));
+  jv_free(fname);
+}
+
+// If parser == NULL -> RAW
+jq_util_input_state jq_util_input_init(jq_err_cb err_cb, void *err_cb_data) {
+  if (err_cb == NULL) {
+    err_cb = fprinter;
+    err_cb_data = stderr;
+  }
+  jq_util_input_state new_state = jv_mem_alloc(sizeof(*new_state));
+  memset(new_state, 0, sizeof(*new_state));
+  new_state->err_cb = err_cb;
+  new_state->err_cb_data = err_cb_data;
+  new_state->parser = NULL;
+  new_state->current_input = NULL;
+  new_state->files = jv_array();
+  new_state->slurped = jv_invalid();
+  new_state->buf[0] = 0;
+
+  return new_state;
+}
+
+void jq_util_input_set_parser(jq_util_input_state state, jv_parser *parser, int slurp) {
+  assert(!jv_is_valid(state->slurped));
+  state->parser = parser;
+
+  if (parser == NULL && slurp)
+    state->slurped = jv_string("");
+  else if (slurp)
+    state->slurped = jv_array();
+  else
+    state->slurped = jv_invalid();
+}
+
+void jq_util_input_free(jq_util_input_state *state) {
+  jq_util_input_state old_state = *state;
+  *state = NULL;
+  if (old_state == NULL)
+    return;
+
+  if (old_state->parser != NULL)
+    jv_parser_free(old_state->parser);
+  jv_free(old_state->files);
+  jv_free(old_state->slurped);
+  jv_mem_free(old_state);
+}
+
+void jq_util_input_add_input(jq_util_input_state state, jv fname) {
+  state->files = jv_array_append(state->files, fname);
+}
+
+int jq_util_input_open_errors(jq_util_input_state state) {
+  return state->open_failures;
+}
+
+static jv next_file(jq_util_input_state state) {
+  jv next = jv_array_get(jv_copy(state->files), 0);
+  if (jv_array_length(jv_copy(state->files)) > 0)
+    state->files = jv_array_slice(state->files, 1, jv_array_length(jv_copy(state->files)));
+  return next;
+}
+
+int jq_util_input_read_more(jq_util_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(state->current_input);
+      }
+      state->current_input = NULL;
+    }
+    jv f = next_file(state);
+    if (jv_is_valid(f)) {
+      if (!strcmp(jv_string_value(f), "-")) {
+        state->current_input = stdin;
+      } else {
+        state->current_input = fopen(jv_string_value(f), "r");
+        if (!state->current_input) {
+          state->err_cb(state->err_cb_data, jv_copy(f));
+          state->open_failures++;
+        }
+      }
+      jv_free(f);
+    }
+  }
+
+  state->buf[0] = 0;
+  if (state->current_input) {
+    if (!fgets(state->buf, sizeof(state->buf), state->current_input))
+      state->buf[0] = 0;
+  }
+  return jv_array_length(jv_copy(state->files)) == 0 && (!state->current_input || feof(state->current_input));
+}
+
+jv jq_util_input_next_input_cb(jq_state *jq, void *data) {
+  return jq_util_input_next_input((jq_util_input_state)data);
+}
+
+// Blocks to read one more input from stdin and/or given files
+// When slurping, it returns just one value
+jv jq_util_input_next_input(jq_util_input_state state) {
+  int is_last = 0;
+  jv value = jv_invalid(); // need more input
+  do {
+    if (state->parser == NULL) {
+      // Raw input
+      is_last = jq_util_input_read_more(state);
+      if (state->buf[0] == '\0')
+        continue;
+      int len = strlen(state->buf); // Raw input doesn't support NULs
+      if (len > 0) {
+        if (jv_is_valid(state->slurped)) {
+          // Slurped raw input
+          state->slurped = jv_string_concat(state->slurped, jv_string(state->buf));
+        } else {
+          if (!jv_is_valid(value))
+            value = jv_string("");
+          if (state->buf[len-1] == '\n') {
+            // whole line
+            state->buf[len-1] = 0;
+            return jv_string_concat(value, jv_string(state->buf));
+          }
+          value = jv_string_concat(value, jv_string(state->buf));
+          state->buf[0] = '\0';
+        }
+      }
+    } else {
+      if (jv_parser_remaining(state->parser) == 0) {
+        is_last = jq_util_input_read_more(state);
+        jv_parser_set_buf(state->parser, state->buf, strlen(state->buf), !is_last);
+      }
+      value = jv_parser_next(state->parser);
+      if (jv_is_valid(state->slurped)) {
+        if (jv_is_valid(value)) {
+          state->slurped = jv_array_append(state->slurped, value);
+          value = jv_invalid();
+        } else if (jv_invalid_has_msg(jv_copy(value)))
+          return value; // Not slurped parsed input
+      } else if (jv_is_valid(value) || jv_invalid_has_msg(jv_copy(value))) {
+        return value;
+      }
+    }
+  } while (!is_last);
+
+  if (jv_is_valid(state->slurped)) {
+    value = state->slurped;
+    state->slurped = jv_invalid();
+  }
+  return value;
+}