Skip to content

Commit 4999f00

Browse files
committed
Add in D'Arcy's cash code
pg_proc.h still needs modifying, but this gets it in there so that we can get around any compiler bugs. Will try and get the pg_proc.h entries done up later tonight...
1 parent 1c688d1 commit 4999f00

File tree

3 files changed

+327
-2
lines changed

3 files changed

+327
-2
lines changed

src/backend/utils/adt/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Makefile for utils/adt
55
#
66
# IDENTIFICATION
7-
# $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.7 1997/03/14 23:19:50 scrappy Exp $
7+
# $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.8 1997/04/09 08:36:04 scrappy Exp $
88
#
99
#-------------------------------------------------------------------------
1010

@@ -17,7 +17,7 @@ INCLUDE_OPT = -I../.. \
1717

1818
CFLAGS+=$(INCLUDE_OPT)
1919

20-
OBJS = acl.o arrayfuncs.o arrayutils.o bool.o char.o chunk.o date.o \
20+
OBJS = acl.o arrayfuncs.o arrayutils.o bool.o cash.o char.o chunk.o date.o \
2121
datum.o dt.o filename.o float.o geo_ops.o geo_selfuncs.o int.o \
2222
misc.o nabstime.o name.o not_in.o numutils.o oid.o \
2323
oidname.o oidint2.o oidint4.o oracle_compat.o regexp.o regproc.o \

