db7686be10eba44a08cec27c945aff7002a5b0fb
[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                 errx(1, "Option %s: unknown entry type %u",
112                      entry->names, entry->type);
113
114         if (!entry->desc)
115                 errx(1, "Option %s: description cannot be NULL", entry->names);
116
117
118         if (entry->names[0] != '-')
119                 errx(1, "Option %s: does not begin with '-'", entry->names);
120
121         for (p = first_name(entry->names, &len); p; p = next_name(p, &len)) {
122                 if (*p == '-') {
123                         if (len == 1)
124                                 errx(1, "Option %s: invalid long option '--'",
125                                      entry->names);
126                         opt_num_long++;
127                 } else {
128                         if (len != 1)
129                                 errx(1, "Option %s: invalid short option"
130                                      " '%.*s'", entry->names, len+1, p-1);
131                         opt_num_short++;
132                         if (entry->type == OPT_HASARG)
133                                 opt_num_short_arg++;
134                 }
135                 /* Don't document args unless there are some. */
136                 if (entry->type == OPT_NOARG) {
137                         if (p[len] == ' ' || p[len] == '=')
138                                 errx(1, "Option %s: does not take arguments"
139                                      " '%s'", entry->names, p+len+1);
140                 }
141         }
142 }
143
144 static void add_opt(const struct opt_table *entry)
145 {
146         opt_table = realloc(opt_table, sizeof(opt_table[0]) * (opt_count+1));
147         opt_table[opt_count++] = *entry;
148 }
149
150 void _opt_register(const char *names, enum opt_type type,
151                    char *(*cb)(void *arg),
152                    char *(*cb_arg)(const char *optarg, void *arg),
153                    void (*show)(char buf[OPT_SHOW_LEN], const void *arg),
154                    const void *arg, const char *desc)
155 {
156         struct opt_table opt;
157         opt.names = names;
158         opt.type = type;
159         opt.cb = cb;
160         opt.cb_arg = cb_arg;
161         opt.show = show;
162         opt.u.carg = arg;
163         opt.desc = desc;
164         check_opt(&opt);
165         add_opt(&opt);
166 }
167
168 void opt_register_table(const struct opt_table entry[], const char *desc)
169 {
170         unsigned int i, start = opt_count;
171
172         if (desc) {
173                 struct opt_table heading = OPT_SUBTABLE(NULL, desc);
174                 add_opt(&heading);
175         }
176         for (i = 0; entry[i].type != OPT_END; i++) {
177                 if (entry[i].type == OPT_SUBTABLE)
178                         opt_register_table(subtable_of(&entry[i]),
179                                            entry[i].desc);
180                 else {
181                         check_opt(&entry[i]);
182                         add_opt(&entry[i]);
183                 }
184         }
185         /* We store the table length in arg ptr. */
186         if (desc)
187                 opt_table[start].u.tlen = (opt_count - start);
188 }
189
190 /* Parse your arguments. */
191 bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...))
192 {
193         int ret;
194         unsigned offset = 0;
195
196         /* This helps opt_usage. */
197         opt_argv0 = argv[0];
198
199         while ((ret = parse_one(argc, argv, &offset, errlog)) == 1);
200
201         /* parse_one returns 0 on finish, -1 on error */
202         return (ret == 0);
203 }
204
205 void opt_free_table(void)
206 {
207         free(opt_table);
208         opt_table = NULL;
209         opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
210 }
211
212 void opt_log_stderr(const char *fmt, ...)
213 {
214         va_list ap;
215
216         va_start(ap, fmt);
217         vfprintf(stderr, fmt, ap);
218         fprintf(stderr, "\n");
219         va_end(ap);
220 }
221
222 void opt_log_stderr_exit(const char *fmt, ...)
223 {
224         va_list ap;
225
226         va_start(ap, fmt);
227         vfprintf(stderr, fmt, ap);
228         fprintf(stderr, "\n");
229         va_end(ap);
230         exit(1);
231 }
232
233 char *opt_invalid_argument(const char *arg)
234 {
235         char *str = malloc(sizeof("Invalid argument '%s'") + strlen(arg));
236         sprintf(str, "Invalid argument '%s'", arg);
237         return str;
238 }