?? cash.c
字號(hào):
/* * cash.c * Written by D'Arcy J.M. Cain * darcy@druid.net * http://www.druid.net/darcy/ * * Functions to allow input and output of money normally but store * and handle it as 64 bit ints * * 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 except that * this version handles 64 bit numbers and so can hold values up to * $92,233,720,368,547,758.07. * * $PostgreSQL: pgsql/src/backend/utils/adt/cash.c,v 1.77.2.1 2008/06/09 19:58:46 tgl Exp $ */#include "postgres.h"#include <limits.h>#include <ctype.h>#include <math.h>#include <locale.h>#include "libpq/pqformat.h"#include "utils/cash.h"#include "utils/pg_locale.h"#define CASH_BUFSZ 36#define TERMINATOR (CASH_BUFSZ - 1)#define LAST_PAREN (TERMINATOR - 1)#define LAST_DIGIT (LAST_PAREN - 1)/* * Cash is a pass-by-ref SQL type, so we must pass and return pointers. * These macros and support routine hide the pass-by-refness. */#define PG_GETARG_CASH(n) (* ((Cash *) PG_GETARG_POINTER(n)))#define PG_RETURN_CASH(x) return CashGetDatum(x)/************************************************************************* * Private routines ************************************************************************/static const char *num_word(Cash 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", "forty", "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;} /* num_word() */static DatumCashGetDatum(Cash value){ Cash *result = (Cash *) palloc(sizeof(Cash)); *result = value; return PointerGetDatum(result);}/* cash_in() * Convert a string to a cash data type. * Format is [$]###[,]###[.##] * Examples: 123.45 $123.45 $123,456.78 * */Datumcash_in(PG_FUNCTION_ARGS){ char *str = PG_GETARG_CSTRING(0); Cash result; Cash value = 0; Cash dec = 0; Cash sgn = 1; int seen_dot = 0; const char *s = str; int fpoint; char dsymbol, ssymbol, psymbol; const char *nsymbol, *csymbol; struct lconv *lconvert = PGLC_localeconv(); /* * frac_digits will be CHAR_MAX in some locales, notably C. However, just * testing for == CHAR_MAX is risky, because of compilers like gcc that * "helpfully" let you alter the platform-standard definition of whether * char is signed or not. If we are so unfortunate as to get compiled * with a nonstandard -fsigned-char or -funsigned-char switch, then our * idea of CHAR_MAX will not agree with libc's. The safest course is not * to test for CHAR_MAX at all, but to impose a range check for plausible * frac_digits values. */ fpoint = lconvert->frac_digits; if (fpoint < 0 || fpoint > 10) fpoint = 2; /* best guess in this case, I think */ dsymbol = ((*lconvert->mon_decimal_point != '\0') ? *lconvert->mon_decimal_point : '.'); if (*lconvert->mon_thousands_sep != '\0') ssymbol = *lconvert->mon_thousands_sep; else /* ssymbol should not equal dsymbol */ ssymbol = (dsymbol != ',') ? ',' : '.'; csymbol = ((*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$"); psymbol = ((*lconvert->positive_sign != '\0') ? *lconvert->positive_sign : '+'); nsymbol = ((*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-");#ifdef CASHDEBUG printf("cashin- precision '%d'; decimal '%c'; thousands '%c'; currency '%s'; positive '%c'; negative '%s'\n", fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);#endif /* we need to add all sorts of checking here. For now just */ /* strip all leading whitespace and any leading currency symbol */ while (isspace((unsigned char) *s)) s++; if (strncmp(s, csymbol, strlen(csymbol)) == 0) s += strlen(csymbol);#ifdef CASHDEBUG printf("cashin- string is '%s'\n", s);#endif /* a leading minus or paren signifies a negative number */ /* again, better heuristics needed */ /* XXX - doesn't properly check for balanced parens - djmc */ if (strncmp(s, nsymbol, strlen(nsymbol)) == 0) { sgn = -1; s += strlen(nsymbol);#ifdef CASHDEBUG printf("cashin- negative symbol; string is '%s'\n", s);#endif } else if (*s == '(') { sgn = -1; s++; } else if (*s == psymbol) s++;#ifdef CASHDEBUG printf("cashin- string is '%s'\n", s);#endif while (isspace((unsigned char) *s)) s++; if (strncmp(s, csymbol, strlen(csymbol)) == 0) s += strlen(csymbol);#ifdef CASHDEBUG printf("cashin- string is '%s'\n", s);#endif for (;; s++) { /* we look for digits as int8 as we have less */ /* than the required number of decimal places */ if (isdigit((unsigned char) *s) && dec < fpoint) { value = (value * 10) + *s - '0'; if (seen_dot) dec++; } /* decimal point? then start counting fractions... */ else if (*s == dsymbol && !seen_dot) { seen_dot = 1; } /* not "thousands" separator? */ else if (*s != ssymbol) { /* round off */ if (isdigit((unsigned char) *s) && *s >= '5') value++; /* adjust for less than required decimal places */ for (; dec < fpoint; dec++) value *= 10; break; } } /* should only be trailing digits followed by whitespace or right paren */ while (isdigit((unsigned char) *s)) s++; while (isspace((unsigned char) *s) || *s == ')') s++; if (*s != '\0') ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type money: \"%s\"", str))); result = value * sgn;#ifdef CASHDEBUG printf("cashin- result is %d\n", result);#endif PG_RETURN_CASH(result);}/* cash_out() * Function to convert cash to a dollars and cents representation. * XXX HACK This code appears to assume US conventions for * positive-valued amounts. - tgl 97/04/14 */Datumcash_out(PG_FUNCTION_ARGS){ Cash value = PG_GETARG_CASH(0); char *result; char buf[CASH_BUFSZ]; int minus = 0; int count = LAST_DIGIT; int point_pos; int ssymbol_position = 0; int points, mon_group; char ssymbol; const char *csymbol, *nsymbol; char dsymbol; char convention; struct lconv *lconvert = PGLC_localeconv(); /* see comments about frac_digits in cash_in() */ points = lconvert->frac_digits; if (points < 0 || points > 10) points = 2; /* best guess in this case, I think */ /* * As with frac_digits, must apply a range check to mon_grouping to avoid * being fooled by variant CHAR_MAX values. */ mon_group = *lconvert->mon_grouping; if (mon_group <= 0 || mon_group > 6) mon_group = 3; convention = lconvert->n_sign_posn; dsymbol = ((*lconvert->mon_decimal_point != '\0') ? *lconvert->mon_decimal_point : '.'); if (*lconvert->mon_thousands_sep != '\0') ssymbol = *lconvert->mon_thousands_sep; else /* ssymbol should not equal dsymbol */ ssymbol = (dsymbol != ',') ? ',' : '.'; csymbol = ((*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$"); nsymbol = ((*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-"); point_pos = LAST_DIGIT - points; point_pos -= (points - 1) / mon_group; ssymbol_position = point_pos % (mon_group + 1); /* we work with positive amounts and add the minus sign at the end */ if (value < 0) { minus = 1; value = -value; } /* 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--] = dsymbol; else if (ssymbol && count % (mon_group + 1) == ssymbol_position) buf[count--] = ssymbol; buf[count--] = ((uint64) value % 10) + '0'; value = ((uint64) value) / 10; } strncpy((buf + count - strlen(csymbol) + 1), csymbol, strlen(csymbol)); count -= strlen(csymbol) - 1; /* * If points == 0 and the number of digits % mon_group == 0, * the code above adds a trailing ssymbol on the far right, * so remove it. */ if (buf[LAST_DIGIT] == ssymbol) buf[LAST_DIGIT] = '\0'; /* see if we need to signify negative amount */ if (minus) { result = palloc(CASH_BUFSZ + 2 - count + strlen(nsymbol)); /* Position code of 0 means use parens */ if (convention == 0) sprintf(result, "(%s)", buf + count); else if (convention == 2) sprintf(result, "%s%s", buf + count, nsymbol); else sprintf(result, "%s%s", nsymbol, buf + count); } else { result = palloc(CASH_BUFSZ + 2 - count); strcpy(result, buf + count); } PG_RETURN_CSTRING(result);}/* * cash_recv - converts external binary format to cash */Datumcash_recv(PG_FUNCTION_ARGS){ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); PG_RETURN_CASH((Cash) pq_getmsgint64(buf));}/* * cash_send - converts cash to binary format */Datumcash_send(PG_FUNCTION_ARGS){ Cash arg1 = PG_GETARG_CASH(0); StringInfoData buf; pq_begintypsend(&buf); pq_sendint64(&buf, arg1); PG_RETURN_BYTEA_P(pq_endtypsend(&buf));}/* * Comparison functions */Datumcash_eq(PG_FUNCTION_ARGS){ Cash c1 = PG_GETARG_CASH(0); Cash c2 = PG_GETARG_CASH(1); PG_RETURN_BOOL(c1 == c2);}Datumcash_ne(PG_FUNCTION_ARGS){ Cash c1 = PG_GETARG_CASH(0); Cash c2 = PG_GETARG_CASH(1); PG_RETURN_BOOL(c1 != c2);}Datumcash_lt(PG_FUNCTION_ARGS){ Cash c1 = PG_GETARG_CASH(0); Cash c2 = PG_GETARG_CASH(1); PG_RETURN_BOOL(c1 < c2);}Datumcash_le(PG_FUNCTION_ARGS){ Cash c1 = PG_GETARG_CASH(0); Cash c2 = PG_GETARG_CASH(1); PG_RETURN_BOOL(c1 <= c2);}Datumcash_gt(PG_FUNCTION_ARGS)
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -