183104Sphantom/*- 287659Sphantom * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org> 383104Sphantom * All rights reserved. 483104Sphantom * 5235785Stheraven * Copyright (c) 2011 The FreeBSD Foundation 6235785Stheraven * All rights reserved. 7235785Stheraven * Portions of this software were developed by David Chisnall 8235785Stheraven * under sponsorship from the FreeBSD Foundation. 9235785Stheraven * 1083104Sphantom * Redistribution and use in source and binary forms, with or without 1183104Sphantom * modification, are permitted provided that the following conditions 1283104Sphantom * are met: 1383104Sphantom * 1. Redistributions of source code must retain the above copyright 1483104Sphantom * notice, this list of conditions and the following disclaimer. 1583104Sphantom * 2. Redistributions in binary form must reproduce the above copyright 1683104Sphantom * notice, this list of conditions and the following disclaimer in the 1783104Sphantom * documentation and/or other materials provided with the distribution. 1883104Sphantom * 1983104Sphantom * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2083104Sphantom * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2183104Sphantom * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2283104Sphantom * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2383104Sphantom * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2483104Sphantom * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2583104Sphantom * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2683104Sphantom * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2783104Sphantom * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2883104Sphantom * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2983104Sphantom * SUCH DAMAGE. 3083104Sphantom * 3183104Sphantom */ 3283104Sphantom 3392986Sobrien#include <sys/cdefs.h> 3492986Sobrien__FBSDID("$FreeBSD$"); 3583104Sphantom 3683104Sphantom#include <sys/types.h> 3783104Sphantom#include <ctype.h> 3883104Sphantom#include <errno.h> 3983104Sphantom#include <limits.h> 4083104Sphantom#include <locale.h> 41150065Sstefanf#include <monetary.h> 4283104Sphantom#include <stdarg.h> 4383104Sphantom#include <stdio.h> 4483104Sphantom#include <stdlib.h> 4583104Sphantom#include <string.h> 46235785Stheraven#include "xlocale_private.h" 4783104Sphantom 4883104Sphantom/* internal flags */ 4983104Sphantom#define NEED_GROUPING 0x01 /* print digits grouped (default) */ 5083104Sphantom#define SIGN_POSN_USED 0x02 /* '+' or '(' usage flag */ 5183104Sphantom#define LOCALE_POSN 0x04 /* use locale defined +/- (default) */ 5283104Sphantom#define PARENTH_POSN 0x08 /* enclose negative amount in () */ 5383104Sphantom#define SUPRESS_CURR_SYMBOL 0x10 /* supress the currency from output */ 5483104Sphantom#define LEFT_JUSTIFY 0x20 /* left justify */ 5583104Sphantom#define USE_INTL_CURRENCY 0x40 /* use international currency symbol */ 5683104Sphantom#define IS_NEGATIVE 0x80 /* is argument value negative ? */ 5783104Sphantom 5883104Sphantom/* internal macros */ 5983559Smike#define PRINT(CH) do { \ 6083104Sphantom if (dst >= s + maxsize) \ 6183104Sphantom goto e2big_error; \ 6283104Sphantom *dst++ = CH; \ 6383559Smike} while (0) 6483104Sphantom 6583559Smike#define PRINTS(STR) do { \ 6683104Sphantom char *tmps = STR; \ 6783104Sphantom while (*tmps != '\0') \ 6883104Sphantom PRINT(*tmps++); \ 6983559Smike} while (0) 7083104Sphantom 7183559Smike#define GET_NUMBER(VAR) do { \ 7283104Sphantom VAR = 0; \ 7383104Sphantom while (isdigit((unsigned char)*fmt)) { \ 74178457Sru if (VAR > INT_MAX / 10) \ 75178457Sru goto e2big_error; \ 7683104Sphantom VAR *= 10; \ 7783104Sphantom VAR += *fmt - '0'; \ 78178312Sru if (VAR < 0) \ 79178312Sru goto e2big_error; \ 8083104Sphantom fmt++; \ 8183104Sphantom } \ 8283559Smike} while (0) 8383104Sphantom 8483559Smike#define GRPCPY(howmany) do { \ 8583559Smike int i = howmany; \ 8683559Smike while (i-- > 0) { \ 8783559Smike avalue_size--; \ 8883559Smike *--bufend = *(avalue+avalue_size+padded); \ 8983559Smike } \ 9083559Smike} while (0) 9183559Smike 9283559Smike#define GRPSEP do { \ 9383559Smike *--bufend = thousands_sep; \ 9483559Smike groups++; \ 9583559Smike} while (0) 9683559Smike 9783104Sphantomstatic void __setup_vars(int, char *, char *, char *, char **); 9883104Sphantomstatic int __calc_left_pad(int, char *); 9983104Sphantomstatic char *__format_grouped_double(double, int *, int, int, int); 10083104Sphantom 101235785Stheravenstatic ssize_t 102235785Stheravenvstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, 103235785Stheraven const char * __restrict format, va_list ap) 10483104Sphantom{ 10583104Sphantom char *dst; /* output destination pointer */ 10683104Sphantom const char *fmt; /* current format poistion pointer */ 10783104Sphantom struct lconv *lc; /* pointer to lconv structure */ 10883104Sphantom char *asciivalue; /* formatted double pointer */ 10983104Sphantom 11083104Sphantom int flags; /* formatting options */ 11183104Sphantom int pad_char; /* padding character */ 11283104Sphantom int pad_size; /* pad size */ 11383104Sphantom int width; /* field width */ 11483104Sphantom int left_prec; /* left precision */ 11583104Sphantom int right_prec; /* right precision */ 11683104Sphantom double value; /* just value */ 11783104Sphantom char space_char = ' '; /* space after currency */ 11883104Sphantom 11983104Sphantom char cs_precedes, /* values gathered from struct lconv */ 12083104Sphantom sep_by_space, 12183104Sphantom sign_posn, 12283104Sphantom *signstr, 12383104Sphantom *currency_symbol; 12483104Sphantom 12583104Sphantom char *tmpptr; /* temporary vars */ 126104946Stjr int sverrno; 127235785Stheraven FIX_LOCALE(loc); 12883104Sphantom 12983104Sphantom 130235785Stheraven lc = localeconv_l(loc); 13183104Sphantom dst = s; 13283104Sphantom fmt = format; 13383104Sphantom asciivalue = NULL; 13483104Sphantom currency_symbol = NULL; 13583104Sphantom pad_size = 0; 13683104Sphantom 13783104Sphantom while (*fmt) { 13883104Sphantom /* pass nonformating characters AS IS */ 13983559Smike if (*fmt != '%') 14083104Sphantom goto literal; 14183104Sphantom 14283104Sphantom /* '%' found ! */ 14383104Sphantom 14483104Sphantom /* "%%" mean just '%' */ 14583104Sphantom if (*(fmt+1) == '%') { 14683104Sphantom fmt++; 14783104Sphantom literal: 14883104Sphantom PRINT(*fmt++); 14983104Sphantom continue; 15083104Sphantom } 15183104Sphantom 15283104Sphantom /* set up initial values */ 15383104Sphantom flags = (NEED_GROUPING|LOCALE_POSN); 15483104Sphantom pad_char = ' '; /* padding character is "space" */ 15583104Sphantom left_prec = -1; /* no left precision specified */ 15683104Sphantom right_prec = -1; /* no right precision specified */ 15783104Sphantom width = -1; /* no width specified */ 15883104Sphantom value = 0; /* we have no value to print now */ 15983104Sphantom 16083104Sphantom /* Flags */ 16183104Sphantom while (1) { 16283104Sphantom switch (*++fmt) { 16383104Sphantom case '=': /* fill character */ 16483104Sphantom pad_char = *++fmt; 16583104Sphantom if (pad_char == '\0') 16683104Sphantom goto format_error; 16783104Sphantom continue; 16883104Sphantom case '^': /* not group currency */ 16983104Sphantom flags &= ~(NEED_GROUPING); 17083104Sphantom continue; 17183104Sphantom case '+': /* use locale defined signs */ 17283104Sphantom if (flags & SIGN_POSN_USED) 17383104Sphantom goto format_error; 17483104Sphantom flags |= (SIGN_POSN_USED|LOCALE_POSN); 17583104Sphantom continue; 17683104Sphantom case '(': /* enclose negatives with () */ 17783104Sphantom if (flags & SIGN_POSN_USED) 17883104Sphantom goto format_error; 17983104Sphantom flags |= (SIGN_POSN_USED|PARENTH_POSN); 18083104Sphantom continue; 18183104Sphantom case '!': /* suppress currency symbol */ 18283104Sphantom flags |= SUPRESS_CURR_SYMBOL; 18383104Sphantom continue; 18483104Sphantom case '-': /* alignment (left) */ 18583104Sphantom flags |= LEFT_JUSTIFY; 18683104Sphantom continue; 18783104Sphantom default: 18883104Sphantom break; 18983104Sphantom } 19083104Sphantom break; 19183104Sphantom } 19283104Sphantom 19383104Sphantom /* field Width */ 19483104Sphantom if (isdigit((unsigned char)*fmt)) { 19583104Sphantom GET_NUMBER(width); 19683104Sphantom /* Do we have enough space to put number with 19783104Sphantom * required width ? 19883104Sphantom */ 199178312Sru if ((unsigned int)width >= maxsize - (dst - s)) 20083104Sphantom goto e2big_error; 20183104Sphantom } 202104942Stjr 203104942Stjr /* Left precision */ 204104942Stjr if (*fmt == '#') { 205104942Stjr if (!isdigit((unsigned char)*++fmt)) 206104942Stjr goto format_error; 207104942Stjr GET_NUMBER(left_prec); 208178312Sru if ((unsigned int)left_prec >= maxsize - (dst - s)) 209178312Sru goto e2big_error; 210104942Stjr } 211104942Stjr 212104942Stjr /* Right precision */ 213104942Stjr if (*fmt == '.') { 214104942Stjr if (!isdigit((unsigned char)*++fmt)) 215104942Stjr goto format_error; 216104942Stjr GET_NUMBER(right_prec); 217178312Sru if ((unsigned int)right_prec >= maxsize - (dst - s) - 218178312Sru left_prec) 219178312Sru goto e2big_error; 220104942Stjr } 221104942Stjr 22283104Sphantom /* Conversion Characters */ 22383104Sphantom switch (*fmt++) { 22483104Sphantom case 'i': /* use internaltion currency format */ 22583104Sphantom flags |= USE_INTL_CURRENCY; 22683104Sphantom break; 22783104Sphantom case 'n': /* use national currency format */ 22883104Sphantom flags &= ~(USE_INTL_CURRENCY); 22983104Sphantom break; 23083104Sphantom default: /* required character is missing or 23183104Sphantom premature EOS */ 23283104Sphantom goto format_error; 23383104Sphantom } 23483104Sphantom 235178313Sru if (currency_symbol != NULL) 236178313Sru free(currency_symbol); 23783104Sphantom if (flags & USE_INTL_CURRENCY) { 23883104Sphantom currency_symbol = strdup(lc->int_curr_symbol); 23983104Sphantom if (currency_symbol != NULL) 24083104Sphantom space_char = *(currency_symbol+3); 24183559Smike } else 24283104Sphantom currency_symbol = strdup(lc->currency_symbol); 24383104Sphantom 24483104Sphantom if (currency_symbol == NULL) 24583104Sphantom goto end_error; /* ENOMEM. */ 24683104Sphantom 24783104Sphantom /* value itself */ 24883104Sphantom value = va_arg(ap, double); 24983104Sphantom 25083104Sphantom /* detect sign */ 25183104Sphantom if (value < 0) { 25283104Sphantom flags |= IS_NEGATIVE; 25383104Sphantom value = -value; 25483104Sphantom } 25583104Sphantom 25683104Sphantom /* fill left_prec with amount of padding chars */ 25783104Sphantom if (left_prec >= 0) { 25883104Sphantom pad_size = __calc_left_pad((flags ^ IS_NEGATIVE), 25983104Sphantom currency_symbol) - 26083104Sphantom __calc_left_pad(flags, currency_symbol); 26183104Sphantom if (pad_size < 0) 26283104Sphantom pad_size = 0; 26383104Sphantom } 26483104Sphantom 265178313Sru if (asciivalue != NULL) 266178313Sru free(asciivalue); 26783104Sphantom asciivalue = __format_grouped_double(value, &flags, 26883104Sphantom left_prec, right_prec, pad_char); 26983104Sphantom if (asciivalue == NULL) 27083104Sphantom goto end_error; /* errno already set */ 27183104Sphantom /* to ENOMEM by malloc() */ 27283104Sphantom 27383104Sphantom /* set some variables for later use */ 27483104Sphantom __setup_vars(flags, &cs_precedes, &sep_by_space, 27583104Sphantom &sign_posn, &signstr); 27683104Sphantom 27783104Sphantom /* 27883104Sphantom * Description of some LC_MONETARY's values: 27983104Sphantom * 28083104Sphantom * p_cs_precedes & n_cs_precedes 28183104Sphantom * 28283104Sphantom * = 1 - $currency_symbol precedes the value 28383104Sphantom * for a monetary quantity with a non-negative value 28483104Sphantom * = 0 - symbol succeeds the value 28583104Sphantom * 28683104Sphantom * p_sep_by_space & n_sep_by_space 28783104Sphantom * 28883104Sphantom * = 0 - no space separates $currency_symbol 28983104Sphantom * from the value for a monetary quantity with a 29083104Sphantom * non-negative value 29183104Sphantom * = 1 - space separates the symbol from the value 29283104Sphantom * = 2 - space separates the symbol and the sign string, 29383104Sphantom * if adjacent. 29483104Sphantom * 29583104Sphantom * p_sign_posn & n_sign_posn 29683104Sphantom * 29783104Sphantom * = 0 - parentheses enclose the quantity and the 29883104Sphantom * $currency_symbol 29983104Sphantom * = 1 - the sign string precedes the quantity and the 30083104Sphantom * $currency_symbol 30183104Sphantom * = 2 - the sign string succeeds the quantity and the 30283104Sphantom * $currency_symbol 30383104Sphantom * = 3 - the sign string precedes the $currency_symbol 30483104Sphantom * = 4 - the sign string succeeds the $currency_symbol 30583104Sphantom * 30683104Sphantom */ 30783104Sphantom 30883104Sphantom tmpptr = dst; 30983104Sphantom 31083104Sphantom while (pad_size-- > 0) 31183104Sphantom PRINT(' '); 31283104Sphantom 313104943Stjr if (sign_posn == 0 && (flags & IS_NEGATIVE)) 314104943Stjr PRINT('('); 31583104Sphantom 31683104Sphantom if (cs_precedes == 1) { 31783104Sphantom if (sign_posn == 1 || sign_posn == 3) { 31883104Sphantom PRINTS(signstr); 31983104Sphantom if (sep_by_space == 2) /* XXX: ? */ 32083104Sphantom PRINT(' '); 32183104Sphantom } 32283104Sphantom 32383104Sphantom if (!(flags & SUPRESS_CURR_SYMBOL)) { 32483104Sphantom PRINTS(currency_symbol); 32583104Sphantom 32683104Sphantom if (sign_posn == 4) { 32783104Sphantom if (sep_by_space == 2) 32883104Sphantom PRINT(space_char); 32983559Smike PRINTS(signstr); 33083104Sphantom if (sep_by_space == 1) 33183104Sphantom PRINT(' '); 33283559Smike } else if (sep_by_space == 1) 33383559Smike PRINT(space_char); 33483104Sphantom } 33583559Smike } else if (sign_posn == 1) 33683559Smike PRINTS(signstr); 33783104Sphantom 33883104Sphantom PRINTS(asciivalue); 33983104Sphantom 34083104Sphantom if (cs_precedes == 0) { 34183104Sphantom if (sign_posn == 3) { 34283104Sphantom if (sep_by_space == 1) 34383104Sphantom PRINT(' '); 34483104Sphantom PRINTS(signstr); 34583104Sphantom } 34683104Sphantom 34783104Sphantom if (!(flags & SUPRESS_CURR_SYMBOL)) { 34883104Sphantom if ((sign_posn == 3 && sep_by_space == 2) 34983559Smike || (sep_by_space == 1 350104963Stjr && (sign_posn == 0 35183559Smike || sign_posn == 1 35283559Smike || sign_posn == 2 35383559Smike || sign_posn == 4))) 35483559Smike PRINT(space_char); 35583104Sphantom PRINTS(currency_symbol); /* XXX: len */ 35683104Sphantom if (sign_posn == 4) { 35783104Sphantom if (sep_by_space == 2) 35883104Sphantom PRINT(' '); 35983104Sphantom PRINTS(signstr); 36083104Sphantom } 36183104Sphantom } 36283104Sphantom } 36383104Sphantom 36483104Sphantom if (sign_posn == 2) { 36583104Sphantom if (sep_by_space == 2) 36683104Sphantom PRINT(' '); 36783104Sphantom PRINTS(signstr); 36883104Sphantom } 36983104Sphantom 37083104Sphantom if (sign_posn == 0 && (flags & IS_NEGATIVE)) 37183104Sphantom PRINT(')'); 37283104Sphantom 37383104Sphantom if (dst - tmpptr < width) { 37483104Sphantom if (flags & LEFT_JUSTIFY) { 375104963Stjr while (dst - tmpptr < width) 37683104Sphantom PRINT(' '); 37783104Sphantom } else { 37883104Sphantom pad_size = dst-tmpptr; 37983559Smike memmove(tmpptr + width-pad_size, tmpptr, 38083559Smike pad_size); 38183104Sphantom memset(tmpptr, ' ', width-pad_size); 38283104Sphantom dst += width-pad_size; 38383104Sphantom } 38483104Sphantom } 38583104Sphantom } 38683104Sphantom 38783104Sphantom PRINT('\0'); 388104963Stjr free(asciivalue); 389104963Stjr free(currency_symbol); 39083104Sphantom return (dst - s - 1); /* return size of put data except trailing '\0' */ 39183104Sphantom 39283104Sphantome2big_error: 39383104Sphantom errno = E2BIG; 39483104Sphantom goto end_error; 39583104Sphantom 39683104Sphantomformat_error: 39783104Sphantom errno = EINVAL; 39883104Sphantom 39983104Sphantomend_error: 400104946Stjr sverrno = errno; 40183104Sphantom if (asciivalue != NULL) 40283104Sphantom free(asciivalue); 40383104Sphantom if (currency_symbol != NULL) 40483104Sphantom free(currency_symbol); 405104946Stjr errno = sverrno; 40683104Sphantom return (-1); 40783104Sphantom} 408235785Stheravenssize_t 409235785Stheravenstrfmon_l(char * __restrict s, size_t maxsize, locale_t loc, const char * __restrict format, 410235785Stheraven ...) 411235785Stheraven{ 412235785Stheraven size_t ret; 413235785Stheraven va_list ap; 414235785Stheraven va_start(ap, format); 415235785Stheraven ret = vstrfmon_l(s, maxsize, loc, format, ap); 416235785Stheraven va_end(ap); 417235785Stheraven return ret; 418235785Stheraven} 41983104Sphantom 420235785Stheravenssize_t 421235785Stheravenstrfmon(char * __restrict s, size_t maxsize, const char * __restrict format, 422235785Stheraven ...) 423235785Stheraven{ 424235785Stheraven size_t ret; 425235785Stheraven va_list ap; 426235785Stheraven va_start(ap, format); 427235785Stheraven ret = vstrfmon_l(s, maxsize, __get_locale(), format, ap); 428235785Stheraven va_end(ap); 429235785Stheraven return ret; 430235785Stheraven} 431235785Stheraven 432235785Stheraven 43383104Sphantomstatic void 43483104Sphantom__setup_vars(int flags, char *cs_precedes, char *sep_by_space, 43583104Sphantom char *sign_posn, char **signstr) { 43683104Sphantom 43783104Sphantom struct lconv *lc = localeconv(); 43883104Sphantom 439104944Stjr if ((flags & IS_NEGATIVE) && (flags & USE_INTL_CURRENCY)) { 440104944Stjr *cs_precedes = lc->int_n_cs_precedes; 441104944Stjr *sep_by_space = lc->int_n_sep_by_space; 442104944Stjr *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_n_sign_posn; 443203734Scracauer *signstr = (lc->negative_sign[0] == '\0') ? "-" 444104944Stjr : lc->negative_sign; 445104944Stjr } else if (flags & USE_INTL_CURRENCY) { 446104944Stjr *cs_precedes = lc->int_p_cs_precedes; 447104944Stjr *sep_by_space = lc->int_p_sep_by_space; 448104944Stjr *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_p_sign_posn; 449104944Stjr *signstr = lc->positive_sign; 450104944Stjr } else if (flags & IS_NEGATIVE) { 45183104Sphantom *cs_precedes = lc->n_cs_precedes; 45283104Sphantom *sep_by_space = lc->n_sep_by_space; 45383559Smike *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->n_sign_posn; 454203734Scracauer *signstr = (lc->negative_sign[0] == '\0') ? "-" 45583559Smike : lc->negative_sign; 45683104Sphantom } else { 45783104Sphantom *cs_precedes = lc->p_cs_precedes; 45883104Sphantom *sep_by_space = lc->p_sep_by_space; 45983559Smike *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->p_sign_posn; 46083104Sphantom *signstr = lc->positive_sign; 46183104Sphantom } 46283104Sphantom 46383104Sphantom /* Set defult values for unspecified information. */ 46483104Sphantom if (*cs_precedes != 0) 46583104Sphantom *cs_precedes = 1; 46683104Sphantom if (*sep_by_space == CHAR_MAX) 46783104Sphantom *sep_by_space = 0; 46883104Sphantom if (*sign_posn == CHAR_MAX) 46983104Sphantom *sign_posn = 0; 47083104Sphantom} 47183104Sphantom 47283104Sphantomstatic int 47383104Sphantom__calc_left_pad(int flags, char *cur_symb) { 47483104Sphantom 47583104Sphantom char cs_precedes, sep_by_space, sign_posn, *signstr; 47683104Sphantom int left_chars = 0; 47783104Sphantom 47883104Sphantom __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr); 47983104Sphantom 48083104Sphantom if (cs_precedes != 0) { 48183104Sphantom left_chars += strlen(cur_symb); 48283104Sphantom if (sep_by_space != 0) 48383104Sphantom left_chars++; 48483104Sphantom } 48583104Sphantom 48683104Sphantom switch (sign_posn) { 48783104Sphantom case 1: 48883104Sphantom left_chars += strlen(signstr); 48983104Sphantom break; 49083104Sphantom case 3: 49183104Sphantom case 4: 49283104Sphantom if (cs_precedes != 0) 49383559Smike left_chars += strlen(signstr); 49483104Sphantom } 49583559Smike return (left_chars); 49683104Sphantom} 49783104Sphantom 49883104Sphantomstatic int 49983104Sphantomget_groups(int size, char *grouping) { 50083104Sphantom 50183104Sphantom int chars = 0; 50283104Sphantom 50383104Sphantom if (*grouping == CHAR_MAX || *grouping <= 0) /* no grouping ? */ 50483559Smike return (0); 50583104Sphantom 50683104Sphantom while (size > (int)*grouping) { 50783104Sphantom chars++; 50883104Sphantom size -= (int)*grouping++; 50983104Sphantom /* no more grouping ? */ 51083104Sphantom if (*grouping == CHAR_MAX) 51183104Sphantom break; 51283104Sphantom /* rest grouping with same value ? */ 51383104Sphantom if (*grouping == 0) { 51483104Sphantom chars += (size - 1) / *(grouping - 1); 51583104Sphantom break; 51683104Sphantom } 51783104Sphantom } 51883559Smike return (chars); 51983104Sphantom} 52083104Sphantom 52183104Sphantom/* convert double to ASCII */ 52283104Sphantomstatic char * 52383104Sphantom__format_grouped_double(double value, int *flags, 52483104Sphantom int left_prec, int right_prec, int pad_char) { 52583104Sphantom 52683104Sphantom char *rslt; 52783104Sphantom char *avalue; 52883104Sphantom int avalue_size; 52983104Sphantom char fmt[32]; 53083104Sphantom 53183104Sphantom size_t bufsize; 53283104Sphantom char *bufend; 53383104Sphantom 53483104Sphantom int padded; 53583104Sphantom 53683104Sphantom struct lconv *lc = localeconv(); 53783104Sphantom char *grouping; 53883104Sphantom char decimal_point; 53983104Sphantom char thousands_sep; 54083104Sphantom 54183104Sphantom int groups = 0; 54283104Sphantom 54383104Sphantom grouping = lc->mon_grouping; 54483104Sphantom decimal_point = *lc->mon_decimal_point; 545112427Sache if (decimal_point == '\0') 54683104Sphantom decimal_point = *lc->decimal_point; 54783104Sphantom thousands_sep = *lc->mon_thousands_sep; 54883559Smike if (thousands_sep == '\0') 54983104Sphantom thousands_sep = *lc->thousands_sep; 55083104Sphantom 55183104Sphantom /* fill left_prec with default value */ 55283104Sphantom if (left_prec == -1) 55383104Sphantom left_prec = 0; 55483104Sphantom 55583104Sphantom /* fill right_prec with default value */ 55683104Sphantom if (right_prec == -1) { 55783104Sphantom if (*flags & USE_INTL_CURRENCY) 55883104Sphantom right_prec = lc->int_frac_digits; 55983104Sphantom else 56083104Sphantom right_prec = lc->frac_digits; 56183104Sphantom 56283104Sphantom if (right_prec == CHAR_MAX) /* POSIX locale ? */ 56383104Sphantom right_prec = 2; 56483104Sphantom } 56583104Sphantom 56683104Sphantom if (*flags & NEED_GROUPING) 56783104Sphantom left_prec += get_groups(left_prec, grouping); 56883104Sphantom 56983104Sphantom /* convert to string */ 57083559Smike snprintf(fmt, sizeof(fmt), "%%%d.%df", left_prec + right_prec + 1, 57183559Smike right_prec); 57283104Sphantom avalue_size = asprintf(&avalue, fmt, value); 57383104Sphantom if (avalue_size < 0) 57483559Smike return (NULL); 57583104Sphantom 57683104Sphantom /* make sure that we've enough space for result string */ 57783104Sphantom bufsize = strlen(avalue)*2+1; 578178175Sdelphij rslt = calloc(1, bufsize); 57983104Sphantom if (rslt == NULL) { 58083104Sphantom free(avalue); 58183559Smike return (NULL); 58283104Sphantom } 58383104Sphantom bufend = rslt + bufsize - 1; /* reserve space for trailing '\0' */ 58483104Sphantom 58583104Sphantom /* skip spaces at beggining */ 58683104Sphantom padded = 0; 58783104Sphantom while (avalue[padded] == ' ') { 58883104Sphantom padded++; 58983104Sphantom avalue_size--; 59083104Sphantom } 59183104Sphantom 59283104Sphantom if (right_prec > 0) { 59383104Sphantom bufend -= right_prec; 59483559Smike memcpy(bufend, avalue + avalue_size+padded-right_prec, 59583559Smike right_prec); 59683104Sphantom *--bufend = decimal_point; 59783104Sphantom avalue_size -= (right_prec + 1); 59883104Sphantom } 59983104Sphantom 60083104Sphantom if ((*flags & NEED_GROUPING) && 60183559Smike thousands_sep != '\0' && /* XXX: need investigation */ 60283559Smike *grouping != CHAR_MAX && 60383559Smike *grouping > 0) { 60483104Sphantom while (avalue_size > (int)*grouping) { 60583104Sphantom GRPCPY(*grouping); 60683104Sphantom GRPSEP; 60783104Sphantom grouping++; 60883104Sphantom 60983104Sphantom /* no more grouping ? */ 61083104Sphantom if (*grouping == CHAR_MAX) 61183104Sphantom break; 61283104Sphantom 61383104Sphantom /* rest grouping with same value ? */ 61483104Sphantom if (*grouping == 0) { 61583104Sphantom grouping--; 61683104Sphantom while (avalue_size > *grouping) { 61783104Sphantom GRPCPY(*grouping); 61883104Sphantom GRPSEP; 61983104Sphantom } 62083104Sphantom } 62183104Sphantom } 62283104Sphantom if (avalue_size != 0) 62383104Sphantom GRPCPY(avalue_size); 62483104Sphantom padded -= groups; 62583104Sphantom 62683104Sphantom } else { 62783104Sphantom bufend -= avalue_size; 62883104Sphantom memcpy(bufend, avalue+padded, avalue_size); 62983104Sphantom if (right_prec == 0) 63083104Sphantom padded--; /* decrease assumed $decimal_point */ 63183104Sphantom } 63283104Sphantom 63383104Sphantom /* do padding with pad_char */ 63483104Sphantom if (padded > 0) { 63583104Sphantom bufend -= padded; 63683104Sphantom memset(bufend, pad_char, padded); 63783104Sphantom } 63883104Sphantom 639104963Stjr bufsize = bufsize - (bufend - rslt) + 1; 64083104Sphantom memmove(rslt, bufend, bufsize); 64183104Sphantom free(avalue); 64283559Smike return (rslt); 64383104Sphantom} 644