strfmon.c revision 178175
183104Sphantom/*- 287659Sphantom * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org> 383104Sphantom * All rights reserved. 483104Sphantom * 583104Sphantom * Redistribution and use in source and binary forms, with or without 683104Sphantom * modification, are permitted provided that the following conditions 783104Sphantom * are met: 883104Sphantom * 1. Redistributions of source code must retain the above copyright 983104Sphantom * notice, this list of conditions and the following disclaimer. 1083104Sphantom * 2. Redistributions in binary form must reproduce the above copyright 1183104Sphantom * notice, this list of conditions and the following disclaimer in the 1283104Sphantom * documentation and/or other materials provided with the distribution. 1383104Sphantom * 1483104Sphantom * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1583104Sphantom * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1683104Sphantom * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1783104Sphantom * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1883104Sphantom * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1983104Sphantom * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2083104Sphantom * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2183104Sphantom * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2283104Sphantom * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2383104Sphantom * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2483104Sphantom * SUCH DAMAGE. 2583104Sphantom * 2683104Sphantom */ 2783104Sphantom 2892986Sobrien#include <sys/cdefs.h> 2992986Sobrien__FBSDID("$FreeBSD: head/lib/libc/stdlib/strfmon.c 178175 2008-04-13 08:05:08Z delphij $"); 3083104Sphantom 3183104Sphantom#include <sys/types.h> 3283104Sphantom#include <ctype.h> 3383104Sphantom#include <errno.h> 3483104Sphantom#include <limits.h> 3583104Sphantom#include <locale.h> 36150065Sstefanf#include <monetary.h> 3783104Sphantom#include <stdarg.h> 3883104Sphantom#include <stdio.h> 3983104Sphantom#include <stdlib.h> 4083104Sphantom#include <string.h> 4183104Sphantom 4283104Sphantom/* internal flags */ 4383104Sphantom#define NEED_GROUPING 0x01 /* print digits grouped (default) */ 4483104Sphantom#define SIGN_POSN_USED 0x02 /* '+' or '(' usage flag */ 4583104Sphantom#define LOCALE_POSN 0x04 /* use locale defined +/- (default) */ 4683104Sphantom#define PARENTH_POSN 0x08 /* enclose negative amount in () */ 4783104Sphantom#define SUPRESS_CURR_SYMBOL 0x10 /* supress the currency from output */ 4883104Sphantom#define LEFT_JUSTIFY 0x20 /* left justify */ 4983104Sphantom#define USE_INTL_CURRENCY 0x40 /* use international currency symbol */ 5083104Sphantom#define IS_NEGATIVE 0x80 /* is argument value negative ? */ 5183104Sphantom 5283104Sphantom/* internal macros */ 5383559Smike#define PRINT(CH) do { \ 5483104Sphantom if (dst >= s + maxsize) \ 5583104Sphantom goto e2big_error; \ 5683104Sphantom *dst++ = CH; \ 5783559Smike} while (0) 5883104Sphantom 5983559Smike#define PRINTS(STR) do { \ 6083104Sphantom char *tmps = STR; \ 6183104Sphantom while (*tmps != '\0') \ 6283104Sphantom PRINT(*tmps++); \ 6383559Smike} while (0) 6483104Sphantom 6583559Smike#define GET_NUMBER(VAR) do { \ 6683104Sphantom VAR = 0; \ 6783104Sphantom while (isdigit((unsigned char)*fmt)) { \ 6883104Sphantom VAR *= 10; \ 6983104Sphantom VAR += *fmt - '0'; \ 7083104Sphantom fmt++; \ 7183104Sphantom } \ 7283559Smike} while (0) 7383104Sphantom 7483559Smike#define GRPCPY(howmany) do { \ 7583559Smike int i = howmany; \ 7683559Smike while (i-- > 0) { \ 7783559Smike avalue_size--; \ 7883559Smike *--bufend = *(avalue+avalue_size+padded); \ 7983559Smike } \ 8083559Smike} while (0) 8183559Smike 8283559Smike#define GRPSEP do { \ 8383559Smike *--bufend = thousands_sep; \ 8483559Smike groups++; \ 8583559Smike} while (0) 8683559Smike 8783104Sphantomstatic void __setup_vars(int, char *, char *, char *, char **); 8883104Sphantomstatic int __calc_left_pad(int, char *); 8983104Sphantomstatic char *__format_grouped_double(double, int *, int, int, int); 9083104Sphantom 9183104Sphantomssize_t 92103668Smikestrfmon(char * __restrict s, size_t maxsize, const char * __restrict format, 93103668Smike ...) 9483104Sphantom{ 9583104Sphantom va_list ap; 9683104Sphantom char *dst; /* output destination pointer */ 9783104Sphantom const char *fmt; /* current format poistion pointer */ 9883104Sphantom struct lconv *lc; /* pointer to lconv structure */ 9983104Sphantom char *asciivalue; /* formatted double pointer */ 10083104Sphantom 10183104Sphantom int flags; /* formatting options */ 10283104Sphantom int pad_char; /* padding character */ 10383104Sphantom int pad_size; /* pad size */ 10483104Sphantom int width; /* field width */ 10583104Sphantom int left_prec; /* left precision */ 10683104Sphantom int right_prec; /* right precision */ 10783104Sphantom double value; /* just value */ 10883104Sphantom char space_char = ' '; /* space after currency */ 10983104Sphantom 11083104Sphantom char cs_precedes, /* values gathered from struct lconv */ 11183104Sphantom sep_by_space, 11283104Sphantom sign_posn, 11383104Sphantom *signstr, 11483104Sphantom *currency_symbol; 11583104Sphantom 11683104Sphantom char *tmpptr; /* temporary vars */ 117104946Stjr int sverrno; 11883104Sphantom 11983104Sphantom va_start(ap, format); 12083104Sphantom 12183104Sphantom lc = localeconv(); 12283104Sphantom dst = s; 12383104Sphantom fmt = format; 12483104Sphantom asciivalue = NULL; 12583104Sphantom currency_symbol = NULL; 12683104Sphantom pad_size = 0; 12783104Sphantom 12883104Sphantom while (*fmt) { 12983104Sphantom /* pass nonformating characters AS IS */ 13083559Smike if (*fmt != '%') 13183104Sphantom goto literal; 13283104Sphantom 13383104Sphantom /* '%' found ! */ 13483104Sphantom 13583104Sphantom /* "%%" mean just '%' */ 13683104Sphantom if (*(fmt+1) == '%') { 13783104Sphantom fmt++; 13883104Sphantom literal: 13983104Sphantom PRINT(*fmt++); 14083104Sphantom continue; 14183104Sphantom } 14283104Sphantom 14383104Sphantom /* set up initial values */ 14483104Sphantom flags = (NEED_GROUPING|LOCALE_POSN); 14583104Sphantom pad_char = ' '; /* padding character is "space" */ 14683104Sphantom left_prec = -1; /* no left precision specified */ 14783104Sphantom right_prec = -1; /* no right precision specified */ 14883104Sphantom width = -1; /* no width specified */ 14983104Sphantom value = 0; /* we have no value to print now */ 15083104Sphantom 15183104Sphantom /* Flags */ 15283104Sphantom while (1) { 15383104Sphantom switch (*++fmt) { 15483104Sphantom case '=': /* fill character */ 15583104Sphantom pad_char = *++fmt; 15683104Sphantom if (pad_char == '\0') 15783104Sphantom goto format_error; 15883104Sphantom continue; 15983104Sphantom case '^': /* not group currency */ 16083104Sphantom flags &= ~(NEED_GROUPING); 16183104Sphantom continue; 16283104Sphantom case '+': /* use locale defined signs */ 16383104Sphantom if (flags & SIGN_POSN_USED) 16483104Sphantom goto format_error; 16583104Sphantom flags |= (SIGN_POSN_USED|LOCALE_POSN); 16683104Sphantom continue; 16783104Sphantom case '(': /* enclose negatives with () */ 16883104Sphantom if (flags & SIGN_POSN_USED) 16983104Sphantom goto format_error; 17083104Sphantom flags |= (SIGN_POSN_USED|PARENTH_POSN); 17183104Sphantom continue; 17283104Sphantom case '!': /* suppress currency symbol */ 17383104Sphantom flags |= SUPRESS_CURR_SYMBOL; 17483104Sphantom continue; 17583104Sphantom case '-': /* alignment (left) */ 17683104Sphantom flags |= LEFT_JUSTIFY; 17783104Sphantom continue; 17883104Sphantom default: 17983104Sphantom break; 18083104Sphantom } 18183104Sphantom break; 18283104Sphantom } 18383104Sphantom 18483104Sphantom /* field Width */ 18583104Sphantom if (isdigit((unsigned char)*fmt)) { 18683104Sphantom GET_NUMBER(width); 18783104Sphantom /* Do we have enough space to put number with 18883104Sphantom * required width ? 18983104Sphantom */ 19083104Sphantom if (dst + width >= s + maxsize) 19183104Sphantom goto e2big_error; 19283104Sphantom } 193104942Stjr 194104942Stjr /* Left precision */ 195104942Stjr if (*fmt == '#') { 196104942Stjr if (!isdigit((unsigned char)*++fmt)) 197104942Stjr goto format_error; 198104942Stjr GET_NUMBER(left_prec); 199104942Stjr } 200104942Stjr 201104942Stjr /* Right precision */ 202104942Stjr if (*fmt == '.') { 203104942Stjr if (!isdigit((unsigned char)*++fmt)) 204104942Stjr goto format_error; 205104942Stjr GET_NUMBER(right_prec); 206104942Stjr } 207104942Stjr 20883104Sphantom /* Conversion Characters */ 20983104Sphantom switch (*fmt++) { 21083104Sphantom case 'i': /* use internaltion currency format */ 21183104Sphantom flags |= USE_INTL_CURRENCY; 21283104Sphantom break; 21383104Sphantom case 'n': /* use national currency format */ 21483104Sphantom flags &= ~(USE_INTL_CURRENCY); 21583104Sphantom break; 21683104Sphantom default: /* required character is missing or 21783104Sphantom premature EOS */ 21883104Sphantom goto format_error; 21983104Sphantom } 22083104Sphantom 22183104Sphantom if (flags & USE_INTL_CURRENCY) { 22283104Sphantom currency_symbol = strdup(lc->int_curr_symbol); 22383104Sphantom if (currency_symbol != NULL) 22483104Sphantom space_char = *(currency_symbol+3); 22583559Smike } else 22683104Sphantom currency_symbol = strdup(lc->currency_symbol); 22783104Sphantom 22883104Sphantom if (currency_symbol == NULL) 22983104Sphantom goto end_error; /* ENOMEM. */ 23083104Sphantom 23183104Sphantom /* value itself */ 23283104Sphantom value = va_arg(ap, double); 23383104Sphantom 23483104Sphantom /* detect sign */ 23583104Sphantom if (value < 0) { 23683104Sphantom flags |= IS_NEGATIVE; 23783104Sphantom value = -value; 23883104Sphantom } 23983104Sphantom 24083104Sphantom /* fill left_prec with amount of padding chars */ 24183104Sphantom if (left_prec >= 0) { 24283104Sphantom pad_size = __calc_left_pad((flags ^ IS_NEGATIVE), 24383104Sphantom currency_symbol) - 24483104Sphantom __calc_left_pad(flags, currency_symbol); 24583104Sphantom if (pad_size < 0) 24683104Sphantom pad_size = 0; 24783104Sphantom } 24883104Sphantom 24983104Sphantom asciivalue = __format_grouped_double(value, &flags, 25083104Sphantom left_prec, right_prec, pad_char); 25183104Sphantom if (asciivalue == NULL) 25283104Sphantom goto end_error; /* errno already set */ 25383104Sphantom /* to ENOMEM by malloc() */ 25483104Sphantom 25583104Sphantom /* set some variables for later use */ 25683104Sphantom __setup_vars(flags, &cs_precedes, &sep_by_space, 25783104Sphantom &sign_posn, &signstr); 25883104Sphantom 25983104Sphantom /* 26083104Sphantom * Description of some LC_MONETARY's values: 26183104Sphantom * 26283104Sphantom * p_cs_precedes & n_cs_precedes 26383104Sphantom * 26483104Sphantom * = 1 - $currency_symbol precedes the value 26583104Sphantom * for a monetary quantity with a non-negative value 26683104Sphantom * = 0 - symbol succeeds the value 26783104Sphantom * 26883104Sphantom * p_sep_by_space & n_sep_by_space 26983104Sphantom * 27083104Sphantom * = 0 - no space separates $currency_symbol 27183104Sphantom * from the value for a monetary quantity with a 27283104Sphantom * non-negative value 27383104Sphantom * = 1 - space separates the symbol from the value 27483104Sphantom * = 2 - space separates the symbol and the sign string, 27583104Sphantom * if adjacent. 27683104Sphantom * 27783104Sphantom * p_sign_posn & n_sign_posn 27883104Sphantom * 27983104Sphantom * = 0 - parentheses enclose the quantity and the 28083104Sphantom * $currency_symbol 28183104Sphantom * = 1 - the sign string precedes the quantity and the 28283104Sphantom * $currency_symbol 28383104Sphantom * = 2 - the sign string succeeds the quantity and the 28483104Sphantom * $currency_symbol 28583104Sphantom * = 3 - the sign string precedes the $currency_symbol 28683104Sphantom * = 4 - the sign string succeeds the $currency_symbol 28783104Sphantom * 28883104Sphantom */ 28983104Sphantom 29083104Sphantom tmpptr = dst; 29183104Sphantom 29283104Sphantom while (pad_size-- > 0) 29383104Sphantom PRINT(' '); 29483104Sphantom 295104943Stjr if (sign_posn == 0 && (flags & IS_NEGATIVE)) 296104943Stjr PRINT('('); 29783104Sphantom 29883104Sphantom if (cs_precedes == 1) { 29983104Sphantom if (sign_posn == 1 || sign_posn == 3) { 30083104Sphantom PRINTS(signstr); 30183104Sphantom if (sep_by_space == 2) /* XXX: ? */ 30283104Sphantom PRINT(' '); 30383104Sphantom } 30483104Sphantom 30583104Sphantom if (!(flags & SUPRESS_CURR_SYMBOL)) { 30683104Sphantom PRINTS(currency_symbol); 30783104Sphantom 30883104Sphantom if (sign_posn == 4) { 30983104Sphantom if (sep_by_space == 2) 31083104Sphantom PRINT(space_char); 31183559Smike PRINTS(signstr); 31283104Sphantom if (sep_by_space == 1) 31383104Sphantom PRINT(' '); 31483559Smike } else if (sep_by_space == 1) 31583559Smike PRINT(space_char); 31683104Sphantom } 31783559Smike } else if (sign_posn == 1) 31883559Smike PRINTS(signstr); 31983104Sphantom 32083104Sphantom PRINTS(asciivalue); 32183104Sphantom 32283104Sphantom if (cs_precedes == 0) { 32383104Sphantom if (sign_posn == 3) { 32483104Sphantom if (sep_by_space == 1) 32583104Sphantom PRINT(' '); 32683104Sphantom PRINTS(signstr); 32783104Sphantom } 32883104Sphantom 32983104Sphantom if (!(flags & SUPRESS_CURR_SYMBOL)) { 33083104Sphantom if ((sign_posn == 3 && sep_by_space == 2) 33183559Smike || (sep_by_space == 1 332104963Stjr && (sign_posn == 0 33383559Smike || sign_posn == 1 33483559Smike || sign_posn == 2 33583559Smike || sign_posn == 4))) 33683559Smike PRINT(space_char); 33783104Sphantom PRINTS(currency_symbol); /* XXX: len */ 33883104Sphantom if (sign_posn == 4) { 33983104Sphantom if (sep_by_space == 2) 34083104Sphantom PRINT(' '); 34183104Sphantom PRINTS(signstr); 34283104Sphantom } 34383104Sphantom } 34483104Sphantom } 34583104Sphantom 34683104Sphantom if (sign_posn == 2) { 34783104Sphantom if (sep_by_space == 2) 34883104Sphantom PRINT(' '); 34983104Sphantom PRINTS(signstr); 35083104Sphantom } 35183104Sphantom 35283104Sphantom if (sign_posn == 0 && (flags & IS_NEGATIVE)) 35383104Sphantom PRINT(')'); 35483104Sphantom 35583104Sphantom if (dst - tmpptr < width) { 35683104Sphantom if (flags & LEFT_JUSTIFY) { 357104963Stjr while (dst - tmpptr < width) 35883104Sphantom PRINT(' '); 35983104Sphantom } else { 36083104Sphantom pad_size = dst-tmpptr; 36183559Smike memmove(tmpptr + width-pad_size, tmpptr, 36283559Smike pad_size); 36383104Sphantom memset(tmpptr, ' ', width-pad_size); 36483104Sphantom dst += width-pad_size; 36583104Sphantom } 36683104Sphantom } 36783104Sphantom } 36883104Sphantom 36983104Sphantom PRINT('\0'); 37083104Sphantom va_end(ap); 371104963Stjr free(asciivalue); 372104963Stjr free(currency_symbol); 37383104Sphantom return (dst - s - 1); /* return size of put data except trailing '\0' */ 37483104Sphantom 37583104Sphantome2big_error: 37683104Sphantom errno = E2BIG; 37783104Sphantom goto end_error; 37883104Sphantom 37983104Sphantomformat_error: 38083104Sphantom errno = EINVAL; 38183104Sphantom 38283104Sphantomend_error: 383104946Stjr sverrno = errno; 38483104Sphantom if (asciivalue != NULL) 38583104Sphantom free(asciivalue); 38683104Sphantom if (currency_symbol != NULL) 38783104Sphantom free(currency_symbol); 388104946Stjr errno = sverrno; 38983104Sphantom va_end(ap); 39083104Sphantom return (-1); 39183104Sphantom} 39283104Sphantom 39383104Sphantomstatic void 39483104Sphantom__setup_vars(int flags, char *cs_precedes, char *sep_by_space, 39583104Sphantom char *sign_posn, char **signstr) { 39683104Sphantom 39783104Sphantom struct lconv *lc = localeconv(); 39883104Sphantom 399104944Stjr if ((flags & IS_NEGATIVE) && (flags & USE_INTL_CURRENCY)) { 400104944Stjr *cs_precedes = lc->int_n_cs_precedes; 401104944Stjr *sep_by_space = lc->int_n_sep_by_space; 402104944Stjr *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_n_sign_posn; 403104944Stjr *signstr = (lc->negative_sign == '\0') ? "-" 404104944Stjr : lc->negative_sign; 405104944Stjr } else if (flags & USE_INTL_CURRENCY) { 406104944Stjr *cs_precedes = lc->int_p_cs_precedes; 407104944Stjr *sep_by_space = lc->int_p_sep_by_space; 408104944Stjr *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_p_sign_posn; 409104944Stjr *signstr = lc->positive_sign; 410104944Stjr } else if (flags & IS_NEGATIVE) { 41183104Sphantom *cs_precedes = lc->n_cs_precedes; 41283104Sphantom *sep_by_space = lc->n_sep_by_space; 41383559Smike *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->n_sign_posn; 41483559Smike *signstr = (lc->negative_sign == '\0') ? "-" 41583559Smike : lc->negative_sign; 41683104Sphantom } else { 41783104Sphantom *cs_precedes = lc->p_cs_precedes; 41883104Sphantom *sep_by_space = lc->p_sep_by_space; 41983559Smike *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->p_sign_posn; 42083104Sphantom *signstr = lc->positive_sign; 42183104Sphantom } 42283104Sphantom 42383104Sphantom /* Set defult values for unspecified information. */ 42483104Sphantom if (*cs_precedes != 0) 42583104Sphantom *cs_precedes = 1; 42683104Sphantom if (*sep_by_space == CHAR_MAX) 42783104Sphantom *sep_by_space = 0; 42883104Sphantom if (*sign_posn == CHAR_MAX) 42983104Sphantom *sign_posn = 0; 43083104Sphantom} 43183104Sphantom 43283104Sphantomstatic int 43383104Sphantom__calc_left_pad(int flags, char *cur_symb) { 43483104Sphantom 43583104Sphantom char cs_precedes, sep_by_space, sign_posn, *signstr; 43683104Sphantom int left_chars = 0; 43783104Sphantom 43883104Sphantom __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr); 43983104Sphantom 44083104Sphantom if (cs_precedes != 0) { 44183104Sphantom left_chars += strlen(cur_symb); 44283104Sphantom if (sep_by_space != 0) 44383104Sphantom left_chars++; 44483104Sphantom } 44583104Sphantom 44683104Sphantom switch (sign_posn) { 44783104Sphantom case 1: 44883104Sphantom left_chars += strlen(signstr); 44983104Sphantom break; 45083104Sphantom case 3: 45183104Sphantom case 4: 45283104Sphantom if (cs_precedes != 0) 45383559Smike left_chars += strlen(signstr); 45483104Sphantom } 45583559Smike return (left_chars); 45683104Sphantom} 45783104Sphantom 45883104Sphantomstatic int 45983104Sphantomget_groups(int size, char *grouping) { 46083104Sphantom 46183104Sphantom int chars = 0; 46283104Sphantom 46383104Sphantom if (*grouping == CHAR_MAX || *grouping <= 0) /* no grouping ? */ 46483559Smike return (0); 46583104Sphantom 46683104Sphantom while (size > (int)*grouping) { 46783104Sphantom chars++; 46883104Sphantom size -= (int)*grouping++; 46983104Sphantom /* no more grouping ? */ 47083104Sphantom if (*grouping == CHAR_MAX) 47183104Sphantom break; 47283104Sphantom /* rest grouping with same value ? */ 47383104Sphantom if (*grouping == 0) { 47483104Sphantom chars += (size - 1) / *(grouping - 1); 47583104Sphantom break; 47683104Sphantom } 47783104Sphantom } 47883559Smike return (chars); 47983104Sphantom} 48083104Sphantom 48183104Sphantom/* convert double to ASCII */ 48283104Sphantomstatic char * 48383104Sphantom__format_grouped_double(double value, int *flags, 48483104Sphantom int left_prec, int right_prec, int pad_char) { 48583104Sphantom 48683104Sphantom char *rslt; 48783104Sphantom char *avalue; 48883104Sphantom int avalue_size; 48983104Sphantom char fmt[32]; 49083104Sphantom 49183104Sphantom size_t bufsize; 49283104Sphantom char *bufend; 49383104Sphantom 49483104Sphantom int padded; 49583104Sphantom 49683104Sphantom struct lconv *lc = localeconv(); 49783104Sphantom char *grouping; 49883104Sphantom char decimal_point; 49983104Sphantom char thousands_sep; 50083104Sphantom 50183104Sphantom int groups = 0; 50283104Sphantom 50383104Sphantom grouping = lc->mon_grouping; 50483104Sphantom decimal_point = *lc->mon_decimal_point; 505112427Sache if (decimal_point == '\0') 50683104Sphantom decimal_point = *lc->decimal_point; 50783104Sphantom thousands_sep = *lc->mon_thousands_sep; 50883559Smike if (thousands_sep == '\0') 50983104Sphantom thousands_sep = *lc->thousands_sep; 51083104Sphantom 51183104Sphantom /* fill left_prec with default value */ 51283104Sphantom if (left_prec == -1) 51383104Sphantom left_prec = 0; 51483104Sphantom 51583104Sphantom /* fill right_prec with default value */ 51683104Sphantom if (right_prec == -1) { 51783104Sphantom if (*flags & USE_INTL_CURRENCY) 51883104Sphantom right_prec = lc->int_frac_digits; 51983104Sphantom else 52083104Sphantom right_prec = lc->frac_digits; 52183104Sphantom 52283104Sphantom if (right_prec == CHAR_MAX) /* POSIX locale ? */ 52383104Sphantom right_prec = 2; 52483104Sphantom } 52583104Sphantom 52683104Sphantom if (*flags & NEED_GROUPING) 52783104Sphantom left_prec += get_groups(left_prec, grouping); 52883104Sphantom 52983104Sphantom /* convert to string */ 53083559Smike snprintf(fmt, sizeof(fmt), "%%%d.%df", left_prec + right_prec + 1, 53183559Smike right_prec); 53283104Sphantom avalue_size = asprintf(&avalue, fmt, value); 53383104Sphantom if (avalue_size < 0) 53483559Smike return (NULL); 53583104Sphantom 53683104Sphantom /* make sure that we've enough space for result string */ 53783104Sphantom bufsize = strlen(avalue)*2+1; 538178175Sdelphij rslt = calloc(1, bufsize); 53983104Sphantom if (rslt == NULL) { 54083104Sphantom free(avalue); 54183559Smike return (NULL); 54283104Sphantom } 54383104Sphantom bufend = rslt + bufsize - 1; /* reserve space for trailing '\0' */ 54483104Sphantom 54583104Sphantom /* skip spaces at beggining */ 54683104Sphantom padded = 0; 54783104Sphantom while (avalue[padded] == ' ') { 54883104Sphantom padded++; 54983104Sphantom avalue_size--; 55083104Sphantom } 55183104Sphantom 55283104Sphantom if (right_prec > 0) { 55383104Sphantom bufend -= right_prec; 55483559Smike memcpy(bufend, avalue + avalue_size+padded-right_prec, 55583559Smike right_prec); 55683104Sphantom *--bufend = decimal_point; 55783104Sphantom avalue_size -= (right_prec + 1); 55883104Sphantom } 55983104Sphantom 56083104Sphantom if ((*flags & NEED_GROUPING) && 56183559Smike thousands_sep != '\0' && /* XXX: need investigation */ 56283559Smike *grouping != CHAR_MAX && 56383559Smike *grouping > 0) { 56483104Sphantom while (avalue_size > (int)*grouping) { 56583104Sphantom GRPCPY(*grouping); 56683104Sphantom GRPSEP; 56783104Sphantom grouping++; 56883104Sphantom 56983104Sphantom /* no more grouping ? */ 57083104Sphantom if (*grouping == CHAR_MAX) 57183104Sphantom break; 57283104Sphantom 57383104Sphantom /* rest grouping with same value ? */ 57483104Sphantom if (*grouping == 0) { 57583104Sphantom grouping--; 57683104Sphantom while (avalue_size > *grouping) { 57783104Sphantom GRPCPY(*grouping); 57883104Sphantom GRPSEP; 57983104Sphantom } 58083104Sphantom } 58183104Sphantom } 58283104Sphantom if (avalue_size != 0) 58383104Sphantom GRPCPY(avalue_size); 58483104Sphantom padded -= groups; 58583104Sphantom 58683104Sphantom } else { 58783104Sphantom bufend -= avalue_size; 58883104Sphantom memcpy(bufend, avalue+padded, avalue_size); 58983104Sphantom if (right_prec == 0) 59083104Sphantom padded--; /* decrease assumed $decimal_point */ 59183104Sphantom } 59283104Sphantom 59383104Sphantom /* do padding with pad_char */ 59483104Sphantom if (padded > 0) { 59583104Sphantom bufend -= padded; 59683104Sphantom memset(bufend, pad_char, padded); 59783104Sphantom } 59883104Sphantom 599104963Stjr bufsize = bufsize - (bufend - rslt) + 1; 60083104Sphantom memmove(rslt, bufend, bufsize); 60183104Sphantom free(avalue); 60283559Smike return (rslt); 60383104Sphantom} 604