Skip to content

Commit 6743a87

Browse files
committed
Support more locale-specific formatting options in cash_out().
The POSIX spec defines locale fields for controlling the ordering of the value, sign, and currency symbol in monetary output, but cash_out only supported a small subset of these options. Fully implement p/n_sign_posn, p/n_cs_precedes, and p/n_sep_by_space per spec. Fix up cash_in so that it will accept all these format variants. Also, make sure that thousands_sep is only inserted to the left of the decimal point, as required by spec. Per bug #6144 from Eduard Kracmar and discussion of bug #6277. This patch includes some ideas from Alexander Lakhin's proposed patch, though it is very different in detail.
1 parent eb5834d commit 6743a87

File tree

1 file changed

+134
-32
lines changed

1 file changed

+134
-32
lines changed

src/backend/utils/adt/cash.c

Lines changed: 134 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ cash_in(PG_FUNCTION_ARGS)
150150
s++;
151151
if (strncmp(s, csymbol, strlen(csymbol)) == 0)
152152
s += strlen(csymbol);
153+
while (isspace((unsigned char) *s))
154+
s++;
153155

154156
#ifdef CASHDEBUG
155157
printf("cashin- string is '%s'\n", s);
@@ -180,6 +182,8 @@ cash_in(PG_FUNCTION_ARGS)
180182
s++;
181183
if (strncmp(s, csymbol, strlen(csymbol)) == 0)
182184
s += strlen(csymbol);
185+
while (isspace((unsigned char) *s))
186+
s++;
183187

184188
#ifdef CASHDEBUG
185189
printf("cashin- string is '%s'\n", s);
@@ -218,10 +222,11 @@ cash_in(PG_FUNCTION_ARGS)
218222

