Open Source Tomb Raider Engine
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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. }