1 #include <ccan/opt/opt.h>
13 struct opt_table *opt_table;
14 unsigned int opt_count;
15 const char *opt_argv0;
17 static void check_opt(const struct opt_table *entry)
19 assert(entry->flags == OPT_HASARG || entry->flags == OPT_NOARG);
20 assert(entry->shortopt || entry->longopt);
21 assert(entry->shortopt != ':');
22 assert(entry->shortopt != '?' || entry->flags == OPT_NOARG);
25 static void add_opt(const struct opt_table *entry)
27 opt_table = realloc(opt_table, sizeof(opt_table[0]) * (opt_count+1));
28 opt_table[opt_count++] = *entry;
31 void _opt_register(const char *longopt, char shortopt, enum opt_flags flags,
32 char *(*cb)(void *arg),
33 char *(*cb_arg)(const char *optarg, void *arg),
34 void *arg, const char *desc)
37 opt.longopt = longopt;
38 opt.shortopt = shortopt;
48 void opt_register_table(const struct opt_table entry[], const char *desc)
50 unsigned int i, start = opt_count;
53 struct opt_table heading = OPT_SUBTABLE(NULL, desc);
56 for (i = 0; entry[i].flags != OPT_END; i++) {
57 if (entry[i].flags == OPT_SUBTABLE)
58 opt_register_table(subtable_of(&entry[i]),
65 /* We store the table length in arg ptr. */
67 opt_table[start].arg = (void *)(intptr_t)(opt_count - start);
70 static char *make_optstring(void)
72 /* Worst case, each one is ":x:", plus nul term. */
73 char *str = malloc(1 + opt_count * 2 + 1);
76 /* This tells getopt_long we want a ':' returned for missing arg. */
79 for (i = 0; i < opt_count; i++) {
80 if (!opt_table[i].shortopt)
82 str[num++] = opt_table[i].shortopt;
83 if (opt_table[i].flags == OPT_HASARG)
90 static struct option *make_options(void)
92 struct option *options = malloc(sizeof(*options) * (opt_count + 1));
95 for (num = i = 0; i < opt_count; i++) {
96 if (!opt_table[i].longopt)
98 options[num].name = opt_table[i].longopt;
99 options[num].has_arg = (opt_table[i].flags == OPT_HASARG);
100 options[num].flag = NULL;
101 options[num].val = 0;
104 memset(&options[num], 0, sizeof(options[num]));
108 static struct opt_table *find_short(char shortopt)
111 for (i = 0; i < opt_count; i++) {
112 if (opt_table[i].shortopt == shortopt)
113 return &opt_table[i];
118 /* We want the index'th long entry. */
119 static struct opt_table *find_long(int index)
122 for (i = 0; i < opt_count; i++) {
123 if (!opt_table[i].longopt)
126 return &opt_table[i];
132 /* glibc does this as:
133 /tmp/opt-example: invalid option -- 'x'
134 /tmp/opt-example: unrecognized option '--long'
135 /tmp/opt-example: option '--someflag' doesn't allow an argument
136 /tmp/opt-example: option '--s' is ambiguous
137 /tmp/opt-example: option requires an argument -- 's'
139 static void parse_fail(void (*errlog)(const char *fmt, ...),
140 char shortopt, const char *longopt, const char *problem)
143 errlog("%s: -%c: %s", opt_argv0, shortopt, problem);
145 errlog("%s: --%s: %s", opt_argv0, longopt, problem);
148 void dump_optstate(void);
149 void dump_optstate(void)
151 printf("opterr = %i, optind = %i, optopt = %i, optarg = %s\n",
152 opterr, optind, optopt, optarg);
155 /* Parse your arguments. */
156 bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...))
158 char *optstring = make_optstring();
159 struct option *options = make_options();
160 int ret, longidx = 0;
163 /* We will do our own error reporting. */
167 /* Reset in case we're called more than once. */
170 while ((ret = getopt_long(*argc, argv, optstring, options, &longidx))
173 bool missing = false;
175 /* optopt is 0 if it's an unknown long option, *or* if
176 * -? is a valid short option. */
178 if (optopt || strncmp(argv[optind-1], "--", 2) == 0) {
179 parse_fail(errlog, optopt, argv[optind-1]+2,
180 "unrecognized option");
183 } else if (ret == ':') {
191 e = find_long(longidx);
193 /* Missing argument */
195 parse_fail(errlog, e->shortopt, e->longopt,
196 "option requires an argument");
200 if (e->flags == OPT_HASARG)
201 problem = e->cb_arg(optarg, e->arg);
203 problem = e->cb(e->arg);
206 parse_fail(errlog, e->shortopt, e->longopt,
217 /* We hide everything but remaining arguments. */
218 memmove(&argv[1], &argv[optind], sizeof(argv[1]) * (*argc-optind+1));
221 return ret == -1 ? true : false;
224 void opt_log_stderr(const char *fmt, ...)
229 vfprintf(stderr, fmt, ap);
233 char *opt_invalid_argument(const char *arg)
235 char *str = malloc(sizeof("Invalid argument '%s'") + strlen(arg));
236 sprintf(str, "Invalid argument '%s'", arg);