LCOV - code coverage report
Current view: top level - src/core/support - cmdline.c (source / functions) Hit Total Coverage
Test: tmp.zDYK9MVh93 Lines: 119 153 77.8 %
Date: 2015-10-10 Functions: 12 15 80.0 %

          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 : }

Generated by: LCOV version 1.10