Esempio n. 1
0
/* int8_cash()
 * Convert int8 (bigint) to cash
 */
Datum
int8_cash(PG_FUNCTION_ARGS)
{
	int64		amount = PG_GETARG_INT64(0);
	Cash		result;
	int			fpoint;
	int64		scale;
	int			i;
	struct lconv *lconvert = PGLC_localeconv();

	/* see comments about frac_digits in cash_in() */
	fpoint = lconvert->frac_digits;
	if (fpoint < 0 || fpoint > 10)
		fpoint = 2;

	/* compute required scale factor */
	scale = 1;
	for (i = 0; i < fpoint; i++)
		scale *= 10;

	/* compute amount * scale, checking for overflow */
	result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
											   Int64GetDatum(scale)));

	PG_RETURN_CASH(result);
}
Esempio n. 2
0
/* cash_numeric()
 * Convert cash to numeric.
 */
Datum
cash_numeric(PG_FUNCTION_ARGS)
{
	Cash		money = PG_GETARG_CASH(0);
	Numeric		result;
	int			fpoint;
	int64		scale;
	int			i;
	Datum		amount;
	Datum		numeric_scale;
	Datum		quotient;
	struct lconv *lconvert = PGLC_localeconv();

	/* see comments about frac_digits in cash_in() */
	fpoint = lconvert->frac_digits;
	if (fpoint < 0 || fpoint > 10)
		fpoint = 2;

	/* compute required scale factor */
	scale = 1;
	for (i = 0; i < fpoint; i++)
		scale *= 10;

	/* form the result as money / scale */
	amount = DirectFunctionCall1(int8_numeric, Int64GetDatum(money));
	numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
	quotient = DirectFunctionCall2(numeric_div, amount, numeric_scale);

	/* forcibly round to exactly the intended number of digits */
	result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
												 quotient,
												 Int32GetDatum(fpoint)));

	PG_RETURN_NUMERIC(result);
}
Esempio n. 3
0
/* numeric_cash()
 * Convert numeric to cash.
 */
