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>
* 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);
/* 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();
}
/* 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;
}
{
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);
/* 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;