src/backend/utils/adt/cash.c

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
/*
2+
cash.c
3+
Written by D'Arcy J.M. Cain
4+
5+
Functions to allow input and output of money normally but store
6+
and handle it as longs
7+
8+
Set tabstops to 4 for best results
9+
10+
A slightly modified version of this file and a discussion of the
11+
workings can be found in the book "Software Solutions in C" by
12+
Dale Schumacher, Academic Press, ISBN: 0-12-632360-7.
13+
14+
*/
15+
16+
#include <stdio.h>
17+
#include <string.h>
18+
#include <limits.h>
19+
#include <ctype.h>
20+
#include <locale.h>
21+
22+
#ifdef TEST_MAIN
23+
# include <stdlib.h>
24+
# define palloc malloc
25+
#else
26+
# include <palloc.h>
27+
#endif
28+
29+
#include "cash.h"
30+
31+
/* when we go to 64 bit values we will have to modify this */
32+
#define CASH_BUFSZ 24
33+
34+
#define TERMINATOR (CASH_BUFSZ - 1)
35+
#define LAST_PAREN (TERMINATOR - 1)
36+
#define LAST_DIGIT (LAST_PAREN - 1)
37+
38+
/* function to convert a long to a dollars and cents representation */
39+
const char *
40+
cash_out(long value)
41+
{
42+
char *retbuf, buf[CASH_BUFSZ];
43+
struct lconv *lc = localeconv();
44+
int mod_group = *lc->mon_grouping;
45+
int comma = *lc->mon_thousands_sep;
46+
int points = lc->frac_digits; /* int_frac_digits? */
47+
int minus = 0;
48+
int count = LAST_DIGIT;
49+
int point_pos;
50+
int comma_position = 0;
51+
52+
/* frac_digits in the C locale seems to return CHAR_MAX */
53+
/* best guess is 2 in this case I think */
54+
if (points == CHAR_MAX)
55+
points = 2;
56+
57+
point_pos = LAST_DIGIT - points;
58+
59+
/* We're playing a little fast and loose with this. Shoot me. */
60+
if (!mod_group || mod_group == CHAR_MAX)
61+
mod_group = 3;
62+
63+
/* allow more than three decimal points and separate them */
64+
if (comma)
65+
{
66+
point_pos -= (points - 1)/mod_group;
67+
comma_position = point_pos % (mod_group + 1);
68+
}
69+
70+
/* we work with positive amounts and add the minus sign at the end */
71+
if (value < 0)
72+
{
73+
minus = 1;
74+
value *= -1;
75+
}
76+
77+
/* allow for trailing negative strings */
78+
memset(buf, ' ', CASH_BUFSZ);
79+
buf[TERMINATOR] = buf[LAST_PAREN] = 0;
80+
81+
while (value || count > (point_pos - 2))
82+
{
83+
if (points && count == point_pos)
84+
buf[count--] = *lc->decimal_point;
85+
else if (comma && count % (mod_group + 1) == comma_position)
86+
buf[count--] = comma;
87+
88+
buf[count--] = (value % 10) + '0';
89+
value /= 10;
90+
}
91+
92+
if (buf[LAST_DIGIT] == ',')
93+
buf[LAST_DIGIT] = buf[LAST_PAREN];
94+
95+
/* see if we need to signify negative amount */
96+
if (minus)
97+
{
98+
retbuf = palloc(CASH_BUFSZ + 2 - count + strlen(lc->negative_sign));
99+
100+
/* Position code of 0 means use parens */
101+
if (!lc->n_sign_posn)
102+
sprintf(retbuf, "(%s)", buf + count);
103+
else if (lc->n_sign_posn == 2)
104+
sprintf(retbuf, "%s%s", buf + count, lc->negative_sign);
105+
else
106+
sprintf(retbuf, "%s%s", lc->negative_sign, buf + count);
107+
}
108+
else
109+
{
110+
retbuf = palloc(CASH_BUFSZ + 2 - count);
111+
strcpy(retbuf, buf + count);
112+
}
113+
114+
return retbuf;
115+
}
116+
117+
/* convert a string to a long integer */
118+
long
119+
cash_in(const char *s)
120+
{
121+
long value = 0;
122+
long dec = 0;
123+
long sgn = 1;
124+
int seen_dot = 0;
125+
struct lconv *lc = localeconv();
126+
int fpoint = lc->frac_digits; /* int_frac_digits? */
127+
128+
/* we need to add all sorts of checking here. For now just */
129+
/* strip all leading whitespace and any leading dollar sign */
130+
while (isspace(*s) || *s == '$')
131+
s++;
132+
133+
/* a leading minus or paren signifies a negative number */
134+
/* again, better heuristics needed */
135+
if (*s == '-' || *s == '(')
136+
{
137+
sgn = -1;
138+
s++;
139+
}
140+
else if (*s == '+')
141+
s++;
142+
143+
/* frac_digits in the C locale seems to return CHAR_MAX */
144+
/* best guess is 2 in this case I think */
145+
if (fpoint == CHAR_MAX)
146+
fpoint = 2;
147+
148+
for (; ; s++)
149+
{
150+
/* we look for digits as long as we have less */
151+
/* than the required number of decimal places */
152+
if (isdigit(*s) && dec < fpoint)
153+
{
154+
value = (value * 10) + *s - '0';
155+
156+
if (seen_dot)
157+
dec++;
158+
}
159+
else if (*s == *lc->decimal_point && !seen_dot)
160+
seen_dot = 1;
161+
else
162+
{
163+
/* round off */
164+
if (isdigit(*s) && *s >= '5')
165+
value++;
166+
167+
/* adjust for less than required decimal places */
168+
for (; dec < fpoint; dec++)
169+
value *= 10;
170+
171+
return(value * sgn);
172+
}
173+
}
174+
}
175+
176+
177+
/* used by cash_words_out() below */
178+
static const char *
179+
num_word(int value)
180+
{
181+
static char buf[128];
182+
static const char *small[] = {
183+
"zero", "one", "two", "three", "four", "five", "six", "seven",
184+
"eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
185+
"fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
186+
"thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety"
187+
};
188+
const char **big = small + 18;
189+
int tu = value % 100;
190+
191+
/* deal with the simple cases first */
192+
if (value <= 20)
193+
return(small[value]);
194+
195+
/* is it an even multiple of 100? */
196+
if (!tu)
197+
{
198+
sprintf(buf, "%s hundred", small[value/100]);
199+
return(buf);
200+
}
201+
202+
/* more than 99? */
203+
if (value > 99)
204+
{
205+
/* is it an even multiple of 10 other than 10? */
206+
if (value % 10 == 0 && tu > 10)
207+
sprintf(buf, "%s hundred %s",
208+
small[value/100], big[tu/10]);
209+
else if (tu < 20)
210+
sprintf(buf, "%s hundred and %s",
211+
small[value/100], small[tu]);
212+
else
213+
sprintf(buf, "%s hundred %s %s",
214+
small[value/100], big[tu/10], small[tu % 10]);
215+
}
216+
else
217+
{
218+
/* is it an even multiple of 10 other than 10? */
219+
if (value % 10 == 0 && tu > 10)
220+
sprintf(buf, "%s", big[tu/10]);
221+
else if (tu < 20)
222+
sprintf(buf, "%s", small[tu]);
223+
else
224+
sprintf(buf, "%s %s", big[tu/10], small[tu % 10]);
225+
}
226+
227+
return(buf);
228+
}
229+
230+
/* this converts a long as well but to a representation using words */
231+
/* obviously way North American centric - sorry */
232+
const char *
233+
cash_words_out(long value)
234+
{
235+
static char buf[128];
236+
char *p = buf;
237+
long m0;
238+
long m1;
239+
long m2;
240+
long m3;
241+
242+
/* work with positive numbers */
243+
if (value < 0)
244+
{
245+
value *= -1;
246+
strcpy(buf, "minus ");
247+
p += 6;
248+
}
249+
else
250+
*buf = 0;
251+
252+
m0 = value % 100; /* cents */
253+
m1 = (value/100) % 1000; /* hundreds */
254+
m2 = (value/100000) % 1000; /* thousands */
255+
m3 = value/100000000 % 1000; /* millions */
256+
257+
if (m3)
258+
{
259+
strcat(buf, num_word(m3));
260+
strcat(buf, " million ");
261+
}
262+
263+
if (m2)
264+
{
265+
strcat(buf, num_word(m2));
266+
strcat(buf, " thousand ");
267+
}
268+
269+
if (m1)
270+
strcat(buf, num_word(m1));
271+
272+
if (!*p)
273+
strcat(buf, "zero");
274+
275+
strcat(buf, (int)(value/100) == 1 ? " dollar and " : " dollars and ");
276+
strcat(buf, num_word(m0));
277+
strcat(buf, m0 == 1 ? " cent" : " cents");
278+
*buf = toupper(*buf);
279+
return(buf);
280+
}
281+
282+
#include <stdlib.h>
283+
284+
#ifdef TEST_MAIN
285+
int
286+
main(void)
287+
{
288+
char p[64], *q;
289+
long v; /* the long value representing the amount */
290+
int d; /* number of decimal places - default 2 */
291+
292+
while (fgets(p, sizeof(p), stdin) != NULL)
293+
{
294+
if ((q = strchr(p, '\n')) != NULL)
295+
*q = 0;
296+
297+
for (q = p; *q && !isspace(*q); q++)
298+
;
299+
300+
v = cash_in(p);
301+
d = *q ? atoi(q) : 2;
302+
303+
printf("%12.12s %10ld ", p, v);
304+
printf("%12s %s\n", cash_out(v), cash_words_out(v));
305+
}
306+
307+
return(0);
308+
}
309+
#endif /* TEST_MAIN */

src/include/utils/cash.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
cash.h
3+
Written by D'Arcy J.M. Cain
4+
5+
functions to allow input and output of money normally but store
6+
and handle it as long integers
7+
*/
8+
9+
#ifndef _CASH_H
10+
#define _CASH_H
11+
12+
const char *cash_out(long value);
13+
long cash_in(const char *str);
14+
const char *cash_words_out(long value);
15+
16+
#endif /* _CASH_H */

0 commit comments

Comments
 (0)