opt: new module to parse commandline options.
[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 /* FIXME: Get all purdy. */
29 char *opt_usage(const char *argv0, const char *extra)
30 {
31         unsigned int i, num, len;
32         char *ret, *p;
33
34         /* An overestimate of our length. */
35         len = strlen("Usage: %s ") + strlen(argv0)
36                 + strlen("[-%.*s]") + opt_count + 1
37                 + strlen(" ") + strlen(extra)
38                 + strlen("\n");
39
40         for (i = 0; i < opt_count; i++) {
41                 if (opt_table[i].flags == OPT_SUBTABLE) {
42                         len += strlen("\n") + strlen(opt_table[i].desc)
43                                 + strlen(":\n");
44                 } else {
45                         len += strlen("--%s/-%c") + strlen(" <arg>");
46                         if (opt_table[i].longopt)
47                                 len += strlen(opt_table[i].longopt);
48                         if (opt_table[i].desc)
49                                 len += 20 + strlen(opt_table[i].desc);
50                         len += strlen("\n");
51                 }
52         }
53
54         p = ret = malloc(len);
55         if (!ret)
56                 return NULL;
57
58         p += sprintf(p, "Usage: %s", argv0);
59         p += sprintf(p, " [-");
60         num = write_short_options(p);
61         if (num) {
62                 p += num;
63                 p += sprintf(p, "]");
64         } else {
65                 /* Remove start of single-entry options */
66                 p -= 3;
67         }
68         if (extra)
69                 p += sprintf(p, " %s", extra);
70         p += sprintf(p, "\n");
71
72         for (i = 0; i < opt_count; i++) {
73                 if (opt_table[i].flags == OPT_SUBTABLE) {
74                         if (opt_table[i].desc == opt_table_hidden) {
75                                 /* Skip these options. */
76                                 i += (intptr_t)opt_table[i].arg - 1;
77                                 continue;
78                         }
79                         p += sprintf(p, "%s:\n", opt_table[i].desc);
80                         continue;
81                 }
82                 if (opt_table[i].shortopt && opt_table[i].longopt)
83                         len = sprintf(p, "--%s/-%c",
84                                      opt_table[i].longopt,
85                                       opt_table[i].shortopt);
86                 else if (opt_table[i].shortopt)
87                         len = sprintf(p, "-%c", opt_table[i].shortopt);
88                 else
89                         len = sprintf(p, "--%s", opt_table[i].longopt);
90                 if (opt_table[i].flags == OPT_HASARG)
91                         len += sprintf(p + len, " <arg>");
92                 if (opt_table[i].desc) {
93                         len += sprintf(p + len, "%.*s",
94                                        len < 20 ? 20 - len : 1,
95                                        "                    ");
96                         len += sprintf(p + len, "%s", opt_table[i].desc);
97                 }
98                 p += len;
99                 p += sprintf(p, "\n");
100         }
101         *p = '\0';
102         return ret;
103 }