Skip to content

Commit 75c147e

Browse files
committed
Modify locale code to defend against possibility that it was compiled
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...
1 parent aa21da2 commit 75c147e

File tree

1 file changed

+32
-18
lines changed

1 file changed

+32
-18
lines changed

src/backend/utils/adt/cash.c

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* workings can be found in the book "Software Solutions in C" by
1010
* Dale Schumacher, Academic Press, ISBN: 0-12-632360-7.
1111
*
12-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.45 2000/08/03 16:34:22 tgl Exp $
12+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.46 2000/11/18 03:55:51 tgl Exp $
1313
*/
1414

1515
#include <limits.h>
@@ -91,9 +91,19 @@ cash_in(PG_FUNCTION_ARGS)
9191
if (lconvert == NULL)
9292
lconvert = localeconv();
9393

94-
/* frac_digits in the C locale seems to return CHAR_MAX */
95-
/* best guess is 2 in this case I think */
96-
fpoint = ((lconvert->frac_digits != (char)CHAR_MAX) ? lconvert->frac_digits : 2); /* int_frac_digits? */
94+
/*
95+
* frac_digits will be CHAR_MAX in some locales, notably C. However,
96+
* just testing for == CHAR_MAX is risky, because of compilers like
97+
* gcc that "helpfully" let you alter the platform-standard definition
98+
* of whether char is signed or not. If we are so unfortunate as to
99+
* get compiled with a nonstandard -fsigned-char or -funsigned-char
100+
* switch, then our idea of CHAR_MAX will not agree with libc's.
101+
* The safest course is not to test for CHAR_MAX at all, but to impose
102+
* a range check for plausible frac_digits values.
103+
*/
104+
fpoint = lconvert->frac_digits;
105+
if (fpoint < 0 || fpoint > 10)
106+
fpoint = 2; /* best guess in this case, I think */
97107

98108
dsymbol = ((*lconvert->mon_decimal_point != '\0') ? *lconvert->mon_decimal_point : '.');
99109
ssymbol = ((*lconvert->mon_thousands_sep != '\0') ? *lconvert->mon_thousands_sep : ',');
@@ -225,9 +235,9 @@ cash_out(PG_FUNCTION_ARGS)
225235
int count = LAST_DIGIT;
226236
int point_pos;
227237
int comma_position = 0;
228-
char mon_group,
229-
comma,
230-
points;
238+
int points,
239+
mon_group;
240+
char comma;
231241
char *csymbol,
232242
dsymbol,
233243
*nsymbol;
@@ -237,32 +247,36 @@ cash_out(PG_FUNCTION_ARGS)
237247
if (lconvert == NULL)
238248
lconvert = localeconv();
239249

250+
/* see comments about frac_digits in cash_in() */
251+
points = lconvert->frac_digits;
252+
if (points < 0 || points > 10)
253+
points = 2; /* best guess in this case, I think */
254+
255+
/*
256+
* As with frac_digits, must apply a range check to mon_grouping
257+
* to avoid being fooled by variant CHAR_MAX values.
258+
*/
240259
mon_group = *lconvert->mon_grouping;
260+
if (mon_group <= 0 || mon_group > 6)
261+
mon_group = 3;
262+
241263
comma = ((*lconvert->mon_thousands_sep != '\0') ? *lconvert->mon_thousands_sep : ',');
242-
/* frac_digits in the C locale seems to return CHAR_MAX */
243-
/* best guess is 2 in this case I think */
244-
points = ((lconvert->frac_digits != (char)CHAR_MAX) ? lconvert->frac_digits : 2); /* int_frac_digits? */
245264
convention = lconvert->n_sign_posn;
246265
dsymbol = ((*lconvert->mon_decimal_point != '\0') ? *lconvert->mon_decimal_point : '.');
247266
csymbol = ((*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$");
248267
nsymbol = ((*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-");
249268
#else
269+
points = 2;
250270
mon_group = 3;
251271
comma = ',';
252-
csymbol = "$";
272+
convention = 0;
253273
dsymbol = '.';
274+
csymbol = "$";
254275
nsymbol = "-";
255-
points = 2;
256-
convention = 0;
257276
#endif
258277

259278
point_pos = LAST_DIGIT - points;
260279

261-
/* We're playing a little fast and loose with this. Shoot me. */
262-
/* Not me, that was the other guy. Haven't fixed it yet - thomas */
263-
if (!mon_group || mon_group == (char)CHAR_MAX)
264-
mon_group = 3;
265-
266280
/* allow more than three decimal points and separate them */
267281
if (comma)
268282
{

0 commit comments

Comments
 (0)