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