isl_arg_parse: support ISL_ARG_BOOL_ARG flag
[platform/upstream/isl.git] / isl_arg.c
index 17c67ff..533efac 100644 (file)
--- a/isl_arg.c
+++ b/isl_arg.c
 #include <stdlib.h>
 #include <string.h>
 
-#include "isl_arg.h"
-#include <isl_ctx.h>
+#include <isl/arg.h>
+#include <isl/ctx.h>
+
+static struct isl_arg help_arg[] = {
+ISL_ARG_PHANTOM_BOOL('h', "help", NULL, "print this help, then exit")
+ISL_ARG_END
+};
 
 static void set_default_choice(struct isl_arg *arg, void *opt)
 {
@@ -26,6 +31,8 @@ static void set_default_flags(struct isl_arg *arg, void *opt)
 
 static void set_default_bool(struct isl_arg *arg, void *opt)
 {
+       if (arg->offset == (size_t) -1)
+               return;
        *(unsigned *)(((char *)opt) + arg->offset) = arg->u.b.default_value;
 }
 
@@ -93,6 +100,7 @@ void isl_arg_set_defaults(struct isl_arg *arg, void *opt)
                case isl_arg_str:
                        set_default_str(&arg[i], opt);
                        break;
+               case isl_arg_alias:
                case isl_arg_version:
                case isl_arg_end:
                        break;
@@ -121,6 +129,7 @@ void isl_arg_free(struct isl_arg *arg, void *opt)
                        if (arg[i].u.user.clear)
                                arg[i].u.user.clear(((char *)opt) + arg[i].offset);
                        break;
+               case isl_arg_alias:
                case isl_arg_bool:
                case isl_arg_choice:
                case isl_arg_flags:
@@ -144,11 +153,16 @@ static int print_arg_help(struct isl_arg *decl, const char *prefix, int no)
                return 4;
        }
 
-       if (decl->short_name)
+       if (decl->short_name) {
                printf("  -%c, --", decl->short_name);
-       else
+               len += 8;
+       } else if (decl->flags & ISL_ARG_SINGLE_DASH) {
+               printf("  -");
+               len += 3;
+       } else {
                printf("      --");
-       len += 8;
+               len += 8;
+       }
 
        if (prefix) {
                printf("%s-", prefix);
@@ -161,6 +175,17 @@ static int print_arg_help(struct isl_arg *decl, const char *prefix, int no)
        printf("%s", decl->long_name);
        len += strlen(decl->long_name);
 
+       while ((++decl)->type == isl_arg_alias) {
+               printf(", --");
+               len += 4;
+               if (no) {
+                       printf("no-");
+                       len += 3;
+               }
+               printf("%s", decl->long_name);
+               len += strlen(decl->long_name);
+       }
+
        return len;
 }
 
@@ -207,20 +232,14 @@ static int print_help_msg(struct isl_arg *decl, int pos)
        return len;
 }
 
-static void print_default_choice(struct isl_arg *decl, int pos)
+static void print_default(struct isl_arg *decl, const char *def, int pos)
 {
        int i;
        const char *default_prefix = "[default: ";
        const char *default_suffix = "]";
-       const char *s = "none";
        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);
+       len = strlen(default_prefix) + strlen(def) + strlen(default_suffix);
 
        if (!decl->help_msg) {
                if (pos >= 29)
@@ -234,7 +253,21 @@ static void print_default_choice(struct isl_arg *decl, int pos)
                else
                        printf(" ");
        }
-       printf("%s%s%s", default_prefix, s, default_suffix);
+       printf("%s%s%s", default_prefix, def, default_suffix);
+}
+
+static void print_default_choice(struct isl_arg *decl, int pos)
+{
+       int i;
+       const char *s = "none";
+
+       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;
+               }
+
+       print_default(decl, s, pos);
 }
 
 static void print_choice_help(struct isl_arg *decl, const char *prefix)
@@ -337,10 +370,18 @@ static void print_bool_help(struct isl_arg *decl, const char *prefix)
        int pos;
        int no = decl->u.b.default_value == 1;
        pos = print_arg_help(decl, prefix, no);
-       print_help_msg(decl, pos);
+       pos = print_help_msg(decl, pos);
+       if (decl->offset != (size_t) -1)
+               print_default(decl, no ? "yes" : "no", pos);
        printf("\n");
 }
 
