#include <ucw/lib.h>
#include <ucw/opt.h>
+#include <ucw/conf.h>
#include <ucw/stkstring.h>
#include <ucw/strtonum.h>
#include <alloca.h>
+static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2);
+static void opt_failure(const char * mesg, ...) {
+ va_list args;
+ va_start(args, mesg);
+ stk_vprintf(mesg, args);
+ exit(OPT_EXIT_BAD_ARGS);
+ va_end(args);
+}
+
struct opt_section * opt_section_root;
void opt_help_noexit_internal(struct opt_section * help) {
}
void opt_init(struct opt_section * options) {
- opt_section_root = options;
- for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++)
- if (!(item->flags & OPT_VALUE_FLAGS)) {
+ for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++) {
+ if (item->cls == OPT_CL_SECTION)
+ opt_init(item->u.section);
+ else 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);
+ ASSERT(0);
} else
item->flags |= opt_default_value_flags[item->cls];
}
+ }
+ opt_section_root = options;
+}
+
+static struct opt_item * opt_find_item_longopt_section(char * str, struct opt_section * options) {
+ uns len = strlen(str);
+ struct opt_item * candidate = NULL;
+
+ for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++) {
+ if (item->cls == OPT_CL_SECTION) {
+ struct opt_item * out = opt_find_item_longopt_section(str, item->u.section);
+ if (out) {
+ if (candidate)
+ opt_failure("Ambiguos prefix %s: Found matching %s and %s.\n", str, candidate->name, item->name);
+ else
+ candidate = out;
+ }
+ } else if (!strncmp(item->name, str, len)) {
+ if (strlen(item->name) == len)
+ return item;
+
+ if (candidate)
+ opt_failure("Ambiguos prefix %s: Found matching %s and %s.\n", str, candidate->name, item->name);
+ else
+ candidate = item;
+ }
+ }
+
+ if (candidate)
+ return candidate;
+ else {
+ }
+}
+
+static struct opt_item * opt_find_item_longopt(char * str) {
+ struct opt_item * out = opt_find_item_longopt_section(str, opt_section_root);
+ if (out == NULL)
+ opt_failure("Invalid argument: %s\n", str);
+ return out;
+}
+
+#define OPT_NAME (longopt ? stk_printf("--%s", item->name) : stk_printf("-%c", item->letter))
+static void opt_parse_value(struct opt_item * item, char * value, int longopt) {
+ switch (item->cls) {
+ case OPT_CL_BOOL:
+ if (!strcasecmp(value, "y") || !strcasecmp(value, "yes") || !strcasecmp(value, "true"))
+ *((int *) item->ptr) = 1;
+ else if (!strcasecmp(value, "n") || !strcasecmp(value, "no") || !strcasecmp(value, "false"))
+ *((int *) item->ptr) = 0;
+ else
+ opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): y/n, yes/no, true/false.\n", OPT_NAME);
+ break;
+ case OPT_CL_STATIC:
+ {
+ char * e = NULL;
+ switch (item->type) {
+ case CT_INT:
+ e = cf_parse_int(value, item->ptr);
+ if (e)
+ opt_failure("Integer value parsing failed for argument %s: %s\n", OPT_NAME, e);
+ break;
+ case CT_U64:
+ e = cf_parse_u64(value, item->ptr);
+ if (e)
+ opt_failure("Unsigned 64-bit value parsing failed for argument %s: %s\n", OPT_NAME, e);
+ break;
+ case CT_DOUBLE:
+ e = cf_parse_double(value, item->ptr);
+ if (e)
+ opt_failure("Double value parsing failed for argument %s: %s\n", OPT_NAME, e);
+ break;
+ case CT_IP:
+ e = cf_parse_ip(value, item->ptr);
+ if (e)
+ opt_failure("IP parsing failed for argument %s: %s\n", OPT_NAME, e);
+ break;
+ case CT_STRING:
+ item->ptr = strdup(value);
+ break;
+ default:
+ ASSERT(0);
+ }
+ break;
+ }
+ case OPT_CL_SWITCH:
+ if (*((int *)item->ptr) != -1)
+ opt_failure("Multiple switches: %s", OPT_NAME);
+ else
+ *((int *)item->ptr) = item->u.value;
+ break;
+ case OPT_CL_INC:
+ if (item->flags | OPT_DECREMENT)
+ (*((int *)item->ptr))--;
+ else
+ (*((int *)item->ptr))++;
+ case OPT_CL_CALL:
+
+ case OPT_CL_USER:
+ {
+ char * e = NULL;
+ e = item->u.utype->parser(value, item->ptr);
+ if (e)
+ opt_failure("User defined type value parsing failed for argument %s: %s\n", OPT_NAME, e);
+ break;
+ }
+ }
+}
+#undef OPT_NAME
+
+static int opt_longopt(char ** argv, int index) {
+ int eaten;
+ char * name_in = argv[index] + 2; // skipping the -- on the beginning
+ uns pos = strchrnul(name_in, '=') - name_in;
+ struct opt_item * item = opt_find_item_longopt(strndupa(name_in, pos));
+ char * value = NULL;
+ if (item->flags | OPT_REQUIRED_VALUE) {
+ if (pos < strlen(name_in))
+ value = name_in + pos + 1;
+ else {
+ value = argv[index+1];
+ eaten++;
+ }
+ }
+ else if (item->flags | OPT_MAYBE_VALUE) {
+ if (pos < strlen(name_in))
+ value = name_in + pos + 1;
+ }
+ else {
+ if (pos < strlen(name_in))
+ opt_failure("Argument %s must not have any value.", item->name);
+ }
}
-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;
+void opt_parse(char ** argv, opt_positional * callback) {
+
+ int force_positional = 0;
+ for (int i=0;argv[i];i++) {
+ if (argv[i][0] != '-' || force_positional) {
+ callback(argv[i]);
+ } else {
+ if (argv[i][1] == '-') {
+ if (argv[i][2] == '\0')
+ force_positional++;
+ else
+ i += opt_longopt(argv, i);
+ }
+ else if (argv[i][1])
+ i += opt_shortopt(argv, i);
+ else
+ callback(argv[i]);
+ }
+ }
}
#ifdef TEST
static int show_version(const char ** param UNUSED) {
printf("This is a simple tea boiling console v0.1.\n");
- exit(0);
+ exit(EXIT_SUCCESS);
}
struct teapot_temperature {
static int english = 0;
static char * name = NULL;
-static uns sugar = 0;
-static uns verbose = 1;
+static int sugar = 0;
+static int verbose = 1;
static int with_gas = 0;
-static uns black_magic = 0;
+static int black_magic = 0;
static int pray = 0;
-static uns water_amount = 0;
+static int water_amount = 0;
static const char * teapot_temperature_parser(char * in, void * ptr) {
struct teapot_temperature * temp = ptr;
temp->scale = TEMP_REAUMUR;
else {
fprintf(stderr, "Unknown scale: %s\n", next);
- exit(1);
+ exit(OPT_EXIT_BAD_ARGS);
}
return next + strlen(next);
}
}
static struct cf_user_type teapot_temperature_t = {
- .size = 2*sizeof(int),
+ .size = sizeof(struct teapot_temperature),
.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)"),
+ OPT_INT('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Amount of water (in mls)"),
OPT_BOOL('G', "with-gas", with_gas, OPT_NO_VALUE, "Use water with gas"),
OPT_END
}
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,
+ OPT_HELP_OPTION,
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_UNS('s', "sugar", sugar, OPT_REQUIRED_VALUE, "Amount of sugar (in teaspoons)"),
+ OPT_INT('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, 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_USER('t', "temperature", temperature, teapot_temperature_t, OPT_REQUIRED_VALUE,
+ "Wanted final temperature of the tea to be served\n"
+ "\t\tSupported scales: \tCelsius [60C], Fahrenheit [140F],"
+ "\t\t\tKelvin [350K], Rankine [600R] and Reaumur [50Re]"
+ "\t\tOnly 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"),
+ OPT_INT('b', "black-magic", black_magic, 0, "Use black magic to make the tea extraordinary delicious"),
OPT_BOOL('p', "pray", pray, 0, "Pray before boiling"),
OPT_HELP(""),
OPT_HELP("Water options:"),
int teas_num;
opt_init(&help);
- teas_num = opt_parse(argv, &teas);
+ opt_parse(argv, NULL);
for (int i=0; i<teas_num; i++)
boil_tea(teas[i]);
#include <stdlib.h>
#include <stdio.h>
+#define OPT_EXIT_BAD_ARGS 2
+
/***
* [[opt]]
***/
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
int value; // value for OPT_SWITCH
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
+ struct cf_user_type * utype; // specification of the user-defined type
} u;
- short flags;
- enum opt_class cls;
- enum cf_type type;
+ const char letter; // short-op
+ byte flags;
+ byte cls; // enum opt_class
+ byte type; // enum cf_type
};
struct opt_section {
* Sub-items to be enclosed in OPT_ITEMS { } list
* ----------------------------------------------
*
- * OPT_SHOW_HELP declares --help and prints a line about that
- * OPT_HELP prints a line into help()
+ * OPT_HELP_OPTION declares --help and prints a line about that
+ * OPT_HELP prints a line into help
* OPT_HELP2 prints two strings onto a line using the same tab structure as the option listing
* OPT_BOOL declares boolean option with an auto-negation (--sth and --no-sth); may be changed by OPT_BOOL_SET_PREFIXES
* OPT_STRING, OPT_UNS, OPT_INT declare simple string/uns/int option
*
***/
-#define OPT_SHOW_HELP OPT_CALL(0, "help", opt_show_help_internal, OPT_NO_VALUE, "Show this help")
+#define OPT_HELP_OPTION 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_HELP2(first, second) { .help = first, .cls = OPT_CL_HELP, .u.help2 = second } // FIXME: remove this
#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_U64(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, u64 *), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_U64 }
#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_DOUBLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, double *), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_DOUBLE }
+#define OPT_IP(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, u32 *), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_IP }
#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 }
void opt_init(struct opt_section * options);
/**
- * Parse all the arguments.
- * Returns the number of positional arguments and an array of them in @posargs.
+ * Positional argument handler to be given to opt_parse()
**/
-int opt_parse(char ** argv, char *** posargs);
+typedef void opt_positional(const char * str);
/**
- * 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.
+ * Parse all the arguments. Run the @callback for each of the positional argument.
**/
-int opt_get(char ** argv);
+void opt_parse(char ** argv, opt_positional * callback);
#endif