Hi, nice work! I was just trying it and I found a small error, see below
On Thu, Jun 5, 2014 at 1:32 PM, <svn_gr...@osgeo.org> wrote: > Author: hcho > Date: 2014-06-05 10:32:48 -0700 (Thu, 05 Jun 2014) > New Revision: 60713 > > Modified: > grass/trunk/include/gis.h > grass/trunk/lib/gis/parser.c > Log: > The "exclusive" member of the Option and Flag structures is a > comma-separated > string. Whitespaces are not ignored. Each name separated by comma can be > used > to group options/flags together, make them mutually exclusive, or make one > of > them conditionally required. > > Names starting with "+" tie together options/flags and names starting with > "*" > (name ignored) make them conditionally required (not always required, but > if > some other options/flags are not used, they become required). Other names > make > options/flags mutually exclusive in the same group. These three different > types > of grouping can be mixed. > > EXAMPLES > > 1. opt1 & opt2 are mutually exclusive. > opt2 & opt3 are mutually exclusive. > > opt1->exclusive = "1"; > opt2->exclusive = "1,2"; > opt3->exclusive = "2"; > > 2. opt1 & opt2 must be used together. > > opt1->exclusive = "+1"; > opt2->exclusive = "+1"; > opt3->exclusive = ""; > > 3. opt1 or opt2 must be used. Both can be used together. Naming ignored. > > opt1->exclusive = "*ignored"; > opt2->exclusive = "*"; > opt3->exclusive = ""; > > 4. (opt1 & opt2 together) or (opt3 & opt4 together) must be used. All four > can > be used together. > > opt1->exclusive = "+1,*"; > opt2->exclusive = "+1"; /* * is optional because opt2 is tied with > opt1 */ > opt3->exclusive = "+2,*"; > opt4->exclusive = "+2"; > It seems that the * is needed for both parameters (opt1 and opt2). When I have two types of inputs and one of them is required (exactly one) and I put * only to one of them (and they are in one group), I get this when I don't specify any of them in the command line: ERROR: One or more of the following options/flags must be used: or input2= instead of ERROR: One or more of the following options/flags must be used: input1= or input2= > 5. Only one of (opt1 & opt2 together) or (opt3 & opt4 together) must be > used. > All four cannot be used together. > > opt1->exclusive = "+1,*,1"; > opt2->exclusive = "+1"; /* * is optional because opt2 is tied with > opt1 */ > opt3->exclusive = "+2,*,1"; > opt4->exclusive = "+2"; /* 1 is optional because opt4 is tied with > opt3 */ > > > Modified: grass/trunk/include/gis.h > =================================================================== > --- grass/trunk/include/gis.h 2014-06-05 04:48:03 UTC (rev 60712) > +++ grass/trunk/include/gis.h 2014-06-05 17:32:48 UTC (rev 60713) > @@ -75,12 +75,12 @@ > #define U_RADIANS 7 > #define U_DEGREES 8 > /* Temporal units from the datetime library */ > -#define U_YEARS DATETIME_YEAR > -#define U_MONTHS DATETIME_MONTH > -#define U_DAYS DATETIME_DAY > -#define U_HOURS DATETIME_HOUR > -#define U_MINUTES DATETIME_MINUTE > -#define U_SECONDS DATETIME_SECOND > +#define U_YEARS DATETIME_YEAR > +#define U_MONTHS DATETIME_MONTH > +#define U_DAYS DATETIME_DAY > +#define U_HOURS DATETIME_HOUR > +#define U_MINUTES DATETIME_MINUTE > +#define U_SECONDS DATETIME_SECOND > > /*! \brief Projection code - XY coordinate system (unreferenced data) */ > #define PROJECTION_XY 0 > @@ -256,7 +256,7 @@ > G_OPT_M_MAPSET, /*!< mapset */ > G_OPT_M_COORDS, /*!< coordinates */ > G_OPT_M_COLR, /*!< color rules */ > - G_OPT_M_DIR, /*!< directory input */ > + G_OPT_M_DIR, /*!< directory input */ > G_OPT_M_REGION, /*!< saved region */ > > G_OPT_STDS_INPUT, /*!< old input space time dataset of type > strds, str3ds or stvds */ > @@ -273,7 +273,7 @@ > G_OPT_STVDS_OUTPUT, /*!< new output space time vector dataset > */ > G_OPT_MAP_INPUT, /*!< old input map of type raster, vector > or raster3d */ > G_OPT_MAP_INPUTS, /*!< old input maps of type raster, > vector or raster3d */ > - G_OPT_STDS_TYPE, /*!< the type of a space time dataset: > strds, str3ds, stvds */ > + G_OPT_STDS_TYPE, /*!< the type of a space time dataset: > strds, str3ds, stvds */ > G_OPT_MAP_TYPE, /*!< The type of an input map: raster, > vect, rast3d */ > G_OPT_T_TYPE, /*!< The temporal type of a space time > dataset */ > G_OPT_T_WHERE, /*!< A temporal GIS framework SQL WHERE > statement */ > @@ -393,15 +393,15 @@ > /*! \brief Resolution - east to west cell size for 2D data */ > double ew_res; > /*! \brief Resolution - east to west cell size for 3D data */ > - double ew_res3; > + double ew_res3; > /*! \brief Resolution - north to south cell size for 2D data */ > - double ns_res; > + double ns_res; > /*! \brief Resolution - north to south cell size for 3D data */ > - double ns_res3; > + double ns_res3; > /*! \brief Resolution - top to bottom cell size for 3D data */ > - double tb_res; > + double tb_res; > /*! \brief Extent coordinates (north) */ > - double north; > + double north; > /*! \brief Extent coordinates (south) */ > double south; > /*! \brief Extent coordinates (east) */ > @@ -455,6 +455,8 @@ > /*! > \brief Structure that stores option information > > + Used by the G_parser() system. > + > The descriptions member contains pairs of option and option > descriptions separated by semicolon ';'. > For example, when options member is set using: > @@ -472,7 +474,61 @@ > GUI dependency is a list of options (separated by commas) to be updated > if the value is changed. > > - Used by the G_parser() system. > + The exclusive member of the Option and Flag structures is a > comma-separated > + string. Whitespaces are not ignored. Each name separated by comma can > be used > + to group options/flags together, make them mutually exclusive, or make > one of > + them conditionally required. Names starting with "+" tie together > + options/flags and names starting with "*" (name ignored) make them > + conditionally required (not always required, but if some other > options/flags > + are not used, they become required). Other names make options/flags > mutually > + exclusive in the same group. These three different types of grouping > can be > + mixed. G_parser() raises a fatal error if any violations are found. > + > + Examples > + > + 1. opt1 & opt2 are mutually exclusive and opt2 & opt3 are mutually > exclusive. > + > + \code > + opt1->exclusive = "1"; > + opt2->exclusive = "1,2"; > + opt3->exclusive = "2"; > + \endcode > + > + 2. opt1 & opt2 must be used together. > + > + \code > + opt1->exclusive = "+1"; > + opt2->exclusive = "+1"; > + opt3->exclusive = ""; > + \endcode > + > + 3. opt1 or opt2 must be used. Both can be used together. Naming ignored. > + > + \code > + opt1->exclusive = "*ignored"; > + opt2->exclusive = "*"; > + opt3->exclusive = ""; > + \endcode > + > + 4. (opt1 & opt2 together) or (opt3 & opt4 together) must be used. All > four > + can be used together. > + > + \code > + opt1->exclusive = "+1,*"; > + opt2->exclusive = "+1"; // * is optional because opt2 is tied with > opt1 > + opt3->exclusive = "+2,*"; > + opt4->exclusive = "+2"; > + \endcode > + > + 5. Only one of (opt1 & opt2 together) or (opt3 & opt4 together) must be > used. > + All four cannot be used together. > + > + \code > + opt1->exclusive = "+1,*,1"; > + opt2->exclusive = "+1"; // * is optional because opt2 is tied with > opt1 > + opt3->exclusive = "+2,*,1"; > + opt4->exclusive = "+2"; // 1 is optional because opt4 is tied with > opt3 > + \endcode > */ > struct Option > { > > Modified: grass/trunk/lib/gis/parser.c > =================================================================== > --- grass/trunk/lib/gis/parser.c 2014-06-05 04:48:03 UTC (rev 60712) > +++ grass/trunk/lib/gis/parser.c 2014-06-05 17:32:48 UTC (rev 60713) > @@ -117,8 +117,13 @@ > static void module_gui_wx(void); > static void add_exclusive(const char *, int, const char *); > static struct Exclusive *find_exclusive(char *); > +static int has_exclusive_name(const char *, char *); > static int has_exclusive_key(int, char **, char *); > -static void check_exclusive(int); > +static int has_either_or(const char *); > +static void check_exclusive(); > +static void check_mutually_exclusive_inputs(); > +static void check_tied_together_inputs(); > +static void check_either_or_inputs(); > static void append_error(const char *); > > /*! > @@ -550,7 +555,7 @@ > > } > > - check_exclusive(0); > + check_exclusive(); > } > > /* Split options where multiple answers are OK */ > @@ -902,6 +907,44 @@ > return NULL; > } > > +static int has_exclusive_name(const char *names, char *name) > +{ > + char *ptr1; > + > + for (ptr1 = (char *)names;;) { > + int len; > + char *ptr2; > + > + for (len = 0, ptr2 = ptr1; *ptr2 != '\0' && *ptr2 != ','; > + ptr2++, len++) ; > + > + if (len > 0) { /* skip ,, */ > + char *aname; > + > + aname = G_malloc(len + 1); > + memcpy(aname, ptr1, len); > + aname[len] = 0; > + > + if (strcmp(name, aname) == 0) { > + G_free(aname); > + return 1; > + } > + > + G_free(aname); > + } > + > + if (*ptr2 == '\0') > + break; > + > + ptr1 = ptr2 + 1; > + > + if (*ptr1 == '\0') > + break; > + } > + > + return 0; > +} > + > static int has_exclusive_key(int n_keys, char **keys, char *key) > { > int i; > @@ -914,87 +957,234 @@ > return 0; > } > > -static void check_exclusive(int print_group) > +static int has_either_or(const char *names) > { > - int i, n, allocated, len; > - char **keys, *err; > + return names && (names[0] == '*' || strstr(names, ",*")); > +} > > - n = 0; > - allocated = 0; > - len = 0; > - keys = NULL; > +static void check_exclusive() > +{ > + check_mutually_exclusive_inputs(); > + check_tied_together_inputs(); > + check_either_or_inputs(); > +} > > +static void check_mutually_exclusive_inputs() > +{ > + int i; > + > for (i = 0; i < st->n_exclusive; i++) { > struct Exclusive *exclusive; > > exclusive = &st->exclusive[i]; > > - G_debug(1, "check_exclusive(): Exclusive option/flag group: %s", > - exclusive->name); > + if (exclusive->name[0] == '+' || exclusive->name[0] == '*') > + continue; > > - if (exclusive->n_keys >= 1) > - G_debug(1, "check_exclusive():\t%s", exclusive->keys[0]); > + G_debug(1, "check_exclusive(): Mutually exclusive option/flag > group: " > + "%s", exclusive->name); > + G_debug(1, "check_exclusive():\t%s", exclusive->keys[0]); > > if (exclusive->n_keys > 1) { > - int j; > + int len, j; > + char *err; > + > + len = 0; > + for (j = 0; j < exclusive->n_keys; j++) { > + len += strlen(exclusive->keys[j]) + 2; /* 2 for comma adn > space > + */ > + if (j > 0) > + G_debug(1, "check_exclusive():\t%s", > exclusive->keys[j]); > + } > + > + err = G_malloc(len + 100); > + sprintf(err, _("The following options/flags cannot be used " > + "together: ")); > + > + len = strlen(err); > + for (j = 0; j < exclusive->n_keys - 2; j++) { > + sprintf(err + len, "%s, ", exclusive->keys[j]); > + len = strlen(err); > + } > + > + sprintf(err + len, _("%s and %s"), exclusive->keys[j], > + exclusive->keys[j + 1]); > + > + append_error(err); > + G_free(err); > + } > + } > +} > > - if (print_group) { > - len = strlen(exclusive->name); > - for (j = 0; j < exclusive->n_keys; j++) { > - len += strlen(exclusive->keys[j]) + 2; /* 2 for comma > adn > - space */ > - if (j > 0) > - G_debug(1, "check_exclusive():\t%s", > - exclusive->keys[j]); > - } > +static void check_tied_together_inputs() > +{ > + int i; > > - err = G_malloc(len + 100); > - sprintf(err, _("Mutually exclusive options/flags " > - "in group '%s': "), exclusive->name); > - > - len = strlen(err); > - for (j = 0; j < exclusive->n_keys - 2; j++) { > - sprintf(err + len, "%s, ", exclusive->keys[j]); > - len = strlen(err); > - } > - > - sprintf(err + len, _("%s and %s"), exclusive->keys[j], > - exclusive->keys[j + 1]); > + for (i = 0; i < st->n_exclusive; i++) { > + struct Exclusive *exclusive; > + struct Option *opt; > + struct Flag *flag; > + int n, n_keys, len; > + char *err; > > - append_error(err); > + exclusive = &st->exclusive[i]; > + > + if (exclusive->name[0] != '+') > + continue; > + > + G_debug(1, "check_exclusive(): Tied-together option/flag group: > %s", > + exclusive->name); > + > + n_keys = 0; > + len = 0; > + opt = &st->first_option; > + while (opt) { > + if (has_exclusive_name(opt->exclusive, exclusive->name)) { > + n_keys++; > + len += strlen(opt->key) + 3; /* 3 for =, comma and space */ > + G_debug(1, "check_exclusive():\t%s=", opt->key); > } > - else { > - for (j = 0; j < exclusive->n_keys; j++) { > - if (!has_exclusive_key(n, keys, exclusive->keys[j])) { > - if (n >= allocated) { > - allocated += 10; > - keys = G_realloc(keys, allocated * sizeof(char > *)); > - } > - keys[n] = exclusive->keys[j]; > - len += strlen(keys[n++]) + 2; /* 2 for comma and > space > - */ > - } > - } > + opt = opt->next_opt; > + } > + > + flag = &st->first_flag; > + while (flag) { > + if (has_exclusive_name(flag->exclusive, exclusive->name)) { > + n_keys++; > + len += 4; /* 4 for -, flag, comma and space */ > + G_debug(1, "check_exclusive():\t-%c", flag->key); > } > + flag = flag->next_flag; > } > - } > > - if (!print_group && n) { > + if (n_keys == 1 || exclusive->n_keys == n_keys) > + continue; > + > err = G_malloc(len + 100); > - sprintf(err, _("Mutually exclusive options/flags: ")); > + sprintf(err, _("The following options/flags must be used together: > ")); > > + n = 0; > len = strlen(err); > - for (i = 0; i < n - 2; i++) { > - sprintf(err + len, "%s, ", keys[i]); > - len = strlen(err); > + opt = &st->first_option; > + while (opt) { > + if (has_exclusive_name(opt->exclusive, exclusive->name)) { > + n++; > + if (n <= n_keys - 2) > + sprintf(err + len, "%s=, ", opt->key); > + else if (n == n_keys - 1) > + sprintf(err + len, "%s= ", opt->key); > + else > + sprintf(err + len, "and %s=", opt->key); > + len = strlen(err); > + } > + opt = opt->next_opt; > } > > - sprintf(err + len, _("%s and %s"), keys[i], keys[i + 1]); > - > + flag = &st->first_flag; > + while (flag) { > + if (has_exclusive_name(flag->exclusive, exclusive->name)) { > + n++; > + if (n <= n_keys - 2) > + sprintf(err + len, "-%c, ", flag->key); > + else if (n == n_keys - 1) > + sprintf(err + len, "-%c ", flag->key); > + else > + sprintf(err + len, "and -%c", flag->key); > + len = strlen(err); > + } > + flag = flag->next_flag; > + } > + > append_error(err); > + G_free(err); > } > } > > +static void check_either_or_inputs() > +{ > + int n_keys, len; > + struct Option *opt; > + struct Flag *flag; > + > + G_debug(1, "check_exclusive(): Either-or options/flags " > + "(group names ignored)"); > + > + n_keys = 0; > + len = 0; > + opt = &st->first_option; > + while (opt) { > + if (has_either_or(opt->exclusive)) { > + n_keys++; > + len += strlen(opt->key) + 3; /* 3 for =, comma and space */ > + G_debug(1, "check_exclusive():\t%s=", opt->key); > + } > + opt = opt->next_opt; > + } > + > + flag = &st->first_flag; > + while (flag) { > + if (has_either_or(flag->exclusive)) { > + n_keys++; > + len += 4; /* 4 for -, flag, comma and space */ > + G_debug(1, "check_exclusive():\t-%c", flag->key); > + } > + flag = flag->next_flag; > + } > + > + if (n_keys > 0) { > + int i; > + > + for (i = 0; i < st->n_exclusive; i++) { > + if (st->exclusive[i].name[0] == '*') > + break; > + } > + > + if (i == st->n_exclusive) { > + int n; > + char *err; > + > + err = G_malloc(len + 100); > + sprintf(err, _("One or more of the following options/flags " > + "must be used: ")); > + > + n = 0; > + len = strlen(err); > + opt = &st->first_option; > + while (opt) { > + if (has_either_or(opt->exclusive)) { > + n++; > + if (n <= n_keys - 2) > + sprintf(err + len, "%s=, ", opt->key); > + else if (n == n_keys - 1) > + sprintf(err + len, "%s= ", opt->key); > + else > + sprintf(err + len, "or %s=", opt->key); > + len = strlen(err); > + } > + opt = opt->next_opt; > + } > + > + flag = &st->first_flag; > + while (flag) { > + if (has_either_or(flag->exclusive)) { > + n++; > + if (n <= n_keys - 2) > + sprintf(err + len, "-%c, ", flag->key); > + else if (n == n_keys - 1) > + sprintf(err + len, "-%c ", flag->key); > + else > + sprintf(err + len, "or -%c", flag->key); > + len = strlen(err); > + } > + flag = flag->next_flag; > + } > + > + append_error(err); > + G_free(err); > + } > + } > +} > + > static void set_flag(int f) > { > struct Flag *flag; > > _______________________________________________ > grass-commit mailing list > grass-com...@lists.osgeo.org > http://lists.osgeo.org/mailman/listinfo/grass-commit >
_______________________________________________ grass-dev mailing list grass-dev@lists.osgeo.org http://lists.osgeo.org/mailman/listinfo/grass-dev