#include <alloca.h>
#include <math.h>
-static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2);
+static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2) NONRET;
static void opt_failure(const char * mesg, ...) {
va_list args;
va_start(args, mesg);
vfprintf(stderr, mesg, args);
fprintf(stderr, "\n");
+ opt_usage();
exit(OPT_EXIT_BAD_ARGS);
va_end(args);
}
#define OPT_ADD_DEFAULT_ITEM_FLAGS(item, flags) \
do { \
+ if (item->letter >= 256) { \
+ if (flags & OPT_VALUE_FLAGS) \
+ flags &= ~OPT_VALUE_FLAGS; \
+ flags |= OPT_REQUIRED_VALUE; \
+ } \
if (!(flags & OPT_VALUE_FLAGS) && \
(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); \
#define FOREACHLINE(text) for (const char * begin = (text), * end = (text); (*end) && (end = strchrnul(begin, '\n')); begin = end+1)
-void opt_help_noexit_internal(const struct opt_section * help) {
+void opt_help_internal(const struct opt_section * help) {
int sections_cnt = 0;
int lines_cnt = 0;
#define LASTFIELD(k) MIN(strchrnul(lines[i][k], '\t')-lines[i][k],strchrnul(lines[i][k], '\n')-lines[i][k]), lines[i][k]
for (int i=0;i<line;i++) {
while (s < sections_cnt && sections[s].pos == i) {
- opt_help_noexit_internal(sections[s].sect);
+ opt_help_internal(sections[s].sect);
s++;
}
if (lines[i][0] == NULL)
printf("%-*.*s %-*.*s %.*s\n", FIELD(0), FIELD(1), LASTFIELD(2));
}
while (s < sections_cnt && sections[s].pos == line) {
- opt_help_noexit_internal(sections[s].sect);
+ opt_help_internal(sections[s].sect);
s++;
}
}
+static int opt_positional_max = 0;
+static int opt_positional_count = 0;
+
struct opt_precomputed {
struct opt_precomputed_option {
struct opt_item * item;
short flags;
short count;
} ** opts;
- struct opt_precomputed_option * shortopt[256];
+ struct opt_precomputed_option ** shortopt;
short opt_count;
};
opt_failure("Invalid option %s.", str);
}
-#define OPT_NAME (longopt ? stk_printf("--%s", opt->name) : stk_printf("-%c", item->letter))
+#define OPT_NAME (longopt == 2 ? stk_printf("positional arg #%d", opt_positional_count) : (longopt == 1 ? stk_printf("--%s", opt->name) : stk_printf("-%c", item->letter)))
static void opt_parse_value(struct opt_precomputed_option * opt, char * value, int longopt) {
struct opt_item * item = opt->item;
switch (item->cls) {
else
e = cf_parse_int(value, item->ptr);
if (e)
- opt_failure("Integer value parsing failed for argument %s: %s", OPT_NAME, e);
+ opt_failure("Integer value parsing failed for %s: %s", OPT_NAME, e);
break;
case CT_U64:
if (!value)
else
e = cf_parse_u64(value, item->ptr);
if (e)
- opt_failure("Unsigned 64-bit value parsing failed for argument %s: %s", OPT_NAME, e);
+ opt_failure("Unsigned 64-bit value parsing failed for %s: %s", OPT_NAME, e);
break;
case CT_DOUBLE:
if (!value)
else
e = cf_parse_double(value, item->ptr);
if (e)
- opt_failure("Double value parsing failed for argument %s: %s", OPT_NAME, e);
+ opt_failure("Double value parsing failed for %s: %s", OPT_NAME, e);
break;
case CT_IP:
if (!value)
else
e = cf_parse_ip(value, item->ptr);
if (e)
- opt_failure("IP parsing failed for argument %s: %s", OPT_NAME, e);
+ opt_failure("IP parsing failed for %s: %s", OPT_NAME, e);
break;
case CT_STRING:
if (!value)
item->ptr = NULL;
else
- item->ptr = xstrdup(value);
+ *((const char **) (item->ptr)) = xstrdup(value);
break;
default:
ASSERT(0);
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", OPT_NAME, e);
+ opt_failure("User defined type value parsing failed for %s: %s", OPT_NAME, e);
break;
}
default:
return 0;
}
+static void opt_positional(char * value, struct opt_precomputed * pre) {
+ opt_positional_count++;
+ struct opt_precomputed_option * opt = opt_find_item_shortopt((opt_positional_count > opt_positional_max ? 256 : opt_positional_count + 256), pre);
+ if (!opt) {
+ ASSERT(opt_positional_count > opt_positional_max);
+ opt_failure("Too many positional args.");
+ }
+
+ opt_parse_value(opt, value, 2);
+}
+
#define OPT_TRAVERSE_SECTIONS \
while (item->cls == OPT_CL_SECTION) { \
if (stk->next) \
continue; \
}
-void opt_parse(const struct opt_section * options, char ** argv, opt_positional * callback) {
+void opt_parse(const struct opt_section * options, char ** argv) {
opt_section_root = options;
struct opt_stack {
count++;
if (item->cls == OPT_CL_BOOL)
count++;
+ if (item->letter > 256)
+ opt_positional_max++;
}
- pre->opts = xmalloc(sizeof(*pre->opts) * count);
+ pre->opts = alloca(sizeof(*pre->opts) * count);
+ pre->shortopt = alloca(sizeof(*pre->shortopt) * (opt_positional_max + 257));
+ memset(pre->shortopt, 0, sizeof(*pre->shortopt) * (opt_positional_max + 257));
+
pre->opt_count = 0;
for (struct opt_item * item = options->opt; ; item++) {
int force_positional = 0;
for (int i=0;argv[i];i++) {
if (argv[i][0] != '-' || force_positional) {
- callback(argv[i]);
+ opt_positional(argv[i], pre);
}
else {
if (argv[i][1] == '-') {
else if (argv[i][1])
i += opt_shortopt(argv, i, pre);
else
- callback(argv[i]);
+ opt_positional(argv[i], pre);
}
}
+
+ for (int i=0;i<opt_positional_max+257;i++) {
+ if (!pre->shortopt[i])
+ continue;
+ if (!pre->shortopt[i]->count && (pre->shortopt[i]->flags | OPT_REQUIRED))
+ if (i < 256)
+ opt_failure("Required option -%c not found.\n", pre->shortopt[i]->item->letter);
+ else
+ opt_failure("Required positional argument #%d not found.\n", (i > 256) ? pre->shortopt[i]->item->letter-256 : opt_positional_max+1);
+ }
+
+ for (int i=0;i<pre->opt_count;i++) {
+ if (!pre->opts[i])
+ continue;
+ if (!pre->opts[i]->count && (pre->opts[i]->flags | OPT_REQUIRED))
+ opt_failure("Required option --%s not found.\n", pre->opts[i]->item->name);
+ }
}
#ifdef TEST
static char * teapot_type_str[] = { "standard", "exclusive", "glass", "hands" };
static int english = 0;
-static char * name = NULL;
static int sugar = 0;
static int verbose = 1;
static int with_gas = 0;
static int black_magic = 0;
static int pray = 0;
static int water_amount = 0;
+static char * first_tea = NULL;
+
+#define MAX_TEA_COUNT 30
+static char * tea_list[MAX_TEA_COUNT];
+static int tea_num = 0;
+static void add_tea(struct opt_item * opt UNUSED, const char * name, void * data) {
+ char ** tea_list = data;
+ if (tea_num >= MAX_TEA_COUNT) {
+ fprintf(stderr, "Cannot boil more than %d teas.\n", MAX_TEA_COUNT);
+ exit(OPT_EXIT_BAD_ARGS);
+ }
+ tea_list[tea_num++] = xstrdup(name);
+}
static const char * teapot_temperature_parser(char * in, void * ptr) {
struct teapot_temperature * temp = ptr;
OPT_INC('q', "quiet", verbose, OPT_NEGATIVE, "\tQuiet (the more -q, the more quiet)"),
OPT_INT('b', "black-magic", black_magic, 0, "<strength>\tUse black magic to make the tea extraordinary delicious"),
OPT_BOOL('p', "pray", pray, OPT_SINGLE, "\tPray before boiling"),
+ OPT_STRING(OPT_POSITIONAL(1), NULL, first_tea, OPT_REQUIRED | OPT_NO_HELP, ""),
+ OPT_CALL(OPT_POSITIONAL_TAIL, NULL, add_tea, &tea_list, OPT_NO_HELP, ""),
OPT_HELP(""),
OPT_HELP("Water options:"),
OPT_SECTION(water_options),
}
};
-#define MAX_TEA_COUNT 30
-static char * tea_list[MAX_TEA_COUNT];
-static int tea_num = 0;
-static void add_tea(const char * name) {
- if (tea_num >= MAX_TEA_COUNT) {
- fprintf(stderr, "Cannot boil more than %d teas.\n", MAX_TEA_COUNT);
- exit(OPT_EXIT_BAD_ARGS);
- }
- tea_list[tea_num++] = xstrdup(name);
-}
-
static void boil_tea(const char * name) {
printf("Boiling a tea: %s\n", name);
}
-int main(int argc, char ** argv)
+int main(int argc UNUSED, char ** argv)
{
- opt_parse(&help, argv+1, add_tea);
+ opt_parse(&help, argv+1);
printf("Parsed values:\n");
printf("English style: %s\n", english ? "yes" : "no");
printf("Prayer: %s\n", pray ? "yes" : "no");
printf("Water amount: %d\n", water_amount);
printf("Gas: %s\n", with_gas ? "yes" : "no");
+ printf("First tea: %s\n", first_tea);
for (int i=0; i<tea_num; i++)
boil_tea(tea_list[i]);
struct opt_section;
struct opt_item {
const char * name; // long-op
+ int letter; // short-op
void * ptr; // where to save
const char * help; // description in --help
union opt_union {
void (* call)(struct opt_item * opt, const char * value, void * data); // function to call for OPT_CALL
struct cf_user_type * utype; // specification of the user-defined type
} u;
- const char letter; // short-op
- byte flags;
+ u16 flags;
byte cls; // enum opt_class
byte type; // enum cf_type
};
#define OPT_SECTION(sec) { .cls = OPT_CL_SECTION, .u.section = &sec }
#define OPT_END { .cls = OPT_CL_END }
+/***
+ * Predefined shortopt arguments
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * for the preceeding calls if positional args wanted.
+ * Arguments are processed in the order of the numbers given to them. There must be first
+ * the args with OPT_REQUIRED (see lower) and after them the args without OPT_REQUIRED, no mixing.
+ * You may define a catch-all option as OPT_POSITIONAL_TAIL. After this, no positional arg is allowed.
+ * You may shuffle the positional arguments in any way in the opt sections but the numbering must obey
+ * the rules given here.
+ ***/
+
+#define OPT_POSITIONAL(n) (OPT_POSITIONAL_TAIL+(n))
+#define OPT_POSITIONAL_TAIL 256
+
+
/***
* Flags for the preceeding calls
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
};
extern const struct opt_section * opt_section_root;
-void opt_help_noexit_internal(const struct opt_section * help);
+void opt_help_internal(const struct opt_section * help);
-static void opt_help_noexit(void) {
- opt_help_noexit_internal(opt_section_root);
+static void opt_help(void) {
+ opt_help_internal(opt_section_root);
}
-static void opt_usage_noexit(void) {
+static void opt_usage(void) {
fprintf(stderr, "Run with argument --help for more information.\n");
}
static void opt_show_help_internal(struct opt_item * opt UNUSED, const char * value UNUSED, void * data UNUSED) {
- opt_help_noexit();
+ opt_help();
exit(0);
}
-static void opt_help(void) {
- opt_help_noexit();
- exit(1);
-}
-
-static void opt_usage(void) {
- opt_usage_noexit();
- exit(1);
-}
-
-/**
- * Positional argument handler to be given to opt_parse()
- **/
-typedef void opt_positional(const char * str);
-
/**
* Parse all the arguments. Run the @callback for each of the positional argument.
**/
-void opt_parse(const struct opt_section * options, char ** argv, opt_positional * callback);
+void opt_parse(const struct opt_section * options, char ** argv);
#endif