219223
/*
220224
* should only be trailing digits followed by whitespace, right paren,
221-
* or possibly a trailing minus sign
225+
* trailing sign, and/or trailing currency symbol
222226
*/
223227
while (isdigit((unsigned char) *s))
224228
s++;
229+
225230
while (*s)
226231
{
227232
if (isspace((unsigned char) *s) || *s == ')')
@@ -231,6 +236,10 @@ cash_in(PG_FUNCTION_ARGS)
231236
sgn = -1;
232237
s += strlen(nsymbol);
233238
}
239+
else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
240+
s += strlen(psymbol);
241+
else if (strncmp(s, csymbol, strlen(csymbol)) == 0)
242+
s += strlen(csymbol);
234243
else
235244
ereport(ERROR,
236245
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
@@ -259,15 +268,16 @@ cash_out(PG_FUNCTION_ARGS)
259268
char *result;
260269
char buf[128];
261270
char *bufptr;
262-
bool minus = false;
263271
int digit_pos;
264272
int points,
265273
mon_group;
266274
char dsymbol;
267275
const char *ssymbol,
268276
*csymbol,
269-
*nsymbol;
270-
char convention;
277+
*signsymbol;
278+
char sign_posn,
279+
cs_precedes,
280+
sep_by_space;
271281
struct lconv *lconvert = PGLC_localeconv();
272282

273283
/* see comments about frac_digits in cash_in() */
@@ -283,8 +293,6 @@ cash_out(PG_FUNCTION_ARGS)
283293
if (mon_group <= 0 || mon_group > 6)
284294
mon_group = 3;
285295

286-
convention = lconvert->n_sign_posn;
287-
288296
/* we restrict dsymbol to be a single byte, but not the other symbols */
289297
if (*lconvert->mon_decimal_point != '\0' &&
290298
lconvert->mon_decimal_point[1] == '\0')
@@ -296,16 +304,26 @@ cash_out(PG_FUNCTION_ARGS)
296304
else /* ssymbol should not equal dsymbol */
297305
ssymbol = (dsymbol != ',') ? "," : ".";
298306
csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
299-
nsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
300307

301-
/* we work with positive amounts and add the minus sign at the end */
302308
if (value < 0)
303309
{
304-
minus = true;
310+
/* make the amount positive for digit-reconstruction loop */
305311
value = -value;
312+
/* set up formatting data */
313+
signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
314+
sign_posn = lconvert->n_sign_posn;
315+
cs_precedes = lconvert->n_cs_precedes;
316+
sep_by_space = lconvert->n_sep_by_space;
317+
}
318+
else
319+
{
320+
signsymbol = lconvert->positive_sign;
321+
sign_posn = lconvert->p_sign_posn;
322+
cs_precedes = lconvert->p_cs_precedes;
323+
sep_by_space = lconvert->p_sep_by_space;
306324
}
307325

308-
/* we build the result string right-to-left in buf[] */
326+
/* we build the digits+decimal-point+sep string right-to-left in buf[] */
309327
bufptr = buf + sizeof(buf) - 1;
310328
*bufptr = '\0';
311329

@@ -320,12 +338,12 @@ cash_out(PG_FUNCTION_ARGS)
320338
{
321339
if (points && digit_pos == 0)
322340
{
323-
/* insert decimal point */
341+
/* insert decimal point, but not if value cannot be fractional */
324342
*(--bufptr) = dsymbol;
325343
}
326-
else if (digit_pos < points && (digit_pos % mon_group) == 0)
344+
else if (digit_pos < 0 && (digit_pos % mon_group) == 0)
327345
{
328-
/* insert thousands sep */
346+
/* insert thousands sep, but only to left of radix point */
329347
bufptr -= strlen(ssymbol);
330348
memcpy(bufptr, ssymbol, strlen(ssymbol));
331349
}
@@ -335,27 +353,111 @@ cash_out(PG_FUNCTION_ARGS)
335353
digit_pos--;
336354
} while (value || digit_pos >= 0);
337355

338-
/* prepend csymbol */
339-
bufptr -= strlen(csymbol);
340-
memcpy(bufptr, csymbol, strlen(csymbol));
341-
342-
/* see if we need to signify negative amount */
343-
if (minus)
344-
{
345-
result = palloc(strlen(bufptr) + strlen(nsymbol) + 3);
356+
/*----------
357+
* Now, attach currency symbol and sign symbol in the correct order.
358+
*
359+
* The POSIX spec defines these values controlling this code:
360+
*
361+
* p/n_sign_posn:
362+
* 0 Parentheses enclose the quantity and the currency_symbol.
363+
* 1 The sign string precedes the quantity and the currency_symbol.
364+
* 2 The sign string succeeds the quantity and the currency_symbol.
365+
* 3 The sign string precedes the currency_symbol.
366+
* 4 The sign string succeeds the currency_symbol.
367+
*
368+
* p/n_cs_precedes: 0 means currency symbol after value, else before it.
369+
*
370+
* p/n_sep_by_space:
371+
* 0 No <space> separates the currency symbol and value.
372+
* 1 If the currency symbol and sign string are adjacent, a <space>
373+
* separates them from the value; otherwise, a <space> separates
374+
* the currency symbol from the value.
375+
* 2 If the currency symbol and sign string are adjacent, a <space>
376+
* separates them; otherwise, a <space> separates the sign string
377+
* from the value.
378+
*----------
379+
*/
380+
result = palloc(strlen(bufptr) + strlen(csymbol) + strlen(signsymbol) + 4);
346381

347-
/* Position code of 0 means use parens */
348-
if (convention == 0)
349-
sprintf(result, "(%s)", bufptr);
350-
else if (convention == 2)
351-
sprintf(result, "%s%s", bufptr, nsymbol);
352-
else
353-
sprintf(result, "%s%s", nsymbol, bufptr);
354-
}
355-
else
382+
switch (sign_posn)
356383
{
357-
/* just emit what we have */
358-
result = pstrdup(bufptr);
384+
case 0:
385+
if (cs_precedes)
386+
sprintf(result, "(%s%s%s)",
387+
csymbol,
388+
(sep_by_space == 1) ? " " : "",
389+
bufptr);
390+
else
391+
sprintf(result, "(%s%s%s)",
392+
bufptr,
393+
(sep_by_space == 1) ? " " : "",
394+
csymbol);
395+
break;
396+
case 1:
397+
default:
398+
if (cs_precedes)
399+
sprintf(result, "%s%s%s%s%s",
400+
signsymbol,
401+
(sep_by_space == 2) ? " " : "",
402+
csymbol,
403+
(sep_by_space == 1) ? " " : "",
404+
bufptr);
405+
else
406+
sprintf(result, "%s%s%s%s%s",
407+
signsymbol,
408+
(sep_by_space == 2) ? " " : "",
409+
bufptr,
410+
(sep_by_space == 1) ? " " : "",
411+
csymbol);
412+
break;
413+
case 2:
414+
if (cs_precedes)
415+
sprintf(result, "%s%s%s%s%s",
416+
csymbol,
417+
(sep_by_space == 1) ? " " : "",
418+
bufptr,
419+
(sep_by_space == 2) ? " " : "",
420+
signsymbol);
421+
else
422+
sprintf(result, "%s%s%s%s%s",
423+
bufptr,
424+
(sep_by_space == 1) ? " " : "",
425+
csymbol,
426+
(sep_by_space == 2) ? " " : "",
427+
signsymbol);
428+
break;
429+
case 3:
430+
if (cs_precedes)
431+
sprintf(result, "%s%s%s%s%s",
432+
signsymbol,
433+
(sep_by_space == 2) ? " " : "",
434+
csymbol,
435+
(sep_by_space == 1) ? " " : "",
436+
bufptr);
437+
else
438+
sprintf(result, "%s%s%s%s%s",
439+
bufptr,
440+
(sep_by_space == 1) ? " " : "",
441+
signsymbol,
442+
(sep_by_space == 2) ? " " : "",
443+
csymbol);
444+
break;
445+
case 4:
446+
if (cs_precedes)
447+
sprintf(result, "%s%s%s%s%s",
448+
csymbol,
449+
(sep_by_space == 2) ? " " : "",
450+
signsymbol,
451+
(sep_by_space == 1) ? " " : "",
452+
bufptr);
453+
else
454+
sprintf(result, "%s%s%s%s%s",
455+
bufptr,
456+
(sep_by_space == 1) ? " " : "",
457+
csymbol,
458+
(sep_by_space == 2) ? " " : "",
459+
signsymbol);
460+
break;
359461
}
360462

361463
PG_RETURN_CSTRING(result);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy