tdb2: copy tdb1's changed expansion logic.
[ccan] / ccan / opt / opt.c
1 /* Licensed under GPLv3+ - see LICENSE file for details */
2 #include <ccan/opt/opt.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <err.h>
8 #include <assert.h>
9 #include <stdarg.h>
10 #include <stdint.h>
11 #include "private.h"
12
13 struct opt_table *opt_table;
14 unsigned int opt_count, opt_num_short, opt_num_short_arg, opt_num_long;
15 const char *opt_argv0;
16
17 /* Returns string after first '-'. */
18 static const char *first_name(const char *names, unsigned *len)
19 {
20         *len = strcspn(names + 1, "|= ");
21         return names + 1;
22 }
23
24 static const char *next_name(const char *names, unsigned *len)
25 {
26         names += *len;
27         if (names[0] == ' ' || names[0] == '=' || names[0] == '\0')
28                 return NULL;
29         return first_name(names + 1, len);
30 }
31
32 static const char *first_opt(unsigned *i, unsigned *len)
33 {
34         for (*i = 0; *i < opt_count; (*i)++) {
35                 if (opt_table[*i].type == OPT_SUBTABLE)
36                         continue;
37                 return first_name(opt_table[*i].names, len);
38         }
39         return NULL;
40 }
41
42 static const char *next_opt(const char *p, unsigned *i, unsigned *len)
43 {
44         for (; *i < opt_count; (*i)++) {
45                 if (opt_table[*i].type == OPT_SUBTABLE)
46                         continue;
47                 if (!p)
48                         return first_name(opt_table[*i].names, len);
49                 p = next_name(p, len);
50                 if (p)
51                         return p;
52         }
53         return NULL;
54 }
55
56 const char *first_lopt(unsigned *i, unsigned *len)
57 {
58         const char *p;
59         for (p = first_opt(i, len); p; p = next_opt(p, i, len)) {
60                 if (p[0] == '-') {
61                         /* Skip leading "-" */
62                         (*len)--;
63                         p++;
64                         break;
65                 }
66         }
67         return p;
68 }
69
70 const char *next_lopt(const char *p, unsigned *i, unsigned *len)
71 {
72         for (p = next_opt(p, i, len); p; p = next_opt(p, i, len)) {
73                 if (p[0] == '-') {
74                         /* Skip leading "-" */
75                         (*len)--;
76                         p++;
77                         break;
78                 }
79         }
80         return p;
81 }
82
83 const char *first_sopt(unsigned *i)
84 {
85         const char *p;
86         unsigned int len = 0 /* GCC bogus warning */;
87
88         for (p = first_opt(i, &len); p; p = next_opt(p, i, &len)) {
89                 if (p[0] != '-')
90                         break;
91         }
92         return p;
93 }
94
95 const char *next_sopt(const char *p, unsigned *i)
96 {
97         unsigned int len = 1;
98         for (p = next_opt(p, i, &len); p; p = next_opt(p, i, &len)) {
99                 if (p[0] != '-')
100                         break;
101         }
102         return p;
103 }
104
105 static void check_opt(const struct opt_table *entry)
106 {
107         const char *p;
108         unsigned len;
109
110         if (entry->type != OPT_HASARG && entry->type != OPT_NOARG
111             && entry->type != (OPT_EARLY|OPT_HASARG)
112             && entry->type != (OPT_EARLY|OPT_NOARG))
113                 errx(1, "Option %s: unknown entry type %u",
114                      entry->names, entry->type);
115
116         if (!entry->desc)
117                 errx(1, "Option %s: description cannot be NULL", entry->names);
118
119
120         if (entry->names[0] != '-')
121                 errx(1, "Option %s: does not begin with '-'", entry->names);
122
123         for (p = first_name(entry->names, &len); p; p = next_name(p, &len)) {
124                 if (*p == '-') {
125                         if (len == 1)
126                                 errx(1, "Option %s: invalid long option '--'",
127                                      entry->names);
128                         opt_num_long++;
129                 } else {
130                         if (len != 1)
131                                 errx(1, "Option %s: invalid short option"
132                                      " '%.*s'", entry->names, len+1, p-1);
133                         opt_num_short++;
134                         if (entry->type == OPT_HASARG)
135                                 opt_num_short_arg++;
136                 }
137                 /* Don't document args unless there are some. */
138                 if (entry->type == OPT_NOARG) {
139                         if (p[len] == ' ' || p[len] == '=')
140                                 errx(1, "Option %s: does not take arguments"
141                                      " '%s'", entry->names, p+len+1);
142                 }
143         }
144 }
145
146 static void add_opt(const struct opt_table *entry)
147 {
148         opt_table = realloc(opt_table, sizeof(opt_table[0]) * (opt_count+1));
149         opt_table[opt_count++] = *entry;
150 }
151
152 void _opt_register(const char *names, enum opt_type type,
153                    char *(*cb)(void *arg),
154                    char *(*cb_arg)(const char *optarg, void *arg),
155                    void (*show)(char buf[OPT_SHOW_LEN], const void *arg),
156                    const void *arg, const char *desc)
157 {
158         struct opt_table opt;
159         opt.names = names;
160         opt.type = type;
161         opt.cb = cb;
162         opt.cb_arg = cb_arg;
163         opt.show = show;
164         opt.u.carg = arg;
165         opt.desc = desc;
166         check_opt(&opt);
167         add_opt(&opt);
168 }
169
170 void opt_register_table(const struct opt_table entry[], const char *desc)
171 {
172         unsigned int i, start = opt_count;
173
174         if (desc) {
175                 struct opt_table heading = OPT_SUBTABLE(NULL, desc);
176                 add_opt(&heading);
177         }
178         for (i = 0; entry[i].type != OPT_END; i++) {
179                 if (entry[i].type == OPT_SUBTABLE)
180                         opt_register_table(subtable_of(&entry[i]),
181                                            entry[i].desc);
182                 else {
183                         check_opt(&entry[i]);
184                         add_opt(&entry[i]);
185                 }
186         }
187         /* We store the table length in arg ptr. */
188         if (desc)
189                 opt_table[start].u.tlen = (opt_count - start);
190 }
191
192 /* Parse your arguments. */
193 bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...))
194 {
195         int ret;
196         unsigned offset = 0;
197
198         /* This helps opt_usage. */
199         opt_argv0 = argv[0];
200
201         while ((ret = parse_one(argc, argv, 0, &offset, errlog)) == 1);
202
203         /* parse_one returns 0 on finish, -1 on error */
204         return (ret == 0);
205 }
206
207 bool opt_early_parse(int argc, char *argv[],
208                      void (*errlog)(const char *fmt, ...))
209 {
210         int ret;
211         unsigned off = 0;
212         char **tmpargv = malloc(sizeof(argv[0]) * (argc + 1));
213
214         /* We could avoid a copy and skip instead, but this is simple. */
215         memcpy(tmpargv, argv, sizeof(argv[0]) * (argc + 1));
216
217         /* This helps opt_usage. */
218         opt_argv0 = argv[0];
219
220         while ((ret = parse_one(&argc, tmpargv, OPT_EARLY, &off, errlog)) == 1);
221
222         free(tmpargv);
223
224         /* parse_one returns 0 on finish, -1 on error */
225         return (ret == 0);
226 }
227
228 void opt_free_table(void)
229 {
230         free(opt_table);
231         opt_table = NULL;
232         opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
233 }
234
235 void opt_log_stderr(const char *fmt, ...)
236 {
237         va_list ap;
238
239         va_start(ap, fmt);
240         vfprintf(stderr, fmt, ap);
241         fprintf(stderr, "\n");
242         va_end(ap);
243 }
244
245 void opt_log_stderr_exit(const char *fmt, ...)
246 {
247         va_list ap;
248
249         va_start(ap, fmt);
250         vfprintf(stderr, fmt, ap);
251         fprintf(stderr, "\n");
252         va_end(ap);
253         exit(1);
254 }
255
256 char *opt_invalid_argument(const char *arg)
257 {
258         char *str = malloc(sizeof("Invalid argument '%s'") + strlen(arg));
259         sprintf(str, "Invalid argument '%s'", arg);
260         return str;
261 }