opt.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #include <ccan/opt/opt.h>
  2. #include <string.h>
  3. #include <errno.h>
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <err.h>
  7. #include <assert.h>
  8. #include <stdarg.h>
  9. #include <stdint.h>
  10. #include "private.h"
  11. struct opt_table *opt_table;
  12. unsigned int opt_count, opt_num_short, opt_num_short_arg, opt_num_long;
  13. const char *opt_argv0;
  14. /* Returns string after first '-'. */
  15. static const char *first_name(const char *names, unsigned *len)
  16. {
  17. *len = strcspn(names + 1, "|= ");
  18. return names + 1;
  19. }
  20. static const char *next_name(const char *names, unsigned *len)
  21. {
  22. names += *len;
  23. if (names[0] == ' ' || names[0] == '=' || names[0] == '\0')
  24. return NULL;
  25. return first_name(names + 1, len);
  26. }
  27. static const char *first_opt(unsigned *i, unsigned *len)
  28. {
  29. for (*i = 0; *i < opt_count; (*i)++) {
  30. if (opt_table[*i].type == OPT_SUBTABLE)
  31. continue;
  32. return first_name(opt_table[*i].names, len);
  33. }
  34. return NULL;
  35. }
  36. static const char *next_opt(const char *p, unsigned *i, unsigned *len)
  37. {
  38. for (; *i < opt_count; (*i)++) {
  39. if (opt_table[*i].type == OPT_SUBTABLE)
  40. continue;
  41. if (!p)
  42. return first_name(opt_table[*i].names, len);
  43. p = next_name(p, len);
  44. if (p)
  45. return p;
  46. }
  47. return NULL;
  48. }
  49. const char *first_lopt(unsigned *i, unsigned *len)
  50. {
  51. const char *p;
  52. for (p = first_opt(i, len); p; p = next_opt(p, i, len)) {
  53. if (p[0] == '-') {
  54. /* Skip leading "-" */
  55. (*len)--;
  56. p++;
  57. break;
  58. }
  59. }
  60. return p;
  61. }
  62. const char *next_lopt(const char *p, unsigned *i, unsigned *len)
  63. {
  64. for (p = next_opt(p, i, len); p; p = next_opt(p, i, len)) {
  65. if (p[0] == '-') {
  66. /* Skip leading "-" */
  67. (*len)--;
  68. p++;
  69. break;
  70. }
  71. }
  72. return p;
  73. }
  74. const char *first_sopt(unsigned *i)
  75. {
  76. const char *p;
  77. unsigned int len = 0 /* GCC bogus warning */;
  78. for (p = first_opt(i, &len); p; p = next_opt(p, i, &len)) {
  79. if (p[0] != '-')
  80. break;
  81. }
  82. return p;
  83. }
  84. const char *next_sopt(const char *p, unsigned *i)
  85. {
  86. unsigned int len = 1;
  87. for (p = next_opt(p, i, &len); p; p = next_opt(p, i, &len)) {
  88. if (p[0] != '-')
  89. break;
  90. }
  91. return p;
  92. }
  93. static void check_opt(const struct opt_table *entry)
  94. {
  95. const char *p;
  96. unsigned len;
  97. if (entry->type != OPT_HASARG && entry->type != OPT_NOARG)
  98. errx(1, "Option %s: unknown entry type %u",
  99. entry->names, entry->type);
  100. if (!entry->desc)
  101. errx(1, "Option %s: description cannot be NULL", entry->names);
  102. if (entry->names[0] != '-')
  103. errx(1, "Option %s: does not begin with '-'", entry->names);
  104. for (p = first_name(entry->names, &len); p; p = next_name(p, &len)) {
  105. if (*p == '-') {
  106. if (len == 1)
  107. errx(1, "Option %s: invalid long option '--'",
  108. entry->names);
  109. opt_num_long++;
  110. } else {
  111. if (len != 1)
  112. errx(1, "Option %s: invalid short option"
  113. " '%.*s'", entry->names, len+1, p-1);
  114. opt_num_short++;
  115. if (entry->type == OPT_HASARG)
  116. opt_num_short_arg++;
  117. }
  118. /* Don't document args unless there are some. */
  119. if (entry->type == OPT_NOARG) {
  120. if (p[len] == ' ' || p[len] == '=')
  121. errx(1, "Option %s: does not take arguments"
  122. " '%s'", entry->names, p+len+1);
  123. }
  124. }
  125. }
  126. static void add_opt(const struct opt_table *entry)
  127. {
  128. opt_table = realloc(opt_table, sizeof(opt_table[0]) * (opt_count+1));
  129. opt_table[opt_count++] = *entry;
  130. }
  131. void _opt_register(const char *names, enum opt_type type,
  132. char *(*cb)(void *arg),
  133. char *(*cb_arg)(const char *optarg, void *arg),
  134. void (*show)(char buf[OPT_SHOW_LEN], const void *arg),
  135. const void *arg, const char *desc)
  136. {
  137. struct opt_table opt;
  138. opt.names = names;
  139. opt.type = type;
  140. opt.cb = cb;
  141. opt.cb_arg = cb_arg;
  142. opt.show = show;
  143. opt.u.carg = arg;
  144. opt.desc = desc;
  145. check_opt(&opt);
  146. add_opt(&opt);
  147. }
  148. void opt_register_table(const struct opt_table entry[], const char *desc)
  149. {
  150. unsigned int i, start = opt_count;
  151. if (desc) {
  152. struct opt_table heading = OPT_SUBTABLE(NULL, desc);
  153. add_opt(&heading);
  154. }
  155. for (i = 0; entry[i].type != OPT_END; i++) {
  156. if (entry[i].type == OPT_SUBTABLE)
  157. opt_register_table(subtable_of(&entry[i]),
  158. entry[i].desc);
  159. else {
  160. check_opt(&entry[i]);
  161. add_opt(&entry[i]);
  162. }
  163. }
  164. /* We store the table length in arg ptr. */
  165. if (desc)
  166. opt_table[start].u.tlen = (opt_count - start);
  167. }
  168. /* Parse your arguments. */
  169. bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...))
  170. {
  171. int ret;
  172. unsigned offset = 0;
  173. /* This helps opt_usage. */
  174. opt_argv0 = argv[0];
  175. while ((ret = parse_one(argc, argv, &offset, errlog)) == 1);
  176. /* parse_one returns 0 on finish, -1 on error */
  177. return (ret == 0);
  178. }
  179. void opt_free_table(void)
  180. {
  181. free(opt_table);
  182. opt_table=0;
  183. }
  184. void opt_log_stderr(const char *fmt, ...)
  185. {
  186. va_list ap;
  187. va_start(ap, fmt);
  188. vfprintf(stderr, fmt, ap);
  189. fprintf(stderr, "\n");
  190. va_end(ap);
  191. }
  192. void opt_log_stderr_exit(const char *fmt, ...)
  193. {
  194. va_list ap;
  195. va_start(ap, fmt);
  196. vfprintf(stderr, fmt, ap);
  197. fprintf(stderr, "\n");
  198. va_end(ap);
  199. exit(1);
  200. }
  201. char *opt_invalid_argument(const char *arg)
  202. {
  203. char *str = malloc(sizeof("Invalid argument '%s'") + strlen(arg));
  204. sprintf(str, "Invalid argument '%s'", arg);
  205. return str;
  206. }