>From 8f1e8a426582f83e56bfbab9ac3e8c4fe20bc318 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Sun, 20 Sep 2026 21:16:22 -0400 Subject: [PATCH 18/18] implement a working, non-stub strfmon --- src/locale/strfmon.c | 207 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 198 insertions(+), 9 deletions(-) diff --git a/src/locale/strfmon.c b/src/locale/strfmon.c index 7cf2136a..9faa38a3 100644 --- a/src/locale/strfmon.c +++ b/src/locale/strfmon.c @@ -3,14 +3,46 @@ #include #include #include +#include #include "locale_impl.h" +locale_t __uselocale(locale_t); + +static int count_groupings(int l, const struct lconv *lc) +{ + int i, cnt = 0; + const char *g = lc->mon_grouping; + for (i=0; g[i]; i++) { + if (l <= g[i] || g[i] == CHAR_MAX || g[i] < 0) + return cnt; + cnt++; + l -= g[i]; + } + if (i > 0) { + cnt += l/g[i-1]; + l %= g[i-1]; + if (!l) cnt--; + } + return cnt; +} + static ssize_t vstrfmon_l(char *s, size_t n, locale_t loc, const char *fmt, va_list ap) { + locale_t old = __uselocale(loc); + const struct lconv *lc = localeconv(); + __uselocale(old); + + const char *radix = lc->mon_decimal_point; + const char *sep = lc->mon_thousands_sep; + if (!*radix) radix = "."; + if (!*sep) sep = ""; + size_t seplen = strlen(sep); + size_t radixlen = strlen(radix); + size_t l; double x; - int fill, nogrp, negpar, nosym, left, intl; - int lp, rp, w, fw; + int fill, nogrp, negpar, showsign, nosym, left, intl; + int lp, rp, fw; char *s0=s; for (; n && *fmt; ) { if (*fmt != '%') { @@ -25,6 +57,7 @@ static ssize_t vstrfmon_l(char *s, size_t n, locale_t loc, const char *fmt, va_l fill = ' '; nogrp = 0; negpar = 0; + showsign = 0; nosym = 0; left = 0; for (; ; fmt++) { @@ -37,7 +70,9 @@ static ssize_t vstrfmon_l(char *s, size_t n, locale_t loc, const char *fmt, va_l continue; case '(': negpar = 1; + continue; case '+': + showsign = 1; continue; case '!': nosym = 1; @@ -52,25 +87,179 @@ static ssize_t vstrfmon_l(char *s, size_t n, locale_t loc, const char *fmt, va_l for (fw=0; isdigit(*fmt); fmt++) fw = 10*fw + (*fmt-'0'); lp = 0; - rp = 2; + rp = -1; if (*fmt=='#') for (lp=0, fmt++; isdigit(*fmt); fmt++) lp = 10*lp + (*fmt-'0'); if (*fmt=='.') for (rp=0, fmt++; isdigit(*fmt); fmt++) rp = 10*rp + (*fmt-'0'); intl = *fmt++ == 'i'; - - w = lp + 1 + rp; - if (!left && fw>w) w = fw; + if (rp < 0) rp = intl ? lc->int_frac_digits : lc->frac_digits; + if (rp < 0 || rp >= CHAR_MAX) rp = 2; x = va_arg(ap, double); - l = snprintf(s, n, "%*.*f", w, rp, x); + l = snprintf(s, n, "%.*f", rp, x); if (l >= n) { errno = E2BIG; return -1; } - s += l; - n -= l; + l -= rp+(rp>0)+(x<0); + int nseps = nogrp ? 0 : count_groupings(l, lc); + if (lp < l) lp = l; + int maxseps = nogrp ? 0 : count_groupings(lp, lc); + + const char *sign = x>=0 ? lc->positive_sign : lc->negative_sign; + int signlen = strlen(sign); + const char *cs = intl ? lc->int_curr_symbol : lc->currency_symbol; + int cslen = strlen(cs); + if (intl && cslen) cslen--; + int cs_precedes = intl + ? x>=0 ? lc->int_p_cs_precedes : lc->int_n_cs_precedes + : x>=0 ? lc->p_cs_precedes : lc->n_cs_precedes; + int sep_by_space = intl + ? x>=0 ? lc->int_p_sep_by_space : lc->int_n_sep_by_space + : x>=0 ? lc->p_sep_by_space : lc->n_sep_by_space; + int sign_posn = intl + ? x>=0 ? lc->int_p_sign_posn : lc->int_n_sign_posn + : x>=0 ? lc->p_sign_posn : lc->n_sign_posn; + + if (sep_by_space < 0 || sep_by_space > 2) + sep_by_space = 0; + + if (nosym) { + cs = ""; + cslen = 0; + } + if (negpar) { + /* explicit ( flag */ + if (x<0) sign_posn = 0; + else if (!sign_posn) sign_posn = 1; + sign = ""; + signlen = 0; + if (sep_by_space == 2) sep_by_space = 0; // is this right? + } else if (showsign || sign_posn < 0 || sign_posn >= CHAR_MAX) { + /* explicit + flag or style cannot be determined */ + if (!lc->positive_sign[0] && !lc->negative_sign[0]) { + if (showsign) { + errno = EINVAL; + return -1; + } + if (x<0) { + sign = "-"; + signlen = 1; + } + } + if (sign_posn <= 0 || sign_posn >= CHAR_MAX) sign_posn = 1; + } + + int lx = 0; + int rx = 0; + switch (sign_posn) { + case 0: + lx++; + rx++; + if (sep_by_space==1 && cs_precedes) lx++; + if (sep_by_space==1 && !cs_precedes) rx++; + break; + case 1: + lx += signlen; + if (sep_by_space==2) lx++; + else if (sep_by_space==1 && cs_precedes) lx++; + break; + case 2: + rx += signlen; + if (sep_by_space==2) rx++; + else if (sep_by_space==1 && !cs_precedes) rx++; + break; + case 3: + case 4: + if (cs_precedes) { + lx += signlen; + if (sep_by_space) lx++; + } else { + rx += signlen; + if (sep_by_space) rx++; + } + break; + } + if (cs_precedes) lx += cslen; + else rx += cslen; + + int total = lx+lp+radixlen*(rp>0)+rp+maxseps*seplen+rx; + int shift = total - (l+rp+(rp>0)); + int pad = 0; + if (total < fw) { + pad = fw - total; + total = fw; + if (!left) shift += pad; + } + if (total >= n) { + errno = E2BIG; + return -1; + } + n -= total; + + memmove(s+shift, s+(x<0), l+rp+(rp>0)); + const char *p = s+shift; + + if (!left) for (; pad; pad--) *s++ = ' '; + + if (!sign_posn) *s++ = '('; + if (sign_posn == 1 || (sign_posn == 3 && cs_precedes)) { + memcpy(s, sign, signlen); + s += signlen; + if (sep_by_space == 2) *s++ = ' '; + } + if (cs_precedes) { + memcpy(s, cs, cslen); + s += cslen; + if (sign_posn == 4) { + if (sep_by_space == 2) *s++ = ' '; + memcpy(s, sign, signlen); + s += signlen; + } + if (sep_by_space == 1) *s++ = ' '; + } + + for (int i=(lp-l)+(maxseps-nseps)*seplen; i; i--) + *s++ = fill; + + for (int i=l; i; i--) { + *s++ = *p++; + int n = count_groupings(i-1, lc); + if (n < nseps) { + memcpy(s, sep, seplen); + s += seplen; + nseps = n; + } + } + if (rp>0) { + memcpy(s, radix, radixlen); + s += radixlen; + p++; + } + for (int i=rp; i; i--) + *s++ = *p++; + + if (!cs_precedes) { + if (sep_by_space == 1) *s++ = ' '; + if (sign_posn == 3) { + memcpy(s, sign, signlen); + s += signlen; + if (sep_by_space == 2) *s++ = ' '; + } + memcpy(s, cs, cslen); + s += cslen; + } + if (sign_posn == 2 || (sign_posn == 4 && !cs_precedes)) { + if (sep_by_space == 2) *s++ = ' '; + memcpy(s, sign, signlen); + s += signlen; + } + if (!sign_posn) *s++ = ')'; + + if (left) for (; pad; pad--) *s++ = ' '; + *s = 0; } return s-s0; } -- 2.21.0