Print this page
2964 need POSIX 2008 locale object support
Reviewed by: Robert Mustacchi <rm@joyent.com>

*** 1,6 **** --- 1,7 ---- /* + * Copyright 2013 Garrett D'Amore <garrett@damore.org> * Copyright 2010 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without
*** 39,48 **** --- 40,52 ---- #include <monetary.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> + #include "localeimpl.h" + #include "lmonetary.h" + #include "lnumeric.h" /* internal flags */ #define NEED_GROUPING 0x01 /* print digits grouped (default) */ #define SIGN_POSN_USED 0x02 /* '+' or '(' usage flag */ #define LOCALE_POSN 0x04 /* use locale defined +/- (default) */
*** 58,68 **** goto e2big_error; \ *dst++ = CH; \ } #define PRINTS(STR) { \ ! char *tmps = STR; \ while (*tmps != '\0') \ PRINT(*tmps++); \ } #define GET_NUMBER(VAR) { \ --- 62,72 ---- goto e2big_error; \ *dst++ = CH; \ } #define PRINTS(STR) { \ ! const char *tmps = STR; \ while (*tmps != '\0') \ PRINT(*tmps++); \ } #define GET_NUMBER(VAR) { \
*** 85,110 **** *--bufend = *(avalue+avalue_size+padded); \ } \ } #define GRPSEP { \ ! *--bufend = thousands_sep; \ groups++; \ } ! 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); ssize_t ! strfmon(char *_RESTRICT_KYWD s, size_t maxsize, ! const char *_RESTRICT_KYWD format, ...) { - va_list ap; 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 */ --- 89,115 ---- *--bufend = *(avalue+avalue_size+padded); \ } \ } #define GRPSEP { \ ! bufend -= thousands_len; \ ! (void) memcpy(bufend, thousands_sep, thousands_len); \ groups++; \ } ! static void setup_vars(const struct lc_monetary *, int, char *, char *, char *, ! const char **); ! static int calc_left_pad(const struct lc_monetary *, int, const char *); ! static char *format_grouped_double(const struct lc_monetary *, ! const struct lc_numeric *, double, int *, int, int, int); ssize_t ! strfmon_impl(char *_RESTRICT_KYWD s, size_t maxsize, locale_t loc, ! const char *_RESTRICT_KYWD format, va_list ap) { char *dst; /* output destination pointer */ const char *fmt; /* current format poistion pointer */ char *asciivalue; /* formatted double pointer */ int flags; /* formatting options */ int pad_char; /* padding character */ int pad_size; /* pad size */
*** 112,133 **** 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 */ char sep_by_space; char sign_posn; ! char *signstr; ! char *currency_symbol; char *tmpptr; /* temporary vars */ int sverrno; ! va_start(ap, format); - lc = localeconv(); dst = s; fmt = format; asciivalue = NULL; currency_symbol = NULL; pad_size = 0; --- 117,140 ---- int left_prec; /* left precision */ int right_prec; /* right precision */ double value; /* just value */ char space_char = ' '; /* space after currency */ ! char cs_precedes; /* values from struct lc_monetary */ char sep_by_space; char sign_posn; ! const char *signstr; ! const char *currency_symbol; char *tmpptr; /* temporary vars */ int sverrno; + const struct lc_monetary *lmon; /* monetary structure */ + const struct lc_numeric *lnum; /* numeric structure */ ! lmon = loc->monetary; ! lnum = loc->numeric; dst = s; fmt = format; asciivalue = NULL; currency_symbol = NULL; pad_size = 0;
*** 229,250 **** default: /* required char 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); } else ! currency_symbol = strdup(lc->currency_symbol); - if (currency_symbol == NULL) - goto end_error; /* ENOMEM. */ - /* value itself */ value = va_arg(ap, double); /* detect sign */ if (value < 0) { --- 236,253 ---- default: /* required char missing or premature EOS */ goto format_error; } if (flags & USE_INTL_CURRENCY) { ! currency_symbol = lmon->int_curr_symbol; ! /* by definition three letters followed by a space */ if (currency_symbol != NULL) ! space_char = currency_symbol[3]; } else ! currency_symbol = lmon->currency_symbol; /* value itself */ value = va_arg(ap, double); /* detect sign */ if (value < 0) {
*** 252,279 **** value = -value; } /* 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); if (pad_size < 0) pad_size = 0; } if (asciivalue != NULL) free(asciivalue); ! asciivalue = __format_grouped_double(value, &flags, left_prec, right_prec, pad_char); if (asciivalue == NULL) 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); /* * Description of some LC_MONETARY's values: * * p_cs_precedes & n_cs_precedes --- 255,282 ---- value = -value; } /* fill left_prec with amount of padding chars */ if (left_prec >= 0) { ! pad_size = calc_left_pad(lmon, (flags ^ IS_NEGATIVE), currency_symbol) - ! calc_left_pad(lmon, flags, currency_symbol); if (pad_size < 0) pad_size = 0; } if (asciivalue != NULL) free(asciivalue); ! asciivalue = format_grouped_double(lmon, lnum, value, &flags, left_prec, right_prec, pad_char); if (asciivalue == NULL) goto end_error; /* errno already set */ /* to ENOMEM by malloc() */ /* set some variables for later use */ ! setup_vars(lmon, flags, &cs_precedes, &sep_by_space, ! &sign_posn, &signstr); /* * Description of some LC_MONETARY's values: * * p_cs_precedes & n_cs_precedes
*** 313,323 **** PRINT('('); if (cs_precedes == 1) { if (sign_posn == 1 || sign_posn == 3) { PRINTS(signstr); ! if (sep_by_space == 2) /* XXX: ? */ PRINT(' '); } if (!(flags & SUPRESS_CURR_SYMBOL)) { PRINTS(currency_symbol); --- 316,326 ---- PRINT('('); if (cs_precedes == 1) { if (sign_posn == 1 || sign_posn == 3) { PRINTS(signstr); ! if (sep_by_space == 2) PRINT(' '); } if (!(flags & SUPRESS_CURR_SYMBOL)) { PRINTS(currency_symbol);
*** 380,392 **** } } } PRINT('\0'); - va_end(ap); free(asciivalue); - free(currency_symbol); return (dst - s - 1); /* size of put data except trailing '\0' */ e2big_error: errno = E2BIG; goto end_error; --- 383,393 ----
*** 396,460 **** end_error: sverrno = errno; if (asciivalue != NULL) free(asciivalue); - if (currency_symbol != NULL) - free(currency_symbol); errno = sverrno; - va_end(ap); return (-1); } ! static void ! __setup_vars(int flags, char *cs_precedes, char *sep_by_space, ! char *sign_posn, char **signstr) { ! struct lconv *lc = localeconv(); 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; } else if (flags & USE_INTL_CURRENCY) { ! *cs_precedes = lc->int_p_cs_precedes; ! *sep_by_space = lc->int_p_sep_by_space; ! *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_p_sign_posn; ! *signstr = lc->positive_sign; } else if (flags & IS_NEGATIVE) { ! *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; } else { ! *cs_precedes = lc->p_cs_precedes; ! *sep_by_space = lc->p_sep_by_space; ! *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->p_sign_posn; ! *signstr = lc->positive_sign; } ! /* Set defult values for unspecified information. */ if (*cs_precedes != 0) *cs_precedes = 1; if (*sep_by_space == CHAR_MAX) *sep_by_space = 0; if (*sign_posn == CHAR_MAX) *sign_posn = 0; } static int ! __calc_left_pad(int flags, char *cur_symb) { ! ! char cs_precedes, sep_by_space, sign_posn, *signstr; int left_chars = 0; ! __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr); if (cs_precedes != 0) { left_chars += strlen(cur_symb); if (sep_by_space != 0) left_chars++; --- 397,483 ---- end_error: sverrno = errno; if (asciivalue != NULL) free(asciivalue); errno = sverrno; return (-1); } ! ssize_t ! strfmon(char *_RESTRICT_KYWD s, size_t maxsize, ! const char *_RESTRICT_KYWD format, ...) { + va_list ap; + ssize_t ret; ! va_start(ap, format); ! ret = strfmon_impl(s, maxsize, uselocale(NULL), format, ap); ! va_end(ap); ! return (ret); ! } + ssize_t + strfmon_l(char *_RESTRICT_KYWD s, size_t maxsize, locale_t loc, + const char *_RESTRICT_KYWD format, ...) + { + ssize_t ret; + va_list ap; + va_start(ap, format); + ret = strfmon_impl(s, maxsize, loc, format, ap); + va_end(ap); + return (ret); + } + + static void + setup_vars(const struct lc_monetary *lmon, int flags, char *cs_precedes, + char *sep_by_space, char *sign_posn, const char **signstr) + { if ((flags & IS_NEGATIVE) && (flags & USE_INTL_CURRENCY)) { ! *cs_precedes = lmon->int_n_cs_precedes[0]; ! *sep_by_space = lmon->int_n_sep_by_space[0]; ! *sign_posn = (flags & PARENTH_POSN) ? 0 : ! lmon->int_n_sign_posn[0]; ! *signstr = (lmon->negative_sign[0] == '\0') ? "-" : ! lmon->negative_sign; } else if (flags & USE_INTL_CURRENCY) { ! *cs_precedes = lmon->int_p_cs_precedes[0]; ! *sep_by_space = lmon->int_p_sep_by_space[0]; ! *sign_posn = (flags & PARENTH_POSN) ? 0 : ! lmon->int_p_sign_posn[0]; ! *signstr = lmon->positive_sign; } else if (flags & IS_NEGATIVE) { ! *cs_precedes = lmon->n_cs_precedes[0]; ! *sep_by_space = lmon->n_sep_by_space[0]; ! *sign_posn = (flags & PARENTH_POSN) ? 0 : lmon->n_sign_posn[0]; ! *signstr = (lmon->negative_sign[0] == '\0') ? "-" : ! lmon->negative_sign; } else { ! *cs_precedes = lmon->p_cs_precedes[0]; ! *sep_by_space = lmon->p_sep_by_space[0]; ! *sign_posn = (flags & PARENTH_POSN) ? 0 : lmon->p_sign_posn[0]; ! *signstr = lmon->positive_sign; } ! /* Set default values for unspecified information. */ if (*cs_precedes != 0) *cs_precedes = 1; if (*sep_by_space == CHAR_MAX) *sep_by_space = 0; if (*sign_posn == CHAR_MAX) *sign_posn = 0; } static int ! calc_left_pad(const struct lc_monetary *lmon, int flags, const char *cur_symb) { ! char cs_precedes, sep_by_space, sign_posn; ! const char *signstr; int left_chars = 0; ! setup_vars(lmon, flags, &cs_precedes, &sep_by_space, &sign_posn, ! &signstr); if (cs_precedes != 0) { left_chars += strlen(cur_symb); if (sep_by_space != 0) left_chars++;
*** 471,481 **** } return (left_chars); } static int ! get_groups(int size, char *grouping) { int chars = 0; if (*grouping == CHAR_MAX || *grouping <= 0) /* no grouping ? */ --- 494,504 ---- } return (left_chars); } static int ! get_groups(int size, const char *grouping) { int chars = 0; if (*grouping == CHAR_MAX || *grouping <= 0) /* no grouping ? */
*** 496,507 **** return (chars); } /* convert double to ASCII */ static char * ! __format_grouped_double(double value, int *flags, ! int left_prec, int right_prec, int pad_char) { char *rslt; char *avalue; int avalue_size; --- 519,531 ---- return (chars); } /* convert double to ASCII */ static char * ! format_grouped_double(const struct lc_monetary *lmon, ! const struct lc_numeric *lnum, ! double value, int *flags, int left_prec, int right_prec, int pad_char) { char *rslt; char *avalue; int avalue_size;
*** 510,544 **** size_t bufsize; char *bufend; int padded; ! struct lconv *lc = localeconv(); ! char *grouping; ! char decimal_point; ! char thousands_sep; int groups = 0; ! grouping = lc->mon_grouping; ! decimal_point = *lc->mon_decimal_point; ! if (decimal_point == '\0') ! decimal_point = *lc->decimal_point; ! thousands_sep = *lc->mon_thousands_sep; ! if (thousands_sep == '\0') ! thousands_sep = *lc->thousands_sep; /* fill left_prec with default value */ if (left_prec == -1) left_prec = 0; /* 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 (right_prec == CHAR_MAX) /* POSIX locale ? */ right_prec = 2; } --- 534,572 ---- size_t bufsize; char *bufend; int padded; ! const char *grouping; ! const char *decimal_point; ! const char *thousands_sep; ! int decimal_len; ! int thousands_len; int groups = 0; ! grouping = lmon->mon_grouping; ! decimal_point = lmon->mon_decimal_point; ! if (*decimal_point == '\0') ! decimal_point = lnum->decimal_point; ! thousands_sep = lmon->mon_thousands_sep; ! if (*thousands_sep == '\0') ! thousands_sep = lnum->thousands_sep; + decimal_len = strlen(decimal_point); /* usually 1 */ + thousands_len = strlen(thousands_sep); /* 0 or 1 usually */ + /* fill left_prec with default value */ if (left_prec == -1) left_prec = 0; /* fill right_prec with default value */ if (right_prec == -1) { if (*flags & USE_INTL_CURRENCY) ! right_prec = lmon->int_frac_digits[0]; else ! right_prec = lmon->frac_digits[0]; if (right_prec == CHAR_MAX) /* POSIX locale ? */ right_prec = 2; }
*** 550,569 **** left_prec + right_prec + 1, right_prec); avalue_size = asprintf(&avalue, fmt, value); if (avalue_size < 0) return (NULL); ! /* make sure that we've enough space for result string */ bufsize = strlen(avalue)*2+1; rslt = calloc(1, bufsize); if (rslt == NULL) { free(avalue); return (NULL); } bufend = rslt + bufsize - 1; /* reserve space for trailing '\0' */ ! /* skip spaces at beggining */ padded = 0; while (avalue[padded] == ' ') { padded++; avalue_size--; } --- 578,605 ---- left_prec + right_prec + 1, right_prec); avalue_size = asprintf(&avalue, fmt, value); if (avalue_size < 0) return (NULL); ! /* ! * Make sure that we've enough space for result string. ! * This assumes that digits take up at least much space as ! * grouping and radix characters. The worst case currently known ! * is for Arabic, where two-byte UTF-8 sequences are used for both ! * decimal and thousands seperators, and groups can be a small as two ! * decimal digits. This will do no worse than doubling the storage ! * requirement. ! */ bufsize = strlen(avalue)*2+1; rslt = calloc(1, bufsize); if (rslt == NULL) { free(avalue); return (NULL); } bufend = rslt + bufsize - 1; /* reserve space for trailing '\0' */ ! /* skip spaces at beginning */ padded = 0; while (avalue[padded] == ' ') { padded++; avalue_size--; }
*** 570,585 **** if (right_prec > 0) { bufend -= right_prec; (void) memcpy(bufend, avalue + avalue_size+padded-right_prec, right_prec); ! *--bufend = decimal_point; ! avalue_size -= (right_prec + 1); } if ((*flags & NEED_GROUPING) && ! thousands_sep != '\0' && /* XXX: need investigation */ *grouping != CHAR_MAX && *grouping > 0) { while (avalue_size > (int)*grouping) { GRPCPY(*grouping); GRPSEP; --- 606,622 ---- if (right_prec > 0) { bufend -= right_prec; (void) memcpy(bufend, avalue + avalue_size+padded-right_prec, right_prec); ! bufend -= decimal_len; ! (void) memcpy(bufend, decimal_point, decimal_len); ! avalue_size -= (right_prec + decimal_len); } if ((*flags & NEED_GROUPING) && ! thousands_len != 0 && *grouping != CHAR_MAX && *grouping > 0) { while (avalue_size > (int)*grouping) { GRPCPY(*grouping); GRPSEP;