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