LCOV - code coverage report
Current view: top level - core/support - cmdline.c (source / functions) Hit Total Coverage
Test: tmp.CaZ6RjdVn2 Lines: 163 166 98.2 %
Date: 2015-12-10 22:15:08 Functions: 16 16 100.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             :   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             : }

Generated by: LCOV version 1.11