bcalc

cli bitcoin unit calculator
git clone git://jb55.com/bcalc
Log | Files | Refs | README | LICENSE

commander.c (5799B)


      1 
      2 //
      3 // commander.c
      4 //
      5 // Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>
      6 //
      7 
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <assert.h>
     12 #include "commander.h"
     13 
     14 /*
     15  * Output error and exit.
     16  */
     17 
     18 static void
     19 error(char *msg) {
     20   fprintf(stderr, "%s\n", msg);
     21   exit(1);
     22 }
     23 
     24 /*
     25  * Output command version.
     26  */
     27 
     28 static void
     29 command_version(command_t *self) {
     30   printf("%s\n", self->version);
     31   command_free(self);
     32   exit(0);
     33 }
     34 
     35 /*
     36  * Output command help.
     37  */
     38 
     39 void
     40 command_help(command_t *self) {
     41   printf("\n");
     42   printf("  Usage: %s %s\n", self->name, self->usage);
     43   printf("\n");
     44   printf("  Options:\n");
     45   printf("\n");
     46 
     47   int i;
     48   for (i = 0; i < self->option_count; ++i) {
     49     command_option_t *option = &self->options[i];
     50     printf("    %s, %-25s %s\n"
     51       , option->small
     52       , option->large_with_arg
     53       , option->description);
     54   }
     55 
     56   printf("\n");
     57   command_free(self);
     58   exit(0);
     59 }
     60 
     61 /*
     62  * Initialize with program `name` and `version`.
     63  */
     64 
     65 void
     66 command_init(command_t *self, const char *name, const char *version) {
     67   self->arg = NULL;
     68   self->name = name;
     69   self->version = version;
     70   self->option_count = self->argc = 0;
     71   self->usage = "[options]";
     72   self->nargv = NULL;
     73   command_option(self, "-V", "--version", "output program version", command_version);
     74   command_option(self, "-h", "--help", "output help information", command_help);
     75 }
     76 
     77 /*
     78  * Free up commander after use.
     79  */
     80 
     81 void
     82 command_free(command_t *self) {
     83   int i;
     84 
     85   for (i = 0; i < self->option_count; ++i) {
     86     command_option_t *option = &self->options[i];
     87     free(option->argname);
     88     free(option->large);
     89   }
     90 
     91   if (self->nargv) {
     92     for (i = 0; self->nargv[i]; ++i) {
     93       free(self->nargv[i]);
     94     }
     95     free(self->nargv);
     96   }
     97 }
     98 
     99 /*
    100  * Parse argname from `str`. For example
    101  * Take "--required <arg>" and populate `flag`
    102  * with "--required" and `arg` with "<arg>".
    103  */
    104 
    105 static void
    106 parse_argname(const char *str, char *flag, char *arg) {
    107   int buffer = 0;
    108   size_t flagpos = 0;
    109   size_t argpos = 0;
    110   size_t len = strlen(str);
    111   size_t i;
    112 
    113   for (i = 0; i < len; ++i) {
    114     if (buffer || '[' == str[i] || '<' == str[i]) {
    115       buffer = 1;
    116       arg[argpos++] = str[i];
    117     } else {
    118       if (' ' == str[i]) continue;
    119       flag[flagpos++] = str[i];
    120     }
    121   }
    122 
    123   arg[argpos] = '\0';
    124   flag[flagpos] = '\0';
    125 }
    126 
    127 /*
    128  * Normalize the argument vector by exploding
    129  * multiple options (if any). For example
    130  * "foo -abc --scm git" -> "foo -a -b -c --scm git"
    131  */
    132 
    133 static char **
    134 normalize_args(int *argc, char **argv) {
    135   int size = 0;
    136   int alloc = *argc + 1;
    137   char **nargv = malloc(alloc * sizeof(char *));
    138   int i;
    139 
    140   for (i = 0; argv[i]; ++i) {
    141     const char *arg = argv[i];
    142     size_t len = strlen(arg);
    143 
    144     // short flag
    145     if (len > 2 && '-' == arg[0] && !strchr(arg + 1, '-')) {
    146       alloc += len - 2;
    147       nargv = realloc(nargv, alloc * sizeof(char *));
    148       for (size_t j = 1; j < len; ++j) {
    149         nargv[size] = malloc(3);
    150         sprintf(nargv[size], "-%c", arg[j]);
    151         size++;
    152       }
    153       continue;
    154     }
    155 
    156     // regular arg
    157     nargv[size] = malloc(len + 1);
    158     strcpy(nargv[size], arg);
    159     size++;
    160   }
    161 
    162   nargv[size] = NULL;
    163   *argc = size;
    164   return nargv;
    165 }
    166 
    167 /*
    168  * Define an option.
    169  */
    170 
    171 void
    172 command_option(command_t *self, const char *small, const char *large, const char *desc, command_callback_t cb) {
    173   if (self->option_count == COMMANDER_MAX_OPTIONS) {
    174     command_free(self);
    175     error("Maximum option definitions exceeded");
    176   }
    177   int n = self->option_count++;
    178   command_option_t *option = &self->options[n];
    179   option->cb = cb;
    180   option->small = small;
    181   option->description = desc;
    182   option->required_arg = option->optional_arg = 0;
    183   option->large_with_arg = large;
    184   option->argname = malloc(strlen(large) + 1);
    185   assert(option->argname);
    186   option->large = malloc(strlen(large) + 1);
    187   assert(option->large);
    188   parse_argname(large, option->large, option->argname);
    189   if ('[' == option->argname[0]) option->optional_arg = 1;
    190   if ('<' == option->argname[0]) option->required_arg = 1;
    191 }
    192 
    193 /*
    194  * Parse `argv` (internal).
    195  * Input arguments should be normalized first
    196  * see `normalize_args`.
    197  */
    198 
    199 static void
    200 command_parse_args(command_t *self, int argc, char **argv) {
    201   int literal = 0;
    202   int i, j;
    203 
    204   for (i = 1; i < argc; ++i) {
    205     const char *arg = argv[i];
    206     for (j = 0; j < self->option_count; ++j) {
    207       command_option_t *option = &self->options[j];
    208 
    209       // match flag
    210       if (!strcmp(arg, option->small) || !strcmp(arg, option->large)) {
    211         self->arg = NULL;
    212 
    213         // required
    214         if (option->required_arg) {
    215           arg = argv[++i];
    216           if (!arg || '-' == arg[0]) {
    217             fprintf(stderr, "%s %s argument required\n", option->large, option->argname);
    218             command_free(self);
    219             exit(1);
    220           }
    221           self->arg = arg;
    222         }
    223 
    224         // optional
    225         if (option->optional_arg) {
    226           if (argv[i + 1] && '-' != argv[i + 1][0]) {
    227             self->arg = argv[++i];
    228           }
    229         }
    230 
    231         // invoke callback
    232         option->cb(self);
    233         goto match;
    234       }
    235     }
    236 
    237     // --
    238     if ('-' == arg[0] && '-' == arg[1] && 0 == arg[2]) {
    239       literal = 1;
    240       goto match;
    241     }
    242 
    243     // unrecognized
    244     if ('-' == arg[0] && !literal) {
    245       fprintf(stderr, "unrecognized flag %s\n", arg);
    246       command_free(self);
    247       exit(1);
    248     }
    249 
    250     int n = self->argc++;
    251     if (n == COMMANDER_MAX_ARGS) {
    252       command_free(self);
    253       error("Maximum number of arguments exceeded");
    254     }
    255     self->argv[n] = (char *) arg;
    256     match:;
    257   }
    258 }
    259 
    260 /*
    261  * Parse `argv` (public).
    262  */
    263 
    264 void
    265 command_parse(command_t *self, int argc, char **argv) {
    266   self->nargv = normalize_args(&argc, argv);
    267   command_parse_args(self, argc, self->nargv);
    268   self->argv[self->argc] = NULL;
    269 }