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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  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. size_t j;
  125. for (j = 1; j < len; ++j) {
  126. nargv[size] = malloc(3);
  127. sprintf(nargv[size], "-%c", arg[j]);
  128. size++;
  129. }
  130. continue;
  131. }
  132. // regular arg
  133. nargv[size] = malloc(len + 1);
  134. strcpy(nargv[size], arg);
  135. size++;
  136. }
  137. nargv[size] = NULL;
  138. *argc = size;
  139. return nargv;
  140. }
  141. /*
  142. * Define an option.
  143. */
  144. void
  145. command_option(command_t *self, const char *small, const char *large, const char *desc, command_callback_t cb) {
  146. if (self->option_count == COMMANDER_MAX_OPTIONS) {
  147. command_free(self);
  148. error("Maximum option definitions exceeded");
  149. }
  150. int n = self->option_count++;
  151. command_option_t *option = &self->options[n];
  152. option->cb = cb;
  153. option->small = small;
  154. option->description = desc;
  155. option->required_arg = option->optional_arg = 0;
  156. option->large_with_arg = large;
  157. option->argname = malloc(strlen(large) + 1);
  158. assert(option->argname);
  159. option->large = malloc(strlen(large) + 1);
  160. assert(option->large);
  161. parse_argname(large, option->large, option->argname);
  162. if ('[' == option->argname[0]) option->optional_arg = 1;
  163. if ('<' == option->argname[0]) option->required_arg = 1;
  164. }
  165. /*
  166. * Parse `argv` (internal).
  167. * Input arguments should be normalized first
  168. * see `normalize_args`.
  169. */
  170. static void
  171. command_parse_args(command_t *self, int argc, char **argv) {
  172. int literal = 0;
  173. int i, j;
  174. for (i = 1; i < argc; ++i) {
  175. const char *arg = argv[i];
  176. for (j = 0; j < self->option_count; ++j) {
  177. command_option_t *option = &self->options[j];
  178. // match flag
  179. if (!strcmp(arg, option->small) || !strcmp(arg, option->large)) {
  180. self->arg = NULL;
  181. // required
  182. if (option->required_arg) {
  183. arg = argv[++i];
  184. if (!arg || '-' == arg[0]) {
  185. fprintf(stderr, "%s %s argument required\n", option->large, option->argname);
  186. command_free(self);
  187. exit(1);
  188. }
  189. self->arg = arg;
  190. }
  191. // optional
  192. if (option->optional_arg) {
  193. if (argv[i + 1] && '-' != argv[i + 1][0]) {
  194. self->arg = argv[++i];
  195. }
  196. }
  197. // invoke callback
  198. option->cb(self);
  199. goto match;
  200. }
  201. }
  202. // --
  203. if ('-' == arg[0] && '-' == arg[1] && 0 == arg[2]) {
  204. literal = 1;
  205. goto match;
  206. }
  207. // unrecognized
  208. if ('-' == arg[0] && !literal) {
  209. fprintf(stderr, "unrecognized flag %s\n", arg);
  210. command_free(self);
  211. exit(1);
  212. }
  213. int n = self->argc++;
  214. if (n == COMMANDER_MAX_ARGS) {
  215. command_free(self);
  216. error("Maximum number of arguments exceeded");
  217. }
  218. self->argv[n] = (char *) arg;
  219. match:;
  220. }
  221. }
  222. /*
  223. * Parse `argv` (public).
  224. */
  225. void
  226. command_parse(command_t *self, int argc, char **argv) {
  227. self->nargv = normalize_args(&argc, argv);
  228. command_parse_args(self, argc, self->nargv);
  229. self->argv[self->argc] = NULL;
  230. }