opt: add opt_log_stderr_exit helper, and opt_usage NULL option.
[ccan] / ccan / opt / usage.c
1 #include <ccan/opt/opt.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <stdint.h>
6 #include "private.h"
7
8 /* We only use this for pointer comparisons. */
9 const char opt_hidden[1];
10
11 static unsigned write_short_options(char *str)
12 {
13         unsigned int i, num = 0;
14         const char *p;
15
16         for (p = first_sopt(&i); p; p = next_sopt(p, &i)) {
17                 if (opt_table[i].desc != opt_hidden)
18                         str[num++] = *p;
19         }
20         return num;
21 }
22
23 #define OPT_SPACE_PAD "                    "
24
25 /* FIXME: Get all purdy. */
26 char *opt_usage(const char *argv0, const char *extra)
27 {
28         unsigned int i, num, len;
29         char *ret, *p;
30
31         if (!extra) {
32                 extra = "";
33                 for (i = 0; i < opt_count; i++) {
34                         if (opt_table[i].cb == (void *)opt_usage_and_exit
35                             && opt_table[i].arg) {
36                                 extra = opt_table[i].arg;
37                                 break;
38                         }
39                 }
40         }
41
42         /* An overestimate of our length. */
43         len = strlen("Usage: %s ") + strlen(argv0)
44                 + strlen("[-%.*s]") + opt_num_short + 1
45                 + strlen(" ") + strlen(extra)
46                 + strlen("\n");
47
48         for (i = 0; i < opt_count; i++) {
49                 if (opt_table[i].type == OPT_SUBTABLE) {
50                         len += strlen("\n") + strlen(opt_table[i].desc)
51                                 + strlen(":\n");
52                 } else if (opt_table[i].desc != opt_hidden) {
53                         len += strlen(opt_table[i].names) + strlen(" <arg>");
54                         len += strlen(OPT_SPACE_PAD)
55                                 + strlen(opt_table[i].desc) + 1;
56                         if (opt_table[i].show) {
57                                 len += strlen("(default: %s)")
58                                         + OPT_SHOW_LEN + sizeof("...");
59                         }
60                         len += strlen("\n");
61                 }
62         }
63
64         p = ret = malloc(len);
65         if (!ret)
66                 return NULL;
67
68         p += sprintf(p, "Usage: %s", argv0);
69         p += sprintf(p, " [-");
70         num = write_short_options(p);
71         if (num) {
72                 p += num;
73                 p += sprintf(p, "]");
74         } else {
75                 /* Remove start of single-entry options */
76                 p -= 3;
77         }
78         if (extra)
79                 p += sprintf(p, " %s", extra);
80         p += sprintf(p, "\n");
81
82         for (i = 0; i < opt_count; i++) {
83                 if (opt_table[i].desc == opt_hidden)
84                         continue;
85                 if (opt_table[i].type == OPT_SUBTABLE) {
86                         p += sprintf(p, "%s:\n", opt_table[i].desc);
87                         continue;
88                 }
89                 len = sprintf(p, "%s", opt_table[i].names);
90                 if (opt_table[i].type == OPT_HASARG
91                     && !strchr(opt_table[i].names, ' ')
92                     && !strchr(opt_table[i].names, '='))
93                         len += sprintf(p + len, " <arg>");
94                 len += sprintf(p + len, "%.*s",
95                                len < strlen(OPT_SPACE_PAD)
96                                ? strlen(OPT_SPACE_PAD) - len : 1,
97                                OPT_SPACE_PAD);
98
99                 len += sprintf(p + len, "%s", opt_table[i].desc);
100                 if (opt_table[i].show) {
101                         char buf[OPT_SHOW_LEN + sizeof("...")];
102                         strcpy(buf + OPT_SHOW_LEN, "...");
103                         opt_table[i].show(buf, opt_table[i].arg);
104                         len += sprintf(p + len, " (default: %s)", buf);
105                 }
106                 p += len;
107                 p += sprintf(p, "\n");
108         }
109         *p = '\0';
110         return ret;
111 }