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 }