+static int print_argument_name(struct isl_arg *decl, const char *name, int pos)
+{
+       printf("%c<%s>", decl->long_name ? '=' : ' ', name);
+       return pos + 3 + strlen(name);
+}
+
 static void print_long_help(struct isl_arg *decl, const char *prefix)
 {
        int pos;
@@ -374,9 +415,10 @@ 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);
+       pos = print_argument_name(decl, a, pos);
+       pos = print_help_msg(decl, pos);
+       if (decl->u.str.default_value)
+               print_default(decl, decl->u.str.default_value, pos);
        printf("\n");
 }
 
@@ -404,9 +446,8 @@ static void print_help(struct isl_arg *arg, const char *prefix)
                case isl_arg_str:
                        print_str_help(&arg[i], prefix);
                        break;
+               case isl_arg_alias:
                case isl_arg_version:
-                       printf("  -V, --version\n");
-                       break;
                case isl_arg_arg:
                case isl_arg_child:
                case isl_arg_user:
@@ -439,6 +480,26 @@ static const char *prog_name(const char *prog)
        return prog;
 }
 
+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 void print_help_and_exit(struct isl_arg *arg, const char *prog)
 {
        int i;
@@ -452,10 +513,35 @@ static void print_help_and_exit(struct isl_arg *arg, const char *prog)
        printf("\n\n");
 
        print_help(arg, NULL);
+       printf("\n");
+       if (any_version(arg))
+               printf("  -V, --version\n");
+       print_bool_help(help_arg, NULL);
 
        exit(0);
 }
 