Datum
numeric_cash(PG_FUNCTION_ARGS)
{
	Datum		amount = PG_GETARG_DATUM(0);
	Cash		result;
	int			fpoint;
	int64		scale;
	int			i;
	Datum		numeric_scale;
	struct lconv *lconvert = PGLC_localeconv();

	/* see comments about frac_digits in cash_in() */
	fpoint = lconvert->frac_digits;
	if (fpoint < 0 || fpoint > 10)
		fpoint = 2;

	/* compute required scale factor */
	scale = 1;
	for (i = 0; i < fpoint; i++)
		scale *= 10;

	/* multiply the input amount by scale factor */
	numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
	amount = DirectFunctionCall2(numeric_mul, amount, numeric_scale);

	/* note that numeric_int8 will round to nearest integer for us */
	result = DatumGetInt64(DirectFunctionCall1(numeric_int8, amount));

	PG_RETURN_CASH(result);
}
Esempio n. 4
0
Datum
orafce_to_char_numeric(PG_FUNCTION_ARGS)
{
	Numeric		arg0 = PG_GETARG_NUMERIC(0);
	StringInfo	buf = makeStringInfo();
	struct lconv *lconv = PGLC_localeconv();
	char	   *p;
	char       *decimal = NULL;

	appendStringInfoString(buf, DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(arg0))));

	for (p = buf->data; *p; p++)
		if (*p == '.')
		{
			*p = lconv->decimal_point[0];
			decimal = p; /* save decimal point position for the next loop */
		}

	/* Simulate the default Oracle to_char template (TM9 - Text Minimum)
	   by removing unneeded digits after the decimal point;
	   if no digits are left, then remove the decimal point too
	*/
	for (p = buf->data + buf->len - 1; decimal && p >= decimal; p--)
	{
		if (*p == '0' || *p == lconv->decimal_point[0])
			*p = 0;
		else
			break; /* non-zero digit found, exit the loop */
	}

	PG_RETURN_TEXT_P(cstring_to_text(buf->data));
}
Esempio n. 5
0
File: convert.c Progetto: 50wu/gpdb
Datum
orafce_to_char_numeric(PG_FUNCTION_ARGS)
{
	Numeric		arg0 = PG_GETARG_NUMERIC(0);
	StringInfo	buf = makeStringInfo();
	struct lconv *lconv = PGLC_localeconv();
	char	   *p;

	appendStringInfoString(buf, DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(arg0))));

	for (p = buf->data; *p; p++)
		if (*p == '.')
			*p = lconv->decimal_point[0];

	PG_RETURN_TEXT_P(cstring_to_text(buf->data));
}
Esempio n. 6
0
File: convert.c Progetto: 50wu/gpdb
Datum
orafce_to_char_float8(PG_FUNCTION_ARGS)
{
	float8		arg0 = PG_GETARG_FLOAT8(0);
	StringInfo	buf = makeStringInfo();
	struct lconv *lconv = PGLC_localeconv();
	char	   *p;

	appendStringInfo(buf, "%f", arg0);

	for (p = buf->data; *p; p++)
		if (*p == '.')
			*p = lconv->decimal_point[0];

	PG_RETURN_TEXT_P(cstring_to_text(buf->data));
}
Esempio n. 7
0
File: convert.c Progetto: 50wu/gpdb
Datum
orafce_to_number(PG_FUNCTION_ARGS)
{
	text	   *arg0 = PG_GETARG_TEXT_PP(0);
	char	   *buf;
	struct lconv *lconv = PGLC_localeconv();
	Numeric		res;
	char	   *p;

	buf = text_to_cstring(arg0);

	for (p = buf; *p; p++)
		if (*p == lconv->decimal_point[0] && lconv->decimal_point[0])
			*p = '.';
		else if (*p == lconv->thousands_sep[0] && lconv->thousands_sep[0])
			*p = ',';

	res = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(buf), 0, -1));

	PG_RETURN_NUMERIC(res);
}
Esempio n. 8
0
/* 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 multibyte.
 *	- thomas 1998-03-01
 */
Datum
cash_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	   *csymbol;
    char		dsymbol,
                ssymbol,
                psymbol,
                *nsymbol;

    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 : '.');
    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 : "-");

#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 */
    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 int4 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;

            /* "thousands" separator? then skip... */
        }
        else if (*s == ssymbol)
        {

        }
        else
        {
            /* round off */
            if (isdigit((unsigned char) *s) && *s >= '5')
                value++;

            /* adjust for less than required decimal places */
            for (; dec < fpoint; dec++)
                value *= 10;

            break;
        }
    }

    while (isspace((unsigned char) *s) || *s == '0' || *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);
}
Esempio n. 9
0
/* 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
 */
Datum
cash_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			comma_position = 0;
    int			points,
                mon_group;
    char		comma;
    char	   *csymbol,
               dsymbol,
               *nsymbol;
    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;

    comma = ((*lconvert->mon_thousands_sep != '\0') ? *lconvert->mon_thousands_sep : ',');
    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 : "-");

    point_pos = LAST_DIGIT - points;

    /* 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 = -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 (comma && count % (mon_group + 1) == comma_position)
            buf[count--] = comma;

        buf[count--] = ((unsigned int) value % 10) + '0';
        value = ((unsigned int) 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))))
            ereport(ERROR,
                    (errcode(ERRCODE_OUT_OF_MEMORY),
                     errmsg("out of memory")));

        /* 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)))
            ereport(ERROR,
                    (errcode(ERRCODE_OUT_OF_MEMORY),
                     errmsg("out of memory")));

        strcpy(result, buf + count);
    }

    PG_RETURN_CSTRING(result);
}
Esempio n. 10
0
/* cash_in()
 * Convert a string to a cash data type.
 * Format is [$]###[,]###[.##]
 * Examples: 123.45 $123.45 $123,456.78
 *
 */
