Modify locale code to defend against possibility that it was compiled
authorTom Lane
Sat, 18 Nov 2000 03:55:51 +0000 (03:55 +0000)
committerTom Lane
Sat, 18 Nov 2000 03:55:51 +0000 (03:55 +0000)
with an -fsigned-char/-funsigned-char setting opposite to that of libc,
thus breaking the convention that 'undefined' values returned by
localeconv() are represented by CHAR_MAX.  It is sheer stupidity that
gcc even has such a switch --- it's just as bad as the structure-packing
control switches offered by the more brain-dead PC compilers --- and
as for the behavior of Linux distribution vendors who set RPM_OPT_FLAGS
differently from the way they built libc, well, words fail me...

src/backend/utils/adt/cash.c

index d924bf3fe09709131dc7aee452f62216e710784e..696559a59ea3019d9c971ede428a1fd3de64ec05 100644 (file)
@@ -9,7 +9,7 @@
  * workings can be found in the book "Software Solutions in C" by
  * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7.
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.45 2000/08/03 16:34:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.46 2000/11/18 03:55:51 tgl Exp $
  */
 
 #include 
@@ -91,9 +91,19 @@ cash_in(PG_FUNCTION_ARGS)
    if (lconvert == NULL)
        lconvert = localeconv();
 
-   /* frac_digits in the C locale seems to return CHAR_MAX */
-   /* best guess is 2 in this case I think */
-   fpoint = ((lconvert->frac_digits != (char)CHAR_MAX) ? lconvert->frac_digits : 2); /* int_frac_digits? */
+   /*
+    * frac_digits will be CHAR_MAX in some locales, notably C.  However,
+    * just testing for == CHAR_MAX is risky, because of compilers like
+    * gcc that "helpfully" let you alter the platform-standard definition
+    * of whether char is signed or not.  If we are so unfortunate as to
+    * get compiled with a nonstandard -fsigned-char or -funsigned-char
+    * switch, then our idea of CHAR_MAX will not agree with libc's.
+    * The safest course is not to test for CHAR_MAX at all, but to impose
+    * a range check for plausible frac_digits values.
+    */
+   fpoint = lconvert->frac_digits;
+   if (fpoint < 0 || fpoint > 10)
+       fpoint = 2;             /* best guess in this case, I think */
 
    dsymbol = ((*lconvert->mon_decimal_point != '\0') ? *lconvert->mon_decimal_point : '.');
    ssymbol = ((*lconvert->mon_thousands_sep != '\0') ? *lconvert->mon_thousands_sep : ',');
@@ -225,9 +235,9 @@ cash_out(PG_FUNCTION_ARGS)
    int         count = LAST_DIGIT;
    int         point_pos;
    int         comma_position = 0;
-   char        mon_group,
-               comma,
-               points;
+   int         points,
+               mon_group;
+   char        comma;
    char       *csymbol,
                dsymbol,
               *nsymbol;
@@ -237,32 +247,36 @@ cash_out(PG_FUNCTION_ARGS)
    if (lconvert == NULL)
        lconvert = localeconv();
 
+   /* see comments about frac_digits in cash_in() */
+   points = lconvert->frac_digits;
+   if (points < 0 || points > 10)
+       points = 2;             /* best guess in this case, I think */
+
+   /*
+    * As with frac_digits, must apply a range check to mon_grouping
+    * to avoid being fooled by variant CHAR_MAX values.
+    */
    mon_group = *lconvert->mon_grouping;
+   if (mon_group <= 0 || mon_group > 6)
+       mon_group = 3;
+
    comma = ((*lconvert->mon_thousands_sep != '\0') ? *lconvert->mon_thousands_sep : ',');
-   /* frac_digits in the C locale seems to return CHAR_MAX */
-   /* best guess is 2 in this case I think */
-   points = ((lconvert->frac_digits != (char)CHAR_MAX) ? lconvert->frac_digits : 2); /* int_frac_digits? */
    convention = lconvert->n_sign_posn;
    dsymbol = ((*lconvert->mon_decimal_point != '\0') ? *lconvert->mon_decimal_point : '.');
    csymbol = ((*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$");
    nsymbol = ((*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-");
 #else
+   points = 2;
    mon_group = 3;
    comma = ',';
-   csymbol = "$";
+   convention = 0;
    dsymbol = '.';
+   csymbol = "$";
    nsymbol = "-";
-   points = 2;
-   convention = 0;
 #endif
 
    point_pos = LAST_DIGIT - points;
 
-   /* We're playing a little fast and loose with this.  Shoot me. */
-   /* Not me, that was the other guy. Haven't fixed it yet - thomas */
-   if (!mon_group || mon_group == (char)CHAR_MAX)
-       mon_group = 3;
-
    /* allow more than three decimal points and separate them */
    if (comma)
    {