d76c3b03ed317e3306f4ccad739ccdcac2fbdd9d
[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_table_hidden[1];
10
11 static unsigned write_short_options(char *str)
12 {
13         unsigned int i, num = 0;
14
15         for (i = 0; i < opt_count; i++) {
16                 if (opt_table[i].flags == OPT_SUBTABLE) {
17                         if (opt_table[i].desc == opt_table_hidden) {
18                                 /* Skip these options. */
19                                 i += (intptr_t)opt_table[i].arg - 1;
20                                 continue;
21                         }
22                 } else if (opt_table[i].shortopt)
23                         str[num++] = opt_table[i].shortopt;
24         }
25         return num;
26 }
27
28 #define OPT_SPACE_PAD "                    "
29
30 /* FIXME: Get all purdy. */
31 char *opt_usage(const char *argv0, const char *extra)
32 {
33         unsigned int i, num, len;
34         char *ret, *p;
35
36         /* An overestimate of our length. */
37         len = strlen("Usage: %s ") + strlen(argv0)
38                 + strlen("[-%.*s]") + opt_count + 1
39                 + strlen(" ") + strlen(extra)
40                 + strlen("\n");
41
42         for (i = 0; i < opt_count; i++) {
43                 if (opt_table[i].flags == OPT_SUBTABLE) {
44                         len += strlen("\n") + strlen(opt_table[i].desc)
45                                 + strlen(":\n");
46                 } else {
47                         len += strlen("--%s/-%c") + strlen(" <arg>");
48                         if (opt_table[i].longopt)
49                                 len += strlen(opt_table[i].longopt);
50                         if (opt_table[i].desc) {
51                                 len += strlen(OPT_SPACE_PAD)
52                                         + strlen(opt_table[i].desc) + 1;
53                         }
54                         if (opt_table[i].show) {
55                                 len += strlen("(default: %s)")
56                                         + OPT_SHOW_LEN + sizeof("...");
57                         }
58                         len += strlen("\n");
59                 }
60         }
61
62         p = ret = malloc(len);
63         if (!ret)
64                 return NULL;
65
66         p += sprintf(p, "Usage: %s", argv0);
67         p += sprintf(p, " [-");
68         num = write_short_options(p);
69         if (num) {
70                 p += num;
71                 p += sprintf(p, "]");
72         } else {
73                 /* Remove start of single-entry options */
74                 p -= 3;
75         }
76         if (extra)
77                 p += sprintf(p, " %s", extra);
78         p += sprintf(p, "\n");
79
80         for (i = 0; i < opt_count; i++) {
81                 if (opt_table[i].flags == OPT_SUBTABLE) {
82                         if (opt_table[i].desc == opt_table_hidden) {
83                                 /* Skip these options. */
84                                 i += (intptr_t)opt_table[i].arg - 1;
85                                 continue;
86                         }
87                         p += sprintf(p, "%s:\n", opt_table[i].desc);
88                         continue;
89                 }
90                 if (opt_table[i].shortopt && opt_table[i].longopt)
91                         len = sprintf(p, "--%s/-%c",
92                                      opt_table[i].longopt,
93                                       opt_table[i].shortopt);
94                 else if (opt_table[i].shortopt)
95                         len = sprintf(p, "-%c", opt_table[i].shortopt);
96                 else
97                         len = sprintf(p, "--%s", opt_table[i].longopt);
98                 if (opt_table[i].flags == OPT_HASARG)
99                         len += sprintf(p + len, " <arg>");
100                 if (opt_table[i].desc || opt_table[i].show)
101                         len += sprintf(p + len, "%.*s",
102                                        len < strlen(OPT_SPACE_PAD)
103                                        ? strlen(OPT_SPACE_PAD) - len : 1,
104                                        OPT_SPACE_PAD);
105
106                 if (opt_table[i].desc)
107                         len += sprintf(p + len, "%s", opt_table[i].desc);
108                 if (opt_table[i].show) {
109                         char buf[OPT_SHOW_LEN + sizeof("...")];
110                         strcpy(buf + OPT_SHOW_LEN, "...");
111                         opt_table[i].show(buf, opt_table[i].arg);
112                         len += sprintf(p + len, "%s(default: %s)",
113                                        opt_table[i].desc ? " " : "", buf);
114                 }
115                 p += len;
116                 p += sprintf(p, "\n");
117         }
118         *p = '\0';
119         return ret;
120 }