strfmon.c revision 4297a3b0d0a35d80f86fff155e288e885a100e6d
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 * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore * Use is subject to license terms.
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) { \
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amorestatic void __setup_vars(int, char *, char *, char *, char **);
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amorestatic int __calc_left_pad(int, char *);
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amorestatic char *__format_grouped_double(double, int *, int, int, int);
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amorestrfmon(char *_RESTRICT_KYWD s, size_t maxsize,
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore const char *fmt; /* current format poistion pointer */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore struct lconv *lc; /* pointer to lconv structure */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore char *asciivalue; /* formatted double pointer */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore char space_char = ' '; /* space after currency */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore char cs_precedes; /* values gathered from struct lconv */
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 */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore currency_symbol = strdup(lc->int_curr_symbol);
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore currency_symbol = strdup(lc->currency_symbol);
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* value itself */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* detect sign */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* fill left_prec with amount of padding chars */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore pad_size = __calc_left_pad((flags ^ IS_NEGATIVE),
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore asciivalue = __format_grouped_double(value, &flags,
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* to ENOMEM by malloc() */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* set some variables for later use */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn,
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))
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore return (dst - s - 1); /* size of put data except trailing '\0' */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore__setup_vars(int flags, char *cs_precedes, char *sep_by_space,
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore if ((flags & IS_NEGATIVE) && (flags & USE_INTL_CURRENCY)) {
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_n_sign_posn;
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore *signstr = (lc->negative_sign[0] == '\0') ? "-"
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_p_sign_posn;
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->n_sign_posn;
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore *signstr = (lc->negative_sign[0] == '\0') ? "-"
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->p_sign_posn;
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* Set defult values for unspecified information. */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore char cs_precedes, sep_by_space, sign_posn, *signstr;
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr);
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 */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore__format_grouped_double(double value, int *flags,
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore int left_prec, int right_prec, int pad_char)
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);
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* make sure that we've enough space for result string */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore bufend = rslt + bufsize - 1; /* reserve space for trailing '\0' */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore /* skip spaces at beggining */
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore (void) memcpy(bufend, avalue + avalue_size+padded-right_prec,
4297a3b0d0a35d80f86fff155e288e885a100e6dGarrett D'Amore thousands_sep != '\0' && /* XXX: need investigation */
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 */