From 8071ba125bb6893b88c02353638eeac220cb99c1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Feb 2014 14:02:17 +1030 Subject: [PATCH] opt: don't wordwrap when description line starts with whitespace. This was suggested by Luke, though his version insisted on using tab. This one is a bit more complex, but allows either tab or space. Suggested-by: Luke Dashjr Signed-off-by: Rusty Russell --- ccan/opt/opt.h | 14 ++++++++ ccan/opt/test/run-consume_words.c | 57 +++++++++++++++++++++++-------- ccan/opt/usage.c | 38 +++++++++++++-------- 3 files changed, 79 insertions(+), 30 deletions(-) diff --git a/ccan/opt/opt.h b/ccan/opt/opt.h index 6c3cbee1..2aee4095 100644 --- a/ccan/opt/opt.h +++ b/ccan/opt/opt.h @@ -350,11 +350,25 @@ char *opt_invalid_argument(const char *arg); * and a table of all the options with their descriptions. If an option has * description opt_hidden, it is not shown here. * + * The table of options is formatted such that descriptions are + * wrapped on space boundaries. If a description has a "\n" that is + * left intact, and the following characters indented appropriately. + * If the description begins with one or more space/tab (or has a + * space or tab following a "\n") that line is output without wrapping. + * * If "extra" is NULL, then the extra information is taken from any * registered option which calls opt_usage_and_exit(). This avoids duplicating * that string in the common case. * * The result should be passed to free(). + * + * See Also: + * opt_usage_and_exit() + * + * Example: + * opt_register_arg("--explode|--boom", explode, NULL, NULL, + * "This line will be wrapped by opt_usage\n" + * " But this won't because it's indented."); */ char *opt_usage(const char *argv0, const char *extra); diff --git a/ccan/opt/test/run-consume_words.c b/ccan/opt/test/run-consume_words.c index 98b2e1e6..9f164656 100644 --- a/ccan/opt/test/run-consume_words.c +++ b/ccan/opt/test/run-consume_words.c @@ -7,31 +7,58 @@ /* Test consume_words helper. */ int main(int argc, char *argv[]) { - size_t start, len; + size_t prefix, len; + bool start = true; - plan_tests(13); + plan_tests(27); /* Every line over width. */ - len = consume_words("hello world", 1, &start); - ok1(start == 0); + len = consume_words("hello world", 1, &prefix, &start); + ok1(prefix == 0); + ok1(!start); ok1(len == strlen("hello")); - len = consume_words(" world", 1, &start); - ok1(start == 1); + len = consume_words(" world", 1, &prefix, &start); + ok1(prefix == 1); ok1(len == strlen("world")); - ok1(consume_words("", 1, &start) == 0); + ok1(!start); + ok1(consume_words("", 1, &prefix, &start) == 0); /* Same with width where won't both fit. */ - len = consume_words("hello world", 5, &start); - ok1(start == 0); + start = true; + len = consume_words("hello world", 5, &prefix, &start); + ok1(!start); + ok1(prefix == 0); ok1(len == strlen("hello")); - len = consume_words(" world", 5, &start); - ok1(start == 1); + len = consume_words(" world", 5, &prefix, &start); + ok1(!start); + ok1(prefix == 1); ok1(len == strlen("world")); - ok1(consume_words("", 5, &start) == 0); + ok1(consume_words("", 5, &prefix, &start) == 0); - len = consume_words("hello world", 11, &start); - ok1(start == 0); + start = true; + len = consume_words("hello world", 11, &prefix, &start); + ok1(!start); + ok1(prefix == 0); ok1(len == strlen("hello world")); - ok1(consume_words("", 11, &start) == 0); + ok1(consume_words("", 11, &prefix, &start) == 0); + + /* Now try a literal, should not be broken */ + start = true; + len = consume_words(" hello world", 5, &prefix, &start); + ok1(!start); + ok1(prefix == 1); + ok1(len == strlen("hello world")); + + /* A literal after an explicit \n also not broken */ + start = true; + len = consume_words("hi\n hello world", 5, &prefix, &start); + ok1(start); + ok1(prefix == 0); + ok1(len == strlen("hi\n")); + len = consume_words(" hello world", 5, &prefix, &start); + ok1(!start); + ok1(prefix == 1); + ok1(len == strlen("hello world")); + return exit_status(); } diff --git a/ccan/opt/usage.c b/ccan/opt/usage.c index 494b179b..3edc4cf8 100644 --- a/ccan/opt/usage.c +++ b/ccan/opt/usage.c @@ -41,27 +41,35 @@ static unsigned int get_columns(void) /* Return number of chars of words to put on this line. * Prefix is set to number to skip at start, maxlen is max width, returns - * length (after prefix) to put on this line. */ -static size_t consume_words(const char *words, size_t maxlen, size_t *prefix) + * length (after prefix) to put on this line. + * start is set if we start a new line in the source description. */ +static size_t consume_words(const char *words, size_t maxlen, size_t *prefix, + bool *start) { size_t oldlen, len; - /* Swallow leading whitespace. */ + /* Always swollow leading whitespace. */ *prefix = strspn(words, " \n"); words += *prefix; - /* Use at least one word, even if it takes us over maxlen. */ - oldlen = len = strcspn(words, " "); - while (len <= maxlen) { - oldlen = len; - len += strspn(words+len, " "); - if (words[len] == '\n') - break; - len += strcspn(words+len, " \n"); - if (len == oldlen) - break; + /* Leading whitespace at start of line means literal. */ + if (*start && *prefix) { + oldlen = strcspn(words, "\n"); + } else { + /* Use at least one word, even if it takes us over maxlen. */ + oldlen = len = strcspn(words, " "); + while (len <= maxlen) { + oldlen = len; + len += strspn(words+len, " "); + if (words[len] == '\n') + break; + len += strcspn(words+len, " \n"); + if (len == oldlen) + break; + } } + *start = (words[oldlen - 1] == '\n'); return oldlen; } @@ -95,7 +103,7 @@ static char *add_desc(char *base, size_t *len, size_t *max, { size_t off, prefix, l; const char *p; - bool same_line = false; + bool same_line = false, start = true; base = add_str(base, len, max, opt->names); off = strlen(opt->names); @@ -118,7 +126,7 @@ static char *add_desc(char *base, size_t *len, size_t *max, /* Indent description. */ p = opt->desc; - while ((l = consume_words(p, width - indent, &prefix)) != 0) { + while ((l = consume_words(p, width - indent, &prefix, &start)) != 0) { if (!same_line) base = add_indent(base, len, max, indent); p += prefix; -- 2.39.2