static int parse_slice(jv array, jv slice, int* pstart, int* pend) { // Array slices int len = jv_array_length(jv_copy(array)); jv start_jv = jv_object_get(jv_copy(slice), jv_string("start")); jv end_jv = jv_object_get(slice, jv_string("end")); if (jv_get_kind(start_jv) == JV_KIND_NULL) { jv_free(start_jv); start_jv = jv_number(0); } if (jv_get_kind(end_jv) == JV_KIND_NULL) { jv_free(end_jv); end_jv = jv_number(len); } if (jv_get_kind(start_jv) != JV_KIND_NUMBER || jv_get_kind(end_jv) != JV_KIND_NUMBER) { jv_free(start_jv); jv_free(end_jv); return 0; } else { int start = (int)jv_number_value(start_jv); int end = (int)jv_number_value(end_jv); if (start < 0) start = len + start; if (end < 0) end = len + end; if (start < 0) start = 0; if (start > len) start = len; if (end > len) end = len; if (end < start) end = start; assert(0 <= start && start <= end && end <= len); *pstart = start; *pend = end; return 1; } }
jv jv_array_concat(jv a, jv b) { assert(jv_get_kind(a) == JV_KIND_ARRAY); assert(jv_get_kind(b) == JV_KIND_ARRAY); // FIXME: could be faster jv_array_foreach(b, i, elem) { a = jv_array_append(a, elem); }
static jv type_error2(jv bad1, jv bad2, const char* msg) { jv err = jv_invalid_with_msg(jv_string_fmt("%s and %s %s", jv_kind_name(jv_get_kind(bad1)), jv_kind_name(jv_get_kind(bad2)), msg)); jv_free(bad1); jv_free(bad2); return err; }
jv jv_get(jv t, jv k) { jv v; if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) { v = jv_object_get(t, k); if (!jv_is_valid(v)) { jv_free(v); v = jv_null(); } } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) { // FIXME: don't do lookup for noninteger index v = jv_array_get(t, (int)jv_number_value(k)); if (!jv_is_valid(v)) { jv_free(v); v = jv_null(); } } else if (jv_get_kind(t) == JV_KIND_NULL && (jv_get_kind(k) == JV_KIND_STRING || jv_get_kind(k) == JV_KIND_NUMBER)) { jv_free(t); jv_free(k); v = jv_null(); } else { v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with %s", jv_kind_name(jv_get_kind(t)), jv_kind_name(jv_get_kind(k)))); jv_free(t); jv_free(k); } return v; }
static jv type_error2(jv bad1, jv bad2, const char* msg) { char errbuf1[15],errbuf2[15]; jv err = jv_invalid_with_msg(jv_string_fmt("%s (%s) and %s (%s) %s", jv_kind_name(jv_get_kind(bad1)), jv_dump_string_trunc(jv_copy(bad1), errbuf1, sizeof(errbuf1)), jv_kind_name(jv_get_kind(bad2)), jv_dump_string_trunc(jv_copy(bad2), errbuf2, sizeof(errbuf2)), msg)); jv_free(bad1); jv_free(bad2); return err; }
static void f_length(jv input[], jv output[]) { if (jv_get_kind(input[0]) == JV_KIND_ARRAY) { output[0] = jv_number(jv_array_length(input[0])); } else if (jv_get_kind(input[0]) == JV_KIND_OBJECT) { output[0] = jv_number(jv_object_length(input[0])); } else if (jv_get_kind(input[0]) == JV_KIND_STRING) { output[0] = jv_number(jv_string_length(input[0])); } else { output[0] = jv_invalid_with_msg(jv_string_fmt("Cannot get the length of %s", jv_kind_name(jv_get_kind(input[0])))); jv_free(input[0]); } }
static void f_divide(jv input[], jv output[]) { jv_free(input[0]); jv a = input[2]; jv b = input[1]; if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) { output[0] = jv_number(jv_number_value(a) / jv_number_value(b)); } else { output[0] = jv_invalid_with_msg(jv_string_fmt("Attempted to divide %s by %s", jv_kind_name(jv_get_kind(a)), jv_kind_name(jv_get_kind(b)))); jv_free(a); jv_free(b); } }
static void f_contains(jv input[], jv output[]) { jv_free(input[0]); jv a = input[2]; jv b = input[1]; jv_kind akind = jv_get_kind(a); if (akind == jv_get_kind(b)) { output[0] = jv_bool(jv_contains(a, b)); } else { output[0] = jv_invalid_with_msg(jv_string_fmt("Can only check containment of values of the same type.")); jv_free(a); jv_free(b); } }
static jv f_startswith(jq_state *jq, jv a, jv b) { if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) return jv_invalid_with_msg(jv_string("startswith() requires string inputs")); int alen = jv_string_length_bytes(jv_copy(a)); int blen = jv_string_length_bytes(jv_copy(b)); jv ret; if (blen <= alen && memcmp(jv_string_value(a), jv_string_value(b), blen) == 0) ret = jv_true(); else ret = jv_false(); jv_free(a); jv_free(b); return ret; }
static int process(jq_state *jq, jv value, int flags) { int ret = 14; // No valid results && -e -> exit(4) jq_start(jq, value, flags); jv result; while (jv_is_valid(result = jq_next(jq))) { if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) { fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout); ret = 0; jv_free(result); } else { int dumpopts; /* Disable colour by default on Windows builds as Windows terminals tend not to display it correctly */ #ifdef WIN32 dumpopts = 0; #else dumpopts = isatty(fileno(stdout)) ? JV_PRINT_COLOUR : 0; #endif if (options & SORTED_OUTPUT) dumpopts |= JV_PRINT_SORTED; if (!(options & COMPACT_OUTPUT)) dumpopts |= JV_PRINT_PRETTY; if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII; if (options & COLOUR_OUTPUT) dumpopts |= JV_PRINT_COLOUR; if (options & NO_COLOUR_OUTPUT) dumpopts &= ~JV_PRINT_COLOUR; if (jv_get_kind(result) == JV_KIND_FALSE || jv_get_kind(result) == JV_KIND_NULL) ret = 11; else ret = 0; jv_dump(result, dumpopts); } if (!(options & RAW_NO_LF)) printf("\n"); if (options & UNBUFFERED_OUTPUT) fflush(stdout); } if (jv_invalid_has_msg(jv_copy(result))) { // Uncaught jq exception jv msg = jv_invalid_get_msg(jv_copy(result)); if (jv_get_kind(msg) == JV_KIND_STRING) { fprintf(stderr, "jq: error: %s\n", jv_string_value(msg)); } else { msg = jv_dump_string(msg, 0); fprintf(stderr, "jq: error (not a string): %s\n", jv_string_value(msg)); } jv_free(msg); } jv_free(result); return ret; }
static void jvp_invalid_free(jv x) { assert(jv_get_kind(x) == JV_KIND_INVALID); if (jvp_refcnt_dec(x.u.ptr)) { jv_free(((jvp_invalid*)x.u.ptr)->errmsg); jv_mem_free(x.u.ptr); } }
static void process(jv value, int flags) { jq_state *jq = NULL; jq_init(bc, value, &jq, flags); jv result; while (jv_is_valid(result = jq_next(jq))) { if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) { fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout); jv_free(result); } else { int dumpopts; /* Disable colour by default on Windows builds as Windows terminals tend not to display it correctly */ #ifdef WIN32 dumpopts = 0; #else dumpopts = isatty(fileno(stdout)) ? JV_PRINT_COLOUR : 0; #endif if (!(options & COMPACT_OUTPUT)) dumpopts |= JV_PRINT_PRETTY; if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII; if (options & COLOUR_OUTPUT) dumpopts |= JV_PRINT_COLOUR; if (options & NO_COLOUR_OUTPUT) dumpopts &= ~JV_PRINT_COLOUR; jv_dump(result, dumpopts); } printf("\n"); } jv_free(result); jq_teardown(&jq); }
uint16_t* stack_restore(jq_state *jq){ while (!forkable_stack_empty(&jq->data_stk) && forkable_stack_pop_will_free(&jq->data_stk)) { jv_free(stack_pop(jq)); } while (!forkable_stack_empty(&jq->frame_stk) && forkable_stack_pop_will_free(&jq->frame_stk)) { frame_pop(&jq->frame_stk); } if (forkable_stack_empty(&jq->fork_stk)) { return 0; } struct forkpoint* fork = forkable_stack_peek(&jq->fork_stk); uint16_t* retaddr = fork->return_address; forkable_stack_restore(&jq->data_stk, &fork->saved_data_stack); forkable_stack_restore(&jq->frame_stk, &fork->saved_call_stack); int path_len = fork->path_len; if (jv_get_kind(jq->path) == JV_KIND_ARRAY) { assert(path_len >= 0); jq->path = jv_array_slice(jq->path, 0, path_len); } else { assert(path_len == 0); } jq->subexp_nest = fork->subexp_nest; forkable_stack_pop(&jq->fork_stk); return retaddr; }
struct bytecode* jq_compile_args(const char* str, jv args) { assert(jv_get_kind(args) == JV_KIND_ARRAY); struct locfile locations; locfile_init(&locations, str, strlen(str)); block program; struct bytecode* bc = 0; int nerrors = jq_parse(&locations, &program); if (nerrors == 0) { for (int i=0; i<jv_array_length(jv_copy(args)); i++) { jv arg = jv_array_get(jv_copy(args), i); jv name = jv_object_get(jv_copy(arg), jv_string("name")); jv value = jv_object_get(arg, jv_string("value")); program = gen_var_binding(gen_const(value), jv_string_value(name), program); jv_free(name); } jv_free(args); program = builtins_bind(program); nerrors = block_compile(program, &locations, &bc); } if (nerrors) { fprintf(stderr, "%d compile %s\n", nerrors, nerrors > 1 ? "errors" : "error"); } locfile_free(&locations); return bc; }
uint16_t* stack_restore(jq_state *jq){ while (!stack_pop_will_free(&jq->stk, jq->fork_top)) { if (stack_pop_will_free(&jq->stk, jq->stk_top)) { jv_free(stack_pop(jq)); } else if (stack_pop_will_free(&jq->stk, jq->curr_frame)) { frame_pop(jq); } else { assert(0); } } if (jq->fork_top == 0) { return 0; } struct forkpoint* fork = stack_block(&jq->stk, jq->fork_top); uint16_t* retaddr = fork->return_address; jq->stk_top = fork->saved_data_stack; jq->curr_frame = fork->saved_curr_frame; int path_len = fork->path_len; if (jv_get_kind(jq->path) == JV_KIND_ARRAY) { assert(path_len >= 0); jq->path = jv_array_slice(jq->path, 0, path_len); } else { assert(path_len == 0); } jq->subexp_nest = fork->subexp_nest; jq->fork_top = stack_pop_block(&jq->stk, jq->fork_top, sizeof(struct forkpoint)); return retaddr; }
static void f_tostring(jv input[], jv output[]) { if (jv_get_kind(input[0]) == JV_KIND_STRING) { output[0] = input[0]; } else { output[0] = jv_dump_string(input[0], 0); } }
static jv f_negate(jv input) { if (jv_get_kind(input) != JV_KIND_NUMBER) { return type_error(input, "cannot be negated"); } jv ret = jv_number(-jv_number_value(input)); jv_free(input); return ret; }
jv jv_keys(jv x) { if (jv_get_kind(x) == JV_KIND_OBJECT) { int nkeys = jv_object_length(jv_copy(x)); jv* keys = malloc(sizeof(jv) * nkeys); int kidx = 0; jv_object_foreach(i, x) { keys[kidx++] = jv_object_iter_key(x, i); }
static jv f_sqrt(jv input) { if (jv_get_kind(input) != JV_KIND_NUMBER) { return type_error(input, "has no square root"); } jv ret = jv_number(sqrt(jv_number_value(input))); jv_free(input); return ret; }
jv jv_array_set(jv j, int idx, jv val) { assert(jv_get_kind(j) == JV_KIND_ARRAY); // copy/free of val,j coalesced jv* slot = jvp_array_write(&j, idx); jv_free(*slot); *slot = val; return j; }
static void f_keys(jv input[], jv output[]) { if (jv_get_kind(input[0]) == JV_KIND_OBJECT) { int nkeys = jv_object_length(jv_copy(input[0])); jv* keys = malloc(sizeof(jv) * nkeys); int kidx = 0; jv_object_foreach(i, input[0]) { keys[kidx++] = jv_object_iter_key(input[0], i); }
void print_error(jv value) { assert(!jv_is_valid(value)); jv msg = jv_invalid_get_msg(value); if (jv_get_kind(msg) == JV_KIND_STRING) { fprintf(stderr, "jq: error: %s\n", jv_string_value(msg)); } jv_free(msg); }
jv jv_has(jv t, jv k) { assert(jv_is_valid(t)); assert(jv_is_valid(k)); jv ret; if (jv_get_kind(t) == JV_KIND_NULL) { jv_free(t); jv_free(k); ret = jv_false(); } else if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) { jv elem = jv_object_get(t, k); ret = jv_bool(jv_is_valid(elem)); jv_free(elem); } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) { jv elem = jv_array_get(t, (int)jv_number_value(k)); ret = jv_bool(jv_is_valid(elem)); jv_free(elem); } else { ret = jv_invalid_with_msg(jv_string_fmt("Cannot check whether %s has a %s key", jv_kind_name(jv_get_kind(t)), jv_kind_name(jv_get_kind(k)))); jv_free(t); jv_free(k); } return ret; }
jv jv_set(jv t, jv k, jv v) { if (!jv_is_valid(v)) { jv_free(t); jv_free(k); return v; } int isnull = jv_get_kind(t) == JV_KIND_NULL; if (jv_get_kind(k) == JV_KIND_STRING && (jv_get_kind(t) == JV_KIND_OBJECT || isnull)) { if (isnull) t = jv_object(); t = jv_object_set(t, k, v); } else if (jv_get_kind(k) == JV_KIND_NUMBER && (jv_get_kind(t) == JV_KIND_ARRAY || isnull)) { if (isnull) t = jv_array(); t = jv_array_set(t, (int)jv_number_value(k), v); } else { jv err = jv_invalid_with_msg(jv_string_fmt("Cannot update field at %s index of %s", jv_kind_name(jv_get_kind(t)), jv_kind_name(jv_get_kind(v)))); jv_free(t); jv_free(k); jv_free(v); t = err; } return t; }
void stack_save(jq_state *jq, uint16_t* retaddr){ struct forkpoint* fork = forkable_stack_push(&jq->fork_stk, sizeof(struct forkpoint)); forkable_stack_save(&jq->data_stk, &fork->saved_data_stack); forkable_stack_save(&jq->frame_stk, &fork->saved_call_stack); fork->path_len = jv_get_kind(jq->path) == JV_KIND_ARRAY ? jv_array_length(jv_copy(jq->path)) : 0; fork->subexp_nest = jq->subexp_nest; fork->return_address = retaddr; }
static jv f_minus(jv input, jv a, jv b) { jv_free(input); if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) { return jv_number(jv_number_value(a) - jv_number_value(b)); } else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) { jv out = jv_array(); jv_array_foreach(a, i, x) { int include = 1; jv_array_foreach(b, j, y) { if (jv_equal(jv_copy(x), y)) { include = 0; break; } } if (include) out = jv_array_append(out, jv_copy(x)); jv_free(x); }
static void f_add(jv input[], jv output[]) { jv array = input[0]; if (jv_get_kind(array) != JV_KIND_ARRAY) { output[0] = jv_invalid_with_msg(jv_string_fmt("Cannot add elements of an %s", jv_kind_name(jv_get_kind(array)))); } else if (jv_array_length(jv_copy(array)) == 0) { output[0] = jv_null(); } else { jv sum = jv_array_get(jv_copy(array), 0); for (int i = 1; i < jv_array_length(jv_copy(array)); i++) { jv x = jv_array_get(jv_copy(array), i); jv add_args[] = {jv_null(), x, sum}; f_plus(add_args, &sum); } output[0] = sum; } jv_free(array); }
static jv f_endswith(jq_state *jq, jv a, jv b) { if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) return jv_invalid_with_msg(jv_string("endswith() requires string inputs")); const char *astr = jv_string_value(a); const char *bstr = jv_string_value(b); size_t alen = jv_string_length_bytes(jv_copy(a)); size_t blen = jv_string_length_bytes(jv_copy(b)); jv ret;; if (alen < blen || memcmp(astr + (alen - blen), bstr, blen) != 0) ret = jv_false(); else ret = jv_true(); jv_free(a); jv_free(b); return ret; }
static jv type_error(jv bad, const char* msg) { char errbuf[15]; jv err = jv_invalid_with_msg(jv_string_fmt("%s (%s) %s", jv_kind_name(jv_get_kind(bad)), jv_dump_string_trunc(jv_copy(bad), errbuf, sizeof(errbuf)), msg)); jv_free(bad); return err; }
static void print_error(jq_state *jq, jv value) { assert(!jv_is_valid(value)); jv msg = jv_invalid_get_msg(value); jv msg2; if (jv_get_kind(msg) == JV_KIND_STRING) msg2 = jv_string_fmt("jq: error: %s", jv_string_value(msg)); else msg2 = jv_string_fmt("jq: error: <unknown>"); jv_free(msg); if (jq->err_cb) jq->err_cb(jq->err_cb_data, msg2); else if (jv_get_kind(msg2) == JV_KIND_STRING) fprintf(stderr, "%s\n", jv_string_value(msg2)); else fprintf(stderr, "jq: error: %s\n", strerror(ENOMEM)); jv_free(msg2); }