2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore * Copyright 2013 Garrett D'Amore <garrett@damore.org>
6b5e5868e7ebf1aff3a5abd7d0c4ef0e5fbf3648Garrett D'Amore * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org>
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * All rights reserved.
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * Redistribution and use in source and binary forms, with or without
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * modification, are permitted provided that the following conditions
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * 1. Redistributions of source code must retain the above copyright
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * notice, this list of conditions and the following disclaimer.
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * 2. Redistributions in binary form must reproduce the above copyright
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * notice, this list of conditions and the following disclaimer in the
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * documentation and/or other materials provided with the distribution.
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * SUCH DAMAGE.
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore/* internal flags */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore#define NEED_GROUPING 0x01 /* print digits grouped (default) */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore#define SIGN_POSN_USED 0x02 /* '+' or '(' usage flag */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore#define LOCALE_POSN 0x04 /* use locale defined +/- (default) */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore#define PARENTH_POSN 0x08 /* enclose negative amount in () */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore#define SUPRESS_CURR_SYMBOL 0x10 /* supress the currency from output */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore#define USE_INTL_CURRENCY 0x40 /* use international currency symbol */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore#define IS_NEGATIVE 0x80 /* is argument value negative ? */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore/* internal macros */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore while (i-- > 0) { \
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore (void) memcpy(bufend, thousands_sep, thousands_len); \
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amorestatic void setup_vars(const struct lc_monetary *, int, char *, char *, char *,
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore const char **);
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amorestatic int calc_left_pad(const struct lc_monetary *, int, const char *);
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amorestatic char *format_grouped_double(const struct lc_monetary *,
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore const struct lc_numeric *, double, int *, int, int, int);
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amorestrfmon_impl(char *_RESTRICT_KYWD s, size_t maxsize, locale_t loc,
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore const char *_RESTRICT_KYWD format, va_list ap)
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore const char *fmt; /* current format poistion pointer */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore char *asciivalue; /* formatted double pointer */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore char space_char = ' '; /* space after currency */
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore char cs_precedes; /* values from struct lc_monetary */
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore const struct lc_monetary *lmon; /* monetary structure */
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore const struct lc_numeric *lnum; /* numeric structure */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* pass nonformating characters AS IS */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* '%' found ! */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* "%%" mean just '%' */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* set up initial values */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore pad_char = ' '; /* padding character is "space" */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore left_prec = -1; /* no left precision specified */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore right_prec = -1; /* no right precision specified */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore value = 0; /* we have no value to print now */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore switch (*++fmt) {
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* field Width */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * Do we have enough space to put number with
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * required width ?
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore if ((unsigned int)width >= maxsize - (dst - s))
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* Left precision */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore if ((unsigned int)left_prec >= maxsize - (dst - s))
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* Right precision */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore if ((unsigned int)right_prec >= maxsize - (dst - s) -
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* Conversion Characters */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore switch (*fmt++) {
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore case 'i': /* use internaltion currency format */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore case 'n': /* use national currency format */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* required char missing or premature EOS */
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore /* by definition three letters followed by a space */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* value itself */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* detect sign */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* fill left_prec with amount of padding chars */
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore pad_size = calc_left_pad(lmon, (flags ^ IS_NEGATIVE),
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore calc_left_pad(lmon, flags, currency_symbol);
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore asciivalue = format_grouped_double(lmon, lnum, value, &flags,
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* to ENOMEM by malloc() */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* set some variables for later use */
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore setup_vars(lmon, flags, &cs_precedes, &sep_by_space,
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * Description of some LC_MONETARY's values:
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * p_cs_precedes & n_cs_precedes
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * = 1 - $currency_symbol precedes the value
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * for a monetary quantity with a non-negative value
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * = 0 - symbol succeeds the value
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * p_sep_by_space & n_sep_by_space
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * = 0 - no space separates $currency_symbol
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * from the value for a monetary quantity with a
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * non-negative value
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * = 1 - space separates the symbol from the value
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * = 2 - space separates the symbol and the sign string,
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * if adjacent.
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * p_sign_posn & n_sign_posn
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * = 0 - parentheses enclose the quantity and the
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * $currency_symbol
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * = 1 - the sign string precedes the quantity and the
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * $currency_symbol
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * = 2 - the sign string succeeds the quantity and the
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * $currency_symbol
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * = 3 - the sign string precedes the $currency_symbol
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * = 4 - the sign string succeeds the $currency_symbol
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore if (sign_posn == 0 && (flags & IS_NEGATIVE))
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore if ((sign_posn == 3 && sep_by_space == 2) ||
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore if (sign_posn == 0 && (flags & IS_NEGATIVE))
eda71b4a8fb1d0b34d0f08c47b43af49428d24c3Garrett D'Amore (void) memmove(tmpptr + width-pad_size, tmpptr,
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore return (dst - s - 1); /* size of put data except trailing '\0' */
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amorestrfmon(char *_RESTRICT_KYWD s, size_t maxsize,
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore ret = strfmon_impl(s, maxsize, uselocale(NULL), format, ap);
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amorestrfmon_l(char *_RESTRICT_KYWD s, size_t maxsize, locale_t loc,
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore ret = strfmon_impl(s, maxsize, loc, format, ap);
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amoresetup_vars(const struct lc_monetary *lmon, int flags, char *cs_precedes,
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore char *sep_by_space, char *sign_posn, const char **signstr)
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore if ((flags & IS_NEGATIVE) && (flags & USE_INTL_CURRENCY)) {
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore *sep_by_space = lmon->int_n_sep_by_space[0];
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore *signstr = (lmon->negative_sign[0] == '\0') ? "-" :
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore *sep_by_space = lmon->int_p_sep_by_space[0];
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore *sign_posn = (flags & PARENTH_POSN) ? 0 : lmon->n_sign_posn[0];
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore *signstr = (lmon->negative_sign[0] == '\0') ? "-" :
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore *sign_posn = (flags & PARENTH_POSN) ? 0 : lmon->p_sign_posn[0];
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore /* Set default values for unspecified information. */
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amorecalc_left_pad(const struct lc_monetary *lmon, int flags, const char *cur_symb)
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore setup_vars(lmon, flags, &cs_precedes, &sep_by_space, &sign_posn,
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore if (*grouping == CHAR_MAX || *grouping <= 0) /* no grouping ? */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* no more grouping ? */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* rest grouping with same value ? */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore/* convert double to ASCII */
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amoreformat_grouped_double(const struct lc_monetary *lmon,
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore double value, int *flags, int left_prec, int right_prec, int pad_char)
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore decimal_len = strlen(decimal_point); /* usually 1 */
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore thousands_len = strlen(thousands_sep); /* 0 or 1 usually */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* fill left_prec with default value */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* fill right_prec with default value */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore if (right_prec == CHAR_MAX) /* POSIX locale ? */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore left_prec += get_groups(left_prec, grouping);
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* convert to string */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore (void) snprintf(fmt, sizeof (fmt), "%%%d.%df",
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore avalue_size = asprintf(&avalue, fmt, value);
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore * Make sure that we've enough space for result string.
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore * This assumes that digits take up at least much space as
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore * grouping and radix characters. The worst case currently known
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore * is for Arabic, where two-byte UTF-8 sequences are used for both
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore * decimal and thousands seperators, and groups can be a small as two
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore * decimal digits. This will do no worse than doubling the storage
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore * requirement.
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore bufend = rslt + bufsize - 1; /* reserve space for trailing '\0' */
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore /* skip spaces at beginning */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore (void) memcpy(bufend, avalue + avalue_size+padded-right_prec,
2d08521bd15501c8370ba2153b9cca4f094979d0Garrett D'Amore (void) memcpy(bufend, decimal_point, decimal_len);
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* no more grouping ? */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* rest grouping with same value ? */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore (void) memcpy(bufend, avalue+padded, avalue_size);
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore padded--; /* decrease assumed $decimal_point */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* do padding with pad_char */