85de935a7d3b77fc2591b1435b1b38e79ff741cf
[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 <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
118         if (entry->type != OPT_HASARG && entry->type != OPT_NOARG
119             && entry->type != (OPT_EARLY|OPT_HASARG)
120             && entry->type != (OPT_EARLY|OPT_NOARG))
121                 failmsg("Option %s: unknown entry type %u",
122                         entry->names, entry->type);
123
124         if (!entry->desc)
125                 failmsg("Option %s: description cannot be NULL", entry->names);
126
127
128         if (entry->names[0] != '-')
129                 failmsg("Option %s: does not begin with '-'", entry->names);
130
131         for (p = first_name(entry->names, &len); p; p = next_name(p, &len)) {
132                 if (*p == '-') {
133                         if (len == 1)
134                                 failmsg("Option %s: invalid long option '--'",
135                                         entry->names);
136                         opt_num_long++;
137                 } else {
138                         if (len != 1)
139                                 failmsg("Option %s: invalid short option"
140                                         " '%.*s'", entry->names, len+1, p-1);
141                         opt_num_short++;
142                         if (entry->type == OPT_HASARG)
143                                 opt_num_short_arg++;
144                 }
145                 /* Don't document args unless there are some. */
146                 if (entry->type == OPT_NOARG) {
147                         if (p[len] == ' ' || p[len] == '=')
148                                 failmsg("Option %s: does not take arguments"
149                                         " '%s'", entry->names, p+len+1);
150                 }
151         }
152 }
153
154 static void add_opt(const struct opt_table *entry)
155 {
156         opt_table = opt_alloc.realloc(opt_table,
157                                       sizeof(opt_table[0]) * (opt_count+1));
158         opt_table[opt_count++] = *entry;
159 }
160
161 void _opt_register(const char *names, enum opt_type type,
162                    char *(*cb)(void *arg),
163                    char *(*cb_arg)(const char *optarg, void *arg),
164                    void (*show)(char buf[OPT_SHOW_LEN], const void *arg),
165                    const void *arg, const char *desc)
166 {
167         struct opt_table opt;
168         opt.names = names;
169         opt.type = type;
170         opt.cb = cb;
171         opt.cb_arg = cb_arg;
172         opt.show = show;
173         opt.u.carg = arg;
174         opt.desc = desc;
175         check_opt(&opt);
176         add_opt(&opt);
177 }
178
179 void opt_register_table(const struct opt_table entry[], const char *desc)
180 {
181         unsigned int i, start = opt_count;
182
183         if (desc) {
184                 struct opt_table heading = OPT_SUBTABLE(NULL, desc);
185                 add_opt(&heading);
186         }
187         for (i = 0; entry[i].type != OPT_END; i++) {
188                 if (entry[i].type == OPT_SUBTABLE)
189                         opt_register_table(subtable_of(&entry[i]),
190                                            entry[i].desc);
191                 else {
192                         check_opt(&entry[i]);
193                         add_opt(&entry[i]);
194                 }
195         }
196         /* We store the table length in arg ptr. */
197         if (desc)
198                 opt_table[start].u.tlen = (opt_count - start);
199 }
200
201 /* Parse your arguments. */
202 bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...))
203 {
204         int ret;
205         unsigned offset = 0;
206
207         /* This helps opt_usage. */
208         opt_argv0 = argv[0];
209
210         while ((ret = parse_one(argc, argv, 0, &offset, errlog)) == 1);
211
212         /* parse_one returns 0 on finish, -1 on error */
213         return (ret == 0);
214 }
215
216 bool opt_early_parse(int argc, char *argv[],
217                      void (*errlog)(const char *fmt, ...))
218 {
219         int ret;
220         unsigned off = 0;
221         char **tmpargv = opt_alloc.alloc(sizeof(argv[0]) * (argc + 1));
222
223         /* We could avoid a copy and skip instead, but this is simple. */
224         memcpy(tmpargv, argv, sizeof(argv[0]) * (argc + 1));
225
226         /* This helps opt_usage. */
227         opt_argv0 = argv[0];
228
229         while ((ret = parse_one(&argc, tmpargv, OPT_EARLY, &off, errlog)) == 1);
230
231         opt_alloc.free(tmpargv);
232
233         /* parse_one returns 0 on finish, -1 on error */
234         return (ret == 0);
235 }
236
237 void opt_free_table(void)
238 {
239         opt_alloc.free(opt_table);
240         opt_table = NULL;
241         opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
242 }
243
244 void opt_log_stderr(const char *fmt, ...)
245 {
246         va_list ap;
247
248         va_start(ap, fmt);
249         vfprintf(stderr, fmt, ap);
250         fprintf(stderr, "\n");
251         va_end(ap);
252 }
253
254 void opt_log_stderr_exit(const char *fmt, ...)
255 {
256         va_list ap;
257
258         va_start(ap, fmt);
259         vfprintf(stderr, fmt, ap);
260         fprintf(stderr, "\n");
261         va_end(ap);
262         exit(1);
263 }
264
265 char *opt_invalid_argument(const char *arg)
266 {
267         char *str = opt_alloc.alloc(sizeof("Invalid argument '%s'") + strlen(arg));
268         sprintf(str, "Invalid argument '%s'", arg);
269         return str;
270 }
271
272 void opt_set_alloc(void *(*allocfn)(size_t size),
273                    void *(*reallocfn)(void *ptr, size_t size),
274                    void (*freefn)(void *ptr))
275 {
276         opt_alloc.alloc = allocfn;
277         opt_alloc.realloc = reallocfn;
278         opt_alloc.free = freefn;
279 }