/src/clib/deps/commander/commander.c
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | // |
3 | | // commander.c |
4 | | // |
5 | | // Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca> |
6 | | // |
7 | | |
8 | | #include <stdio.h> |
9 | | #include <stdlib.h> |
10 | | #include <string.h> |
11 | | #include <assert.h> |
12 | | #include "commander.h" |
13 | | |
14 | | /* |
15 | | * Output error and exit. |
16 | | */ |
17 | | |
18 | | static void |
19 | 0 | error(char *msg) { |
20 | 0 | fprintf(stderr, "%s\n", msg); |
21 | 0 | exit(1); |
22 | 0 | } |
23 | | |
24 | | /* |
25 | | * Output command version. |
26 | | */ |
27 | | |
28 | | static void |
29 | 0 | command_version(command_t *self) { |
30 | 0 | printf("%s\n", self->version); |
31 | 0 | command_free(self); |
32 | 0 | exit(0); |
33 | 0 | } |
34 | | |
35 | | /* |
36 | | * Output command help. |
37 | | */ |
38 | | |
39 | | void |
40 | 0 | command_help(command_t *self) { |
41 | 0 | printf("\n"); |
42 | 0 | printf(" Usage: %s %s\n", self->name, self->usage); |
43 | 0 | printf("\n"); |
44 | 0 | printf(" Options:\n"); |
45 | 0 | printf("\n"); |
46 | |
|
47 | 0 | int i; |
48 | 0 | for (i = 0; i < self->option_count; ++i) { |
49 | 0 | command_option_t *option = &self->options[i]; |
50 | 0 | printf(" %s, %-25s %s\n" |
51 | 0 | , option->small |
52 | 0 | , option->large_with_arg |
53 | 0 | , option->description); |
54 | 0 | } |
55 | |
|
56 | 0 | printf("\n"); |
57 | 0 | command_free(self); |
58 | 0 | exit(0); |
59 | 0 | } |
60 | | |
61 | | /* |
62 | | * Initialize with program `name` and `version`. |
63 | | */ |
64 | | |
65 | | void |
66 | 0 | command_init(command_t *self, const char *name, const char *version) { |
67 | 0 | self->arg = NULL; |
68 | 0 | self->name = name; |
69 | 0 | self->version = version; |
70 | 0 | self->option_count = self->argc = 0; |
71 | 0 | self->usage = "[options]"; |
72 | 0 | self->nargv = NULL; |
73 | 0 | command_option(self, "-V", "--version", "output program version", command_version); |
74 | 0 | command_option(self, "-h", "--help", "output help information", command_help); |
75 | 0 | } |
76 | | |
77 | | /* |
78 | | * Free up commander after use. |
79 | | */ |
80 | | |
81 | | void |
82 | 0 | command_free(command_t *self) { |
83 | 0 | int i; |
84 | |
|
85 | 0 | for (i = 0; i < self->option_count; ++i) { |
86 | 0 | command_option_t *option = &self->options[i]; |
87 | 0 | free(option->argname); |
88 | 0 | free(option->large); |
89 | 0 | } |
90 | |
|
91 | 0 | if (self->nargv) { |
92 | 0 | for (i = 0; self->nargv[i]; ++i) { |
93 | 0 | free(self->nargv[i]); |
94 | 0 | } |
95 | 0 | free(self->nargv); |
96 | 0 | } |
97 | 0 | } |
98 | | |
99 | | /* |
100 | | * Parse argname from `str`. For example |
101 | | * Take "--required <arg>" and populate `flag` |
102 | | * with "--required" and `arg` with "<arg>". |
103 | | */ |
104 | | |
105 | | static void |
106 | 0 | parse_argname(const char *str, char *flag, char *arg) { |
107 | 0 | int buffer = 0; |
108 | 0 | size_t flagpos = 0; |
109 | 0 | size_t argpos = 0; |
110 | 0 | size_t len = strlen(str); |
111 | 0 | size_t i; |
112 | |
|
113 | 0 | for (i = 0; i < len; ++i) { |
114 | 0 | if (buffer || '[' == str[i] || '<' == str[i]) { |
115 | 0 | buffer = 1; |
116 | 0 | arg[argpos++] = str[i]; |
117 | 0 | } else { |
118 | 0 | if (' ' == str[i]) continue; |
119 | 0 | flag[flagpos++] = str[i]; |
120 | 0 | } |
121 | 0 | } |
122 | |
|
123 | 0 | arg[argpos] = '\0'; |
124 | 0 | flag[flagpos] = '\0'; |
125 | 0 | } |
126 | | |
127 | | /* |
128 | | * Normalize the argument vector by exploding |
129 | | * multiple options (if any). For example |
130 | | * "foo -abc --scm git" -> "foo -a -b -c --scm git" |
131 | | */ |
132 | | |
133 | | static char ** |
134 | 0 | normalize_args(int *argc, char **argv) { |
135 | 0 | int size = 0; |
136 | 0 | int alloc = *argc + 1; |
137 | 0 | char **nargv = malloc(alloc * sizeof(char *)); |
138 | 0 | int i; |
139 | |
|
140 | 0 | for (i = 0; argv[i]; ++i) { |
141 | 0 | const char *arg = argv[i]; |
142 | 0 | size_t len = strlen(arg); |
143 | | |
144 | | // short flag |
145 | 0 | if (len > 2 && '-' == arg[0] && !strchr(arg + 1, '-')) { |
146 | 0 | alloc += len - 2; |
147 | 0 | nargv = realloc(nargv, alloc * sizeof(char *)); |
148 | 0 | for (size_t j = 1; j < len; ++j) { |
149 | 0 | nargv[size] = malloc(3); |
150 | 0 | sprintf(nargv[size], "-%c", arg[j]); |
151 | 0 | size++; |
152 | 0 | } |
153 | 0 | continue; |
154 | 0 | } |
155 | | |
156 | | // regular arg |
157 | 0 | nargv[size] = malloc(len + 1); |
158 | 0 | strcpy(nargv[size], arg); |
159 | 0 | size++; |
160 | 0 | } |
161 | |
|
162 | 0 | nargv[size] = NULL; |
163 | 0 | *argc = size; |
164 | 0 | return nargv; |
165 | 0 | } |
166 | | |
167 | | /* |
168 | | * Define an option. |
169 | | */ |
170 | | |
171 | | void |
172 | 0 | command_option(command_t *self, const char *small, const char *large, const char *desc, command_callback_t cb) { |
173 | 0 | if (self->option_count == COMMANDER_MAX_OPTIONS) { |
174 | 0 | command_free(self); |
175 | 0 | error("Maximum option definitions exceeded"); |
176 | 0 | } |
177 | 0 | int n = self->option_count++; |
178 | 0 | command_option_t *option = &self->options[n]; |
179 | 0 | option->cb = cb; |
180 | 0 | option->small = small; |
181 | 0 | option->description = desc; |
182 | 0 | option->required_arg = option->optional_arg = 0; |
183 | 0 | option->large_with_arg = large; |
184 | 0 | option->argname = malloc(strlen(large) + 1); |
185 | 0 | assert(option->argname); |
186 | 0 | option->large = malloc(strlen(large) + 1); |
187 | 0 | assert(option->large); |
188 | 0 | parse_argname(large, option->large, option->argname); |
189 | 0 | if ('[' == option->argname[0]) option->optional_arg = 1; |
190 | 0 | if ('<' == option->argname[0]) option->required_arg = 1; |
191 | 0 | } |
192 | | |
193 | | /* |
194 | | * Parse `argv` (internal). |
195 | | * Input arguments should be normalized first |
196 | | * see `normalize_args`. |
197 | | */ |
198 | | |
199 | | static void |
200 | 0 | command_parse_args(command_t *self, int argc, char **argv) { |
201 | 0 | int literal = 0; |
202 | 0 | int i, j; |
203 | |
|
204 | 0 | for (i = 1; i < argc; ++i) { |
205 | 0 | const char *arg = argv[i]; |
206 | 0 | for (j = 0; j < self->option_count; ++j) { |
207 | 0 | command_option_t *option = &self->options[j]; |
208 | | |
209 | | // match flag |
210 | 0 | if (!strcmp(arg, option->small) || !strcmp(arg, option->large)) { |
211 | 0 | self->arg = NULL; |
212 | | |
213 | | // required |
214 | 0 | if (option->required_arg) { |
215 | 0 | arg = argv[++i]; |
216 | 0 | if (!arg || '-' == arg[0]) { |
217 | 0 | fprintf(stderr, "%s %s argument required\n", option->large, option->argname); |
218 | 0 | command_free(self); |
219 | 0 | exit(1); |
220 | 0 | } |
221 | 0 | self->arg = arg; |
222 | 0 | } |
223 | | |
224 | | // optional |
225 | 0 | if (option->optional_arg) { |
226 | 0 | if (argv[i + 1] && '-' != argv[i + 1][0]) { |
227 | 0 | self->arg = argv[++i]; |
228 | 0 | } |
229 | 0 | } |
230 | | |
231 | | // invoke callback |
232 | 0 | option->cb(self); |
233 | 0 | goto match; |
234 | 0 | } |
235 | 0 | } |
236 | | |
237 | | // -- |
238 | 0 | if ('-' == arg[0] && '-' == arg[1] && 0 == arg[2]) { |
239 | 0 | literal = 1; |
240 | 0 | goto match; |
241 | 0 | } |
242 | | |
243 | | // unrecognized |
244 | 0 | if ('-' == arg[0] && !literal) { |
245 | 0 | fprintf(stderr, "unrecognized flag %s\n", arg); |
246 | 0 | command_free(self); |
247 | 0 | exit(1); |
248 | 0 | } |
249 | | |
250 | 0 | int n = self->argc++; |
251 | 0 | if (n == COMMANDER_MAX_ARGS) { |
252 | 0 | command_free(self); |
253 | 0 | error("Maximum number of arguments exceeded"); |
254 | 0 | } |
255 | 0 | self->argv[n] = (char *) arg; |
256 | 0 | match:; |
257 | 0 | } |
258 | 0 | } |
259 | | |
260 | | /* |
261 | | * Parse `argv` (public). |
262 | | */ |
263 | | |
264 | | void |
265 | 0 | command_parse(command_t *self, int argc, char **argv) { |
266 | 0 | self->nargv = normalize_args(&argc, argv); |
267 | 0 | command_parse_args(self, argc, self->nargv); |
268 | 0 | self->argv[self->argc] = NULL; |
269 | 0 | } |