diff --git a/cli/lightning-cli.c b/cli/lightning-cli.c index 510fb3fc4..c7ff930d8 100644 --- a/cli/lightning-cli.c +++ b/cli/lightning-cli.c @@ -3,6 +3,7 @@ */ #include "config.h" #include +#include #include #include #include @@ -10,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -94,21 +96,104 @@ static size_t human_readable(const char *buffer, const jsmntok_t *t, char term) abort(); } -static void human_help(const char *buffer, const jsmntok_t *result, bool has_command) { - int i; - const jsmntok_t * help_array = result + 2; - /* the first command object */ - const jsmntok_t * curr = help_array + 1; - /* iterate through all commands, printing the name and description */ - for (i = 0; i < help_array->size; i++) { - curr += 2; - printf("%.*s\n", curr->end - curr->start, buffer + curr->start); - curr += 2; - printf(" %.*s\n\n", curr->end - curr->start, buffer + curr->start); - curr += 2; - /* advance to next command */ - curr++; +static int compare_tok(const jsmntok_t *a, const jsmntok_t *b, + const char *buffer) +{ + int a_len = a->end - a->start, b_len = b->end - b->start, min_len, cmp; + + if (a_len > b_len) + min_len = b_len; + else + min_len = a_len; + + cmp = memcmp(buffer + a->start, buffer + b->start, min_len); + if (cmp != 0) + return cmp; + /* If equal, shorter one wins. */ + return a_len - b_len; +} + +static int compare_help(const jsmntok_t *const *a, + const jsmntok_t *const *b, + char *buffer) +{ + const jsmntok_t *cat_a, *cat_b; + bool a_is_developer, b_is_developer; + int cmp; + + cat_a = json_get_member(buffer, *a, "category"); + cat_b = json_get_member(buffer, *b, "category"); + + /* Just in case it's an older lightningd! */ + if (!cat_a) + goto same_category; + + /* We always tweak "developer" category to last. */ + a_is_developer = json_tok_streq(buffer, cat_a, "developer"); + b_is_developer = json_tok_streq(buffer, cat_b, "developer"); + + if (a_is_developer && b_is_developer) + cmp = 0; + else if (a_is_developer) + cmp = 1; + else if (b_is_developer) + cmp = -1; + else + /* Otherwise we order category alphabetically. */ + cmp = compare_tok(cat_a, cat_b, buffer); + + if (cmp != 0) + return cmp; + + /* After category, we order by name */ +same_category: + return compare_tok(json_get_member(buffer, *a, "command"), + json_get_member(buffer, *b, "command"), + buffer); +} + +static void human_help(char *buffer, const jsmntok_t *result, bool has_command) { + unsigned int i; + /* `curr` is used as a temporary token */ + const jsmntok_t *curr; + const char *prev_cat; + /* Contains all commands objects, which have the following structure : + * { + * "command": "The command name and usage", + * "category": "The command category", + * "description": "The command's description", + * "verbose": "The command's detailed description" + * } + */ + const jsmntok_t * help_array = json_get_member(buffer, result, "help"); + const jsmntok_t **help = tal_arr(NULL, const jsmntok_t *, + help_array->size); + + /* Populate an array for easy sorting with asort */ + json_for_each_arr(i, curr, help_array) + help[i] = curr; + + asort(help, tal_count(help), compare_help, buffer); + + prev_cat = ""; + for (i = 0; i < tal_count(help); i++) { + const jsmntok_t *category, *command, *desc; + + category = json_get_member(buffer, help[i], "category"); + if (category && !json_tok_streq(buffer, category, prev_cat)) { + prev_cat = json_strdup(help, buffer, category); + if (!has_command) + printf("=== %s ===\n\n", prev_cat); + } + + command = json_get_member(buffer, help[i], "command"); + desc = json_get_member(buffer, help[i], "description"); + printf("%.*s\n", + command->end - command->start, buffer + command->start); + printf(" %.*s\n\n", + desc->end - desc->start, buffer + desc->start); } + tal_free(help); if (!has_command) printf("---\nrun `lightning-cli help ` for more information on a specific command\n"); diff --git a/cli/test/run-large-input.c b/cli/test/run-large-input.c index 6d8806bdc..7217bb61c 100644 --- a/cli/test/run-large-input.c +++ b/cli/test/run-large-input.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include