[GRASS-dev] [GRASS-SVN] r60713 - in grass/trunk: include lib/gis

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_grass@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”;

  1. opt1 & opt2 must be used together.

opt1->exclusive = “+1”;
opt2->exclusive = “+1”;
opt3->exclusive = “”;

  1. opt1 or opt2 must be used. Both can be used together. Naming ignored.

opt1->exclusive = “ignored";
opt2->exclusive = "
”;
opt3->exclusive = “”;

  1. (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=

  1. 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
    1. opt1 & opt2 must be used together.
  • \code
  • opt1->exclusive = “+1”;
  • opt2->exclusive = “+1”;
  • opt3->exclusive = “”;
  • \endcode
    1. opt1 or opt2 must be used. Both can be used together. Naming ignored.
  • \code
  • opt1->exclusive = “*ignored”;
  • opt2->exclusive = “*”;
  • opt3->exclusive = “”;
  • \endcode
    1. (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
    1. 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;:wink: {
  • 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-commit@lists.osgeo.org
http://lists.osgeo.org/mailman/listinfo/grass-commit

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

Reverted.

This issue warrants an actual discussion. A discussion which I've been
trying to have for *years*.

--
Glynn Clements <glynn@gclements.plus.com>

2014-06-06 9:52 GMT+02:00 Glynn Clements <glynn@gclements.plus.com>:

Reverted.

This issue warrants an actual discussion. A discussion which I've been
trying to have for *years*.

cool Glynn! Please could you provide your solution at least or you
will just continue reverting other people work in the better case or
breaking the whole software in the worse case. Your behaviour is
starting to be absolutely *unaccepteble* for me!!! You behave like a
king with absolute power, funny...

Martin

--
Martin Landa * http://geo.fsv.cvut.cz/gwiki/Landa

2014-06-06 12:52 GMT+02:00 Martin Landa <landa.martin@gmail.com>:

2014-06-06 9:52 GMT+02:00 Glynn Clements <glynn@gclements.plus.com>:

This issue warrants an actual discussion. A discussion which I've been
trying to have for *years*.

I remember some mails at ML some years ago, but without any *real*
outcome (wiki page, see for RFC's from GDAL project). Simply *nothing*
happen during the last years. And BTW, you are almost never discussing
your implementations on ML, you just commit it without no discussion.
Same with your reverts. No discussion. So from this POV your
"explanation" for the revert is somehow weak.

Martin

--
Martin Landa * http://geo.fsv.cvut.cz/gwiki/Landa

Anna,

I think your code looks like:

opt1->exclusive = “+1,*”;
opt2->exclusive = “+1”;
opt3->exclusive = “+2,”;
opt4->exclusive = “+2”;

  1. opt1->required has to be YES if you have only one *.

  2. the message “or input1” has to be fixed to “input1”

  3. but, if you use input1 only, you’ll get a message “must be used together: input1 and input2”

  4. if you add * to opt2, the message will say “input1 or input2”, not “input1 and input2”

So even if you get the “or input1” message, you still have to use input1 and input2. It’s the message that can be misleading (?). One solution would be to find all tied options and print them together in the message above in step 2.

Anyway, you don’t have to worry about it anymore because this code has been reverted by Glynn. Losing hours of work is not fun.

Thanks.
Huidae

···

On Thu, Jun 5, 2014 at 11:03 PM, Anna Petrášová <kratochanna@gmail.com> wrote:

Hi,


grass-dev mailing list
grass-dev@lists.osgeo.org
http://lists.osgeo.org/mailman/listinfo/grass-dev

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_grass@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”;

  1. opt1 & opt2 must be used together.

opt1->exclusive = “+1”;
opt2->exclusive = “+1”;
opt3->exclusive = “”;

  1. opt1 or opt2 must be used. Both can be used together. Naming ignored.

opt1->exclusive = “ignored";
opt2->exclusive = "
”;
opt3->exclusive = “”;

  1. (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=

  1. 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
    1. opt1 & opt2 must be used together.
  • \code
  • opt1->exclusive = “+1”;
  • opt2->exclusive = “+1”;
  • opt3->exclusive = “”;
  • \endcode
    1. opt1 or opt2 must be used. Both can be used together. Naming ignored.
  • \code
  • opt1->exclusive = “*ignored”;
  • opt2->exclusive = “*”;
  • opt3->exclusive = “”;
  • \endcode
    1. (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
    1. 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;:wink: {
  • 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-commit@lists.osgeo.org
http://lists.osgeo.org/mailman/listinfo/grass-commit

All,

I can feel strong frictions between Glynn and some of us. I was shocked when Glynn simply broke 7.1 on Windows. From my experience, I can see that he is more of a purist and doesn’t like workarounds and hacks. I totally understand his points, but just because there is an issue doesn’t make it any more acceptable to break other people’s solutions that have been proved to work for a long time, without providing his own better solution. I don’t mind any existing code being replaced by more correct solutions, but simply reverting it and breaking the whole system is not acceptable at all. I think that the whole system should always be in a working condition no matter what “magic” was used in the code. If that magic is hackery, dirty or whatever, it has to be fixed, not just removed. If he submitted a part of his solution in the middle, it’s not acceptable again because he should have submitted a whole solution to make sure the system is at least not worse than before.

Now, regarding my “exclusive” hack or implementations without any discussion, first, I apologize for not discussing this issue before submitting my implementations. Putting aside who’s right and who’s wrong, it’s very frustrating and demotivating to see hours of effort is gone in seconds of typing with no better solutions coming in. As Martin said, I saw a lot of core implementations from Glynn without clear discussions and he often insists that he’s right and he even said that he would revert any changes he doesn’t like. Looks like, any core changes have to be approved by Glynn after serious discussions with him? He may be one of the best developers in the team, but does it give him “exclusive” rights to revert or break things with no solutions? I don’t know.

Maybe, I was just simply wrong because I didn’t have any discussion before submitting the “exclusive” implementations and don’t have rights to complain about the revert. Now, I’m not sure what to discuss and what not to. I even posted a couple of messages calling for a discussion, but they got no attention at all. This kind of experience just demotivates and pushes me away from real implementations and keeps me fixing small bugs and typos here and there.

Last, I have a strong feeling that we really need defined procedures that we can follow when making changes to the core and even individual modules. Otherwise, this same situation will arise again and again.

Best,
Huidae

Hi,
just to add my 2 cent:

2014-06-06 16:35 GMT+02:00 Huidae Cho <grass4u@gmail.com>:

All,

I can feel strong frictions between Glynn and some of us. I was shocked when
Glynn simply broke 7.1 on Windows. From my experience, I can see that he is
more of a purist and doesn't like workarounds and hacks. I totally

IMHO that has nothing to do with purism or with liking or not liking
workarounds and hacks. It is a decision made on common sense to keep
complex code bases maintainable.

understand his points, but just because there is an issue doesn't make it
any more acceptable to break other people's solutions that have been proved
to work for a long time, *without* providing his own better solution. I
don't mind any existing code being replaced by more correct solutions, but
simply reverting it and breaking the whole system is not acceptable at all.

Actually, hacks and workarounds shouldn't be committed in the first
place if the problem isn't fully understand.
Usually the workaround/hack will be accepted as a working solution (if
it works for several people) and nobody will try again to implement a
reasonable solution that actually fix the problem. The danger is that
such an approach leads to new errors that are even more difficult to
fix and nobody is aware anymore what the root of the problem was.
So the pragmatic solution is: "Don't allow workarounds/hacks even if
it breaks the software to force developers to implement reasonable
solutions".

I think that the whole system *should* always be in a working condition no
matter what "magic" was used in the code. If that magic is hackery, dirty or
whatever, it has to be *fixed*, not just removed. If he submitted a part of
his solution in the middle, it's not acceptable again because he should have
submitted a whole solution to make sure the system is at least not worse
than before.
Now, regarding my "exclusive" hack or implementations without any
discussion, first, I apologize for not discussing this issue before
submitting my implementations. Putting aside who's right and who's wrong,
it's very frustrating and demotivating to see hours of effort is gone in
seconds of typing with no better solutions coming in. As Martin said, I saw
a lot of core implementations from Glynn without clear discussions and he
often insists that he's right and he even said that he would revert any
changes he doesn't like. Looks like, any core changes have to be approved by
Glynn after serious discussions with him? He may be one of the best
developers in the team, but does it give him "exclusive" rights to revert or
break things with no solutions? I don't know.

Why should Glynn provide always a solution when he tries to keep the
GRASS code in libgis and libraster in a maintainable state by
reverting bad solutions? You can see it as a hint to provide a better
solution.
It is sometimes quite undiplomatic to revert changes by simply stating
"this doesn't solve the problem". But the correct approach to apply
changes would be to commit a change request in trac wiki so that the
changes/patches/code can be discussed in the first place. I hope that
all of us will consider this approach. However, since i am the lead
developer of the temporal framework i will not discuss all changes
that i will make in the core of the framework. Simply because i am
(hopefully) aware of what i am doing and i am the conceptual mind
behind it, feeling fully responsible for it. I will not do this with
any other part of GRASS (except temporal modules and maybe the gpde
and voxel libraries, since i feel responsibility for them as well). I
think the same is true for Glynn and many other developers.

I guess that Martin Landa and Markus Metz will revert any
hack/workaround that i will implement in the vector library without
discussion if they see that my code is a bad solution (at least i hope
they will do it). Well i can say in this case "If you don't agree to
my solution then provide a better one" but here i miss the point that
i actually don't understand what i was doing wrong.

Maybe, I was just simply wrong because I didn't have any discussion before
submitting the "exclusive" implementations and don't have rights to complain
about the revert. Now, I'm not sure what to discuss and what not to. I even
posted a couple of messages calling for a discussion, but they got no
attention at all. This kind of experience just demotivates and pushes me
away from real implementations and keeps me fixing small bugs and typos here
and there.

I think this is the usual friction in open source software
development. Please don't feel demotivated, working with people
together from different parts of the world with different cultural
backgrounds is always challenging. It is sometimes hard to fully
understand the motivation/opinion of each other.
But it think the GRASS developers are doing very well, i didn't saw
here "real" flame wars that are common in other open source projects
(Linux Kernel ...). Everyone is in my opinion (opinion with strong
German cultural background) really polite and thoughtful. :slight_smile:

Last, I have a strong feeling that we really need defined procedures that we
can follow when making changes to the core and even individual modules.
Otherwise, this same situation will arise again and again.

I fully agree.
Making change requests including code patches in the trac wiki for
parts of GRASS that are not directly maintained by the requester is
maybe a reasonable approach?

Best regards
Soeren

Best,
Huidae

_______________________________________________
grass-dev mailing list
grass-dev@lists.osgeo.org
http://lists.osgeo.org/mailman/listinfo/grass-dev

Martin Landa wrote:

2014-06-06 9:52 GMT+02:00 Glynn Clements <glynn@gclements.plus.com>:
> Reverted.
>
> This issue warrants an actual discussion. A discussion which I've been
> trying to have for *years*.

cool Glynn! Please could you provide your solution at least

I did propose a solution (read the thread).

Although that appears to have been a waste of time, as the solution
had already been decided by that point.

or you
will just continue reverting other people work in the better case or
breaking the whole software in the worse case. Your behaviour is
starting to be absolutely *unaccepteble* for me!!! You behave like a
king with absolute power, funny...

Whereas imposing a "solution" without waiting for even a single
response isn't?

Why is making one change (the revert) without consultation being
dictatorial but making the original change without consultation not
so?

A change is a change, whether it's a revert or some other change.

Except ... there is a difference betwen a revert and some other
change. The situation created by a revert at least has an "implied
consensus" arising from the fact that the state which is being imposed
existed for years without anyone being particularly bothered (or at
least, not bothered *enough* to participate in any discussion).

If implementation was the issue, this would have been done years ago.
But, as always, implementation isn't the issue. Design is the issue.
Implementation is easy if you know *what* you're supposed to be
implementing.

The reason this wasn't done years ago was because I was hoping to get
some kind of discussion going ("triumph of hope over experience", I
suppose). This isn't an implementation detail; this will affect anyone
who wants to write a module.

Apparently, I should have just gone and imposed something without any
discussion.

--
Glynn Clements <glynn@gclements.plus.com>

Martin Landa wrote:

>> This issue warrants an actual discussion. A discussion which I've been
>> trying to have for *years*.

I remember some mails at ML some years ago, but without any *real*
outcome (wiki page, see for RFC's from GDAL project). Simply *nothing*
happen during the last years. And BTW, you are almost never discussing
your implementations on ML, you just commit it without no discussion.

Implementation doesn't require discussion. Either it works or it
doesn't; no-one cares how.

Interface requires discussion. The design will affect anyone wanting
to write a module. It will affect whoever deals with the GUI side of
it (which isn't me).

--
Glynn Clements <glynn@gclements.plus.com>

Huidae Cho wrote:

I can feel strong frictions between Glynn and some of us. I was shocked
when Glynn simply broke 7.1 on Windows. From my experience, I can see that
he is more of a purist and doesn't like workarounds and hacks. I totally
understand his points, but just because there is an issue doesn't make it
any more acceptable to break other people's solutions that have been proved
to work for a long time, *without* providing his own better solution. I
don't mind any existing code being replaced by more correct solutions, but
simply reverting it and breaking the whole system is not acceptable at all.
I think that the whole system *should* always be in a working condition no
matter what "magic" was used in the code. If that magic is hackery, dirty
or whatever, it has to be *fixed*, not just removed. If he submitted a part
of his solution in the middle, it's not acceptable again because he should
have submitted a whole solution to make sure the system is at least not
worse than before.

Sometimes that's not possible. Sometimes the job cannot be done by any
one person. So if no-one is allowed to start anything they can't
finish single-handedly, such issues will never be solved.

Now, regarding my "exclusive" hack or implementations without any
discussion, first, I apologize for not discussing this issue before
submitting my implementations. Putting aside who's right and who's wrong,
it's very frustrating and demotivating to see hours of effort is gone in
seconds of typing with no better solutions coming in.

I outlined a possible solution as soon as I read your message. What
are your objections to it?

[I'm being facetious. Partly. I'm aware that the change had already
been done before my message was posted. But the point about appearing
to start a discussion then not waiting for a reply (or even following
up in the thread to state that somehting had been done) is serious.]

As Martin said, I saw
a lot of core implementations from Glynn without clear discussions and he
often insists that he's right and he even said that he would revert any
changes he doesn't like. Looks like, any core changes have to be approved
by Glynn after serious discussions with him? He may be one of the best
developers in the team, but does it give him "exclusive" rights to revert
or break things with no solutions? I don't know.

It's hard to address your comments without seeing any specifics. But
AFAICT, there are two issues here.

1. I've made major structural changes (e.g. display re-write) which
weren't discussed in detail (because that would have taken decades and
I'm not assuming that I'll live to 100). But the broad outline was
discussed long before any changes started.

2. When I've said "if you do X, I'll revert it", it's for a reason,
and I'm always willing to discuss the issue. Usually, such a position
is a desperate response to someone a) stating (or, more likely,
implying) that they're going to make an ill-advised change and b)
trying to avoid discussing the merits of it (in fact, the change or
threat of change is often seems like an attempt to forestall
discussion).

It may come across as being dictatorial, but that's more psychology
than fact. Trying to evade discussion by imposing something as a
/fait accompli/ is actually rather more dictatorial. By coming
straight out and saying "I'll revert that", I'm just telling them not
to expect the "screw discussion, make facts on the ground" approach to
work. Even if it means looking like the bad guy.

Note: I don't consider the change being discussed here to be an
instance of that. I think that it was just an unfortunate coincidence
of Huidae being impatient and me not having the time to read my list
mail that day.

This has been up for discussion for years, and a few more days
wouldn't have hurt.

Maybe, I was just simply wrong because I didn't have any discussion before
submitting the "exclusive" implementations and don't have rights to
complain about the revert. Now, I'm not sure what to discuss and what not
to. I even posted a couple of messages calling for a discussion, but they
got no attention at all. This kind of experience just demotivates and
pushes me away from real implementations and keeps me fixing small bugs and
typos here and there.

Last, I have a strong feeling that we really need defined procedures that
we can follow when making changes to the core and even individual modules.
Otherwise, this same situation will arise again and again.

We don't need defined procedures (if we do need them, we're screwed,
because they aren't likely to happen). But if there's a realistic
prospect that other people might have different ideas, at least try to
get it discussed first (and don't assume that people are scanning the
list 24/7; we live in different time zones, and some of us might
occassionally miss a whole day).

That might sound hypocritical in light of ... recent events regarding
the scripts-on-Windows issue. But that issue had actually been
discussed ad nauseum, and with precious little to show for it.

Automatically setting shell=True on Windows was my mistake (I wasn't
aware that it would affect argument parsing), and I fixed it.

Unfortunately, as is often the case with such partial "solutions", the
result is that we believed we were further ahead than we really were.
Execution of scripts never actually worked on Windows, it just
happened to be close enough to fool the casual observer, myself
included.

Which is a large part of the reason I'm so cautious (paranoid, some
might say) about such issues.

--
Glynn Clements <glynn@gclements.plus.com>