cash.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:16k
源码类别:

数据库系统

开发平台:

Unix_Linux

  1. /*
  2.  * cash.c
  3.  * Written by D'Arcy J.M. Cain
  4.  *
  5.  * Functions to allow input and output of money normally but store
  6.  * and handle it as int4s
  7.  *
  8.  * A slightly modified version of this file and a discussion of the
  9.  * workings can be found in the book "Software Solutions in C" by
  10.  * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7.
  11.  *
  12.  * $Header: /usr/local/cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.28.2.1 1999/08/02 05:24:50 scrappy Exp $
  13.  */
  14. #include <limits.h>
  15. #include <ctype.h>
  16. #include <locale.h>
  17. #include "postgres.h"
  18. #include "miscadmin.h"
  19. #include "utils/builtins.h"
  20. #include "utils/cash.h"
  21. static const char *num_word(Cash value);
  22. /* when we go to 64 bit values we will have to modify this */
  23. #define CASH_BUFSZ 24
  24. #define TERMINATOR (CASH_BUFSZ - 1)
  25. #define LAST_PAREN (TERMINATOR - 1)
  26. #define LAST_DIGIT (LAST_PAREN - 1)
  27. #ifdef USE_LOCALE
  28. static struct lconv *lconvert = NULL;
  29. #endif
  30. /* cash_in()
  31.  * Convert a string to a cash data type.
  32.  * Format is [$]###[,]###[.##]
  33.  * Examples: 123.45 $123.45 $123,456.78
  34.  *
  35.  * This is currently implemented as a 32-bit integer.
  36.  * XXX HACK It looks as though some of the symbols for
  37.  * monetary values returned by localeconv() can be multiple
  38.  * bytes/characters. This code assumes one byte only. - tgl 97/04/14
  39.  * XXX UNHACK Allow the currency symbol to be multi-byte.
  40.  * - thomas 1998-03-01
  41.  */
  42. Cash *
  43. cash_in(const char *str)
  44. {
  45. Cash    *result;
  46. Cash value = 0;
  47. Cash dec = 0;
  48. Cash sgn = 1;
  49. int seen_dot = 0;
  50. const char *s = str;
  51. int fpoint;
  52. char    *csymbol;
  53. char dsymbol,
  54. ssymbol,
  55. psymbol,
  56.    *nsymbol;
  57. #ifdef USE_LOCALE
  58. #ifdef CASHDEBUG
  59. setlocale(LC_ALL, "");
  60. lconvert = localeconv();
  61. #endif
  62. if (lconvert == NULL)
  63. lconvert = localeconv();
  64. /* frac_digits in the C locale seems to return CHAR_MAX */
  65. /* best guess is 2 in this case I think */
  66. fpoint = ((lconvert->frac_digits != CHAR_MAX) ? lconvert->frac_digits : 2); /* int_frac_digits? */
  67. dsymbol = ((*lconvert->mon_decimal_point != '') ? *lconvert->mon_decimal_point : '.');
  68. ssymbol = ((*lconvert->mon_thousands_sep != '') ? *lconvert->mon_thousands_sep : ',');
  69. csymbol = ((*lconvert->currency_symbol != '') ? lconvert->currency_symbol : "$");
  70. psymbol = ((*lconvert->positive_sign != '') ? *lconvert->positive_sign : '+');
  71. nsymbol = ((*lconvert->negative_sign != '') ? lconvert->negative_sign : "-");
  72. #else
  73. fpoint = 2;
  74. dsymbol = '.';
  75. ssymbol = ',';
  76. csymbol = "$";
  77. psymbol = '+';
  78. nsymbol = "-";
  79. #endif
  80. #ifdef CASHDEBUG
  81. printf("cashin- precision '%d'; decimal '%c'; thousands '%c'; currency '%s'; positive '%c'; negative '%s'n",
  82.    fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);
  83. #endif
  84. /* we need to add all sorts of checking here.  For now just */
  85. /* strip all leading whitespace and any leading currency symbol */
  86. while (isspace(*s))
  87. s++;
  88. if (strncmp(s, csymbol, strlen(csymbol)) == 0)
  89. s += strlen(csymbol);
  90. #ifdef CASHDEBUG
  91. printf("cashin- string is '%s'n", s);
  92. #endif
  93. /* a leading minus or paren signifies a negative number */
  94. /* again, better heuristics needed */
  95. if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
  96. {
  97. sgn = -1;
  98. s += strlen(nsymbol);
  99. #ifdef CASHDEBUG
  100. printf("cashin- negative symbol; string is '%s'n", s);
  101. #endif
  102. }
  103. else if (*s == '(')
  104. {
  105. sgn = -1;
  106. s++;
  107. }
  108. else if (*s == psymbol)
  109. s++;
  110. #ifdef CASHDEBUG
  111. printf("cashin- string is '%s'n", s);
  112. #endif
  113. while (isspace(*s))
  114. s++;
  115. if (strncmp(s, csymbol, strlen(csymbol)) == 0)
  116. s += strlen(csymbol);
  117. #ifdef CASHDEBUG
  118. printf("cashin- string is '%s'n", s);
  119. #endif
  120. for (;; s++)
  121. {
  122. /* we look for digits as int4 as we have less */
  123. /* than the required number of decimal places */
  124. if (isdigit(*s) && dec < fpoint)
  125. {
  126. value = (value * 10) + *s - '0';
  127. if (seen_dot)
  128. dec++;
  129. /* decimal point? then start counting fractions... */
  130. }
  131. else if (*s == dsymbol && !seen_dot)
  132. {
  133. seen_dot = 1;
  134. /* "thousands" separator? then skip... */
  135. }
  136. else if (*s == ssymbol)
  137. {
  138. }
  139. else
  140. {
  141. /* round off */
  142. if (isdigit(*s) && *s >= '5')
  143. value++;
  144. /* adjust for less than required decimal places */
  145. for (; dec < fpoint; dec++)
  146. value *= 10;
  147. break;
  148. }
  149. }
  150. while (isspace(*s) || *s == '0' || *s == ')')
  151. s++;
  152. if (*s != '')
  153. elog(ERROR, "Bad money external representation %s", str);
  154. if (!PointerIsValid(result = palloc(sizeof(Cash))))
  155. elog(ERROR, "Memory allocation failed, can't input cash '%s'", str);
  156. *result = (value * sgn);
  157. #ifdef CASHDEBUG
  158. printf("cashin- result is %dn", *result);
  159. #endif
  160. return result;
  161. } /* cash_in() */
  162. /* cash_out()
  163.  * Function to convert cash to a dollars and cents representation.
  164.  * XXX HACK This code appears to assume US conventions for
  165.  * positive-valued amounts. - tgl 97/04/14
  166.  */
  167. const char *
  168. cash_out(Cash *in_value)
  169. {
  170. Cash value = *in_value;
  171. char    *result;
  172. char buf[CASH_BUFSZ];
  173. int minus = 0;
  174. int count = LAST_DIGIT;
  175. int point_pos;
  176. int comma_position = 0;
  177. char mon_group,
  178. comma,
  179. points;
  180. char    *csymbol,
  181. dsymbol,
  182.    *nsymbol;
  183. char convention;
  184. #ifdef USE_LOCALE
  185. if (lconvert == NULL)
  186. lconvert = localeconv();
  187. mon_group = *lconvert->mon_grouping;
  188. comma = ((*lconvert->mon_thousands_sep != '') ? *lconvert->mon_thousands_sep : ',');
  189. /* frac_digits in the C locale seems to return CHAR_MAX */
  190. /* best guess is 2 in this case I think */
  191. points = ((lconvert->frac_digits != CHAR_MAX) ? lconvert->frac_digits : 2); /* int_frac_digits? */
  192. convention = lconvert->n_sign_posn;
  193. dsymbol = ((*lconvert->mon_decimal_point != '') ? *lconvert->mon_decimal_point : '.');
  194. csymbol = ((*lconvert->currency_symbol != '') ? lconvert->currency_symbol : "$");
  195. nsymbol = ((*lconvert->negative_sign != '') ? lconvert->negative_sign : "-");
  196. #else
  197. mon_group = 3;
  198. comma = ',';
  199. csymbol = "$";
  200. dsymbol = '.';
  201. nsymbol = "-";
  202. points = 2;
  203. convention = 0;
  204. #endif
  205. point_pos = LAST_DIGIT - points;
  206. /* We're playing a little fast and loose with this.  Shoot me. */
  207. /* Not me, that was the other guy. Haven't fixed it yet - thomas */
  208. if (!mon_group || mon_group == CHAR_MAX)
  209. mon_group = 3;
  210. /* allow more than three decimal points and separate them */
  211. if (comma)
  212. {
  213. point_pos -= (points - 1) / mon_group;
  214. comma_position = point_pos % (mon_group + 1);
  215. }
  216. /* we work with positive amounts and add the minus sign at the end */
  217. if (value < 0)
  218. {
  219. minus = 1;
  220. value *= -1;
  221. }
  222. /* allow for trailing negative strings */
  223. MemSet(buf, ' ', CASH_BUFSZ);
  224. buf[TERMINATOR] = buf[LAST_PAREN] = '';
  225. while (value || count > (point_pos - 2))
  226. {
  227. if (points && count == point_pos)
  228. buf[count--] = dsymbol;
  229. else if (comma && count % (mon_group + 1) == comma_position)
  230. buf[count--] = comma;
  231. buf[count--] = (value % 10) + '0';
  232. value /= 10;
  233. }
  234. strncpy((buf + count - strlen(csymbol) + 1), csymbol, strlen(csymbol));
  235. count -= strlen(csymbol) - 1;
  236. if (buf[LAST_DIGIT] == ',')
  237. buf[LAST_DIGIT] = buf[LAST_PAREN];
  238. /* see if we need to signify negative amount */
  239. if (minus)
  240. {
  241. if (!PointerIsValid(result = palloc(CASH_BUFSZ + 2 - count + strlen(nsymbol))))
  242. elog(ERROR, "Memory allocation failed, can't output cash", NULL);
  243. /* Position code of 0 means use parens */
  244. if (convention == 0)
  245. sprintf(result, "(%s)", buf + count);
  246. else if (convention == 2)
  247. sprintf(result, "%s%s", buf + count, nsymbol);
  248. else
  249. sprintf(result, "%s%s", nsymbol, buf + count);
  250. }
  251. else
  252. {
  253. if (!PointerIsValid(result = palloc(CASH_BUFSZ + 2 - count)))
  254. elog(ERROR, "Memory allocation failed, can't output cash", NULL);
  255. strcpy(result, buf + count);
  256. }
  257. return result;
  258. } /* cash_out() */
  259. bool
  260. cash_eq(Cash *c1, Cash *c2)
  261. {
  262. if (!PointerIsValid(c1) || !PointerIsValid(c2))
  263. return FALSE;
  264. return *c1 == *c2;
  265. } /* cash_eq() */
  266. bool
  267. cash_ne(Cash *c1, Cash *c2)
  268. {
  269. if (!PointerIsValid(c1) || !PointerIsValid(c2))
  270. return FALSE;
  271. return *c1 != *c2;
  272. } /* cash_ne() */
  273. bool
  274. cash_lt(Cash *c1, Cash *c2)
  275. {
  276. if (!PointerIsValid(c1) || !PointerIsValid(c2))
  277. return FALSE;
  278. return *c1 < *c2;
  279. } /* cash_lt() */
  280. bool
  281. cash_le(Cash *c1, Cash *c2)
  282. {
  283. if (!PointerIsValid(c1) || !PointerIsValid(c2))
  284. return FALSE;
  285. return *c1 <= *c2;
  286. } /* cash_le() */
  287. bool
  288. cash_gt(Cash *c1, Cash *c2)
  289. {
  290. if (!PointerIsValid(c1) || !PointerIsValid(c2))
  291. return FALSE;
  292. return *c1 > *c2;
  293. } /* cash_gt() */
  294. bool
  295. cash_ge(Cash *c1, Cash *c2)
  296. {
  297. if (!PointerIsValid(c1) || !PointerIsValid(c2))
  298. return FALSE;
  299. return *c1 >= *c2;
  300. } /* cash_ge() */
  301. /* cash_pl()
  302.  * Add two cash values.
  303.  */
  304. Cash *
  305. cash_pl(Cash *c1, Cash *c2)
  306. {
  307. Cash    *result;
  308. if (!PointerIsValid(c1) || !PointerIsValid(c2))
  309. return NULL;
  310. if (!PointerIsValid(result = palloc(sizeof(Cash))))
  311. elog(ERROR, "Memory allocation failed, can't add cash", NULL);
  312. *result = (*c1 + *c2);
  313. return result;
  314. } /* cash_pl() */
  315. /* cash_mi()
  316.  * Subtract two cash values.
  317.  */
  318. Cash *
  319. cash_mi(Cash *c1, Cash *c2)
  320. {
  321. Cash    *result;
  322. if (!PointerIsValid(c1) || !PointerIsValid(c2))
  323. return NULL;
  324. if (!PointerIsValid(result = palloc(sizeof(Cash))))
  325. elog(ERROR, "Memory allocation failed, can't subtract cash", NULL);
  326. *result = (*c1 - *c2);
  327. return result;
  328. } /* cash_mi() */
  329. /* cash_mul_flt8()
  330.  * Multiply cash by float8.
  331.  */
  332. Cash *
  333. cash_mul_flt8(Cash *c, float8 *f)
  334. {
  335. Cash    *result;
  336. if (!PointerIsValid(f) || !PointerIsValid(c))
  337. return NULL;
  338. if (!PointerIsValid(result = palloc(sizeof(Cash))))
  339. elog(ERROR, "Memory allocation failed, can't multiply cash", NULL);
  340. *result = ((*f) * (*c));
  341. return result;
  342. } /* cash_mul_flt8() */
  343. /* flt8_mul_cash()
  344.  * Multiply float8 by cash.
  345.  */
  346. Cash *
  347. flt8_mul_cash(float8 *f, Cash *c)
  348. {
  349. return cash_mul_flt8(c, f);
  350. } /* flt8_mul_cash() */
  351. /* cash_div_flt8()
  352.  * Divide cash by float8.
  353.  *
  354.  * XXX Don't know if rounding or truncating is correct behavior.
  355.  * Round for now. - tgl 97/04/15
  356.  */
  357. Cash *
  358. cash_div_flt8(Cash *c, float8 *f)
  359. {
  360. Cash    *result;
  361. if (!PointerIsValid(f) || !PointerIsValid(c))
  362. return NULL;
  363. if (!PointerIsValid(result = palloc(sizeof(Cash))))
  364. elog(ERROR, "Memory allocation failed, can't divide cash", NULL);
  365. if (*f == 0.0)
  366. elog(ERROR, "cash_div:  divide by 0.0 error");
  367. *result = rint(*c / *f);
  368. return result;
  369. } /* cash_div_flt8() */
  370. /* cash_mul_flt4()
  371.  * Multiply cash by float4.
  372.  */
  373. Cash *
  374. cash_mul_flt4(Cash *c, float4 *f)
  375. {
  376. Cash    *result;
  377. if (!PointerIsValid(f) || !PointerIsValid(c))
  378. return NULL;
  379. if (!PointerIsValid(result = palloc(sizeof(Cash))))
  380. elog(ERROR, "Memory allocation failed, can't multiply cash", NULL);
  381. *result = ((*f) * (*c));
  382. return result;
  383. } /* cash_mul_flt4() */
  384. /* flt4_mul_cash()
  385.  * Multiply float4 by float4.
  386.  */
  387. Cash *
  388. flt4_mul_cash(float4 *f, Cash *c)
  389. {
  390. return cash_mul_flt4(c, f);
  391. } /* flt4_mul_cash() */
  392. /* cash_div_flt4()
  393.  * Divide cash by float4.
  394.  *
  395.  * XXX Don't know if rounding or truncating is correct behavior.
  396.  * Round for now. - tgl 97/04/15
  397.  */
  398. Cash *
  399. cash_div_flt4(Cash *c, float4 *f)
  400. {
  401. Cash    *result;
  402. if (!PointerIsValid(f) || !PointerIsValid(c))
  403. return NULL;
  404. if (!PointerIsValid(result = palloc(sizeof(Cash))))
  405. elog(ERROR, "Memory allocation failed, can't divide cash", NULL);
  406. if (*f == 0.0)
  407. elog(ERROR, "cash_div:  divide by 0.0 error");
  408. *result = rint(*c / *f);
  409. return result;
  410. } /* cash_div_flt4() */
  411. /* cash_mul_int4()
  412.  * Multiply cash by int4.
  413.  */
  414. Cash *
  415. cash_mul_int4(Cash *c, int4 i)
  416. {
  417. Cash    *result;
  418. if (!PointerIsValid(c))
  419. return NULL;
  420. if (!PointerIsValid(result = palloc(sizeof(Cash))))
  421. elog(ERROR, "Memory allocation failed, can't multiply cash", NULL);
  422. *result = ((i) * (*c));
  423. return result;
  424. } /* cash_mul_int4() */
  425. /* int4_mul_cash()
  426.  * Multiply int4 by cash.
  427.  */
  428. Cash *
  429. int4_mul_cash(int4 i, Cash *c)
  430. {
  431. return cash_mul_int4(c, i);
  432. } /* int4_mul_cash() */
  433. /* cash_div_int4()
  434.  * Divide cash by 4-byte integer.
  435.  *
  436.  * XXX Don't know if rounding or truncating is correct behavior.
  437.  * Round for now. - tgl 97/04/15
  438.  */
  439. Cash *
  440. cash_div_int4(Cash *c, int4 i)
  441. {
  442. Cash    *result;
  443. if (!PointerIsValid(c))
  444. return NULL;
  445. if (!PointerIsValid(result = palloc(sizeof(Cash))))
  446. elog(ERROR, "Memory allocation failed, can't divide cash", NULL);
  447. if (i == 0)
  448. elog(ERROR, "cash_idiv:  divide by 0 error");
  449. *result = rint(*c / i);
  450. return result;
  451. } /* cash_div_int4() */
  452. /* cash_mul_int2()
  453.  * Multiply cash by int2.
  454.  */
  455. Cash *
  456. cash_mul_int2(Cash *c, int2 s)
  457. {
  458. Cash    *result;
  459. if (!PointerIsValid(c))
  460. return NULL;
  461. if (!PointerIsValid(result = palloc(sizeof(Cash))))
  462. elog(ERROR, "Memory allocation failed, can't multiply cash", NULL);
  463. *result = ((s) * (*c));
  464. return result;
  465. } /* cash_mul_int2() */
  466. /* int2_mul_cash()
  467.  * Multiply int2 by cash.
  468.  */
  469. Cash *
  470. int2_mul_cash(int2 s, Cash *c)
  471. {
  472. return cash_mul_int2(c, s);
  473. } /* int2_mul_cash() */
  474. /* cash_div_int2()
  475.  * Divide cash by int2.
  476.  *
  477.  * XXX Don't know if rounding or truncating is correct behavior.
  478.  * Round for now. - tgl 97/04/15
  479.  */
  480. Cash *
  481. cash_div_int2(Cash *c, int2 s)
  482. {
  483. Cash    *result;
  484. if (!PointerIsValid(c))
  485. return NULL;
  486. if (!PointerIsValid(result = palloc(sizeof(Cash))))
  487. elog(ERROR, "Memory allocation failed, can't divide cash", NULL);
  488. if (s == 0)
  489. elog(ERROR, "cash_div:  divide by 0 error");
  490. *result = rint(*c / s);
  491. return result;
  492. } /* cash_div_int2() */
  493. /* cashlarger()
  494.  * Return larger of two cash values.
  495.  */
  496. Cash *
  497. cashlarger(Cash *c1, Cash *c2)
  498. {
  499. Cash    *result;
  500. if (!PointerIsValid(c1) || !PointerIsValid(c2))
  501. return NULL;
  502. if (!PointerIsValid(result = palloc(sizeof(Cash))))
  503. elog(ERROR, "Memory allocation failed, can't return larger cash", NULL);
  504. *result = ((*c1 > *c2) ? *c1 : *c2);
  505. return result;
  506. } /* cashlarger() */
  507. /* cashsmaller()
  508.  * Return smaller of two cash values.
  509.  */
  510. Cash *
  511. cashsmaller(Cash *c1, Cash *c2)
  512. {
  513. Cash    *result;
  514. if (!PointerIsValid(c1) || !PointerIsValid(c2))
  515. return NULL;
  516. if (!PointerIsValid(result = palloc(sizeof(Cash))))
  517. elog(ERROR, "Memory allocation failed, can't return smaller cash", NULL);
  518. *result = ((*c1 < *c2) ? *c1 : *c2);
  519. return result;
  520. } /* cashsmaller() */
  521. /* cash_words_out()
  522.  * This converts a int4 as well but to a representation using words
  523.  * Obviously way North American centric - sorry
  524.  */
  525. text *
  526. cash_words_out(Cash *value)
  527. {
  528. static char buf[128];
  529. char    *p = buf;
  530. Cash m0;
  531. Cash m1;
  532. Cash m2;
  533. Cash m3;
  534. text    *result;
  535. /* work with positive numbers */
  536. if (*value < 0)
  537. {
  538. *value *= -1;
  539. strcpy(buf, "minus ");
  540. p += 6;
  541. }
  542. else
  543. *buf = 0;
  544. m0 = *value % 100; /* cents */
  545. m1 = (*value / 100) % 1000; /* hundreds */
  546. m2 = (*value / 100000) % 1000; /* thousands */
  547. m3 = *value / 100000000 % 1000; /* millions */
  548. if (m3)
  549. {
  550. strcat(buf, num_word(m3));
  551. strcat(buf, " million ");
  552. }
  553. if (m2)
  554. {
  555. strcat(buf, num_word(m2));
  556. strcat(buf, " thousand ");
  557. }
  558. if (m1)
  559. strcat(buf, num_word(m1));
  560. if (!*p)
  561. strcat(buf, "zero");
  562. strcat(buf, (int) (*value / 100) == 1 ? " dollar and " : " dollars and ");
  563. strcat(buf, num_word(m0));
  564. strcat(buf, m0 == 1 ? " cent" : " cents");
  565. /* capitalize output */
  566. *buf = toupper(*buf);
  567. /* make a text type for output */
  568. result = (text *) palloc(strlen(buf) + VARHDRSZ);
  569. VARSIZE(result) = strlen(buf) + VARHDRSZ;
  570. StrNCpy(VARDATA(result), buf, strlen(buf));
  571. return result;
  572. } /* cash_words_out() */
  573. /*************************************************************************
  574.  * Private routines
  575.  ************************************************************************/
  576. static const char *
  577. num_word(Cash value)
  578. {
  579. static char buf[128];
  580. static const char *small[] = {
  581. "zero", "one", "two", "three", "four", "five", "six", "seven",
  582. "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
  583. "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
  584. "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety"
  585. };
  586. const char **big = small + 18;
  587. int tu = value % 100;
  588. /* deal with the simple cases first */
  589. if (value <= 20)
  590. return small[value];
  591. /* is it an even multiple of 100? */
  592. if (!tu)
  593. {
  594. sprintf(buf, "%s hundred", small[value / 100]);
  595. return buf;
  596. }
  597. /* more than 99? */
  598. if (value > 99)
  599. {
  600. /* is it an even multiple of 10 other than 10? */
  601. if (value % 10 == 0 && tu > 10)
  602. sprintf(buf, "%s hundred %s",
  603. small[value / 100], big[tu / 10]);
  604. else if (tu < 20)
  605. sprintf(buf, "%s hundred and %s",
  606. small[value / 100], small[tu]);
  607. else
  608. sprintf(buf, "%s hundred %s %s",
  609. small[value / 100], big[tu / 10], small[tu % 10]);
  610. }
  611. else
  612. {
  613. /* is it an even multiple of 10 other than 10? */
  614. if (value % 10 == 0 && tu > 10)
  615. sprintf(buf, "%s", big[tu / 10]);
  616. else if (tu < 20)
  617. sprintf(buf, "%s", small[tu]);
  618. else
  619. sprintf(buf, "%s %s", big[tu / 10], small[tu % 10]);
  620. }
  621. return buf;
  622. } /* num_word() */