Line data Source code
1 : /*
2 : *
3 : * Copyright 2015, Google Inc.
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions are
8 : * met:
9 : *
10 : * * Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : * * Redistributions in binary form must reproduce the above
13 : * copyright notice, this list of conditions and the following disclaimer
14 : * in the documentation and/or other materials provided with the
15 : * distribution.
16 : * * Neither the name of Google Inc. nor the names of its
17 : * contributors may be used to endorse or promote products derived from
18 : * this software without specific prior written permission.
19 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 : * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 : * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 : * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 : *
32 : */
33 :
34 : #include <grpc/support/cmdline.h>
35 :
36 : #include <limits.h>
37 : #include <stdio.h>
38 : #include <string.h>
39 :
40 : #include "src/core/support/string.h"
41 : #include <grpc/support/alloc.h>
42 : #include <grpc/support/log.h>
43 : #include <grpc/support/string_util.h>
44 :
45 : typedef enum { ARGTYPE_INT, ARGTYPE_BOOL, ARGTYPE_STRING } argtype;
46 :
47 : typedef struct arg {
48 : const char *name;
49 : const char *help;
50 : argtype type;
51 : void *value;
52 : struct arg *next;
53 : } arg;
54 :
55 : struct gpr_cmdline {
56 : const char *description;
57 : arg *args;
58 : const char *argv0;
59 :
60 : const char *extra_arg_name;
61 : const char *extra_arg_help;
62 : void (*extra_arg)(void *user_data, const char *arg);
63 : void *extra_arg_user_data;
64 :
65 : int (*state)(gpr_cmdline *cl, char *arg);
66 : arg *cur_arg;
67 :
68 : int survive_failure;
69 : };
70 :
71 : static int normal_state(gpr_cmdline *cl, char *arg);
72 :
73 25 : gpr_cmdline *gpr_cmdline_create(const char *description) {
74 25 : gpr_cmdline *cl = gpr_malloc(sizeof(gpr_cmdline));
75 25 : memset(cl, 0, sizeof(gpr_cmdline));
76 :
77 25 : cl->description = description;
78 25 : cl->state = normal_state;
79 :
80 25 : return cl;
81 : }
82 :
83 5 : void gpr_cmdline_set_survive_failure(gpr_cmdline *cl) {
84 5 : cl->survive_failure = 1;
85 5 : }
86 :
87 25 : void gpr_cmdline_destroy(gpr_cmdline *cl) {
88 93 : while (cl->args) {
89 43 : arg *a = cl->args;
90 43 : cl->args = a->next;
91 43 : gpr_free(a);
92 : }
93 25 : gpr_free(cl);
94 25 : }
95 :
96 43 : static void add_arg(gpr_cmdline *cl, const char *name, const char *help,
97 : argtype type, void *value) {
98 : arg *a;
99 :
100 76 : for (a = cl->args; a; a = a->next) {
101 33 : GPR_ASSERT(0 != strcmp(a->name, name));
102 : }
103 :
104 43 : a = gpr_malloc(sizeof(arg));
105 43 : memset(a, 0, sizeof(arg));
106 43 : a->name = name;
107 43 : a->help = help;
108 43 : a->type = type;
109 43 : a->value = value;
110 43 : a->next = cl->args;
111 43 : cl->args = a;
112 43 : }
113 :
114 13 : void gpr_cmdline_add_int(gpr_cmdline *cl, const char *name, const char *help,
115 : int *value) {
116 13 : add_arg(cl, name, help, ARGTYPE_INT, value);
117 13 : }
118 :
119 15 : void gpr_cmdline_add_flag(gpr_cmdline *cl, const char *name, const char *help,
120 : int *value) {
121 15 : add_arg(cl, name, help, ARGTYPE_BOOL, value);
122 15 : }
123 :
124 15 : void gpr_cmdline_add_string(gpr_cmdline *cl, const char *name, const char *help,
125 : char **value) {
126 15 : add_arg(cl, name, help, ARGTYPE_STRING, value);
127 15 : }
128 :
129 8 : void gpr_cmdline_on_extra_arg(
130 : gpr_cmdline *cl, const char *name, const char *help,
131 : void (*on_extra_arg)(void *user_data, const char *arg), void *user_data) {
132 8 : GPR_ASSERT(!cl->extra_arg);
133 8 : GPR_ASSERT(on_extra_arg);
134 :
135 8 : cl->extra_arg = on_extra_arg;
136 8 : cl->extra_arg_user_data = user_data;
137 8 : cl->extra_arg_name = name;
138 8 : cl->extra_arg_help = help;
139 8 : }
140 :
141 : /* recursively descend argument list, adding the last element
142 : to s first - so that arguments are added in the order they were
143 : added to the list by api calls */
144 28 : static void add_args_to_usage(gpr_strvec *s, arg *a) {
145 : char *tmp;
146 :
147 56 : if (!a) return;
148 21 : add_args_to_usage(s, a->next);
149 :
150 21 : switch (a->type) {
151 : case ARGTYPE_BOOL:
152 7 : gpr_asprintf(&tmp, " [--%s|--no-%s]", a->name, a->name);
153 7 : gpr_strvec_add(s, tmp);
154 7 : break;
155 : case ARGTYPE_STRING:
156 7 : gpr_asprintf(&tmp, " [--%s=string]", a->name);
157 7 : gpr_strvec_add(s, tmp);
158 7 : break;
159 : case ARGTYPE_INT:
160 7 : gpr_asprintf(&tmp, " [--%s=int]", a->name);
161 7 : gpr_strvec_add(s, tmp);
162 7 : break;
163 : }
164 : }
165 :
166 7 : char *gpr_cmdline_usage_string(gpr_cmdline *cl, const char *argv0) {
167 : /* TODO(ctiller): make this prettier */
168 : gpr_strvec s;
169 : char *tmp;
170 7 : const char *name = strrchr(argv0, '/');
171 :
172 7 : if (name) {
173 6 : name++;
174 : } else {
175 1 : name = argv0;
176 : }
177 :
178 7 : gpr_strvec_init(&s);
179 :
180 7 : gpr_asprintf(&tmp, "Usage: %s", name);
181 7 : gpr_strvec_add(&s, tmp);
182 7 : add_args_to_usage(&s, cl->args);
183 7 : if (cl->extra_arg) {
184 7 : gpr_asprintf(&tmp, " [%s...]", cl->extra_arg_name);
185 7 : gpr_strvec_add(&s, tmp);
186 : }
187 7 : gpr_strvec_add(&s, gpr_strdup("\n"));
188 :
189 7 : tmp = gpr_strvec_flatten(&s, NULL);
190 7 : gpr_strvec_destroy(&s);
191 7 : return tmp;
192 : }
193 :
194 5 : static int print_usage_and_die(gpr_cmdline *cl) {
195 5 : char *usage = gpr_cmdline_usage_string(cl, cl->argv0);
196 5 : fprintf(stderr, "%s", usage);
197 5 : gpr_free(usage);
198 5 : if (!cl->survive_failure) {
199 0 : exit(1);
200 : }
201 5 : return 0;
202 : }
203 :
204 6 : static int extra_state(gpr_cmdline *cl, char *str) {
205 6 : if (!cl->extra_arg) {
206 0 : return print_usage_and_die(cl);
207 : }
208 6 : cl->extra_arg(cl->extra_arg_user_data, str);
209 6 : return 1;
210 : }
211 :
212 27 : static arg *find_arg(gpr_cmdline *cl, char *name) {
213 : arg *a;
214 :
215 42 : for (a = cl->args; a; a = a->next) {
216 41 : if (0 == strcmp(a->name, name)) {
217 26 : break;
218 : }
219 : }
220 :
221 27 : if (!a) {
222 1 : fprintf(stderr, "Unknown argument: %s\n", name);
223 1 : return NULL;
224 : }
225 :
226 26 : return a;
227 : }
228 :
229 20 : static int value_state(gpr_cmdline *cl, char *str) {
230 : long intval;
231 : char *end;
232 :
233 20 : GPR_ASSERT(cl->cur_arg);
234 :
235 20 : switch (cl->cur_arg->type) {
236 : case ARGTYPE_INT:
237 6 : intval = strtol(str, &end, 0);
238 6 : if (*end || intval < INT_MIN || intval > INT_MAX) {
239 1 : fprintf(stderr, "expected integer, got '%s' for %s\n", str,
240 1 : cl->cur_arg->name);
241 1 : return print_usage_and_die(cl);
242 : }
243 5 : *(int *)cl->cur_arg->value = (int)intval;
244 5 : break;
245 : case ARGTYPE_BOOL:
246 5 : if (0 == strcmp(str, "1") || 0 == strcmp(str, "true")) {
247 2 : *(int *)cl->cur_arg->value = 1;
248 3 : } else if (0 == strcmp(str, "0") || 0 == strcmp(str, "false")) {
249 2 : *(int *)cl->cur_arg->value = 0;
250 : } else {
251 1 : fprintf(stderr, "expected boolean, got '%s' for %s\n", str,
252 1 : cl->cur_arg->name);
253 1 : return print_usage_and_die(cl);
254 : }
255 4 : break;
256 : case ARGTYPE_STRING:
257 9 : *(char **)cl->cur_arg->value = str;
258 9 : break;
259 : }
260 :
261 18 : cl->state = normal_state;
262 18 : return 1;
263 : }
264 :
265 32 : static int normal_state(gpr_cmdline *cl, char *str) {
266 32 : char *eq = NULL;
267 32 : char *tmp = NULL;
268 32 : char *arg_name = NULL;
269 32 : int r = 1;
270 :
271 64 : if (0 == strcmp(str, "-help") || 0 == strcmp(str, "--help") ||
272 32 : 0 == strcmp(str, "-h")) {
273 1 : return print_usage_and_die(cl);
274 : }
275 :
276 31 : cl->cur_arg = NULL;
277 :
278 31 : if (str[0] == '-') {
279 28 : if (str[1] == '-') {
280 22 : if (str[2] == 0) {
281 : /* handle '--' to move to just extra args */
282 1 : cl->state = extra_state;
283 1 : return 1;
284 : }
285 21 : str += 2;
286 : } else {
287 6 : str += 1;
288 : }
289 : /* first byte of str is now past the leading '-' or '--' */
290 27 : if (str[0] == 'n' && str[1] == 'o' && str[2] == '-') {
291 : /* str is of the form '--no-foo' - it's a flag disable */
292 5 : str += 3;
293 5 : cl->cur_arg = find_arg(cl, str);
294 5 : if (cl->cur_arg == NULL) {
295 0 : return print_usage_and_die(cl);
296 : }
297 5 : if (cl->cur_arg->type != ARGTYPE_BOOL) {
298 1 : fprintf(stderr, "%s is not a flag argument\n", str);
299 1 : return print_usage_and_die(cl);
300 : }
301 4 : *(int *)cl->cur_arg->value = 0;
302 4 : return 1; /* early out */
303 : }
304 22 : eq = strchr(str, '=');
305 22 : if (eq != NULL) {
306 : /* copy the string into a temp buffer and extract the name */
307 12 : tmp = arg_name = gpr_malloc((size_t)(eq - str + 1));
308 12 : memcpy(arg_name, str, (size_t)(eq - str));
309 12 : arg_name[eq - str] = 0;
310 : } else {
311 10 : arg_name = str;
312 : }
313 22 : cl->cur_arg = find_arg(cl, arg_name);
314 22 : if (cl->cur_arg == NULL) {
315 1 : return print_usage_and_die(cl);
316 : }
317 21 : if (eq != NULL) {
318 : /* str was of the type --foo=value, parse the value */
319 12 : r = value_state(cl, eq + 1);
320 9 : } else if (cl->cur_arg->type != ARGTYPE_BOOL) {
321 : /* flag types don't have a '--foo value' variant, other types do */
322 8 : cl->state = value_state;
323 : } else {
324 : /* flag parameter: just set the value */
325 1 : *(int *)cl->cur_arg->value = 1;
326 : }
327 : } else {
328 3 : r = extra_state(cl, str);
329 : }
330 :
331 24 : gpr_free(tmp);
332 24 : return r;
333 : }
334 :
335 24 : int gpr_cmdline_parse(gpr_cmdline *cl, int argc, char **argv) {
336 : int i;
337 :
338 24 : GPR_ASSERT(argc >= 1);
339 24 : cl->argv0 = argv[0];
340 :
341 62 : for (i = 1; i < argc; i++) {
342 43 : if (!cl->state(cl, argv[i])) {
343 5 : return 0;
344 : }
345 : }
346 19 : return 1;
347 : }
|