Open Source Tomb Raider Engine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

commander.c 5.7KB

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