Browse Source

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 <luke-jr+git@utopios.org>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Rusty Russell 12 years ago
parent
commit
8071ba125b
3 changed files with 79 additions and 30 deletions
  1. 14 0
      ccan/opt/opt.h
  2. 42 15
      ccan/opt/test/run-consume_words.c
  3. 23 15
      ccan/opt/usage.c

+ 14 - 0
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);
 

+ 42 - 15
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();
 }

+ 23 - 15
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;