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 : void (*state)(gpr_cmdline *cl, char *arg);
66 : arg *cur_arg;
67 : };
68 :
69 : static void normal_state(gpr_cmdline *cl, char *arg);
70 :
71 18 : gpr_cmdline *gpr_cmdline_create(const char *description) {
72 18 : gpr_cmdline *cl = gpr_malloc(sizeof(gpr_cmdline));
73 18 : memset(cl, 0, sizeof(gpr_cmdline));
74 :
75 18 : cl->description = description;
76 18 : cl->state = normal_state;
77 :
78 18 : return cl;
79 : }
80 :
81 18 : void gpr_cmdline_destroy(gpr_cmdline *cl) {
82 64 : while (cl->args) {
83 28 : arg *a = cl->args;
84 28 : cl->args = a->next;
85 28 : gpr_free(a);
86 : }
87 18 : gpr_free(cl);
88 18 : }
89 :
90 28 : static void add_arg(gpr_cmdline *cl, const char *name, const char *help,
91 : argtype type, void *value) {
92 : arg *a;
93 :
94 46 : for (a = cl->args; a; a = a->next) {
95 18 : GPR_ASSERT(0 != strcmp(a->name, name));
96 : }
97 :
98 28 : a = gpr_malloc(sizeof(arg));
99 28 : memset(a, 0, sizeof(arg));
100 28 : a->name = name;
101 28 : a->help = help;
102 28 : a->type = type;
103 28 : a->value = value;
104 28 : a->next = cl->args;
105 28 : cl->args = a;
106 28 : }
107 :
108 8 : void gpr_cmdline_add_int(gpr_cmdline *cl, const char *name, const char *help,
109 : int *value) {
110 8 : add_arg(cl, name, help, ARGTYPE_INT, value);
111 8 : }
112 :
113 10 : void gpr_cmdline_add_flag(gpr_cmdline *cl, const char *name, const char *help,
114 : int *value) {
115 10 : add_arg(cl, name, help, ARGTYPE_BOOL, value);
116 10 : }
117 :
118 10 : void gpr_cmdline_add_string(gpr_cmdline *cl, const char *name, const char *help,
119 : char **value) {
120 10 : add_arg(cl, name, help, ARGTYPE_STRING, value);
121 10 : }
122 :
123 0 : void gpr_cmdline_on_extra_arg(
124 : gpr_cmdline *cl, const char *name, const char *help,
125 : void (*on_extra_arg)(void *user_data, const char *arg), void *user_data) {
126 0 : GPR_ASSERT(!cl->extra_arg);
127 0 : GPR_ASSERT(on_extra_arg);
128 :
129 0 : cl->extra_arg = on_extra_arg;
130 0 : cl->extra_arg_user_data = user_data;
131 0 : cl->extra_arg_name = name;
132 0 : cl->extra_arg_help = help;
133 0 : }
134 :
135 : /* recursively descend argument list, adding the last element
136 : to s first - so that arguments are added in the order they were
137 : added to the list by api calls */
138 4 : static void add_args_to_usage(gpr_strvec *s, arg *a) {
139 : char *tmp;
140 :
141 8 : if (!a) return;
142 3 : add_args_to_usage(s, a->next);
143 :
144 3 : switch (a->type) {
145 : case ARGTYPE_BOOL:
146 1 : gpr_asprintf(&tmp, " [--%s|--no-%s]", a->name, a->name);
147 1 : gpr_strvec_add(s, tmp);
148 1 : break;
149 : case ARGTYPE_STRING:
150 1 : gpr_asprintf(&tmp, " [--%s=string]", a->name);
151 1 : gpr_strvec_add(s, tmp);
152 1 : break;
153 : case ARGTYPE_INT:
154 1 : gpr_asprintf(&tmp, " [--%s=int]", a->name);
155 1 : gpr_strvec_add(s, tmp);
156 1 : break;
157 : }
158 : }
159 :
160 1 : char *gpr_cmdline_usage_string(gpr_cmdline *cl, const char *argv0) {
161 : /* TODO(ctiller): make this prettier */
162 : gpr_strvec s;
163 : char *tmp;
164 1 : const char *name = strrchr(argv0, '/');
165 :
166 1 : if (name) {
167 0 : name++;
168 : } else {
169 1 : name = argv0;
170 : }
171 :
172 1 : gpr_strvec_init(&s);
173 :
174 1 : gpr_asprintf(&tmp, "Usage: %s", name);
175 1 : gpr_strvec_add(&s, tmp);
176 1 : add_args_to_usage(&s, cl->args);
177 1 : if (cl->extra_arg) {
178 0 : gpr_asprintf(&tmp, " [%s...]", cl->extra_arg_name);
179 0 : gpr_strvec_add(&s, tmp);
180 : }
181 1 : gpr_strvec_add(&s, gpr_strdup("\n"));
182 :
183 1 : tmp = gpr_strvec_flatten(&s, NULL);
184 1 : gpr_strvec_destroy(&s);
185 1 : return tmp;
186 : }
187 :
188 0 : static void print_usage_and_die(gpr_cmdline *cl) {
189 0 : char *usage = gpr_cmdline_usage_string(cl, cl->argv0);
190 0 : fprintf(stderr, "%s", usage);
191 0 : gpr_free(usage);
192 0 : exit(1);
193 : }
194 :
195 0 : static void extra_state(gpr_cmdline *cl, char *str) {
196 0 : if (!cl->extra_arg) print_usage_and_die(cl);
197 0 : cl->extra_arg(cl->extra_arg_user_data, str);
198 0 : }
199 :
200 23 : static arg *find_arg(gpr_cmdline *cl, char *name) {
201 : arg *a;
202 :
203 32 : for (a = cl->args; a; a = a->next) {
204 32 : if (0 == strcmp(a->name, name)) {
205 23 : break;
206 : }
207 : }
208 :
209 23 : if (!a) {
210 0 : fprintf(stderr, "Unknown argument: %s\n", name);
211 0 : print_usage_and_die(cl);
212 : }
213 :
214 23 : return a;
215 : }
216 :
217 18 : static void value_state(gpr_cmdline *cl, char *str) {
218 : long intval;
219 : char *end;
220 :
221 18 : GPR_ASSERT(cl->cur_arg);
222 :
223 18 : switch (cl->cur_arg->type) {
224 : case ARGTYPE_INT:
225 5 : intval = strtol(str, &end, 0);
226 5 : if (*end || intval < INT_MIN || intval > INT_MAX) {
227 0 : fprintf(stderr, "expected integer, got '%s' for %s\n", str,
228 0 : cl->cur_arg->name);
229 0 : print_usage_and_die(cl);
230 : }
231 5 : *(int *)cl->cur_arg->value = (int)intval;
232 5 : break;
233 : case ARGTYPE_BOOL:
234 4 : if (0 == strcmp(str, "1") || 0 == strcmp(str, "true")) {
235 2 : *(int *)cl->cur_arg->value = 1;
236 2 : } else if (0 == strcmp(str, "0") || 0 == strcmp(str, "false")) {
237 2 : *(int *)cl->cur_arg->value = 0;
238 : } else {
239 0 : fprintf(stderr, "expected boolean, got '%s' for %s\n", str,
240 0 : cl->cur_arg->name);
241 0 : print_usage_and_die(cl);
242 : }
243 4 : break;
244 : case ARGTYPE_STRING:
245 9 : *(char **)cl->cur_arg->value = str;
246 9 : break;
247 : }
248 :
249 18 : cl->state = normal_state;
250 18 : }
251 :
252 23 : static void normal_state(gpr_cmdline *cl, char *str) {
253 23 : char *eq = NULL;
254 23 : char *tmp = NULL;
255 23 : char *arg_name = NULL;
256 :
257 46 : if (0 == strcmp(str, "-help") || 0 == strcmp(str, "--help") ||
258 23 : 0 == strcmp(str, "-h")) {
259 0 : print_usage_and_die(cl);
260 : }
261 :
262 23 : cl->cur_arg = NULL;
263 :
264 23 : if (str[0] == '-') {
265 23 : if (str[1] == '-') {
266 17 : if (str[2] == 0) {
267 : /* handle '--' to move to just extra args */
268 0 : cl->state = extra_state;
269 0 : return;
270 : }
271 17 : str += 2;
272 : } else {
273 6 : str += 1;
274 : }
275 : /* first byte of str is now past the leading '-' or '--' */
276 23 : if (str[0] == 'n' && str[1] == 'o' && str[2] == '-') {
277 : /* str is of the form '--no-foo' - it's a flag disable */
278 4 : str += 3;
279 4 : cl->cur_arg = find_arg(cl, str);
280 4 : if (cl->cur_arg->type != ARGTYPE_BOOL) {
281 0 : fprintf(stderr, "%s is not a flag argument\n", str);
282 0 : print_usage_and_die(cl);
283 : }
284 4 : *(int *)cl->cur_arg->value = 0;
285 4 : return; /* early out */
286 : }
287 19 : eq = strchr(str, '=');
288 19 : if (eq != NULL) {
289 : /* copy the string into a temp buffer and extract the name */
290 11 : tmp = arg_name = gpr_malloc((size_t)(eq - str + 1));
291 11 : memcpy(arg_name, str, (size_t)(eq - str));
292 11 : arg_name[eq - str] = 0;
293 : } else {
294 8 : arg_name = str;
295 : }
296 19 : cl->cur_arg = find_arg(cl, arg_name);
297 19 : if (eq != NULL) {
298 : /* str was of the type --foo=value, parse the value */
299 11 : value_state(cl, eq + 1);
300 8 : } else if (cl->cur_arg->type != ARGTYPE_BOOL) {
301 : /* flag types don't have a '--foo value' variant, other types do */
302 7 : cl->state = value_state;
303 : } else {
304 : /* flag parameter: just set the value */
305 1 : *(int *)cl->cur_arg->value = 1;
306 : }
307 : } else {
308 0 : extra_state(cl, str);
309 : }
310 :
311 19 : gpr_free(tmp);
312 : }
313 :
314 17 : void gpr_cmdline_parse(gpr_cmdline *cl, int argc, char **argv) {
315 : int i;
316 :
317 17 : GPR_ASSERT(argc >= 1);
318 17 : cl->argv0 = argv[0];
319 :
320 47 : for (i = 1; i < argc; i++) {
321 30 : cl->state(cl, argv[i]);
322 : }
323 17 : }
|