diff options
author | Nicolas Williams <nico@cryptonector.com> | 2014-12-27 17:58:47 -0600 |
---|---|---|
committer | Nicolas Williams <nico@cryptonector.com> | 2015-02-13 15:55:31 -0600 |
commit | 91fb3df4954ead7914255a093d55192d0420b962 (patch) | |
tree | dd685ba5043614c2c94063f194d3f01ac0638a86 /main.c | |
parent | 373ef71febac87ae48368e3b8c0f3bc4012d9b13 (diff) |
Refactor handling of inputs in main() (fix #667)
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.
Diffstat (limited to 'main.c')
-rw-r--r-- | main.c | 158 |
1 files changed, 101 insertions, 57 deletions
@@ -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; |