14297a3b0SGarrett D'Amore /* 2*2d08521bSGarrett D'Amore * Copyright 2013 Garrett D'Amore <garrett@damore.org> 36b5e5868SGarrett D'Amore * Copyright 2010 Nexenta Systems, Inc. All rights reserved. 44297a3b0SGarrett D'Amore * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org> 54297a3b0SGarrett D'Amore * All rights reserved. 64297a3b0SGarrett D'Amore * 74297a3b0SGarrett D'Amore * Redistribution and use in source and binary forms, with or without 84297a3b0SGarrett D'Amore * modification, are permitted provided that the following conditions 94297a3b0SGarrett D'Amore * are met: 104297a3b0SGarrett D'Amore * 1. Redistributions of source code must retain the above copyright 114297a3b0SGarrett D'Amore * notice, this list of conditions and the following disclaimer. 124297a3b0SGarrett D'Amore * 2. Redistributions in binary form must reproduce the above copyright 134297a3b0SGarrett D'Amore * notice, this list of conditions and the following disclaimer in the 144297a3b0SGarrett D'Amore * documentation and/or other materials provided with the distribution. 154297a3b0SGarrett D'Amore * 164297a3b0SGarrett D'Amore * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 174297a3b0SGarrett D'Amore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 184297a3b0SGarrett D'Amore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 194297a3b0SGarrett D'Amore * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 204297a3b0SGarrett D'Amore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 214297a3b0SGarrett D'Amore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 224297a3b0SGarrett D'Amore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 234297a3b0SGarrett D'Amore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 244297a3b0SGarrett D'Amore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 254297a3b0SGarrett D'Amore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 264297a3b0SGarrett D'Amore * SUCH DAMAGE. 274297a3b0SGarrett D'Amore * 284297a3b0SGarrett D'Amore */ 294297a3b0SGarrett D'Amore 304297a3b0SGarrett D'Amore #ifndef _LCONV_C99 314297a3b0SGarrett D'Amore #define _LCONV_C99 324297a3b0SGarrett D'Amore #endif 334297a3b0SGarrett D'Amore 344297a3b0SGarrett D'Amore #include "lint.h" 354297a3b0SGarrett D'Amore #include <sys/types.h> 364297a3b0SGarrett D'Amore #include <ctype.h> 374297a3b0SGarrett D'Amore #include <errno.h> 384297a3b0SGarrett D'Amore #include <limits.h> 394297a3b0SGarrett D'Amore #include <locale.h> 404297a3b0SGarrett D'Amore #include <monetary.h> 414297a3b0SGarrett D'Amore #include <stdarg.h> 424297a3b0SGarrett D'Amore #include <stdio.h> 434297a3b0SGarrett D'Amore #include <stdlib.h> 444297a3b0SGarrett D'Amore #include <string.h> 45*2d08521bSGarrett D'Amore #include "localeimpl.h" 46*2d08521bSGarrett D'Amore #include "lmonetary.h" 47*2d08521bSGarrett D'Amore #include "lnumeric.h" 484297a3b0SGarrett D'Amore 494297a3b0SGarrett D'Amore /* internal flags */ 504297a3b0SGarrett D'Amore #define NEED_GROUPING 0x01 /* print digits grouped (default) */ 514297a3b0SGarrett D'Amore #define SIGN_POSN_USED 0x02 /* '+' or '(' usage flag */ 524297a3b0SGarrett D'Amore #define LOCALE_POSN 0x04 /* use locale defined +/- (default) */ 534297a3b0SGarrett D'Amore #define PARENTH_POSN 0x08 /* enclose negative amount in () */ 544297a3b0SGarrett D'Amore #define SUPRESS_CURR_SYMBOL 0x10 /* supress the currency from output */ 554297a3b0SGarrett D'Amore #define LEFT_JUSTIFY 0x20 /* left justify */ 564297a3b0SGarrett D'Amore #define USE_INTL_CURRENCY 0x40 /* use international currency symbol */ 574297a3b0SGarrett D'Amore #define IS_NEGATIVE 0x80 /* is argument value negative ? */ 584297a3b0SGarrett D'Amore 594297a3b0SGarrett D'Amore /* internal macros */ 604297a3b0SGarrett D'Amore #define PRINT(CH) { \ 614297a3b0SGarrett D'Amore if (dst >= s + maxsize) \ 624297a3b0SGarrett D'Amore goto e2big_error; \ 634297a3b0SGarrett D'Amore *dst++ = CH; \ 644297a3b0SGarrett D'Amore } 654297a3b0SGarrett D'Amore 664297a3b0SGarrett D'Amore #define PRINTS(STR) { \ 67*2d08521bSGarrett D'Amore const char *tmps = STR; \ 684297a3b0SGarrett D'Amore while (*tmps != '\0') \ 694297a3b0SGarrett D'Amore PRINT(*tmps++); \ 704297a3b0SGarrett D'Amore } 714297a3b0SGarrett D'Amore 724297a3b0SGarrett D'Amore #define GET_NUMBER(VAR) { \ 734297a3b0SGarrett D'Amore VAR = 0; \ 744297a3b0SGarrett D'Amore while (isdigit((unsigned char)*fmt)) { \ 754297a3b0SGarrett D'Amore if (VAR > INT_MAX / 10) \ 764297a3b0SGarrett D'Amore goto e2big_error; \ 774297a3b0SGarrett D'Amore VAR *= 10; \ 784297a3b0SGarrett D'Amore VAR += *fmt - '0'; \ 794297a3b0SGarrett D'Amore if (VAR < 0) \ 804297a3b0SGarrett D'Amore goto e2big_error; \ 814297a3b0SGarrett D'Amore fmt++; \ 824297a3b0SGarrett D'Amore } \ 834297a3b0SGarrett D'Amore } 844297a3b0SGarrett D'Amore 854297a3b0SGarrett D'Amore #define GRPCPY(howmany) { \ 864297a3b0SGarrett D'Amore int i = howmany; \ 874297a3b0SGarrett D'Amore while (i-- > 0) { \ 884297a3b0SGarrett D'Amore avalue_size--; \ 894297a3b0SGarrett D'Amore *--bufend = *(avalue+avalue_size+padded); \ 904297a3b0SGarrett D'Amore } \ 914297a3b0SGarrett D'Amore } 924297a3b0SGarrett D'Amore 934297a3b0SGarrett D'Amore #define GRPSEP { \ 94*2d08521bSGarrett D'Amore bufend -= thousands_len; \ 95*2d08521bSGarrett D'Amore (void) memcpy(bufend, thousands_sep, thousands_len); \ 964297a3b0SGarrett D'Amore groups++; \ 974297a3b0SGarrett D'Amore } 984297a3b0SGarrett D'Amore 99*2d08521bSGarrett D'Amore static void setup_vars(const struct lc_monetary *, int, char *, char *, char *, 100*2d08521bSGarrett D'Amore const char **); 101*2d08521bSGarrett D'Amore static int calc_left_pad(const struct lc_monetary *, int, const char *); 102*2d08521bSGarrett D'Amore static char *format_grouped_double(const struct lc_monetary *, 103*2d08521bSGarrett D'Amore const struct lc_numeric *, double, int *, int, int, int); 1044297a3b0SGarrett D'Amore 1054297a3b0SGarrett D'Amore ssize_t 106*2d08521bSGarrett D'Amore strfmon_impl(char *_RESTRICT_KYWD s, size_t maxsize, locale_t loc, 107*2d08521bSGarrett D'Amore const char *_RESTRICT_KYWD format, va_list ap) 1084297a3b0SGarrett D'Amore { 1094297a3b0SGarrett D'Amore char *dst; /* output destination pointer */ 1104297a3b0SGarrett D'Amore const char *fmt; /* current format poistion pointer */ 1114297a3b0SGarrett D'Amore char *asciivalue; /* formatted double pointer */ 1124297a3b0SGarrett D'Amore 1134297a3b0SGarrett D'Amore int flags; /* formatting options */ 1144297a3b0SGarrett D'Amore int pad_char; /* padding character */ 1154297a3b0SGarrett D'Amore int pad_size; /* pad size */ 1164297a3b0SGarrett D'Amore int width; /* field width */ 1174297a3b0SGarrett D'Amore int left_prec; /* left precision */ 1184297a3b0SGarrett D'Amore int right_prec; /* right precision */ 1194297a3b0SGarrett D'Amore double value; /* just value */ 1204297a3b0SGarrett D'Amore char space_char = ' '; /* space after currency */ 1214297a3b0SGarrett D'Amore 122*2d08521bSGarrett D'Amore char cs_precedes; /* values from struct lc_monetary */ 1234297a3b0SGarrett D'Amore char sep_by_space; 1244297a3b0SGarrett D'Amore char sign_posn; 125*2d08521bSGarrett D'Amore const char *signstr; 126*2d08521bSGarrett D'Amore const char *currency_symbol; 1274297a3b0SGarrett D'Amore 1284297a3b0SGarrett D'Amore char *tmpptr; /* temporary vars */ 1294297a3b0SGarrett D'Amore int sverrno; 130*2d08521bSGarrett D'Amore const struct lc_monetary *lmon; /* monetary structure */ 131*2d08521bSGarrett D'Amore const struct lc_numeric *lnum; /* numeric structure */ 1324297a3b0SGarrett D'Amore 133*2d08521bSGarrett D'Amore lmon = loc->monetary; 134*2d08521bSGarrett D'Amore lnum = loc->numeric; 1354297a3b0SGarrett D'Amore 1364297a3b0SGarrett D'Amore dst = s; 1374297a3b0SGarrett D'Amore fmt = format; 1384297a3b0SGarrett D'Amore asciivalue = NULL; 1394297a3b0SGarrett D'Amore currency_symbol = NULL; 1404297a3b0SGarrett D'Amore pad_size = 0; 1414297a3b0SGarrett D'Amore 1424297a3b0SGarrett D'Amore while (*fmt) { 1434297a3b0SGarrett D'Amore /* pass nonformating characters AS IS */ 1444297a3b0SGarrett D'Amore if (*fmt != '%') 1454297a3b0SGarrett D'Amore goto literal; 1464297a3b0SGarrett D'Amore 1474297a3b0SGarrett D'Amore /* '%' found ! */ 1484297a3b0SGarrett D'Amore 1494297a3b0SGarrett D'Amore /* "%%" mean just '%' */ 1504297a3b0SGarrett D'Amore if (*(fmt+1) == '%') { 1514297a3b0SGarrett D'Amore fmt++; 1524297a3b0SGarrett D'Amore literal: 1534297a3b0SGarrett D'Amore PRINT(*fmt++); 1544297a3b0SGarrett D'Amore continue; 1554297a3b0SGarrett D'Amore } 1564297a3b0SGarrett D'Amore 1574297a3b0SGarrett D'Amore /* set up initial values */ 1584297a3b0SGarrett D'Amore flags = (NEED_GROUPING|LOCALE_POSN); 1594297a3b0SGarrett D'Amore pad_char = ' '; /* padding character is "space" */ 1604297a3b0SGarrett D'Amore left_prec = -1; /* no left precision specified */ 1614297a3b0SGarrett D'Amore right_prec = -1; /* no right precision specified */ 1624297a3b0SGarrett D'Amore width = -1; /* no width specified */ 1634297a3b0SGarrett D'Amore value = 0; /* we have no value to print now */ 1644297a3b0SGarrett D'Amore 1654297a3b0SGarrett D'Amore /* Flags */ 1664297a3b0SGarrett D'Amore for (;;) { 1674297a3b0SGarrett D'Amore switch (*++fmt) { 1684297a3b0SGarrett D'Amore case '=': /* fill character */ 1694297a3b0SGarrett D'Amore pad_char = *++fmt; 1704297a3b0SGarrett D'Amore if (pad_char == '\0') 1714297a3b0SGarrett D'Amore goto format_error; 1724297a3b0SGarrett D'Amore continue; 1734297a3b0SGarrett D'Amore case '^': /* not group currency */ 1744297a3b0SGarrett D'Amore flags &= ~(NEED_GROUPING); 1754297a3b0SGarrett D'Amore continue; 1764297a3b0SGarrett D'Amore case '+': /* use locale defined signs */ 1774297a3b0SGarrett D'Amore if (flags & SIGN_POSN_USED) 1784297a3b0SGarrett D'Amore goto format_error; 1794297a3b0SGarrett D'Amore flags |= (SIGN_POSN_USED|LOCALE_POSN); 1804297a3b0SGarrett D'Amore continue; 1814297a3b0SGarrett D'Amore case '(': /* enclose negatives with () */ 1824297a3b0SGarrett D'Amore if (flags & SIGN_POSN_USED) 1834297a3b0SGarrett D'Amore goto format_error; 1844297a3b0SGarrett D'Amore flags |= (SIGN_POSN_USED|PARENTH_POSN); 1854297a3b0SGarrett D'Amore continue; 1864297a3b0SGarrett D'Amore case '!': /* suppress currency symbol */ 1874297a3b0SGarrett D'Amore flags |= SUPRESS_CURR_SYMBOL; 1884297a3b0SGarrett D'Amore continue; 1894297a3b0SGarrett D'Amore case '-': /* alignment (left) */ 1904297a3b0SGarrett D'Amore flags |= LEFT_JUSTIFY; 1914297a3b0SGarrett D'Amore continue; 1924297a3b0SGarrett D'Amore default: 1934297a3b0SGarrett D'Amore break; 1944297a3b0SGarrett D'Amore } 1954297a3b0SGarrett D'Amore break; 1964297a3b0SGarrett D'Amore } 1974297a3b0SGarrett D'Amore 1984297a3b0SGarrett D'Amore /* field Width */ 1994297a3b0SGarrett D'Amore if (isdigit((unsigned char)*fmt)) { 2004297a3b0SGarrett D'Amore GET_NUMBER(width); 2014297a3b0SGarrett D'Amore /* 2024297a3b0SGarrett D'Amore * Do we have enough space to put number with 2034297a3b0SGarrett D'Amore * required width ? 2044297a3b0SGarrett D'Amore */ 2054297a3b0SGarrett D'Amore if ((unsigned int)width >= maxsize - (dst - s)) 2064297a3b0SGarrett D'Amore goto e2big_error; 2074297a3b0SGarrett D'Amore } 2084297a3b0SGarrett D'Amore 2094297a3b0SGarrett D'Amore /* Left precision */ 2104297a3b0SGarrett D'Amore if (*fmt == '#') { 2114297a3b0SGarrett D'Amore if (!isdigit((unsigned char)*++fmt)) 2124297a3b0SGarrett D'Amore goto format_error; 2134297a3b0SGarrett D'Amore GET_NUMBER(left_prec); 2144297a3b0SGarrett D'Amore if ((unsigned int)left_prec >= maxsize - (dst - s)) 2154297a3b0SGarrett D'Amore goto e2big_error; 2164297a3b0SGarrett D'Amore } 2174297a3b0SGarrett D'Amore 2184297a3b0SGarrett D'Amore /* Right precision */ 2194297a3b0SGarrett D'Amore if (*fmt == '.') { 2204297a3b0SGarrett D'Amore if (!isdigit((unsigned char)*++fmt)) 2214297a3b0SGarrett D'Amore goto format_error; 2224297a3b0SGarrett D'Amore GET_NUMBER(right_prec); 2234297a3b0SGarrett D'Amore if ((unsigned int)right_prec >= maxsize - (dst - s) - 2244297a3b0SGarrett D'Amore left_prec) 2254297a3b0SGarrett D'Amore goto e2big_error; 2264297a3b0SGarrett D'Amore } 2274297a3b0SGarrett D'Amore 2284297a3b0SGarrett D'Amore /* Conversion Characters */ 2294297a3b0SGarrett D'Amore switch (*fmt++) { 2304297a3b0SGarrett D'Amore case 'i': /* use internaltion currency format */ 2314297a3b0SGarrett D'Amore flags |= USE_INTL_CURRENCY; 2324297a3b0SGarrett D'Amore break; 2334297a3b0SGarrett D'Amore case 'n': /* use national currency format */ 2344297a3b0SGarrett D'Amore flags &= ~(USE_INTL_CURRENCY); 2354297a3b0SGarrett D'Amore break; 2364297a3b0SGarrett D'Amore default: 2374297a3b0SGarrett D'Amore /* required char missing or premature EOS */ 2384297a3b0SGarrett D'Amore goto format_error; 2394297a3b0SGarrett D'Amore } 2404297a3b0SGarrett D'Amore 2414297a3b0SGarrett D'Amore if (flags & USE_INTL_CURRENCY) { 242*2d08521bSGarrett D'Amore currency_symbol = lmon->int_curr_symbol; 243*2d08521bSGarrett D'Amore /* by definition three letters followed by a space */ 2444297a3b0SGarrett D'Amore if (currency_symbol != NULL) 245*2d08521bSGarrett D'Amore space_char = currency_symbol[3]; 2464297a3b0SGarrett D'Amore } else 247*2d08521bSGarrett D'Amore currency_symbol = lmon->currency_symbol; 2484297a3b0SGarrett D'Amore 2494297a3b0SGarrett D'Amore /* value itself */ 2504297a3b0SGarrett D'Amore value = va_arg(ap, double); 2514297a3b0SGarrett D'Amore 2524297a3b0SGarrett D'Amore /* detect sign */ 2534297a3b0SGarrett D'Amore if (value < 0) { 2544297a3b0SGarrett D'Amore flags |= IS_NEGATIVE; 2554297a3b0SGarrett D'Amore value = -value; 2564297a3b0SGarrett D'Amore } 2574297a3b0SGarrett D'Amore 2584297a3b0SGarrett D'Amore /* fill left_prec with amount of padding chars */ 2594297a3b0SGarrett D'Amore if (left_prec >= 0) { 260*2d08521bSGarrett D'Amore pad_size = calc_left_pad(lmon, (flags ^ IS_NEGATIVE), 2614297a3b0SGarrett D'Amore currency_symbol) - 262*2d08521bSGarrett D'Amore calc_left_pad(lmon, flags, currency_symbol); 2634297a3b0SGarrett D'Amore if (pad_size < 0) 2644297a3b0SGarrett D'Amore pad_size = 0; 2654297a3b0SGarrett D'Amore } 2664297a3b0SGarrett D'Amore 2674297a3b0SGarrett D'Amore if (asciivalue != NULL) 2684297a3b0SGarrett D'Amore free(asciivalue); 269*2d08521bSGarrett D'Amore asciivalue = format_grouped_double(lmon, lnum, value, &flags, 2704297a3b0SGarrett D'Amore left_prec, right_prec, pad_char); 2714297a3b0SGarrett D'Amore if (asciivalue == NULL) 2724297a3b0SGarrett D'Amore goto end_error; /* errno already set */ 2734297a3b0SGarrett D'Amore /* to ENOMEM by malloc() */ 2744297a3b0SGarrett D'Amore 2754297a3b0SGarrett D'Amore /* set some variables for later use */ 276*2d08521bSGarrett D'Amore setup_vars(lmon, flags, &cs_precedes, &sep_by_space, 277*2d08521bSGarrett D'Amore &sign_posn, &signstr); 2784297a3b0SGarrett D'Amore 2794297a3b0SGarrett D'Amore /* 2804297a3b0SGarrett D'Amore * Description of some LC_MONETARY's values: 2814297a3b0SGarrett D'Amore * 2824297a3b0SGarrett D'Amore * p_cs_precedes & n_cs_precedes 2834297a3b0SGarrett D'Amore * 2844297a3b0SGarrett D'Amore * = 1 - $currency_symbol precedes the value 2854297a3b0SGarrett D'Amore * for a monetary quantity with a non-negative value 2864297a3b0SGarrett D'Amore * = 0 - symbol succeeds the value 2874297a3b0SGarrett D'Amore * 2884297a3b0SGarrett D'Amore * p_sep_by_space & n_sep_by_space 2894297a3b0SGarrett D'Amore * 2904297a3b0SGarrett D'Amore * = 0 - no space separates $currency_symbol 2914297a3b0SGarrett D'Amore * from the value for a monetary quantity with a 2924297a3b0SGarrett D'Amore * non-negative value 2934297a3b0SGarrett D'Amore * = 1 - space separates the symbol from the value 2944297a3b0SGarrett D'Amore * = 2 - space separates the symbol and the sign string, 2954297a3b0SGarrett D'Amore * if adjacent. 2964297a3b0SGarrett D'Amore * 2974297a3b0SGarrett D'Amore * p_sign_posn & n_sign_posn 2984297a3b0SGarrett D'Amore * 2994297a3b0SGarrett D'Amore * = 0 - parentheses enclose the quantity and the 3004297a3b0SGarrett D'Amore * $currency_symbol 3014297a3b0SGarrett D'Amore * = 1 - the sign string precedes the quantity and the 3024297a3b0SGarrett D'Amore * $currency_symbol 3034297a3b0SGarrett D'Amore * = 2 - the sign string succeeds the quantity and the 3044297a3b0SGarrett D'Amore * $currency_symbol 3054297a3b0SGarrett D'Amore * = 3 - the sign string precedes the $currency_symbol 3064297a3b0SGarrett D'Amore * = 4 - the sign string succeeds the $currency_symbol 3074297a3b0SGarrett D'Amore * 3084297a3b0SGarrett D'Amore */ 3094297a3b0SGarrett D'Amore 3104297a3b0SGarrett D'Amore tmpptr = dst; 3114297a3b0SGarrett D'Amore 3124297a3b0SGarrett D'Amore while (pad_size-- > 0) 3134297a3b0SGarrett D'Amore PRINT(' '); 3144297a3b0SGarrett D'Amore 3154297a3b0SGarrett D'Amore if (sign_posn == 0 && (flags & IS_NEGATIVE)) 3164297a3b0SGarrett D'Amore PRINT('('); 3174297a3b0SGarrett D'Amore 3184297a3b0SGarrett D'Amore if (cs_precedes == 1) { 3194297a3b0SGarrett D'Amore if (sign_posn == 1 || sign_posn == 3) { 3204297a3b0SGarrett D'Amore PRINTS(signstr); 321*2d08521bSGarrett D'Amore if (sep_by_space == 2) 3224297a3b0SGarrett D'Amore PRINT(' '); 3234297a3b0SGarrett D'Amore } 3244297a3b0SGarrett D'Amore 3254297a3b0SGarrett D'Amore if (!(flags & SUPRESS_CURR_SYMBOL)) { 3264297a3b0SGarrett D'Amore PRINTS(currency_symbol); 3274297a3b0SGarrett D'Amore 3284297a3b0SGarrett D'Amore if (sign_posn == 4) { 3294297a3b0SGarrett D'Amore if (sep_by_space == 2) 3304297a3b0SGarrett D'Amore PRINT(space_char); 3314297a3b0SGarrett D'Amore PRINTS(signstr); 3324297a3b0SGarrett D'Amore if (sep_by_space == 1) 3334297a3b0SGarrett D'Amore PRINT(' '); 3344297a3b0SGarrett D'Amore } else if (sep_by_space == 1) 3354297a3b0SGarrett D'Amore PRINT(space_char); 3364297a3b0SGarrett D'Amore } 3374297a3b0SGarrett D'Amore } else if (sign_posn == 1) 3384297a3b0SGarrett D'Amore PRINTS(signstr); 3394297a3b0SGarrett D'Amore 3404297a3b0SGarrett D'Amore PRINTS(asciivalue); 3414297a3b0SGarrett D'Amore 3424297a3b0SGarrett D'Amore if (cs_precedes == 0) { 3434297a3b0SGarrett D'Amore if (sign_posn == 3) { 3444297a3b0SGarrett D'Amore if (sep_by_space == 1) 3454297a3b0SGarrett D'Amore PRINT(' '); 3464297a3b0SGarrett D'Amore PRINTS(signstr); 3474297a3b0SGarrett D'Amore } 3484297a3b0SGarrett D'Amore 3494297a3b0SGarrett D'Amore if (!(flags & SUPRESS_CURR_SYMBOL)) { 3504297a3b0SGarrett D'Amore if ((sign_posn == 3 && sep_by_space == 2) || 3514297a3b0SGarrett D'Amore (sep_by_space == 1 && (sign_posn == 0 || 3524297a3b0SGarrett D'Amore sign_posn == 1 || sign_posn == 2 || 3534297a3b0SGarrett D'Amore sign_posn == 4))) 3544297a3b0SGarrett D'Amore PRINT(space_char); 3554297a3b0SGarrett D'Amore PRINTS(currency_symbol); /* XXX: len */ 3564297a3b0SGarrett D'Amore if (sign_posn == 4) { 3574297a3b0SGarrett D'Amore if (sep_by_space == 2) 3584297a3b0SGarrett D'Amore PRINT(' '); 3594297a3b0SGarrett D'Amore PRINTS(signstr); 3604297a3b0SGarrett D'Amore } 3614297a3b0SGarrett D'Amore } 3624297a3b0SGarrett D'Amore } 3634297a3b0SGarrett D'Amore 3644297a3b0SGarrett D'Amore if (sign_posn == 2) { 3654297a3b0SGarrett D'Amore if (sep_by_space == 2) 3664297a3b0SGarrett D'Amore PRINT(' '); 3674297a3b0SGarrett D'Amore PRINTS(signstr); 3684297a3b0SGarrett D'Amore } 3694297a3b0SGarrett D'Amore 3704297a3b0SGarrett D'Amore if (sign_posn == 0 && (flags & IS_NEGATIVE)) 3714297a3b0SGarrett D'Amore PRINT(')'); 3724297a3b0SGarrett D'Amore 3734297a3b0SGarrett D'Amore if (dst - tmpptr < width) { 3744297a3b0SGarrett D'Amore if (flags & LEFT_JUSTIFY) { 3754297a3b0SGarrett D'Amore while (dst - tmpptr < width) 3764297a3b0SGarrett D'Amore PRINT(' '); 3774297a3b0SGarrett D'Amore } else { 3784297a3b0SGarrett D'Amore pad_size = dst-tmpptr; 379eda71b4aSGarrett D'Amore (void) memmove(tmpptr + width-pad_size, tmpptr, 3804297a3b0SGarrett D'Amore pad_size); 3814297a3b0SGarrett D'Amore (void) memset(tmpptr, ' ', width-pad_size); 3824297a3b0SGarrett D'Amore dst += width-pad_size; 3834297a3b0SGarrett D'Amore } 3844297a3b0SGarrett D'Amore } 3854297a3b0SGarrett D'Amore } 3864297a3b0SGarrett D'Amore 3874297a3b0SGarrett D'Amore PRINT('\0'); 3884297a3b0SGarrett D'Amore free(asciivalue); 3894297a3b0SGarrett D'Amore return (dst - s - 1); /* size of put data except trailing '\0' */ 3904297a3b0SGarrett D'Amore 3914297a3b0SGarrett D'Amore e2big_error: 3924297a3b0SGarrett D'Amore errno = E2BIG; 3934297a3b0SGarrett D'Amore goto end_error; 3944297a3b0SGarrett D'Amore 3954297a3b0SGarrett D'Amore format_error: 3964297a3b0SGarrett D'Amore errno = EINVAL; 3974297a3b0SGarrett D'Amore 3984297a3b0SGarrett D'Amore end_error: 3994297a3b0SGarrett D'Amore sverrno = errno; 4004297a3b0SGarrett D'Amore if (asciivalue != NULL) 4014297a3b0SGarrett D'Amore free(asciivalue); 4024297a3b0SGarrett D'Amore errno = sverrno; 4034297a3b0SGarrett D'Amore return (-1); 4044297a3b0SGarrett D'Amore } 4054297a3b0SGarrett D'Amore 406*2d08521bSGarrett D'Amore ssize_t 407*2d08521bSGarrett D'Amore strfmon(char *_RESTRICT_KYWD s, size_t maxsize, 408*2d08521bSGarrett D'Amore const char *_RESTRICT_KYWD format, ...) 4094297a3b0SGarrett D'Amore { 410*2d08521bSGarrett D'Amore va_list ap; 411*2d08521bSGarrett D'Amore ssize_t ret; 4124297a3b0SGarrett D'Amore 413*2d08521bSGarrett D'Amore va_start(ap, format); 414*2d08521bSGarrett D'Amore ret = strfmon_impl(s, maxsize, uselocale(NULL), format, ap); 415*2d08521bSGarrett D'Amore va_end(ap); 416*2d08521bSGarrett D'Amore return (ret); 4174297a3b0SGarrett D'Amore } 4184297a3b0SGarrett D'Amore 419*2d08521bSGarrett D'Amore ssize_t 420*2d08521bSGarrett D'Amore strfmon_l(char *_RESTRICT_KYWD s, size_t maxsize, locale_t loc, 421*2d08521bSGarrett D'Amore const char *_RESTRICT_KYWD format, ...) 422*2d08521bSGarrett D'Amore { 423*2d08521bSGarrett D'Amore ssize_t ret; 424*2d08521bSGarrett D'Amore va_list ap; 425*2d08521bSGarrett D'Amore va_start(ap, format); 426*2d08521bSGarrett D'Amore ret = strfmon_impl(s, maxsize, loc, format, ap); 427*2d08521bSGarrett D'Amore va_end(ap); 428*2d08521bSGarrett D'Amore return (ret); 429*2d08521bSGarrett D'Amore } 430*2d08521bSGarrett D'Amore 431*2d08521bSGarrett D'Amore static void 432*2d08521bSGarrett D'Amore setup_vars(const struct lc_monetary *lmon, int flags, char *cs_precedes, 433*2d08521bSGarrett D'Amore char *sep_by_space, char *sign_posn, const char **signstr) 434*2d08521bSGarrett D'Amore { 435*2d08521bSGarrett D'Amore if ((flags & IS_NEGATIVE) && (flags & USE_INTL_CURRENCY)) { 436*2d08521bSGarrett D'Amore *cs_precedes = lmon->int_n_cs_precedes[0]; 437*2d08521bSGarrett D'Amore *sep_by_space = lmon->int_n_sep_by_space[0]; 438*2d08521bSGarrett D'Amore *sign_posn = (flags & PARENTH_POSN) ? 0 : 439*2d08521bSGarrett D'Amore lmon->int_n_sign_posn[0]; 440*2d08521bSGarrett D'Amore *signstr = (lmon->negative_sign[0] == '\0') ? "-" : 441*2d08521bSGarrett D'Amore lmon->negative_sign; 442*2d08521bSGarrett D'Amore } else if (flags & USE_INTL_CURRENCY) { 443*2d08521bSGarrett D'Amore *cs_precedes = lmon->int_p_cs_precedes[0]; 444*2d08521bSGarrett D'Amore *sep_by_space = lmon->int_p_sep_by_space[0]; 445*2d08521bSGarrett D'Amore *sign_posn = (flags & PARENTH_POSN) ? 0 : 446*2d08521bSGarrett D'Amore lmon->int_p_sign_posn[0]; 447*2d08521bSGarrett D'Amore *signstr = lmon->positive_sign; 448*2d08521bSGarrett D'Amore } else if (flags & IS_NEGATIVE) { 449*2d08521bSGarrett D'Amore *cs_precedes = lmon->n_cs_precedes[0]; 450*2d08521bSGarrett D'Amore *sep_by_space = lmon->n_sep_by_space[0]; 451*2d08521bSGarrett D'Amore *sign_posn = (flags & PARENTH_POSN) ? 0 : lmon->n_sign_posn[0]; 452*2d08521bSGarrett D'Amore *signstr = (lmon->negative_sign[0] == '\0') ? "-" : 453*2d08521bSGarrett D'Amore lmon->negative_sign; 454*2d08521bSGarrett D'Amore } else { 455*2d08521bSGarrett D'Amore *cs_precedes = lmon->p_cs_precedes[0]; 456*2d08521bSGarrett D'Amore *sep_by_space = lmon->p_sep_by_space[0]; 457*2d08521bSGarrett D'Amore *sign_posn = (flags & PARENTH_POSN) ? 0 : lmon->p_sign_posn[0]; 458*2d08521bSGarrett D'Amore *signstr = lmon->positive_sign; 459*2d08521bSGarrett D'Amore } 460*2d08521bSGarrett D'Amore 461*2d08521bSGarrett D'Amore /* Set default values for unspecified information. */ 4624297a3b0SGarrett D'Amore if (*cs_precedes != 0) 4634297a3b0SGarrett D'Amore *cs_precedes = 1; 4644297a3b0SGarrett D'Amore if (*sep_by_space == CHAR_MAX) 4654297a3b0SGarrett D'Amore *sep_by_space = 0; 4664297a3b0SGarrett D'Amore if (*sign_posn == CHAR_MAX) 4674297a3b0SGarrett D'Amore *sign_posn = 0; 4684297a3b0SGarrett D'Amore } 4694297a3b0SGarrett D'Amore 4704297a3b0SGarrett D'Amore static int 471*2d08521bSGarrett D'Amore calc_left_pad(const struct lc_monetary *lmon, int flags, const char *cur_symb) 4724297a3b0SGarrett D'Amore { 473*2d08521bSGarrett D'Amore char cs_precedes, sep_by_space, sign_posn; 474*2d08521bSGarrett D'Amore const char *signstr; 4754297a3b0SGarrett D'Amore int left_chars = 0; 4764297a3b0SGarrett D'Amore 477*2d08521bSGarrett D'Amore setup_vars(lmon, flags, &cs_precedes, &sep_by_space, &sign_posn, 478*2d08521bSGarrett D'Amore &signstr); 4794297a3b0SGarrett D'Amore 4804297a3b0SGarrett D'Amore if (cs_precedes != 0) { 4814297a3b0SGarrett D'Amore left_chars += strlen(cur_symb); 4824297a3b0SGarrett D'Amore if (sep_by_space != 0) 4834297a3b0SGarrett D'Amore left_chars++; 4844297a3b0SGarrett D'Amore } 4854297a3b0SGarrett D'Amore 4864297a3b0SGarrett D'Amore switch (sign_posn) { 4874297a3b0SGarrett D'Amore case 1: 4884297a3b0SGarrett D'Amore left_chars += strlen(signstr); 4894297a3b0SGarrett D'Amore break; 4904297a3b0SGarrett D'Amore case 3: 4914297a3b0SGarrett D'Amore case 4: 4924297a3b0SGarrett D'Amore if (cs_precedes != 0) 4934297a3b0SGarrett D'Amore left_chars += strlen(signstr); 4944297a3b0SGarrett D'Amore } 4954297a3b0SGarrett D'Amore return (left_chars); 4964297a3b0SGarrett D'Amore } 4974297a3b0SGarrett D'Amore 4984297a3b0SGarrett D'Amore static int 499*2d08521bSGarrett D'Amore get_groups(int size, const char *grouping) 5004297a3b0SGarrett D'Amore { 5014297a3b0SGarrett D'Amore 5024297a3b0SGarrett D'Amore int chars = 0; 5034297a3b0SGarrett D'Amore 5044297a3b0SGarrett D'Amore if (*grouping == CHAR_MAX || *grouping <= 0) /* no grouping ? */ 5054297a3b0SGarrett D'Amore return (0); 5064297a3b0SGarrett D'Amore 5074297a3b0SGarrett D'Amore while (size > (int)*grouping) { 5084297a3b0SGarrett D'Amore chars++; 5094297a3b0SGarrett D'Amore size -= (int)*grouping++; 5104297a3b0SGarrett D'Amore /* no more grouping ? */ 5114297a3b0SGarrett D'Amore if (*grouping == CHAR_MAX) 5124297a3b0SGarrett D'Amore break; 5134297a3b0SGarrett D'Amore /* rest grouping with same value ? */ 5144297a3b0SGarrett D'Amore if (*grouping == 0) { 5154297a3b0SGarrett D'Amore chars += (size - 1) / *(grouping - 1); 5164297a3b0SGarrett D'Amore break; 5174297a3b0SGarrett D'Amore } 5184297a3b0SGarrett D'Amore } 5194297a3b0SGarrett D'Amore return (chars); 5204297a3b0SGarrett D'Amore } 5214297a3b0SGarrett D'Amore 5224297a3b0SGarrett D'Amore /* convert double to ASCII */ 5234297a3b0SGarrett D'Amore static char * 524*2d08521bSGarrett D'Amore format_grouped_double(const struct lc_monetary *lmon, 525*2d08521bSGarrett D'Amore const struct lc_numeric *lnum, 526*2d08521bSGarrett D'Amore double value, int *flags, int left_prec, int right_prec, int pad_char) 5274297a3b0SGarrett D'Amore { 5284297a3b0SGarrett D'Amore 5294297a3b0SGarrett D'Amore char *rslt; 5304297a3b0SGarrett D'Amore char *avalue; 5314297a3b0SGarrett D'Amore int avalue_size; 5324297a3b0SGarrett D'Amore char fmt[32]; 5334297a3b0SGarrett D'Amore 5344297a3b0SGarrett D'Amore size_t bufsize; 5354297a3b0SGarrett D'Amore char *bufend; 5364297a3b0SGarrett D'Amore 5374297a3b0SGarrett D'Amore int padded; 5384297a3b0SGarrett D'Amore 539*2d08521bSGarrett D'Amore const char *grouping; 540*2d08521bSGarrett D'Amore const char *decimal_point; 541*2d08521bSGarrett D'Amore const char *thousands_sep; 542*2d08521bSGarrett D'Amore int decimal_len; 543*2d08521bSGarrett D'Amore int thousands_len; 5444297a3b0SGarrett D'Amore 5454297a3b0SGarrett D'Amore int groups = 0; 5464297a3b0SGarrett D'Amore 547*2d08521bSGarrett D'Amore grouping = lmon->mon_grouping; 548*2d08521bSGarrett D'Amore decimal_point = lmon->mon_decimal_point; 549*2d08521bSGarrett D'Amore if (*decimal_point == '\0') 550*2d08521bSGarrett D'Amore decimal_point = lnum->decimal_point; 551*2d08521bSGarrett D'Amore thousands_sep = lmon->mon_thousands_sep; 552*2d08521bSGarrett D'Amore if (*thousands_sep == '\0') 553*2d08521bSGarrett D'Amore thousands_sep = lnum->thousands_sep; 554*2d08521bSGarrett D'Amore 555*2d08521bSGarrett D'Amore decimal_len = strlen(decimal_point); /* usually 1 */ 556*2d08521bSGarrett D'Amore thousands_len = strlen(thousands_sep); /* 0 or 1 usually */ 5574297a3b0SGarrett D'Amore 5584297a3b0SGarrett D'Amore /* fill left_prec with default value */ 5594297a3b0SGarrett D'Amore if (left_prec == -1) 5604297a3b0SGarrett D'Amore left_prec = 0; 5614297a3b0SGarrett D'Amore 5624297a3b0SGarrett D'Amore /* fill right_prec with default value */ 5634297a3b0SGarrett D'Amore if (right_prec == -1) { 5644297a3b0SGarrett D'Amore if (*flags & USE_INTL_CURRENCY) 565*2d08521bSGarrett D'Amore right_prec = lmon->int_frac_digits[0]; 5664297a3b0SGarrett D'Amore else 567*2d08521bSGarrett D'Amore right_prec = lmon->frac_digits[0]; 5684297a3b0SGarrett D'Amore 5694297a3b0SGarrett D'Amore if (right_prec == CHAR_MAX) /* POSIX locale ? */ 5704297a3b0SGarrett D'Amore right_prec = 2; 5714297a3b0SGarrett D'Amore } 5724297a3b0SGarrett D'Amore 5734297a3b0SGarrett D'Amore if (*flags & NEED_GROUPING) 5744297a3b0SGarrett D'Amore left_prec += get_groups(left_prec, grouping); 5754297a3b0SGarrett D'Amore 5764297a3b0SGarrett D'Amore /* convert to string */ 5774297a3b0SGarrett D'Amore (void) snprintf(fmt, sizeof (fmt), "%%%d.%df", 5784297a3b0SGarrett D'Amore left_prec + right_prec + 1, right_prec); 5794297a3b0SGarrett D'Amore avalue_size = asprintf(&avalue, fmt, value); 5804297a3b0SGarrett D'Amore if (avalue_size < 0) 5814297a3b0SGarrett D'Amore return (NULL); 5824297a3b0SGarrett D'Amore 583*2d08521bSGarrett D'Amore /* 584*2d08521bSGarrett D'Amore * Make sure that we've enough space for result string. 585*2d08521bSGarrett D'Amore * This assumes that digits take up at least much space as 586*2d08521bSGarrett D'Amore * grouping and radix characters. The worst case currently known 587*2d08521bSGarrett D'Amore * is for Arabic, where two-byte UTF-8 sequences are used for both 588*2d08521bSGarrett D'Amore * decimal and thousands seperators, and groups can be a small as two 589*2d08521bSGarrett D'Amore * decimal digits. This will do no worse than doubling the storage 590*2d08521bSGarrett D'Amore * requirement. 591*2d08521bSGarrett D'Amore */ 5924297a3b0SGarrett D'Amore bufsize = strlen(avalue)*2+1; 5934297a3b0SGarrett D'Amore rslt = calloc(1, bufsize); 5944297a3b0SGarrett D'Amore if (rslt == NULL) { 5954297a3b0SGarrett D'Amore free(avalue); 5964297a3b0SGarrett D'Amore return (NULL); 5974297a3b0SGarrett D'Amore } 5984297a3b0SGarrett D'Amore bufend = rslt + bufsize - 1; /* reserve space for trailing '\0' */ 5994297a3b0SGarrett D'Amore 600*2d08521bSGarrett D'Amore /* skip spaces at beginning */ 6014297a3b0SGarrett D'Amore padded = 0; 6024297a3b0SGarrett D'Amore while (avalue[padded] == ' ') { 6034297a3b0SGarrett D'Amore padded++; 6044297a3b0SGarrett D'Amore avalue_size--; 6054297a3b0SGarrett D'Amore } 6064297a3b0SGarrett D'Amore 6074297a3b0SGarrett D'Amore if (right_prec > 0) { 6084297a3b0SGarrett D'Amore bufend -= right_prec; 6094297a3b0SGarrett D'Amore (void) memcpy(bufend, avalue + avalue_size+padded-right_prec, 6104297a3b0SGarrett D'Amore right_prec); 611*2d08521bSGarrett D'Amore bufend -= decimal_len; 612*2d08521bSGarrett D'Amore (void) memcpy(bufend, decimal_point, decimal_len); 613*2d08521bSGarrett D'Amore avalue_size -= (right_prec + decimal_len); 6144297a3b0SGarrett D'Amore } 6154297a3b0SGarrett D'Amore 6164297a3b0SGarrett D'Amore if ((*flags & NEED_GROUPING) && 617*2d08521bSGarrett D'Amore thousands_len != 0 && 6184297a3b0SGarrett D'Amore *grouping != CHAR_MAX && 6194297a3b0SGarrett D'Amore *grouping > 0) { 6204297a3b0SGarrett D'Amore while (avalue_size > (int)*grouping) { 6214297a3b0SGarrett D'Amore GRPCPY(*grouping); 6224297a3b0SGarrett D'Amore GRPSEP; 6234297a3b0SGarrett D'Amore grouping++; 6244297a3b0SGarrett D'Amore 6254297a3b0SGarrett D'Amore /* no more grouping ? */ 6264297a3b0SGarrett D'Amore if (*grouping == CHAR_MAX) 6274297a3b0SGarrett D'Amore break; 6284297a3b0SGarrett D'Amore 6294297a3b0SGarrett D'Amore /* rest grouping with same value ? */ 6304297a3b0SGarrett D'Amore if (*grouping == 0) { 6314297a3b0SGarrett D'Amore grouping--; 6324297a3b0SGarrett D'Amore while (avalue_size > *grouping) { 6334297a3b0SGarrett D'Amore GRPCPY(*grouping); 6344297a3b0SGarrett D'Amore GRPSEP; 6354297a3b0SGarrett D'Amore } 6364297a3b0SGarrett D'Amore } 6374297a3b0SGarrett D'Amore } 6384297a3b0SGarrett D'Amore if (avalue_size != 0) 6394297a3b0SGarrett D'Amore GRPCPY(avalue_size); 6404297a3b0SGarrett D'Amore padded -= groups; 6414297a3b0SGarrett D'Amore 6424297a3b0SGarrett D'Amore } else { 6434297a3b0SGarrett D'Amore bufend -= avalue_size; 6444297a3b0SGarrett D'Amore (void) memcpy(bufend, avalue+padded, avalue_size); 6454297a3b0SGarrett D'Amore if (right_prec == 0) 6464297a3b0SGarrett D'Amore padded--; /* decrease assumed $decimal_point */ 6474297a3b0SGarrett D'Amore } 6484297a3b0SGarrett D'Amore 6494297a3b0SGarrett D'Amore /* do padding with pad_char */ 6504297a3b0SGarrett D'Amore if (padded > 0) { 6514297a3b0SGarrett D'Amore bufend -= padded; 6524297a3b0SGarrett D'Amore (void) memset(bufend, pad_char, padded); 6534297a3b0SGarrett D'Amore } 6544297a3b0SGarrett D'Amore 6554297a3b0SGarrett D'Amore bufsize = bufsize - (bufend - rslt) + 1; 656eda71b4aSGarrett D'Amore (void) memmove(rslt, bufend, bufsize); 6574297a3b0SGarrett D'Amore free(avalue); 6584297a3b0SGarrett D'Amore return (rslt); 6594297a3b0SGarrett D'Amore } 660