]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Add in D'Arcy's cash code
authorMarc G. Fournier <scrappy@hub.org>
Wed, 9 Apr 1997 08:36:21 +0000 (08:36 +0000)
committerMarc G. Fournier <scrappy@hub.org>
Wed, 9 Apr 1997 08:36:21 +0000 (08:36 +0000)
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
src/backend/utils/adt/cash.c [new file with mode: 0644]
src/include/utils/cash.h [new file with mode: 0644]

index 3385a3cc4e4c07e81f4dbe311be07ebe4b594e74..2d356848f977a94ba79725340869426eb2139a5e 100644 (file)
@@ -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 (file)
index 0000000..795095e
--- /dev/null
@@ -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       <stdio.h>
+#include       <string.h>
+#include       <limits.h>
+#include       <ctype.h>
+#include       <locale.h>
+
+#ifdef         TEST_MAIN
+# include      <stdlib.h>
+# define       palloc malloc
+#else
+# include      <palloc.h>
+#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       <stdlib.h>
+
+#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 (file)
index 0000000..02655c8
--- /dev/null
@@ -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 */