+static int match_long_name(struct isl_arg *decl,
+       const char *start, const char *end)
+{
+       do {
+               if (end - start == strlen(decl->long_name) &&
+                   !strncmp(start, decl->long_name, end - start))
+                       return 1;
+       } while ((++decl)->type == isl_arg_alias);
+
+       return 0;
+}
+
+static const char *skip_dash_dash(struct isl_arg *decl, const char *arg)
+{
+       if (!strncmp(arg, "--", 2))
+               return arg + 2;
+       if ((decl->flags & ISL_ARG_SINGLE_DASH) && arg[0] == '-')
+               return arg + 1;
+       return NULL;
+}
+
 static const char *skip_name(struct isl_arg *decl, const char *arg,
        const char *prefix, int need_argument, int *has_argument)
 {
@@ -470,11 +556,13 @@ static const char *skip_name(struct isl_arg *decl, const char *arg,
                        *has_argument = arg[2] != '\0';
                return arg + 2;
        }
+       if (!decl->long_name)
+               return NULL;
 
-       if (strncmp(arg, "--", 2))
+       name = skip_dash_dash(decl, arg);
+       if (!name)
                return NULL;
 
-       name = arg + 2;
        equal = strchr(name, '=');
        if (need_argument && !equal)
                return NULL;
@@ -490,8 +578,7 @@ static const char *skip_name(struct isl_arg *decl, const char *arg,
                        name += prefix_len + 1;
        }
 
-       if (end - name != strlen(decl->long_name) ||
-           strncmp(name, decl->long_name, end - name))
+       if (!match_long_name(decl, name, end))
                return NULL;
 
        return equal ? equal + 1 : end;
@@ -590,28 +677,64 @@ static int parse_flags_option(struct isl_arg *decl, char **arg,
        return has_argument ? 1 : 2;
 }
 
-static int parse_bool_option(struct isl_arg *decl, const char *arg,
+static int parse_bool_option(struct isl_arg *decl, char **arg,
        const char *prefix, void *opt)
 {
-       if (skip_name(decl, arg, prefix, 0, NULL)) {
-               *(unsigned *)(((char *)opt) + decl->offset) = 1;
+       const char *name;
+       unsigned *p = (unsigned *)(((char *)opt) + decl->offset);
+
+       if (skip_name(decl, arg[0], prefix, 0, NULL)) {
+               if ((decl->flags && ISL_ARG_BOOL_ARG) && arg[1]) {
+                       char *endptr;
+                       int val = strtol(arg[1], &endptr, 0);
+                       if (*endptr == '\0' && (val == 0 || val == 1)) {
+                               if (decl->u.b.set)
+                                       decl->u.b.set(opt, val);
+                               else if (decl->offset != (size_t) -1)
+                                       *p = val;
+                               return 2;
+                       }
+               }
+               if (decl->u.b.set)
+                       decl->u.b.set(opt, 1);
+               else if (decl->offset != (size_t) -1)
+                       *p = 1;
 
                return 1;
        }
 
-       if (strncmp(arg, "--no-", 5))
+       if (!decl->long_name)
+               return 0;
+
+       name = skip_dash_dash(decl, arg[0]);
+       if (!name)
+               return 0;
+
+       if (prefix) {
+               size_t prefix_len = strlen(prefix);
+               if (strncmp(name, prefix, prefix_len) == 0 &&
+                   name[prefix_len] == '-') {
+                       name += prefix_len + 1;
+                       prefix = NULL;
+               }
+       }
+
+       if (strncmp(name, "no-", 3))
                return 0;
-       arg += 5;
+       name += 3;
 
        if (prefix) {
                size_t prefix_len = strlen(prefix);
-               if (strncmp(arg, prefix, prefix_len) == 0 &&
-                   arg[prefix_len] == '-')
-                       arg += prefix_len + 1;
+               if (strncmp(name, prefix, prefix_len) == 0 &&
+                   name[prefix_len] == '-')
+                       name += prefix_len + 1;
        }
 
-       if (!strcmp(arg, decl->long_name)) {
-               *(unsigned *)(((char *)opt) + decl->offset) = 0;
+       if (match_long_name(decl, name, name + strlen(name))) {
+               if (decl->u.b.set)
+                       decl->u.b.set(opt, 0);
+               else if (decl->offset != (size_t) -1)
+                       *p = 0;
 
                return 1;
        }
@@ -631,11 +754,13 @@ static int parse_str_option(struct isl_arg *decl, char **arg,
                return 0;
 
        if (has_argument) {
+               free(*p);
                *p = strdup(s);
                return 1;
        }
 
        if (arg[1]) {
+               free(*p);
                *p = strdup(arg[1]);
                return 2;
        }
@@ -744,7 +869,7 @@ static int parse_option(struct isl_arg *decl, char **arg,
                        parsed = parse_ulong_option(&decl[i], arg, prefix, opt);
                        break;
                case isl_arg_bool:
-                       parsed = parse_bool_option(&decl[i], *arg, prefix, opt);
+                       parsed = parse_bool_option(&decl[i], arg, prefix, opt);
                        break;
                case isl_arg_str:
                        parsed = parse_str_option(&decl[i], arg, prefix, opt);
@@ -752,6 +877,7 @@ static int parse_option(struct isl_arg *decl, char **arg,
                case isl_arg_child:
                        parsed = parse_child_option(&decl[i], arg, opt);
                        break;
+               case isl_arg_alias:
                case isl_arg_arg:
                case isl_arg_user:
                case isl_arg_version:
@@ -765,26 +891,6 @@ static int parse_option(struct isl_arg *decl, char **arg,
        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 void print_version(struct isl_arg *decl)
 {
        int i;
@@ -830,9 +936,19 @@ static int n_arg(struct isl_arg *arg)
        return n_arg;
 }
 
+static int next_arg(struct isl_arg *arg, int a)
+{
+       for (++a; arg[a].type != isl_arg_end; ++a)
+               if (arg[a].type == isl_arg_arg)
+                       return a;
+
+       return -1;
+}
+
 int isl_arg_parse(struct isl_arg *arg, int argc, char **argv, void *opt,
        unsigned flags)
 {
+       int a = -1;
        int skip = 0;
        int i;
        int n;
@@ -852,8 +968,23 @@ int isl_arg_parse(struct isl_arg *arg, int argc, char **argv, void *opt,
 
        while (argc > 1 + skip) {
                int parsed;
-               if (argv[1 + skip][0] != '-')
-                       break;
+               if (argv[1 + skip][0] != '-') {
+                       a = next_arg(arg, a);
+                       if (a >= 0) {
+                               char **p;
+                               p = (char **)(((char *)opt)+arg[a].offset);
+                               free(*p);
+                               *p = strdup(argv[1 + skip]);
+                               argc = drop_argument(argc, argv, 1 + skip, 1);
+                               --n;
+                       } else if (ISL_FL_ISSET(flags, ISL_ARG_ALL)) {
+                               fprintf(stderr, "%s: extra argument: %s\n",
+                                           prog_name(argv[0]), argv[1 + skip]);
+                               exit(-1);
+                       } else
+                               ++skip;
+                       continue;
+               }
                parsed = parse_option(arg, &argv[1 + skip], NULL, opt);
                if (parsed)
                        argc = drop_argument(argc, argv, 1 + skip, parsed);
@@ -865,21 +996,11 @@ int isl_arg_parse(struct isl_arg *arg, int argc, char **argv, void *opt,
                        ++skip;
        }
 
-       if (ISL_FL_ISSET(flags, ISL_ARG_ALL) ? argc != 1 + n
-                                            : argc < 1 + skip + n) {
-               fprintf(stderr, "%s: expecting %d arguments\n",
+       if (n > 0) {
+               fprintf(stderr, "%s: expecting %d more argument(s)\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;
 }