]> mj.ucw.cz Git - libucw.git/commitdiff
Opt: not yet compiling, written part of the parser
authorJan 'Moskyt' Matejka <mq@ucw.cz>
Thu, 25 Apr 2013 15:06:32 +0000 (17:06 +0200)
committerJan 'Moskyt' Matejka <mq@ucw.cz>
Thu, 25 Apr 2013 15:06:32 +0000 (17:06 +0200)
ucw/opt.c
ucw/opt.h

index 0bd8df2585ed1b35041f68ebde3a755217933743..92c7a7372c851db809beccfd7f050cd3e05f0c8e 100644 (file)
--- a/ucw/opt.c
+++ b/ucw/opt.c
@@ -9,11 +9,21 @@
 
 #include <ucw/lib.h>
 #include <ucw/opt.h>
+#include <ucw/conf.h>
 #include <ucw/stkstring.h>
 #include <ucw/strtonum.h>
 
 #include <alloca.h>
 
+static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2);
+static void opt_failure(const char * mesg, ...) {
+  va_list args;
+  va_start(args, mesg);
+  stk_vprintf(mesg, args);
+  exit(OPT_EXIT_BAD_ARGS);
+  va_end(args);
+}
+
 struct opt_section * opt_section_root;
 
 void opt_help_noexit_internal(struct opt_section * help) {
@@ -79,25 +89,168 @@ void opt_help_noexit_internal(struct opt_section * help) {
 }
 
 void opt_init(struct opt_section * options) {
-  opt_section_root = options;
-  for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++)
-    if (!(item->flags & OPT_VALUE_FLAGS)) {
+  for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++) {
+    if (item->cls == OPT_CL_SECTION)
+      opt_init(item->u.section);
+    else if (!(item->flags & OPT_VALUE_FLAGS)) {
       if (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);
-       exit(2);
+       ASSERT(0);
       } else
        item->flags |= opt_default_value_flags[item->cls];
     }
+  }
+  opt_section_root = options;
+}
+
+static struct opt_item * opt_find_item_longopt_section(char * str, struct opt_section * options) {
+  uns len = strlen(str);
+  struct opt_item * candidate = NULL;
+
+  for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++) {
+    if (item->cls == OPT_CL_SECTION) {
+      struct opt_item * out = opt_find_item_longopt_section(str, item->u.section);
+      if (out) {
+       if (candidate)
+         opt_failure("Ambiguos prefix %s: Found matching %s and %s.\n", str, candidate->name, item->name);
+       else
+         candidate = out;
+      }
+    } else if (!strncmp(item->name, str, len)) {
+      if (strlen(item->name) == len)
+       return item;
+
+      if (candidate)
+       opt_failure("Ambiguos prefix %s: Found matching %s and %s.\n", str, candidate->name, item->name);
+      else
+       candidate = item;
+    }
+  }
+
+  if (candidate)
+    return candidate;
+  else {
+  }
+}
+
+static struct opt_item * opt_find_item_longopt(char * str) {
+  struct opt_item * out = opt_find_item_longopt_section(str, opt_section_root);
+  if (out == NULL)
+    opt_failure("Invalid argument: %s\n", str);
+  return out;
+}
+
+#define OPT_NAME (longopt ? stk_printf("--%s", item->name) : stk_printf("-%c", item->letter))
+static void opt_parse_value(struct opt_item * item, char * value, int longopt) {
+  switch (item->cls) {
+    case OPT_CL_BOOL:
+      if (!strcasecmp(value, "y") || !strcasecmp(value, "yes") || !strcasecmp(value, "true"))
+       *((int *) item->ptr) = 1;
+      else if (!strcasecmp(value, "n") || !strcasecmp(value, "no") || !strcasecmp(value, "false"))
+       *((int *) item->ptr) = 0;
+      else
+       opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): y/n, yes/no, true/false.\n", OPT_NAME);
+      break;
+    case OPT_CL_STATIC:
+      {
+       char * e = NULL;
+       switch (item->type) {
+         case CT_INT:
+           e = cf_parse_int(value, item->ptr);
+           if (e)
+             opt_failure("Integer value parsing failed for argument %s: %s\n", OPT_NAME, e);
+           break;
+         case CT_U64:
+           e = cf_parse_u64(value, item->ptr);
+           if (e)
+             opt_failure("Unsigned 64-bit value parsing failed for argument %s: %s\n", OPT_NAME, e);
+           break;
+         case CT_DOUBLE:
+           e = cf_parse_double(value, item->ptr);
+           if (e)
+             opt_failure("Double value parsing failed for argument %s: %s\n", OPT_NAME, e);
+           break;
+         case CT_IP:
+           e = cf_parse_ip(value, item->ptr);
+           if (e)
+             opt_failure("IP parsing failed for argument %s: %s\n", OPT_NAME, e);
+           break;
+         case CT_STRING:
+           item->ptr = strdup(value);
+           break;
+         default:
+           ASSERT(0);
+       }
+       break;
+      }
+    case OPT_CL_SWITCH:
+      if (*((int *)item->ptr) != -1)
+       opt_failure("Multiple switches: %s", OPT_NAME);
+      else
+       *((int *)item->ptr) = item->u.value;
+      break;
+    case OPT_CL_INC:
+      if (item->flags | OPT_DECREMENT)
+       (*((int *)item->ptr))--;
+      else
+       (*((int *)item->ptr))++;
+    case OPT_CL_CALL:
+
+    case OPT_CL_USER:
+      {
+       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\n", OPT_NAME, e);
+       break;
+      }
+  }
+}
+#undef OPT_NAME
+
+static int opt_longopt(char ** argv, int index) {
+  int eaten;
+  char * name_in = argv[index] + 2; // skipping the -- on the beginning
+  uns pos = strchrnul(name_in, '=') - name_in;
+  struct opt_item * item = opt_find_item_longopt(strndupa(name_in, pos));
+  char * value = NULL;
+  if (item->flags | OPT_REQUIRED_VALUE) {
+    if (pos < strlen(name_in))
+      value = name_in + pos + 1;
+    else {
+      value = argv[index+1];
+      eaten++;
+    }
+  }
+  else if (item->flags | OPT_MAYBE_VALUE) {
+    if (pos < strlen(name_in))
+      value = name_in + pos + 1;
+  }
+  else {
+    if (pos < strlen(name_in))
+      opt_failure("Argument %s must not have any value.", item->name);
+  }
 }
 
