diff options
Diffstat (limited to 'lib/libc/stdlib/strfmon.c')
-rw-r--r-- | lib/libc/stdlib/strfmon.c | 379 |
1 files changed, 197 insertions, 182 deletions
diff --git a/lib/libc/stdlib/strfmon.c b/lib/libc/stdlib/strfmon.c index d646ec0b001a..68a36a6d5567 100644 --- a/lib/libc/stdlib/strfmon.c +++ b/lib/libc/stdlib/strfmon.c @@ -1,11 +1,11 @@ /*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org> * All rights reserved. * * Copyright (c) 2011 The FreeBSD Foundation - * All rights reserved. + * * Portions of this software were developed by David Chisnall * under sponsorship from the FreeBSD Foundation. * @@ -32,10 +32,8 @@ * */ -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/types.h> + #include <ctype.h> #include <errno.h> #include <limits.h> @@ -45,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include <stdio.h> #include <stdlib.h> #include <string.h> + #include "xlocale_private.h" /* internal flags */ @@ -52,27 +51,27 @@ __FBSDID("$FreeBSD$"); #define SIGN_POSN_USED 0x02 /* '+' or '(' usage flag */ #define LOCALE_POSN 0x04 /* use locale defined +/- (default) */ #define PARENTH_POSN 0x08 /* enclose negative amount in () */ -#define SUPRESS_CURR_SYMBOL 0x10 /* suppress the currency from output */ +#define SUPPRESS_CURR_SYMBOL 0x10 /* suppress the currency from output */ #define LEFT_JUSTIFY 0x20 /* left justify */ #define USE_INTL_CURRENCY 0x40 /* use international currency symbol */ -#define IS_NEGATIVE 0x80 /* is argument value negative ? */ +#define IS_NEGATIVE 0x80 /* is argument value negative ? */ /* internal macros */ -#define PRINT(CH) do { \ - if (dst >= s + maxsize) \ +#define PRINT(CH) do { \ + if (dst >= s + maxsize) \ goto e2big_error; \ *dst++ = CH; \ } while (0) -#define PRINTS(STR) do { \ +#define PRINTS(STR) do { \ char *tmps = STR; \ while (*tmps != '\0') \ PRINT(*tmps++); \ } while (0) -#define GET_NUMBER(VAR) do { \ +#define GET_NUMBER(VAR, LOC) do { \ VAR = 0; \ - while (isdigit((unsigned char)*fmt)) { \ + while (isdigit_l((unsigned char)*fmt, LOC)) { \ if (VAR > INT_MAX / 10) \ goto e2big_error; \ VAR *= 10; \ @@ -83,41 +82,42 @@ __FBSDID("$FreeBSD$"); } \ } while (0) -#define GRPCPY(howmany) do { \ +#define GRPCPY(howmany) do { \ int i = howmany; \ while (i-- > 0) { \ avalue_size--; \ - *--bufend = *(avalue+avalue_size+padded); \ + *--bufend = *(avalue + avalue_size + padded); \ } \ } while (0) -#define GRPSEP do { \ +#define GRPSEP do { \ bufend -= thousands_sep_size; \ memcpy(bufend, thousands_sep, thousands_sep_size); \ groups++; \ } while (0) -static void __setup_vars(int, char *, char *, char *, char **); -static int __calc_left_pad(int, char *); -static char *__format_grouped_double(double, int *, int, int, int); +static void __setup_vars(int, char *, char *, char *, char **, struct lconv *); +static int __calc_left_pad(int, char *, struct lconv *); +static char *__format_grouped_double(double, int *, int, int, int, + struct lconv *, locale_t); static ssize_t -vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, - const char * __restrict format, va_list ap) +vstrfmon_l(char *__restrict s, size_t maxsize, locale_t loc, + const char *__restrict format, va_list ap) { - char *dst; /* output destination pointer */ - const char *fmt; /* current format poistion pointer */ - struct lconv *lc; /* pointer to lconv structure */ + char *dst; /* output destination pointer */ + const char *fmt; /* current format poistion pointer */ + struct lconv *lc; /* pointer to lconv structure */ char *asciivalue; /* formatted double pointer */ - int flags; /* formatting options */ - int pad_char; /* padding character */ - int pad_size; /* pad size */ - int width; /* field width */ - int left_prec; /* left precision */ - int right_prec; /* right precision */ - double value; /* just value */ - char space_char = ' '; /* space after currency */ + int flags; /* formatting options */ + int pad_char; /* padding character */ + int pad_size; /* pad size */ + int width; /* field width */ + int left_prec; /* left precision */ + int right_prec; /* right precision */ + double value; /* just value */ + char space_char = ' '; /* space after currency */ char cs_precedes, /* values gathered from struct lconv */ sep_by_space, @@ -127,17 +127,16 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, char *tmpptr; /* temporary vars */ int sverrno; - FIX_LOCALE(loc); + FIX_LOCALE(loc); lc = localeconv_l(loc); dst = s; fmt = format; asciivalue = NULL; currency_symbol = NULL; - pad_size = 0; - while (*fmt) { + while (*fmt != 0) { /* pass nonformating characters AS IS */ if (*fmt != '%') goto literal; @@ -145,58 +144,59 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, /* '%' found ! */ /* "%%" mean just '%' */ - if (*(fmt+1) == '%') { + if (*(fmt + 1) == '%') { fmt++; - literal: +literal: PRINT(*fmt++); continue; } /* set up initial values */ - flags = (NEED_GROUPING|LOCALE_POSN); + flags = NEED_GROUPING | LOCALE_POSN; pad_char = ' '; /* padding character is "space" */ + pad_size = 0; /* no padding initially */ left_prec = -1; /* no left precision specified */ right_prec = -1; /* no right precision specified */ width = -1; /* no width specified */ - value = 0; /* we have no value to print now */ /* Flags */ while (1) { switch (*++fmt) { - case '=': /* fill character */ - pad_char = *++fmt; - if (pad_char == '\0') - goto format_error; - continue; - case '^': /* not group currency */ - flags &= ~(NEED_GROUPING); - continue; - case '+': /* use locale defined signs */ - if (flags & SIGN_POSN_USED) - goto format_error; - flags |= (SIGN_POSN_USED|LOCALE_POSN); - continue; - case '(': /* enclose negatives with () */ - if (flags & SIGN_POSN_USED) - goto format_error; - flags |= (SIGN_POSN_USED|PARENTH_POSN); - continue; - case '!': /* suppress currency symbol */ - flags |= SUPRESS_CURR_SYMBOL; - continue; - case '-': /* alignment (left) */ - flags |= LEFT_JUSTIFY; - continue; - default: - break; + case '=': /* fill character */ + pad_char = *++fmt; + if (pad_char == '\0') + goto format_error; + continue; + case '^': /* not group currency */ + flags &= ~(NEED_GROUPING); + continue; + case '+': /* use locale defined signs */ + if (flags & SIGN_POSN_USED) + goto format_error; + flags |= (SIGN_POSN_USED | LOCALE_POSN); + continue; + case '(': /* enclose negatives with () */ + if (flags & SIGN_POSN_USED) + goto format_error; + flags |= (SIGN_POSN_USED | PARENTH_POSN); + continue; + case '!': /* suppress currency symbol */ + flags |= SUPPRESS_CURR_SYMBOL; + continue; + case '-': /* alignment (left) */ + flags |= LEFT_JUSTIFY; + continue; + default: + break; } break; } /* field Width */ - if (isdigit((unsigned char)*fmt)) { - GET_NUMBER(width); - /* Do we have enough space to put number with + if (isdigit_l((unsigned char)*fmt, loc)) { + GET_NUMBER(width, loc); + /* + * Do we have enough space to put number with * required width ? */ if ((unsigned int)width >= maxsize - (dst - s)) @@ -205,47 +205,52 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, /* Left precision */ if (*fmt == '#') { - if (!isdigit((unsigned char)*++fmt)) + if (!isdigit_l((unsigned char)*++fmt, loc)) goto format_error; - GET_NUMBER(left_prec); + GET_NUMBER(left_prec, loc); if ((unsigned int)left_prec >= maxsize - (dst - s)) goto e2big_error; } /* Right precision */ if (*fmt == '.') { - if (!isdigit((unsigned char)*++fmt)) + if (!isdigit_l((unsigned char)*++fmt, loc)) goto format_error; - GET_NUMBER(right_prec); - if ((unsigned int)right_prec >= maxsize - (dst - s) - - left_prec) + GET_NUMBER(right_prec, loc); + if ((unsigned int)right_prec >= + maxsize - (dst - s) - left_prec) goto e2big_error; } /* Conversion Characters */ switch (*fmt++) { - case 'i': /* use internaltion currency format */ - flags |= USE_INTL_CURRENCY; - break; - case 'n': /* use national currency format */ - flags &= ~(USE_INTL_CURRENCY); - break; - default: /* required character is missing or - premature EOS */ - goto format_error; + case 'i': /* use international currency format */ + flags |= USE_INTL_CURRENCY; + break; + case 'n': /* use national currency format */ + flags &= ~(USE_INTL_CURRENCY); + break; + default: /* + * required character is missing or + * premature EOS + */ + goto format_error; } if (currency_symbol != NULL) free(currency_symbol); if (flags & USE_INTL_CURRENCY) { currency_symbol = strdup(lc->int_curr_symbol); - if (currency_symbol != NULL) - space_char = *(currency_symbol+3); + if (currency_symbol != NULL && + strlen(currency_symbol) > 3) { + space_char = currency_symbol[3]; + currency_symbol[3] = '\0'; + } } else currency_symbol = strdup(lc->currency_symbol); if (currency_symbol == NULL) - goto end_error; /* ENOMEM. */ + goto end_error; /* ENOMEM. */ /* value itself */ value = va_arg(ap, double); @@ -259,23 +264,25 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, /* fill left_prec with amount of padding chars */ if (left_prec >= 0) { pad_size = __calc_left_pad((flags ^ IS_NEGATIVE), - currency_symbol) - - __calc_left_pad(flags, currency_symbol); + currency_symbol, lc) - + __calc_left_pad(flags, currency_symbol, lc); if (pad_size < 0) pad_size = 0; } if (asciivalue != NULL) free(asciivalue); - asciivalue = __format_grouped_double(value, &flags, - left_prec, right_prec, pad_char); + asciivalue = __format_grouped_double(value, &flags, left_prec, + right_prec, pad_char, lc, loc); if (asciivalue == NULL) - goto end_error; /* errno already set */ - /* to ENOMEM by malloc() */ + goto end_error; /* + * errno already set to ENOMEM by + * malloc() + */ /* set some variables for later use */ - __setup_vars(flags, &cs_precedes, &sep_by_space, - &sign_posn, &signstr); + __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, + &signstr, lc); /* * Description of some LC_MONETARY's values: @@ -287,25 +294,25 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, * = 0 - symbol succeeds the value * * p_sep_by_space & n_sep_by_space - * + * * = 0 - no space separates $currency_symbol * from the value for a monetary quantity with a - * non-negative value + * non-negative value * = 1 - space separates the symbol from the value * = 2 - space separates the symbol and the sign string, - * if adjacent. - * + * if adjacent; otherwise, a space separates + * the sign string from the value + * * p_sign_posn & 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 + * = 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 - * */ tmpptr = dst; @@ -319,11 +326,11 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, if (cs_precedes == 1) { if (sign_posn == 1 || sign_posn == 3) { PRINTS(signstr); - if (sep_by_space == 2) /* XXX: ? */ + if (sep_by_space == 2) PRINT(' '); } - if (!(flags & SUPRESS_CURR_SYMBOL)) { + if (!(flags & SUPPRESS_CURR_SYMBOL)) { PRINTS(currency_symbol); if (sign_posn == 4) { @@ -335,8 +342,11 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, } else if (sep_by_space == 1) PRINT(space_char); } - } else if (sign_posn == 1) + } else if (sign_posn == 1) { PRINTS(signstr); + if (sep_by_space == 2) + PRINT(' '); + } PRINTS(asciivalue); @@ -347,15 +357,13 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, PRINTS(signstr); } - if (!(flags & SUPRESS_CURR_SYMBOL)) { - if ((sign_posn == 3 && sep_by_space == 2) - || (sep_by_space == 1 - && (sign_posn == 0 - || sign_posn == 1 - || sign_posn == 2 - || sign_posn == 4))) + if (!(flags & SUPPRESS_CURR_SYMBOL)) { + if ((sign_posn == 3 && sep_by_space == 2) || + (sep_by_space == 1 && + (sign_posn == 0 || sign_posn == 1 || + sign_posn == 2 || sign_posn == 4))) PRINT(space_char); - PRINTS(currency_symbol); /* XXX: len */ + PRINTS(currency_symbol); if (sign_posn == 4) { if (sep_by_space == 2) PRINT(' '); @@ -370,19 +378,23 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, PRINTS(signstr); } - if (sign_posn == 0 && (flags & IS_NEGATIVE)) - PRINT(')'); + if (sign_posn == 0) { + if (flags & IS_NEGATIVE) + PRINT(')'); + else if (left_prec >= 0) + PRINT(' '); + } if (dst - tmpptr < width) { if (flags & LEFT_JUSTIFY) { while (dst - tmpptr < width) PRINT(' '); } else { - pad_size = dst-tmpptr; - memmove(tmpptr + width-pad_size, tmpptr, + pad_size = dst - tmpptr; + memmove(tmpptr + width - pad_size, tmpptr, pad_size); - memset(tmpptr, ' ', width-pad_size); - dst += width-pad_size; + memset(tmpptr, ' ', width - pad_size); + dst += width - pad_size; } } } @@ -390,7 +402,7 @@ vstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, PRINT('\0'); free(asciivalue); free(currency_symbol); - return (dst - s - 1); /* return size of put data except trailing '\0' */ + return (dst - s - 1); /* size of put data except trailing '\0' */ e2big_error: errno = E2BIG; @@ -408,43 +420,17 @@ end_error: errno = sverrno; return (-1); } -ssize_t -strfmon_l(char * __restrict s, size_t maxsize, locale_t loc, const char * __restrict format, - ...) -{ - size_t ret; - va_list ap; - va_start(ap, format); - ret = vstrfmon_l(s, maxsize, loc, format, ap); - va_end(ap); - return ret; -} - -ssize_t -strfmon(char * __restrict s, size_t maxsize, const char * __restrict format, - ...) -{ - size_t ret; - va_list ap; - va_start(ap, format); - ret = vstrfmon_l(s, maxsize, __get_locale(), format, ap); - va_end(ap); - return ret; -} - static void -__setup_vars(int flags, char *cs_precedes, char *sep_by_space, - char *sign_posn, char **signstr) { - - struct lconv *lc = localeconv(); - +__setup_vars(int flags, char *cs_precedes, char *sep_by_space, char *sign_posn, + char **signstr, struct lconv *lc) +{ if ((flags & IS_NEGATIVE) && (flags & USE_INTL_CURRENCY)) { *cs_precedes = lc->int_n_cs_precedes; *sep_by_space = lc->int_n_sep_by_space; *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_n_sign_posn; - *signstr = (lc->negative_sign[0] == '\0') ? "-" - : lc->negative_sign; + *signstr = (lc->negative_sign[0] == '\0') ? "-" : + lc->negative_sign; } else if (flags & USE_INTL_CURRENCY) { *cs_precedes = lc->int_p_cs_precedes; *sep_by_space = lc->int_p_sep_by_space; @@ -454,8 +440,8 @@ __setup_vars(int flags, char *cs_precedes, char *sep_by_space, *cs_precedes = lc->n_cs_precedes; *sep_by_space = lc->n_sep_by_space; *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->n_sign_posn; - *signstr = (lc->negative_sign[0] == '\0') ? "-" - : lc->negative_sign; + *signstr = (lc->negative_sign[0] == '\0') ? "-" : + lc->negative_sign; } else { *cs_precedes = lc->p_cs_precedes; *sep_by_space = lc->p_sep_by_space; @@ -463,7 +449,7 @@ __setup_vars(int flags, char *cs_precedes, char *sep_by_space, *signstr = lc->positive_sign; } - /* Set defult values for unspecified information. */ + /* Set default values for unspecified information. */ if (*cs_precedes != 0) *cs_precedes = 1; if (*sep_by_space == CHAR_MAX) @@ -473,12 +459,13 @@ __setup_vars(int flags, char *cs_precedes, char *sep_by_space, } static int -__calc_left_pad(int flags, char *cur_symb) { - +__calc_left_pad(int flags, char *cur_symb, struct lconv *lc) +{ char cs_precedes, sep_by_space, sign_posn, *signstr; int left_chars = 0; - __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr); + __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr, + lc); if (cs_precedes != 0) { left_chars += strlen(cur_symb); @@ -487,20 +474,24 @@ __calc_left_pad(int flags, char *cur_symb) { } switch (sign_posn) { - case 1: + case 0: + if (flags & IS_NEGATIVE) + left_chars++; + break; + case 1: + left_chars += strlen(signstr); + break; + case 3: + case 4: + if (cs_precedes != 0) left_chars += strlen(signstr); - break; - case 3: - case 4: - if (cs_precedes != 0) - left_chars += strlen(signstr); } return (left_chars); } static int -get_groups(int size, char *grouping) { - +get_groups(int size, char *grouping) +{ int chars = 0; if (*grouping == CHAR_MAX || *grouping <= 0) /* no grouping ? */ @@ -523,8 +514,9 @@ get_groups(int size, char *grouping) { /* convert double to locale-encoded string */ static char * -__format_grouped_double(double value, int *flags, - int left_prec, int right_prec, int pad_char) { +__format_grouped_double(double value, int *flags, int left_prec, int right_prec, + int pad_char, struct lconv *lc, locale_t loc) +{ char *rslt; char *avalue; @@ -535,14 +527,13 @@ __format_grouped_double(double value, int *flags, int padded; - struct lconv *lc = localeconv(); char *grouping; const char *decimal_point; const char *thousands_sep; size_t decimal_point_size; size_t thousands_sep_size; - int groups = 0; + int groups = 0; grouping = lc->mon_grouping; decimal_point = lc->mon_decimal_point; @@ -561,10 +552,10 @@ __format_grouped_double(double value, int *flags, /* fill right_prec with default value */ if (right_prec == -1) { - if (*flags & USE_INTL_CURRENCY) - right_prec = lc->int_frac_digits; - else - right_prec = lc->frac_digits; + if (*flags & USE_INTL_CURRENCY) + right_prec = lc->int_frac_digits; + else + right_prec = lc->frac_digits; if (right_prec == CHAR_MAX) /* POSIX locale ? */ right_prec = 2; @@ -574,8 +565,8 @@ __format_grouped_double(double value, int *flags, left_prec += get_groups(left_prec, grouping); /* convert to string */ - avalue_size = asprintf(&avalue, "%*.*f", left_prec + right_prec + 1, - right_prec, value); + avalue_size = asprintf_l(&avalue, loc, "%*.*f", + left_prec + right_prec + 1, right_prec, value); if (avalue_size < 0) return (NULL); @@ -598,17 +589,15 @@ __format_grouped_double(double value, int *flags, if (right_prec > 0) { bufend -= right_prec; - memcpy(bufend, avalue + avalue_size+padded-right_prec, + memcpy(bufend, avalue + avalue_size + padded - right_prec, right_prec); bufend -= decimal_point_size; memcpy(bufend, decimal_point, decimal_point_size); avalue_size -= (right_prec + 1); } - if ((*flags & NEED_GROUPING) && - thousands_sep_size > 0 && /* XXX: need investigation */ - *grouping != CHAR_MAX && - *grouping > 0) { + if ((*flags & NEED_GROUPING) && thousands_sep_size > 0 && + *grouping != CHAR_MAX && *grouping > 0) { while (avalue_size > (int)*grouping) { GRPCPY(*grouping); GRPSEP; @@ -630,10 +619,9 @@ __format_grouped_double(double value, int *flags, if (avalue_size != 0) GRPCPY(avalue_size); padded -= groups; - } else { bufend -= avalue_size; - memcpy(bufend, avalue+padded, avalue_size); + memcpy(bufend, avalue + padded, avalue_size); /* decrease assumed $decimal_point */ if (right_prec == 0) padded -= decimal_point_size; @@ -650,3 +638,30 @@ __format_grouped_double(double value, int *flags, free(avalue); return (rslt); } + +ssize_t +strfmon(char *restrict s, size_t maxsize, const char *restrict format, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, format); + ret = vstrfmon_l(s, maxsize, __get_locale(), format, ap); + va_end(ap); + + return (ret); +} + +ssize_t +strfmon_l(char *restrict s, size_t maxsize, locale_t loc, + const char *restrict format, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, format); + ret = vstrfmon_l(s, maxsize, loc, format, ap); + va_end(ap); + + return (ret); +} |