Browse Source

Implement locale-specific grouping in printf for base 10 integer conversions
when the grouping flag "'" is specified. Grouping for floating point values
may wait until I do a rewrite of the floating pt to string code...

Manuel Novoa III 22 years ago
parent
commit
868df46adc
3 changed files with 95 additions and 5 deletions
  1. 19 5
      libc/stdio/printf.c
  2. 75 0
      libc/stdio/stdio.c
  3. 1 0
      libc/sysdeps/linux/common/bits/uClibc_stdio.h

+ 19 - 5
libc/stdio/printf.c

@@ -1103,10 +1103,18 @@ int _do_one_spec(FILE * __restrict stream, register ppfs_t *ppfs, int *count)
 	size_t slen;
 	int base;
 	int numpad;
+	int alphacase;
 	int numfill = 0;			/* TODO: fix */
 	int prefix_num = PREFIX_NONE;
 	char padchar = ' ';
-	char buf[64];
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning REMINDER: buf size
+#endif
+	/* TODO: buf needs to be big enough for any possible error return strings
+	 * and also for any locale-grouped long long integer strings generated.
+	 * This should be large enough for any of the current archs/locales, but
+	 * eventually this should be handled robustly. */
+	char buf[128];
 
 #ifdef NDEBUG
 	_ppfs_parsespec(ppfs);
@@ -1147,8 +1155,16 @@ int _do_one_spec(FILE * __restrict stream, register ppfs_t *ppfs, int *count)
 			return 0;
 		}
 		if (ppfs->conv_num <= CONV_i) {	/* pointer or (un)signed int */
-			base = spec_base[(int)(ppfs->conv_num - CONV_p)];
+			alphacase = __UIM_LOWER;
+			if (((base = spec_base[(int)(ppfs->conv_num - CONV_p)]) == 10)
+				&& (PRINT_INFO_FLAG_VAL(&(ppfs->info),group))
+				) {
+				alphacase = __UIM_GROUP;
+			}
 			if (ppfs->conv_num <= CONV_u) { /* pointer or unsigned int */
+				if (ppfs->conv_num == CONV_X) {
+					alphacase = __UIM_UPPER;
+				}
 				if (ppfs->conv_num == CONV_p) { /* pointer */
 					prefix_num = PREFIX_LWR_X;
 				} else {		/* unsigned int */
@@ -1162,9 +1178,7 @@ int _do_one_spec(FILE * __restrict stream, register ppfs_t *ppfs, int *count)
 			s = _uintmaxtostr(buf + sizeof(buf) - 1,
 							  (uintmax_t)
 							  _load_inttype(*argtype & __PA_INTMASK,
-											*argptr, base), base,
-							  ((ppfs->conv_num == CONV_X)
-							   ? __UIM_UPPER : __UIM_LOWER));
+											*argptr, base), base, alphacase);
 			if (ppfs->conv_num > CONV_u) { /* signed int */
 				if (*s == '-') {
 					PRINT_INFO_SET_FLAG(&(ppfs->info),showsign);

+ 75 - 0
libc/stdio/stdio.c

@@ -3308,6 +3308,15 @@ void _stdio_fdout(int fd, ...)
 #define INTERNAL_DIV_MOD
 #endif
 
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning REMINDER: move _uintmaxtostr to locale.c???
+#endif
+#include <locale.h>
+
+#ifndef __LOCALE_C_ONLY
+#define CUR_LOCALE			(__global_locale)
+#endif /* __LOCALE_C_ONLY */
+
 char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval,
 					int base, __UIM_CASE alphacase)
 {
@@ -3316,6 +3325,11 @@ char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval,
 #ifdef INTERNAL_DIV_MOD
 	unsigned int H, L, high, low, rh;
 #endif
+#ifndef __LOCALE_C_ONLY
+	int grouping;
+	size_t gslen;		   /* This does not need to be initialized. */
+	const char *g;		   /* This does not need to be initialized. */
+#endif /* __LOCALE_C_ONLY */
 
 	negative = 0;
 	if (base < 0) {				/* signed value */
@@ -3329,10 +3343,46 @@ char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval,
 	/* this is an internal routine -- we shouldn't need to check this */
 	assert(!((base < 2) || (base > 36)));
 
+#ifndef __LOCALE_C_ONLY
+	grouping = -1;
+	if (alphacase == __UIM_GROUP) {
+		assert(base == 10);
+		if (*(g = CUR_LOCALE.grouping)
+			&& ((gslen = strlen(CUR_LOCALE.thousands_sep)) > 0)
+			) {
+			grouping = *g;
+		}
+	}
+#endif /* __LOCALE_C_ONLY */
+
     *bufend = '\0';
 
 #ifndef INTERNAL_DIV_MOD
     do {
+#ifndef __LOCALE_C_ONLY
+		if (!grouping) {		/* Finished a group. */
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning REMINDER: decide about memcpy in _uintmaxtostr
+#endif
+#if 0
+			bufend -= gslen;
+			memcpy(bufend, CUR_LOCALE.thousands_sep, gslen);
+#else
+			grouping = gslen;
+			do {
+				*--bufend = CUR_LOCALE.thousands_sep[--grouping];
+			} while (grouping);
+#endif
+			if (g[1] != 0) { 	/* g[1] == 0 means repeat last grouping. */
+				/* Note: g[1] == -1 means no further grouping.  But since
+				 * we'll never wrap around, we can set grouping to -1 without
+				 * fear of */
+				++g;
+			}
+			grouping = *g;
+		}
+		--grouping;
+#endif /* __LOCALE_C_ONLY */
 		digit = uval % base;
 		uval /= base;
 
@@ -3351,6 +3401,31 @@ char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval,
 	high = (unsigned int) (uval >> (sizeof(unsigned int) * CHAR_BIT));
 
     do {
+#ifndef __LOCALE_C_ONLY
+		if (!grouping) {		/* Finished a group. */
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning REMINDER: decide about memcpy in _uintmaxtostr
+#endif
+#if 0
+			bufend -= gslen;
+			memcpy(bufend, CUR_LOCALE.thousands_sep, gslen);
+#else
+			grouping = gslen;
+			do {
+				*--bufend = CUR_LOCALE.thousands_sep[--grouping];
+			} while (grouping);
+#endif
+			if (g[1] != 0) { 	/* g[1] == 0 means repeat last grouping. */
+				/* Note: g[1] == -1 means no further grouping.  But since
+				 * we'll never wrap around, we can set grouping to -1 without
+				 * fear of */
+				++g;
+			}
+			grouping = *g;
+		}
+		--grouping;
+#endif /* __LOCALE_C_ONLY */
+
 		rh = high % base;
 		high /= base;
 		digit = (low % base) + (L * rh);

+ 1 - 0
libc/sysdeps/linux/common/bits/uClibc_stdio.h

@@ -463,6 +463,7 @@ extern void __stdio_validate_FILE(FILE *stream);
 
 typedef enum {
 	__UIM_DECIMAL = 0,
+	__UIM_GROUP = ',',			/* Base 10 locale-dependent grouping. */
 	__UIM_LOWER = 'a' - 10,
 	__UIM_UPPER = 'A' - 10,
 } __UIM_CASE;