3 Written by D'Arcy J.M. Cain
5 Functions to allow input and output of money normally but store
8 Set tabstops to 4 for best results
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.
14 $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.3 1997/04/10 20:51:13 scrappy Exp $
23 #include <utils/cash.h>
25 /* when we go to 64 bit values we will have to modify this */
28 #define TERMINATOR (CASH_BUFSZ - 1)
29 #define LAST_PAREN (TERMINATOR - 1)
30 #define LAST_DIGIT (LAST_PAREN - 1)
32 /* function to convert a long to a dollars and cents representation */
36 char *retbuf, buf[CASH_BUFSZ];
37 struct lconv *lc = localeconv();
38 int mod_group = *lc->mon_grouping;
39 int comma = *lc->mon_thousands_sep;
40 int points = lc->frac_digits; /* int_frac_digits? */
42 int count = LAST_DIGIT;
44 int comma_position = 0;
46 /* frac_digits in the C locale seems to return CHAR_MAX */
47 /* best guess is 2 in this case I think */
48 if (points == CHAR_MAX)
51 point_pos = LAST_DIGIT - points;
53 /* We're playing a little fast and loose with this. Shoot me. */
54 if (!mod_group || mod_group == CHAR_MAX)
57 /* allow more than three decimal points and separate them */
60 point_pos -= (points - 1)/mod_group;
61 comma_position = point_pos % (mod_group + 1);
64 /* we work with positive amounts and add the minus sign at the end */
71 /* allow for trailing negative strings */
72 memset(buf, ' ', CASH_BUFSZ);
73 buf[TERMINATOR] = buf[LAST_PAREN] = 0;
75 while (value || count > (point_pos - 2))
77 if (points && count == point_pos)
78 buf[count--] = *lc->decimal_point;
79 else if (comma && count % (mod_group + 1) == comma_position)
82 buf[count--] = (value % 10) + '0';
86 if (buf[LAST_DIGIT] == ',')
87 buf[LAST_DIGIT] = buf[LAST_PAREN];
89 /* see if we need to signify negative amount */
92 retbuf = palloc(CASH_BUFSZ + 2 - count + strlen(lc->negative_sign));
94 /* Position code of 0 means use parens */
96 sprintf(retbuf, "(%s)", buf + count);
97 else if (lc->n_sign_posn == 2)
98 sprintf(retbuf, "%s%s", buf + count, lc->negative_sign);
100 sprintf(retbuf, "%s%s", lc->negative_sign, buf + count);
104 retbuf = palloc(CASH_BUFSZ + 2 - count);
105 strcpy(retbuf, buf + count);
111 /* convert a string to a long integer */
113 cash_in(const char *s)
119 struct lconv *lc = localeconv();
120 int fpoint = lc->frac_digits; /* int_frac_digits? */
122 /* we need to add all sorts of checking here. For now just */
123 /* strip all leading whitespace and any leading dollar sign */
124 while (isspace(*s) || *s == '$')
127 /* a leading minus or paren signifies a negative number */
128 /* again, better heuristics needed */
129 if (*s == '-' || *s == '(')
137 /* frac_digits in the C locale seems to return CHAR_MAX */
138 /* best guess is 2 in this case I think */
139 if (fpoint == CHAR_MAX)
144 /* we look for digits as long as we have less */
145 /* than the required number of decimal places */
146 if (isdigit(*s) && dec < fpoint)
148 value = (value * 10) + *s - '0';
153 else if (*s == *lc->decimal_point && !seen_dot)
158 if (isdigit(*s) && *s >= '5')
161 /* adjust for less than required decimal places */
162 for (; dec < fpoint; dec++)
171 /* used by cash_words_out() below */
175 static char buf[128];
176 static const char *small[] = {
177 "zero", "one", "two", "three", "four", "five", "six", "seven",
178 "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
179 "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
180 "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety"
182 const char **big = small + 18;
183 int tu = value % 100;
185 /* deal with the simple cases first */
187 return(small[value]);
189 /* is it an even multiple of 100? */
192 sprintf(buf, "%s hundred", small[value/100]);
199 /* is it an even multiple of 10 other than 10? */
200 if (value % 10 == 0 && tu > 10)
201 sprintf(buf, "%s hundred %s",
202 small[value/100], big[tu/10]);
204 sprintf(buf, "%s hundred and %s",
205 small[value/100], small[tu]);
207 sprintf(buf, "%s hundred %s %s",
208 small[value/100], big[tu/10], small[tu % 10]);
212 /* is it an even multiple of 10 other than 10? */
213 if (value % 10 == 0 && tu > 10)
214 sprintf(buf, "%s", big[tu/10]);
216 sprintf(buf, "%s", small[tu]);
218 sprintf(buf, "%s %s", big[tu/10], small[tu % 10]);
224 /* this converts a long as well but to a representation using words */
225 /* obviously way North American centric - sorry */
227 cash_words_out(long value)
229 static char buf[128];
236 /* work with positive numbers */
240 strcpy(buf, "minus ");
246 m0 = value % 100; /* cents */
247 m1 = (value/100) % 1000; /* hundreds */
248 m2 = (value/100000) % 1000; /* thousands */
249 m3 = value/100000000 % 1000; /* millions */
253 strcat(buf, num_word(m3));
254 strcat(buf, " million ");
259 strcat(buf, num_word(m2));
260 strcat(buf, " thousand ");
264 strcat(buf, num_word(m1));
269 strcat(buf, (int)(value/100) == 1 ? " dollar and " : " dollars and ");
270 strcat(buf, num_word(m0));
271 strcat(buf, m0 == 1 ? " cent" : " cents");
272 *buf = toupper(*buf);