Datum
cash_in(PG_FUNCTION_ARGS)
{
	char	   *str = PG_GETARG_CSTRING(0);
	Cash		result;
	Cash		value = 0;
	Cash		dec = 0;
	Cash		sgn = 1;
	bool		seen_dot = false;
	const char *s = str;
	int			fpoint;
	char		dsymbol;
	const char *ssymbol,
			   *psymbol,
			   *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 */

	/* we restrict dsymbol to be a single byte, but not the other symbols */
	if (*lconvert->mon_decimal_point != '\0' &&
		lconvert->mon_decimal_point[1] == '\0')
		dsymbol = *lconvert->mon_decimal_point;
	else
		dsymbol = '.';
	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 '%s'; currency '%s'; positive '%s'; 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);
	while (isspace((unsigned char) *s))
		s++;

#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);
	}
	else if (*s == '(')
	{
		sgn = -1;
		s++;
	}
	else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
		s += strlen(psymbol);

#ifdef CASHDEBUG
	printf("cashin- string is '%s'\n", s);
#endif

	/* allow whitespace and currency symbol after the sign, too */
	while (isspace((unsigned char) *s))
		s++;
	if (strncmp(s, csymbol, strlen(csymbol)) == 0)
		s += strlen(csymbol);
	while (isspace((unsigned char) *s))
		s++;

#ifdef CASHDEBUG
	printf("cashin- string is '%s'\n", s);
#endif

	/*
	 * We accumulate the absolute amount in "value" and then apply the sign at
	 * the end.  (The sign can appear before or after the digits, so it would
	 * be more complicated to do otherwise.)  Because of the larger range of
	 * negative signed integers, we build "value" in the negative and then
	 * flip the sign at the end, catching most-negative-number overflow if
	 * necessary.
	 */

	for (; *s; s++)
	{
		/*
		 * We look for digits as long as we have found less than the required
		 * number of decimal places.
		 */
		if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
		{
			int8		digit = *s - '0';

			if (pg_mul_s64_overflow(value, 10, &value) ||
				pg_sub_s64_overflow(value, digit, &value))
				ereport(ERROR,
						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
						 errmsg("value \"%s\" is out of range for type %s",
								str, "money")));

			if (seen_dot)
				dec++;
		}
		/* decimal point? then start counting fractions... */
		else if (*s == dsymbol && !seen_dot)
		{
			seen_dot = true;
		}
		/* ignore if "thousands" separator, else we're done */
		else if (strncmp(s, ssymbol, strlen(ssymbol)) == 0)
			s += strlen(ssymbol) - 1;
		else
			break;
	}

	/* round off if there's another digit */
	if (isdigit((unsigned char) *s) && *s >= '5')
	{
		/* remember we build the value in the negative */
		if (pg_sub_s64_overflow(value, 1, &value))
			ereport(ERROR,
					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
					 errmsg("value \"%s\" is out of range for type %s",
							str, "money")));
	}

	/* adjust for less than required decimal places */
	for (; dec < fpoint; dec++)
	{
		if (pg_mul_s64_overflow(value, 10, &value))
			ereport(ERROR,
					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
					 errmsg("value \"%s\" is out of range for type %s",
							str, "money")));
	}

	/*
	 * should only be trailing digits followed by whitespace, right paren,
	 * trailing sign, and/or trailing currency symbol
	 */
	while (isdigit((unsigned char) *s))
		s++;

	while (*s)
	{
		if (isspace((unsigned char) *s) || *s == ')')
			s++;
		else if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
		{
			sgn = -1;
			s += strlen(nsymbol);
		}
		else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
			s += strlen(psymbol);
		else if (strncmp(s, csymbol, strlen(csymbol)) == 0)
			s += strlen(csymbol);
		else
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid input syntax for type %s: \"%s\"",
							"money", str)));
	}

	/*
	 * If the value is supposed to be positive, flip the sign, but check for
	 * the most negative number.
	 */
	if (sgn > 0)
	{
		if (value == PG_INT64_MIN)
			ereport(ERROR,
					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
					 errmsg("value \"%s\" is out of range for type %s",
							str, "money")));
		result = -value;
	}
	else
		result = value;

