opt.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /* Licensed under GPLv3+ - see LICENSE file for details */
  2. #include <ccan/opt/opt.h>
  3. #include <string.h>
  4. #include <errno.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. 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. 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. opt_num_short++;
  116. if (entry->type == OPT_HASARG)
  117. opt_num_short_arg++;
  118. }
  119. /* Don't document args unless there are some. */
  120. if (entry->type == OPT_NOARG) {
  121. if (p[len] == ' ' || p[len] == '=')
  122. errx(1, "Option %s: does not take arguments"
  123. " '%s'", entry->names, p+len+1);
  124. }
  125. }
  126. }
  127. static void add_opt(const struct opt_table *entry)
  128. {
  129. opt_table = realloc(opt_table, sizeof(opt_table[0]) * (opt_count+1));
  130. opt_table[opt_count++] = *entry;
  131. }
  132. void _opt_register(const char *names, enum opt_type type,
  133. char *(*cb)(void *arg),
  134. char *(*cb_arg)(const char *optarg, void *arg),
  135. void (*show)(char buf[OPT_SHOW_LEN], const void *arg),
  136. const void *arg, const char *desc)
  137. {
  138. struct opt_table opt;
  139. opt.names = names;
  140. opt.type = type;
  141. opt.cb = cb;
  142. opt.cb_arg = cb_arg;
  143. opt.show = show;
  144. opt.u.carg = arg;
  145. opt.desc = desc;
  146. check_opt(&opt);
  147. add_opt(&opt);
  148. }
  149. void opt_register_table(const struct opt_table entry[], const char *desc)
  150. {
  151. unsigned int i, start = opt_count;
  152. if (desc) {
  153. struct opt_table heading = OPT_SUBTABLE(NULL, desc);
  154. add_opt(&heading);
  155. }
  156. for (i = 0; entry[i].type != OPT_END; i++) {
  157. if (entry[i].type == OPT_SUBTABLE)
  158. opt_register_table(subtable_of(&entry[i]),
  159. entry[i].desc);
  160. else {
  161. check_opt(&entry[i]);
  162. add_opt(&entry[i]);
  163. }
  164. }
  165. /* We store the table length in arg ptr. */
  166. if (desc)
  167. opt_table[start].u.tlen = (opt_count - start);
  168. }
  169. /* Parse your arguments. */
  170. bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...))
  171. {
  172. int ret;
  173. unsigned offset = 0;
  174. /* This helps opt_usage. */
  175. opt_argv0 = argv[0];
  176. while ((ret = parse_one(argc, argv, &offset, errlog)) == 1);
  177. /* parse_one returns 0 on finish, -1 on error */
  178. return (ret == 0);
  179. }
  180. void opt_free_table(void)
  181. {
  182. free(opt_table);
  183. opt_table = NULL;
  184. opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
  185. }
  186. void opt_log_stderr(const char *fmt, ...)
  187. {
  188. va_list ap;
  189. va_start(ap, fmt);
  190. vfprintf(stderr, fmt, ap);
  191. fprintf(stderr, "\n");
  192. va_end(ap);
  193. }
  194. void opt_log_stderr_exit(const char *fmt, ...)
  195. {
  196. va_list ap;
  197. va_start(ap, fmt);
  198. vfprintf(stderr, fmt, ap);
  199. fprintf(stderr, "\n");
  200. va_end(ap);
  201. exit(1);
  202. }
  203. char *opt_invalid_argument(const char *arg)
  204. {
  205. char *str = malloc(sizeof("Invalid argument '%s'") + strlen(arg));
  206. sprintf(str, "Invalid argument '%s'", arg);
  207. return str;
  208. }