-/* Licensed under GPLv3+ - see LICENSE file for details */
+/* Licensed under GPLv2+ - see LICENSE file for details */
#include <ccan/opt/opt.h>
+#if HAVE_SYS_TERMIOS_H
#include <sys/ioctl.h>
#include <sys/termios.h> /* Required on Solaris for struct winsize */
+#endif
+#if HAVE_SYS_UNISTD_H
#include <sys/unistd.h> /* Required on Solaris for ioctl */
+#endif
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
+#include <stdarg.h>
#include "private.h"
/* We only use this for pointer comparisons. */
#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;
}
{
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, " <arg>");
/* 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;
if (opt->show) {
char buf[OPT_SHOW_LEN + sizeof("...")];
strcpy(buf + OPT_SHOW_LEN, "...");
- opt->show(buf, opt->u.arg);
+ 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(")")
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(" <arg>");
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;
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 : "<program>", NULL));
+ exit(1);
+}