Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 10 Feb 2002 20:09:13 +0200
From:      Alexey Zelkin <phantom@freebsd.org>
To:        standards@freebsd.org
Subject:   generalized "\'" flag support for vfprintf() (for both decimal and float numbers)
Message-ID:  <20020210200913.A96833@gate.sim.ionidea.com>

next in thread | raw e-mail | index | archive | help
hi,

Heh, just finished it and it passed my basic tests.
Tommorow I'll go with its cleanup, but anyway
it would be interesting to listen opinions on the
way how it's done.

Index: vfprintf.c
===================================================================
RCS file: /home/cvs/freebsd/src/lib/libc/stdio/vfprintf.c,v
retrieving revision 1.36
diff -u -r1.36 vfprintf.c
--- vfprintf.c	17 Dec 2001 15:11:29 -0000	1.36
+++ vfprintf.c	10 Feb 2002 18:04:30 -0000
@@ -114,12 +114,11 @@
 
 static int	__sprint __P((FILE *, struct __suio *));
 static int	__sbprintf __P((FILE *, const char *, va_list)) __printflike(2, 0);
-static char	*__ujtoa __P((uintmax_t, char *, int, int, char *, int,
-		     char, const char *));
-static char	*__ultoa __P((u_long, char *, int, int, char *, int,
-		     char, const char *));
+static char	*__ujtoa __P((uintmax_t, char *, int, int, char *));
+static char	*__ultoa __P((u_long, char *, int, int, char *));
 static void	__find_arguments __P((const char *, va_list, union arg **));
 static void	__grow_type_table __P((int, enum typeid **, int *));
+static int	__do_grouping __P((char *buf, char **cp, int size, int safety));
 
 /*
  * Flush out all the vectors defined by the given uio,
@@ -186,12 +185,10 @@
  * use the given digits.
  */
 static char *
-__ultoa(u_long val, char *endp, int base, int octzero, char *xdigs,
-	int needgrp, char thousep, const char *grp)
+__ultoa(u_long val, char *endp, int base, int octzero, char *xdigs)
 {
 	register char *cp = endp;
 	register long sval;
-	int ndig;
 
 	/*
 	 * Handle the three cases separately, in the hope of getting
@@ -203,7 +200,6 @@
 			*--cp = to_char(val);
 			return (cp);
 		}
-		ndig = 0;
 		/*
 		 * On many machines, unsigned arithmetic is harder than
 		 * signed arithmetic, so we do at most one unsigned mod and
@@ -212,29 +208,11 @@
 		 */
 		if (val > LONG_MAX) {
 			*--cp = to_char(val % 10);
-			ndig++;
 			sval = val / 10;
 		} else
 			sval = val;
 		do {
 			*--cp = to_char(sval % 10);
-			ndig++;
-			/*
-			 * If (*grp == CHAR_MAX) then no more grouping
-			 * should be performed.
-			 */
-			if (needgrp && ndig == *grp && *grp != CHAR_MAX
-					&& sval > 9) {
-				*--cp = thousep;
-				ndig = 0;
-				/*
-				 * If (*(grp+1) == '\0') then we have to
-				 * use *grp character (last grouping rule)
-				 * for all next cases
-				 */
-				if (*(grp+1) != '\0')
-					grp++;
-			}
 			sval /= 10;
 		} while (sval != 0);
 		break;
@@ -263,50 +241,28 @@
 
 /* Identical to __ultoa, but for intmax_t. */
 static char *
-__ujtoa(uintmax_t val, char *endp, int base, int octzero, char *xdigs, 
-	int needgrp, char thousep, const char *grp)
+__ujtoa(uintmax_t val, char *endp, int base, int octzero, char *xdigs)
 {
 	char *cp = endp;
 	intmax_t sval;
-	int ndig;
 
 	/* quick test for small values; __ultoa is typically much faster */
 	/* (perhaps instead we should run until small, then call __ultoa?) */
 	if (val <= ULONG_MAX)
-		return (__ultoa((u_long)val, endp, base, octzero, xdigs,
-		    needgrp, thousep, grp));
+		return (__ultoa((u_long)val, endp, base, octzero, xdigs));
 	switch (base) {
 	case 10:
 		if (val < 10) {
 			*--cp = to_char(val % 10);
 			return (cp);
 		}
-		ndig = 0;
 		if (val > INTMAX_MAX) {
 			*--cp = to_char(val % 10);
-			ndig++;
 			sval = val / 10;
 		} else
 			sval = val;
 		do {
 			*--cp = to_char(sval % 10);
-			ndig++;
-			/*
-			 * If (*grp == CHAR_MAX) then no more grouping
-			 * should be performed.
-			 */
-			if (needgrp && *grp != CHAR_MAX && ndig == *grp
-					&& sval > 9) {
-				*--cp = thousep;
-				ndig = 0;
-				/*
-				 * If (*(grp+1) == '\0') then we have to
-				 * use *grp character (last grouping rule)
-				 * for all next cases
-				 */
-				if (*(grp+1) != '\0')
-					grp++;
-			}
 			sval /= 10;
 		} while (sval != 0);
 		break;
@@ -400,8 +356,6 @@
 	int width;		/* width from format (%8d), or 0 */
 	int prec;		/* precision from format (%.3d), or -1 */
 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
-	char thousands_sep;	/* locale specific thousands separator */
-	const char *grouping;	/* locale specific numeric grouping rules */
 #ifdef FLOATING_POINT
 	char *decimal_point;	/* locale specific decimal point */
 	char softsign;		/* temporary negative sign for floats */
@@ -532,8 +486,6 @@
         }
 
 
-	thousands_sep = '\0';
-	grouping = NULL;
 #ifdef FLOATING_POINT
 	dtoaresult = NULL;
 	decimal_point = localeconv()->decimal_point;
@@ -614,8 +566,6 @@
 			goto rflag;
 		case '\'':
 			flags |= GROUPING;
-			thousands_sep = *(localeconv()->thousands_sep);
-			grouping = localeconv()->grouping;
 			goto rflag;
 		case '.':
 			if ((ch = *fmt++) == '*') {
@@ -750,6 +700,7 @@
 				else
 					cp = "inf";
 				size = 3;
+				flags &= ~GROUPING;
 				break;
 			}
 			if (isnan(_double)) {
@@ -758,6 +709,7 @@
 				else
 					cp = "nan";
 				size = 3;
+				flags &= ~GROUPING;
 				break;
 			}
 			flags |= FPT;
@@ -912,15 +864,11 @@
 			if (flags & INTMAX_SIZE) {
 				if (ujval != 0 || prec != 0)
 					cp = __ujtoa(ujval, cp, base,
-					    flags & ALT, xdigs,
-					    flags & GROUPING, thousands_sep,
-					    grouping);
+					    flags & ALT, xdigs);
 			} else {
 				if (ulval != 0 || prec != 0)
 					cp = __ultoa(ulval, cp, base,
-					    flags & ALT, xdigs,
-					    flags & GROUPING, thousands_sep,
-					    grouping);
+					    flags & ALT, xdigs);
 			}
 			size = buf + BUF - cp;
 			break;
