improved argument parsing
authorSven Verdoolaege <skimo@kotnet.org>
Tue, 18 May 2010 09:56:46 +0000 (11:56 +0200)
committerSven Verdoolaege <skimo@kotnet.org>
Sun, 30 May 2010 15:23:27 +0000 (17:23 +0200)
include/isl_arg.h
isl_arg.c

index 75c9016..c7bbc41 100644 (file)
@@ -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);              \
index 71c987d..a9529b5 100644 (file)
--- 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;
 }