#ifdef CASHDEBUG
	printf("cashin- result is " INT64_FORMAT "\n", result);
#endif

	PG_RETURN_CASH(result);
}
Esempio n. 11
0
/* cash_out()
 * Function to convert cash to a dollars and cents representation, using
 * the lc_monetary locale's formatting.
 */
Datum
cash_out(PG_FUNCTION_ARGS)
{
	Cash		value = PG_GETARG_CASH(0);
	char	   *result;
	char		buf[128];
	char	   *bufptr;
	int			digit_pos;
	int			points,
				mon_group;
	char		dsymbol;
	const char *ssymbol,
			   *csymbol,
			   *signsymbol;
	char		sign_posn,
				cs_precedes,
				sep_by_space;
	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;

	/* we restrict dsymbol to be a single byte, but not the other symbols */
	if (*lconvert->mon_decimal_point != '\0' &&
		lconvert->mon_decimal_point[1] == '\0')
		dsymbol = *lconvert->mon_decimal_point;
	else
		dsymbol = '.';
	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 : "$";

	if (value < 0)
	{
		/* make the amount positive for digit-reconstruction loop */
		value = -value;
		/* set up formatting data */
		signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
		sign_posn = lconvert->n_sign_posn;
		cs_precedes = lconvert->n_cs_precedes;
		sep_by_space = lconvert->n_sep_by_space;
	}
	else
	{
		signsymbol = lconvert->positive_sign;
		sign_posn = lconvert->p_sign_posn;
		cs_precedes = lconvert->p_cs_precedes;
		sep_by_space = lconvert->p_sep_by_space;
	}

	/* we build the digits+decimal-point+sep string right-to-left in buf[] */
	bufptr = buf + sizeof(buf) - 1;
	*bufptr = '\0';

	/*
	 * Generate digits till there are no non-zero digits left and we emitted
	 * at least one to the left of the decimal point.  digit_pos is the
	 * current digit position, with zero as the digit just left of the decimal
	 * point, increasing to the right.
	 */
	digit_pos = points;
	do
	{
		if (points && digit_pos == 0)
		{
			/* insert decimal point, but not if value cannot be fractional */
			*(--bufptr) = dsymbol;
		}
		else if (digit_pos < 0 && (digit_pos % mon_group) == 0)
		{
			/* insert thousands sep, but only to left of radix point */
			bufptr -= strlen(ssymbol);
			memcpy(bufptr, ssymbol, strlen(ssymbol));
		}

		*(--bufptr) = ((uint64) value % 10) + '0';
		value = ((uint64) value) / 10;
		digit_pos--;
	} while (value || digit_pos >= 0);

	/*----------
	 * Now, attach currency symbol and sign symbol in the correct order.
	 *
	 * The POSIX spec defines these values controlling this code:
	 *
	 * p/n_sign_posn:
	 *	0	Parentheses enclose the quantity and the currency_symbol.
	 *	1	The sign string precedes the quantity and the currency_symbol.
	 *	2	The sign string succeeds the quantity and the currency_symbol.
	 *	3	The sign string precedes the currency_symbol.
	 *	4	The sign string succeeds the currency_symbol.
	 *
	 * p/n_cs_precedes: 0 means currency symbol after value, else before it.
	 *
	 * p/n_sep_by_space:
	 *	0	No <space> separates the currency symbol and value.
	 *	1	If the currency symbol and sign string are adjacent, a <space>
	 *		separates them from the value; otherwise, a <space> separates
	 *		the currency symbol from the value.
	 *	2	If the currency symbol and sign string are adjacent, a <space>
	 *		separates them; otherwise, a <space> separates the sign string
	 *		from the value.
	 *----------
	 */
	switch (sign_posn)
	{
		case 0:
			if (cs_precedes)
				result = psprintf("(%s%s%s)",
								  csymbol,
								  (sep_by_space == 1) ? " " : "",
								  bufptr);
			else
				result = psprintf("(%s%s%s)",
								  bufptr,
								  (sep_by_space == 1) ? " " : "",
								  csymbol);
			break;
		case 1:
		default:
			if (cs_precedes)
				result = psprintf("%s%s%s%s%s",
								  signsymbol,
								  (sep_by_space == 2) ? " " : "",
								  csymbol,
								  (sep_by_space == 1) ? " " : "",
								  bufptr);
			else
				result = psprintf("%s%s%s%s%s",
								  signsymbol,
								  (sep_by_space == 2) ? " " : "",
								  bufptr,
								  (sep_by_space == 1) ? " " : "",
								  csymbol);
			break;
		case 2:
			if (cs_precedes)
				result = psprintf("%s%s%s%s%s",
								  csymbol,
								  (sep_by_space == 1) ? " " : "",
								  bufptr,
								  (sep_by_space == 2) ? " " : "",
								  signsymbol);
			else
				result = psprintf("%s%s%s%s%s",
								  bufptr,
								  (sep_by_space == 1) ? " " : "",
								  csymbol,
								  (sep_by_space == 2) ? " " : "",
								  signsymbol);
			break;
		case 3:
			if (cs_precedes)
				result = psprintf("%s%s%s%s%s",
								  signsymbol,
								  (sep_by_space == 2) ? " " : "",
								  csymbol,
								  (sep_by_space == 1) ? " " : "",
								  bufptr);
			else
				result = psprintf("%s%s%s%s%s",
								  bufptr,
								  (sep_by_space == 1) ? " " : "",
								  signsymbol,
								  (sep_by_space == 2) ? " " : "",
								  csymbol);
			break;
		case 4:
			if (cs_precedes)
				result = psprintf("%s%s%s%s%s",
								  csymbol,
								  (sep_by_space == 2) ? " " : "",
								  signsymbol,
								  (sep_by_space == 1) ? " " : "",
								  bufptr);
			else
				result = psprintf("%s%s%s%s%s",
								  bufptr,
								  (sep_by_space == 1) ? " " : "",
								  csymbol,
								  (sep_by_space == 2) ? " " : "",
								  signsymbol);
			break;
	}

	PG_RETURN_CSTRING(result);
}
Esempio n. 12
0
/* 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
 */
