2 * UCW Library -- Parsing of command line options
4 * (c) 2013 Jan Moskyto Matejka <mq@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
13 #include <ucw/stkstring.h>
14 #include <ucw/strtonum.h>
18 static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2);
19 static void opt_failure(const char * mesg, ...) {
22 stk_vprintf(mesg, args);
23 exit(OPT_EXIT_BAD_ARGS);
27 struct opt_section * opt_section_root;
29 void opt_help_noexit_internal(struct opt_section * help) {
32 for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
33 if (item->flags & OPT_NO_HELP) continue;
34 if (item->cls == OPT_CL_HELP && item->u.help2 == NULL) continue;
35 if (item->cls == OPT_CL_SECTION) continue;
38 if (item->cls == OPT_CL_HELP) { // two-column help line
39 if (first_column < strlen(item->help))
40 first_column = strlen(item->help);
44 if (item->letter) { // will write sth like "-x, --exclusive"
45 linelen = strlen("-x, --") + strlen(item->name);
46 } else { // will write sth like "--exclusive"
47 linelen = strlen("--") + strlen(item->name);
50 ASSERT(item->flags & OPT_VALUE_FLAGS);
52 if (item->flags & OPT_REQUIRED_VALUE) {
53 linelen += strlen("=value");
54 } else if (item->flags & OPT_MAYBE_VALUE) {
55 linelen += strlen("(=value)");
58 if (linelen > first_column)
59 first_column = linelen;
62 char * spaces = alloca(first_column + 1);
63 char * buf = alloca(first_column + 1);
64 for (uns i=0;i<first_column;i++)
67 spaces[first_column] = 0;
69 #define VAL(it) ((it->flags & OPT_REQUIRED_VALUE) ? "=value" : ((it->flags & OPT_NO_VALUE) ? "" : "(=value)"))
70 for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
71 if (item->flags & OPT_NO_HELP) continue;
73 if (item->cls == OPT_CL_HELP) {
74 fprintf(stderr, "%s", item->help);
75 if (item->u.help2 == NULL)
76 fprintf(stderr, "\n");
78 fprintf(stderr, "%s %s\n", spaces + strlen(item->help), item->u.help2);
79 } else if (item->cls == OPT_CL_SECTION) {
80 opt_help_noexit_internal(item->u.section);
81 } else if (item->letter) {
82 sprintf(buf, "-%c, --%s%s", item->letter, item->name, VAL(item));
83 fprintf(stderr, "%s%s %s\n", buf, spaces + strlen(buf), item->help);
85 sprintf(buf, "--%s%s", item->name, VAL(item));
86 fprintf(stderr, "%s%s %s\n", buf, spaces + strlen(buf), item->help);
91 void opt_init(struct opt_section * options) {
92 for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++) {
93 if (item->cls == OPT_CL_SECTION)
94 opt_init(item->u.section);
95 else if (!(item->flags & OPT_VALUE_FLAGS)) {
96 if (item->cls == OPT_CL_CALL || item->cls == OPT_CL_USER) {
97 fprintf(stderr, "You MUST specify some of the value flags for the %c/%s item.\n", item->letter, item->name);
100 item->flags |= opt_default_value_flags[item->cls];
103 opt_section_root = options;
106 static struct opt_item * opt_find_item_longopt_section(char * str, struct opt_section * options) {
107 uns len = strlen(str);
108 struct opt_item * candidate = NULL;
110 for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++) {
111 if (item->cls == OPT_CL_SECTION) {
112 struct opt_item * out = opt_find_item_longopt_section(str, item->u.section);
115 opt_failure("Ambiguous prefix %s: Found matching %s and %s.\n", str, candidate->name, item->name);
119 } else if (!strncmp(item->name, str, len)) {
120 if (strlen(item->name) == len)
124 opt_failure("Ambiguous prefix %s: Found matching %s and %s.\n", str, candidate->name, item->name);
136 static struct opt_item * opt_find_item_longopt(char * str) {
137 struct opt_item * out = opt_find_item_longopt_section(str, opt_section_root);
139 opt_failure("Invalid argument: %s\n", str);
143 #define OPT_NAME (longopt ? stk_printf("--%s", item->name) : stk_printf("-%c", item->letter))
144 static void opt_parse_value(struct opt_item * item, char * value, int longopt) {
147 if (!strcasecmp(value, "y") || !strcasecmp(value, "yes") || !strcasecmp(value, "true"))
148 *((int *) item->ptr) = 1;
149 else if (!strcasecmp(value, "n") || !strcasecmp(value, "no") || !strcasecmp(value, "false"))
150 *((int *) item->ptr) = 0;
152 opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): y/n, yes/no, true/false.\n", OPT_NAME);
157 switch (item->type) {
159 e = cf_parse_int(value, item->ptr);
161 opt_failure("Integer value parsing failed for argument %s: %s\n", OPT_NAME, e);
164 e = cf_parse_u64(value, item->ptr);
166 opt_failure("Unsigned 64-bit value parsing failed for argument %s: %s\n", OPT_NAME, e);
169 e = cf_parse_double(value, item->ptr);
171 opt_failure("Double value parsing failed for argument %s: %s\n", OPT_NAME, e);
174 e = cf_parse_ip(value, item->ptr);
176 opt_failure("IP parsing failed for argument %s: %s\n", OPT_NAME, e);
179 item->ptr = strdup(value);
187 if (*((int *)item->ptr) != -1)
188 opt_failure("Multiple switches: %s", OPT_NAME);
190 *((int *)item->ptr) = item->u.value;
193 if (item->flags | OPT_DECREMENT)
194 (*((int *)item->ptr))--;
196 (*((int *)item->ptr))++;
202 e = item->u.utype->parser(value, item->ptr);
204 opt_failure("User defined type value parsing failed for argument %s: %s\n", OPT_NAME, e);
211 static int opt_longopt(char ** argv, int index) {
213 char * name_in = argv[index] + 2; // skipping the -- on the beginning
214 uns pos = strchrnul(name_in, '=') - name_in;
215 struct opt_item * item = opt_find_item_longopt(strndupa(name_in, pos));
217 if (item->flags | OPT_REQUIRED_VALUE) {
218 if (pos < strlen(name_in))
219 value = name_in + pos + 1;
221 value = argv[index+1];
225 else if (item->flags | OPT_MAYBE_VALUE) {
226 if (pos < strlen(name_in))
227 value = name_in + pos + 1;
230 if (pos < strlen(name_in))
231 opt_failure("Argument %s must not have any value.", item->name);
235 void opt_parse(char ** argv, opt_positional * callback) {
237 int force_positional = 0;
238 for (int i=0;argv[i];i++) {
239 if (argv[i][0] != '-' || force_positional) {
242 if (argv[i][1] == '-') {
243 if (argv[i][2] == '\0')
246 i += opt_longopt(argv, i);
249 i += opt_shortopt(argv, i);
257 #include <ucw/fastbuf.h>
259 static int show_version(const char ** param UNUSED) {
260 printf("This is a simple tea boiling console v0.1.\n");
264 struct teapot_temperature {
275 static char * temp_scale_str[] = { "C", "F", "K", "Re", "R" };
277 static enum TEAPOT_TYPE {
282 TEAPOT_UNDEFINED = -1
283 } set = TEAPOT_UNDEFINED;
285 static int english = 0;
286 static char * name = NULL;
287 static int sugar = 0;
288 static int verbose = 1;
289 static int with_gas = 0;
290 static int black_magic = 0;
292 static int water_amount = 0;
294 static const char * teapot_temperature_parser(char * in, void * ptr) {
295 struct teapot_temperature * temp = ptr;
297 const char * err = str_to_int(&temp->value, in, &next, 0);
300 if (!strcmp("C", next))
301 temp->scale = TEMP_CELSIUS;
302 else if (!strcmp("F", next))
303 temp->scale = TEMP_FAHRENHEIT;
304 else if (!strcmp("K", next))
305 temp->scale = TEMP_KELVIN;
306 else if (!strcmp("R", next))
307 temp->scale = TEMP_RANKINE;
308 else if (!strcmp("Re", next))
309 temp->scale = TEMP_REAUMUR;
311 fprintf(stderr, "Unknown scale: %s\n", next);
312 exit(OPT_EXIT_BAD_ARGS);
314 return next + strlen(next);
317 static void teapot_temperature_dumper(struct fastbuf * fb, void * ptr) {
318 struct teapot_temperature * temp = ptr;
319 bprintf(fb, "%d%s", temp->value, temp_scale_str[temp->scale]);
322 static struct cf_user_type teapot_temperature_t = {
323 .size = sizeof(struct teapot_temperature),
324 .name = "teapot_temperature_t",
325 .parser = (cf_parser1*) teapot_temperature_parser,
326 .dumper = (cf_dumper1*) teapot_temperature_dumper
329 static struct opt_section water_options = {
331 OPT_INT('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Amount of water (in mls)"),
332 OPT_BOOL('G', "with-gas", with_gas, OPT_NO_VALUE, "Use water with gas"),
337 static struct opt_section help = {
339 OPT_HELP("A simple tea boiling console."),
340 OPT_HELP("Usage: teapot [options] name-of-the-tea"),
341 OPT_HELP("Black, green or white tea supported as well as fruit or herbal tea."),
342 OPT_HELP("You may specify more kinds of tea, all of them will be boiled for you, in the given order."),
344 OPT_HELP("Options:"),
346 OPT_CALL('V', "version", show_version, OPT_NO_VALUE, "Show the version"),
348 OPT_BOOL('e', "english-style", english, 0, "English style (with milk)"),
349 OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "Amount of sugar (in teaspoons)"),
350 OPT_SWITCH(0, "standard-set", set, TEAPOT_STANDARD, 0, "Standard teapot"),
351 OPT_SWITCH('x', "exclusive-set", set, TEAPOT_EXCLUSIVE, 0, "Exclusive teapot"),
352 OPT_SWITCH('g', "glass-set", set, TEAPOT_GLASS, 0, "Transparent glass teapot"),
353 OPT_SWITCH('h', "hands", set, TEAPOT_HANDS, 0, "Use user's hands as a teapot (a bit dangerous)"),
354 OPT_USER('t', "temperature", temperature, teapot_temperature_t, OPT_REQUIRED_VALUE,
355 "Wanted final temperature of the tea to be served\n"
356 "\t\tSupported scales: \tCelsius [60C], Fahrenheit [140F],"
357 "\t\t\tKelvin [350K], Rankine [600R] and Reaumur [50Re]"
358 "\t\tOnly integer values allowed."),
359 OPT_INC('v', "verbose", verbose, 0, "Verbose (the more -v, the more verbose)"),
360 OPT_INC('q', "quiet", verbose, OPT_DECREMENT, "Quiet (the more -q, the more quiet)"),
361 OPT_INT('b', "black-magic", black_magic, 0, "Use black magic to make the tea extraordinary delicious"),
362 OPT_BOOL('p', "pray", pray, 0, "Pray before boiling"),
364 OPT_HELP("Water options:"),
365 OPT_SECTION(water_options),
370 static void boil_tea(const char * name) {
371 printf("Boiling a tea: %s\n", name);
374 int main(int argc, char ** argv)
380 opt_parse(argv, NULL);
382 for (int i=0; i<teas_num; i++)
385 printf("Everything OK. Bye.\n");