opt: allow parameter names in arguments.
[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         /* An overestimate of our length. */
32         len = strlen("Usage: %s ") + strlen(argv0)
33                 + strlen("[-%.*s]") + opt_num_short + 1
34                 + strlen(" ") + strlen(extra)
35                 + strlen("\n");
36
37         for (i = 0; i < opt_count; i++) {
38                 if (opt_table[i].flags == OPT_SUBTABLE) {
39                         len += strlen("\n") + strlen(opt_table[i].desc)
40                                 + strlen(":\n");
41                 } else if (opt_table[i].desc != opt_hidden) {
42                         len += strlen(opt_table[i].names) + strlen(" <arg>");
43                         if (opt_table[i].desc) {
44                                 len += strlen(OPT_SPACE_PAD)
45                                         + strlen(opt_table[i].desc) + 1;
46                         }
47                         if (opt_table[i].show) {
48                                 len += strlen("(default: %s)")
49                                         + OPT_SHOW_LEN + sizeof("...");
50                         }
51                         len += strlen("\n");
52                 }
53         }
54
55         p = ret = malloc(len);
56         if (!ret)
57                 return NULL;
58
59         p += sprintf(p, "Usage: %s", argv0);
60         p += sprintf(p, " [-");
61         num = write_short_options(p);
62         if (num) {
63                 p += num;
64                 p += sprintf(p, "]");
65         } else {
66                 /* Remove start of single-entry options */
67                 p -= 3;
68         }
69         if (extra)
70                 p += sprintf(p, " %s", extra);
71         p += sprintf(p, "\n");
72
73         for (i = 0; i < opt_count; i++) {
74                 if (opt_table[i].desc == opt_hidden)
75                         continue;
76                 if (opt_table[i].flags == OPT_SUBTABLE) {
77                         p += sprintf(p, "%s:\n", opt_table[i].desc);
78                         continue;
79                 }
80                 len = sprintf(p, "%s", opt_table[i].names);
81                 if (opt_table[i].flags == OPT_HASARG
82                     && !strchr(opt_table[i].names, ' ')
83                     && !strchr(opt_table[i].names, '='))
84                         len += sprintf(p + len, " <arg>");
85                 if (opt_table[i].desc || opt_table[i].show)
86                         len += sprintf(p + len, "%.*s",
87                                        len < strlen(OPT_SPACE_PAD)
88                                        ? strlen(OPT_SPACE_PAD) - len : 1,
89                                        OPT_SPACE_PAD);
90
91                 if (opt_table[i].desc)
92                         len += sprintf(p + len, "%s", opt_table[i].desc);
93                 if (opt_table[i].show) {
94                         char buf[OPT_SHOW_LEN + sizeof("...")];
95                         strcpy(buf + OPT_SHOW_LEN, "...");
96                         opt_table[i].show(buf, opt_table[i].arg);
97                         len += sprintf(p + len, "%s(default: %s)",
98                                        opt_table[i].desc ? " " : "", buf);
99                 }
100                 p += len;
101                 p += sprintf(p, "\n");
102         }
103         *p = '\0';
104         return ret;
105 }