X-Authentication-Warning: delorie.com: mail set sender to djgpp-workers-bounces using -f X-Recipient: djgpp-workers AT delorie DOT com X-Authenticated: #27081556 X-Provags-ID: V01U2FsdGVkX1+Xk2No8BKX18Ti2Qi0qe60DtgXKnNDmKqJ8K1baZ zM3mXx/rMhu3/L From: Juan Manuel Guerrero To: djgpp-workers AT delorie DOT com Subject: Re: Implementation of certain conversion specifiers in _doprnt (patch #1) Date: Thu, 24 Apr 2008 16:27:25 +0200 User-Agent: KMail/1.9.5 MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline Message-Id: <200804241627.27055.juan.guerrero@gmx.de> X-Y-GMX-Trusted: 0 Reply-To: djgpp-workers AT delorie DOT com The goal of this patch is to implement the grouping functionality in _doprnt(). Because there is only C locale it does nothing. Neitherles the function has been checked with modified and hardcoded values in the locale structure. Because certain configure scripts test for this flag it must be implemented. Regards, Juan M. Guerrero 2008-04-23 Juan Manuel Guerrero Diffs against djgpp CVS head of 2008-04-23. * src/libc/ansi/stdio/doprnt.c: New function __grouping_format. Formats the integer portion of the result of a decimal conversion (%i, %d, %u, %f, %F, %g and %G) using the non-monetary thousends' grouping character. * src/libc/ansi/stdio/printf.c: Info about grouping flag added. * src/docs/kb/wc204.txi: Info about implemetation of grouping flag for _doprnt added. diff -aprNU3 djgpp.orig/src/docs/kb/wc204.txi djgpp/src/docs/kb/wc204.txi --- djgpp.orig/src/docs/kb/wc204.txi 2008-04-11 18:50:30 +0000 +++ djgpp/src/docs/kb/wc204.txi 2008-04-23 20:09:36 +0000 @@ -1118,3 +1118,8 @@ The functions @code{strtod}, @code{strto @code{_strtold} now understand hex floating point strings like @code{[+|-]0xH.HHHp[+|-]DDD} and any variations of case in the input string. + +@findex _doprnt AT r{, and C99 conversion flags} +@findex printf AT r{, and C99 conversion flags} +The @code{'} conversion flag is now supported by @code{_doprnt} +and the @code{printf} family of functions. diff -aprNU3 djgpp.orig/src/libc/ansi/stdio/doprnt.c djgpp/src/libc/ansi/stdio/doprnt.c --- djgpp.orig/src/libc/ansi/stdio/doprnt.c 2008-04-23 16:37:36 +0000 +++ djgpp/src/libc/ansi/stdio/doprnt.c 2008-04-23 20:10:16 +0000 @@ -12,14 +12,17 @@ #include #include #include -#include +#include #include #include +#include #include #include #include -static char decimal = '.'; +static char decimal_point; +static char thousands_sep; +static char *grouping; /* 11-bit exponent (VAX G floating point) is 308 decimal digits */ #define MAXEXP 308 @@ -41,8 +44,11 @@ static char decimal = '.'; flags&CHARINT ? (char basetype)va_arg(argp, int) : \ (basetype)va_arg(argp, int) -#define IS_ZERO(x) ((x).ldt.exponent == 0x0U && (x).ldt.mantissah == 0x0UL && (x).ldt.mantissal == 0x0UL) -#define IS_NAN(x) ((x).ldt.exponent == 0x7FFFU && ((x).ldt.mantissah & 0x7FFFFFFFUL || (x).ldt.mantissal)) +#define IS_FINITE(x) (((x).ldt.exponent < 0x7FFFU && (x).ldt.exponent > 0x0000U && (x).ldt.mantissah & 0x80000000UL) \ + || ((x).ldt.exponent == 0x0000U && !((x).ldt.mantissah & 0x80000000UL))) + +#define IS_ZERO(x) ((x).ldt.exponent == 0x0U && (x).ldt.mantissah == 0x0UL && (x).ldt.mantissal == 0x0UL) +#define IS_NAN(x) ((x).ldt.exponent == 0x7FFFU && ((x).ldt.mantissah & 0x7FFFFFFFUL || (x).ldt.mantissal)) static __inline__ int todigit(char c) @@ -60,14 +66,17 @@ static __inline__ char tochar(int n) /* have to deal with the negative buffer count kludge */ -#define LONGINT 0x01 /* long integer */ -#define LONGDBL 0x02 /* long double */ -#define SHORTINT 0x04 /* short integer */ -#define CHARINT 0x08 /* char */ -#define ALT 0x10 /* alternate form */ -#define LADJUST 0x20 /* left adjustment */ -#define ZEROPAD 0x40 /* zero (as opposed to blank) pad */ -#define HEXPREFIX 0x80 /* add 0x or 0X prefix */ +#define LONGINT 0x0001 /* long integer */ +#define LONGDBL 0x0002 /* long double */ +#define SHORTINT 0x0004 /* short integer */ +#define CHARINT 0x0008 /* char */ +#define FLOAT 0x0010 /* %f, %F, %g or %G decimal conversions */ +#define FINITENUMBER 0x0020 /* not set if NAN, INF or Unnormal */ +#define ALT 0x0040 /* alternate form */ +#define LADJUST 0x0080 /* left adjustment */ +#define ZEROPAD 0x0100 /* zero (as opposed to blank) pad */ +#define HEXPREFIX 0x0200 /* add 0x or 0X prefix */ +#define GROUPING 0x0400 /* non monetary thousands grouping */ static int cvtl(long double number, int prec, int flags, char *signp, unsigned char fmtch, char *startp, char *endp); @@ -77,10 +86,12 @@ static char *exponentl(char *p, int expv #ifdef __GO32__ static int isspeciall(long double d, char *bufp); #endif +static __inline__ char * __grouping_format(char *string_start, char *string_end, char *buffer_end, int flags); static char NULL_REP[] = "(null)"; static char UNNORMAL_REP[] = "Unnormal"; + int _doprnt(const char *fmt0, va_list argp, FILE *fp) { @@ -106,13 +117,15 @@ _doprnt(const char *fmt0, va_list argp, const char *digs; /* digits for [diouxX] conversion */ char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ int neg_ldouble = 0; /* non-zero if _ldouble is negative */ + struct lconv *locale_info; /* current locale information */ - decimal = localeconv()->decimal_point[0]; + locale_info = localeconv(); + decimal_point = locale_info->decimal_point[0]; if (fp->_flag & _IORW) { fp->_flag |= _IOWRT; - fp->_flag &= ~(_IOEOF|_IOREAD); + fp->_flag &= ~(_IOEOF | _IOREAD); } if ((fp->_flag & _IOWRT) == 0) return (EOF); @@ -121,6 +134,8 @@ _doprnt(const char *fmt0, va_list argp, digs = "0123456789abcdef"; for (cnt = 0;; ++fmt) { + _longdouble_union_t ieee_value; + while ((ch = *fmt) && ch != '%') { PUTC (ch); @@ -129,12 +144,22 @@ _doprnt(const char *fmt0, va_list argp, } if (!ch) return cnt; + base = 0; flags = 0; dprec = 0; fpprec = 0; width = 0; prec = -1; sign = '\0'; rflag: switch (*++fmt) { + case '\'': + /* + * If the locale is C or POSIX + * this is a NO OP. + */ + flags |= GROUPING; + thousands_sep = locale_info->thousands_sep[0]; + grouping = locale_info->grouping; + goto rflag; case ' ': /* * ``If the space and + flags both appear, the space @@ -197,7 +222,7 @@ _doprnt(const char *fmt0, va_list argp, flags |= LONGDBL; goto rflag; case 'h': - if (flags&SHORTINT) { + if (flags & SHORTINT) { /* C99 */ /* for 'hh' - char */ flags |= CHARINT; @@ -207,7 +232,7 @@ _doprnt(const char *fmt0, va_list argp, } goto rflag; case 'l': - if (flags&LONGINT) + if (flags & LONGINT) flags |= LONGDBL; /* for 'll' - long long */ else flags |= LONGINT; @@ -238,37 +263,37 @@ _doprnt(const char *fmt0, va_list argp, sign = '-'; } base = 10; + flags |= FINITENUMBER; goto number; case 'e': case 'E': case 'f': case 'g': case 'G': + flags |= FLOAT; if (flags & LONGDBL) _ldouble = va_arg(argp, long double); else _ldouble = (long double)va_arg(argp, double); - if (flags & ZEROPAD) - { - _longdouble_union_t ip; - - ip.ld = _ldouble; - if (ip.ldt.exponent == 0x7fff) - flags &= ~ZEROPAD; - } + ieee_value.ld = _ldouble; + if (IS_FINITE(ieee_value)) + flags |= FINITENUMBER; + else if (flags & ZEROPAD) + /* Do not pad with zeros if infinty or NAN. */ + flags &= ~ZEROPAD; /* * don't do unrealistic precision; just pad it with * zeroes later, so buffer size stays rational. */ if (prec > MAXFRACT) { - if (*fmt != 'g' && (*fmt != 'G' || (flags&ALT))) + if (*fmt != 'g' && (*fmt != 'G' || (flags & ALT))) fpprec = prec - MAXFRACT; prec = MAXFRACT; } else if (prec == -1) { - if (flags&LONGINT) + if (flags & LONGINT) prec = DEFLPREC; else prec = DEFPREC; @@ -302,7 +327,7 @@ _doprnt(const char *fmt0, va_list argp, /* * cvt may have to round up past the "start" of the * buffer, i.e. ``intf("%.2f", (double)9.999);''; - * if the first char isn't NULL, it did. + * if the first char isn't NUL, it did. */ *buf = '\0'; size = cvtl(_ldouble, prec, flags, &softsign, *fmt, buf, @@ -316,6 +341,7 @@ _doprnt(const char *fmt0, va_list argp, if (softsign || (sign == '+' && neg_ldouble)) sign = '-'; t = *buf ? buf : buf + 1; + base = 10; goto pforw; case 'n': if (flags & LONGDBL) @@ -335,6 +361,7 @@ _doprnt(const char *fmt0, va_list argp, case 'o': ARG(unsigned); base = 8; + flags |= FINITENUMBER; goto nosign; case 'p': /* @@ -379,6 +406,7 @@ _doprnt(const char *fmt0, va_list argp, case 'u': ARG(unsigned); base = 10; + flags |= FINITENUMBER; goto nosign; case 'X': digs = "0123456789ABCDEF"; @@ -386,8 +414,9 @@ _doprnt(const char *fmt0, va_list argp, case 'x': ARG(unsigned); base = 16; + flags |= FINITENUMBER; /* leading 0x/X only if non-zero */ - if (flags & ALT && _ulonglong != 0) + if ((flags & ALT) && _ulonglong != 0) flags |= HEXPREFIX; nosign: @@ -412,21 +441,21 @@ _doprnt(const char *fmt0, va_list argp, if (_ulonglong != 0 || prec != 0) { /* conversion is done separately since operations - with long long are much slower */ -#define CONVERT(type) \ - { \ - register type _n = (type)_ulonglong; \ - do { \ - *--t = digs[_n % base]; \ - _n /= base; \ - } while (_n); \ - } - if (flags&LONGDBL) - CONVERT(unsigned long long) /* no ; */ + with long long are much slower */ +#define CONVERT(type) \ + do { \ + register type _n = (type)_ulonglong; \ + do { \ + *--t = digs[_n % base]; \ + _n /= base; \ + } while (_n); \ + } while (0) + if (flags & LONGDBL) + CONVERT(unsigned long long); else - CONVERT(unsigned long) /* no ; */ + CONVERT(unsigned long); #undef CONVERT - if (flags & ALT && base == 8 && *t != '0') + if ((flags & ALT) && base == 8 && *t != '0') *--t = '0'; /* octal leading 0 */ } @@ -434,6 +463,16 @@ _doprnt(const char *fmt0, va_list argp, size = buf + BUF - t; pforw: + if ((flags & FINITENUMBER) && (flags & GROUPING) && (base == 10) && thousands_sep && (*grouping != CHAR_MAX)) + { + register char *p; + + for (p = buf; *t; *p++ = *t++) + ; /* The function expects the string to be formated at the beginning of the buffer. */ + t = __grouping_format(buf, p, buf + BUF, flags); + size = buf + BUF - t; + } + /* * All reasonable formats wind up here. At this point, * `t' points to a string which (if not flags&LADJUST) @@ -460,7 +499,7 @@ _doprnt(const char *fmt0, va_list argp, realsz += 2; /* right-adjusting blank padding */ - if ((flags & (LADJUST|ZEROPAD)) == 0 && width) + if ((flags & (LADJUST | ZEROPAD)) == 0 && width) for (n = realsz; n < width; n++) PUTC(' '); /* prefix */ @@ -472,7 +511,7 @@ _doprnt(const char *fmt0, va_list argp, PUTC((char)*fmt); } /* right-adjusting zero padding */ - if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) + if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) for (n = realsz; n < width; n++) PUTC('0'); /* leading zeroes from decimal precision */ @@ -585,11 +624,12 @@ cvtl(long double number, int prec, int f number = integer; fract = modfl(number, &integer); /* If integer is zero then we need to look at where the sig figs are */ - if (integer<1) { - /* If fract is zero the zero before the decimal point is a sig fig */ - if (fract==0.0) doingzero=1; - /* If fract is non-zero all sig figs are in fractional part */ - else doextradps=1; + if (integer < 1) + { + /* If fract is zero the zero before the decimal point is a sig fig */ + if (fract == 0.0) doingzero = 1; + /* If fract is non-zero all sig figs are in fractional part */ + else doextradps = 1; } /* * get integer portion of number; put into the end of the buffer. @@ -617,8 +657,8 @@ cvtl(long double number, int prec, int f * if precision required or alternate flag set, add in a * decimal point. */ - if (prec || flags&ALT) - *t++ = decimal; + if (prec || (flags & ALT)) + *t++ = decimal_point; /* if requires more precision and some fraction left */ if (fract) { @@ -639,8 +679,8 @@ cvtl(long double number, int prec, int f if (expcnt) { *t++ = *++p; - if (prec || flags&ALT) - *t++ = decimal; + if (prec || (flags & ALT)) + *t++ = decimal_point; /* if requires more precision and some integer left */ for (; prec && ++p < endp; --prec) *t++ = *p; @@ -696,14 +736,14 @@ cvtl(long double number, int prec, int f break; } *t++ = tochar((int)tmp); - if (prec || flags&ALT) - *t++ = decimal; + if (prec || (flags & ALT)) + *t++ = decimal_point; } else { *t++ = '0'; - if (prec || flags&ALT) - *t++ = decimal; + if (prec || (flags & ALT)) + *t++ = decimal_point; } /* if requires more precision and some fraction left */ if (fract) @@ -721,10 +761,10 @@ cvtl(long double number, int prec, int f for (; prec--; *t++ = '0'); /* unless alternate flag, trim any g/G format trailing 0's */ - if (gformat && !(flags&ALT)) + if (gformat && !(flags & ALT)) { while (t > startp && *--t == '0'); - if (*t == decimal) + if (*t == decimal_point) --t; ++t; } @@ -773,10 +813,10 @@ cvtl(long double number, int prec, int f * if precision required or alternate flag set, add in a * decimal point. If no digits yet, add in leading 0. */ - if (prec || flags&ALT) + if (prec || (flags & ALT)) { dotrim = 1; - *t++ = decimal; + *t++ = decimal_point; } else dotrim = 0; @@ -788,8 +828,9 @@ cvtl(long double number, int prec, int f /* If we're not adding 0s * or we are but they're sig figs: * decrement the precision */ - if ((doextradps!=1) || ((int)tmp!=0)) { - doextradps=0; + if ((doextradps != 1) || ((int)tmp != 0)) + { + doextradps = 0; prec--; } } @@ -797,12 +838,12 @@ cvtl(long double number, int prec, int f startp = doprnt_roundl(fract, (int *)NULL, startp, t - 1, (char)0, signp); /* alternate format, adds 0's for precision, else trim 0's */ - if (flags&ALT) + if (flags & ALT) for (; prec--; *t++ = '0'); else if (dotrim) { while (t > startp && *--t == '0'); - if (*t != decimal) + if (*t != decimal_point) ++t; } } @@ -820,7 +861,7 @@ doprnt_roundl(long double fract, int *ex if (fract == 0.5L) { char *e = end; - if (*e == decimal) + if (*e == decimal_point) e--; if (*e == '0' || *e == '2' || *e == '4' || *e == '6' || *e == '8') @@ -837,7 +878,7 @@ doprnt_roundl(long double fract, int *ex if (tmp > 4) for (;; --end) { - if (*end == decimal) + if (*end == decimal_point) --end; if (++*end <= '9') break; @@ -861,7 +902,7 @@ doprnt_roundl(long double fract, int *ex else if (*signp == '-') for (;; --end) { - if (*end == decimal) + if (*end == decimal_point) --end; if (*end != '0') break; @@ -939,3 +980,54 @@ isspeciall(long double d, char *bufp) (void)strcpy(bufp, "Inf"); return(3); } + +static __inline__ char * +__grouping_format(char *string_start, char *string_end, char *buffer_end, int flags) +{ + /* + * Format the string representing the integer portion of a decimal + * conversion using non-mometary thousands' grouping characters. + * It is assumed that STRING_START points at the beginning and + * STRING_END points to the end of the string to be formatted. + * BUFFER_END points to the end of the buffer that will store + * the formated string. + */ + + ptrdiff_t grouping_size; + register char *pos, *src, *dst; + + + src = string_end; + dst = buffer_end; + + /* + * The fractional portion of the result of a decimal conversion + * (%f, %F, %g or %G) is left unaltered. + */ + if (flags & FLOAT) + for (; *src != decimal_point; *--dst = *--src) + ; /* Copy fractional portion to the end of the buffer. */ + + pos = dst; + grouping_size = (ptrdiff_t)*grouping; + if (grouping_size == (ptrdiff_t)CHAR_MAX) + for (; src > string_start; *--dst = *--src) + ; /* Group the remaindaer digits together. */ + else + for (; src > string_start; *--dst = *--src) + if (pos - dst == grouping_size) + { + *--dst = thousands_sep; + pos = dst; + if (grouping_size) + { + grouping++; + if (*grouping == (ptrdiff_t)CHAR_MAX) + grouping_size = 0; /* Group the remaindaer digits together. */ + else if (*grouping) + grouping_size = (ptrdiff_t)*grouping; /* Get next size of group of digits to be formatted. */ + } + } + + return (*dst == thousands_sep) ? dst + 1 : dst; /* Remove leading thousands separator character. */ +} diff -aprNU3 djgpp.orig/src/libc/ansi/stdio/printf.txh djgpp/src/libc/ansi/stdio/printf.txh --- djgpp.orig/src/libc/ansi/stdio/printf.txh 2003-01-29 12:28:44 +0000 +++ djgpp/src/libc/ansi/stdio/printf.txh 2008-04-23 20:09:36 +0000 @@ -24,6 +24,11 @@ an optional flag, which may alter the co @table @code +@item ' + +Formats the integer portion of the result of a decimal conversion (%i, %d, %u, +%f, %F, %g and %G) using the non-monetary thousends' grouping character. + @item - left-justify the field.