jsmntok_t *json_parse_input(const char *input, int len, bool *valid) { jsmn_parser parser; jsmntok_t *toks; jsmnerr_t ret; toks = tal_arr(input, jsmntok_t, 10); again: jsmn_init(&parser); ret = jsmn_parse(&parser, input, len, toks, tal_count(toks) - 1); switch (ret) { case JSMN_ERROR_INVAL: *valid = false; return tal_free(toks); case JSMN_ERROR_PART: *valid = true; return tal_free(toks); case JSMN_ERROR_NOMEM: tal_resize(&toks, tal_count(toks) * 2); goto again; } /* Cut to length and return. */ *valid = true; tal_resize(&toks, ret + 1); /* Make sure last one is always referencable. */ toks[ret].type = -1; toks[ret].start = toks[ret].end = toks[ret].size = 0; return toks; }
static void result_append(struct json_result *res, const char *str) { size_t len = tal_count(res->s) - 1; tal_resize(&res->s, len + strlen(str) + 1); strcpy(res->s + len, str); }
static void add_token(struct token **toks, const char *p, size_t len) { size_t n = tal_count(*toks); tal_resize(toks, n+1); (*toks)[n].p = p; (*toks)[n].len = len; }
static struct io_plan *read_done(struct io_conn *conn, struct data *d) { tal_resize(&d->pattern, tal_count(d->pattern) + 1 + strlen(d->buf)); strcat(d->pattern, "<"); strcat(d->pattern, d->buf); return read_more(conn, d); }
static char *read_from(const tal_t *ctx, int fd) { size_t max = 128, done = 0; int r; char *p = tal_arr(ctx, char, max); while ((r = read(fd, p + done, max - done)) > 0) { done += r; if (done == max) tal_resize(&p, max *= 2); } tal_resize(&p, done + 1); p[done] = '\0'; return p; }
/* Make copy of src, replacing "from" with "to". */ static char *replace(const void *ctx, const char *src, const char *from, const char *to) { char *ret = tal_strdup(ctx, ""); unsigned int rlen, len, add; rlen = len = 0; for (;;) { const char *next = strstr(src+len, from); if (!next) add = strlen(src+len) + 1; else add = next - (src+len); tal_resize(&ret, rlen + add + strlen(to)+1); memcpy(ret+rlen, src+len, add); if (!next) return ret; len += add; rlen += add; strcpy(ret+rlen, to); rlen += strlen(to); len += strlen(from); } }
static void add_linearize(const void *data, size_t len, void *pptr_) { u8 **pptr = pptr_; size_t oldsize = tal_count(*pptr); tal_resize(pptr, oldsize + len); memcpy(*pptr + oldsize, memcheck(data, len), len); }
jsmntok_t *json_parse_input(const tal_t *ctx, const char *input, int len, bool *valid) { jsmn_parser parser; jsmntok_t *toks; int ret; toks = tal_arr(ctx, jsmntok_t, 10); toks[0].type = JSMN_UNDEFINED; jsmn_init(&parser); again: ret = jsmn_parse(&parser, input, len, toks, tal_count(toks) - 1); switch (ret) { case JSMN_ERROR_INVAL: *valid = false; return tal_free(toks); case JSMN_ERROR_NOMEM: tal_resize(&toks, tal_count(toks) * 2); goto again; } /* Check whether we read at least one full root element, i.e., root * element has its end set. */ if (toks[0].type == JSMN_UNDEFINED || toks[0].end == -1) { *valid = true; return tal_free(toks); } /* If we read a partial element at the end of the stream we'll get a * ret=JSMN_ERROR_PART, but due to the previous check we know we read at * least one full element, so count tokens that are part of this root * element. */ ret = json_next(toks) - toks; /* Cut to length and return. */ *valid = true; tal_resize(&toks, ret + 1); /* Make sure last one is always referenceable. */ toks[ret].type = -1; toks[ret].start = toks[ret].end = toks[ret].size = 0; return toks; }
/* enum ... */ static bool tok_take_enum(struct parse_state *ps) { size_t n = 0; struct cdump_type *e; const char *name; name = tok_take_ident(ps->defs, &ps->toks); if (!name) { complain(ps, "Expected enum name"); return false; } e = get_type(ps->defs, CDUMP_ENUM, name); /* Duplicate name? */ if (type_defined(e)) { complain(ps, "enum already defined"); return false; } if (!tok_take_if(&ps->toks, "{")) { complain(ps, "Expected { after enum name"); return false; } e->u.enum_vals = tal_arr(e, struct cdump_enum_val, n); do { struct cdump_enum_val *v; /* GCC extension: comma and end of enum */ if (tok_is(&ps->toks, "}")) break; tal_resize(&e->u.enum_vals, n+1); v = &e->u.enum_vals[n++]; v->name = tok_take_ident(e, &ps->toks); if (!v->name) { complain(ps, "Expected enum value name"); return false; } if (tok_take_if(&ps->toks, "=")) { v->value = tok_take_until(e, &ps->toks, ",}"); if (!v->value) { complain(ps, "Expected , or } to end value"); return false; } } else v->value = NULL; } while (tok_take_if(&ps->toks, ",")); if (tok_take_if(&ps->toks, "}") && tok_take_if(&ps->toks, ";")) return true; complain(ps, "Expected }; at end of enum"); return false; }
static struct io_plan *read_json(struct io_conn *conn, struct json_connection *jcon) { jsmntok_t *toks; bool valid; log_io(jcon->log, true, jcon->buffer + jcon->used, jcon->len_read); /* Resize larger if we're full. */ jcon->used += jcon->len_read; if (jcon->used == tal_count(jcon->buffer)) tal_resize(&jcon->buffer, jcon->used * 2); again: toks = json_parse_input(jcon->buffer, jcon->used, &valid); if (!toks) { if (!valid) { log_unusual(jcon->dstate->base_log, "Invalid token in json input: '%.*s'", (int)jcon->used, jcon->buffer); return io_close(conn); } /* We need more. */ goto read_more; } /* Empty buffer? (eg. just whitespace). */ if (tal_count(toks) == 1) { jcon->used = 0; goto read_more; } parse_request(jcon, toks); /* Remove first {}. */ memmove(jcon->buffer, jcon->buffer + toks[0].end, tal_count(jcon->buffer) - toks[0].end); jcon->used -= toks[0].end; tal_free(toks); /* Need to wait for command to finish? */ if (jcon->current) { jcon->len_read = 0; return io_wait(conn, jcon, read_json, jcon); } /* See if we can parse the rest. */ goto again; read_more: tal_free(toks); return io_read_partial(conn, jcon->buffer + jcon->used, tal_count(jcon->buffer) - jcon->used, &jcon->len_read, read_json, jcon); }
static void queue_raw_pkt(struct peer *peer, Pkt *pkt) { size_t n = tal_count(peer->outpkt); tal_resize(&peer->outpkt, n+1); peer->outpkt[n] = pkt; log_debug(peer->log, "Queued pkt %s", pkt_name(pkt->pkt_case)); /* In case it was waiting for output. */ io_wake(peer); }
static void remove_htlc(struct channel_oneside *oneside, size_t n) { size_t num = tal_count(oneside->htlcs); assert(n < num); /* Remove. */ if (num > 0) oneside->htlcs[n] = oneside->htlcs[num-1]; tal_resize(&oneside->htlcs, num - 1); }
static void add_htlc(struct channel_oneside *oneside, UpdateAddHtlc *ah, const char *file) { size_t num = tal_count(oneside->htlcs); if (find_htlc(oneside, ah->r_hash) != num) errx(1, "Duplicate R hash in %s", file); tal_resize(&oneside->htlcs, num+1); oneside->htlcs[num] = ah; }
static void queue_raw_pkt(struct peer *peer, Pkt *pkt, void (*ack_cb)(struct peer *peer, void *arg), void *ack_arg) { size_t n = tal_count(peer->outpkt); tal_resize(&peer->outpkt, n+1); peer->outpkt[n].pkt = pkt; peer->outpkt[n].ack_cb = ack_cb; peer->outpkt[n].ack_arg = ack_arg; /* In case it was waiting for output. */ io_wake(peer); }
result_append_fmt(struct json_result *res, const char *fmt, ...) { size_t len = tal_count(res->s) - 1, fmtlen; va_list ap; va_start(ap, fmt); fmtlen = vsnprintf(NULL, 0, fmt, ap); va_end(ap); tal_resize(&res->s, len + fmtlen + 1); va_start(ap, fmt); vsprintf(res->s + len, fmt, ap); va_end(ap); }
void json_tok_remove(jsmntok_t **tokens, jsmntok_t *tok, size_t num) { assert(*tokens); assert((*tokens)->type == JSMN_ARRAY || (*tokens)->type == JSMN_OBJECT); const jsmntok_t *src = tok; const jsmntok_t *end = json_next(*tokens); jsmntok_t *dest = tok; int remove_count; for (int i = 0; i < num; i++) src = json_next(src); remove_count = src - tok; memmove(dest, src, sizeof(jsmntok_t) * (end - src)); tal_resize(tokens, tal_count(*tokens) - remove_count); (*tokens)->size -= num; }
int main(void) { char *parent, *c[4]; int i; plan_tests(11); parent = tal(NULL, char); ok1(parent); /* Zeroing allocations. */ for (i = 0; i < 4; i++) { c[i] = talz(parent, char); ok1(*c[i] == '\0'); tal_free(c[i]); } /* Array allocation. */ for (i = 0; i < 4; i++) { c[i] = tal_arr(parent, char, 4); strcpy(c[i], "abc"); tal_free(c[i]); } /* Zeroing array allocation. */ for (i = 0; i < 4; i++) { c[i] = tal_arrz(parent, char, 4); ok1(!c[i][0] && !c[i][1] && !c[i][2] && !c[i][3]); strcpy(c[i], "abc"); tal_free(c[i]); } /* Resizing. */ c[0] = tal_arrz(parent, char, 4); ok1(tal_resize(&c[0], 6)); strcpy(c[0], "hello"); tal_free(c[0]); ok1(talloc_total_blocks(parent) == 1); tal_free(parent); return exit_status(); }
static void add_files(struct manifest *m, const char *base, const char *subdir) { DIR *d; struct dirent *ent; char **subs = tal_arr(m, char *, 0); const char *thisdir; if (!subdir) thisdir = base; else thisdir = path_join(subs, base, subdir); d = opendir(thisdir); if (!d) err(1, "Opening directory %s", thisdir); while ((ent = readdir(d)) != NULL) { struct stat st; struct ccan_file *f; struct list_head *dest; bool is_c_src; if (ent->d_name[0] == '.') continue; f = new_ccan_file(m, m->dir, subdir ? path_join(m, subdir, ent->d_name) : ent->d_name); if (lstat(f->fullname, &st) != 0) err(1, "lstat %s", f->fullname); if (S_ISDIR(st.st_mode)) { size_t len = tal_count(subs); tal_resize(&subs, len+1); subs[len] = tal_strcat(subs, f->name, "/"); continue; } if (!S_ISREG(st.st_mode)) { tal_free(f); continue; } if (streq(f->name, "_info")) { m->info_file = f; continue; } is_c_src = strends(f->name, ".c"); if (!is_c_src && !strends(f->name, ".h")) { dest = &m->other_files; } else if (!strchr(f->name, '/')) { if (is_c_src) dest = &m->c_files; else dest = &m->h_files; } else if (strstarts(f->name, "test/")) { if (is_c_src) { if (strstarts(f->name, "test/api")) dest = &m->api_tests; else if (strstarts(f->name, "test/run")) dest = &m->run_tests; else if (strstarts(f->name, "test/compile_ok")) dest = &m->compile_ok_tests; else if (strstarts(f->name, "test/compile_fail")) dest = &m->compile_fail_tests; else dest = &m->other_test_c_files; } else dest = &m->other_test_files; } else dest = &m->other_files; list_add(dest, &f->list); } closedir(d); /* Before we recurse, sanity check this is a ccan module. */ if (!subdir) { size_t i; if (!m->info_file && list_empty(&m->c_files) && list_empty(&m->h_files)) errx(1, "No _info, C or H files found here!"); for (i = 0; i < tal_count(subs); i++) add_files(m, base, subs[i]); } tal_free(subs); }
/* struct|union ... */ static bool tok_take_conglom(struct parse_state *ps, enum cdump_type_kind conglom_kind) { struct cdump_type *e; const char *name; size_t n; assert(conglom_kind == CDUMP_STRUCT || conglom_kind == CDUMP_UNION); name = tok_take_ident(ps->defs, &ps->toks); if (!name) { complain(ps, "Invalid struct/union name"); return false; } e = get_type(ps->defs, conglom_kind, name); if (type_defined(e)) { complain(ps, "Type already defined"); return false; } if (!tok_take_if(&ps->toks, "{")) { complain(ps, "Expected { for struct/union"); return false; } e->u.members = tal_arr(e, struct cdump_member, n = 0); while (!tok_is(&ps->toks, "}")) { struct cdump_type *basetype; const struct token *quals; unsigned int num_quals = 0; /* Anything can have these prepended. */ quals = ps->toks; while (tok_take_if(&ps->toks, "const") || tok_take_if(&ps->toks, "volatile")) num_quals++; /* eg. "struct foo" or "varint_t" */ if (!tok_take_type(ps, &basetype)) { complain(ps, "Expected typename inside struct/union"); return false; } do { struct cdump_member *m; tal_resize(&e->u.members, n+1); m = &e->u.members[n++]; m->type = basetype; if (num_quals) { m->qualifiers = string_of_toks(e, quals, quals + num_quals); } else m->qualifiers = NULL; /* May have multiple asterisks. */ while (tok_take_if(&ps->toks, "*")) m->type = ptr_of(ps, m->type); m->name = tok_take_ident(e, &ps->toks); if (!m->name) { complain(ps, "Expected name for member"); return false; } /* May be an array. */ while (tok_take_if(&ps->toks, "[")) { if (!tok_take_array(ps, &m->type)) return false; } } while (tok_take_if(&ps->toks, ",")); if (!tok_take_if(&ps->toks, ";")) { complain(ps, "Expected ; at end of member"); return false; } } if (tok_take_if(&ps->toks, "}") && tok_take_if(&ps->toks, ";")) return true; complain(ps, "Expected }; at end of struct/union"); return false; }
static struct io_plan *write_done(struct io_conn *conn, struct data *d) { tal_resize(&d->pattern, tal_count(d->pattern) + 1); strcat(d->pattern, ">"); return write_more(conn, d); }
/* Simple test code to create a gateway transaction */ int main(int argc, char *argv[]) { int fd, i, off; const char *method; char *cmd, *resp, *idstr, *rpc_filename; char *result_end; struct sockaddr_un addr; jsmntok_t *toks; const jsmntok_t *result, *error, *id; char *pettycoin_dir; const tal_t *ctx = tal(NULL, char); size_t num_opens, num_closes; bool valid; err_set_progname(argv[0]); opt_set_alloc(opt_allocfn, tal_reallocfn, tal_freefn); pettycoin_dir_register_opts(ctx, &pettycoin_dir, &rpc_filename); opt_register_noarg("--help|-h", opt_usage_and_exit, "<command> [<params>...]", "Show this message"); opt_register_noarg("--version|-V", opt_version_and_exit, VERSION, "Display version and exit"); opt_early_parse(argc, argv, opt_log_stderr_exit); opt_parse(&argc, argv, opt_log_stderr_exit); method = argv[1]; if (!method) errx(ERROR_USAGE, "Need at least one argument\n%s", opt_usage(argv[0], NULL)); if (chdir(pettycoin_dir) != 0) err(ERROR_TALKING_TO_PETTYCOIN, "Moving into '%s'", pettycoin_dir); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (strlen(rpc_filename) + 1 > sizeof(addr.sun_path)) errx(ERROR_USAGE, "rpc filename '%s' too long", rpc_filename); strcpy(addr.sun_path, rpc_filename); addr.sun_family = AF_UNIX; if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) err(ERROR_TALKING_TO_PETTYCOIN, "Connecting to '%s'", rpc_filename); idstr = tal_fmt(ctx, "pettycoin_query-%i", getpid()); cmd = tal_fmt(ctx, "{ \"method\" : \"%s\", \"id\" : \"%s\", \"params\" : [ ", method, idstr); for (i = 2; i < argc; i++) { /* Numbers are left unquoted, and quoted things left alone. */ if (strspn(argv[i], "0123456789") == strlen(argv[i]) || argv[i][0] == '"') tal_append_fmt(&cmd, "%s", argv[i]); else tal_append_fmt(&cmd, "\"%s\"", argv[i]); if (i != argc - 1) tal_append_fmt(&cmd, ", "); } tal_append_fmt(&cmd, "] }"); if (!write_all(fd, cmd, strlen(cmd))) err(ERROR_TALKING_TO_PETTYCOIN, "Writing command"); resp = tal_arr(cmd, char, 100); off = 0; num_opens = num_closes = 0; while ((i = read(fd, resp + off, tal_count(resp) - 1 - off)) > 0) { resp[off + i] = '\0'; num_opens += strcount(resp + off, "{"); num_closes += strcount(resp + off, "}"); off += i; if (off == tal_count(resp) - 1) tal_resize(&resp, tal_count(resp) * 2); /* parsing huge outputs is slow: do quick check first. */ if (num_opens == num_closes && strstr(resp, "\"result\"")) break; } if (i < 0) err(ERROR_TALKING_TO_PETTYCOIN, "reading response"); /* Parsing huge results is too slow, so hack fastpath common case */ result_end = tal_fmt(ctx, ", \"error\" : null, \"id\" : \"%s\" }\n", idstr); if (strstarts(resp, "{ \"result\" : ") && strends(resp, result_end)) { /* Result is OK, so dump it */ resp += strlen("{ \"result\" : "); printf("%.*s\n", (int)(strlen(resp) - strlen(result_end)), resp); tal_free(ctx); return 0; } toks = json_parse_input(resp, off, &valid); if (!toks || !valid) errx(ERROR_TALKING_TO_PETTYCOIN, "Malformed response '%s'", resp); result = json_get_member(resp, toks, "result"); if (!result) errx(ERROR_TALKING_TO_PETTYCOIN, "Missing 'result' in response '%s'", resp); error = json_get_member(resp, toks, "error"); if (!error) errx(ERROR_TALKING_TO_PETTYCOIN, "Missing 'error' in response '%s'", resp); id = json_get_member(resp, toks, "id"); if (!id) errx(ERROR_TALKING_TO_PETTYCOIN, "Missing 'id' in response '%s'", resp); if (!json_tok_streq(resp, id, idstr)) errx(ERROR_TALKING_TO_PETTYCOIN, "Incorrect 'id' in response: %.*s", json_tok_len(id), json_tok_contents(resp, id)); if (json_tok_is_null(resp, error)) { printf("%.*s\n", json_tok_len(result), json_tok_contents(resp, result)); tal_free(ctx); return 0; } printf("%.*s\n", json_tok_len(error), json_tok_contents(resp, error)); tal_free(ctx); return 1; }
static void add_mod(struct manifest ***deps, struct manifest *m) { unsigned int num = tal_count(*deps); tal_resize(deps, num + 1); (*deps)[num] = m; }
static void add(u8 **scriptp, const void *mem, size_t len) { size_t oldlen = tal_count(*scriptp); tal_resize(scriptp, oldlen + len); memcpy(*scriptp + oldlen, mem, len); }