From 4999f002e6e5e76903c7f33027544d3e3ffff2be Mon Sep 17 00:00:00 2001 From: "Marc G. Fournier" Date: Wed, 9 Apr 1997 08:36:21 +0000 Subject: [PATCH] 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... --- src/backend/utils/adt/Makefile | 4 +- src/backend/utils/adt/cash.c | 309 +++++++++++++++++++++++++++++++++ src/include/utils/cash.h | 16 ++ 3 files changed, 327 insertions(+), 2 deletions(-) create mode 100644 src/backend/utils/adt/cash.c create mode 100644 src/include/utils/cash.h diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 3385a3cc4e..2d356848f9 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -4,7 +4,7 @@ # Makefile for utils/adt # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.7 1997/03/14 23:19:50 scrappy Exp $ +# $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.8 1997/04/09 08:36:04 scrappy Exp $ # #------------------------------------------------------------------------- @@ -17,7 +17,7 @@ INCLUDE_OPT = -I../.. \ CFLAGS+=$(INCLUDE_OPT) -OBJS = acl.o arrayfuncs.o arrayutils.o bool.o char.o chunk.o date.o \ +OBJS = acl.o arrayfuncs.o arrayutils.o bool.o cash.o char.o chunk.o date.o \ datum.o dt.o filename.o float.o geo_ops.o geo_selfuncs.o int.o \ misc.o nabstime.o name.o not_in.o numutils.o oid.o \ oidname.o oidint2.o oidint4.o oracle_compat.o regexp.o regproc.o \ diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c new file mode 100644 index 0000000000..795095ec4e --- /dev/null +++ b/src/backend/utils/adt/cash.c @@ -0,0 +1,309 @@ +/* +cash.c +Written by D'Arcy J.M. Cain + +Functions to allow input and output of money normally but store +and handle it as longs + +Set tabstops to 4 for best results + +A slightly modified version of this file and a discussion of the +workings can be found in the book "Software Solutions in C" by +Dale Schumacher, Academic Press, ISBN: 0-12-632360-7. + +*/ + +#include +#include +#include +#include +#include + +#ifdef TEST_MAIN +# include +# define palloc malloc +#else +# include +#endif + +#include "cash.h" + +/* when we go to 64 bit values we will have to modify this */ +#define CASH_BUFSZ 24 + +#define TERMINATOR (CASH_BUFSZ - 1) +#define LAST_PAREN (TERMINATOR - 1) +#define LAST_DIGIT (LAST_PAREN - 1) + +/* function to convert a long to a dollars and cents representation */ +const char * +cash_out(long value) +{ + char *retbuf, buf[CASH_BUFSZ]; + struct lconv *lc = localeconv(); + int mod_group = *lc->mon_grouping; + int comma = *lc->mon_thousands_sep; + int points = lc->frac_digits; /* int_frac_digits? */ + int minus = 0; + int count = LAST_DIGIT; + int point_pos; + int comma_position = 0; + + /* frac_digits in the C locale seems to return CHAR_MAX */ + /* best guess is 2 in this case I think */ + if (points == CHAR_MAX) + points = 2; + + point_pos = LAST_DIGIT - points; + + /* We're playing a little fast and loose with this. Shoot me. */ + if (!mod_group || mod_group == CHAR_MAX) + mod_group = 3; + + /* allow more than three decimal points and separate them */ + if (comma) + { + point_pos -= (points - 1)/mod_group; + comma_position = point_pos % (mod_group + 1); + } + + /* we work with positive amounts and add the minus sign at the end */ + if (value < 0) + { + minus = 1; + value *= -1; + } + + /* allow for trailing negative strings */ + memset(buf, ' ', CASH_BUFSZ); + buf[TERMINATOR] = buf[LAST_PAREN] = 0; + + while (value || count > (point_pos - 2)) + { + if (points && count == point_pos) + buf[count--] = *lc->decimal_point; + else if (comma && count % (mod_group + 1) == comma_position) + buf[count--] = comma; + + buf[count--] = (value % 10) + '0'; + value /= 10; + } + + if (buf[LAST_DIGIT] == ',') + buf[LAST_DIGIT] = buf[LAST_PAREN]; + + /* see if we need to signify negative amount */ + if (minus) + { + retbuf = palloc(CASH_BUFSZ + 2 - count + strlen(lc->negative_sign)); + + /* Position code of 0 means use parens */ + if (!lc->n_sign_posn) + sprintf(retbuf, "(%s)", buf + count); + else if (lc->n_sign_posn == 2) + sprintf(retbuf, "%s%s", buf + count, lc->negative_sign); + else + sprintf(retbuf, "%s%s", lc->negative_sign, buf + count); + } + else + { + retbuf = palloc(CASH_BUFSZ + 2 - count); + strcpy(retbuf, buf + count); + } + + return retbuf; +} + +/* convert a string to a long integer */ +long +cash_in(const char *s) +{ + long value = 0; + long dec = 0; + long sgn = 1; + int seen_dot = 0; + struct lconv *lc = localeconv(); + int fpoint = lc->frac_digits; /* int_frac_digits? */ + + /* we need to add all sorts of checking here. For now just */ + /* strip all leading whitespace and any leading dollar sign */ + while (isspace(*s) || *s == '$') + s++; + + /* a leading minus or paren signifies a negative number */ + /* again, better heuristics needed */ + if (*s == '-' || *s == '(') + { + sgn = -1; + s++; + } + else if (*s == '+') + s++; + + /* frac_digits in the C locale seems to return CHAR_MAX */ + /* best guess is 2 in this case I think */ + if (fpoint == CHAR_MAX) + fpoint = 2; + + for (; ; s++) + { + /* we look for digits as long as we have less */ + /* than the required number of decimal places */ + if (isdigit(*s) && dec < fpoint) + { + value = (value * 10) + *s - '0'; + + if (seen_dot) + dec++; + } + else if (*s == *lc->decimal_point && !seen_dot) + seen_dot = 1; + else + { + /* round off */ + if (isdigit(*s) && *s >= '5') + value++; + + /* adjust for less than required decimal places */ + for (; dec < fpoint; dec++) + value *= 10; + + return(value * sgn); + } + } +} + + +/* used by cash_words_out() below */ +static const char * +num_word(int value) +{ + static char buf[128]; + static const char *small[] = { + "zero", "one", "two", "three", "four", "five", "six", "seven", + "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", + "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty", + "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety" + }; + const char **big = small + 18; + int tu = value % 100; + + /* deal with the simple cases first */ + if (value <= 20) + return(small[value]); + + /* is it an even multiple of 100? */ + if (!tu) + { + sprintf(buf, "%s hundred", small[value/100]); + return(buf); + } + + /* more than 99? */ + if (value > 99) + { + /* is it an even multiple of 10 other than 10? */ + if (value % 10 == 0 && tu > 10) + sprintf(buf, "%s hundred %s", + small[value/100], big[tu/10]); + else if (tu < 20) + sprintf(buf, "%s hundred and %s", + small[value/100], small[tu]); + else + sprintf(buf, "%s hundred %s %s", + small[value/100], big[tu/10], small[tu % 10]); + } + else + { + /* is it an even multiple of 10 other than 10? */ + if (value % 10 == 0 && tu > 10) + sprintf(buf, "%s", big[tu/10]); + else if (tu < 20) + sprintf(buf, "%s", small[tu]); + else + sprintf(buf, "%s %s", big[tu/10], small[tu % 10]); + } + + return(buf); +} + +/* this converts a long as well but to a representation using words */ +/* obviously way North American centric - sorry */ +const char * +cash_words_out(long value) +{ + static char buf[128]; + char *p = buf; + long m0; + long m1; + long m2; + long m3; + + /* work with positive numbers */ + if (value < 0) + { + value *= -1; + strcpy(buf, "minus "); + p += 6; + } + else + *buf = 0; + + m0 = value % 100; /* cents */ + m1 = (value/100) % 1000; /* hundreds */ + m2 = (value/100000) % 1000; /* thousands */ + m3 = value/100000000 % 1000; /* millions */ + + if (m3) + { + strcat(buf, num_word(m3)); + strcat(buf, " million "); + } + + if (m2) + { + strcat(buf, num_word(m2)); + strcat(buf, " thousand "); + } + + if (m1) + strcat(buf, num_word(m1)); + + if (!*p) + strcat(buf, "zero"); + + strcat(buf, (int)(value/100) == 1 ? " dollar and " : " dollars and "); + strcat(buf, num_word(m0)); + strcat(buf, m0 == 1 ? " cent" : " cents"); + *buf = toupper(*buf); + return(buf); +} + +#include + +#ifdef TEST_MAIN +int +main(void) +{ + char p[64], *q; + long v; /* the long value representing the amount */ + int d; /* number of decimal places - default 2 */ + + while (fgets(p, sizeof(p), stdin) != NULL) + { + if ((q = strchr(p, '\n')) != NULL) + *q = 0; + + for (q = p; *q && !isspace(*q); q++) + ; + + v = cash_in(p); + d = *q ? atoi(q) : 2; + + printf("%12.12s %10ld ", p, v); + printf("%12s %s\n", cash_out(v), cash_words_out(v)); + } + + return(0); +} +#endif /* TEST_MAIN */ diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h new file mode 100644 index 0000000000..02655c8874 --- /dev/null +++ b/src/include/utils/cash.h @@ -0,0 +1,16 @@ +/* +cash.h +Written by D'Arcy J.M. Cain + +functions to allow input and output of money normally but store +and handle it as long integers +*/ + +#ifndef _CASH_H +#define _CASH_H + +const char *cash_out(long value); +long cash_in(const char *str); +const char *cash_words_out(long value); + +#endif /* _CASH_H */