mirror of
https://github.com/aljazceru/lightning.git
synced 2026-01-08 16:44:22 +01:00
ccan: import ccan/json_out and ccan/json_escape.
These are generalized from our internal implementations. The main difference is that 'struct json_escaped' is now 'struct json_escape', so we replace that immediately. The difference between lightningd's json-writing ringbuffer and the more generic ccan/json_out is that the latter has a better API and handles escaping transparently if something slips through (though it does offer direct accessors so you can mess things up yourself!). Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
10
Makefile
10
Makefile
@@ -31,7 +31,7 @@ SANITIZER_FLAGS=
|
||||
endif
|
||||
|
||||
ifeq ($(DEVELOPER),1)
|
||||
DEV_CFLAGS=-DCCAN_TAKE_DEBUG=1 -DCCAN_TAL_DEBUG=1
|
||||
DEV_CFLAGS=-DCCAN_TAKE_DEBUG=1 -DCCAN_TAL_DEBUG=1 -DCCAN_JSON_OUT_DEBUG=1
|
||||
else
|
||||
DEV_CFLAGS=
|
||||
endif
|
||||
@@ -81,6 +81,8 @@ CCAN_OBJS := \
|
||||
ccan-io-fdpass.o \
|
||||
ccan-isaac.o \
|
||||
ccan-isaac64.o \
|
||||
ccan-json_escape.o \
|
||||
ccan-json_out.o \
|
||||
ccan-list.o \
|
||||
ccan-mem.o \
|
||||
ccan-membuf.o \
|
||||
@@ -144,6 +146,8 @@ CCAN_HEADERS := \
|
||||
$(CCANDIR)/ccan/io/io_plan.h \
|
||||
$(CCANDIR)/ccan/isaac/isaac.h \
|
||||
$(CCANDIR)/ccan/isaac/isaac64.h \
|
||||
$(CCANDIR)/ccan/json_escape/json_escape.h \
|
||||
$(CCANDIR)/ccan/json_out/json_out.h \
|
||||
$(CCANDIR)/ccan/likely/likely.h \
|
||||
$(CCANDIR)/ccan/list/list.h \
|
||||
$(CCANDIR)/ccan/mem/mem.h \
|
||||
@@ -657,3 +661,7 @@ ccan-bitmap.o: $(CCANDIR)/ccan/bitmap/bitmap.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-membuf.o: $(CCANDIR)/ccan/membuf/membuf.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-json_escape.o: $(CCANDIR)/ccan/json_escape/json_escape.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-json_out.o: $(CCANDIR)/ccan/json_out/json_out.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
1
ccan/ccan/json_escape/LICENSE
Symbolic link
1
ccan/ccan/json_escape/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../licenses/BSD-MIT
|
||||
39
ccan/ccan/json_escape/_info
Normal file
39
ccan/ccan/json_escape/_info
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* json_escape - Escape sequences for JSON strings
|
||||
*
|
||||
* This code helps you format strings into forms useful for JSON.
|
||||
*
|
||||
* Author: Rusty Russell <rusty@rustcorp.com.au>
|
||||
* License: BSD-MIT
|
||||
* Example:
|
||||
* // Print arguments as a JSON array.
|
||||
* #include <ccan/json_escape/json_escape.h>
|
||||
*
|
||||
* int main(int argc, char *argv[])
|
||||
* {
|
||||
* printf("[");
|
||||
* for (int i = 1; i < argc; i++) {
|
||||
* struct json_escape *e = json_escape(NULL, argv[i]);
|
||||
* printf("%s\"%s\"", i == 1 ? "" : ",", e->s);
|
||||
* }
|
||||
* printf("]\n");
|
||||
* return 0;
|
||||
* }
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
/* Expect exactly one argument */
|
||||
if (argc != 2)
|
||||
return 1;
|
||||
|
||||
if (strcmp(argv[1], "depends") == 0) {
|
||||
printf("ccan/tal\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1,46 +1,58 @@
|
||||
#include <common/json_escaped.h>
|
||||
/* MIT (BSD) license - see LICENSE file for details */
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct json_escaped *json_escaped_string_(const tal_t *ctx,
|
||||
const void *bytes, size_t len)
|
||||
struct json_escape *json_escape_string_(const tal_t *ctx,
|
||||
const void *bytes, size_t len)
|
||||
{
|
||||
struct json_escaped *esc;
|
||||
struct json_escape *esc;
|
||||
|
||||
esc = (void *)tal_arr_label(ctx, char, len + 1,
|
||||
TAL_LABEL(struct json_escaped, ""));
|
||||
TAL_LABEL(struct json_escape, ""));
|
||||
memcpy(esc->s, bytes, len);
|
||||
esc->s[len] = '\0';
|
||||
return esc;
|
||||
}
|
||||
|
||||
struct json_escaped *json_to_escaped_string(const tal_t *ctx,
|
||||
const char *buffer,
|
||||
const jsmntok_t *tok)
|
||||
{
|
||||
if (tok->type != JSMN_STRING)
|
||||
return NULL;
|
||||
/* jsmn always gives us ~ well-formed strings. */
|
||||
return json_escaped_string_(ctx, buffer + tok->start,
|
||||
tok->end - tok->start);
|
||||
}
|
||||
|
||||
bool json_escaped_eq(const struct json_escaped *a,
|
||||
const struct json_escaped *b)
|
||||
bool json_escape_eq(const struct json_escape *a, const struct json_escape *b)
|
||||
{
|
||||
return streq(a->s, b->s);
|
||||
}
|
||||
|
||||
static struct json_escaped *escape(const tal_t *ctx,
|
||||
const char *str TAKES,
|
||||
bool partial)
|
||||
bool json_escape_needed(const char *str, size_t len)
|
||||
{
|
||||
struct json_escaped *esc;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if ((unsigned)str[i] < ' '
|
||||
|| str[i] == 127
|
||||
|| str[i] == '"'
|
||||
|| str[i] == '\\')
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct json_escape *escape(const tal_t *ctx,
|
||||
const char *str TAKES,
|
||||
size_t len,
|
||||
bool partial)
|
||||
{
|
||||
struct json_escape *esc;
|
||||
size_t i, n;
|
||||
|
||||
/* Worst case: all \uXXXX */
|
||||
esc = (struct json_escaped *)tal_arr(ctx, char, strlen(str) * 6 + 1);
|
||||
/* Fast path: can steal, and nothing to escape. */
|
||||
if (is_taken(str)
|
||||
&& tal_count(str) > len
|
||||
&& !json_escape_needed(str, len)) {
|
||||
taken(str);
|
||||
esc = (struct json_escape *)tal_steal(ctx, str);
|
||||
esc->s[len] = '\0';
|
||||
return esc;
|
||||
}
|
||||
|
||||
for (i = n = 0; str[i]; i++, n++) {
|
||||
/* Worst case: all \uXXXX */
|
||||
esc = (struct json_escape *)tal_arr(ctx, char, len * 6 + 1);
|
||||
|
||||
for (i = n = 0; i < len; i++, n++) {
|
||||
char escape = 0;
|
||||
switch (str[i]) {
|
||||
case '\n':
|
||||
@@ -107,19 +119,24 @@ static struct json_escaped *escape(const tal_t *ctx,
|
||||
return esc;
|
||||
}
|
||||
|
||||
struct json_escaped *json_partial_escape(const tal_t *ctx, const char *str TAKES)
|
||||
struct json_escape *json_partial_escape(const tal_t *ctx, const char *str TAKES)
|
||||
{
|
||||
return escape(ctx, str, true);
|
||||
return escape(ctx, str, strlen(str), true);
|
||||
}
|
||||
|
||||
struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES)
|
||||
struct json_escape *json_escape(const tal_t *ctx, const char *str TAKES)
|
||||
{
|
||||
return escape(ctx, str, false);
|
||||
return escape(ctx, str, strlen(str), false);
|
||||
}
|
||||
|
||||
struct json_escape *json_escape_len(const tal_t *ctx, const char *str TAKES,
|
||||
size_t len)
|
||||
{
|
||||
return escape(ctx, str, len, false);
|
||||
}
|
||||
|
||||
/* By policy, we don't handle \u. Use UTF-8. */
|
||||
const char *json_escaped_unescape(const tal_t *ctx,
|
||||
const struct json_escaped *esc)
|
||||
const char *json_escape_unescape(const tal_t *ctx, const struct json_escape *esc)
|
||||
{
|
||||
char *unesc = tal_arr(ctx, char, strlen(esc->s) + 1);
|
||||
size_t i, n;
|
||||
44
ccan/ccan/json_escape/json_escape.h
Normal file
44
ccan/ccan/json_escape/json_escape.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* MIT (BSD) license - see LICENSE file for details */
|
||||
#ifndef CCAN_JSON_ESCAPE_H
|
||||
#define CCAN_JSON_ESCAPE_H
|
||||
#include "config.h"
|
||||
#include <ccan/tal/tal.h>
|
||||
|
||||
/* Type differentiation for a correctly-escaped JSON string */
|
||||
struct json_escape {
|
||||
/* NUL terminated string. */
|
||||
char s[1];
|
||||
};
|
||||
|
||||
/**
|
||||
* json_escape - escape a valid UTF-8 string.
|
||||
* @ctx: tal context to allocate from.
|
||||
* @str: the string to escape.
|
||||
*
|
||||
* Allocates and returns a valid JSON string (without surrounding quotes).
|
||||
*/
|
||||
struct json_escape *json_escape(const tal_t *ctx, const char *str TAKES);
|
||||
|
||||
/* Version with @len */
|
||||
struct json_escape *json_escape_len(const tal_t *ctx,
|
||||
const char *str TAKES, size_t len);
|
||||
|
||||
/* @str is a valid UTF-8 string which may already contain escapes. */
|
||||
struct json_escape *json_partial_escape(const tal_t *ctx,
|
||||
const char *str TAKES);
|
||||
|
||||
/* Do we need to escape this str? */
|
||||
bool json_escape_needed(const char *str, size_t len);
|
||||
|
||||
/* Are two escape json strings identical? */
|
||||
bool json_escape_eq(const struct json_escape *a,
|
||||
const struct json_escape *b);
|
||||
|
||||
/* Internal routine for creating json_escape from bytes. */
|
||||
struct json_escape *json_escape_string_(const tal_t *ctx,
|
||||
const void *bytes, size_t len);
|
||||
|
||||
/* Be very careful here! Can fail! Doesn't handle \u: use UTF-8 please. */
|
||||
const char *json_escape_unescape(const tal_t *ctx,
|
||||
const struct json_escape *esc);
|
||||
#endif /* CCAN_JSON_ESCAPE_H */
|
||||
41
ccan/ccan/json_escape/test/run-partial.c
Normal file
41
ccan/ccan/json_escape/test/run-partial.c
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
/* Include the C files directly. */
|
||||
#include <ccan/json_escape/json_escape.c>
|
||||
#include <ccan/tap/tap.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const tal_t *ctx = tal(NULL, char);
|
||||
|
||||
/* This is how many tests you plan to run */
|
||||
plan_tests(21);
|
||||
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\")->s, "\\\\"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\\\")->s, "\\\\"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\\\\\")->s, "\\\\\\\\"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\\\\\\\")->s, "\\\\\\\\"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\n")->s, "\\n"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\n")->s, "\\n"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\\"")->s, "\\\""));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\"")->s, "\\\""));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\t")->s, "\\t"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\t")->s, "\\t"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\b")->s, "\\b"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\b")->s, "\\b"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\r")->s, "\\r"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\r")->s, "\\r"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\f")->s, "\\f"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\f")->s, "\\f"));
|
||||
/* You're allowed to escape / according to json.org. */
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\/")->s, "\\/"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "/")->s, "/"));
|
||||
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\u0FFF")->s, "\\u0FFF"));
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\u0FFFx")->s, "\\u0FFFx"));
|
||||
|
||||
/* Unknown escapes should be escaped. */
|
||||
ok1(!strcmp(json_partial_escape(ctx, "\\x")->s, "\\\\x"));
|
||||
tal_free(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
35
ccan/ccan/json_escape/test/run-take.c
Normal file
35
ccan/ccan/json_escape/test/run-take.c
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
/* Include the C files directly. */
|
||||
#include <ccan/json_escape/json_escape.c>
|
||||
#include <ccan/tap/tap.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const tal_t *ctx = tal(NULL, char);
|
||||
struct json_escape *e;
|
||||
char *p;
|
||||
|
||||
/* This is how many tests you plan to run */
|
||||
plan_tests(5);
|
||||
|
||||
/* This should simply be tal_steal */
|
||||
p = tal_dup_arr(NULL, char, "Hello", 6, 0);
|
||||
e = json_escape(ctx, take(p));
|
||||
ok1(!strcmp(e->s, "Hello"));
|
||||
ok1((void *)e == (void *)p);
|
||||
ok1(tal_parent(e) == ctx);
|
||||
|
||||
/* This can't be tal_steal, but still should be freed. */
|
||||
p = tal_dup_arr(NULL, char,
|
||||
"\\\b\f\n\r\t\""
|
||||
"\\\\\\b\\f\\n\\r\\t\\\"", 22, 0);
|
||||
e = json_escape(ctx, take(p));
|
||||
ok1(tal_parent(e) == ctx);
|
||||
ok1(!strcmp(e->s,
|
||||
"\\\\\\b\\f\\n\\r\\t\\\""
|
||||
"\\\\\\\\\\\\b\\\\f\\\\n\\\\r\\\\t\\\\\\\""));
|
||||
tal_free(ctx);
|
||||
|
||||
/* This exits depending on whether all tests passed */
|
||||
return exit_status();
|
||||
}
|
||||
44
ccan/ccan/json_escape/test/run.c
Normal file
44
ccan/ccan/json_escape/test/run.c
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
/* Include the C files directly. */
|
||||
#include <ccan/json_escape/json_escape.c>
|
||||
#include <ccan/tap/tap.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const tal_t *ctx = tal(NULL, char);
|
||||
struct json_escape *e;
|
||||
|
||||
/* This is how many tests you plan to run */
|
||||
plan_tests(6);
|
||||
|
||||
e = json_escape(ctx, "Hello");
|
||||
ok1(!strcmp(e->s, "Hello"));
|
||||
ok1(!strcmp(json_escape_unescape(ctx, e),
|
||||
"Hello"));
|
||||
|
||||
e = json_escape(ctx,
|
||||
"\\\b\f\n\r\t\""
|
||||
"\\\\\\b\\f\\n\\r\\t\\\"");
|
||||
ok1(!strcmp(e->s,
|
||||
"\\\\\\b\\f\\n\\r\\t\\\""
|
||||
"\\\\\\\\\\\\b\\\\f\\\\n\\\\r\\\\t\\\\\\\""));
|
||||
ok1(!strcmp(json_escape_unescape(ctx, e),
|
||||
"\\\b\f\n\r\t\""
|
||||
"\\\\\\b\\f\\n\\r\\t\\\""));
|
||||
|
||||
/* This one doesn't escape the already-escaped chars */
|
||||
e = json_partial_escape(ctx,
|
||||
"\\\b\f\n\r\t\""
|
||||
"\\\\\\b\\f\\n\\r\\t\\\"");
|
||||
ok1(!strcmp(e->s,
|
||||
"\\\\\\b\\f\\n\\r\\t\\\""
|
||||
"\\\\\\b\\f\\n\\r\\t\\\""));
|
||||
ok1(!strcmp(json_escape_unescape(ctx, e),
|
||||
"\\\b\f\n\r\t\""
|
||||
"\\\b\f\n\r\t\""));
|
||||
|
||||
tal_free(ctx);
|
||||
|
||||
/* This exits depending on whether all tests passed */
|
||||
return exit_status();
|
||||
}
|
||||
1
ccan/ccan/json_out/LICENSE
Symbolic link
1
ccan/ccan/json_out/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../licenses/BSD-MIT
|
||||
82
ccan/ccan/json_out/_info
Normal file
82
ccan/ccan/json_out/_info
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* json_out - Code for creating simple JSON output.
|
||||
*
|
||||
* This code helps you create well-formed JSON strings.
|
||||
*
|
||||
* Author: Rusty Russell <rusty@rustcorp.com.au>
|
||||
* License: BSD-MIT
|
||||
*
|
||||
* Example:
|
||||
* // Given "a 1 true" outputs {"argv1":"a","argv2":1,"argv3":true}
|
||||
* // Print arguments as a JSON array.
|
||||
* #include <ccan/json_out/json_out.h>
|
||||
* #include <stdio.h>
|
||||
* #include <string.h>
|
||||
* #include <unistd.h>
|
||||
*
|
||||
* // Simplistic test to see if str needs quotes.
|
||||
* static bool can_be_json_literal(const char *str)
|
||||
* {
|
||||
* char *endp;
|
||||
* if (strtol(str, &endp, 10) != LONG_MIN
|
||||
* && endp != str
|
||||
* && *endp == '\0')
|
||||
* return true;
|
||||
* return !strcmp(str, "true")
|
||||
* || !strcmp(str, "false")
|
||||
* || !strcmp(str, "null");
|
||||
* }
|
||||
*
|
||||
* int main(int argc, char *argv[])
|
||||
* {
|
||||
* struct json_out *jout = json_out_new(NULL);
|
||||
* size_t len;
|
||||
* const char *p;
|
||||
*
|
||||
* json_out_start(jout, NULL, '{');
|
||||
* for (int i = 1; i < argc; i++) {
|
||||
* char fieldname[80];
|
||||
* sprintf(fieldname, "argv%i", i);
|
||||
* json_out_add(jout, fieldname,
|
||||
* !can_be_json_literal(argv[i]),
|
||||
* "%s", argv[i]);
|
||||
* }
|
||||
* json_out_end(jout, '}');
|
||||
* // Force appending of \n
|
||||
* json_out_direct(jout, 1)[0] = '\n';
|
||||
* json_out_finished(jout);
|
||||
*
|
||||
* // Now write it out.
|
||||
* while ((p = json_out_contents(jout, &len)) != NULL) {
|
||||
* int i = write(STDOUT_FILENO, p, len);
|
||||
* if (i <= 0)
|
||||
* exit(1);
|
||||
* json_out_consume(jout, i);
|
||||
* }
|
||||
*
|
||||
* tal_free(jout);
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
/* Expect exactly one argument */
|
||||
if (argc != 2)
|
||||
return 1;
|
||||
|
||||
if (strcmp(argv[1], "depends") == 0) {
|
||||
printf("ccan/compiler\n");
|
||||
printf("ccan/json_escape\n");
|
||||
printf("ccan/membuf\n");
|
||||
printf("ccan/tal\n");
|
||||
printf("ccan/typesafe_cb\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
356
ccan/ccan/json_out/json_out.c
Normal file
356
ccan/ccan/json_out/json_out.c
Normal file
@@ -0,0 +1,356 @@
|
||||
/* MIT (BSD) license - see LICENSE file for details */
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
#include <ccan/json_out/json_out.h>
|
||||
#include <ccan/membuf/membuf.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct json_out {
|
||||
/* Callback if we reallocate. */
|
||||
void (*move_cb)(struct json_out *jout, ptrdiff_t delta, void *arg);
|
||||
void *cb_arg;
|
||||
|
||||
#ifdef CCAN_JSON_OUT_DEBUG
|
||||
/* tal_arr of types ( or [ we're enclosed in. NULL if oom. */
|
||||
char *wrapping;
|
||||
#endif
|
||||
/* True if we haven't yet put an element in current wrapping */
|
||||
bool empty;
|
||||
|
||||
/* Output. */
|
||||
MEMBUF(char) outbuf;
|
||||
};
|
||||
|
||||
/* Realloc helper for tal membufs */
|
||||
static void *membuf_tal_realloc(struct membuf *mb,
|
||||
void *rawelems, size_t newsize)
|
||||
{
|
||||
char *p = rawelems;
|
||||
|
||||
if (!tal_resize(&p, newsize))
|
||||
return NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
struct json_out *json_out_new(const tal_t *ctx)
|
||||
{
|
||||
struct json_out *jout = tal(ctx, struct json_out);
|
||||
char *pool;
|
||||
|
||||
if (!jout)
|
||||
return NULL;
|
||||
pool = tal_arr(jout, char, 64);
|
||||
if (!pool)
|
||||
return tal_free(jout);
|
||||
|
||||
membuf_init(&jout->outbuf, pool, tal_count(pool), membuf_tal_realloc);
|
||||
#ifdef CCAN_JSON_OUT_DEBUG
|
||||
jout->wrapping = tal_arr(jout, char, 0);
|
||||
#endif
|
||||
jout->empty = true;
|
||||
jout->move_cb = NULL;
|
||||
return jout;
|
||||
}
|
||||
|
||||
void json_out_call_on_move_(struct json_out *jout,
|
||||
void (*cb)(struct json_out *jout, ptrdiff_t delta,
|
||||
void *arg),
|
||||
void *arg)
|
||||
{
|
||||
if (cb)
|
||||
assert(!jout->move_cb);
|
||||
jout->move_cb = cb;
|
||||
jout->cb_arg = arg;
|
||||
}
|
||||
|
||||
struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src)
|
||||
{
|
||||
size_t num_elems = membuf_num_elems(&src->outbuf);
|
||||
char *elems = membuf_elems(&src->outbuf);
|
||||
struct json_out *jout = tal_dup(ctx, struct json_out, src);
|
||||
char *pool;
|
||||
|
||||
if (!jout)
|
||||
return NULL;
|
||||
pool = tal_dup_arr(jout, char, elems, num_elems, 0);
|
||||
if (!pool)
|
||||
return tal_free(jout);
|
||||
membuf_init(&jout->outbuf, pool, num_elems, membuf_tal_realloc);
|
||||
membuf_added(&jout->outbuf, num_elems);
|
||||
#ifdef CCAN_JSON_OUT_DEBUG
|
||||
jout->wrapping = tal_dup_arr(jout, char,
|
||||
jout->wrapping, tal_count(jout->wrapping),
|
||||
0);
|
||||
#endif
|
||||
return jout;
|
||||
}
|
||||
|
||||
static void indent(struct json_out *jout, char type)
|
||||
{
|
||||
#ifdef CCAN_JSON_OUT_DEBUG
|
||||
/* Can't check if we ran out of memory. */
|
||||
if (jout->wrapping) {
|
||||
size_t n = tal_count(jout->wrapping);
|
||||
if (!tal_resize(&jout->wrapping, n+1))
|
||||
jout->wrapping = tal_free(jout->wrapping);
|
||||
else
|
||||
jout->wrapping[n] = type;
|
||||
}
|
||||
#endif
|
||||
jout->empty = true;
|
||||
}
|
||||
|
||||
static void unindent(struct json_out *jout, char type)
|
||||
{
|
||||
#ifdef CCAN_JSON_OUT_DEBUG
|
||||
/* Can't check if we ran out of memory. */
|
||||
if (jout->wrapping) {
|
||||
size_t indent = tal_count(jout->wrapping);
|
||||
assert(indent > 0);
|
||||
/* Both [ and ] and { and } are two apart in ASCII */
|
||||
assert(jout->wrapping[indent-1] == type - 2);
|
||||
tal_resize(&jout->wrapping, indent-1);
|
||||
}
|
||||
#endif
|
||||
jout->empty = false;
|
||||
}
|
||||
|
||||
/* Make sure jout->outbuf has room for len: return pointer */
|
||||
static char *mkroom(struct json_out *jout, size_t len)
|
||||
{
|
||||
ptrdiff_t delta = membuf_prepare_space(&jout->outbuf, len);
|
||||
|
||||
if (delta && jout->move_cb)
|
||||
jout->move_cb(jout, delta, jout->cb_arg);
|
||||
|
||||
return membuf_space(&jout->outbuf);
|
||||
}
|
||||
|
||||
static void check_fieldname(const struct json_out *jout,
|
||||
const char *fieldname)
|
||||
{
|
||||
#ifdef CCAN_JSON_OUT_DEBUG
|
||||
/* We don't escape this for you */
|
||||
assert(!fieldname || !json_escape_needed(fieldname, strlen(fieldname)));
|
||||
|
||||
/* Can't check anything else if we ran out of memory. */
|
||||
if (jout->wrapping) {
|
||||
size_t n = tal_count(jout->wrapping);
|
||||
if (n == 0)
|
||||
/* Can't have a fieldname if not in anything! */
|
||||
assert(!fieldname);
|
||||
else if (jout->wrapping[n-1] == '[')
|
||||
/* No fieldnames in arrays. */
|
||||
assert(!fieldname);
|
||||
else {
|
||||
/* Must have fieldnames in objects. */
|
||||
assert(fieldname);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
char *json_out_member_direct(struct json_out *jout,
|
||||
const char *fieldname, size_t extra)
|
||||
{
|
||||
char *dest;
|
||||
|
||||
/* Prepend comma if required. */
|
||||
if (!jout->empty)
|
||||
extra++;
|
||||
|
||||
check_fieldname(jout, fieldname);
|
||||
if (fieldname)
|
||||
extra += 1 + strlen(fieldname) + 2;
|
||||
|
||||
dest = mkroom(jout, extra);
|
||||
if (!dest)
|
||||
goto out;
|
||||
|
||||
if (!jout->empty)
|
||||
*(dest++) = ',';
|
||||
if (fieldname) {
|
||||
*(dest++) = '"';
|
||||
memcpy(dest, fieldname, strlen(fieldname));
|
||||
dest += strlen(fieldname);
|
||||
*(dest++) = '"';
|
||||
*(dest++) = ':';
|
||||
}
|
||||
membuf_added(&jout->outbuf, extra);
|
||||
|
||||
out:
|
||||
jout->empty = false;
|
||||
return dest;
|
||||
}
|
||||
|
||||
bool json_out_start(struct json_out *jout, const char *fieldname, char type)
|
||||
{
|
||||
char *p;
|
||||
|
||||
assert(type == '[' || type == '{');
|
||||
p = json_out_member_direct(jout, fieldname, 1);
|
||||
if (p)
|
||||
p[0] = type;
|
||||
indent(jout, type);
|
||||
|
||||
return p != NULL;
|
||||
}
|
||||
|
||||
bool json_out_end(struct json_out *jout, char type)
|
||||
{
|
||||
char *p;
|
||||
|
||||
assert(type == '}' || type == ']');
|
||||
p = json_out_direct(jout, 1);
|
||||
if (p)
|
||||
p[0] = type;
|
||||
unindent(jout, type);
|
||||
|
||||
return p != NULL;
|
||||
}
|
||||
|
||||
bool json_out_addv(struct json_out *jout,
|
||||
const char *fieldname,
|
||||
bool quote,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
size_t fmtlen, avail;
|
||||
va_list ap2;
|
||||
char *dst;
|
||||
|
||||
if (!json_out_member_direct(jout, fieldname, 0))
|
||||
return false;
|
||||
|
||||
/* Make a copy in case we need it below. */
|
||||
va_copy(ap2, ap);
|
||||
|
||||
/* We can use any additional space, but need room for ". */
|
||||
avail = membuf_num_space(&jout->outbuf);
|
||||
if (quote) {
|
||||
if (avail < 2)
|
||||
avail = 0;
|
||||
else
|
||||
avail -= 2;
|
||||
}
|
||||
|
||||
/* Try printing in place first. */
|
||||
dst = membuf_space(&jout->outbuf);
|
||||
fmtlen = vsnprintf(dst + quote, avail, fmt, ap);
|
||||
|
||||
/* Horrible subtlety: vsnprintf *will* NUL terminate, even if it means
|
||||
* chopping off the last character. So if fmtlen ==
|
||||
* membuf_num_space(&jout->outbuf), the result was truncated! */
|
||||
if (fmtlen + (int)quote*2 >= membuf_num_space(&jout->outbuf)) {
|
||||
/* Make room for NUL terminator, even though we don't want it */
|
||||
dst = mkroom(jout, fmtlen + 1 + (int)quote*2);
|
||||
if (!dst)
|
||||
goto out;
|
||||
vsprintf(dst + quote, fmt, ap2);
|
||||
}
|
||||
|
||||
#ifdef CCAN_JSON_OUT_DEBUG
|
||||
/* You're not inserting junk here, are you? */
|
||||
assert(quote || !json_escape_needed(dst, fmtlen));
|
||||
#endif
|
||||
|
||||
/* Of course, if we need to escape it, we have to redo it all. */
|
||||
if (quote) {
|
||||
if (json_escape_needed(dst + quote, fmtlen)) {
|
||||
struct json_escape *e;
|
||||
e = json_escape_len(NULL, dst + quote, fmtlen);
|
||||
fmtlen = strlen(e->s);
|
||||
dst = mkroom(jout, fmtlen + (int)quote*2);
|
||||
if (!dst)
|
||||
goto out;
|
||||
memcpy(dst + quote, e, fmtlen);
|
||||
tal_free(e);
|
||||
}
|
||||
dst[0] = '"';
|
||||
dst[fmtlen+1] = '"';
|
||||
}
|
||||
membuf_added(&jout->outbuf, fmtlen + (int)quote*2);
|
||||
|
||||
out:
|
||||
va_end(ap2);
|
||||
return dst != NULL;
|
||||
}
|
||||
|
||||
bool json_out_add(struct json_out *jout,
|
||||
const char *fieldname,
|
||||
bool quote,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
bool ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = json_out_addv(jout, fieldname, quote, fmt, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool json_out_addstr(struct json_out *jout,
|
||||
const char *fieldname,
|
||||
const char *str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
char *p;
|
||||
struct json_escape *e;
|
||||
|
||||
if (json_escape_needed(str, len)) {
|
||||
e = json_escape(NULL, str);
|
||||
str = e->s;
|
||||
len = strlen(str);
|
||||
} else
|
||||
e = NULL;
|
||||
|
||||
p = json_out_member_direct(jout, fieldname, len + 2);
|
||||
if (p) {
|
||||
p[0] = p[1+len] = '"';
|
||||
memcpy(p+1, str, len);
|
||||
}
|
||||
tal_free(e);
|
||||
|
||||
return p != NULL;
|
||||
}
|
||||
|
||||
bool json_out_add_splice(struct json_out *jout,
|
||||
const char *fieldname,
|
||||
const struct json_out *src)
|
||||
{
|
||||
const char *p;
|
||||
size_t len;
|
||||
|
||||
p = json_out_contents(src, &len);
|
||||
if (!p)
|
||||
return false;
|
||||
memcpy(json_out_member_direct(jout, fieldname, len), p, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
char *json_out_direct(struct json_out *jout, size_t len)
|
||||
{
|
||||
char *p = mkroom(jout, len);
|
||||
if (p)
|
||||
membuf_added(&jout->outbuf, len);
|
||||
return p;
|
||||
}
|
||||
|
||||
void json_out_finished(const struct json_out *jout)
|
||||
{
|
||||
#ifdef CCAN_JSON_OUT_DEBUG
|
||||
assert(tal_count(jout->wrapping) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *json_out_contents(const struct json_out *jout, size_t *len)
|
||||
{
|
||||
*len = membuf_num_elems(&jout->outbuf);
|
||||
return *len ? membuf_elems(&jout->outbuf) : NULL;
|
||||
}
|
||||
|
||||
void json_out_consume(struct json_out *jout, size_t len)
|
||||
{
|
||||
membuf_consume(&jout->outbuf, len);
|
||||
}
|
||||
200
ccan/ccan/json_out/json_out.h
Normal file
200
ccan/ccan/json_out/json_out.h
Normal file
@@ -0,0 +1,200 @@
|
||||
/* MIT (BSD) license - see LICENSE file for details */
|
||||
#ifndef CCAN_JSON_OUT_H
|
||||
#define CCAN_JSON_OUT_H
|
||||
#include <ccan/compiler/compiler.h>
|
||||
#include <ccan/tal/tal.h>
|
||||
#include <ccan/typesafe_cb/typesafe_cb.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct json_out;
|
||||
|
||||
/**
|
||||
* json_out_new - allocate a json_out stream.
|
||||
* @ctx: the tal_context to allocate from, or NULL
|
||||
*
|
||||
* Returns NULL if tal allocation fails.
|
||||
*/
|
||||
struct json_out *json_out_new(const tal_t *ctx);
|
||||
|
||||
/**
|
||||
* json_out_call_on_move - callback for when buffer is reallocated.
|
||||
* @jout: the json_out object to attach to.
|
||||
* @cb: the callback to call.
|
||||
* @arg: the argument to @cb (must match type).
|
||||
*
|
||||
* A NULL @cb disables. You can't currently have more than one callback.
|
||||
* The @delta argument to @cb is the difference between the old location
|
||||
* and the new one, and is never zero.
|
||||
*/
|
||||
#define json_out_call_on_move(jout, cb, arg) \
|
||||
json_out_call_on_move_((jout), \
|
||||
typesafe_cb_preargs(void, void *, \
|
||||
(cb), (arg), \
|
||||
struct json_out *, \
|
||||
ptrdiff_t), \
|
||||
(arg))
|
||||
|
||||
void json_out_call_on_move_(struct json_out *jout,
|
||||
void (*cb)(struct json_out *jout, ptrdiff_t delta,
|
||||
void *arg),
|
||||
void *arg);
|
||||
|
||||
/**
|
||||
* json_out_dup - duplicate a json_out stream.
|
||||
* @ctx: the tal_context to allocate from, or NULL
|
||||
* @src: the json_out to copy.
|
||||
*/
|
||||
struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src);
|
||||
|
||||
/**
|
||||
* json_out_start - start an array or object.
|
||||
* @jout: the json_out object to write into.
|
||||
* @fieldname: the fieldname, if inside an object, or NULL if inside an array.
|
||||
* @type: '[' or '{' to start an array or object, respectively.
|
||||
*
|
||||
* Returns true unless tal_resize() fails.
|
||||
* Literally writes '"@fieldname": @type' or '@type ' if fieldname is NULL.
|
||||
* @fieldname must not need JSON escaping.
|
||||
*/
|
||||
bool json_out_start(struct json_out *jout, const char *fieldname, char type);
|
||||
|
||||
/**
|
||||
* json_out_end - end an array or object.
|
||||
* @jout: the json_out object to write into.
|
||||
* @type: '}' or ']' to end an array or object, respectively.
|
||||
*
|
||||
* Returns true unless tal_resize() fails.
|
||||
*
|
||||
* Literally writes ']' or '}', keeping track of whether we need to append
|
||||
* a comma.
|
||||
*/
|
||||
bool json_out_end(struct json_out *jout, char type);
|
||||
|
||||
/**
|
||||
* json_out_add - add a formatted member.
|
||||
* @jout: the json_out object to write into.
|
||||
* @fieldname: optional fieldname to prepend (must not need escaping).
|
||||
* @quote: if true, surround fmt by " and ".
|
||||
* @fmt...: the printf-style format
|
||||
*
|
||||
* Returns true unless tal_resize() fails.
|
||||
*
|
||||
* If you're in an array, @fieldname must be NULL. If you're in an
|
||||
* object, @fieldname must be non-NULL. This is checked if
|
||||
* CCAN_JSON_OUT_DEBUG is defined.
|
||||
* @fieldname must not need JSON escaping.
|
||||
*
|
||||
* If the resulting string requires escaping, and @quote is true, we
|
||||
* call json_escape().
|
||||
*/
|
||||
PRINTF_FMT(4,5)
|
||||
bool json_out_add(struct json_out *jout,
|
||||
const char *fieldname,
|
||||
bool quote,
|
||||
const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* json_out_addv - add a formatted member (vararg variant)
|
||||
* @jout: the json_out object to write into.
|
||||
* @fieldname: optional fieldname to prepend.
|
||||
* @quote: if true, surround fmt by " and ".
|
||||
* @fmt: the printf-style format
|
||||
* @ap: the argument list.
|
||||
*
|
||||
* See json_out_add() above.
|
||||
*/
|
||||
bool json_out_addv(struct json_out *jout,
|
||||
const char *fieldname,
|
||||
bool quote,
|
||||
const char *fmt,
|
||||
va_list ap);
|
||||
|
||||
/**
|
||||
* json_out_addstr - convenience helper to add a string field.
|
||||
* @jout: the json_out object to write into.
|
||||
* @fieldname: optional fieldname to prepend.
|
||||
* @str: the string to add (must not be NULL).
|
||||
*
|
||||
* Equivalent to json_out_add(@jout, @fieldname, true, "%s", @str);
|
||||
*/
|
||||
bool json_out_addstr(struct json_out *jout,
|
||||
const char *fieldname,
|
||||
const char *str);
|
||||
|
||||
/**
|
||||
* json_out_member_direct - add a field, with direct access.
|
||||
* @jout: the json_out object to write into.
|
||||
* @fieldname: optional fieldname to prepend.
|
||||
* @extra: how many bytes to allocate.
|
||||
*
|
||||
* @fieldname must not need JSON escaping. Returns a direct pointer into
|
||||
* the @extra bytes, or NULL if tal_resize() fails.
|
||||
*
|
||||
* This allows you to write your own efficient type-specific helpers.
|
||||
*/
|
||||
char *json_out_member_direct(struct json_out *jout,
|
||||
const char *fieldname, size_t extra);
|
||||
|
||||
/**
|
||||
* json_out_direct - make room in output and access directly.
|
||||
* @jout: the json_out object to write into.
|
||||
* @len: the length to allocate.
|
||||
*
|
||||
* This lets you access the json_out stream directly, to save a copy,
|
||||
* if you know exactly how much you will write.
|
||||
*
|
||||
* Returns a pointer to @len bytes at the end of @jout, or NULL if
|
||||
* tal_resize() fails.
|
||||
*
|
||||
* This is dangerous, since it doesn't automatically prepend a ","
|
||||
* like the internal logic does, but can be used (carefully) to add
|
||||
* entire objects, or whitespace.
|
||||
*/
|
||||
char *json_out_direct(struct json_out *jout, size_t extra);
|
||||
|
||||
/**
|
||||
* json_out_add_splice - copy a field from another json_out.
|
||||
* @jout: the json_out object to write into.
|
||||
* @fieldname: optional fieldname to prepend.
|
||||
* @src: the json_out object to copy from.
|
||||
*
|
||||
* This asserts that @src is well-formed (as per json_out_finished()),
|
||||
* then places it into @jout with optional @fieldname prepended. This
|
||||
* can be used to assemble sub-objects for your JSON and then copy
|
||||
* them in.
|
||||
*
|
||||
* Note that it will call json_out_contents(@src), so it expects that
|
||||
* object to be unconsumed.
|
||||
*
|
||||
* Returns false if tal_resize() fails.
|
||||
*/
|
||||
bool json_out_add_splice(struct json_out *jout,
|
||||
const char *fieldname,
|
||||
const struct json_out *src);
|
||||
|
||||
/**
|
||||
* json_out_finished - assert that the json buffer is finished.
|
||||
* @jout: the json_out object written to.
|
||||
*
|
||||
* This simply causes internal assertions that all arrays and objects are
|
||||
* finished. It needs CCAN_JSON_OUT_DEBUG defined to have any effect.
|
||||
*/
|
||||
void json_out_finished(const struct json_out *jout);
|
||||
|
||||
/**
|
||||
* json_out_contents - read contents from json_out stream.
|
||||
* @jout: the json_out object we want to read from.
|
||||
* @len: set to the length of the buffer returned.
|
||||
*
|
||||
* This returns a pointer into the JSON written so far. Returns NULL
|
||||
* and sets @len to 0 if there's nothing left in the buffer.
|
||||
*/
|
||||
const char *json_out_contents(const struct json_out *jout, size_t *len);
|
||||
|
||||
/**
|
||||
* json_out_consume - discard contents from json_out stream.
|
||||
* @jout: the json_out object we read from.
|
||||
* @len: the length to consume (must be <= @len from json_out_contents)
|
||||
*/
|
||||
void json_out_consume(struct json_out *jout, size_t len);
|
||||
#endif /* CCAN_JSON_OUT_H */
|
||||
2
ccan/ccan/json_out/test/run-debugging.c
Normal file
2
ccan/ccan/json_out/test/run-debugging.c
Normal file
@@ -0,0 +1,2 @@
|
||||
#define CCAN_JSON_OUT_DEBUG 1
|
||||
#include "run.c"
|
||||
48
ccan/ccan/json_out/test/run-move_cb.c
Normal file
48
ccan/ccan/json_out/test/run-move_cb.c
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <ccan/json_out/json_out.h>
|
||||
/* Include the C files directly. */
|
||||
#include <ccan/json_out/json_out.c>
|
||||
#include <ccan/tap/tap.h>
|
||||
|
||||
static const char *ptr;
|
||||
static bool called = false;
|
||||
|
||||
static void move_cb(struct json_out *jout, ptrdiff_t delta,
|
||||
struct json_out *arg)
|
||||
{
|
||||
ptr += delta;
|
||||
called = true;
|
||||
ok1(arg == jout);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const tal_t *ctx = tal(NULL, char);
|
||||
struct json_out *jout;
|
||||
char *p;
|
||||
size_t len;
|
||||
|
||||
/* This is how many tests you plan to run */
|
||||
plan_tests(3);
|
||||
|
||||
/* Test nested arrays. */
|
||||
jout = json_out_new(ctx);
|
||||
json_out_call_on_move(jout, move_cb, jout);
|
||||
|
||||
json_out_start(jout, NULL, '{');
|
||||
ptr = json_out_contents(jout, &len);
|
||||
|
||||
p = json_out_member_direct(jout, "fieldname", 102);
|
||||
p[0] = '"';
|
||||
p[101] = '"';
|
||||
memset(p+1, 'p', 100);
|
||||
|
||||
json_out_finished(jout);
|
||||
ok1(called);
|
||||
/* Contents should have moved correctly. */
|
||||
ok1(json_out_contents(jout, &len) == ptr);
|
||||
|
||||
tal_free(ctx);
|
||||
|
||||
/* This exits depending on whether all tests passed */
|
||||
return exit_status();
|
||||
}
|
||||
141
ccan/ccan/json_out/test/run.c
Normal file
141
ccan/ccan/json_out/test/run.c
Normal file
@@ -0,0 +1,141 @@
|
||||
#include <ccan/json_out/json_out.h>
|
||||
/* Include the C files directly. */
|
||||
#include <ccan/json_out/json_out.c>
|
||||
#include <ccan/tap/tap.h>
|
||||
|
||||
static void test_json_out_add(const tal_t *ctx,
|
||||
char c, bool quote, const char *escaped)
|
||||
{
|
||||
/* 64 is the size of the initial buf, so we test that. */
|
||||
for (size_t i = 1; i < 64; i++) {
|
||||
struct json_out *jout;
|
||||
char str[64 + 1];
|
||||
const char *r;
|
||||
size_t len;
|
||||
char fieldname[64 + 1];
|
||||
|
||||
jout = json_out_new(ctx);
|
||||
json_out_start(jout, NULL, '{');
|
||||
memset(str, c, i);
|
||||
str[i] = '\0';
|
||||
memset(fieldname, 'f', i);
|
||||
fieldname[i] = '\0';
|
||||
json_out_add(jout, fieldname, quote, "%s", str);
|
||||
json_out_end(jout, '}');
|
||||
json_out_finished(jout);
|
||||
|
||||
r = json_out_contents(jout, &len);
|
||||
ok1(len == strlen("{\"") + i + strlen("\":")
|
||||
+ quote * 2 + strlen(escaped) * i + strlen("}"));
|
||||
|
||||
ok1(len > strlen("{\""));
|
||||
ok1(memcmp(r, "{\"", strlen("{\"")) == 0);
|
||||
json_out_consume(jout, strlen("{\""));
|
||||
|
||||
r = json_out_contents(jout, &len);
|
||||
ok1(len > strlen(fieldname));
|
||||
ok1(memcmp(r, fieldname, strlen(fieldname)) == 0);
|
||||
json_out_consume(jout, strlen(fieldname));
|
||||
|
||||
r = json_out_contents(jout, &len);
|
||||
ok1(len > strlen("\":"));
|
||||
ok1(memcmp(r, "\":", strlen("\":")) == 0);
|
||||
json_out_consume(jout, strlen("\":"));
|
||||
|
||||
r = json_out_contents(jout, &len);
|
||||
if (quote) {
|
||||
ok1(len > 0);
|
||||
ok1(r[0] == '"');
|
||||
json_out_consume(jout, 1);
|
||||
}
|
||||
for (size_t n = 0; n < i; n++) {
|
||||
r = json_out_contents(jout, &len);
|
||||
ok1(len > strlen(escaped));
|
||||
ok1(memcmp(r, escaped, strlen(escaped)) == 0);
|
||||
json_out_consume(jout, strlen(escaped));
|
||||
}
|
||||
r = json_out_contents(jout, &len);
|
||||
if (quote) {
|
||||
ok1(len > 0);
|
||||
ok1(r[0] == '"');
|
||||
json_out_consume(jout, 1);
|
||||
}
|
||||
r = json_out_contents(jout, &len);
|
||||
ok1(len == 1);
|
||||
ok1(memcmp(r, "}", 1) == 0);
|
||||
json_out_consume(jout, 1);
|
||||
ok1(!json_out_contents(jout, &len));
|
||||
ok1(len == 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void json_eq(const struct json_out *jout, const char *expect)
|
||||
{
|
||||
size_t len;
|
||||
const char *p;
|
||||
|
||||
json_out_finished(jout);
|
||||
p = json_out_contents(jout, &len);
|
||||
ok1(len == strlen(expect));
|
||||
ok1(memcmp(expect, p, len) == 0);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const tal_t *ctx = tal(NULL, char);
|
||||
struct json_out *jout;
|
||||
char *p;
|
||||
|
||||
/* This is how many tests you plan to run */
|
||||
plan_tests(14689);
|
||||
|
||||
/* Simple tests */
|
||||
test_json_out_add(ctx, '1', false, "1");
|
||||
test_json_out_add(ctx, 'x', true, "x");
|
||||
test_json_out_add(ctx, '\n', true, "\\n");
|
||||
|
||||
/* Test nested arrays. */
|
||||
jout = json_out_new(ctx);
|
||||
for (size_t i = 0; i < 64; i++)
|
||||
json_out_start(jout, NULL, '[');
|
||||
for (size_t i = 0; i < 64; i++)
|
||||
json_out_end(jout, ']');
|
||||
json_eq(jout, "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]");
|
||||
|
||||
/* Test nested objects. */
|
||||
jout = json_out_new(ctx);
|
||||
json_out_start(jout, NULL, '{');
|
||||
for (size_t i = 0; i < 63; i++)
|
||||
json_out_start(jout, "x", '{');
|
||||
for (size_t i = 0; i < 64; i++)
|
||||
json_out_end(jout, '}');
|
||||
json_eq(jout, "{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}");
|
||||
|
||||
jout = json_out_new(ctx);
|
||||
json_out_start(jout, NULL, '{');
|
||||
p = json_out_member_direct(jout, "x", 7);
|
||||
memcpy(p, "\"hello\"", 7);
|
||||
json_out_end(jout, '}');
|
||||
json_eq(jout, "{\"x\":\"hello\"}");
|
||||
|
||||
jout = json_out_new(ctx);
|
||||
p = json_out_direct(jout, strlen("{\"x\":\"hello\"}\n"));
|
||||
memcpy(p, "{\"x\":\"hello\"}\n", strlen("{\"x\":\"hello\"}\n"));
|
||||
json_eq(jout, "{\"x\":\"hello\"}\n");
|
||||
|
||||
jout = json_out_new(ctx);
|
||||
json_out_start(jout, NULL, '{');
|
||||
struct json_out *jout2 = json_out_new(ctx);
|
||||
json_out_start(jout2, NULL, '{');
|
||||
json_out_addstr(jout2, "x", "hello");
|
||||
json_out_end(jout2, '}');
|
||||
json_out_finished(jout2);
|
||||
json_out_add_splice(jout, "inner", jout2);
|
||||
json_out_end(jout, '}');
|
||||
json_eq(jout, "{\"inner\":{\"x\":\"hello\"}}");
|
||||
|
||||
tal_free(ctx);
|
||||
|
||||
/* This exits depending on whether all tests passed */
|
||||
return exit_status();
|
||||
}
|
||||
@@ -4,7 +4,6 @@ LIGHTNING_CLI_OBJS := $(LIGHTNING_CLI_SRC:.c=.o)
|
||||
LIGHTNING_CLI_COMMON_OBJS := \
|
||||
common/configdir.o \
|
||||
common/json.o \
|
||||
common/json_escaped.o \
|
||||
common/memleak.o \
|
||||
common/utils.o \
|
||||
common/version.o
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <assert.h>
|
||||
#include <ccan/asort/asort.h>
|
||||
#include <ccan/err/err.h>
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
#include <ccan/opt/opt.h>
|
||||
#include <ccan/read_write_all/read_write_all.h>
|
||||
#include <ccan/str/str.h>
|
||||
@@ -12,7 +13,6 @@
|
||||
#include <common/configdir.h>
|
||||
#include <common/json.h>
|
||||
#include <common/json_command.h>
|
||||
#include <common/json_escaped.h>
|
||||
#include <common/memleak.h>
|
||||
#include <common/utils.h>
|
||||
#include <common/version.h>
|
||||
|
||||
@@ -12,7 +12,6 @@ CLI_TEST_COMMON_OBJS := \
|
||||
common/daemon_conn.o \
|
||||
common/htlc_state.o \
|
||||
common/json.o \
|
||||
common/json_escaped.o \
|
||||
common/pseudorand.o \
|
||||
common/memleak.o \
|
||||
common/msg_queue.o \
|
||||
|
||||
@@ -28,7 +28,6 @@ COMMON_SRC_NOGEN := \
|
||||
common/initial_commit_tx.c \
|
||||
common/io_lock.c \
|
||||
common/json.c \
|
||||
common/json_escaped.c \
|
||||
common/json_helpers.c \
|
||||
common/json_tok.c \
|
||||
common/key_derive.c \
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef LIGHTNING_COMMON_JSON_ESCAPED_H
|
||||
#define LIGHTNING_COMMON_JSON_ESCAPED_H
|
||||
#include "config.h"
|
||||
#include <common/json.h>
|
||||
|
||||
/* Type differentiation for a correctly-escaped JSON string */
|
||||
struct json_escaped {
|
||||
/* NUL terminated string. */
|
||||
char s[1];
|
||||
};
|
||||
|
||||
/* @str be a valid UTF-8 string */
|
||||
struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES);
|
||||
|
||||
/* @str is a valid UTF-8 string which may already contain escapes. */
|
||||
struct json_escaped *json_partial_escape(const tal_t *ctx,
|
||||
const char *str TAKES);
|
||||
|
||||
/* Extract a JSON-escaped string. */
|
||||
struct json_escaped *json_to_escaped_string(const tal_t *ctx,
|
||||
const char *buffer,
|
||||
const jsmntok_t *tok);
|
||||
|
||||
/* Are two escaped json strings identical? */
|
||||
bool json_escaped_eq(const struct json_escaped *a,
|
||||
const struct json_escaped *b);
|
||||
|
||||
/* Internal routine for creating json_escaped from bytes. */
|
||||
struct json_escaped *json_escaped_string_(const tal_t *ctx,
|
||||
const void *bytes, size_t len);
|
||||
|
||||
/* Be very careful here! Can fail! Doesn't handle \u: use UTF-8 please. */
|
||||
const char *json_escaped_unescape(const tal_t *ctx,
|
||||
const struct json_escaped *esc);
|
||||
#endif /* LIGHTNING_COMMON_JSON_ESCAPED_H */
|
||||
@@ -1,9 +1,9 @@
|
||||
#include <ccan/crypto/sha256/sha256.h>
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
#include <ccan/str/hex/hex.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/amount.h>
|
||||
#include <common/json_command.h>
|
||||
#include <common/json_escaped.h>
|
||||
#include <common/json_tok.h>
|
||||
#include <common/jsonrpc_errors.h>
|
||||
#include <common/param.h>
|
||||
@@ -53,9 +53,12 @@ struct command_result *param_escaped_string(struct command *cmd,
|
||||
const jsmntok_t *tok,
|
||||
const char **str)
|
||||
{
|
||||
struct json_escaped *esc = json_to_escaped_string(cmd, buffer, tok);
|
||||
if (esc) {
|
||||
*str = json_escaped_unescape(cmd, esc);
|
||||
if (tok->type == JSMN_STRING) {
|
||||
struct json_escape *esc;
|
||||
/* jsmn always gives us ~ well-formed strings. */
|
||||
esc = json_escape_string_(cmd, buffer + tok->start,
|
||||
tok->end - tok->start);
|
||||
*str = json_escape_unescape(cmd, esc);
|
||||
if (*str)
|
||||
return NULL;
|
||||
}
|
||||
@@ -77,10 +80,10 @@ struct command_result *param_string(struct command *cmd, const char *name,
|
||||
|
||||
struct command_result *param_label(struct command *cmd, const char *name,
|
||||
const char * buffer, const jsmntok_t *tok,
|
||||
struct json_escaped **label)
|
||||
struct json_escape **label)
|
||||
{
|
||||
/* We accept both strings and number literals here. */
|
||||
*label = json_escaped_string_(cmd, buffer + tok->start, tok->end - tok->start);
|
||||
*label = json_escape_string_(cmd, buffer + tok->start, tok->end - tok->start);
|
||||
if (*label && (tok->type == JSMN_STRING || json_tok_is_num(buffer, tok)))
|
||||
return NULL;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ struct amount_msat;
|
||||
struct amount_sat;
|
||||
struct command;
|
||||
struct command_result;
|
||||
struct json_escaped;
|
||||
struct json_escape;
|
||||
struct sha256;
|
||||
|
||||
/* Extract json array token */
|
||||
@@ -42,7 +42,7 @@ struct command_result *param_string(struct command *cmd, const char *name,
|
||||
/* Extract a label. It is either an escaped string or a number. */
|
||||
struct command_result *param_label(struct command *cmd, const char *name,
|
||||
const char * buffer, const jsmntok_t *tok,
|
||||
struct json_escaped **label);
|
||||
struct json_escape **label);
|
||||
|
||||
/* Extract number from this (may be a string, or a number literal) */
|
||||
struct command_result *param_number(struct command *cmd, const char *name,
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
#include "../json_escaped.c"
|
||||
#include <assert.h>
|
||||
#include <common/utils.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static void test_json_partial(void)
|
||||
{
|
||||
const tal_t *ctx = tal(NULL, char);
|
||||
|
||||
assert(streq(json_partial_escape(ctx, "\\")->s, "\\\\"));
|
||||
assert(streq(json_partial_escape(ctx, "\\\\")->s, "\\\\"));
|
||||
assert(streq(json_partial_escape(ctx, "\\\\\\")->s, "\\\\\\\\"));
|
||||
assert(streq(json_partial_escape(ctx, "\\\\\\\\")->s, "\\\\\\\\"));
|
||||
assert(streq(json_partial_escape(ctx, "\\n")->s, "\\n"));
|
||||
assert(streq(json_partial_escape(ctx, "\n")->s, "\\n"));
|
||||
assert(streq(json_partial_escape(ctx, "\\\"")->s, "\\\""));
|
||||
assert(streq(json_partial_escape(ctx, "\"")->s, "\\\""));
|
||||
assert(streq(json_partial_escape(ctx, "\\t")->s, "\\t"));
|
||||
assert(streq(json_partial_escape(ctx, "\t")->s, "\\t"));
|
||||
assert(streq(json_partial_escape(ctx, "\\b")->s, "\\b"));
|
||||
assert(streq(json_partial_escape(ctx, "\b")->s, "\\b"));
|
||||
assert(streq(json_partial_escape(ctx, "\\r")->s, "\\r"));
|
||||
assert(streq(json_partial_escape(ctx, "\r")->s, "\\r"));
|
||||
assert(streq(json_partial_escape(ctx, "\\f")->s, "\\f"));
|
||||
assert(streq(json_partial_escape(ctx, "\f")->s, "\\f"));
|
||||
/* You're allowed to escape / according to json.org. */
|
||||
assert(streq(json_partial_escape(ctx, "\\/")->s, "\\/"));
|
||||
assert(streq(json_partial_escape(ctx, "/")->s, "/"));
|
||||
|
||||
assert(streq(json_partial_escape(ctx, "\\u0FFF")->s, "\\u0FFF"));
|
||||
assert(streq(json_partial_escape(ctx, "\\u0FFFx")->s, "\\u0FFFx"));
|
||||
|
||||
/* Unknown escapes should be escaped. */
|
||||
assert(streq(json_partial_escape(ctx, "\\x")->s, "\\\\x"));
|
||||
tal_free(ctx);
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
setup_locale();
|
||||
|
||||
test_json_partial();
|
||||
assert(!taken_any());
|
||||
take_cleanup();
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "config.h"
|
||||
#include "../amount.c"
|
||||
#include "../json.c"
|
||||
#include "../json_escaped.c"
|
||||
#include "../json_tok.c"
|
||||
#include "../param.c"
|
||||
#include <ccan/array_size/array_size.h>
|
||||
@@ -438,7 +437,7 @@ static void advanced(void)
|
||||
{
|
||||
struct json *j = json_parse(cmd, "[ 'lightning', 24, 'tok', 543 ]");
|
||||
|
||||
struct json_escaped *label;
|
||||
struct json_escape *label;
|
||||
u64 *msat;
|
||||
u64 *msat_opt1, *msat_opt2;
|
||||
const jsmntok_t *tok;
|
||||
@@ -460,7 +459,7 @@ static void advanced(void)
|
||||
}
|
||||
{
|
||||
struct json *j = json_parse(cmd, "[ 3, 'foo' ]");
|
||||
struct json_escaped *label, *foo;
|
||||
struct json_escape *label, *foo;
|
||||
assert(param(cmd, j->buffer, j->toks,
|
||||
p_req("label", param_label, &label),
|
||||
p_opt("foo", param_label, &foo),
|
||||
@@ -532,7 +531,7 @@ static void test_invoice(struct command *cmd,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
u64 *msatoshi_val;
|
||||
struct json_escaped *label_val;
|
||||
struct json_escape *label_val;
|
||||
const char *desc_val;
|
||||
u64 *expiry;
|
||||
const jsmntok_t *fallbacks;
|
||||
|
||||
@@ -36,7 +36,6 @@ LIGHTNINGD_COMMON_OBJS := \
|
||||
common/key_derive.o \
|
||||
common/io_lock.o \
|
||||
common/json.o \
|
||||
common/json_escaped.o \
|
||||
common/json_helpers.o \
|
||||
common/json_tok.o \
|
||||
common/memleak.o \
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
#include <ccan/crypto/siphash24/siphash24.h>
|
||||
#include <ccan/err/err.h>
|
||||
#include <ccan/fdpass/fdpass.h>
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
#include <ccan/take/take.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/amount.h>
|
||||
#include <common/features.h>
|
||||
#include <common/json_command.h>
|
||||
#include <common/json_escaped.h>
|
||||
#include <common/json_helpers.h>
|
||||
#include <common/jsonrpc_errors.h>
|
||||
#include <common/param.h>
|
||||
@@ -202,7 +202,7 @@ static void json_getnodes_reply(struct subd *gossip UNUSED, const u8 *reply,
|
||||
json_array_start(response, "nodes");
|
||||
|
||||
for (i = 0; i < tal_count(nodes); i++) {
|
||||
struct json_escaped *esc;
|
||||
struct json_escape *esc;
|
||||
|
||||
json_object_start(response, NULL);
|
||||
json_add_node_id(response, "nodeid", &nodes[i]->nodeid);
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
#include <bitcoin/base58.h>
|
||||
#include <bitcoin/script.h>
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
#include <ccan/str/hex/hex.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/amount.h>
|
||||
#include <common/bech32.h>
|
||||
#include <common/bolt11.h>
|
||||
#include <common/json_command.h>
|
||||
#include <common/json_escaped.h>
|
||||
#include <common/json_helpers.h>
|
||||
#include <common/jsonrpc_errors.h>
|
||||
#include <common/overflows.h>
|
||||
@@ -108,7 +108,7 @@ struct invoice_payment_hook_payload {
|
||||
/* Set to NULL if it is deleted while waiting for plugin */
|
||||
struct htlc_in *hin;
|
||||
/* What invoice it's trying to pay. */
|
||||
const struct json_escaped *label;
|
||||
const struct json_escape *label;
|
||||
/* Amount it's offering. */
|
||||
struct amount_msat msat;
|
||||
/* Preimage we'll give it if succeeds. */
|
||||
@@ -448,7 +448,7 @@ struct invoice_info {
|
||||
struct command *cmd;
|
||||
struct preimage payment_preimage;
|
||||
struct bolt11 *b11;
|
||||
struct json_escaped *label;
|
||||
struct json_escape *label;
|
||||
};
|
||||
|
||||
static void gossipd_incoming_channels_reply(struct subd *gossipd,
|
||||
@@ -799,7 +799,7 @@ AUTODATA(json_command, &invoice_command);
|
||||
|
||||
static void json_add_invoices(struct json_stream *response,
|
||||
struct wallet *wallet,
|
||||
const struct json_escaped *label)
|
||||
const struct json_escape *label)
|
||||
{
|
||||
struct invoice_iterator it;
|
||||
const struct invoice_details *details;
|
||||
@@ -826,7 +826,7 @@ static struct command_result *json_listinvoices(struct command *cmd,
|
||||
const jsmntok_t *obj UNNEEDED,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
struct json_escaped *label;
|
||||
struct json_escape *label;
|
||||
struct json_stream *response;
|
||||
struct wallet *wallet = cmd->ld->wallet;
|
||||
if (!param(cmd, buffer, params,
|
||||
@@ -859,7 +859,7 @@ static struct command_result *json_delinvoice(struct command *cmd,
|
||||
const struct invoice_details *details;
|
||||
struct json_stream *response;
|
||||
const char *status, *actual_status;
|
||||
struct json_escaped *label;
|
||||
struct json_escape *label;
|
||||
struct wallet *wallet = cmd->ld->wallet;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
@@ -978,7 +978,7 @@ static struct command_result *json_waitinvoice(struct command *cmd,
|
||||
struct invoice i;
|
||||
const struct invoice_details *details;
|
||||
struct wallet *wallet = cmd->ld->wallet;
|
||||
struct json_escaped *label;
|
||||
struct json_escape *label;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_req("label", param_label, &label),
|
||||
@@ -1079,7 +1079,7 @@ static struct command_result *json_decodepay(struct command *cmd,
|
||||
json_add_amount_msat_compat(response, *b11->msat,
|
||||
"msatoshi", "amount_msat");
|
||||
if (b11->description) {
|
||||
struct json_escaped *esc = json_escape(NULL, b11->description);
|
||||
struct json_escape *esc = json_escape(NULL, b11->description);
|
||||
json_add_escaped_string(response, "description", take(esc));
|
||||
}
|
||||
if (b11->description_hash)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
#include <ccan/mem/mem.h>
|
||||
#include <ccan/str/hex/hex.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/json.h>
|
||||
#include <common/json_command.h>
|
||||
#include <common/json_escaped.h>
|
||||
#include <common/json_helpers.h>
|
||||
#include <common/jsonrpc_errors.h>
|
||||
#include <common/memleak.h>
|
||||
@@ -349,7 +349,7 @@ void json_add_literal(struct json_stream *result, const char *fieldname,
|
||||
|
||||
void json_add_string(struct json_stream *result, const char *fieldname, const char *value TAKES)
|
||||
{
|
||||
struct json_escaped *esc = json_partial_escape(NULL, value);
|
||||
struct json_escape *esc = json_partial_escape(NULL, value);
|
||||
|
||||
json_add_member(result, fieldname, "\"%s\"", esc->s);
|
||||
tal_free(esc);
|
||||
@@ -380,7 +380,7 @@ void json_add_tx(struct json_stream *result,
|
||||
}
|
||||
|
||||
void json_add_escaped_string(struct json_stream *result, const char *fieldname,
|
||||
const struct json_escaped *esc TAKES)
|
||||
const struct json_escape *esc TAKES)
|
||||
{
|
||||
json_add_member(result, fieldname, "\"%s\"", esc->s);
|
||||
if (taken(esc))
|
||||
|
||||
@@ -22,7 +22,7 @@ struct bitcoin_txid;
|
||||
struct chainparams;
|
||||
struct channel_id;
|
||||
struct command;
|
||||
struct json_escaped;
|
||||
struct json_escape;
|
||||
struct json_stream;
|
||||
struct pubkey;
|
||||
struct node_id;
|
||||
@@ -123,7 +123,7 @@ void json_add_string(struct json_stream *result, const char *fieldname, const ch
|
||||
* already be JSON escaped as necessary. */
|
||||
void json_add_escaped_string(struct json_stream *result,
|
||||
const char *fieldname,
|
||||
const struct json_escaped *esc TAKES);
|
||||
const struct json_escape *esc TAKES);
|
||||
|
||||
/* '"fieldname" : literal' or 'literal' if fieldname is NULL*/
|
||||
void json_add_literal(struct json_stream *result, const char *fieldname,
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
#include <ccan/asort/asort.h>
|
||||
#include <ccan/err/err.h>
|
||||
#include <ccan/io/io.h>
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
#include <ccan/str/hex/hex.h>
|
||||
#include <ccan/strmap/strmap.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/bech32.h>
|
||||
#include <common/json_command.h>
|
||||
#include <common/json_escaped.h>
|
||||
#include <common/jsonrpc_errors.h>
|
||||
#include <common/memleak.h>
|
||||
#include <common/param.h>
|
||||
@@ -332,7 +332,7 @@ static void json_add_help_command(struct command *cmd,
|
||||
" a description for this"
|
||||
" json_command!");
|
||||
} else {
|
||||
struct json_escaped *esc;
|
||||
struct json_escape *esc;
|
||||
|
||||
esc = json_escape(NULL, json_command->verbose);
|
||||
json_add_escaped_string(response, "verbose", take(esc));
|
||||
@@ -551,7 +551,7 @@ struct json_stream *json_stream_fail_nodata(struct command *cmd,
|
||||
const char *errmsg)
|
||||
{
|
||||
struct json_stream *r = json_start(cmd);
|
||||
struct json_escaped *e = json_partial_escape(tmpctx, errmsg);
|
||||
struct json_escape *e = json_partial_escape(tmpctx, errmsg);
|
||||
|
||||
assert(code);
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <ccan/err/err.h>
|
||||
#include <ccan/io/fdpass/fdpass.h>
|
||||
#include <ccan/io/io.h>
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
#include <ccan/noerr/noerr.h>
|
||||
#include <ccan/pipecmd/pipecmd.h>
|
||||
#include <ccan/read_write_all/read_write_all.h>
|
||||
@@ -57,7 +58,6 @@
|
||||
/*~ This is common code: routines shared by one or more executables
|
||||
* (separate daemons, or the lightning-cli program). */
|
||||
#include <common/daemon.h>
|
||||
#include <common/json_escaped.h>
|
||||
#include <common/timeout.h>
|
||||
#include <common/utils.h>
|
||||
#include <common/version.h>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <bitcoin/chainparams.h>
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/err/err.h>
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
#include <ccan/mem/mem.h>
|
||||
#include <ccan/opt/opt.h>
|
||||
#include <ccan/opt/private.h>
|
||||
@@ -11,7 +12,6 @@
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/configdir.h>
|
||||
#include <common/json_command.h>
|
||||
#include <common/json_escaped.h>
|
||||
#include <common/jsonrpc_errors.h>
|
||||
#include <common/memleak.h>
|
||||
#include <common/param.h>
|
||||
@@ -1044,7 +1044,7 @@ static void add_config(struct lightningd *ld,
|
||||
}
|
||||
|
||||
if (answer) {
|
||||
struct json_escaped *esc = json_escape(NULL, answer);
|
||||
struct json_escape *esc = json_escape(NULL, answer);
|
||||
json_add_escaped_string(response, name0, take(esc));
|
||||
}
|
||||
tal_free(name0);
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <channeld/gen_channel_wire.h>
|
||||
#include <common/json_command.h>
|
||||
#include <common/json_escaped.h>
|
||||
#include <common/jsonrpc_errors.h>
|
||||
#include <common/overflows.h>
|
||||
#include <common/param.h>
|
||||
|
||||
@@ -14,7 +14,6 @@ LIGHTNINGD_TEST_COMMON_OBJS := \
|
||||
common/htlc_state.o \
|
||||
common/io_lock.o \
|
||||
common/json.o \
|
||||
common/json_escaped.o \
|
||||
common/key_derive.o \
|
||||
common/pseudorand.o \
|
||||
common/memleak.o \
|
||||
|
||||
@@ -154,7 +154,7 @@ void json_add_bool(struct json_stream *result UNNEEDED, const char *fieldname UN
|
||||
/* Generated stub for json_add_escaped_string */
|
||||
void json_add_escaped_string(struct json_stream *result UNNEEDED,
|
||||
const char *fieldname UNNEEDED,
|
||||
const struct json_escaped *esc TAKES UNNEEDED)
|
||||
const struct json_escape *esc TAKES UNNEEDED)
|
||||
{ fprintf(stderr, "json_add_escaped_string called!\n"); abort(); }
|
||||
/* Generated stub for json_add_hex */
|
||||
void json_add_hex(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
|
||||
@@ -323,7 +323,7 @@ struct command_result *param_escaped_string(struct command *cmd UNNEEDED,
|
||||
/* Generated stub for param_label */
|
||||
struct command_result *param_label(struct command *cmd UNNEEDED, const char *name UNNEEDED,
|
||||
const char * buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
|
||||
struct json_escaped **label UNNEEDED)
|
||||
struct json_escape **label UNNEEDED)
|
||||
{ fprintf(stderr, "param_label called!\n"); abort(); }
|
||||
/* Generated stub for param_loglevel */
|
||||
struct command_result *param_loglevel(struct command *cmd UNNEEDED,
|
||||
@@ -495,7 +495,7 @@ bool wallet_htlcs_load_for_channel(struct wallet *wallet UNNEEDED,
|
||||
bool wallet_invoice_create(struct wallet *wallet UNNEEDED,
|
||||
struct invoice *pinvoice UNNEEDED,
|
||||
const struct amount_msat *msat TAKES UNNEEDED,
|
||||
const struct json_escaped *label TAKES UNNEEDED,
|
||||
const struct json_escape *label TAKES UNNEEDED,
|
||||
u64 expiry UNNEEDED,
|
||||
const char *b11enc UNNEEDED,
|
||||
const char *description UNNEEDED,
|
||||
@@ -518,7 +518,7 @@ const struct invoice_details *wallet_invoice_details(const tal_t *ctx UNNEEDED,
|
||||
/* Generated stub for wallet_invoice_find_by_label */
|
||||
bool wallet_invoice_find_by_label(struct wallet *wallet UNNEEDED,
|
||||
struct invoice *pinvoice UNNEEDED,
|
||||
const struct json_escaped *label UNNEEDED)
|
||||
const struct json_escape *label UNNEEDED)
|
||||
{ fprintf(stderr, "wallet_invoice_find_by_label called!\n"); abort(); }
|
||||
/* Generated stub for wallet_invoice_find_by_rhash */
|
||||
bool wallet_invoice_find_by_rhash(struct wallet *wallet UNNEEDED,
|
||||
|
||||
@@ -141,7 +141,7 @@ static void test_json_escape(void)
|
||||
for (i = 1; i < 256; i++) {
|
||||
char badstr[2];
|
||||
struct json_stream *result = new_json_stream(NULL, NULL, NULL);
|
||||
struct json_escaped *esc;
|
||||
struct json_escape *esc;
|
||||
|
||||
badstr[0] = i;
|
||||
badstr[1] = 0;
|
||||
|
||||
@@ -25,7 +25,6 @@ PLUGIN_COMMON_OBJS := \
|
||||
common/daemon.o \
|
||||
common/hash_u5.o \
|
||||
common/json.o \
|
||||
common/json_escaped.o \
|
||||
common/json_helpers.o \
|
||||
common/json_tok.o \
|
||||
common/memleak.o \
|
||||
|
||||
16
wallet/db.c
16
wallet/db.c
@@ -1,8 +1,8 @@
|
||||
#include "db.h"
|
||||
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/json_escaped.h>
|
||||
#include <common/node_id.h>
|
||||
#include <common/version.h>
|
||||
#include <inttypes.h>
|
||||
@@ -1121,16 +1121,16 @@ bool sqlite3_bind_sha256_double(sqlite3_stmt *stmt, int col, const struct sha256
|
||||
return err == SQLITE_OK;
|
||||
}
|
||||
|
||||
struct json_escaped *sqlite3_column_json_escaped(const tal_t *ctx,
|
||||
sqlite3_stmt *stmt, int col)
|
||||
struct json_escape *sqlite3_column_json_escape(const tal_t *ctx,
|
||||
sqlite3_stmt *stmt, int col)
|
||||
{
|
||||
return json_escaped_string_(ctx,
|
||||
sqlite3_column_blob(stmt, col),
|
||||
sqlite3_column_bytes(stmt, col));
|
||||
return json_escape_string_(ctx,
|
||||
sqlite3_column_blob(stmt, col),
|
||||
sqlite3_column_bytes(stmt, col));
|
||||
}
|
||||
|
||||
bool sqlite3_bind_json_escaped(sqlite3_stmt *stmt, int col,
|
||||
const struct json_escaped *esc)
|
||||
bool sqlite3_bind_json_escape(sqlite3_stmt *stmt, int col,
|
||||
const struct json_escape *esc)
|
||||
{
|
||||
int err = sqlite3_bind_text(stmt, col, esc->s, strlen(esc->s), SQLITE_TRANSIENT);
|
||||
return err == SQLITE_OK;
|
||||
|
||||
@@ -202,10 +202,10 @@ bool sqlite3_bind_sha256_double(sqlite3_stmt *stmt, int col, const struct sha256
|
||||
struct secret *sqlite3_column_secrets(const tal_t *ctx,
|
||||
sqlite3_stmt *stmt, int col);
|
||||
|
||||
struct json_escaped *sqlite3_column_json_escaped(const tal_t *ctx,
|
||||
sqlite3_stmt *stmt, int col);
|
||||
bool sqlite3_bind_json_escaped(sqlite3_stmt *stmt, int col,
|
||||
const struct json_escaped *esc);
|
||||
struct json_escape *sqlite3_column_json_escape(const tal_t *ctx,
|
||||
sqlite3_stmt *stmt, int col);
|
||||
bool sqlite3_bind_json_escape(sqlite3_stmt *stmt, int col,
|
||||
const struct json_escape *esc);
|
||||
|
||||
struct amount_msat sqlite3_column_amount_msat(sqlite3_stmt *stmt, int col);
|
||||
struct amount_sat sqlite3_column_amount_sat(sqlite3_stmt *stmt, int col);
|
||||
|
||||
@@ -96,7 +96,7 @@ static struct invoice_details *wallet_stmt2invoice_details(const tal_t *ctx,
|
||||
|
||||
sqlite3_column_sha256(stmt, 2, &dtl->rhash);
|
||||
|
||||
dtl->label = sqlite3_column_json_escaped(dtl, stmt, 3);
|
||||
dtl->label = sqlite3_column_json_escape(dtl, stmt, 3);
|
||||
|
||||
if (sqlite3_column_type(stmt, 4) != SQLITE_NULL) {
|
||||
dtl->msat = tal(dtl, struct amount_msat);
|
||||
@@ -255,7 +255,7 @@ static void install_expiration_timer(struct invoices *invoices)
|
||||
bool invoices_create(struct invoices *invoices,
|
||||
struct invoice *pinvoice,
|
||||
const struct amount_msat *msat TAKES,
|
||||
const struct json_escaped *label TAKES,
|
||||
const struct json_escape *label TAKES,
|
||||
u64 expiry,
|
||||
const char *b11enc,
|
||||
const char *description,
|
||||
@@ -300,7 +300,7 @@ bool invoices_create(struct invoices *invoices,
|
||||
sqlite3_bind_amount_msat(stmt, 4, *msat);
|
||||
else
|
||||
sqlite3_bind_null(stmt, 4);
|
||||
sqlite3_bind_json_escaped(stmt, 5, label);
|
||||
sqlite3_bind_json_escape(stmt, 5, label);
|
||||
sqlite3_bind_int64(stmt, 6, expiry_time);
|
||||
sqlite3_bind_text(stmt, 7, b11enc, strlen(b11enc), SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 8, description, strlen(description), SQLITE_TRANSIENT);
|
||||
@@ -327,7 +327,7 @@ bool invoices_create(struct invoices *invoices,
|
||||
|
||||
bool invoices_find_by_label(struct invoices *invoices,
|
||||
struct invoice *pinvoice,
|
||||
const struct json_escaped *label)
|
||||
const struct json_escape *label)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
@@ -335,7 +335,7 @@ bool invoices_find_by_label(struct invoices *invoices,
|
||||
"id"
|
||||
" FROM invoices"
|
||||
" WHERE label = ?;");
|
||||
sqlite3_bind_json_escaped(stmt, 1, label);
|
||||
sqlite3_bind_json_escape(stmt, 1, label);
|
||||
if (!db_select_step(invoices->db, stmt))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
struct amount_msat;
|
||||
struct db;
|
||||
struct json_escaped;
|
||||
struct json_escape;
|
||||
struct invoice;
|
||||
struct invoice_details;
|
||||
struct invoice_iterator;
|
||||
@@ -49,7 +49,7 @@ struct invoices *invoices_new(const tal_t *ctx,
|
||||
bool invoices_create(struct invoices *invoices,
|
||||
struct invoice *pinvoice,
|
||||
const struct amount_msat *msat TAKES,
|
||||
const struct json_escaped *label TAKES,
|
||||
const struct json_escape *label TAKES,
|
||||
u64 expiry,
|
||||
const char *b11enc,
|
||||
const char *description,
|
||||
@@ -68,7 +68,7 @@ bool invoices_create(struct invoices *invoices,
|
||||
*/
|
||||
bool invoices_find_by_label(struct invoices *invoices,
|
||||
struct invoice *pinvoice,
|
||||
const struct json_escaped *label);
|
||||
const struct json_escape *label);
|
||||
|
||||
/**
|
||||
* invoices_find_by_rhash - Search for an invoice by
|
||||
|
||||
@@ -18,10 +18,6 @@ static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, bool ca
|
||||
#include <unistd.h>
|
||||
|
||||
/* AUTOGENERATED MOCKS START */
|
||||
/* Generated stub for json_escaped_string_ */
|
||||
struct json_escaped *json_escaped_string_(const tal_t *ctx UNNEEDED,
|
||||
const void *bytes UNNEEDED, size_t len UNNEEDED)
|
||||
{ fprintf(stderr, "json_escaped_string_ called!\n"); abort(); }
|
||||
/* AUTOGENERATED MOCKS END */
|
||||
|
||||
static char *db_err;
|
||||
|
||||
@@ -123,7 +123,7 @@ bool htlc_is_trimmed(enum side htlc_owner UNNEEDED,
|
||||
bool invoices_create(struct invoices *invoices UNNEEDED,
|
||||
struct invoice *pinvoice UNNEEDED,
|
||||
const struct amount_msat *msat TAKES UNNEEDED,
|
||||
const struct json_escaped *label TAKES UNNEEDED,
|
||||
const struct json_escape *label TAKES UNNEEDED,
|
||||
u64 expiry UNNEEDED,
|
||||
const char *b11enc UNNEEDED,
|
||||
const char *description UNNEEDED,
|
||||
@@ -141,7 +141,7 @@ void invoices_delete_expired(struct invoices *invoices UNNEEDED,
|
||||
/* Generated stub for invoices_find_by_label */
|
||||
bool invoices_find_by_label(struct invoices *invoices UNNEEDED,
|
||||
struct invoice *pinvoice UNNEEDED,
|
||||
const struct json_escaped *label UNNEEDED)
|
||||
const struct json_escape *label UNNEEDED)
|
||||
{ fprintf(stderr, "invoices_find_by_label called!\n"); abort(); }
|
||||
/* Generated stub for invoices_find_by_rhash */
|
||||
bool invoices_find_by_rhash(struct invoices *invoices UNNEEDED,
|
||||
@@ -305,10 +305,6 @@ void json_array_end(struct json_stream *js UNNEEDED)
|
||||
/* Generated stub for json_array_start */
|
||||
void json_array_start(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED)
|
||||
{ fprintf(stderr, "json_array_start called!\n"); abort(); }
|
||||
/* Generated stub for json_escaped_string_ */
|
||||
struct json_escaped *json_escaped_string_(const tal_t *ctx UNNEEDED,
|
||||
const void *bytes UNNEEDED, size_t len UNNEEDED)
|
||||
{ fprintf(stderr, "json_escaped_string_ called!\n"); abort(); }
|
||||
/* Generated stub for json_get_member */
|
||||
const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED,
|
||||
const char *label UNNEEDED)
|
||||
|
||||
@@ -1629,7 +1629,7 @@ bool wallet_htlcs_load_for_channel(struct wallet *wallet,
|
||||
bool wallet_invoice_create(struct wallet *wallet,
|
||||
struct invoice *pinvoice,
|
||||
const struct amount_msat *msat TAKES,
|
||||
const struct json_escaped *label TAKES,
|
||||
const struct json_escape *label TAKES,
|
||||
u64 expiry,
|
||||
const char *b11enc,
|
||||
const char *description,
|
||||
@@ -1640,7 +1640,7 @@ bool wallet_invoice_create(struct wallet *wallet,
|
||||
}
|
||||
bool wallet_invoice_find_by_label(struct wallet *wallet,
|
||||
struct invoice *pinvoice,
|
||||
const struct json_escaped *label)
|
||||
const struct json_escape *label)
|
||||
{
|
||||
return invoices_find_by_label(wallet->invoices, pinvoice, label);
|
||||
}
|
||||
|
||||
@@ -641,7 +641,7 @@ struct invoice_details {
|
||||
/* Hash of preimage r */
|
||||
struct sha256 rhash;
|
||||
/* Label assigned by user */
|
||||
const struct json_escaped *label;
|
||||
const struct json_escape *label;
|
||||
/* NULL if they specified "any" */
|
||||
struct amount_msat *msat;
|
||||
/* Absolute UNIX epoch time this will expire */
|
||||
@@ -693,7 +693,7 @@ struct invoice {
|
||||
bool wallet_invoice_create(struct wallet *wallet,
|
||||
struct invoice *pinvoice,
|
||||
const struct amount_msat *msat TAKES,
|
||||
const struct json_escaped *label TAKES,
|
||||
const struct json_escape *label TAKES,
|
||||
u64 expiry,
|
||||
const char *b11enc,
|
||||
const char *description,
|
||||
@@ -712,7 +712,7 @@ bool wallet_invoice_create(struct wallet *wallet,
|
||||
*/
|
||||
bool wallet_invoice_find_by_label(struct wallet *wallet,
|
||||
struct invoice *pinvoice,
|
||||
const struct json_escaped *label);
|
||||
const struct json_escape *label);
|
||||
|
||||
/**
|
||||
* wallet_invoice_find_by_rhash - Search for an invoice by payment_hash
|
||||
|
||||
Reference in New Issue
Block a user