diff --git a/common/json.c b/common/json.c index cb33f1586..7d8248edc 100644 --- a/common/json.c +++ b/common/json.c @@ -782,11 +782,14 @@ static bool handle_percent(const char *buffer, } } -/* GUIDE := OBJ | '%' +/* GUIDE := OBJ | ARRAY | '%' * OBJ := '{' FIELDLIST '}' * FIELDLIST := FIELD [',' FIELD]* * FIELD := LITERAL ':' FIELDVAL - * FIELDVAL := OBJ | LITERAL | '%' + * FIELDVAL := OBJ | ARRAY | LITERAL | '%' + * ARRAY := '[' ARRLIST ']' + * ARRLIST := ARRELEM [',' ARRELEM]* + * ARRELEM := NUMBER ':' FIELDVAL */ /* Returns NULL on failure, or offset into guide */ @@ -803,12 +806,34 @@ static const char *parse_literal(const char *guide, return guide + *len; } +static const char *parse_number(const char *guide, u32 *number) +{ + char *endp; + long int l; + + l = strtol(guide, &endp, 10); + if (endp == guide || errno == ERANGE) + return NULL; + + /* Test for overflow */ + *number = l; + if (*number != l) + return NULL; + + return endp; +} + /* Recursion */ static const char *parse_obj(const char *buffer, const jsmntok_t *tok, const char *guide, va_list *ap); +static const char *parse_arr(const char *buffer, + const jsmntok_t *tok, + const char *guide, + va_list *ap); + static const char *parse_guide(const char *buffer, const jsmntok_t *tok, const char *guide, @@ -820,6 +845,12 @@ static const char *parse_guide(const char *buffer, return NULL; assert(*guide == '}'); return guide + 1; + } else if (*guide == '[') { + guide = parse_arr(buffer, tok, guide, ap); + if (!guide) + return NULL; + assert(*guide == ']'); + return guide + 1; } else { assert(*guide == '%'); if (!handle_percent(buffer, tok, ap)) @@ -839,6 +870,12 @@ static const char *parse_fieldval(const char *buffer, return NULL; assert(*guide == '}'); return guide + 1; + } else if (*guide == '[') { + guide = parse_arr(buffer, tok, guide, ap); + if (!guide) + return NULL; + assert(*guide == ']'); + return guide + 1; } else if (*guide == '%') { if (!handle_percent(buffer, tok, ap)) return NULL; @@ -907,6 +944,55 @@ static const char *parse_obj(const char *buffer, return guide; } +static const char *parse_arrelem(const char *buffer, + const jsmntok_t *tok, + const char *guide, + va_list *ap) +{ + const jsmntok_t *member; + u32 idx; + + guide = parse_number(guide, &idx); + assert(*guide == ':'); + + member = json_get_arr(tok, idx); + if (!member) + return NULL; + + return parse_fieldval(buffer, member, guide + 1, ap); +} + +static const char *parse_arrlist(const char *buffer, + const jsmntok_t *tok, + const char *guide, + va_list *ap) +{ + for (;;) { + guide = parse_arrelem(buffer, tok, guide, ap); + if (!guide) + return NULL; + if (*guide != ',') + break; + guide++; + } + return guide; +} +static const char *parse_arr(const char *buffer, + const jsmntok_t *tok, + const char *guide, + va_list *ap) +{ + assert(*guide == '['); + + if (tok->type != JSMN_ARRAY) + return NULL; + + guide = parse_arrlist(buffer, tok, guide + 1, ap); + if (!guide) + return NULL; + return guide; +} + bool json_scanv(const char *buffer, const jsmntok_t *tok, const char *guide, diff --git a/common/test/run-json_scan.c b/common/test/run-json_scan.c index 23de936d4..b736feed9 100644 --- a/common/test/run-json_scan.c +++ b/common/test/run-json_scan.c @@ -140,16 +140,19 @@ int main(int argc, char *argv[]) common_setup(argv[0]); - buf = tal_strdup(tmpctx, "{\"1\":\"one\", \"2\":\"two\", \"3\":{\"three\": {\"deeper\": 17}}}"); + buf = tal_strdup(tmpctx, "{\"1\":\"one\", \"2\":\"two\", \"3\":{\"three\": {\"deeper\": 17}}, \"arr\": [{\"1\": \"arrone\"}, 2, [3, 4]]}"); toks = json_parse_simple(tmpctx, buf, strlen(buf)); assert(toks); - assert(toks->size == 3); + assert(toks->size == 4); /* These are direct matches, and they work. */ assert(json_scan(buf, toks, "{1:one}")); assert(json_scan(buf, toks, "{1:one,2:two}")); assert(json_scan(buf, toks, "{2:two,1:one}")); assert(json_scan(buf, toks, "{2:two,1:one,3:{three:{deeper:17}}}")); + assert(json_scan(buf, toks, "{2:two,1:one,3:{three:{deeper:17}},arr:[0:{1:arrone}]}")); + assert(json_scan(buf, toks, "{2:two,1:one,3:{three:{deeper:17}},arr:[1:2]}")); + assert(json_scan(buf, toks, "{2:two,1:one,3:{three:{deeper:17}},arr:[1:2,2:[0:3,1:4]]}")); /* These do not match */ assert(!json_scan(buf, toks, "{2:one}")); @@ -159,6 +162,12 @@ int main(int argc, char *argv[]) assert(!json_scan(buf, toks, "{2:two,1:one,3:three}")); assert(!json_scan(buf, toks, "{3:{three:deeper}}")); assert(!json_scan(buf, toks, "{3:{three:{deeper:{}}}}")); + assert(!json_scan(buf, toks, "{arr:{}}")); + assert(!json_scan(buf, toks, "{arr:[0:1]}")); + assert(!json_scan(buf, toks, "{arr:[4:0]}")); + assert(!json_scan(buf, toks, "{arr:[0:{1:arrtwo}]}")); + assert(!json_scan(buf, toks, "{arr:[1:3]}")); + assert(!json_scan(buf, toks, "{arr:[2:[0:1]]}")); /* These capture simple values. */ assert(json_scan(buf, toks, "{3:{three:{deeper:%}}}", @@ -177,5 +186,16 @@ int main(int argc, char *argv[]) assert(json_scan(buf, toks, "{3:%}", JSON_SCAN(json_to_tok, &t))); assert(t == &toks[6]); + assert(json_scan(buf, toks, "{arr:%}", JSON_SCAN(json_to_tok, &t))); + assert(t == &toks[12]); + + assert(json_scan(buf, toks, "{arr:[1:%]}", + JSON_SCAN(json_to_number, &u32val))); + assert(u32val == 2); + + assert(json_scan(buf, toks, "{arr:[2:[1:%]]}", + JSON_SCAN(json_to_number, &u32val))); + assert(u32val == 4); + common_shutdown(); }