26
26
27
27
static const char * num_word (Cash value );
28
28
29
- /* when we go to 64 bit values we will have to modify this */
30
- #define CASH_BUFSZ 24
31
-
32
- #define TERMINATOR (CASH_BUFSZ - 1)
33
- #define LAST_PAREN (TERMINATOR - 1)
34
- #define LAST_DIGIT (LAST_PAREN - 1)
35
-
36
29
37
30
/*
38
31
* Cash is a pass-by-ref SQL type, so we must pass and return pointers.
@@ -71,14 +64,14 @@ cash_in(PG_FUNCTION_ARGS)
71
64
Cash value = 0 ;
72
65
Cash dec = 0 ;
73
66
Cash sgn = 1 ;
74
- int seen_dot = 0 ;
67
+ bool seen_dot = false ;
75
68
const char * s = str ;
76
69
int fpoint ;
77
- char * csymbol ;
78
- char dsymbol ,
79
- ssymbol ,
80
- psymbol ,
81
- * nsymbol ;
70
+ char dsymbol ;
71
+ const char * ssymbol ,
72
+ * psymbol ,
73
+ * nsymbol ,
74
+ * csymbol ;
82
75
83
76
struct lconv * lconvert = PGLC_localeconv ();
84
77
@@ -96,14 +89,22 @@ cash_in(PG_FUNCTION_ARGS)
96
89
if (fpoint < 0 || fpoint > 10 )
97
90
fpoint = 2 ; /* best guess in this case, I think */
98
91
99
- dsymbol = ((* lconvert -> mon_decimal_point != '\0' ) ? * lconvert -> mon_decimal_point : '.' );
100
- ssymbol = ((* lconvert -> mon_thousands_sep != '\0' ) ? * lconvert -> mon_thousands_sep : ',' );
101
- csymbol = ((* lconvert -> currency_symbol != '\0' ) ? lconvert -> currency_symbol : "$" );
102
- psymbol = ((* lconvert -> positive_sign != '\0' ) ? * lconvert -> positive_sign : '+' );
103
- nsymbol = ((* lconvert -> negative_sign != '\0' ) ? lconvert -> negative_sign : "-" );
92
+ /* we restrict dsymbol to be a single byte, but not the other symbols */
93
+ if (* lconvert -> mon_decimal_point != '\0' &&
94
+ lconvert -> mon_decimal_point [1 ] == '\0' )
95
+ dsymbol = * lconvert -> mon_decimal_point ;
96
+ else
97
+ dsymbol = '.' ;
98
+ if (* lconvert -> mon_thousands_sep != '\0' )
99
+ ssymbol = lconvert -> mon_thousands_sep ;
100
+ else /* ssymbol should not equal dsymbol */
101
+ ssymbol = (dsymbol != ',' ) ? "," : "." ;
102
+ csymbol = (* lconvert -> currency_symbol != '\0' ) ? lconvert -> currency_symbol : "$" ;
103
+ psymbol = (* lconvert -> positive_sign != '\0' ) ? lconvert -> positive_sign : "+" ;
104
+ nsymbol = (* lconvert -> negative_sign != '\0' ) ? lconvert -> negative_sign : "-" ;
104
105
105
106
#ifdef CASHDEBUG
106
- printf ("cashin- precision '%d'; decimal '%c'; thousands '%c '; currency '%s'; positive '%c '; negative '%s'\n" ,
107
+ printf ("cashin- precision '%d'; decimal '%c'; thousands '%s '; currency '%s'; positive '%s '; negative '%s'\n" ,
107
108
fpoint , dsymbol , ssymbol , csymbol , psymbol , nsymbol );
108
109
#endif
109
110
@@ -124,23 +125,20 @@ cash_in(PG_FUNCTION_ARGS)
124
125
{
125
126
sgn = -1 ;
126
127
s += strlen (nsymbol );
127
- #ifdef CASHDEBUG
128
- printf ("cashin- negative symbol; string is '%s'\n" , s );
129
- #endif
130
128
}
131
129
else if (* s == '(' )
132
130
{
133
131
sgn = -1 ;
134
132
s ++ ;
135
-
136
133
}
137
- else if (* s == psymbol )
138
- s ++ ;
134
+ else if (strncmp ( s , psymbol , strlen ( psymbol )) == 0 )
135
+ s += strlen ( psymbol ) ;
139
136
140
137
#ifdef CASHDEBUG
141
138
printf ("cashin- string is '%s'\n" , s );
142
139
#endif
143
140
141
+ /* allow whitespace and currency symbol after the sign, too */
144
142
while (isspace ((unsigned char ) * s ))
145
143
s ++ ;
146
144
if (strncmp (s , csymbol , strlen (csymbol )) == 0 )
@@ -150,7 +148,7 @@ cash_in(PG_FUNCTION_ARGS)
150
148
printf ("cashin- string is '%s'\n" , s );
151
149
#endif
152
150
153
- for (;; s ++ )
151
+ for (; * s ; s ++ )
154
152
{
155
153
/* we look for digits as long as we have found less */
156
154
/* than the required number of decimal places */
@@ -164,30 +162,44 @@ cash_in(PG_FUNCTION_ARGS)
164
162
/* decimal point? then start counting fractions... */
165
163
else if (* s == dsymbol && !seen_dot )
166
164
{
167
- seen_dot = 1 ;
165
+ seen_dot = true ;
168
166
}
169
167
/* ignore if "thousands" separator, else we're done */
170
- else if (* s != ssymbol )
171
- {
172
- /* round off */
173
- if (isdigit ((unsigned char ) * s ) && * s >= '5' )
174
- value ++ ;
175
-
176
- /* adjust for less than required decimal places */
177
- for (; dec < fpoint ; dec ++ )
178
- value *= 10 ;
179
-
168
+ else if (strncmp (s , ssymbol , strlen (ssymbol )) == 0 )
169
+ s += strlen (ssymbol ) - 1 ;
170
+ else
180
171
break ;
181
- }
182
172
}
183
173
184
- while (isspace ((unsigned char ) * s ) || * s == '0' || * s == ')' )
185
- s ++ ;
174
+ /* round off if there's another digit */
175
+ if (isdigit ((unsigned char ) * s ) && * s >= '5' )
176
+ value ++ ;
186
177
187
- if (* s != '\0' )
188
- ereport (ERROR ,
189
- (errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
190
- errmsg ("invalid input syntax for type money: \"%s\"" , str )));
178
+ /* adjust for less than required decimal places */
179
+ for (; dec < fpoint ; dec ++ )
180
+ value *= 10 ;
181
+
182
+ /*
183
+ * should only be trailing digits followed by whitespace, right paren,
184
+ * or possibly a trailing minus sign
185
+ */
186
+ while (isdigit ((unsigned char ) * s ))
187
+ s ++ ;
188
+ while (* s )
189
+ {
190
+ if (isspace ((unsigned char ) * s ) || * s == ')' )
191
+ s ++ ;
192
+ else if (strncmp (s , nsymbol , strlen (nsymbol )) == 0 )
193
+ {
194
+ sgn = -1 ;
195
+ s += strlen (nsymbol );
196
+ }
197
+ else
198
+ ereport (ERROR ,
199
+ (errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
200
+ errmsg ("invalid input syntax for type money: \"%s\"" ,
201
+ str )));
202
+ }
191
203
192
204
result = value * sgn ;
193
205
@@ -200,25 +212,23 @@ cash_in(PG_FUNCTION_ARGS)
200
212
201
213
202
214
/* cash_out()
203
- * Function to convert cash to a dollars and cents representation.
204
- * XXX HACK This code appears to assume US conventions for
205
- * positive-valued amounts. - tgl 97/04/14
215
+ * Function to convert cash to a dollars and cents representation, using
216
+ * the lc_monetary locale's formatting.
206
217
*/
207
218
Datum
208
219
cash_out (PG_FUNCTION_ARGS )
209
220
{
210
221
Cash value = PG_GETARG_CASH (0 );
211
222
char * result ;
212
- char buf [CASH_BUFSZ ];
213
- int minus = 0 ;
214
- int count = LAST_DIGIT ;
215
- int point_pos ;
216
- int comma_position = 0 ;
223
+ char buf [128 ];
224
+ char * bufptr ;
225
+ bool minus = false;
226
+ int digit_pos ;
217
227
int points ,
218
228
mon_group ;
219
- char comma ;
220
- char * csymbol ,
221
- dsymbol ,
229
+ char dsymbol ;
230
+ const char * ssymbol ,
231
+ * csymbol ,
222
232
* nsymbol ;
223
233
char convention ;
224
234
@@ -237,66 +247,79 @@ cash_out(PG_FUNCTION_ARGS)
237
247
if (mon_group <= 0 || mon_group > 6 )
238
248
mon_group = 3 ;
239
249
240
- comma = ((* lconvert -> mon_thousands_sep != '\0' ) ? * lconvert -> mon_thousands_sep : ',' );
241
250
convention = lconvert -> n_sign_posn ;
242
- dsymbol = ((* lconvert -> mon_decimal_point != '\0' ) ? * lconvert -> mon_decimal_point : '.' );
243
- csymbol = ((* lconvert -> currency_symbol != '\0' ) ? lconvert -> currency_symbol : "$" );
244
- nsymbol = ((* lconvert -> negative_sign != '\0' ) ? lconvert -> negative_sign : "-" );
245
-
246
- point_pos = LAST_DIGIT - points ;
247
251
248
- /* allow more than three decimal points and separate them */
249
- if (comma )
250
- {
251
- point_pos -= (points - 1 ) / mon_group ;
252
- comma_position = point_pos % (mon_group + 1 );
253
- }
252
+ /* we restrict dsymbol to be a single byte, but not the other symbols */
253
+ if (* lconvert -> mon_decimal_point != '\0' &&
254
+ lconvert -> mon_decimal_point [1 ] == '\0' )
255
+ dsymbol = * lconvert -> mon_decimal_point ;
256
+ else
257
+ dsymbol = '.' ;
258
+ if (* lconvert -> mon_thousands_sep != '\0' )
259
+ ssymbol = lconvert -> mon_thousands_sep ;
260
+ else /* ssymbol should not equal dsymbol */
261
+ ssymbol = (dsymbol != ',' ) ? "," : "." ;
262
+ csymbol = (* lconvert -> currency_symbol != '\0' ) ? lconvert -> currency_symbol : "$" ;
263
+ nsymbol = (* lconvert -> negative_sign != '\0' ) ? lconvert -> negative_sign : "-" ;
254
264
255
265
/* we work with positive amounts and add the minus sign at the end */
256
266
if (value < 0 )
257
267
{
258
- minus = 1 ;
268
+ minus = true ;
259
269
value = - value ;
260
270
}
261
271
262
- /* allow for trailing negative strings */
263
- MemSet ( buf , ' ' , CASH_BUFSZ ) ;
264
- buf [ TERMINATOR ] = buf [ LAST_PAREN ] = '\0' ;
272
+ /* we build the result string right-to-left in buf[] */
273
+ bufptr = buf + sizeof ( buf ) - 1 ;
274
+ * bufptr = '\0' ;
265
275
266
- while (value || count > (point_pos - 2 ))
276
+ /*
277
+ * Generate digits till there are no non-zero digits left and we emitted
278
+ * at least one to the left of the decimal point. digit_pos is the
279
+ * current digit position, with zero as the digit just left of the decimal
280
+ * point, increasing to the right.
281
+ */
282
+ digit_pos = points ;
283
+ do
267
284
{
268
- if (points && count == point_pos )
269
- buf [count -- ] = dsymbol ;
270
- else if (comma && count % (mon_group + 1 ) == comma_position )
271
- buf [count -- ] = comma ;
285
+ if (points && digit_pos == 0 )
286
+ {
287
+ /* insert decimal point */
288
+ * (-- bufptr ) = dsymbol ;
289
+ }
290
+ else if (digit_pos < points && (digit_pos % mon_group ) == 0 )
291
+ {
292
+ /* insert thousands sep */
293
+ bufptr -= strlen (ssymbol );
294
+ memcpy (bufptr , ssymbol , strlen (ssymbol ));
295
+ }
272
296
273
- buf [ count -- ] = ((unsigned int ) value % 10 ) + '0' ;
297
+ * ( -- bufptr ) = ((unsigned int ) value % 10 ) + '0' ;
274
298
value = ((unsigned int ) value ) / 10 ;
275
- }
276
-
277
- strncpy ((buf + count - strlen (csymbol ) + 1 ), csymbol , strlen (csymbol ));
278
- count -= strlen (csymbol ) - 1 ;
299
+ digit_pos -- ;
300
+ } while (value || digit_pos >= 0 );
279
301
280
- if (buf [LAST_DIGIT ] == ',' )
281
- buf [LAST_DIGIT ] = buf [LAST_PAREN ];
302
+ /* prepend csymbol */
303
+ bufptr -= strlen (csymbol );
304
+ memcpy (bufptr , csymbol , strlen (csymbol ));
282
305
283
306
/* see if we need to signify negative amount */
284
307
if (minus )
285
308
{
286
- result = palloc (CASH_BUFSZ + 2 - count + strlen (nsymbol ));
309
+ result = palloc (strlen ( bufptr ) + strlen (nsymbol ) + 3 );
287
310
288
311
/* Position code of 0 means use parens */
289
312
if (convention == 0 )
290
- sprintf (result , "(%s)" , buf + count );
313
+ sprintf (result , "(%s)" , bufptr );
291
314
else if (convention == 2 )
292
- sprintf (result , "%s%s" , buf + count , nsymbol );
315
+ sprintf (result , "%s%s" , bufptr , nsymbol );
293
316
else
294
- sprintf (result , "%s%s" , nsymbol , buf + count );
317
+ sprintf (result , "%s%s" , nsymbol , bufptr );
295
318
}
296
319
else
297
320
{
298
- result = palloc ( CASH_BUFSZ + 2 - count );
299
- strcpy ( result , buf + count );
321
+ /* just emit what we have */
322
+ result = pstrdup ( bufptr );
300
323
}
301
324
302
325
PG_RETURN_CSTRING (result );
0 commit comments