#include <ucw/lib.h>
#include <ucw/opt.h>
#include <ucw/stkstring.h>
+#include <ucw/strtonum.h>
#include <alloca.h>
void opt_help_noexit_internal(struct opt_section * help) {
uns first_column = 0;
+
for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
- if (item->flags | OPT_NO_HELP) continue;
+ if (item->flags & OPT_NO_HELP) continue;
if (item->cls == OPT_CL_HELP && item->u.help2 == NULL) continue;
if (item->cls == OPT_CL_SECTION) continue;
linelen = strlen("--") + strlen(item->name);
}
- if (item->flags | OPT_REQUIRED_VALUE) {
+ ASSERT(item->flags & OPT_VALUE_FLAGS);
+
+ if (item->flags & OPT_REQUIRED_VALUE) {
linelen += strlen("=value");
- } else if (!(item->flags | OPT_NO_VALUE)) {
+ } else if (item->flags & OPT_MAYBE_VALUE) {
linelen += strlen("(=value)");
}
spaces[first_column] = 0;
-#define VAL(it) ((it->flags | OPT_REQUIRED_VALUE) ? "=value" : ((it->flags | OPT_NO_VALUE) ? "" : "(=value)"))
+#define VAL(it) ((it->flags & OPT_REQUIRED_VALUE) ? "=value" : ((it->flags & OPT_NO_VALUE) ? "" : "(=value)"))
for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
- if (item->flags | OPT_NO_HELP) continue;
+ if (item->flags & OPT_NO_HELP) continue;
if (item->cls == OPT_CL_HELP) {
fprintf(stderr, "%s", item->help);
}
}
-void opt_parse(struct opt_section * options) {
+void opt_init(struct opt_section * options) {
opt_section_root = options;
- opt_help();
+ for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++)
+ if (!(item->flags & OPT_VALUE_FLAGS)) {
+ if (item->cls == OPT_CL_CALL || item->cls == OPT_CL_USER) {
+ fprintf(stderr, "You MUST specify some of the value flags for the %c/%s item.\n", item->letter, item->name);
+ exit(2);
+ } else
+ item->flags |= opt_default_value_flags[item->cls];
+ }
+}
+
+int opt_parse(char ** argv, char *** posargs) {
+ // Temporary. To be fixed.
+ static char ** d;
+ d = xmalloc(sizeof (*d) * 2);
+ d[0] = "darjeeling";
+ d[1] = "puerh";
+ *posargs = d;
+ return 2;
}
#ifdef TEST
+#include <ucw/fastbuf.h>
+
+static int show_version(const char ** param UNUSED) {
+ printf("This is a simple tea boiling console v0.1.\n");
+ exit(0);
+}
+
+struct teapot_temperature {
+ enum {
+ TEMP_CELSIUS = 0,
+ TEMP_FAHRENHEIT,
+ TEMP_KELVIN,
+ TEMP_REAUMUR,
+ TEMP_RANKINE
+ } scale;
+ int value;
+} temperature;
+
+static char * temp_scale_str[] = { "C", "F", "K", "Re", "R" };
static enum TEAPOT_TYPE {
TEAPOT_STANDARD = 0,
static int pray = 0;
static uns water_amount = 0;
-static struct teapot_temperature {
- enum {
- TEMP_CELSIUS,
- TEMP_FAHRENHEIT,
- TEMP_KELVIN,
- TEMP_REAUMUR
- } scale;
- int value;
-} temperature;
+static const char * teapot_temperature_parser(char * in, void * ptr) {
+ struct teapot_temperature * temp = ptr;
+ const char * next;
+ const char * err = str_to_int(&temp->value, in, &next, 0);
+ if (err)
+ return err;
+ if (!strcmp("C", next))
+ temp->scale = TEMP_CELSIUS;
+ else if (!strcmp("F", next))
+ temp->scale = TEMP_FAHRENHEIT;
+ else if (!strcmp("K", next))
+ temp->scale = TEMP_KELVIN;
+ else if (!strcmp("R", next))
+ temp->scale = TEMP_RANKINE;
+ else if (!strcmp("Re", next))
+ temp->scale = TEMP_REAUMUR;
+ else {
+ fprintf(stderr, "Unknown scale: %s\n", next);
+ exit(1);
+ }
+ return next + strlen(next);
+}
-static int parse_temperature(const char * param, void * target) {
- return 1;
+static void teapot_temperature_dumper(struct fastbuf * fb, void * ptr) {
+ struct teapot_temperature * temp = ptr;
+ bprintf(fb, "%d%s", temp->value, temp_scale_str[temp->scale]);
}
+static struct cf_user_type teapot_temperature_t = {
+ .size = 2*sizeof(int),
+ .name = "teapot_temperature_t",
+ .parser = (cf_parser1*) teapot_temperature_parser,
+ .dumper = (cf_dumper1*) teapot_temperature_dumper
+};
+
static struct opt_section water_options = {
OPT_ITEMS {
OPT_UNS('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Amount of water (in mls)"),
static struct opt_section help = {
OPT_ITEMS {
OPT_HELP("A simple tea boiling console."),
+ OPT_HELP("Usage: teapot [options] name-of-the-tea"),
+ OPT_HELP("Black, green or white tea supported as well as fruit or herbal tea."),
+ OPT_HELP("You may specify more kinds of tea, all of them will be boiled for you, in the given order."),
+ OPT_HELP(""),
OPT_HELP("Options:"),
- OPT_SHOW_HELP(0),
+ OPT_SHOW_HELP,
+ OPT_CALL('V', "version", show_version, OPT_NO_VALUE, "Show the version"),
+ OPT_HELP(""),
OPT_BOOL('e', "english-style", english, 0, "English style (with milk)"),
- OPT_STRING('n', "name", name, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Name of the tea to be prepared"),
OPT_UNS('s', "sugar", sugar, OPT_REQUIRED_VALUE, "Amount of sugar (in teaspoons)"),
OPT_SWITCH(0, "standard-set", set, TEAPOT_STANDARD, 0, "Standard teapot"),
OPT_SWITCH('x', "exclusive-set", set, TEAPOT_EXCLUSIVE, 0, "Exclusive teapot"),
OPT_SWITCH('g', "glass-set", set, TEAPOT_GLASS, 0, "Transparent glass teapot"),
OPT_SWITCH('h', "hands", set, TEAPOT_HANDS, 0, "Use user's hands as a teapot (a bit dangerous)"),
- OPT_USER('t', "temperature", temperature, parse_temperature, OPT_REQUIRED_VALUE, "Wanted final temperature of the tea to be served"),
+ OPT_USER('t', "temperature", temperature, teapot_temperature_t, OPT_REQUIRED_VALUE, "Wanted final temperature of the tea to be served"),
OPT_HELP2("", "Supported scales: Celsius [60C], Fahrenheit [140F],"),
OPT_HELP2("", " Kelvin [350K], Rankine [600R] and Reaumur [50Re]"),
+ OPT_HELP2("", "Only integer values allowed."),
OPT_INC('v', "verbose", verbose, 0, "Verbose (the more -v, the more verbose)"),
OPT_INC('q', "quiet", verbose, OPT_DECREMENT, "Quiet (the more -q, the more quiet)"),
OPT_UNS('b', "black-magic", black_magic, 0, "Use black magic to make the tea extraordinary delicious"),
}
};
+static void boil_tea(const char * name) {
+ printf("Boiling a tea: %s\n", name);
+}
+
int main(int argc, char ** argv)
{
- opt_parse(&help);
+ char ** teas;
+ int teas_num;
+
+ opt_init(&help);
+ teas_num = opt_parse(argv, &teas);
+
+ for (int i=0; i<teas_num; i++)
+ boil_tea(teas[i]);
+
+ printf("Everything OK. Bye.\n");
}
#endif
#ifndef _UCW_OPT_H
#define _UCW_OPT_H
+#include <ucw/lib.h>
+#include <ucw/conf.h>
+
#include <stdlib.h>
#include <stdio.h>
OPT_CL_STATIC, // static value
OPT_CL_SWITCH, // lookup/switch
OPT_CL_INC, // incremental value
+ OPT_CL_CALL, // call a function
OPT_CL_USER, // user defined value
OPT_CL_SECTION, // subsection
OPT_CL_HELP, // help line
};
-enum opt_type {
- OPT_CT_INT, OPT_CT_64, OPT_CT_DOUBLE, // number
- OPT_CT_STRING, // string
- OPT_CT_LOOKUP, // lookup/switch
- OPT_CT_USER, // user defined
-};
-
-typedef int opt_custom_parser(const char * param, void * target);
+typedef void opt_custom_function(const char ** param);
struct opt_section;
struct opt_item {
const char letter; // short-op
- const char *name; // long-op
- void *ptr; // where to save
- const char *help; // description in --help
+ const char * name; // long-op
+ void * ptr; // where to save
+ const char * help; // description in --help
union opt_union {
- struct opt_section *section; // subsection for OPT_SECTION
+ struct opt_section * section; // subsection for OPT_SECTION
int value; // value for OPT_SWITCH
- opt_custom_parser *parser; // parser for OPT_USER
- const char *help2; // second value for OPT_HELP2
+ const char * help2; // second value for OPT_HELP2
+ int (* call)(const char ** param); // function to call for OPT_CALL
+ struct cf_user_type * utype; // specification of the user-defined type
} u;
short flags;
enum opt_class cls;
- enum opt_type type;
+ enum cf_type type;
};
struct opt_section {
- struct opt_item *opt;
+ struct opt_item * opt;
};
#define OPT_ITEMS .opt = ( struct opt_item[] ) /** List of sub-items. **/
* OPT_SWITCH declares one choice of a switch statement; these have common target and different `value`s; last wins unless OPT_SINGLE is set;
* parser fails if it matches an OPT_SWITCH with OPT_SINGLE set and also target set.
* Target must be of signed integer type; it is set to -1 if no switch appears at the command-line.
+ * OPT_CALL calls the given function with all the remaining command line, it returns the number of arguments to be skipped.
* OPT_USER declares a custom type of value; parser is of type opt_custom_parser
* and returns 1 on success and 0 on failure
* OPT_INC declares an incremental value like -v/--verbose
*
* **/
-#define OPT_SHOW_HELP(flags) OPT_USER(0, "help", *(NULL), opt_help_success2, flags, "Show this help")
+#define OPT_SHOW_HELP OPT_CALL(0, "help", opt_show_help_internal, OPT_NO_VALUE, "Show this help")
#define OPT_HELP(line) OPT_HELP2(line, NULL)
#define OPT_HELP2(first, second) { .help = first, .cls = OPT_CL_HELP, .u.help2 = second }
-#define OPT_BOOL(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_BOOL, .type = OPT_CT_INT }
-#define OPT_STRING(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, char**), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = OPT_CT_STRING }
-#define OPT_UNS(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, uns*), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = OPT_CT_INT }
-#define OPT_INT(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, int*), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = OPT_CT_INT }
-#define OPT_SWITCH(shortopt, longopt, target, val, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, int*), .help = desc, .flags = fl, .cls = OPT_CL_SWITCH, .type = OPT_CT_INT, .u.value = val }
-#define OPT_USER(shortopt, longopt, target, pa, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .u.parser = pa, .flags = fl, .help = desc, .cls = OPT_CL_USER, .type = OPT_CT_USER }
-#define OPT_INC(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .flags = fl, .help = desc, .cls = OPT_CL_INC, .type = OPT_CT_INT }
+#define OPT_BOOL(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_BOOL, .type = CT_INT }
+#define OPT_STRING(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, char **), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_STRING }
+#define OPT_UNS(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, uns *), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_INT }
+#define OPT_INT(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, int *), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_INT }
+#define OPT_SWITCH(shortopt, longopt, target, val, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, int *), .help = desc, .flags = fl, .cls = OPT_CL_SWITCH, .type = CT_LOOKUP, .u.value = val }
+#define OPT_CALL(shortopt, longopt, fn, fl, desc) { .letter = shortopt, .name = longopt, .ptr = NULL, .help = desc, .u.call = fn, .flags = fl, .cls = OPT_CL_CALL, .type = CT_USER }
+#define OPT_USER(shortopt, longopt, target, ttype, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .u.utype = &ttype, .flags = fl, .help = desc, .cls = OPT_CL_USER, .type = CT_USER }
+#define OPT_INC(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .flags = fl, .help = desc, .cls = OPT_CL_INC, .type = CT_INT }
#define OPT_SECTION(sec) { .cls = OPT_CL_SECTION, .u.section = &sec }
#define OPT_END { .cls = OPT_CL_END }
#define OPT_REQUIRED 0x1 // Argument must appear at the command line
#define OPT_REQUIRED_VALUE 0x2 // Argument must have a value
#define OPT_NO_VALUE 0x4 // Argument must have no value
-#define OPT_DECREMENT 0x8 // Reversing the effect of OPT_INC
-#define OPT_SINGLE 0x10 // Argument must appear at most once
-#define OPT_NO_HELP 0x20 // Omit this line from help
+#define OPT_MAYBE_VALUE 0x8 // Argument may have a value
+#define OPT_VALUE_FLAGS (OPT_REQUIRED_VALUE | OPT_NO_VALUE | OPT_MAYBE_VALUE)
+#define OPT_DECREMENT 0x10 // Reversing the effect of OPT_INC
+#define OPT_SINGLE 0x20 // Argument must appear at most once
+#define OPT_NO_HELP 0x40 // Omit this line from help
+
+/** Value flags defaults:
+ * OPT_NO_VALUE for OPT_BOOL, OPT_SWITCH and OPT_INC
+ * OPT_MAYBE_VALUE for OPT_STRING, OPT_UNS, OPT_INT
+ * Some of the value flags (OPT_NO_VALUE, OPT_MAYBE_VALUE, OPT_REQUIRED_VALUE)
+ * must be specified for OPT_CALL and OPT_USER.
+ */
+static uns opt_default_value_flags[] = {
+ [OPT_CL_BOOL] = OPT_NO_VALUE,
+ [OPT_CL_STATIC] = OPT_MAYBE_VALUE,
+ [OPT_CL_SWITCH] = OPT_NO_VALUE,
+ [OPT_CL_INC] = OPT_NO_VALUE,
+ [OPT_CL_CALL] = 0,
+ [OPT_CL_USER] = 0,
+ [OPT_CL_SECTION] = 0,
+ [OPT_CL_HELP] = 0
+};
extern struct opt_section * opt_section_root;
void opt_help_noexit_internal(struct opt_section * help);
fprintf(stderr, "Run with argument --help for more information.\n");
}
-static int opt_help_success2(const char * param UNUSED, void * target UNUSED) {
+static int opt_show_help_internal(const char ** param UNUSED) {
opt_help_noexit();
exit(0);
}
exit(1);
}
-void opt_parse(struct opt_section * options);
+/**
+ * Init the opt engine.
+ */
+void opt_init(struct opt_section * options);
+
+/**
+ * Parse all the arguments.
+ * Returns the number of positional arguments and an array of them in @posargs.
+ */
+int opt_parse(char ** argv, char *** posargs);
+
+/**
+ * Parse all the arguments until first positional argument is found.
+ * Returns the position of that argument in argv.
+ * On next call of this function, start from the next item in argv.
+ * Iterate this function to get all the positional arguments.
+ */
+int opt_get(char ** argv);
#endif