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