<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>