1 /* Licensed under GPLv2+ - see LICENSE file for details */
2 #include <ccan/opt/opt.h>
5 #include <sys/termios.h> /* Required on Solaris for struct winsize */
8 #include <sys/unistd.h> /* Required on Solaris for ioctl */
17 /* We only use this for pointer comparisons. */
18 const char opt_hidden[1];
20 #define MIN_DESC_WIDTH 40
21 #define MIN_TOTAL_WIDTH 50
23 /* Maximum length of arg to show in opt_usage */
24 #define OPT_SHOW_LEN 80
26 static unsigned int get_columns(void)
29 const char *env = getenv("COLUMNS");
38 if (ioctl(0, TIOCGWINSZ, &w) != -1)
48 /* Return number of chars of words to put on this line.
49 * Prefix is set to number to skip at start, maxlen is max width, returns
50 * length (after prefix) to put on this line.
51 * start is set if we start a new line in the source description. */
52 static size_t consume_words(const char *words, size_t maxlen, size_t *prefix,
57 /* Always swollow leading whitespace. */
58 *prefix = strspn(words, " \n");
61 /* Leading whitespace at start of line means literal. */
62 if (*start && *prefix) {
63 oldlen = strcspn(words, "\n");
65 /* Use at least one word, even if it takes us over maxlen. */
66 oldlen = len = strcspn(words, " ");
67 while (len <= maxlen) {
69 len += strspn(words+len, " ");
70 if (words[len] == '\n')
72 len += strcspn(words+len, " \n");
79 *start = (words[oldlen - 1] == '\n');
83 static char *add_str_len(char *base, size_t *len, size_t *max,
84 const char *str, size_t slen)
86 if (slen >= *max - *len)
87 base = opt_alloc.realloc(base, *max = (*max * 2 + slen + 1));
88 memcpy(base + *len, str, slen);
93 static char *add_str(char *base, size_t *len, size_t *max, const char *str)
95 return add_str_len(base, len, max, str, strlen(str));
98 static char *add_indent(char *base, size_t *len, size_t *max, size_t indent)
100 if (indent >= *max - *len)
101 base = opt_alloc.realloc(base, *max = (*max * 2 + indent + 1));
102 memset(base + *len, ' ', indent);
107 static char *add_desc(char *base, size_t *len, size_t *max,
108 unsigned int indent, unsigned int width,
109 const struct opt_table *opt)
111 size_t off, prefix, l;
113 bool same_line = false, start = true;
115 base = add_str(base, len, max, opt->names);
116 off = strlen(opt->names);
117 if ((opt->type & OPT_HASARG)
118 && !strchr(opt->names, ' ')
119 && !strchr(opt->names, '=')) {
120 base = add_str(base, len, max, " <arg>");
121 off += strlen(" <arg>");
124 /* Do we start description on next line? */
125 if (off + 2 > indent) {
126 base = add_str(base, len, max, "\n");
129 base = add_indent(base, len, max, indent - off);
134 /* Indent description. */
136 while ((l = consume_words(p, width - indent, &prefix, &start)) != 0) {
138 base = add_indent(base, len, max, indent);
140 base = add_str_len(base, len, max, p, l);
141 base = add_str(base, len, max, "\n");
147 /* Empty description? Make it match normal case. */
149 base = add_str(base, len, max, "\n");
152 char buf[OPT_SHOW_LEN + sizeof("...")];
153 strcpy(buf + OPT_SHOW_LEN, "...");
154 if (opt->show(buf, OPT_SHOW_LEN, opt->u.arg)) {
155 /* If it doesn't fit on this line, indent. */
156 if (off + strlen(" (default: ") + strlen(buf) + strlen(")")
158 base = add_indent(base, len, max, indent);
164 base = add_str(base, len, max, " (default: ");
165 base = add_str(base, len, max, buf);
166 base = add_str(base, len, max, ")\n");
172 char *opt_usage(const char *argv0, const char *extra)
175 size_t max, len, width, indent;
178 width = get_columns();
179 if (width < MIN_TOTAL_WIDTH)
180 width = MIN_TOTAL_WIDTH;
182 /* Figure out longest option. */
184 for (i = 0; i < opt_count; i++) {
186 if (opt_table[i].desc == opt_hidden)
188 if (opt_table[i].type & OPT_SUBTABLE)
190 l = strlen(opt_table[i].names);
191 if ((opt_table[i].type & OPT_HASARG)
192 && !strchr(opt_table[i].names, ' ')
193 && !strchr(opt_table[i].names, '='))
194 l += strlen(" <arg>");
199 /* Now we know how much to indent */
200 if (indent + MIN_DESC_WIDTH > width)
201 indent = width - MIN_DESC_WIDTH;
206 ret = add_str(ret, &len, &max, "Usage: ");
207 ret = add_str(ret, &len, &max, argv0);
209 /* Find usage message from among registered options if necessary. */
212 for (i = 0; i < opt_count; i++) {
213 if (opt_table[i].cb == (void *)opt_usage_and_exit
214 && opt_table[i].u.carg) {
215 extra = opt_table[i].u.carg;
220 ret = add_str(ret, &len, &max, " ");
221 ret = add_str(ret, &len, &max, extra);
222 ret = add_str(ret, &len, &max, "\n");
224 for (i = 0; i < opt_count; i++) {
225 if (opt_table[i].desc == opt_hidden)
227 if (opt_table[i].type & OPT_SUBTABLE) {
228 ret = add_str(ret, &len, &max, opt_table[i].desc);
229 ret = add_str(ret, &len, &max, ":\n");
232 ret = add_desc(ret, &len, &max, indent, width, &opt_table[i]);
238 void opt_usage_exit_fail(const char *msg, ...)
243 fprintf(stderr, "%s: ", opt_argv0);
245 vfprintf(stderr, msg, ap);
247 fprintf(stderr, "\n%s",
248 opt_usage(opt_argv0 ? opt_argv0 : "<program>", NULL));