strtonum \
resource trans res-fd res-mem res-subpool res-mempool res-eltpool \
daemon daemon-ctrl \
- signames
+ signames \
+ opt
LIBUCW_MAIN_INCLUDES= \
lib.h log.h threads.h time.h \
strtonum.h \
resource.h trans.h \
daemon.h \
- signames.h
+ signames.h \
+ opt.h
ifdef CONFIG_UCW_THREADS
# Some modules require threading
fb-socket.test trie-test.test string.test sha1.test asort-test.test binheap-test.test \
redblack-test.test fb-file.test fb-grow.test fb-pool.test fb-atomic.test \
fb-limfd.test fb-temp.test fb-mem.test fb-buffer.test fb-mmap.test fb-multi.test url.test strtonum-test.test \
- gary.test time.test crc.test signames.test md5.test)
+ gary.test time.test crc.test signames.test md5.test opt.test)
$(o)/ucw/varint.test: $(o)/ucw/varint-t
$(o)/ucw/regex.test: $(o)/ucw/regex-t
$(o)/ucw/crc.test: $(o)/ucw/crc-t
$(o)/ucw/signames.test: $(o)/ucw/signames-t
$(o)/ucw/md5.test: $(o)/ucw/md5-t
+$(o)/ucw/opt.test: $(o)/ucw/opt-t
ifdef CONFIG_UCW_THREADS
TESTS+=$(addprefix $(o)/ucw/,asio.test)
--- /dev/null
+/*
+ * UCW Library -- Parsing of command line options
+ *
+ * (c) 2013 Jan Moskyto Matejka <mq@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#include <ucw/lib.h>
+#include <ucw/opt.h>
+#include <ucw/stkstring.h>
+
+#include <alloca.h>
+
+struct opt_section * opt_section_root;
+
+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->cls == OPT_CL_HELP && item->u.help2 == NULL) continue;
+ if (item->cls == OPT_CL_SECTION) continue;
+
+ uns linelen = 0;
+ if (item->cls == OPT_CL_HELP) { // two-column help line
+ if (first_column < strlen(item->help))
+ first_column = strlen(item->help);
+ continue;
+ }
+
+ if (item->letter) { // will write sth like "-x, --exclusive"
+ linelen = strlen("-x, --") + strlen(item->name);
+ } else { // will write sth like "--exclusive"
+ linelen = strlen("--") + strlen(item->name);
+ }
+
+ if (item->flags | OPT_REQUIRED_VALUE) {
+ linelen += strlen("=value");
+ } else if (!(item->flags | OPT_NO_VALUE)) {
+ linelen += strlen("(=value)");
+ }
+
+ if (linelen > first_column)
+ first_column = linelen;
+ }
+
+ char * spaces = alloca(first_column + 1);
+ char * buf = alloca(first_column + 1);
+ for (uns i=0;i<first_column;i++)
+ spaces[i] = ' ';
+
+ spaces[first_column] = 0;
+
+#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->cls == OPT_CL_HELP) {
+ fprintf(stderr, "%s", item->help);
+ if (item->u.help2 == NULL)
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, "%s %s\n", spaces + strlen(item->help), item->u.help2);
+ } else if (item->cls == OPT_CL_SECTION) {
+ opt_help_noexit_internal(item->u.section);
+ } else if (item->letter) {
+ sprintf(buf, "-%c, --%s%s", item->letter, item->name, VAL(item));
+ fprintf(stderr, "%s%s %s\n", buf, spaces + strlen(buf), item->help);
+ } else {
+ sprintf(buf, "--%s%s", item->name, VAL(item));
+ fprintf(stderr, "%s%s %s\n", buf, spaces + strlen(buf), item->help);
+ }
+ }
+}
+
+void opt_parse(struct opt_section * options) {
+ opt_section_root = options;
+ opt_help();
+}
+
+#ifdef TEST
+
+static enum TEAPOT_TYPE {
+ TEAPOT_STANDARD = 0,
+ TEAPOT_EXCLUSIVE,
+ TEAPOT_GLASS,
+ TEAPOT_HANDS,
+ TEAPOT_UNDEFINED = -1
+} set = TEAPOT_UNDEFINED;
+
+static int english = 0;
+static char * name = NULL;
+static uns sugar = 0;
+static uns verbose = 1;
+static int with_gas = 0;
+static uns black_magic = 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 int parse_temperature(const char * param, void * target) {
+ return 1;
+}
+
+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_BOOL('G', "with-gas", with_gas, OPT_NO_VALUE, "Use water with gas"),
+ OPT_END
+ }
+};
+
+static struct opt_section help = {
+ OPT_ITEMS {
+ OPT_HELP("A simple tea boiling console."),
+ OPT_HELP("Options:"),
+ OPT_SHOW_HELP(0),
+ 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_HELP2("", "Supported scales: Celsius [60C], Fahrenheit [140F],"),
+ OPT_HELP2("", " Kelvin [350K], Rankine [600R] and Reaumur [50Re]"),
+ 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_BOOL('p', "pray", pray, 0, "Pray before boiling"),
+ OPT_HELP(""),
+ OPT_HELP("Water options:"),
+ OPT_SECTION(water_options),
+ OPT_END
+ }
+};
+
+int main(int argc, char ** argv)
+{
+ opt_parse(&help);
+}
+
+#endif
--- /dev/null
+/*
+ * UCW Library -- Parsing of command line options
+ *
+ * (c) 2013 Jan Moskyto Matejka <mq@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#ifndef _UCW_OPT_H
+#define _UCW_OPT_H
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/***
+ * [[opt]]
+ * Parsing of command line options
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ***/
+
+enum opt_class {
+ OPT_CL_END, // end of list
+ OPT_CL_BOOL, // boolean value
+ OPT_CL_STATIC, // static value
+ OPT_CL_SWITCH, // lookup/switch
+ OPT_CL_INC, // incremental value
+ 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);
+
+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
+ union opt_union {
+ 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
+ } u;
+ short flags;
+ enum opt_class cls;
+ enum opt_type type;
+};
+
+struct opt_section {
+ struct opt_item *opt;
+};
+
+#define OPT_ITEMS .opt = ( struct opt_item[] ) /** List of sub-items. **/
+
+/** 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_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
+ * 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_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
+ * OPT_SECTION declares a subsection
+ *
+ * **/
+
+#define OPT_SHOW_HELP(flags) OPT_USER(0, "help", *(NULL), opt_help_success2, flags, "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_SECTION(sec) { .cls = OPT_CL_SECTION, .u.section = &sec }
+#define OPT_END { .cls = OPT_CL_END }
+
+/** Flags for the preceeding calls **/
+#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
+
+extern struct opt_section * opt_section_root;
+void opt_help_noexit_internal(struct opt_section * help);
+
+static void opt_help_noexit(void) {
+ opt_help_noexit_internal(opt_section_root);
+}
+
+static void opt_usage_noexit(void) {
+ fprintf(stderr, "Run with argument --help for more information.\n");
+}
+
+static int opt_help_success2(const char * param UNUSED, void * target UNUSED) {
+ opt_help_noexit();
+ exit(0);
+}
+
+static void opt_help(void) {
+ opt_help_noexit();
+ exit(1);
+}
+
+static void opt_usage(void) {
+ opt_usage_noexit();
+ exit(1);
+}
+
+void opt_parse(struct opt_section * options);
+
+#endif
--- /dev/null
+# Tests of the command line option parser
+
+Name: Opt-1
+Run: ../obj/ucw/opt-t --help
+Out: bagr kombajn