-int opt_parse(char ** argv, char *** posargs) {
-  // Temporary. To be fixed.
-  static char ** d;
-  d = xmalloc(sizeof (*d) * 2);
-  d[0] = "darjeeling";
-  d[1] = "puerh";
-  *posargs = d;
-  return 2;
+void opt_parse(char ** argv, opt_positional * callback) {
+
+  int force_positional = 0;
+  for (int i=0;argv[i];i++) {
+    if (argv[i][0] != '-' || force_positional) {
+      callback(argv[i]);
+    } else {
+      if (argv[i][1] == '-') {
+       if (argv[i][2] == '\0')
+         force_positional++;
+       else
+         i += opt_longopt(argv, i);
+      }
+      else if (argv[i][1])
+       i += opt_shortopt(argv, i);
+      else
+       callback(argv[i]);
+    }
+  }
 }
 
 #ifdef TEST
@@ -105,7 +258,7 @@ int opt_parse(char ** argv, char *** posargs) {
 
 static int show_version(const char ** param UNUSED) {
   printf("This is a simple tea boiling console v0.1.\n");
-  exit(0);
+  exit(EXIT_SUCCESS);
 }
 
 struct teapot_temperature {
@@ -131,12 +284,12 @@ static enum TEAPOT_TYPE {
 
 static int english = 0;
 static char * name = NULL;
-static uns sugar = 0;
-static uns verbose = 1;
+static int sugar = 0;
+static int verbose = 1;
 static int with_gas = 0;
-static uns black_magic = 0;
+static int black_magic = 0;
 static int pray = 0;
-static uns water_amount = 0;
+static int water_amount = 0;
 
 static const char * teapot_temperature_parser(char * in, void * ptr) {
   struct teapot_temperature * temp = ptr;
@@ -156,7 +309,7 @@ static const char * teapot_temperature_parser(char * in, void * ptr) {
     temp->scale = TEMP_REAUMUR;
   else {
     fprintf(stderr, "Unknown scale: %s\n", next);
-    exit(1);
+    exit(OPT_EXIT_BAD_ARGS);
   }
   return next + strlen(next);
 }
@@ -167,7 +320,7 @@ static void teapot_temperature_dumper(struct fastbuf * fb, void * ptr) {
 }
 
 static struct cf_user_type teapot_temperature_t = {
-  .size = 2*sizeof(int),
+  .size = sizeof(struct teapot_temperature),
   .name = "teapot_temperature_t",
   .parser = (cf_parser1*) teapot_temperature_parser,
   .dumper = (cf_dumper1*) teapot_temperature_dumper
@@ -175,7 +328,7 @@ static struct cf_user_type teapot_temperature_t = {
 
 static struct opt_section water_options = {
   OPT_ITEMS {
-    OPT_UNS('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Amount of water (in mls)"),
+    OPT_INT('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Amount of water (in mls)"),
     OPT_BOOL('G', "with-gas", with_gas, OPT_NO_VALUE, "Use water with gas"),
     OPT_END
   }
@@ -189,22 +342,23 @@ static struct opt_section help = {
     OPT_HELP("You may specify more kinds of tea, all of them will be boiled for you, in the given order."),
     OPT_HELP(""),
     OPT_HELP("Options:"),
-    OPT_SHOW_HELP,
+    OPT_HELP_OPTION,
     OPT_CALL('V', "version", show_version, OPT_NO_VALUE, "Show the version"),
     OPT_HELP(""),
     OPT_BOOL('e', "english-style", english, 0, "English style (with milk)"),
-    OPT_UNS('s', "sugar", sugar, OPT_REQUIRED_VALUE, "Amount of sugar (in teaspoons)"),
+    OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "Amount of sugar (in teaspoons)"),
     OPT_SWITCH(0, "standard-set", set, TEAPOT_STANDARD, 0, "Standard teapot"),
     OPT_SWITCH('x', "exclusive-set", set, TEAPOT_EXCLUSIVE, 0, "Exclusive teapot"),
     OPT_SWITCH('g', "glass-set", set, TEAPOT_GLASS, 0, "Transparent glass teapot"),
     OPT_SWITCH('h', "hands", set, TEAPOT_HANDS, 0, "Use user's hands as a teapot (a bit dangerous)"),
-    OPT_USER('t', "temperature", temperature, teapot_temperature_t, OPT_REQUIRED_VALUE, "Wanted final temperature of the tea to be served"),
-    OPT_HELP2("", "Supported scales: Celsius [60C], Fahrenheit [140F],"),
-    OPT_HELP2("", "                  Kelvin [350K], Rankine [600R] and Reaumur [50Re]"),
-    OPT_HELP2("", "Only integer values allowed."),
+    OPT_USER('t', "temperature", temperature, teapot_temperature_t, OPT_REQUIRED_VALUE,
+                 "Wanted final temperature of the tea to be served\n"
+             "\t\tSupported scales: \tCelsius [60C], Fahrenheit [140F],"
+             "\t\t\tKelvin [350K], Rankine [600R] and Reaumur [50Re]"
+             "\t\tOnly integer values allowed."),
     OPT_INC('v', "verbose", verbose, 0, "Verbose (the more -v, the more verbose)"),
     OPT_INC('q', "quiet", verbose, OPT_DECREMENT, "Quiet (the more -q, the more quiet)"),
-    OPT_UNS('b', "black-magic", black_magic, 0, "Use black magic to make the tea extraordinary delicious"),
+    OPT_INT('b', "black-magic", black_magic, 0, "Use black magic to make the tea extraordinary delicious"),
     OPT_BOOL('p', "pray", pray, 0, "Pray before boiling"),
     OPT_HELP(""),
     OPT_HELP("Water options:"),
@@ -223,7 +377,7 @@ int main(int argc, char ** argv)
   int teas_num;
 
   opt_init(&help);
-  teas_num = opt_parse(argv, &teas);
+  opt_parse(argv, NULL);
 
   for (int i=0; i<teas_num; i++)
     boil_tea(teas[i]);
index 368a9801c7499f6eddf7c0ccd7c43a949daee0cb..497bab641c28e593f3a581f1b5b81c5b836d0bf4 100644 (file)
--- a/ucw/opt.h
+++ b/ucw/opt.h
@@ -16,6 +16,8 @@
 #include <stdlib.h>
 #include <stdio.h>
 
+#define OPT_EXIT_BAD_ARGS 2
+
 /***
  * [[opt]]
  ***/
@@ -36,7 +38,6 @@ typedef void opt_custom_function(const char ** param);
 
 struct opt_section;
 struct opt_item {
-  const char letter;                   // short-op
   const char * name;                   // long-op
   void * ptr;                          // where to save
   const char * help;                   // description in --help
@@ -45,11 +46,12 @@ struct opt_item {
     int value;                         // value for OPT_SWITCH
     const char * help2;                        // second value for OPT_HELP2
     int (* call)(const char ** param); // function to call for OPT_CALL
-    struct cf_user_type * utype;               // specification of the user-defined type
+    struct cf_user_type * utype;       // specification of the user-defined type
   } u;
-  short flags;
-  enum opt_class cls;
-  enum cf_type type;
+  const char letter;                   // short-op
+  byte flags;
+  byte cls;                            // enum opt_class
+  byte type;                           // enum cf_type
 };
 
 struct opt_section {
@@ -62,8 +64,8 @@ struct opt_section {
  * Sub-items to be enclosed in OPT_ITEMS { } list
  * ----------------------------------------------
  *
- * OPT_SHOW_HELP declares --help and prints a line about that
- * OPT_HELP prints a line into help()
+ * OPT_HELP_OPTION declares --help and prints a line about that
+ * OPT_HELP prints a line into help
  * OPT_HELP2 prints two strings onto a line using the same tab structure as the option listing
  * OPT_BOOL declares boolean option with an auto-negation (--sth and --no-sth); may be changed by OPT_BOOL_SET_PREFIXES
  * OPT_STRING, OPT_UNS, OPT_INT declare simple string/uns/int option
@@ -78,13 +80,15 @@ struct opt_section {
  *
  ***/
 
-#define OPT_SHOW_HELP OPT_CALL(0, "help", opt_show_help_internal, OPT_NO_VALUE, "Show this help")
+#define OPT_HELP_OPTION OPT_CALL(0, "help", opt_show_help_internal, OPT_NO_VALUE, "Show this help")
 #define OPT_HELP(line) OPT_HELP2(line, NULL)
-#define OPT_HELP2(first, second) { .help = first, .cls = OPT_CL_HELP, .u.help2 = second }
+#define OPT_HELP2(first, second) { .help = first, .cls = OPT_CL_HELP, .u.help2 = second } // FIXME: remove this
 #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_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 }
-#define OPT_UNS(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, uns *), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_INT }
+#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 }
 #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 }
+#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 }
+#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 }
 #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 }
 #define OPT_CALL(shortopt, longopt, fn, fl, desc) { .letter = shortopt, .name = longopt, .ptr = NULL, .help = desc, .u.call = fn, .flags = fl, .cls = OPT_CL_CALL, .type = CT_USER }
 #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_USER, .type = CT_USER }
@@ -159,17 +163,13 @@ static void opt_usage(void) {
 void opt_init(struct opt_section * options);
 
 /**
- * Parse all the arguments.
- * Returns the number of positional arguments and an array of them in @posargs.
+ * Positional argument handler to be given to opt_parse()
  **/
-int opt_parse(char ** argv, char *** posargs);
+typedef void opt_positional(const char * str);
 
 /**
- * Parse all the arguments until first positional argument is found.
- * Returns the position of that argument in argv.
- * On next call of this function, start from the next item in argv.
- * Iterate this function to get all the positional arguments.
+ * Parse all the arguments. Run the @callback for each of the positional argument.
  **/
-int opt_get(char ** argv);
+void opt_parse(char ** argv, opt_positional * callback);
 
 #endif