opt: don't wordwrap when description line starts with whitespace.
authorRusty Russell <rusty@rustcorp.com.au>
Tue, 11 Feb 2014 03:32:17 +0000 (14:02 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Tue, 11 Feb 2014 03:32:17 +0000 (14:02 +1030)
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 <luke-jr+git@utopios.org>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ccan/opt/opt.h
ccan/opt/test/run-consume_words.c
ccan/opt/usage.c

index 6c3cbee148f02ef241806b1f293b21774b4134f1..2aee409589b5ecf97a4da7d8b65a206a917c5954 100644 (file)
@@ -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);
 
index 98b2e1e67a019b32e3c8a9d2f68cea5d5d241bef..9f164656abd602d0b4fe414664bb51c498a62553 100644 (file)
@@ -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();
 }
index 494b179bfde97ccb93c8969ce066f1b5860b5097..3edc4cf87ade8c8158eed5f01f9500944ae7c264 100644 (file)
@@ -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;