From: Nicolas Williams Date: Sat, 27 Dec 2014 23:58:47 +0000 (-0600) Subject: Refactor handling of inputs in main() (fix #667) X-Git-Tag: jq-1.5rc2~150 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=91fb3df4954ead7914255a093d55192d0420b962;p=jq 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. --- diff --git a/main.c b/main.c index 7a91f03..6321cab 100644 --- 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; ininput_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;