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