Datum
cash_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);
}
Esempio n. 13
0
File: cash.c Progetto: GisKook/Gis
/* cash_in()
 * Convert a string to a cash data type.
 * Format is [$]###[,]###[.##]
 * Examples: 123.45 $123.45 $123,456.78
 *
 */
Datum
cash_in(PG_FUNCTION_ARGS)
{
	char	   *str = PG_GETARG_CSTRING(0);
	Cash		result;
	Cash		value = 0;
	Cash		dec = 0;
	Cash		sgn = 1;
	bool		seen_dot = false;
	const char *s = str;
	int			fpoint;
	char		dsymbol;
	const char *ssymbol,
			   *psymbol,
			   *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 */

	/* we restrict dsymbol to be a single byte, but not the other symbols */
	if (*lconvert->mon_decimal_point != '\0' &&
		lconvert->mon_decimal_point[1] == '\0')
		dsymbol = *lconvert->mon_decimal_point;
	else
		dsymbol = '.';
	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 '%s'; currency '%s'; positive '%s'; 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);
	}
	else if (*s == '(')
	{
		sgn = -1;
		s++;
	}
	else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
		s += strlen(psymbol);

#ifdef CASHDEBUG
	printf("cashin- string is '%s'\n", s);
#endif

	/* allow whitespace and currency symbol after the sign, too */
	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; s++)
	{
		/* we look for digits as long as we have found less */
		/* than the required number of decimal places */
		if (isdigit((unsigned char) *s) && (!seen_dot || 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 = true;
		}
		/* ignore if "thousands" separator, else we're done */
		else if (strncmp(s, ssymbol, strlen(ssymbol)) == 0)
			s += strlen(ssymbol) - 1;
		else
			break;
	}

	/* round off if there's another digit */
	if (isdigit((unsigned char) *s) && *s >= '5')
		value++;

	/* adjust for less than required decimal places */
	for (; dec < fpoint; dec++)
		value *= 10;

	/*
	 * should only be trailing digits followed by whitespace, right paren,
	 * or possibly a trailing minus sign
	 */
	while (isdigit((unsigned char) *s))
		s++;
	while (*s)
	{
		if (isspace((unsigned char) *s) || *s == ')')
			s++;
		else if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
		{
			sgn = -1;
			s += strlen(nsymbol);
		}
		else
			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 " INT64_FORMAT "\n", result);
#endif

	PG_RETURN_CASH(result);
}
Esempio n. 14
0
File: cash.c Progetto: GisKook/Gis
/* cash_out()
 * Function to convert cash to a dollars and cents representation, using
 * the lc_monetary locale's formatting.
 */
Datum
cash_out(PG_FUNCTION_ARGS)
{
	Cash		value = PG_GETARG_CASH(0);
	char	   *result;
	char		buf[128];
	char	   *bufptr;
	bool		minus = false;
	int			digit_pos;
	int			points,
				mon_group;
	char		dsymbol;
	const char *ssymbol,
			   *csymbol,
			   *nsymbol;
	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;

	/* we restrict dsymbol to be a single byte, but not the other symbols */
	if (*lconvert->mon_decimal_point != '\0' &&
		lconvert->mon_decimal_point[1] == '\0')
		dsymbol = *lconvert->mon_decimal_point;
	else
		dsymbol = '.';
	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 : "-";

	/* we work with positive amounts and add the minus sign at the end */
	if (value < 0)
	{
		minus = true;
		value = -value;
	}

	/* we build the result string right-to-left in buf[] */
	bufptr = buf + sizeof(buf) - 1;
	*bufptr = '\0';

	/*
	 * Generate digits till there are no non-zero digits left and we emitted
	 * at least one to the left of the decimal point.  digit_pos is the
	 * current digit position, with zero as the digit just left of the decimal
	 * point, increasing to the right.
	 */
	digit_pos = points;
	do
	{
		if (points && digit_pos == 0)
		{
			/* insert decimal point */
			*(--bufptr) = dsymbol;
		}
		else if (digit_pos < points && (digit_pos % mon_group) == 0)
		{
			/* insert thousands sep */
			bufptr -= strlen(ssymbol);
			memcpy(bufptr, ssymbol, strlen(ssymbol));
		}

		*(--bufptr) = ((uint64) value % 10) + '0';
		value = ((uint64) value) / 10;
		digit_pos--;
	} while (value || digit_pos >= 0);

	/* prepend csymbol */
	bufptr -= strlen(csymbol);
	memcpy(bufptr, csymbol, strlen(csymbol));

	/* see if we need to signify negative amount */
	if (minus)
	{
		result = palloc(strlen(bufptr) + strlen(nsymbol) + 3);

		/* Position code of 0 means use parens */
		if (convention == 0)
			sprintf(result, "(%s)", bufptr);
		else if (convention == 2)
			sprintf(result, "%s%s", bufptr, nsymbol);
		else
			sprintf(result, "%s%s", nsymbol, bufptr);
	}
	else
	{
		/* just emit what we have */
		result = pstrdup(bufptr);
	}

	PG_RETURN_CSTRING(result);
}