opt.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. #include <ccan/opt/opt.h>
  2. #include <string.h>
  3. #include <errno.h>
  4. #include <getopt.h>
  5. #include <stdlib.h>
  6. #include <stdio.h>
  7. #include <err.h>
  8. #include <assert.h>
  9. #include <stdarg.h>
  10. #include <stdint.h>
  11. #include "private.h"
  12. struct opt_table *opt_table;
  13. unsigned int opt_count, opt_num_short, opt_num_short_arg, opt_num_long;
  14. const char *opt_argv0;
  15. /* Returns string after first '-'. */
  16. static const char *first_name(const char *names, unsigned *len)
  17. {
  18. *len = strcspn(names + 1, "/= ");
  19. return names + 1;
  20. }
  21. static const char *next_name(const char *names, unsigned *len)
  22. {
  23. names += *len;
  24. if (names[0] == ' ' || names[0] == '=' || names[0] == '\0')
  25. return NULL;
  26. return first_name(names + 1, len);
  27. }
  28. static const char *first_opt(unsigned *i, unsigned *len)
  29. {
  30. for (*i = 0; *i < opt_count; (*i)++) {
  31. if (opt_table[*i].type == OPT_SUBTABLE)
  32. continue;
  33. return first_name(opt_table[*i].names, len);
  34. }
  35. return NULL;
  36. }
  37. static const char *next_opt(const char *p, unsigned *i, unsigned *len)
  38. {
  39. for (; *i < opt_count; (*i)++) {
  40. if (opt_table[*i].type == OPT_SUBTABLE)
  41. continue;
  42. if (!p)
  43. return first_name(opt_table[*i].names, len);
  44. p = next_name(p, len);
  45. if (p)
  46. return p;
  47. }
  48. return NULL;
  49. }
  50. static const char *first_lopt(unsigned *i, unsigned *len)
  51. {
  52. const char *p;
  53. for (p = first_opt(i, len); p; p = next_opt(p, i, len)) {
  54. if (p[0] == '-') {
  55. /* Skip leading "-" */
  56. (*len)--;
  57. p++;
  58. break;
  59. }
  60. }
  61. return p;
  62. }
  63. static const char *next_lopt(const char *p, unsigned *i, unsigned *len)
  64. {
  65. for (p = next_opt(p, i, len); p; p = next_opt(p, i, len)) {
  66. if (p[0] == '-') {
  67. /* Skip leading "-" */
  68. (*len)--;
  69. p++;
  70. break;
  71. }
  72. }
  73. return p;
  74. }
  75. const char *first_sopt(unsigned *i)
  76. {
  77. const char *p;
  78. unsigned int len = 0 /* GCC bogus warning */;
  79. for (p = first_opt(i, &len); p; p = next_opt(p, i, &len)) {
  80. if (p[0] != '-')
  81. break;
  82. }
  83. return p;
  84. }
  85. const char *next_sopt(const char *p, unsigned *i)
  86. {
  87. unsigned int len = 1;
  88. for (p = next_opt(p, i, &len); p; p = next_opt(p, i, &len)) {
  89. if (p[0] != '-')
  90. break;
  91. }
  92. return p;
  93. }
  94. static void check_opt(const struct opt_table *entry)
  95. {
  96. const char *p;
  97. unsigned len;
  98. if (entry->type != OPT_HASARG && entry->type != OPT_NOARG)
  99. errx(1, "Option %s: unknown entry type %u",
  100. entry->names, entry->type);
  101. if (!entry->desc)
  102. errx(1, "Option %s: description cannot be NULL", entry->names);
  103. if (entry->names[0] != '-')
  104. errx(1, "Option %s: does not begin with '-'", entry->names);
  105. for (p = first_name(entry->names, &len); p; p = next_name(p, &len)) {
  106. if (*p == '-') {
  107. if (len == 1)
  108. errx(1, "Option %s: invalid long option '--'",
  109. entry->names);
  110. opt_num_long++;
  111. } else {
  112. if (len != 1)
  113. errx(1, "Option %s: invalid short option"
  114. " '%.*s'", entry->names, len+1, p-1);
  115. if (*p == ':')
  116. errx(1, "Option %s: invalid short option '-:'",
  117. entry->names);
  118. opt_num_short++;
  119. if (entry->type == OPT_HASARG) {
  120. opt_num_short_arg++;
  121. if (*p == '?')
  122. errx(1, "Option %s: '-?' cannot take"
  123. " an argument", entry->names);
  124. }
  125. }
  126. /* Don't document args unless there are some. */
  127. if (entry->type == OPT_NOARG) {
  128. if (p[len] == ' ' || p[len] == '=')
  129. errx(1, "Option %s: does not take arguments"
  130. "'%s'", entry->names, p+len+1);
  131. }
  132. }
  133. }
  134. static void add_opt(const struct opt_table *entry)
  135. {
  136. opt_table = realloc(opt_table, sizeof(opt_table[0]) * (opt_count+1));
  137. opt_table[opt_count++] = *entry;
  138. }
  139. void _opt_register(const char *names, enum opt_type type,
  140. char *(*cb)(void *arg),
  141. char *(*cb_arg)(const char *optarg, void *arg),
  142. void (*show)(char buf[OPT_SHOW_LEN], const void *arg),
  143. void *arg, const char *desc)
  144. {
  145. struct opt_table opt;
  146. opt.names = names;
  147. opt.type = type;
  148. opt.cb = cb;
  149. opt.cb_arg = cb_arg;
  150. opt.show = show;
  151. opt.arg = arg;
  152. opt.desc = desc;
  153. check_opt(&opt);
  154. add_opt(&opt);
  155. }
  156. void opt_register_table(const struct opt_table entry[], const char *desc)
  157. {
  158. unsigned int i, start = opt_count;
  159. if (desc) {
  160. struct opt_table heading = OPT_SUBTABLE(NULL, desc);
  161. add_opt(&heading);
  162. }
  163. for (i = 0; entry[i].type != OPT_END; i++) {
  164. if (entry[i].type == OPT_SUBTABLE)
  165. opt_register_table(subtable_of(&entry[i]),
  166. entry[i].desc);
  167. else {
  168. check_opt(&entry[i]);
  169. add_opt(&entry[i]);
  170. }
  171. }
  172. /* We store the table length in arg ptr. */
  173. if (desc)
  174. opt_table[start].arg = (void *)(intptr_t)(opt_count - start);
  175. }
  176. static char *make_optstring(void)
  177. {
  178. char *str = malloc(1 + opt_num_short + opt_num_short_arg + 1);
  179. const char *p;
  180. unsigned int i, num = 0;
  181. /* This tells getopt_long we want a ':' returned for missing arg. */
  182. str[num++] = ':';
  183. for (p = first_sopt(&i); p; p = next_sopt(p, &i)) {
  184. str[num++] = *p;
  185. if (opt_table[i].type == OPT_HASARG)
  186. str[num++] = ':';
  187. }
  188. str[num++] = '\0';
  189. assert(num == 1 + opt_num_short + opt_num_short_arg + 1);
  190. return str;
  191. }
  192. static struct option *make_options(void)
  193. {
  194. struct option *options = malloc(sizeof(*options) * (opt_num_long + 1));
  195. unsigned int i, num = 0, len = 0 /* GCC bogus warning */;
  196. const char *p;
  197. for (p = first_lopt(&i, &len); p; p = next_lopt(p, &i, &len)) {
  198. char *buf = malloc(len + 1);
  199. memcpy(buf, p, len);
  200. buf[len] = 0;
  201. options[num].name = buf;
  202. options[num].has_arg = (opt_table[i].type == OPT_HASARG);
  203. options[num].flag = NULL;
  204. options[num].val = 0;
  205. num++;
  206. }
  207. memset(&options[num], 0, sizeof(options[num]));
  208. assert(num == opt_num_long);
  209. return options;
  210. }
  211. static struct opt_table *find_short(char shortopt)
  212. {
  213. unsigned int i;
  214. const char *p;
  215. for (p = first_sopt(&i); p; p = next_sopt(p, &i)) {
  216. if (*p == shortopt)
  217. return &opt_table[i];
  218. }
  219. abort();
  220. }
  221. /* We want the index'th long entry. */
  222. static struct opt_table *find_long(int index, const char **name)
  223. {
  224. unsigned int i, len;
  225. const char *p;
  226. for (p = first_lopt(&i, &len); p; p = next_lopt(p, &i, &len)) {
  227. if (index == 0) {
  228. *name = p;
  229. return &opt_table[i];
  230. }
  231. index--;
  232. }
  233. abort();
  234. }
  235. /* glibc does this as:
  236. /tmp/opt-example: invalid option -- 'x'
  237. /tmp/opt-example: unrecognized option '--long'
  238. /tmp/opt-example: option '--someflag' doesn't allow an argument
  239. /tmp/opt-example: option '--s' is ambiguous
  240. /tmp/opt-example: option requires an argument -- 's'
  241. */
  242. static void parse_fail(void (*errlog)(const char *fmt, ...),
  243. char shortopt, const char *longopt, const char *problem)
  244. {
  245. if (shortopt)
  246. errlog("%s: -%c: %s", opt_argv0, shortopt, problem);
  247. else
  248. errlog("%s: --%.*s: %s", opt_argv0,
  249. strcspn(longopt, "/"), longopt, problem);
  250. }
  251. /* Parse your arguments. */
  252. bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...))
  253. {
  254. char *optstring = make_optstring();
  255. struct option *options = make_options();
  256. int ret, longidx = 0;
  257. struct opt_table *e;
  258. /* We will do our own error reporting. */
  259. opterr = 0;
  260. opt_argv0 = argv[0];
  261. /* Reset in case we're called more than once. */
  262. optopt = 0;
  263. optind = 0;
  264. while ((ret = getopt_long(*argc, argv, optstring, options, &longidx))
  265. != -1) {
  266. char *problem;
  267. const char *name = NULL; /* GCC bogus warning */
  268. /* optopt is 0 if it's an unknown long option, *or* if
  269. * -? is a valid short option. */
  270. if (ret == '?') {
  271. if (optopt || strncmp(argv[optind-1], "--", 2) == 0) {
  272. parse_fail(errlog, optopt, argv[optind-1]+2,
  273. "unrecognized option");
  274. break;
  275. }
  276. } else if (ret == ':') {
  277. /* Missing argument: longidx not updated :( */
  278. parse_fail(errlog, optopt, argv[optind-1]+2,
  279. "option requires an argument");
  280. break;
  281. }
  282. if (ret != 0)
  283. e = find_short(ret);
  284. else
  285. e = find_long(longidx, &name);
  286. if (e->type == OPT_HASARG)
  287. problem = e->cb_arg(optarg, e->arg);
  288. else
  289. problem = e->cb(e->arg);
  290. if (problem) {
  291. parse_fail(errlog, ret, name, problem);
  292. free(problem);
  293. break;
  294. }
  295. }
  296. free(optstring);
  297. free(options);
  298. if (ret != -1)
  299. return false;
  300. /* We hide everything but remaining arguments. */
  301. memmove(&argv[1], &argv[optind], sizeof(argv[1]) * (*argc-optind+1));
  302. *argc -= optind - 1;
  303. return ret == -1 ? true : false;
  304. }
  305. void opt_log_stderr(const char *fmt, ...)
  306. {
  307. va_list ap;
  308. va_start(ap, fmt);
  309. vfprintf(stderr, fmt, ap);
  310. fprintf(stderr, "\n");
  311. va_end(ap);
  312. }
  313. void opt_log_stderr_exit(const char *fmt, ...)
  314. {
  315. va_list ap;
  316. va_start(ap, fmt);
  317. vfprintf(stderr, fmt, ap);
  318. fprintf(stderr, "\n");
  319. va_end(ap);
  320. exit(1);
  321. }
  322. char *opt_invalid_argument(const char *arg)
  323. {
  324. char *str = malloc(sizeof("Invalid argument '%s'") + strlen(arg));
  325. sprintf(str, "Invalid argument '%s'", arg);
  326. return str;
  327. }