X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Fopt%2Fusage.c;h=568e4661d8098bf1fd009a169867b0d369339fb2;hp=7c1971c2e48ed919db13625c027ffcfc328c8f6c;hb=HEAD;hpb=754891d3b9c41a6d0f38c638c261691a3fe2b36e diff --git a/ccan/opt/usage.c b/ccan/opt/usage.c index 7c1971c2..568e4661 100644 --- a/ccan/opt/usage.c +++ b/ccan/opt/usage.c @@ -1,12 +1,17 @@ -/* Licensed under GPLv3+ - see LICENSE file for details */ +/* Licensed under GPLv2+ - see LICENSE file for details */ #include +#if HAVE_SYS_TERMIOS_H #include #include /* Required on Solaris for struct winsize */ +#endif +#if HAVE_SYS_UNISTD_H #include /* Required on Solaris for ioctl */ +#endif #include #include #include #include +#include #include "private.h" /* We only use this for pointer comparisons. */ @@ -15,44 +20,63 @@ const char opt_hidden[1]; #define MIN_DESC_WIDTH 40 #define MIN_TOTAL_WIDTH 50 +/* Maximum length of arg to show in opt_usage */ +#define OPT_SHOW_LEN 80 + static unsigned int get_columns(void) { - struct winsize w; + int ws_col = 0; const char *env = getenv("COLUMNS"); - w.ws_col = 0; if (env) - w.ws_col = atoi(env); - if (!w.ws_col) - if (ioctl(0, TIOCGWINSZ, &w) == -1) - w.ws_col = 0; - if (!w.ws_col) - w.ws_col = 80; - - return w.ws_col; + ws_col = atoi(env); + +#ifdef TIOCGWINSZ + if (!ws_col) + { + struct winsize w; + if (ioctl(0, TIOCGWINSZ, &w) != -1) + ws_col = w.ws_col; + } +#endif + if (!ws_col) + ws_col = 80; + + return ws_col; } /* 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. */ - *prefix = strspn(words, " "); + /* 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, " "); - len += strcspn(words+len, " "); - 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; + } } + if (oldlen != 0) + *start = (words[oldlen - 1] == '\n'); return oldlen; } @@ -60,7 +84,7 @@ static char *add_str_len(char *base, size_t *len, size_t *max, const char *str, size_t slen) { if (slen >= *max - *len) - base = realloc(base, *max = (*max * 2 + slen + 1)); + base = opt_alloc.realloc(base, *max = (*max * 2 + slen + 1)); memcpy(base + *len, str, slen); *len += slen; return base; @@ -74,7 +98,7 @@ static char *add_str(char *base, size_t *len, size_t *max, const char *str) static char *add_indent(char *base, size_t *len, size_t *max, size_t indent) { if (indent >= *max - *len) - base = realloc(base, *max = (*max * 2 + indent + 1)); + base = opt_alloc.realloc(base, *max = (*max * 2 + indent + 1)); memset(base + *len, ' ', indent); *len += indent; return base; @@ -86,11 +110,11 @@ 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); - if (opt->type == OPT_HASARG + if ((opt->type & OPT_HASARG) && !strchr(opt->names, ' ') && !strchr(opt->names, '=')) { base = add_str(base, len, max, " "); @@ -109,7 +133,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; @@ -127,20 +151,20 @@ static char *add_desc(char *base, size_t *len, size_t *max, if (opt->show) { char buf[OPT_SHOW_LEN + sizeof("...")]; strcpy(buf + OPT_SHOW_LEN, "..."); - opt->show(buf, opt->u.arg); + if (opt->show(buf, OPT_SHOW_LEN, opt->u.arg)) { + /* If it doesn't fit on this line, indent. */ + if (off + strlen(" (default: ") + strlen(buf) + strlen(")") + > width) { + base = add_indent(base, len, max, indent); + } else { + /* Remove \n. */ + (*len)--; + } - /* If it doesn't fit on this line, indent. */ - if (off + strlen(" (default: ") + strlen(buf) + strlen(")") - > width) { - base = add_indent(base, len, max, indent); - } else { - /* Remove \n. */ - (*len)--; + base = add_str(base, len, max, " (default: "); + base = add_str(base, len, max, buf); + base = add_str(base, len, max, ")\n"); } - - base = add_str(base, len, max, " (default: "); - base = add_str(base, len, max, buf); - base = add_str(base, len, max, ")\n"); } return base; } @@ -161,10 +185,10 @@ char *opt_usage(const char *argv0, const char *extra) size_t l; if (opt_table[i].desc == opt_hidden) continue; - if (opt_table[i].type == OPT_SUBTABLE) + if (opt_table[i].type & OPT_SUBTABLE) continue; l = strlen(opt_table[i].names); - if (opt_table[i].type == OPT_HASARG + if ((opt_table[i].type & OPT_HASARG) && !strchr(opt_table[i].names, ' ') && !strchr(opt_table[i].names, '=')) l += strlen(" "); @@ -200,7 +224,7 @@ char *opt_usage(const char *argv0, const char *extra) for (i = 0; i < opt_count; i++) { if (opt_table[i].desc == opt_hidden) continue; - if (opt_table[i].type == OPT_SUBTABLE) { + if (opt_table[i].type & OPT_SUBTABLE) { ret = add_str(ret, &len, &max, opt_table[i].desc); ret = add_str(ret, &len, &max, ":\n"); continue; @@ -210,3 +234,17 @@ char *opt_usage(const char *argv0, const char *extra) ret[len] = '\0'; return ret; } + +void opt_usage_exit_fail(const char *msg, ...) +{ + va_list ap; + + if (opt_argv0) + fprintf(stderr, "%s: ", opt_argv0); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); + fprintf(stderr, "\n%s", + opt_usage(opt_argv0 ? opt_argv0 : "", NULL)); + exit(1); +}