28
28
#include "utils/cash.h"
29
29
#include "utils/pg_locale.h"
30
30
31
- #define CASH_BUFSZ 36
32
-
33
- #define TERMINATOR (CASH_BUFSZ - 1)
34
- #define LAST_PAREN (TERMINATOR - 1)
35
- #define LAST_DIGIT (LAST_PAREN - 1)
36
-
37
31
38
32
/*************************************************************************
39
33
* Private routines
@@ -106,13 +100,13 @@ cash_in(PG_FUNCTION_ARGS)
106
100
Cash value = 0 ;
107
101
Cash dec = 0 ;
108
102
Cash sgn = 1 ;
109
- int seen_dot = 0 ;
103
+ bool seen_dot = false ;
110
104
const char * s = str ;
111
105
int fpoint ;
112
- char dsymbol ,
113
- ssymbol ,
114
- psymbol ;
115
- const char * nsymbol ,
106
+ char dsymbol ;
107
+ const char * ssymbol ,
108
+ * psymbol ,
109
+ * nsymbol ,
116
110
* csymbol ;
117
111
118
112
struct lconv * lconvert = PGLC_localeconv ();
@@ -131,18 +125,22 @@ cash_in(PG_FUNCTION_ARGS)
131
125
if (fpoint < 0 || fpoint > 10 )
132
126
fpoint = 2 ; /* best guess in this case, I think */
133
127
134
- dsymbol = ((* lconvert -> mon_decimal_point != '\0' ) ? * lconvert -> mon_decimal_point : '.' );
135
- if (* lconvert -> mon_thousands_sep != '\0' )
136
- ssymbol = * lconvert -> mon_thousands_sep ;
128
+ /* we restrict dsymbol to be a single byte, but not the other symbols */
129
+ if (* lconvert -> mon_decimal_point != '\0' &&
130
+ lconvert -> mon_decimal_point [1 ] == '\0' )
131
+ dsymbol = * lconvert -> mon_decimal_point ;
137
132
else
138
- /* ssymbol should not equal dsymbol */
139
- ssymbol = (dsymbol != ',' ) ? ',' : '.' ;
140
- csymbol = ((* lconvert -> currency_symbol != '\0' ) ? lconvert -> currency_symbol : "$" );
141
- psymbol = ((* lconvert -> positive_sign != '\0' ) ? * lconvert -> positive_sign : '+' );
142
- nsymbol = ((* lconvert -> negative_sign != '\0' ) ? lconvert -> negative_sign : "-" );
133
+ dsymbol = '.' ;
134
+ if (* lconvert -> mon_thousands_sep != '\0' )
135
+ ssymbol = lconvert -> mon_thousands_sep ;
136
+ else /* ssymbol should not equal dsymbol */
137
+ ssymbol = (dsymbol != ',' ) ? "," : "." ;
138
+ csymbol = (* lconvert -> currency_symbol != '\0' ) ? lconvert -> currency_symbol : "$" ;
139
+ psymbol = (* lconvert -> positive_sign != '\0' ) ? lconvert -> positive_sign : "+" ;
140
+ nsymbol = (* lconvert -> negative_sign != '\0' ) ? lconvert -> negative_sign : "-" ;
143
141
144
142
#ifdef CASHDEBUG
145
- printf ("cashin- precision '%d'; decimal '%c'; thousands '%c '; currency '%s'; positive '%c '; negative '%s'\n" ,
143
+ printf ("cashin- precision '%d'; decimal '%c'; thousands '%s '; currency '%s'; positive '%s '; negative '%s'\n" ,
146
144
fpoint , dsymbol , ssymbol , csymbol , psymbol , nsymbol );
147
145
#endif
148
146
@@ -164,22 +162,20 @@ cash_in(PG_FUNCTION_ARGS)
164
162
{
165
163
sgn = -1 ;
166
164
s += strlen (nsymbol );
167
- #ifdef CASHDEBUG
168
- printf ("cashin- negative symbol; string is '%s'\n" , s );
169
- #endif
170
165
}
171
166
else if (* s == '(' )
172
167
{
173
168
sgn = -1 ;
174
169
s ++ ;
175
170
}
176
- else if (* s == psymbol )
177
- s ++ ;
171
+ else if (strncmp ( s , psymbol , strlen ( psymbol )) == 0 )
172
+ s += strlen ( psymbol ) ;
178
173
179
174
#ifdef CASHDEBUG
180
175
printf ("cashin- string is '%s'\n" , s );
181
176
#endif
182
177
178
+ /* allow whitespace and currency symbol after the sign, too */
183
179
while (isspace ((unsigned char ) * s ))
184
180
s ++ ;
185
181
if (strncmp (s , csymbol , strlen (csymbol )) == 0 )
@@ -189,7 +185,7 @@ cash_in(PG_FUNCTION_ARGS)
189
185
printf ("cashin- string is '%s'\n" , s );
190
186
#endif
191
187
192
- for (;; s ++ )
188
+ for (; * s ; s ++ )
193
189
{
194
190
/* we look for digits as long as we have found less */
195
191
/* than the required number of decimal places */
@@ -203,33 +199,44 @@ cash_in(PG_FUNCTION_ARGS)
203
199
/* decimal point? then start counting fractions... */
204
200
else if (* s == dsymbol && !seen_dot )
205
201
{
206
- seen_dot = 1 ;
202
+ seen_dot = true ;
207
203
}
208
204
/* ignore if "thousands" separator, else we're done */
209
- else if (* s != ssymbol )
210
- {
211
- /* round off */
212
- if (isdigit ((unsigned char ) * s ) && * s >= '5' )
213
- value ++ ;
214
-
215
- /* adjust for less than required decimal places */
216
- for (; dec < fpoint ; dec ++ )
217
- value *= 10 ;
218
-
205
+ else if (strncmp (s , ssymbol , strlen (ssymbol )) == 0 )
206
+ s += strlen (ssymbol ) - 1 ;
207
+ else
219
208
break ;
220
- }
221
209
}
222
210
223
- /* should only be trailing digits followed by whitespace or right paren */
211
+ /* round off if there's another digit */
212
+ if (isdigit ((unsigned char ) * s ) && * s >= '5' )
213
+ value ++ ;
214
+
215
+ /* adjust for less than required decimal places */
216
+ for (; dec < fpoint ; dec ++ )
217
+ value *= 10 ;
218
+
219
+ /*
220
+ * should only be trailing digits followed by whitespace, right paren,
221
+ * or possibly a trailing minus sign
222
+ */
224
223
while (isdigit ((unsigned char ) * s ))
225
224
s ++ ;
226
- while (isspace ((unsigned char ) * s ) || * s == ')' )
227
- s ++ ;
228
-
229
- if (* s != '\0' )
230
- ereport (ERROR ,
231
- (errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
232
- errmsg ("invalid input syntax for type money: \"%s\"" , str )));
225
+ while (* s )
226
+ {
227
+ if (isspace ((unsigned char ) * s ) || * s == ')' )
228
+ s ++ ;
229
+ else if (strncmp (s , nsymbol , strlen (nsymbol )) == 0 )
230
+ {
231
+ sgn = -1 ;
232
+ s += strlen (nsymbol );
233
+ }
234
+ else
235
+ ereport (ERROR ,
236
+ (errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
237
+ errmsg ("invalid input syntax for type money: \"%s\"" ,
238
+ str )));
239
+ }
233
240
234
241
result = value * sgn ;
235
242
@@ -242,26 +249,24 @@ cash_in(PG_FUNCTION_ARGS)
242
249
243
250
244
251
/* cash_out()
245
- * Function to convert cash to a dollars and cents representation.
246
- * XXX HACK This code appears to assume US conventions for
247
- * positive-valued amounts. - tgl 97/04/14
252
+ * Function to convert cash to a dollars and cents representation, using
253
+ * the lc_monetary locale's formatting.
248
254
*/
249
255
Datum
250
256
cash_out (PG_FUNCTION_ARGS )
251
257
{
252
258
Cash value = PG_GETARG_CASH (0 );
253
259
char * result ;
254
- char buf [CASH_BUFSZ ];
255
- int minus = 0 ;
256
- int count = LAST_DIGIT ;
257
- int point_pos ;
258
- int ssymbol_position = 0 ;
260
+ char buf [128 ];
261
+ char * bufptr ;
262
+ bool minus = false;
263
+ int digit_pos ;
259
264
int points ,
260
265
mon_group ;
261
- char ssymbol ;
262
- const char * csymbol ,
263
- * nsymbol ;
264
266
char dsymbol ;
267
+ const char * ssymbol ,
268
+ * csymbol ,
269
+ * nsymbol ;
265
270
char convention ;
266
271
267
272
struct lconv * lconvert = PGLC_localeconv ();
@@ -280,69 +285,78 @@ cash_out(PG_FUNCTION_ARGS)
280
285
mon_group = 3 ;
281
286
282
287
convention = lconvert -> n_sign_posn ;
283
- dsymbol = ((* lconvert -> mon_decimal_point != '\0' ) ? * lconvert -> mon_decimal_point : '.' );
284
- if (* lconvert -> mon_thousands_sep != '\0' )
285
- ssymbol = * lconvert -> mon_thousands_sep ;
286
- else
287
- /* ssymbol should not equal dsymbol */
288
- ssymbol = (dsymbol != ',' ) ? ',' : '.' ;
289
- csymbol = ((* lconvert -> currency_symbol != '\0' ) ? lconvert -> currency_symbol : "$" );
290
- nsymbol = ((* lconvert -> negative_sign != '\0' ) ? lconvert -> negative_sign : "-" );
291
-
292
- point_pos = LAST_DIGIT - points ;
293
288
294
- point_pos -= (points - 1 ) / mon_group ;
295
- ssymbol_position = point_pos % (mon_group + 1 );
289
+ /* we restrict dsymbol to be a single byte, but not the other symbols */
290
+ if (* lconvert -> mon_decimal_point != '\0' &&
291
+ lconvert -> mon_decimal_point [1 ] == '\0' )
292
+ dsymbol = * lconvert -> mon_decimal_point ;
293
+ else
294
+ dsymbol = '.' ;
295
+ if (* lconvert -> mon_thousands_sep != '\0' )
296
+ ssymbol = lconvert -> mon_thousands_sep ;
297
+ else /* ssymbol should not equal dsymbol */
298
+ ssymbol = (dsymbol != ',' ) ? "," : "." ;
299
+ csymbol = (* lconvert -> currency_symbol != '\0' ) ? lconvert -> currency_symbol : "$" ;
300
+ nsymbol = (* lconvert -> negative_sign != '\0' ) ? lconvert -> negative_sign : "-" ;
296
301
297
302
/* we work with positive amounts and add the minus sign at the end */
298
303
if (value < 0 )
299
304
{
300
- minus = 1 ;
305
+ minus = true ;
301
306
value = - value ;
302
307
}
303
308
304
- /* allow for trailing negative strings */
305
- MemSet ( buf , ' ' , CASH_BUFSZ ) ;
306
- buf [ TERMINATOR ] = buf [ LAST_PAREN ] = '\0' ;
309
+ /* we build the result string right-to-left in buf[] */
310
+ bufptr = buf + sizeof ( buf ) - 1 ;
311
+ * bufptr = '\0' ;
307
312
308
- while (value || count > (point_pos - 2 ))
313
+ /*
314
+ * Generate digits till there are no non-zero digits left and we emitted
315
+ * at least one to the left of the decimal point. digit_pos is the
316
+ * current digit position, with zero as the digit just left of the decimal
317
+ * point, increasing to the right.
318
+ */
319
+ digit_pos = points ;
320
+ do
309
321
{
310
- if (points && count == point_pos )
311
- buf [count -- ] = dsymbol ;
312
- else if (ssymbol && count % (mon_group + 1 ) == ssymbol_position )
313
- buf [count -- ] = ssymbol ;
322
+ if (points && digit_pos == 0 )
323
+ {
324
+ /* insert decimal point */
325
+ * (-- bufptr ) = dsymbol ;
326
+ }
327
+ else if (digit_pos < points && (digit_pos % mon_group ) == 0 )
328
+ {
329
+ /* insert thousands sep */
330
+ bufptr -= strlen (ssymbol );
331
+ memcpy (bufptr , ssymbol , strlen (ssymbol ));
332
+ }
314
333
315
- buf [ count -- ] = ((uint64 ) value % 10 ) + '0' ;
334
+ * ( -- bufptr ) = ((uint64 ) value % 10 ) + '0' ;
316
335
value = ((uint64 ) value ) / 10 ;
317
- }
318
-
319
- strncpy ((buf + count - strlen (csymbol ) + 1 ), csymbol , strlen (csymbol ));
320
- count -= strlen (csymbol ) - 1 ;
336
+ digit_pos -- ;
337
+ } while (value || digit_pos >= 0 );
321
338
322
- /*
323
- * If points == 0 and the number of digits % mon_group == 0, the code
324
- * above adds a trailing ssymbol on the far right, so remove it.
325
- */
326
- if (buf [LAST_DIGIT ] == ssymbol )
327
- buf [LAST_DIGIT ] = '\0' ;
339
+ /* prepend csymbol */
340
+ bufptr -= strlen (csymbol );
341
+ memcpy (bufptr , csymbol , strlen (csymbol ));
328
342
329
343
/* see if we need to signify negative amount */
330
344
if (minus )
331
345
{
332
- result = palloc (CASH_BUFSZ + 2 - count + strlen (nsymbol ));
346
+ result = palloc (strlen ( bufptr ) + strlen (nsymbol ) + 3 );
333
347
334
348
/* Position code of 0 means use parens */
335
349
if (convention == 0 )
336
- sprintf (result , "(%s)" , buf + count );
350
+ sprintf (result , "(%s)" , bufptr );
337
351
else if (convention == 2 )
338
- sprintf (result , "%s%s" , buf + count , nsymbol );
352
+ sprintf (result , "%s%s" , bufptr , nsymbol );
339
353
else
340
- sprintf (result , "%s%s" , nsymbol , buf + count );
354
+ sprintf (result , "%s%s" , nsymbol , bufptr );
341
355
}
342
356
else
343
357
{
344
- result = palloc ( CASH_BUFSZ + 2 - count );
345
- strcpy ( result , buf + count );
358
+ /* just emit what we have */
359
+ result = pstrdup ( bufptr );
346
360
}
347
361
348
362
PG_RETURN_CSTRING (result );
0 commit comments