strfmon.c revision 83559
183104Sphantom/*- 283104Sphantom * Copyright (c) 2001 Alexey Zelkin 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 2883104Sphantom#if defined(LIBC_RCS) && !defined(lint) 2983104Sphantomstatic char rcsid[] = "$FreeBSD: head/lib/libc/stdlib/strfmon.c 83559 2001-09-17 00:23:19Z mike $"; 3083104Sphantom#endif 3183104Sphantom 3283104Sphantom#include <sys/types.h> 3383104Sphantom#include <ctype.h> 3483104Sphantom#include <errno.h> 3583104Sphantom#include <limits.h> 3683104Sphantom#include <locale.h> 3783104Sphantom#if __STDC__ 3883104Sphantom#include <stdarg.h> 3983104Sphantom#else 4083104Sphantom#include <varargs.h> 4183104Sphantom#endif 4283104Sphantom#include <stdio.h> 4383104Sphantom#include <stdlib.h> 4483104Sphantom#include <string.h> 4583104Sphantom 4683104Sphantom/* internal flags */ 4783104Sphantom#define NEED_GROUPING 0x01 /* print digits grouped (default) */ 4883104Sphantom#define SIGN_POSN_USED 0x02 /* '+' or '(' usage flag */ 4983104Sphantom#define LOCALE_POSN 0x04 /* use locale defined +/- (default) */ 5083104Sphantom#define PARENTH_POSN 0x08 /* enclose negative amount in () */ 5183104Sphantom#define SUPRESS_CURR_SYMBOL 0x10 /* supress the currency from output */ 5283104Sphantom#define LEFT_JUSTIFY 0x20 /* left justify */ 5383104Sphantom#define USE_INTL_CURRENCY 0x40 /* use international currency symbol */ 5483104Sphantom#define IS_NEGATIVE 0x80 /* is argument value negative ? */ 5583104Sphantom 5683104Sphantom/* internal macros */ 5783559Smike#define PRINT(CH) do { \ 5883104Sphantom if (dst >= s + maxsize) \ 5983104Sphantom goto e2big_error; \ 6083104Sphantom *dst++ = CH; \ 6183559Smike} while (0) 6283104Sphantom 6383559Smike#define PRINTS(STR) do { \ 6483104Sphantom char *tmps = STR; \ 6583104Sphantom while (*tmps != '\0') \ 6683104Sphantom PRINT(*tmps++); \ 6783559Smike} while (0) 6883104Sphantom 6983559Smike#define GET_NUMBER(VAR) do { \ 7083104Sphantom VAR = 0; \ 7183104Sphantom while (isdigit((unsigned char)*fmt)) { \ 7283104Sphantom VAR *= 10; \ 7383104Sphantom VAR += *fmt - '0'; \ 7483104Sphantom fmt++; \ 7583104Sphantom } \ 7683559Smike} while (0) 7783104Sphantom 7883559Smike#define GRPCPY(howmany) do { \ 7983559Smike int i = howmany; \ 8083559Smike while (i-- > 0) { \ 8183559Smike avalue_size--; \ 8283559Smike *--bufend = *(avalue+avalue_size+padded); \ 8383559Smike } \ 8483559Smike} while (0) 8583559Smike 8683559Smike#define GRPSEP do { \ 8783559Smike *--bufend = thousands_sep; \ 8883559Smike groups++; \ 8983559Smike} while (0) 9083559Smike 9183104Sphantomstatic void __setup_vars(int, char *, char *, char *, char **); 9283104Sphantomstatic int __calc_left_pad(int, char *); 9383104Sphantomstatic char *__format_grouped_double(double, int *, int, int, int); 9483104Sphantom 9583104Sphantom#if __STDC__ 9683104Sphantomssize_t 9783104Sphantomstrfmon(char *s, size_t maxsize, const char *format, ...) 9883104Sphantom#else 9983104Sphantomssize_t 10083104Sphantomstrfmon(s, maxsize, format, va_alist) 10183104Sphantom char *s; 10283104Sphantom size_t maxsize; 10383104Sphantom const char *format; 10483104Sphantom va_dcl 10583104Sphantom#endif 10683104Sphantom{ 10783104Sphantom va_list ap; 10883104Sphantom char *dst; /* output destination pointer */ 10983104Sphantom const char *fmt; /* current format poistion pointer */ 11083104Sphantom struct lconv *lc; /* pointer to lconv structure */ 11183104Sphantom char *asciivalue; /* formatted double pointer */ 11283104Sphantom 11383104Sphantom int flags; /* formatting options */ 11483104Sphantom int pad_char; /* padding character */ 11583104Sphantom int pad_size; /* pad size */ 11683104Sphantom int width; /* field width */ 11783104Sphantom int left_prec; /* left precision */ 11883104Sphantom int right_prec; /* right precision */ 11983104Sphantom double value; /* just value */ 12083104Sphantom char space_char = ' '; /* space after currency */ 12183104Sphantom 12283104Sphantom char cs_precedes, /* values gathered from struct lconv */ 12383104Sphantom sep_by_space, 12483104Sphantom sign_posn, 12583104Sphantom *signstr, 12683104Sphantom *currency_symbol; 12783104Sphantom 12883104Sphantom char *tmpptr; /* temporary vars */ 12983104Sphantom int *ntmp; 13083104Sphantom 13183104Sphantom#if __STDC__ 13283104Sphantom va_start(ap, format); 13383104Sphantom#else 13483104Sphantom va_start(ap); 13583104Sphantom#endif 13683104Sphantom 13783104Sphantom lc = localeconv(); 13883104Sphantom dst = s; 13983104Sphantom fmt = format; 14083104Sphantom asciivalue = NULL; 14183104Sphantom currency_symbol = NULL; 14283104Sphantom pad_size = 0; 14383104Sphantom 14483104Sphantom while (*fmt) { 14583104Sphantom /* pass nonformating characters AS IS */ 14683559Smike if (*fmt != '%') 14783104Sphantom goto literal; 14883104Sphantom 14983104Sphantom /* '%' found ! */ 15083104Sphantom 15183104Sphantom /* "%%" mean just '%' */ 15283104Sphantom if (*(fmt+1) == '%') { 15383104Sphantom fmt++; 15483104Sphantom literal: 15583104Sphantom PRINT(*fmt++); 15683104Sphantom continue; 15783104Sphantom } 15883104Sphantom 15983104Sphantom /* set up initial values */ 16083104Sphantom flags = (NEED_GROUPING|LOCALE_POSN); 16183104Sphantom pad_char = ' '; /* padding character is "space" */ 16283104Sphantom left_prec = -1; /* no left precision specified */ 16383104Sphantom right_prec = -1; /* no right precision specified */ 16483104Sphantom width = -1; /* no width specified */ 16583104Sphantom value = 0; /* we have no value to print now */ 16683104Sphantom 16783104Sphantom /* Flags */ 16883104Sphantom while (1) { 16983104Sphantom switch (*++fmt) { 17083104Sphantom case '=': /* fill character */ 17183104Sphantom pad_char = *++fmt; 17283104Sphantom if (pad_char == '\0') 17383104Sphantom goto format_error; 17483104Sphantom continue; 17583104Sphantom case '^': /* not group currency */ 17683104Sphantom flags &= ~(NEED_GROUPING); 17783104Sphantom continue; 17883104Sphantom case '+': /* use locale defined signs */ 17983104Sphantom if (flags & SIGN_POSN_USED) 18083104Sphantom goto format_error; 18183104Sphantom flags |= (SIGN_POSN_USED|LOCALE_POSN); 18283104Sphantom continue; 18383104Sphantom case '(': /* enclose negatives with () */ 18483104Sphantom if (flags & SIGN_POSN_USED) 18583104Sphantom goto format_error; 18683104Sphantom flags |= (SIGN_POSN_USED|PARENTH_POSN); 18783104Sphantom continue; 18883104Sphantom case '!': /* suppress currency symbol */ 18983104Sphantom flags |= SUPRESS_CURR_SYMBOL; 19083104Sphantom continue; 19183104Sphantom case '-': /* alignment (left) */ 19283104Sphantom flags |= LEFT_JUSTIFY; 19383104Sphantom continue; 19483104Sphantom case '#': /* left || right precision */ 19583104Sphantom case '.': 19683104Sphantom if (*fmt == '#') 19783104Sphantom ntmp = &left_prec; 19883104Sphantom else 19983104Sphantom ntmp = &right_prec; 20083104Sphantom 20183104Sphantom if (!isdigit((unsigned char)*++fmt)) 20283104Sphantom goto format_error; 20383104Sphantom GET_NUMBER(*ntmp); 20483104Sphantom fmt--; 20583104Sphantom continue; 20683104Sphantom default: 20783104Sphantom break; 20883104Sphantom } 20983104Sphantom break; 21083104Sphantom } 21183104Sphantom 21283104Sphantom /* field Width */ 21383104Sphantom if (isdigit((unsigned char)*fmt)) { 21483104Sphantom GET_NUMBER(width); 21583104Sphantom /* Do we have enough space to put number with 21683104Sphantom * required width ? 21783104Sphantom */ 21883104Sphantom if (dst + width >= s + maxsize) 21983104Sphantom goto e2big_error; 22083104Sphantom } 22183104Sphantom 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 23583104Sphantom if (flags & USE_INTL_CURRENCY) { 23683104Sphantom currency_symbol = strdup(lc->int_curr_symbol); 23783104Sphantom if (currency_symbol != NULL) 23883104Sphantom space_char = *(currency_symbol+3); 23983559Smike } else 24083104Sphantom currency_symbol = strdup(lc->currency_symbol); 24183104Sphantom 24283104Sphantom if (currency_symbol == NULL) 24383104Sphantom goto end_error; /* ENOMEM. */ 24483104Sphantom 24583104Sphantom /* value itself */ 24683104Sphantom value = va_arg(ap, double); 24783104Sphantom 24883104Sphantom /* detect sign */ 24983104Sphantom if (value < 0) { 25083104Sphantom flags |= IS_NEGATIVE; 25183104Sphantom value = -value; 25283104Sphantom } 25383104Sphantom 25483104Sphantom /* fill left_prec with amount of padding chars */ 25583104Sphantom if (left_prec >= 0) { 25683104Sphantom pad_size = __calc_left_pad((flags ^ IS_NEGATIVE), 25783104Sphantom currency_symbol) - 25883104Sphantom __calc_left_pad(flags, currency_symbol); 25983104Sphantom if (pad_size < 0) 26083104Sphantom pad_size = 0; 26183104Sphantom } 26283104Sphantom 26383104Sphantom asciivalue = __format_grouped_double(value, &flags, 26483104Sphantom left_prec, right_prec, pad_char); 26583104Sphantom if (asciivalue == NULL) 26683104Sphantom goto end_error; /* errno already set */ 26783104Sphantom /* to ENOMEM by malloc() */ 26883104Sphantom 26983104Sphantom /* set some variables for later use */ 27083104Sphantom __setup_vars(flags, &cs_precedes, &sep_by_space, 27183104Sphantom &sign_posn, &signstr); 27283104Sphantom 27383104Sphantom /* 27483104Sphantom * Description of some LC_MONETARY's values: 27583104Sphantom * 27683104Sphantom * p_cs_precedes & n_cs_precedes 27783104Sphantom * 27883104Sphantom * = 1 - $currency_symbol precedes the value 27983104Sphantom * for a monetary quantity with a non-negative value 28083104Sphantom * = 0 - symbol succeeds the value 28183104Sphantom * 28283104Sphantom * p_sep_by_space & n_sep_by_space 28383104Sphantom * 28483104Sphantom * = 0 - no space separates $currency_symbol 28583104Sphantom * from the value for a monetary quantity with a 28683104Sphantom * non-negative value 28783104Sphantom * = 1 - space separates the symbol from the value 28883104Sphantom * = 2 - space separates the symbol and the sign string, 28983104Sphantom * if adjacent. 29083104Sphantom * 29183104Sphantom * p_sign_posn & n_sign_posn 29283104Sphantom * 29383104Sphantom * = 0 - parentheses enclose the quantity and the 29483104Sphantom * $currency_symbol 29583104Sphantom * = 1 - the sign string precedes the quantity and the 29683104Sphantom * $currency_symbol 29783104Sphantom * = 2 - the sign string succeeds the quantity and the 29883104Sphantom * $currency_symbol 29983104Sphantom * = 3 - the sign string precedes the $currency_symbol 30083104Sphantom * = 4 - the sign string succeeds the $currency_symbol 30183104Sphantom * 30283104Sphantom */ 30383104Sphantom 30483104Sphantom tmpptr = dst; 30583104Sphantom 30683104Sphantom while (pad_size-- > 0) 30783104Sphantom PRINT(' '); 30883104Sphantom 30983104Sphantom if (sign_posn == 0) { 31083559Smike if (flags & IS_NEGATIVE) 31183104Sphantom PRINT('('); 31283559Smike else 31383104Sphantom PRINT(' '); 31483104Sphantom } 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 35083559Smike && (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) { 37583104Sphantom 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'); 38883104Sphantom va_end(ap); 38983104Sphantom return (dst - s - 1); /* return size of put data except trailing '\0' */ 39083104Sphantom 39183104Sphantome2big_error: 39283104Sphantom errno = E2BIG; 39383104Sphantom goto end_error; 39483104Sphantom 39583104Sphantomformat_error: 39683104Sphantom errno = EINVAL; 39783104Sphantom 39883104Sphantomend_error: 39983104Sphantom if (asciivalue != NULL) 40083104Sphantom free(asciivalue); 40183104Sphantom if (currency_symbol != NULL) 40283104Sphantom free(currency_symbol); 40383104Sphantom va_end(ap); 40483104Sphantom return (-1); 40583104Sphantom} 40683104Sphantom 40783104Sphantomstatic void 40883104Sphantom__setup_vars(int flags, char *cs_precedes, char *sep_by_space, 40983104Sphantom char *sign_posn, char **signstr) { 41083104Sphantom 41183104Sphantom struct lconv *lc = localeconv(); 41283104Sphantom 41383104Sphantom if (flags & IS_NEGATIVE) { 41483104Sphantom *cs_precedes = lc->n_cs_precedes; 41583104Sphantom *sep_by_space = lc->n_sep_by_space; 41683559Smike *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->n_sign_posn; 41783559Smike *signstr = (lc->negative_sign == '\0') ? "-" 41883559Smike : lc->negative_sign; 41983104Sphantom } else { 42083104Sphantom *cs_precedes = lc->p_cs_precedes; 42183104Sphantom *sep_by_space = lc->p_sep_by_space; 42283559Smike *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->p_sign_posn; 42383104Sphantom *signstr = lc->positive_sign; 42483104Sphantom } 42583104Sphantom 42683104Sphantom /* Set defult values for unspecified information. */ 42783104Sphantom if (*cs_precedes != 0) 42883104Sphantom *cs_precedes = 1; 42983104Sphantom if (*sep_by_space == CHAR_MAX) 43083104Sphantom *sep_by_space = 0; 43183104Sphantom if (*sign_posn == CHAR_MAX) 43283104Sphantom *sign_posn = 0; 43383104Sphantom} 43483104Sphantom 43583104Sphantomstatic int 43683104Sphantom__calc_left_pad(int flags, char *cur_symb) { 43783104Sphantom 43883104Sphantom char cs_precedes, sep_by_space, sign_posn, *signstr; 43983104Sphantom int left_chars = 0; 44083104Sphantom 44183104Sphantom __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr); 44283104Sphantom 44383104Sphantom if (cs_precedes != 0) { 44483104Sphantom left_chars += strlen(cur_symb); 44583104Sphantom if (sep_by_space != 0) 44683104Sphantom left_chars++; 44783104Sphantom } 44883104Sphantom 44983104Sphantom switch (sign_posn) { 45083104Sphantom case 1: 45183104Sphantom left_chars += strlen(signstr); 45283104Sphantom break; 45383104Sphantom case 3: 45483104Sphantom case 4: 45583104Sphantom if (cs_precedes != 0) 45683559Smike left_chars += strlen(signstr); 45783104Sphantom } 45883559Smike return (left_chars); 45983104Sphantom} 46083104Sphantom 46183104Sphantomstatic int 46283104Sphantomget_groups(int size, char *grouping) { 46383104Sphantom 46483104Sphantom int chars = 0; 46583104Sphantom 46683104Sphantom if (*grouping == CHAR_MAX || *grouping <= 0) /* no grouping ? */ 46783559Smike return (0); 46883104Sphantom 46983104Sphantom while (size > (int)*grouping) { 47083104Sphantom chars++; 47183104Sphantom size -= (int)*grouping++; 47283104Sphantom /* no more grouping ? */ 47383104Sphantom if (*grouping == CHAR_MAX) 47483104Sphantom break; 47583104Sphantom /* rest grouping with same value ? */ 47683104Sphantom if (*grouping == 0) { 47783104Sphantom chars += (size - 1) / *(grouping - 1); 47883104Sphantom break; 47983104Sphantom } 48083104Sphantom } 48183559Smike return (chars); 48283104Sphantom} 48383104Sphantom 48483104Sphantom/* convert double to ASCII */ 48583104Sphantomstatic char * 48683104Sphantom__format_grouped_double(double value, int *flags, 48783104Sphantom int left_prec, int right_prec, int pad_char) { 48883104Sphantom 48983104Sphantom char *rslt; 49083104Sphantom char *avalue; 49183104Sphantom int avalue_size; 49283104Sphantom char fmt[32]; 49383104Sphantom 49483104Sphantom size_t bufsize; 49583104Sphantom char *bufend; 49683104Sphantom 49783104Sphantom int padded; 49883104Sphantom 49983104Sphantom struct lconv *lc = localeconv(); 50083104Sphantom char *grouping; 50183104Sphantom char decimal_point; 50283104Sphantom char thousands_sep; 50383104Sphantom 50483104Sphantom int groups = 0; 50583104Sphantom 50683104Sphantom grouping = lc->mon_grouping; 50783104Sphantom decimal_point = *lc->mon_decimal_point; 50883104Sphantom if (decimal_point == '\0') { 50983104Sphantom decimal_point = *lc->decimal_point; 51083104Sphantom if (decimal_point == '\0') 51183104Sphantom decimal_point = '.'; 51283104Sphantom } 51383104Sphantom thousands_sep = *lc->mon_thousands_sep; 51483559Smike if (thousands_sep == '\0') 51583104Sphantom thousands_sep = *lc->thousands_sep; 51683104Sphantom 51783104Sphantom /* fill left_prec with default value */ 51883104Sphantom if (left_prec == -1) 51983104Sphantom left_prec = 0; 52083104Sphantom 52183104Sphantom /* fill right_prec with default value */ 52283104Sphantom if (right_prec == -1) { 52383104Sphantom if (*flags & USE_INTL_CURRENCY) 52483104Sphantom right_prec = lc->int_frac_digits; 52583104Sphantom else 52683104Sphantom right_prec = lc->frac_digits; 52783104Sphantom 52883104Sphantom if (right_prec == CHAR_MAX) /* POSIX locale ? */ 52983104Sphantom right_prec = 2; 53083104Sphantom } 53183104Sphantom 53283104Sphantom if (*flags & NEED_GROUPING) 53383104Sphantom left_prec += get_groups(left_prec, grouping); 53483104Sphantom 53583104Sphantom /* convert to string */ 53683559Smike snprintf(fmt, sizeof(fmt), "%%%d.%df", left_prec + right_prec + 1, 53783559Smike right_prec); 53883104Sphantom avalue_size = asprintf(&avalue, fmt, value); 53983104Sphantom if (avalue_size < 0) 54083559Smike return (NULL); 54183104Sphantom 54283104Sphantom /* make sure that we've enough space for result string */ 54383104Sphantom bufsize = strlen(avalue)*2+1; 54483104Sphantom rslt = malloc(bufsize); 54583104Sphantom if (rslt == NULL) { 54683104Sphantom free(avalue); 54783559Smike return (NULL); 54883104Sphantom } 54983104Sphantom memset(rslt, 0, bufsize); 55083104Sphantom bufend = rslt + bufsize - 1; /* reserve space for trailing '\0' */ 55183104Sphantom 55283104Sphantom /* skip spaces at beggining */ 55383104Sphantom padded = 0; 55483104Sphantom while (avalue[padded] == ' ') { 55583104Sphantom padded++; 55683104Sphantom avalue_size--; 55783104Sphantom } 55883104Sphantom 55983104Sphantom if (right_prec > 0) { 56083104Sphantom bufend -= right_prec; 56183559Smike memcpy(bufend, avalue + avalue_size+padded-right_prec, 56283559Smike right_prec); 56383104Sphantom *--bufend = decimal_point; 56483104Sphantom avalue_size -= (right_prec + 1); 56583104Sphantom } 56683104Sphantom 56783104Sphantom if ((*flags & NEED_GROUPING) && 56883559Smike thousands_sep != '\0' && /* XXX: need investigation */ 56983559Smike *grouping != CHAR_MAX && 57083559Smike *grouping > 0) { 57183104Sphantom while (avalue_size > (int)*grouping) { 57283104Sphantom GRPCPY(*grouping); 57383104Sphantom GRPSEP; 57483104Sphantom grouping++; 57583104Sphantom 57683104Sphantom /* no more grouping ? */ 57783104Sphantom if (*grouping == CHAR_MAX) 57883104Sphantom break; 57983104Sphantom 58083104Sphantom /* rest grouping with same value ? */ 58183104Sphantom if (*grouping == 0) { 58283104Sphantom grouping--; 58383104Sphantom while (avalue_size > *grouping) { 58483104Sphantom GRPCPY(*grouping); 58583104Sphantom GRPSEP; 58683104Sphantom } 58783104Sphantom } 58883104Sphantom } 58983104Sphantom if (avalue_size != 0) 59083104Sphantom GRPCPY(avalue_size); 59183104Sphantom padded -= groups; 59283104Sphantom 59383104Sphantom } else { 59483104Sphantom bufend -= avalue_size; 59583104Sphantom memcpy(bufend, avalue+padded, avalue_size); 59683104Sphantom if (right_prec == 0) 59783104Sphantom padded--; /* decrease assumed $decimal_point */ 59883104Sphantom } 59983104Sphantom 60083104Sphantom /* do padding with pad_char */ 60183104Sphantom if (padded > 0) { 60283104Sphantom bufend -= padded; 60383104Sphantom memset(bufend, pad_char, padded); 60483104Sphantom } 60583104Sphantom 60683559Smike bufsize = bufsize - (rslt - bufend); 60783104Sphantom memmove(rslt, bufend, bufsize); 60883104Sphantom free(avalue); 60983559Smike return (rslt); 61083104Sphantom} 611