>From 83f293f48d20cc8468ab6dd1969b5aa6f5aa7cdb Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Sat, 3 Oct 2026 22:35:13 -0400 Subject: [PATCH 08/10] printf: add numeric grouping for wide character variants this is implemented by having the base (byte-oriented) printf core count characters rather than bytes when the FILE is wide-oriented, which will be the case when it is called from vfwprintf. if such a call is made by the application, the behavior is undefined. this approach is something of a hack, but there do not seem to be good alternatives. one approach I tried first was a sort of black box one, having vfwprintf call snprintf twice with and without the grouping modifier and take the difference of return values to get the size of the grouping characters, then munge the requested width and final size accordingly. however, this produces spurious EOVERFLOW on widths close to INT_MAX, so it is not viable. another approach would be having vfwprintf always setup a fake locale with single-byte grouping separator to call fprintf, running it through a wrapper FILE that converts to the actual character provided by the locale. this seems viable, but it's a lot of machinery. --- src/stdio/vfprintf.c | 19 ++++++++++++------- src/stdio/vfwprintf.c | 3 ++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/stdio/vfprintf.c b/src/stdio/vfprintf.c index c2c0d942..097b7619 100644 --- a/src/stdio/vfprintf.c +++ b/src/stdio/vfprintf.c @@ -447,7 +447,7 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t, int ps, c int ng = __count_groupings(&dg, e+1, gr); if (ng * gsl > INT_MAX-l) return -1; - l += ng * gsl; + l += f->mode>0 ? ng : ng * gsl; } } else { estr=fmt_u(e<0 ? -e : e, ebuf); @@ -534,7 +534,8 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, wchar_t wc[2], *ws; char mb[4]; const char *gr=0, *gs=0; - size_t gsl=0; + size_t gsl=0, gcl; + int l_adj; for (;;) { /* This error is only specified for snprintf, but since it's @@ -638,9 +639,12 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, /* - and 0 flags are mutually exclusive */ if (fl & LEFT_ADJ) fl &= ~ZERO_PAD; - if ((fl & GROUPED) && !gr) + if ((fl & GROUPED) && !gr) { gr = __get_grouping(&gs, &gsl); + gcl = f->mode>0 ? 1 : gsl; + } + l_adj = 0; switch(t) { case 'n': switch(ps) { @@ -680,11 +684,12 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, struct digit_grouper dg; int ng = __count_groupings(&dg, z-a, gr); if (p > 0) { - if (ng * gsl > INT_MAX-p) + if (ng * gcl > INT_MAX-p) goto overflow; - p += ng * gsl; + p += ng * gcl; } a = __expand_grouping(a, z, &dg, gs, gsl); + l_adj = ng * (gsl - gcl); } ifmt_tail: if (xp && p<0) goto overflow; @@ -736,7 +741,7 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, continue; } - if (p < z-a) p = z-a; + if (p < z-a-l_adj) p = z-a-l_adj; if (p > INT_MAX-pl) goto overflow; if (w < pl+p) w = pl+p; if (w > INT_MAX-cnt) goto overflow; @@ -744,7 +749,7 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, pad(f, ' ', w, pl+p, fl); out(f, prefix, pl); pad(f, '0', w, pl+p, fl^ZERO_PAD); - pad(f, '0', p, z-a, 0); + pad(f, '0', p, z-a-l_adj, 0); out(f, a, z-a); pad(f, ' ', w, pl+p, fl^LEFT_ADJ); diff --git a/src/stdio/vfwprintf.c b/src/stdio/vfwprintf.c index d47e045d..c5031ba3 100644 --- a/src/stdio/vfwprintf.c +++ b/src/stdio/vfwprintf.c @@ -310,7 +310,8 @@ static int wprintf_core(FILE *f, const wchar_t *fmt, va_list *ap, union arg *nl_ } if (xp && p<0) goto overflow; - snprintf(charfmt, sizeof charfmt, "%%%s%s%s%s%s*.*%c%c", + snprintf(charfmt, sizeof charfmt, "%%%s%s%s%s%s%s*.*%c%c", + "'"+!(fl & GROUPED), "#"+!(fl & ALT_FORM), "+"+!(fl & MARK_POS), "-"+!(fl & LEFT_ADJ), -- 2.21.0