?? cash.c
字號:
/* * cash.c * Written by D'Arcy J.M. Cain * * Functions to allow input and output of money normally but store * and handle it as int4s * * 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. * * $Header: /usr/local/cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.28.2.1 1999/08/02 05:24:50 scrappy Exp $ */#include <limits.h>#include <ctype.h>#include <locale.h>#include "postgres.h"#include "miscadmin.h"#include "utils/builtins.h"#include "utils/cash.h"static const char *num_word(Cash value);/* 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)#ifdef USE_LOCALEstatic struct lconv *lconvert = NULL;#endif/* cash_in() * Convert a string to a cash data type. * Format is [$]###[,]###[.##] * Examples: 123.45 $123.45 $123,456.78 * * This is currently implemented as a 32-bit integer. * XXX HACK It looks as though some of the symbols for * monetary values returned by localeconv() can be multiple * bytes/characters. This code assumes one byte only. - tgl 97/04/14 * XXX UNHACK Allow the currency symbol to be multi-byte. * - thomas 1998-03-01 */Cash *cash_in(const char *str){ Cash *result; Cash value = 0; Cash dec = 0; Cash sgn = 1; int seen_dot = 0; const char *s = str; int fpoint; char *csymbol; char dsymbol, ssymbol, psymbol, *nsymbol;#ifdef USE_LOCALE#ifdef CASHDEBUG setlocale(LC_ALL, ""); lconvert = localeconv();#endif if (lconvert == NULL) lconvert = localeconv(); /* frac_digits in the C locale seems to return CHAR_MAX */ /* best guess is 2 in this case I think */ fpoint = ((lconvert->frac_digits != CHAR_MAX) ? lconvert->frac_digits : 2); /* int_frac_digits? */ dsymbol = ((*lconvert->mon_decimal_point != '\0') ? *lconvert->mon_decimal_point : '.'); ssymbol = ((*lconvert->mon_thousands_sep != '\0') ? *lconvert->mon_thousands_sep : ','); csymbol = ((*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$"); psymbol = ((*lconvert->positive_sign != '\0') ? *lconvert->positive_sign : '+'); nsymbol = ((*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-");#else fpoint = 2; dsymbol = '.'; ssymbol = ','; csymbol = "$"; psymbol = '+'; nsymbol = "-";#endif#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(*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 */ 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(*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 int4 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++; /* decimal point? then start counting fractions... */ } else if (*s == dsymbol && !seen_dot) { seen_dot = 1; /* "thousands" separator? then skip... */ } else if (*s == ssymbol) { } else { /* round off */ if (isdigit(*s) && *s >= '5') value++; /* adjust for less than required decimal places */ for (; dec < fpoint; dec++) value *= 10; break; } } while (isspace(*s) || *s == '0' || *s == ')') s++; if (*s != '\0') elog(ERROR, "Bad money external representation %s", str); if (!PointerIsValid(result = palloc(sizeof(Cash)))) elog(ERROR, "Memory allocation failed, can't input cash '%s'", str); *result = (value * sgn);#ifdef CASHDEBUG printf("cashin- result is %d\n", *result);#endif return result;} /* cash_in() *//* 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 */const char *cash_out(Cash *in_value){ Cash value = *in_value; char *result; char buf[CASH_BUFSZ]; int minus = 0; int count = LAST_DIGIT; int point_pos; int comma_position = 0; char mon_group, comma, points; char *csymbol, dsymbol, *nsymbol; char convention;#ifdef USE_LOCALE if (lconvert == NULL) lconvert = localeconv(); mon_group = *lconvert->mon_grouping; comma = ((*lconvert->mon_thousands_sep != '\0') ? *lconvert->mon_thousands_sep : ','); /* frac_digits in the C locale seems to return CHAR_MAX */ /* best guess is 2 in this case I think */ points = ((lconvert->frac_digits != CHAR_MAX) ? lconvert->frac_digits : 2); /* int_frac_digits? */ convention = lconvert->n_sign_posn; dsymbol = ((*lconvert->mon_decimal_point != '\0') ? *lconvert->mon_decimal_point : '.'); csymbol = ((*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$"); nsymbol = ((*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-");#else mon_group = 3; comma = ','; csymbol = "$"; dsymbol = '.'; nsymbol = "-"; points = 2; convention = 0;#endif point_pos = LAST_DIGIT - points; /* We're playing a little fast and loose with this. Shoot me. */ /* Not me, that was the other guy. Haven't fixed it yet - thomas */ if (!mon_group || mon_group == CHAR_MAX) mon_group = 3; /* allow more than three decimal points and separate them */ if (comma) { point_pos -= (points - 1) / mon_group; comma_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 *= -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--] = dsymbol; else if (comma && count % (mon_group + 1) == comma_position) buf[count--] = comma; buf[count--] = (value % 10) + '0'; value /= 10; } strncpy((buf + count - strlen(csymbol) + 1), csymbol, strlen(csymbol)); count -= strlen(csymbol) - 1; if (buf[LAST_DIGIT] == ',') buf[LAST_DIGIT] = buf[LAST_PAREN]; /* see if we need to signify negative amount */ if (minus) { if (!PointerIsValid(result = palloc(CASH_BUFSZ + 2 - count + strlen(nsymbol)))) elog(ERROR, "Memory allocation failed, can't output cash", NULL); /* 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 { if (!PointerIsValid(result = palloc(CASH_BUFSZ + 2 - count))) elog(ERROR, "Memory allocation failed, can't output cash", NULL); strcpy(result, buf + count); } return result;} /* cash_out() */boolcash_eq(Cash *c1, Cash *c2){ if (!PointerIsValid(c1) || !PointerIsValid(c2)) return FALSE; return *c1 == *c2;} /* cash_eq() */boolcash_ne(Cash *c1, Cash *c2){ if (!PointerIsValid(c1) || !PointerIsValid(c2)) return FALSE; return *c1 != *c2;} /* cash_ne() */boolcash_lt(Cash *c1, Cash *c2){ if (!PointerIsValid(c1) || !PointerIsValid(c2)) return FALSE; return *c1 < *c2;} /* cash_lt() */boolcash_le(Cash *c1, Cash *c2){ if (!PointerIsValid(c1) || !PointerIsValid(c2)) return FALSE; return *c1 <= *c2;} /* cash_le() */boolcash_gt(Cash *c1, Cash *c2){ if (!PointerIsValid(c1) || !PointerIsValid(c2)) return FALSE; return *c1 > *c2;} /* cash_gt() */boolcash_ge(Cash *c1, Cash *c2){ if (!PointerIsValid(c1) || !PointerIsValid(c2)) return FALSE; return *c1 >= *c2;} /* cash_ge() *//* cash_pl() * Add two cash values. */Cash *cash_pl(Cash *c1, Cash *c2){ Cash *result; if (!PointerIsValid(c1) || !PointerIsValid(c2)) return NULL; if (!PointerIsValid(result = palloc(sizeof(Cash)))) elog(ERROR, "Memory allocation failed, can't add cash", NULL); *result = (*c1 + *c2); return result;} /* cash_pl() *//* cash_mi() * Subtract two cash values. */Cash *cash_mi(Cash *c1, Cash *c2){ Cash *result; if (!PointerIsValid(c1) || !PointerIsValid(c2)) return NULL;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -