*
* (c) 2013 Jan Moskyto Matejka <mq@ucw.cz>
* (c) 2014 Martin Mares <mj@ucw.cz>
+ * (c) 2014 Pavel Charvat <pchar@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
* the option is given as `--no-`'option' with no argument.
* - `OPT_CL_STATIC`: options of this class just take a value and store
* it in the variable.
+ * - `OPT_CL_MULTIPLE`: collect values from all occurrences of this
+ * option in a growing array (see `gary.h`).
* - `OPT_CL_SWITCH`: a multiple-choice switch, which sets the variable
* to a fixed value provided in option definition.
* - `OPT_CL_INC`: increments the variable (or decrements, if the
* contents of another list of options.
* - `OPT_CL_HELP`: no option, just print a help text.
* - `OPT_CL_HOOK`: no option, but a definition of a <<hooks,hook>>.
+ * - `OPT_CL_BREAK`: when a given option occurs, stop parsing and keep
+ * the option in the argument list.
***/
enum opt_class {
OPT_CL_END,
OPT_CL_BOOL,
OPT_CL_STATIC,
+ OPT_CL_MULTIPLE,
OPT_CL_SWITCH,
OPT_CL_INC,
OPT_CL_CALL,
OPT_CL_SECTION,
OPT_CL_HELP,
OPT_CL_HOOK,
+ OPT_CL_BREAK,
};
/***
/** A section of option list. **/
struct opt_section {
- struct opt_item * opt;
+ const struct opt_item * opt;
};
/** A definition of a single option item. **/
const char * name; // long name (NULL if none)
int letter; // short name (0 if none)
void * ptr; // variable to store the value to
- const char * help; // description in --help
+ const char * help; // description in --help (NULL to omit the option from the help)
union opt_union {
- struct opt_section * section; // subsection for OPT_CL_SECTION
+ const struct opt_section * section; // subsection for OPT_CL_SECTION
int value; // value for OPT_CL_SWITCH
- void (* call)(struct opt_item * opt, const char * value, void * data); // function to call for OPT_CL_CALL
- void (* hook)(struct opt_item * opt, uns event, const char * value, void * data); // function to call for OPT_CL_HOOK
+ void (* call)(const struct opt_item * opt, const char * value, void * data); // function to call for OPT_CL_CALL
+ void (* hook)(const struct opt_item * opt, uint event, const char * value, void * data); // function to call for OPT_CL_HOOK
struct cf_user_type * utype; // specification of the user-defined type for CT_USER
} u;
u16 flags; // as defined below (for hooks, event mask is stored instead)
#define OPT_NO_VALUE 0x4 /** The option must have no value. **/
#define OPT_MAYBE_VALUE 0x8 /** The option may have a value. **/
#define OPT_NEGATIVE 0x10 /** Reversing the effect of OPT_INC or saving @false into OPT_BOOL. **/
-#define OPT_NO_HELP 0x20 /** Exclude this option from the help. **/
#define OPT_LAST_ARG 0x40 /** Stop processing arguments after this line. **/
#define OPT_SINGLE 0x100 /** The option must appear at most once. **/
#define OPT_MULTIPLE 0x200 /** The option may appear multiple times; will save all the values into a simple list. **/
#define OPT_SEEN_AS_LONG 0x400 // Used internally to signal that we currently process the long form of the option
#define OPT_BEFORE_CONFIG 0x800 /** The option may appear before a config file is loaded. **/
+#define OPT_HELP_COL 0x1000 /** Used for OPT_CL_HELP to signal that tabs switch columns. **/
#define OPT_INTERNAL 0x4000 // Used internally to ask for passing of struct opt_context to OPT_CALL
/**
* according to option class:
*
* - `OPT_MAYBE_VALUE` for `OPT_CL_STATIC`
+ * - `OPT_REQUIRED_VALUE` for `OPT_CL_MULTIPLE`
* - `OPT_NO_VALUE` for `OPT_CL_BOOL`, `OPT_CL_SWITCH` and `OPT_CL_INC`
* - An error is reported in all other cases.
**/
/** No option, just a piece of help text. **/
#define OPT_HELP(line) { .help = line, .cls = OPT_CL_HELP }
+/** Like OPT_HELP, but the help text uses tab characters to switch columns like help text for ordinary options does. **/
+#define OPT_HELP_COLUMNS(line) { .help = line, .flags = OPT_HELP_COL, .cls = OPT_CL_HELP }
+
/** Standard `--help` option. **/
#define OPT_HELP_OPTION OPT_CALL(0, "help", opt_handle_help, NULL, OPT_BEFORE_CONFIG | OPT_INTERNAL | OPT_NO_VALUE, "\tShow this help")
/** Boolean option. @target should be a variable of type `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_BOOL(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, int *), .help = desc, .flags = fl, .cls = OPT_CL_BOOL, .type = CT_INT }
/** String option. @target should be a variable of type `char *`. **/
-#define OPT_STRING(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_STRING }
+#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 }
-// FIXME: Check that the target is of the right type (likewise in other statically typed options)
/** Ordinary integer option. @target should be a variable of type `int`. **/
-#define OPT_INT(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .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 }
+
+/** Unsigned integer option. @target should be a variable of type `uint`. **/
+#define OPT_UINT(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, uint *), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_INT }
/** 64-bit integer option. @target should be a variable of type `u64`. **/
-#define OPT_U64(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_U64 }
+#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 }
/** Floating-point option. @target should be a variable of type `double`. **/
-#define OPT_DOUBLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_DOUBLE }
+#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 }
/** IP address option, currently IPv4 only. @target should be a variable of type `u32`. **/
-#define OPT_IP(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_IP }
+#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 }
+
+/** Multi-valued string option. @target should be a growing array of `char *`s. **/
+#define OPT_STRING_MULTIPLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, char ***), .help = desc, .flags = fl, .cls = OPT_CL_MULTIPLE, .type = CT_STRING }
+
+/** Multi-valued integer option. @target should be a growing array of `int`s. **/
+#define OPT_INT_MULTIPLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, int **), .help = desc, .flags = fl, .cls = OPT_CL_MULTIPLE, .type = CT_INT }
+
+/** Multi-valued unsigned integer option. @target should be a growing array of `uint`s. **/
+#define OPT_UINT_MULTIPLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, uint **), .help = desc, .flags = fl, .cls = OPT_CL_MULTIPLE, .type = CT_INT }
+
+/** Multi-valued 64-bit integer option. @target should be a growing array of `u64`s. **/
+#define OPT_U64_MULTIPLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, u64 **), .help = desc, .flags = fl, .cls = OPT_CL_MULTIPLE, .type = CT_U64 }
+
+/** Multi-valued floating-point option. @target should be a growing array of `double`s. **/
+#define OPT_DOUBLE_MULTIPLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, double **), .help = desc, .flags = fl, .cls = OPT_CL_MULTIPLE, .type = CT_DOUBLE }
+
+/** Multi-valued IPv4 address option. @target should be a growing array of `u32`s. **/
+#define OPT_IP_MULTIPLE(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, u32 **), .help = desc, .flags = fl, .cls = OPT_CL_MULTIPLE, .type = CT_IP }
/** Switch option. @target should be a variable of type `int` and it will be set to the value @val. **/
-#define OPT_SWITCH(shortopt, longopt, target, val, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_SWITCH, .type = CT_LOOKUP, .u.value = val }
+#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 }
/** Incrementing option. @target should be a variable of type `int`. **/
-#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_INC(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, int *), .flags = fl, .help = desc, .cls = OPT_CL_INC, .type = CT_INT }
+
+/** Breakpoint option. When this option occurs, parsing is terminated and the option is kept in the argument array. **/
+#define OPT_BREAK(shortopt, longopt, fl) { .letter = shortopt, .name = longopt, .flags = fl, .cls = OPT_CL_BREAK }
+
+/* FIXME: Backwards compatibility only, should not be used anymore. */
+#define OPT_UNS OPT_UINT
+#define OPT_UNS_MULTIPLE OPT_UINT_MULTIPLE
+
/**
* When this option appears, call the function @fn with parameters @item, @value, @data,
/**
* An option with user-defined syntax. @ttype is a <<conf:struct_cf_user_type,`cf_user_type`>>
- * describing the syntax, @target is a variable of the corresponding type.
+ * describing the syntax, @target is a variable of the corresponding type. If the @OPT_REQUIRED_VALUE
+ * flag is not set, the parser must be able to parse a NULL value.
**/
#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_STATIC, .type = CT_USER }
+/** Multi-valued option of user-defined type. @target should be a growing array of the right kind of items. **/
+#define OPT_USER_MULTIPLE(shortopt, longopt, target, ttype, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .u.utype = &ttype, .flags = fl, .help = desc, .cls = OPT_CL_MULTIPLE, .type = CT_USER }
+
/** A sub-section. **/
#define OPT_SECTION(sec) { .cls = OPT_CL_SECTION, .u.section = &sec }
void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2) NONRET;
void opt_help(const struct opt_section * sec);
-void opt_handle_help(struct opt_item * opt, const char * value, void * data);
+void opt_handle_help(const struct opt_item * opt, const char * value, void * data);
/***
* [[conf]]
#define OPT_CONF_DUMPCONFIG OPT_CALL(0, "dumpconfig", opt_handle_dumpconfig, NULL, OPT_INTERNAL | OPT_NO_VALUE, "\tDump program configuration")
#define OPT_CONF_HOOK OPT_HOOK(opt_conf_hook_internal, NULL, OPT_HOOK_BEFORE_VALUE | OPT_HOOK_FINAL | OPT_HOOK_INTERNAL)
-void opt_handle_config(struct opt_item * opt, const char * value, void * data);
-void opt_handle_set(struct opt_item * opt, const char * value, void * data);
-void opt_handle_dumpconfig(struct opt_item * opt, const char * value, void * data);
-void opt_conf_hook_internal(struct opt_item * opt, uns event, const char * value, void * data);
+void opt_handle_config(const struct opt_item * opt, const char * value, void * data);
+void opt_handle_set(const struct opt_item * opt, const char * value, void * data);
+void opt_handle_dumpconfig(const struct opt_item * opt, const char * value, void * data);
+void opt_conf_hook_internal(const struct opt_item * opt, uint event, const char * value, void * data);
// XXX: This is duplicated with <ucw/getopt.h>, but that one will hopefully go away one day.
/**
#define OPT_HOOK_BEFORE_ARG 0x1 /** Call before option parsing **/
#define OPT_HOOK_BEFORE_VALUE 0x2 /** Call before value parsing **/
#define OPT_HOOK_AFTER_VALUE 0x4 /** Call after value parsing **/
-#define OPT_HOOK_FINAL 0x8 /** Call just before opt_parse() returns **/
+#define OPT_HOOK_FINAL 0x8 /** Call just before @opt_parse() returns **/
#define OPT_HOOK_INTERNAL 0x4000 // Used internally to ask for passing of struct opt_context
#endif