]> mj.ucw.cz Git - libucw.git/commitdiff
Opt: OPT_MULTIPLE with clists replaced by OPT_CL_MULTIPLE with gary's
authorMartin Mares <mj@ucw.cz>
Tue, 28 Jan 2014 17:28:57 +0000 (18:28 +0100)
committerMartin Mares <mj@ucw.cz>
Tue, 28 Jan 2014 17:28:57 +0000 (18:28 +0100)
ucw/opt-test.c
ucw/opt.c
ucw/opt.h

index b9bedde5160a8621c314b7a1524e39392f907a71..a030eefea944c6d775ff618eae0e369a9e505d74 100644 (file)
@@ -2,6 +2,7 @@
  *     UCW Library -- Parsing of command line options
  *
  *     (c) 2013 Jan Moskyto Matejka <mq@ucw.cz>
+ *     (c) 2014 Martin Mares <mj@ucw.cz>
  *
  *     This software may be freely distributed and used according to the terms
  *     of the GNU Lesser General Public License.
@@ -11,6 +12,7 @@
 #include <ucw/opt.h>
 #include <ucw/strtonum.h>
 #include <ucw/fastbuf.h>
+#include <ucw/gary.h>
 
 static void show_version(struct opt_item * opt UNUSED, const char * value UNUSED, void * data UNUSED) {
   printf("This is a simple tea boiling console v0.1.\n");
@@ -45,7 +47,7 @@ static int english = 0;
 static int sugar = 0;
 static int verbose = 1;
 static int with_gas = 0;
-static clist black_magic;
+static int *black_magic;
 static int pray = 0;
 static int water_amount = 0;
 static char * first_tea = NULL;
@@ -140,7 +142,7 @@ static struct opt_section options = {
              "\t\tOnly integer values allowed."),
     OPT_INC('v', "verbose", verbose, 0, "\tVerbose (the more -v, the more verbose)"),
     OPT_INC('q', "quiet", verbose, OPT_NEGATIVE, "\tQuiet (the more -q, the more quiet)"),
-    OPT_INT('b', NULL, black_magic, OPT_MULTIPLE, "<strength>\tUse black magic to make the tea extraordinarily delicious.\n\t\tMay be specified more than once to describe the amounts of black magic to be invoked in each step of tea boiling."),
+    OPT_INT_MULTIPLE('b', NULL, black_magic, 0, "<strength>\tUse black magic to make the tea extraordinarily delicious.\n\t\tMay be specified more than once to describe the amounts of black magic to be invoked in each step of tea boiling."),
     OPT_BOOL('p', "pray", pray, OPT_SINGLE, "\tPray before boiling"),
     OPT_STRING(OPT_POSITIONAL(1), NULL, first_tea, OPT_REQUIRED, ""),
     OPT_CALL(OPT_POSITIONAL_TAIL, NULL, add_tea, &tea_list, 0, ""),
@@ -166,7 +168,7 @@ struct intnode {
 int main(int argc UNUSED, char ** argv)
 {
   cf_def_file = "etc/libucw";
-  clist_init(&black_magic);
+  GARY_INIT(black_magic, 0);
   opt_parse(&options, argv+1);
 
   printf("English style: %s|", english ? "yes" : "no");
@@ -176,8 +178,9 @@ int main(int argc UNUSED, char ** argv)
     printf("Chosen teapot: %s|", teapot_type_str[set]);
   printf("Temperature: %d%s|", temperature.value, temp_scale_str[temperature.scale]);
   printf("Verbosity: %d|", verbose);
-  CLIST_FOR_EACH(struct intnode *, n, black_magic)
-    printf("Black magic: %d|", n->x);
+  uns magick = GARY_SIZE(black_magic);
+  for (uns i=0; i<magick; i++)
+    printf("Black magic: %d|", black_magic[i]);
   printf("Prayer: %s|", pray ? "yes" : "no");
   printf("Water amount: %d|", water_amount);
   printf("Gas: %s|", with_gas ? "yes" : "no");
index 0709e820b61cba08d15455340256c17bbeabde21..5598a13b340bdbff9402495d8c38781feebfefa6 100644 (file)
--- a/ucw/opt.c
+++ b/ucw/opt.c
@@ -11,6 +11,7 @@
 #include <ucw/lib.h>
 #include <ucw/opt.h>
 #include <ucw/opt-internal.h>
+#include <ucw/gary.h>
 #include <ucw/stkstring.h>
 #include <ucw/strtonum.h>
 
@@ -20,6 +21,7 @@
 static uns opt_default_value_flags[] = {
     [OPT_CL_BOOL] = OPT_NO_VALUE,
     [OPT_CL_STATIC] = OPT_MAYBE_VALUE,
+    [OPT_CL_MULTIPLE] = OPT_REQUIRED_VALUE,
     [OPT_CL_SWITCH] = OPT_NO_VALUE,
     [OPT_CL_INC] = OPT_NO_VALUE,
     [OPT_CL_CALL] = 0,
@@ -110,20 +112,6 @@ static struct opt_precomputed * opt_find_item_longopt(struct opt_context * oc, c
   opt_failure("Invalid option --%s.", str);
 }
 
-// FIXME: Use simple-lists?
-#define OPT_PTR(type) ({                       \
-  type * ptr;                                  \
-  if (item->flags & OPT_MULTIPLE) {            \
-    struct {                                   \
-      cnode n;                                         \
-      type v;                                  \
-    } * n = xmalloc(sizeof(*n));               \
-    clist_add_tail(item->ptr, &(n->n));        \
-    ptr = &(n->v);                             \
-  } else                                       \
-    ptr = item->ptr;                           \
-  ptr; })
-
 static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * opt, char * value) {
   struct opt_item * item = opt->item;
 
@@ -145,8 +133,15 @@ static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * op
        opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): 1/0, y/n, yes/no, true/false.", THIS_OPT);
       break;
     case OPT_CL_STATIC:
+    case OPT_CL_MULTIPLE:
       {
        char * e = NULL;
+       void * ptr;
+       if (item->cls == OPT_CL_STATIC)
+         ptr = item->ptr;
+       else
+         ptr = GARY_PUSH_GENERIC(*(void **)item->ptr);
+#define OPT_PTR(type) ((type *) ptr)
        switch (item->type) {
          case CT_INT:
            if (!value)
@@ -188,7 +183,7 @@ static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * op
            break;
          case CT_USER:
              {
-               char * e = item->u.utype->parser(value, item->ptr);
+               char * e = item->u.utype->parser(value, ptr);
                if (e)
                  opt_failure("Cannot parse the value of %s: %s", THIS_OPT, e);
                break;
@@ -196,6 +191,7 @@ static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * op
          default:
            ASSERT(0);
        }
+#undef OPT_PTR
        break;
       }
     case OPT_CL_SWITCH:
index aea225a22c53cb44ff856c275c063442e85402f6..e37fb68c1f42d7284ca6f9b221bfa0bf2fe34a03 100644 (file)
--- a/ucw/opt.h
+++ b/ucw/opt.h
@@ -50,6 +50,8 @@
  *   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
@@ -66,6 +68,7 @@ enum opt_class {
   OPT_CL_END,
   OPT_CL_BOOL,
   OPT_CL_STATIC,
+  OPT_CL_MULTIPLE,
   OPT_CL_SWITCH,
   OPT_CL_INC,
   OPT_CL_CALL,
@@ -135,6 +138,7 @@ struct opt_item {
  * 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.
  **/
@@ -158,29 +162,43 @@ struct opt_item {
 #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 }
 
 /** 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 `int`s. **/
+#define OPT_BOOL_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 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 }
 
 /**
  * When this option appears, call the function @fn with parameters @item, @value, @data,
@@ -197,6 +215,9 @@ struct opt_item {
  **/
 #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 }