From 57bcba36ff6b9d4854ad739e3ddc397cf1ca2343 Mon Sep 17 00:00:00 2001 From: Sven Verdoolaege Date: Tue, 18 May 2010 11:56:46 +0200 Subject: [PATCH] improved argument parsing --- include/isl_arg.h | 128 ++++++++++- isl_arg.c | 622 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 707 insertions(+), 43 deletions(-) diff --git a/include/isl_arg.h b/include/isl_arg.h index 75c9016..c7bbc41 100644 --- a/include/isl_arg.h +++ b/include/isl_arg.h @@ -22,41 +22,94 @@ struct isl_arg_choice { unsigned value; }; +struct isl_arg_flags { + const char *name; + unsigned mask; + unsigned value; +}; + enum isl_arg_type { isl_arg_end, - isl_arg_choice, + isl_arg_arg, isl_arg_bool, - isl_arg_child + isl_arg_child, + isl_arg_choice, + isl_arg_flags, + isl_arg_user, + isl_arg_long, + isl_arg_ulong, + isl_arg_str, + isl_arg_version }; struct isl_arg { enum isl_arg_type type; char short_name; const char *long_name; + const char *argument_name; size_t offset; const char *help_msg; union { struct { struct isl_arg_choice *choice; unsigned default_value; + unsigned default_selected; } choice; struct { + struct isl_arg_flags *flags; + unsigned default_value; + } flags; + struct { unsigned default_value; } b; struct { + long default_value; + long default_selected; + int (*set)(void *opt, long val); + } l; + struct { + unsigned long default_value; + } ul; + struct { + const char *default_value; + } str; + struct { struct isl_arg *child; size_t size; } child; + struct { + void (*print_version)(void); + } version; + struct { + int (*init)(void*); + void (*clear)(void*); + } user; } u; }; +#define ISL_ARG_ARG(st,f,a,d) { \ + .type = isl_arg_arg, \ + .argument_name = a, \ + .offset = offsetof(st, f), \ + .u = { .str = { .default_value = d } } \ +}, #define ISL_ARG_CHOICE(st,f,s,l,c,d,h) { \ .type = isl_arg_choice, \ .short_name = s, \ .long_name = l, \ .offset = offsetof(st, f), \ .help_msg = h, \ - .u = { .choice = { .choice = c, .default_value = d } } \ + .u = { .choice = { .choice = c, .default_value = d, \ + .default_selected = d } } \ +}, +#define ISL_ARG_OPT_CHOICE(st,f,s,l,c,d,ds,h) { \ + .type = isl_arg_choice, \ + .short_name = s, \ + .long_name = l, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .choice = { .choice = c, .default_value = d, \ + .default_selected = ds } } \ }, #define ISL_ARG_BOOL(st,f,s,l,d,h) { \ .type = isl_arg_bool, \ @@ -66,6 +119,50 @@ struct isl_arg { .help_msg = h, \ .u = { .b = { .default_value = d } } \ }, +#define ISL_ARG_LONG(st,f,s,lo,d,h) { \ + .type = isl_arg_long, \ + .short_name = s, \ + .long_name = lo, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .l = { .default_value = d, .default_selected = d, \ + .set = NULL } } \ +}, +#define ISL_ARG_USER_LONG(st,f,s,lo,setter,d,h) { \ + .type = isl_arg_long, \ + .short_name = s, \ + .long_name = lo, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .l = { .default_value = d, .default_selected = d, \ + .set = setter } } \ +}, +#define ISL_ARG_OPT_LONG(st,f,s,lo,d,ds,h) { \ + .type = isl_arg_long, \ + .short_name = s, \ + .long_name = lo, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .l = { .default_value = d, .default_selected = ds, \ + .set = NULL } } \ +}, +#define ISL_ARG_ULONG(st,f,s,l,d,h) { \ + .type = isl_arg_ulong, \ + .short_name = s, \ + .long_name = l, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .ul = { .default_value = d } } \ +}, +#define ISL_ARG_STR(st,f,s,l,a,d,h) { \ + .type = isl_arg_str, \ + .short_name = s, \ + .long_name = l, \ + .argument_name = a, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .str = { .default_value = d } } \ +}, #define ISL_ARG_CHILD(st,f,l,c,h) { \ .type = isl_arg_child, \ .long_name = l, \ @@ -73,16 +170,36 @@ struct isl_arg { .help_msg = h, \ .u = { .child = { .child = c, .size = sizeof(*((st *)NULL)->f) } }\ }, +#define ISL_ARG_FLAGS(st,f,s,l,c,d,h) { \ + .type = isl_arg_flags, \ + .short_name = s, \ + .long_name = l, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .flags = { .flags = c, .default_value = d } } \ +}, +#define ISL_ARG_USER(st,f,i,c) { \ + .type = isl_arg_user, \ + .offset = offsetof(st, f), \ + .u = { .user = { .init = i, .clear = c} } \ +}, +#define ISL_ARG_VERSION(print) { \ + .type = isl_arg_version, \ + .u = { .version = { .print_version = print } } \ +}, #define ISL_ARG_END { isl_arg_end } #define ISL_ARG_ALL (1 << 0) void isl_arg_set_defaults(struct isl_arg *arg, void *opt); +void isl_arg_free(struct isl_arg *arg, void *opt); int isl_arg_parse(struct isl_arg *arg, int argc, char **argv, void *opt, unsigned flags); #define ISL_ARG_DECL(prefix,st,arg) \ +extern struct isl_arg arg[]; \ st *prefix ## _new_with_defaults(); \ +void prefix ## _free(st *opt); \ int prefix ## _parse(st *opt, int argc, char **argv, unsigned flags); #define ISL_ARG_DEF(prefix,st,arg) \ @@ -94,6 +211,11 @@ st *prefix ## _new_with_defaults() \ return opt; \ } \ \ +void prefix ## _free(st *opt) \ +{ \ + isl_arg_free(arg, opt); \ +} \ + \ int prefix ## _parse(st *opt, int argc, char **argv, unsigned flags) \ { \ return isl_arg_parse(arg, argc, argv, opt, flags); \ diff --git a/isl_arg.c b/isl_arg.c index 71c987d..a9529b5 100644 --- a/isl_arg.c +++ b/isl_arg.c @@ -19,6 +19,11 @@ static void set_default_choice(struct isl_arg *arg, void *opt) *(unsigned *)(((char *)opt) + arg->offset) = arg->u.choice.default_value; } +static void set_default_flags(struct isl_arg *arg, void *opt) +{ + *(unsigned *)(((char *)opt) + arg->offset) = arg->u.flags.default_value; +} + static void set_default_bool(struct isl_arg *arg, void *opt) { *(unsigned *)(((char *)opt) + arg->offset) = arg->u.b.default_value; @@ -34,6 +39,29 @@ static void set_default_child(struct isl_arg *arg, void *opt) *(void **)(((char *)opt) + arg->offset) = child; } +static void set_default_user(struct isl_arg *arg, void *opt) +{ + arg->u.user.init(((char *)opt) + arg->offset); +} + +static void set_default_long(struct isl_arg *arg, void *opt) +{ + *(long *)(((char *)opt) + arg->offset) = arg->u.l.default_value; +} + +static void set_default_ulong(struct isl_arg *arg, void *opt) +{ + *(unsigned long *)(((char *)opt) + arg->offset) = arg->u.ul.default_value; +} + +static void set_default_str(struct isl_arg *arg, void *opt) +{ + const char *str = NULL; + if (arg->u.str.default_value) + str = strdup(arg->u.str.default_value); + *(const char **)(((char *)opt) + arg->offset) = str; +} + void isl_arg_set_defaults(struct isl_arg *arg, void *opt) { int i; @@ -43,20 +71,79 @@ void isl_arg_set_defaults(struct isl_arg *arg, void *opt) case isl_arg_choice: set_default_choice(&arg[i], opt); break; + case isl_arg_flags: + set_default_flags(&arg[i], opt); + break; case isl_arg_bool: set_default_bool(&arg[i], opt); break; case isl_arg_child: set_default_child(&arg[i], opt); break; + case isl_arg_user: + set_default_user(&arg[i], opt); + break; + case isl_arg_long: + set_default_long(&arg[i], opt); + break; + case isl_arg_ulong: + set_default_ulong(&arg[i], opt); + break; + case isl_arg_arg: + case isl_arg_str: + set_default_str(&arg[i], opt); + break; + case isl_arg_version: + case isl_arg_end: + break; + } + } +} + +void isl_arg_free(struct isl_arg *arg, void *opt) +{ + int i; + + if (!opt) + return; + + for (i = 0; arg[i].type != isl_arg_end; ++i) { + switch (arg[i].type) { + case isl_arg_child: + isl_arg_free(arg[i].u.child.child, + *(void **)(((char *)opt) + arg[i].offset)); + break; + case isl_arg_arg: + case isl_arg_str: + free(*(char **)(((char *)opt) + arg[i].offset)); + break; + case isl_arg_user: + if (arg[i].u.user.clear) + arg[i].u.user.clear(((char *)opt) + arg[i].offset); + break; + case isl_arg_bool: + case isl_arg_choice: + case isl_arg_flags: + case isl_arg_long: + case isl_arg_ulong: + case isl_arg_version: + case isl_arg_end: + break; } } + + free(opt); } -static int print_arg_help(struct isl_arg *decl, const char *prefix) +static int print_arg_help(struct isl_arg *decl, const char *prefix, int no) { int len = 0; + if (!decl->long_name) { + printf(" -%c", decl->short_name); + return 4; + } + if (decl->short_name) printf(" -%c, --", decl->short_name); else @@ -67,6 +154,10 @@ static int print_arg_help(struct isl_arg *decl, const char *prefix) printf("%s-", prefix); len += strlen(prefix) + 1; } + if (no) { + printf("no-"); + len += 3; + } printf("%s", decl->long_name); len += strlen(decl->long_name); @@ -78,7 +169,7 @@ const void *isl_memrchr(const void *s, int c, size_t n) const char *p = s; while (n-- > 0) if (p[n] == c) - return s + n; + return p + n; return NULL; } @@ -122,13 +213,14 @@ static void print_default_choice(struct isl_arg *decl, int pos) const char *default_prefix = "[default: "; const char *default_suffix = "]"; const char *s = "none"; - int len = strlen(default_prefix) + strlen(s) + strlen(default_suffix); + int len; for (i = 0; decl->u.choice.choice[i].name; ++i) if (decl->u.choice.choice[i].value == decl->u.choice.default_value) { s = decl->u.choice.choice[i].name; break; } + len = strlen(default_prefix) + strlen(s) + strlen(default_suffix); if (!decl->help_msg) { if (pos >= 29) @@ -136,12 +228,12 @@ static void print_default_choice(struct isl_arg *decl, int pos) else printf("%*s", 30 - pos, ""); pos = 0; + } else { + if (pos + len >= 48) + printf("\n%30s", ""); + else + printf(" "); } - - if (pos && pos + len >= 48) - printf("\n%30s", ""); - else - printf(" "); printf("%s%s%s", default_prefix, s, default_suffix); } @@ -150,7 +242,7 @@ static void print_choice_help(struct isl_arg *decl, const char *prefix) int i; int pos; - pos = print_arg_help(decl, prefix); + pos = print_arg_help(decl, prefix, 0); printf("="); pos++; @@ -169,10 +261,121 @@ static void print_choice_help(struct isl_arg *decl, const char *prefix) printf("\n"); } +static void print_default_flags(struct isl_arg *decl, int pos) +{ + int i, first; + const char *default_prefix = "[default: "; + const char *default_suffix = "]"; + int len = strlen(default_prefix) + strlen(default_suffix); + + for (i = 0; decl->u.flags.flags[i].name; ++i) + if ((decl->u.flags.default_value & decl->u.flags.flags[i].mask) == + decl->u.flags.flags[i].value) + len += strlen(decl->u.flags.flags[i].name); + + if (!decl->help_msg) { + if (pos >= 29) + printf("\n%30s", ""); + else + printf("%*s", 30 - pos, ""); + pos = 0; + } else { + if (pos + len >= 48) + printf("\n%30s", ""); + else + printf(" "); + } + printf("%s", default_prefix); + + for (first = 1, i = 0; decl->u.flags.flags[i].name; ++i) + if ((decl->u.flags.default_value & decl->u.flags.flags[i].mask) == + decl->u.flags.flags[i].value) { + if (!first) + printf(","); + printf("%s", decl->u.flags.flags[i].name); + first = 0; + } + + printf("%s", default_suffix); +} + +static void print_flags_help(struct isl_arg *decl, const char *prefix) +{ + int i, j; + int pos; + + pos = print_arg_help(decl, prefix, 0); + printf("="); + pos++; + + for (i = 0; decl->u.flags.flags[i].name; ++i) { + if (i) { + printf(","); + pos++; + } + for (j = i; + decl->u.flags.flags[j].mask == decl->u.flags.flags[i].mask; + ++j) { + if (j != i) { + printf("|"); + pos++; + } + printf("%s", decl->u.flags.flags[j].name); + pos += strlen(decl->u.flags.flags[j].name); + } + i = j - 1; + } + + pos = print_help_msg(decl, pos); + print_default_flags(decl, pos); + + printf("\n"); +} + static void print_bool_help(struct isl_arg *decl, const char *prefix) { int pos; - pos = print_arg_help(decl, prefix); + int no = decl->u.b.default_value == 1; + pos = print_arg_help(decl, prefix, no); + print_help_msg(decl, pos); + printf("\n"); +} + +static void print_long_help(struct isl_arg *decl, const char *prefix) +{ + int pos; + pos = print_arg_help(decl, prefix, 0); + if (decl->u.l.default_value != decl->u.l.default_selected) { + printf("["); + pos++; + } + printf("=long"); + pos += 5; + if (decl->u.l.default_value != decl->u.l.default_selected) { + printf("]"); + pos++; + } + print_help_msg(decl, pos); + printf("\n"); +} + +static void print_ulong_help(struct isl_arg *decl, const char *prefix) +{ + int pos; + pos = print_arg_help(decl, prefix, 0); + printf("=ulong"); + pos += 6; + print_help_msg(decl, pos); + printf("\n"); +} + +static void print_str_help(struct isl_arg *decl, const char *prefix) +{ + int pos; + const char *a = decl->argument_name ? decl->argument_name : "string"; + pos = print_arg_help(decl, prefix, 0); + printf("=%s", a); + pos += 1 + strlen(a); print_help_msg(decl, pos); printf("\n"); } @@ -183,12 +386,32 @@ static void print_help(struct isl_arg *arg, const char *prefix) for (i = 0; arg[i].type != isl_arg_end; ++i) { switch (arg[i].type) { + case isl_arg_flags: + print_flags_help(&arg[i], prefix); + break; case isl_arg_choice: print_choice_help(&arg[i], prefix); break; case isl_arg_bool: print_bool_help(&arg[i], prefix); break; + case isl_arg_long: + print_long_help(&arg[i], prefix); + break; + case isl_arg_ulong: + print_ulong_help(&arg[i], prefix); + break; + case isl_arg_str: + print_str_help(&arg[i], prefix); + break; + case isl_arg_version: + printf(" -V, --version\n"); + break; + case isl_arg_arg: + case isl_arg_child: + case isl_arg_user: + case isl_arg_end: + break; } } @@ -197,37 +420,68 @@ static void print_help(struct isl_arg *arg, const char *prefix) continue; printf("\n"); + if (arg[i].help_msg) + printf(" %s\n", arg[i].help_msg); print_help(arg[i].u.child.child, arg[i].long_name); } } -static void print_help_and_exit(struct isl_arg *arg, const char *prog) +static const char *prog_name(const char *prog) { const char *slash; slash = strrchr(prog, '/'); if (slash) - printf("Usage: %s [OPTION...]\n\n", slash + 1); + prog = slash + 1; + if (strncmp(prog, "lt-", 3) == 0) + prog += 3; + + return prog; +} + +static void print_help_and_exit(struct isl_arg *arg, const char *prog) +{ + int i; + + printf("Usage: %s [OPTION...]", prog_name(prog)); + + for (i = 0; arg[i].type != isl_arg_end; ++i) + if (arg[i].type == isl_arg_arg) + printf(" %s", arg[i].argument_name); + + printf("\n\n"); print_help(arg, NULL); exit(0); } -static int parse_choice_option(struct isl_arg *decl, const char *arg, - const char *prefix, void *opt) +static const char *skip_name(struct isl_arg *decl, const char *arg, + const char *prefix, int need_argument, int *has_argument) { - int i; const char *equal; const char *name; + const char *end; + + if (arg[0] == '-' && arg[1] && arg[1] == decl->short_name) { + if (need_argument && !arg[2]) + return NULL; + if (has_argument) + *has_argument = arg[2] != '\0'; + return arg + 2; + } if (strncmp(arg, "--", 2)) - return 0; + return NULL; name = arg + 2; equal = strchr(name, '='); - if (!equal) - return 0; + if (need_argument && !equal) + return NULL; + + if (has_argument) + *has_argument = !!equal; + end = equal ? equal : name + strlen(name); if (prefix) { size_t prefix_len = strlen(prefix); @@ -236,77 +490,336 @@ static int parse_choice_option(struct isl_arg *decl, const char *arg, name += prefix_len + 1; } - if (strncmp(name, decl->long_name, equal - name)) + if (end - name != strlen(decl->long_name) || + strncmp(name, decl->long_name, end - name)) + return NULL; + + return equal ? equal + 1 : end; +} + +static int parse_choice_option(struct isl_arg *decl, char **arg, + const char *prefix, void *opt) +{ + int i; + int has_argument; + const char *choice; + + choice = skip_name(decl, arg[0], prefix, 0, &has_argument); + if (!choice) return 0; + if (!has_argument && (!arg[1] || arg[1][0] == '-')) { + *(unsigned *)(((char *)opt) + decl->offset) = + decl->u.choice.default_selected; + + return 1; + } + + if (!has_argument) + choice = arg[1]; + for (i = 0; decl->u.choice.choice[i].name; ++i) { - if (strcmp(equal + 1, decl->u.choice.choice[i].name)) + if (strcmp(choice, decl->u.choice.choice[i].name)) continue; *(unsigned *)(((char *)opt) + decl->offset) = decl->u.choice.choice[i].value; - return 1; + return has_argument ? 1 : 2; } return 0; } -static int parse_bool_option(struct isl_arg *decl, const char *arg, void *opt) +static int set_flag(struct isl_arg *decl, unsigned *val, const char *flag, + size_t len) { int i; - if ((arg[0] == '-' && arg[1] == decl->short_name && arg[2] == '\0') || - (strncmp(arg, "--", 2) == 0 && - strcmp(arg + 2, decl->long_name) == 0)) { + for (i = 0; decl->u.flags.flags[i].name; ++i) { + if (strncmp(flag, decl->u.flags.flags[i].name, len)) + continue; + + *val &= ~decl->u.flags.flags[i].mask; + *val |= decl->u.flags.flags[i].value; + + return 1; + } + + return 0; +} + +static int parse_flags_option(struct isl_arg *decl, char **arg, + const char *prefix, void *opt) +{ + int has_argument; + const char *flags; + const char *comma; + unsigned val; + + flags = skip_name(decl, arg[0], prefix, 0, &has_argument); + if (!flags) + return 0; + + if (!has_argument && !arg[1]) + return 0; + + if (!has_argument) + flags = arg[1]; + + val = *(unsigned *)(((char *)opt) + decl->offset); + + while ((comma = strchr(flags, ',')) != NULL) { + if (!set_flag(decl, &val, flags, comma - flags)) + return 0; + flags = comma + 1; + } + if (!set_flag(decl, &val, flags, strlen(flags))) + return 0; + + *(unsigned *)(((char *)opt) + decl->offset) = val; + + return has_argument ? 1 : 2; +} + +static int parse_bool_option(struct isl_arg *decl, const char *arg, + const char *prefix, void *opt) +{ + if (skip_name(decl, arg, prefix, 0, NULL)) { *(unsigned *)(((char *)opt) + decl->offset) = 1; return 1; } + if (strncmp(arg, "--no-", 5)) + return 0; + arg += 5; + + if (prefix) { + size_t prefix_len = strlen(prefix); + if (strncmp(arg, prefix, prefix_len) == 0 && + arg[prefix_len] == '-') + arg += prefix_len + 1; + } + + if (!strcmp(arg, decl->long_name)) { + *(unsigned *)(((char *)opt) + decl->offset) = 0; + + return 1; + } + + return 0; +} + +static int parse_str_option(struct isl_arg *decl, char **arg, + const char *prefix, void *opt) +{ + int has_argument; + const char *s; + char **p = (char **)(((char *)opt) + decl->offset); + + s = skip_name(decl, arg[0], prefix, 0, &has_argument); + if (!s) + return 0; + + if (has_argument) { + *p = strdup(s); + return 1; + } + + if (arg[1]) { + *p = strdup(arg[1]); + return 2; + } + return 0; } -static int parse_option(struct isl_arg *decl, const char *arg, +static int parse_long_option(struct isl_arg *decl, char **arg, + const char *prefix, void *opt) +{ + int has_argument; + const char *val; + char *endptr; + long *p = (long *)(((char *)opt) + decl->offset); + + val = skip_name(decl, arg[0], prefix, 0, &has_argument); + if (!val) + return 0; + + if (has_argument) { + long l = strtol(val, NULL, 0); + if (decl->u.l.set) + decl->u.l.set(opt, l); + else + *p = l; + return 1; + } + + if (arg[1]) { + long l = strtol(arg[1], &endptr, 0); + if (*endptr == '\0') { + if (decl->u.l.set) + decl->u.l.set(opt, l); + else + *p = l; + return 2; + } + } + + if (decl->u.l.default_value != decl->u.l.default_selected) { + if (decl->u.l.set) + decl->u.l.set(opt, decl->u.l.default_selected); + else + *p = decl->u.l.default_selected; + return 1; + } + + return 0; +} + +static int parse_ulong_option(struct isl_arg *decl, char **arg, + const char *prefix, void *opt) +{ + int has_argument; + const char *val; + char *endptr; + unsigned long *p = (unsigned long *)(((char *)opt) + decl->offset); + + val = skip_name(decl, arg[0], prefix, 0, &has_argument); + if (!val) + return 0; + + if (has_argument) { + *p = strtoul(val, NULL, 0); + return 1; + } + + if (arg[1]) { + unsigned long ul = strtoul(arg[1], &endptr, 0); + if (*endptr == '\0') { + *p = ul; + return 2; + } + } + + return 0; +} + +static int parse_option(struct isl_arg *decl, char **arg, const char *prefix, void *opt); -static int parse_child_option(struct isl_arg *decl, const char *arg, void *opt) +static int parse_child_option(struct isl_arg *decl, char **arg, void *opt) { return parse_option(decl->u.child.child, arg, decl->long_name, *(void **)(((char *)opt) + decl->offset)); } -static int parse_option(struct isl_arg *decl, const char *arg, +static int parse_option(struct isl_arg *decl, char **arg, const char *prefix, void *opt) { int i; for (i = 0; decl[i].type != isl_arg_end; ++i) { + int parsed = 0; switch (decl[i].type) { case isl_arg_choice: - if (parse_choice_option(&decl[i], arg, prefix, opt)) - return 1; + parsed = parse_choice_option(&decl[i], arg, prefix, opt); + break; + case isl_arg_flags: + parsed = parse_flags_option(&decl[i], arg, prefix, opt); + break; + case isl_arg_long: + parsed = parse_long_option(&decl[i], arg, prefix, opt); + break; + case isl_arg_ulong: + parsed = parse_ulong_option(&decl[i], arg, prefix, opt); break; case isl_arg_bool: - if (parse_bool_option(&decl[i], arg, opt)) - return 1; + parsed = parse_bool_option(&decl[i], *arg, prefix, opt); + break; + case isl_arg_str: + parsed = parse_str_option(&decl[i], arg, prefix, opt); break; case isl_arg_child: - if (parse_child_option(&decl[i], arg, opt)) + parsed = parse_child_option(&decl[i], arg, opt); + break; + case isl_arg_arg: + case isl_arg_user: + case isl_arg_version: + case isl_arg_end: + break; + } + if (parsed) + return parsed; + } + + return 0; +} + +static int any_version(struct isl_arg *decl) +{ + int i; + + for (i = 0; decl[i].type != isl_arg_end; ++i) { + switch (decl[i].type) { + case isl_arg_version: + return 1; + case isl_arg_child: + if (any_version(decl[i].u.child.child)) return 1; break; + default: + break; } } return 0; } -static int drop_argument(int argc, char **argv, int drop) +static void print_version(struct isl_arg *decl) +{ + int i; + + for (i = 0; decl[i].type != isl_arg_end; ++i) { + switch (decl[i].type) { + case isl_arg_version: + decl[i].u.version.print_version(); + break; + case isl_arg_child: + print_version(decl[i].u.child.child); + break; + default: + break; + } + } +} + +static void print_version_and_exit(struct isl_arg *decl) +{ + print_version(decl); + + exit(0); +} + +static int drop_argument(int argc, char **argv, int drop, int n) { for (; drop < argc; ++drop) - argv[drop] = argv[drop + 1]; + argv[drop] = argv[drop + n]; - return argc - 1; + return argc - n; +} + +static int n_arg(struct isl_arg *arg) +{ + int i; + int n_arg = 0; + + for (i = 0; arg[i].type != isl_arg_end; ++i) + if (arg[i].type == isl_arg_arg) + n_arg++; + + return n_arg; } int isl_arg_parse(struct isl_arg *arg, int argc, char **argv, void *opt, @@ -314,22 +827,51 @@ int isl_arg_parse(struct isl_arg *arg, int argc, char **argv, void *opt, { int skip = 0; int i; + int n; + + n = n_arg(arg); for (i = 1; i < argc; ++i) { if (strcmp(argv[i], "--help") == 0) print_help_and_exit(arg, argv[0]); } + for (i = 1; i < argc; ++i) { + if ((strcmp(argv[i], "--version") == 0 || + strcmp(argv[i], "-V") == 0) && any_version(arg)) + print_version_and_exit(arg); + } + while (argc > 1 + skip) { - if (parse_option(arg, argv[1 + skip], NULL, opt)) - argc = drop_argument(argc, argv, 1 + skip); + int parsed; + if (argv[1 + skip][0] != '-') + break; + parsed = parse_option(arg, &argv[1 + skip], NULL, opt); + if (parsed) + argc = drop_argument(argc, argv, 1 + skip, parsed); else if (ISL_FL_ISSET(flags, ISL_ARG_ALL)) { - fprintf(stderr, "unrecognized option: %s\n", - argv[1 + skip]); + fprintf(stderr, "%s: unrecognized option: %s\n", + prog_name(argv[0]), argv[1 + skip]); exit(-1); } else ++skip; } + if (ISL_FL_ISSET(flags, ISL_ARG_ALL) ? argc != 1 + n + : argc < 1 + skip + n) { + fprintf(stderr, "%s: expecting %d arguments\n", + prog_name(argv[0]), n); + exit(-1); + } + + for (i = 0; arg[i].type != isl_arg_end; ++i) { + const char *str; + if (arg[i].type != isl_arg_arg) + continue; + str = strdup(argv[1 + skip]); + *(const char **)(((char *)opt) + arg[i].offset) = str; + argc = drop_argument(argc, argv, 1 + skip, 1); + } + return argc; } -- 2.7.4