@@ -981,9 +929,18 @@
 		/* leading zeroes from decimal precision */
 		PAD(dprec - size, zeroes);
 
+#define DOGRP(PTR, SIZE, SAFETY) {				\
+	if (flags & GROUPING) {					\
+		n2 = __do_grouping(buf, &PTR, SIZE, SAFETY);	\
+		SIZE += n2;					\
+		realsz += n2;					\
+	}							\
+}
+
 		/* the string or number proper */
 #ifdef FLOATING_POINT
 		if ((flags & FPT) == 0) {
+			DOGRP(cp, size, 0);
 			PRINT(cp, size);
 		} else {	/* glue together f_p fragments */
 			if (ch >= 'f') {	/* 'f' or 'g' */
@@ -1000,11 +957,13 @@
 					PAD(-expt, zeroes);
 					PRINT(cp, ndig);
 				} else if (expt >= ndig) {
+					DOGRP(cp, ndig, 0);
 					PRINT(cp, ndig);
 					PAD(expt - ndig, zeroes);
 					if (flags & ALT)
 						PRINT(decimal_point, 1);
 				} else {
+					DOGRP(cp, expt, ndig-expt);
 					PRINT(cp, expt);
 					cp += expt;
 					PRINT(decimal_point, 1);
@@ -1026,6 +985,7 @@
 			}
 		}
 #else
+		DOGRP(cp, size, 0);
 		PRINT(cp, size);
 #endif
 		/* left-adjusting padding (always blank) */
@@ -1387,6 +1347,64 @@
 
 	*typetable = newtable;
 	*tablesize = newsize;
+}
+
+/*-
+ * Reformat buffer to include grouping characters
+ *
+ * params:
+ *   buf -- pointer to begining of the buffer
+ *   cp -- address of pointer to actual adta 
+ *   size -- amount of characters to group
+ *   safety -- amount of characters after (*cp+size) to preserve
+ *             (post-decimal part of FPT number)
+ */
+static int
+__do_grouping(char *buf, char **cp, int size, int safety) {
+
+	char thousands_sep;
+	char *grouping;
+	char *ptr, *tp;
+	int ndig, proceed, result, n;
+
+	grouping = localeconv()->grouping;
+	if (*grouping == CHAR_MAX)
+		return (0);
+
+	thousands_sep = *(localeconv()->thousands_sep);
+	if (thousands_sep == '\0')
+		return (0);
+
+	result = 0;
+
+	/* assume that data is at the begin of buffer */
+	/* NOTE: strings can't overlap since actual value can fill only 1/3 part of buffer */
+	if (buf != *cp) {
+		memmove(buf, *cp, size+safety);
+		*cp = buf;
+	}
+
+	ndig = 0;
+	proceed = 0;
+	ptr = (*cp + size);
+	while (proceed < size) {
+		if (*grouping <= ndig) {
+			memmove(ptr+1, ptr, proceed+result+safety);
+			*ptr = thousands_sep;
+			result++;
+			if (*(grouping+1) != '\0')
+				grouping++;
+			if (*grouping == CHAR_MAX)	/* no more grouping */
+				break;
+			ndig = 0;
+		} else {
+			ndig++;
+			proceed++;
+			ptr--;
+		}
+	}
+
+	return (result);
 }
 
 

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-standards" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020210200913.A96833>