Message-ID: <3E93C1ED.32C6571D@hrz1.hrz.tu-darmstadt.de> Date: Wed, 09 Apr 2003 08:47:09 +0200 From: Juan Manuel Guerrero Organization: TU Darmstadt X-Mailer: Mozilla 4.61 [en] (WinNT; I) X-Accept-Language: en MIME-Version: 1.0 To: djgpp-workers AT delorie DOT com Subject: Re: %n$, *m$ and some other c99 support for doprnt.c Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-MailScanner: Found to be clean Reply-To: djgpp-workers AT delorie DOT com Here is a mofified version of the previous patch. This time against DJGPP CVS. Richard Dawe wrote: [snip] > I think removing my code from _doprnt may be the easiest way to do it. I can't > remember if I made any bugfixes at the same time. I don't think so. But it's > worth checking the comments in CVS log and doing some diffs. There have been a modification to macro ARG to handle hh length modifiers but that macro will be replaced completely by the new macros SIGNED_ARG(), UNSIGNED_ARG(), SIGNED_TYPEDEF_ARG() and UNSIGNED_TYPEDEF_ARG() and they will handle that case now. With the introduction of the j, t and z length modifiers you have used: flags |= LONGDBL to code usage of j length modifier and flags |= LONGINT to code usage of z length modifier. I had to remove this because my code expects flags |= INTMAXT for j length modifier, flags |= PTRDIFFT for t length modifier and flags |= SIZET for z length modifier. There are no other modifications concerning your patches for new C99 length modifiers support implementation. [snip] > > + static char INF_REP[2][4] = {"inf", "INF"}; /* inf for [aefg] and INF for [aefg] */ > > + static char NAN_REP[2][4] = {"nan", "NAN"}; /* nan for [aefg] and NAN for [aefg] */ > > Surely the second "[aefg]" should be "[AEFG]" for both INF_REP and NAN_REP? > > Where possible, I think we should declare these character arrays as "const". > From what I remember of my work on making printf output nan(0x[0-9a-f]*) for > NaNs, NULL_REP can't be made const, but the others can. Fixed and arrays declared as const. [snip] > > *************** cvtl(long double number, int prec, int f > > *** 510,515 **** > > --- 1143,1284 ---- > > if ((expcnt = isspeciall(number, startp))) > > return(expcnt); > > > > + if (fmtch == 'a' || fmtch == 'A') > > + { > > + /* > > + * We are dealing with intel's extended double format. > > + * The 64-bit mantissa explicitely contains the leading integer digit. > > + * C99 standard defines hex float format as: 0xh.hhhp+ddd > > + * where h is a hex digit, d is a decimal digit and p represents 2. > > + * The standard defines that the hex representation of the binary > > + * mantissa must be exact for all precisions greater zero. > > + * This implies that the 15 hex digits of the fractional part of the > > + * mantissa will always be outputed even if the requested precision > > + * is less than 15. If the requested precision is more than 15 digits > > + * then the mantissa is paded with zeros to the right. If the requested > > + * precision is zero then the leading hex digit will be rounded > > + * appropiately and the hole fractional part will be omitted. > > + * The 64 bit of the mantissa can be subdivided into 16 nibbles, > > + * so exact hex representation of the binary coded mantissa is > > + * possible. To get a single hex digit in the integer part of > > + * the mantissa the period must be shifted by three places > > + * to the right. This makes it necessary to adjust the bias > > + * from 0x3fff to 0x4002. > > + */ > > + > > + #define IEEE754_LONG_DOUBLE_BIAS 0x3fff > > + #define LONG_DOUBLE_BIAS (IEEE754_LONG_DOUBLE_BIAS + 0x03) > > + #define LONG_DOUBLE_DENORMALIZED_BIAS (LONG_DOUBLE_BIAS - 0x01) > > + > > + #define ITOA(value, base, string_end, case) \ > > + /* Create a string from a positive/unsigned integer */ \ > > + do { \ > > + const char udigits[] = "0123456789ABCDEF"; \ > > + const char ldigits[] = "0123456789abcdef"; \ > > + const char *digit = (case) ? udigits : ldigits; \ > > You should probably set these up outside the loop. I expect the compiler will > do that optimisation for you, but why rely on the compiler? Sorry, but I do not understand this. ITOA() is a macro that should be expanded by the preprocessor inside the if-block. I have defined the macro inside the if-block because it should only be used there. Anyway, there is a similar macro called CONVERSION somewhere defined in another if-block in _doprnt(). I have removed the CONVERSION definition from inside that block and I have modified the macro in such a way that it will do the job for both calls (call somewhere in _doprnt and call here in cvtl()). The macro is now defined outside of both functions. [snip] ..all the things about printf.txh... [snip] Everything fixed. [snip] > I wrote some test cases for them (see tests/libc/ansi/stdio/printf2.c), > which we can still use. It is probably worth checking that the test behaves > the same way before and after applying your patch. Done. Output before patching: 127 127 127 0x7f 0x7F -1 -1 255 0xff 0xFF -128 -128 -1 -1 0377 255 0xff 0xFF -1 -1 0377 255 0xff 0xFF 9223372036854775807 9223372036854775807 0777777777777777777777 9223372036854775807 0x7fffffffffffffff 0x7FFFFFFFFFFFFFFF -1 -1 01777777777777777777777 18446744073709551615 0xffffffffffffffff 0xFFFFFFFFFFFFFFFF 2147483647 2147483647 017777777777 2147483647 0x7fffffff 0x7FFFFFFF -1 -1 037777777777 4294967295 0xffffffff 0xFFFFFFFF 2147483647 2147483647 017777777777 2147483647 0x7fffffff 0x7FFFFFFF -1 -1 037777777777 4294967295 0xffffffff 0xFFFFFFFF Output after patching: 127 127 127 0x7f 0x7F -1 -1 255 0xff 0xFF -128 -128 -1 -1 0377 255 0xff 0xFF -1 -1 0377 255 0xff 0xFF 9223372036854775807 9223372036854775807 0777777777777777777777 9223372036854775807 0x7fffffffffffffff 0x7FFFFFFFFFFFFFFF -1 -1 01777777777777777777777 18446744073709551615 0xffffffffffffffff 0xFFFFFFFFFFFFFFFF 2147483647 2147483647 017777777777 2147483647 0x7fffffff 0x7FFFFFFF -1 -1 037777777777 4294967295 0xffffffff 0xFFFFFFFF 2147483647 2147483647 017777777777 2147483647 0x7fffffff 0x7FFFFFFF -1 -1 037777777777 4294967295 0xffffffff 0xFFFFFFFF As can be seen, the output is the same (after I fixed a bug). Concerning A,a conversion specifier. Because I can not distinguish between float, double and long double args passed to _doprnt(), I have assumed a default precision of 15 hex digits in the fractional part of the mantissa. This means, if the user does not specify a precision, always the full 60 binary digits will be converted to hex digits and printed no matter if the passed argument is really a long double or not. This is different from the way Linux behaves. They seems to be capable to identify floats and then printing only the 5 or 6 hex digits of the float mantissa if no precision is given by the user. I have also adopted some of the minor changes proposed in the nan(0x[0-9a-f]*)- support patch like the global definition struct IEEExp, etc. Please, look at the patch. Comments, suggestions, objections are welcome. Regards, Juan M. Guerrero Index: djgpp/src/libc/ansi/stdio/doprnt.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdio/doprnt.c,v retrieving revision 1.14 diff -u -r1.14 doprnt.c --- djgpp/src/libc/ansi/stdio/doprnt.c 24 Jan 2003 18:53:33 -0000 1.14 +++ djgpp/src/libc/ansi/stdio/doprnt.c 9 Apr 2003 06:42:49 -0000 @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -18,7 +20,20 @@ #include #include -static char decimal = '.'; +struct IEEExp { + unsigned manl:32; + unsigned manh:32; + unsigned exp:15; + unsigned sign:1; +}; + +static char decimal_point; +static char thousands_sep; + +#undef FALSE +#define FALSE 0 +#undef TRUE +#define TRUE 1 /* 11-bit exponent (VAX G floating point) is 308 decimal digits */ #define MAXEXP 308 @@ -28,17 +43,54 @@ #define DEFPREC 6 #define DEFLPREC 6 +#define DEFHEXPREC 15 #define BUF (MAXEXPLD+MAXFRACT+1) /* + decimal point */ #define PUTC(ch) (void) putc(ch, fp) -#define ARG(basetype) _ulonglong = \ - flags&LONGDBL ? va_arg(argp, long long basetype) : \ - flags&LONGINT ? va_arg(argp, long basetype) : \ - flags&SHORTINT ? (short basetype)va_arg(argp, int) : \ - flags&CHARINT ? (char basetype)va_arg(argp, int) : \ - (basetype)va_arg(argp, int) +union _arg { + int int_arg; + unsigned int uint_arg; + long long_arg; + unsigned long ulong_arg; + long long longlong_arg; + unsigned long long ulonglong_arg; + intmax_t intmax_t_arg; + uintmax_t uintmax_t_arg; + ptrdiff_t ptrdiff_t_arg; + size_t size_t_arg; + double double_arg; + long double longdouble_arg; + long long *plonglong_arg; + long *plong_arg; + int *pint_arg; + short *pshort_arg; + char *pchar_arg; + unsigned char *puchar_arg; + intmax_t *pintmax_t_arg; + ptrdiff_t *pptrdiff_t_arg; + size_t *psize_t_arg; + void *pvoid_arg; +}; +typedef union _arg ARGUMENT_TABLE_OBJECT; + +enum _conv_spec_type { + EMPTY, + INTEGER, U_INTEGER, + LONG, U_LONG, + LONG_LONG, U_LONG_LONG, + INTMAX_T, U_INTMAX_T, + PTRDIFF_T, SIZE_T, + DOUBLE, LONG_DOUBLE, + P_LONG_LONG, P_LONG, + P_INTEGER, P_SHORT, + P_CHAR, P_U_CHAR, + P_INTMAX_T, P_PTRDIFF_T, + P_SIZE_T, + P_VOID +}; +typedef enum _conv_spec_type CONV_SPEC_TABLE_OBJECT; static int nan_p = 0; @@ -60,11 +112,15 @@ #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 CHARINT 0x08 /* print char using integer format */ +#define INTMAXT 0x10 /* intmax_t */ +#define PTRDIFFT 0x20 /* ptrdiff_t */ +#define SIZET 0x40 /* size_t */ +#define ALT 0x80 /* alternate form */ +#define LADJUST 0x100 /* left adjustment */ +#define ZEROPAD 0x200 /* zero (as opposed to blank) pad */ +#define HEXPREFIX 0x400 /* add 0x or 0X prefix */ +#define GROUPING 0x800 /* thousands grouping */ static int cvtl(long double number, int prec, int flags, char *signp, unsigned char fmtch, char *startp, char *endp); @@ -75,8 +131,456 @@ static int isspeciall(long double d, char *bufp); #endif +static const char LOWER_DIGITS[] = "0123456789abcdef"; +static const char UPPER_DIGITS[] = "0123456789ABCDEF"; +static const char UNNORMAL_REP[] = "Unnormal"; static char NULL_REP[] = "(null)"; -static char UNNORMAL_REP[] = "Unnormal"; +static char INF_REP[2][4] = {"inf", "INF"}; /* inf for [aefg] and INF for [AEFG] */ +static char NAN_REP[2][4] = {"nan", "NAN"}; /* nan for [aefg] and NAN for [AEFG] */ +static int style; /* selects case of INF/NAN representation to be used*/ + +static __inline__ int +__get_amount_of_args(const char *fmt) +{ + /* + Determinate the amount of arguments (objects) on the stack. + + ``The results of mixing numbered and unnumbered argument + conversion specifications in a format string are undefined.'' + --- IEEE Std 1003.1-2001 + */ + + int ch; /* character from fmt */ + int number_of_args; /* number of arguments*/ + int using_numbered_conv_spec; /* type of conv specs in use */ + +#define GET_STAR_ARG_NUMBER() \ + /* \ + Determinate the actual value of number_of_args. \ + If unnumbered argument conversion specifiers are used number_of_args will \ + be simply incremented from its previous value. \ + If numbered argument conversion specifiers are used number_of_args will \ + be compared with n, where n is the number that may appear in the conver- \ + sion specifiers like: %n1$*n2$.*n3$. If n is greater than number_of_args, \ + number_of_args will be set to n, so the number_of_args will always be \ + equal to the actually greatest known number of arguments placed on stack. \ + The final result will be the amount of arguments to be retired from stack. \ + */ \ + do { \ + register int n = 0; \ + fmt++; \ + while (isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt)) \ + n = 10 * n + todigit(*fmt++); \ + if (*fmt == '$') \ + { \ + using_numbered_conv_spec = TRUE; \ + if (n > number_of_args) \ + number_of_args = n; \ + } \ + else \ + { \ + number_of_args++; /* unnumbered argument conversion specification */ \ + fmt--; \ + } \ + } while (0) + +#define GET_ARG_NUMBER() \ + /* \ + Determinate the actual value of number_of_args. \ + Check for %n$ argument conversion specifiers. \ + The actually valid value for the number of arguments is returned in \ + number_of_args. \ + */ \ + do { \ + register int n = 0; \ + while (isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt)) \ + n = 10 * n + todigit(*fmt++); \ + if (*fmt == '$') \ + { \ + using_numbered_conv_spec = TRUE; \ + if (n > number_of_args) \ + number_of_args = n; \ + } \ + else \ + fmt--; \ + } while (0) + + + using_numbered_conv_spec = FALSE; /* Unnumbered argument conversion specifications selected by default. */ + for (number_of_args = 1;; fmt++) + { + while ((ch = *fmt) && ch != '%') + fmt++; + if (!ch) + break; + rflag: + switch (*++fmt) + { + /* Flags */ + case ' ': case '#': + case '+': case '-': + case '.': case '\'': + goto rflag; + + /* Field width, precision and *nn$ argument conversion specifier */ + case '*': + GET_STAR_ARG_NUMBER(); + goto rflag; + + /* Field width, precision and %nn$ argument conversion specifier */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + GET_ARG_NUMBER(); + goto rflag; + + /* Length modifiers */ + case 'L': case 'h': case 'j': + case 'l': case 't': case 'z': + goto rflag; + + /* Conversion specifiers */ + case 'a': case 'A': + case 'c': + case 'D': + case 'd': case 'i': + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + case 'n': + case 'O': case 'o': + case 'p': + case 's': + case 'U': case 'u': + case 'X': case 'x': + if (!using_numbered_conv_spec) + number_of_args++; + default: + break; + } + } + if (using_numbered_conv_spec) + number_of_args++; + + return number_of_args; + +#undef GET_STAR_ARG_NUMBER +#undef GET_ARG_NUMBER +} + +static __inline__ void +__load_arg_array(const char *fmt, va_list argp, + int number_of_args, ARGUMENT_TABLE_OBJECT *argument) +{ + /* + Initialise argument array with objects from stack. + + ``The results of mixing numbered and unnumbered argument + conversion specifications in a format string are undefined.'' + --- IEEE Std 1003.1-2001 + */ + + int ch; /* character from fmt */ + int flags; /* flags for aAdieEfFgG */ + int table_index; /* index of the arrays */ + CONV_SPEC_TABLE_OBJECT *conv_specifier; /* array of conversion specifiers */ + +#define SIGNED_ARG_TYPE() flags & INTMAXT ? INTMAX_T : \ + flags & PTRDIFFT ? PTRDIFF_T : \ + flags & SIZET ? SIZE_T : \ + flags & LONGDBL ? LONG_LONG : \ + flags & LONGINT ? LONG : INTEGER + +#define UNSIGNED_ARG_TYPE() flags & INTMAXT ? U_INTMAX_T : \ + flags & PTRDIFFT ? PTRDIFF_T : \ + flags & SIZET ? SIZE_T : \ + flags & LONGDBL ? U_LONG_LONG : \ + flags & LONGINT ? U_LONG : U_INTEGER + +#define GET_ARG_INDEX() \ + /* \ + Check for %n$ argument specifiers. \ + If numbered argument conversion specifiers are used, table_index will \ + be set to the appropriate value. For unnumbered argument conversion \ + specifier this is a no action. \ + */ \ + do { \ + register int n = 0; \ + while (isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt)) \ + n = 10 * n + todigit(*fmt++); \ + if (*fmt == '$') \ + table_index = n; \ + else \ + fmt--; /* unnumbered argument conversion specification */ \ + } while (0) + +#define GET_STAR_ARG_INDEX() \ + /* \ + Add * or *n$ argument coversion specifiers to the conversion specifier \ + array. \ + */ \ + do { \ + register int n = 0; \ + fmt++; \ + while (isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt)) \ + n = 10 * n + todigit(*fmt++); \ + if (*fmt == '$') \ + conv_specifier[n] = INTEGER; \ + else \ + { \ + conv_specifier[table_index++] = INTEGER; \ + fmt--; /* unnumbered argument conversion specification */ \ + } \ + } while (0) + + + argument[0].int_arg = !EOF; + if (!(conv_specifier = malloc(number_of_args * sizeof(CONV_SPEC_TABLE_OBJECT)))) + { + argument[0].int_arg = EOF; + return; + } + + for (table_index = 1;; fmt++, table_index++) /* Generate conv specifier array. */ + { + while ((ch = *fmt) && ch != '%') + fmt++; + if (!ch) + break; + flags = 0; + rflag: + switch (*++fmt) + { + /* Flags */ + case ' ': case '#': + case '+': case '-': + case '.': case '\'': + goto rflag; + + /* Field width and precision */ + case '*': + GET_STAR_ARG_INDEX(); + goto rflag; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + GET_ARG_INDEX(); + goto rflag; + + /* Length modifiers */ + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + if (flags & SHORTINT) + { + flags &= ~SHORTINT; + flags |= CHARINT; + } + else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; + goto rflag; + case 'l': + if (flags & LONGINT) + flags |= LONGDBL; /* for 'll' - long long */ + else + flags |= LONGINT; + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; + goto rflag; + + /* Conversion specifiers */ + case 'c': + conv_specifier[table_index] = INTEGER; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + conv_specifier[table_index] = SIGNED_ARG_TYPE(); + break; + case 'a': case 'A': + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + if (flags & LONGDBL) + conv_specifier[table_index] = LONG_DOUBLE; + else + conv_specifier[table_index] = DOUBLE; + break; + case 'n': + if (flags & LONGDBL) + conv_specifier[table_index] = P_LONG_LONG; + else if (flags & LONGINT) + conv_specifier[table_index] = P_LONG; + else if (flags & SHORTINT) + conv_specifier[table_index] = P_SHORT; + else if (flags & CHARINT) + conv_specifier[table_index] = P_CHAR; + else if (flags & INTMAXT) + conv_specifier[table_index] = P_INTMAX_T; + else if (flags & PTRDIFFT) + conv_specifier[table_index] = P_PTRDIFF_T; + else if (flags & SIZET) + conv_specifier[table_index] = P_SIZE_T; + else + conv_specifier[table_index] = P_INTEGER; + break; + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + conv_specifier[table_index] = UNSIGNED_ARG_TYPE(); + break; + case 'p': + conv_specifier[table_index] = P_VOID; + break; + case 's': + conv_specifier[table_index] = P_CHAR; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + case 'X': case 'x': + conv_specifier[table_index] = UNSIGNED_ARG_TYPE(); + break; + default: + break; + } + } + + /* Load argument array with objects from stack. */ + for (table_index = 1; table_index < number_of_args; table_index++) + { + switch (conv_specifier[table_index]) + { + case EMPTY: + argument[table_index].int_arg = EOF; /* Something went wrong. */ + break; + case INTEGER: + argument[table_index].int_arg = va_arg(argp, int); + break; + case U_INTEGER: + argument[table_index].uint_arg = va_arg(argp, unsigned int); + break; + case LONG: + argument[table_index].long_arg = va_arg(argp, long); + break; + case U_LONG: + argument[table_index].ulong_arg = va_arg(argp, unsigned long); + break; + case LONG_LONG: + argument[table_index].longlong_arg = va_arg(argp, long long); + break; + case U_LONG_LONG: + argument[table_index].ulonglong_arg = va_arg(argp, unsigned long long); + break; + case INTMAX_T: + argument[table_index].intmax_t_arg = va_arg(argp, intmax_t); + break; + case U_INTMAX_T: + argument[table_index].uintmax_t_arg = va_arg(argp, uintmax_t); + break; + case PTRDIFF_T: + argument[table_index].ptrdiff_t_arg = va_arg(argp, ptrdiff_t); + break; + case SIZE_T: + argument[table_index].size_t_arg = va_arg(argp, size_t); + break; + case DOUBLE: + argument[table_index].double_arg = va_arg(argp, double); + break; + case LONG_DOUBLE: + argument[table_index].longdouble_arg = va_arg(argp, long double); + break; + case P_LONG_LONG: + argument[table_index].plonglong_arg = va_arg(argp, long long *); + break; + case P_LONG: + argument[table_index].plong_arg = va_arg(argp, long *); + break; + case P_INTEGER: + argument[table_index].pint_arg = va_arg(argp, int *); + break; + case P_SHORT: + argument[table_index].pshort_arg = va_arg(argp, short *); + break; + case P_CHAR: + argument[table_index].pchar_arg = va_arg(argp, char *); + break; + case P_U_CHAR: + argument[table_index].pchar_arg = va_arg(argp, unsigned char *); + break; + case P_INTMAX_T: + argument[table_index].pintmax_t_arg = va_arg(argp, intmax_t *); + break; + case P_PTRDIFF_T: + argument[table_index].pptrdiff_t_arg = va_arg(argp, ptrdiff_t *); + break; + case P_SIZE_T: + argument[table_index].psize_t_arg = va_arg(argp, size_t *); + break; + case P_VOID: + argument[table_index].pvoid_arg = va_arg(argp, void *); + break; + } + } + + free(conv_specifier); + +#undef GET_ARG_INDEX +#undef GET_STAR_ARG_INDEX +#undef SIGNED_ARG_TYPE +#undef UNSIGNED_ARG_TYPE +} + +#define GROUPING_FORMAT(string_start, string_end, buffer_end) \ + do { \ + /* \ + * 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 begining of the \ + * string to be formatted. \ + */ \ + char *strp, *pos, *src, *dst; \ + long int grouping_size; \ + \ + strp = localeconv()->grouping; \ + grouping_size = strtol(strp, &strp, 10); \ + for (src = (string_end), dst = pos = (buffer_end); src > (string_start); *--dst = *--src) \ + if (pos - dst == grouping_size) \ + { \ + *--dst = thousands_sep; \ + pos = dst; \ + if (*strp) \ + { \ + strp++; /* Get next size of group of digits to be formatted. */ \ + grouping_size = strtol(strp, &strp, 10); \ + } \ + } \ + src = (*dst == thousands_sep) ? dst + 1 : dst; /* Remove leading thousands separator character. */ \ + for (dst = (string_start); src < (buffer_end); *dst++ = *src++) \ + ; \ + (string_end) = dst; \ + } while (0) + +#define CONVERT(type, value, base, string_end, case) \ + do { \ + const char *digit = (case) ? UPPER_DIGITS : LOWER_DIGITS; \ + register type _value = (type)(value); \ + \ + do { \ + *--(string_end) = digit[(_value) % (base)]; \ + } while ((_value) /= (base)); \ + } while (0) + int _doprnt(const char *fmt0, va_list argp, FILE *fp) @@ -89,6 +593,7 @@ long double _ldouble; /* double and long double precision arguments %L.[eEfgG] */ unsigned long long _ulonglong=0; /* integer arguments %[diouxX] */ + uintmax_t _uintmax = 0; /* integer arguments %[jtz] */ int base; /* base for [diouxX] conversion */ int dprec; /* decimal precision in [diouxX] */ int fieldsz; /* field size expanded by sign, etc */ @@ -101,10 +606,64 @@ char sign; /* sign prefix (' ', '+', '-', or \0) */ char softsign; /* temporary negative sign for floats */ const char *digs; /* digits for [diouxX] conversion */ - char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ + char buf[BUF]; /* space for %c, %[diouxX], %[aAeEfgG] */ int neg_ldouble = 0; /* non-zero if _ldouble is negative */ + int table_index; /* index of array of arguments */ + int number_of_args; /* number of arguments*/ + ARGUMENT_TABLE_OBJECT *argument; /* 1-based array of arguments */ + +#define SIGNED_ARG() _ulonglong = \ + flags & LONGDBL ? (signed long long)argument[table_index].longlong_arg : \ + flags & LONGINT ? (signed long long)argument[table_index].long_arg : \ + flags & SHORTINT ? (signed long long)(signed short)argument[table_index].int_arg : \ + flags & CHARINT ? (signed long long)(signed char)argument[table_index].int_arg : \ + (long long)argument[table_index].int_arg + +#define UNSIGNED_ARG() _ulonglong = \ + flags & LONGDBL ? argument[table_index].ulonglong_arg : \ + flags & LONGINT ? (unsigned long long)argument[table_index].ulong_arg : \ + flags & SHORTINT ? (unsigned long long)(unsigned short)argument[table_index].uint_arg : \ + flags & CHARINT ? (unsigned long long)(unsigned char)argument[table_index].uint_arg : \ + (unsigned long long)argument[table_index].uint_arg + +#define SIGNED_TYPEDEF_ARG() _uintmax = \ + flags & INTMAXT ? (intmax_t)argument[table_index].intmax_t_arg : \ + flags & PTRDIFFT ? (intmax_t)(ptrdiff_t)argument[table_index].ptrdiff_t_arg : \ + flags & SIZET ? (intmax_t)(ssize_t)argument[table_index].size_t_arg : \ + (intmax_t)argument[table_index].longlong_arg + +#define UNSIGNED_TYPEDEF_ARG() _uintmax = \ + flags & INTMAXT ? argument[table_index].uintmax_t_arg : \ + flags & PTRDIFFT ? (uintmax_t)(unsigned int)argument[table_index].ptrdiff_t_arg : \ + flags & SIZET ? (uintmax_t)(size_t)argument[table_index].size_t_arg : \ + (uintmax_t)argument[table_index].ulonglong_arg + +#define GET_STAR_ARG_VALUE(v) \ + /* \ + Get value of a * or *n$ argument conversion specifier. \ + If unnumbered arguments conversion specifiers are used, table_index \ + will be incremented to point to next argument in array. \ + If numbered arguments conversion specifiers are used, table_index \ + will be recomputed appropriately before accessing next argument in \ + array so this table_index incrementation here has no efect. \ + */ \ + do { \ + register int i = 0; \ + fmt++; \ + while (isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt)) \ + i = 10 * i + todigit(*fmt++); \ + if (*fmt == '$') \ + (v) = argument[i].int_arg; \ + else \ + { \ + (v) = argument[table_index++].int_arg; \ + fmt--; /* unnumbered argument conversion specification */ \ + } \ + } while (0) - decimal = localeconv()->decimal_point[0]; + + decimal_point = localeconv()->decimal_point[0]; + thousands_sep = localeconv()->thousands_sep[0]; if (fp->_flag & _IORW) { @@ -114,10 +673,34 @@ if ((fp->_flag & _IOWRT) == 0) return (EOF); + /* Determinate the amount of arguments (objects) on the stack. */ fmt = fmt0; - digs = "0123456789abcdef"; - for (cnt = 0;; ++fmt) + number_of_args = __get_amount_of_args(fmt); + if (!(argument = malloc(number_of_args * sizeof(ARGUMENT_TABLE_OBJECT)))) + return (EOF); + + /* Initialise argument array with objects from stack. */ + fmt = fmt0; + __load_arg_array(fmt, argp, number_of_args, argument); + if (argument[0].int_arg == EOF) /* Something went wrong. */ + return (EOF); + + /* Format and print */ + fmt = fmt0; + digs = LOWER_DIGITS; + for (cnt = 0, table_index = 1;; fmt++, table_index++) { + /* + For unnumbered argument conversion specifiers, table_index is + incremented in the for-loop so the argument array is accessed + in the usual sequential way as the stack is accessed. + For numbered argument conversion specifiers, table_index is always + recomputed in the appropriate way by the code selected with the + ``case '1': through case '9':'' chain of cases and by the + ``case '*''' and ``case '.''' using macro GET_STAR_ARG_VALUE. + */ + + style = 0; /* nan/inf will be outputed */ while ((ch = *fmt) && ch != '%') { PUTC (ch); @@ -125,13 +708,16 @@ cnt++; } if (!ch) - return cnt; + goto finished; flags = 0; dprec = 0; fpprec = 0; width = 0; prec = -1; sign = '\0'; rflag: switch (*++fmt) { + case '\'': + flags |= GROUPING; + goto rflag; case ' ': /* * ``If the space and + flags both appear, the space @@ -151,8 +737,9 @@ * -- ANSI X3J11 * They don't exclude field widths read from args. */ - if ((width = va_arg(argp, int)) >= 0) - goto rflag; + GET_STAR_ARG_VALUE(width); + if (width >= 0) + goto rflag; width = -width; /* FALLTHROUGH */ case '-': @@ -163,13 +750,13 @@ goto rflag; case '.': if (*++fmt == '*') - n = va_arg(argp, int); + GET_STAR_ARG_VALUE(n); else { - n = 0; - while (isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt)) - n = 10 * n + todigit(*fmt++); - --fmt; + n = 0; + while (isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt)) + n = 10 * n + todigit(*fmt++); + --fmt; } prec = n < 0 ? -1 : n; goto rflag; @@ -185,41 +772,47 @@ case '5': case '6': case '7': case '8': case '9': n = 0; do { - n = 10 * n + todigit(*fmt); + n = 10 * n + todigit(*fmt); } while (isascii((unsigned char)*++fmt) && isdigit((unsigned char)*fmt)); - width = n; - --fmt; + if (*fmt == '$') + table_index = n; /* Set index for next numbered argument array access. */ + else + { + --fmt; + width = n; + } goto rflag; case 'L': flags |= LONGDBL; goto rflag; case 'h': - if (flags&SHORTINT) { + if (flags & SHORTINT) + { /* C99 */ /* for 'hh' - char */ - flags |= CHARINT; flags &= ~SHORTINT; - } else { - flags |= SHORTINT; + flags |= CHARINT; } + else + flags |= SHORTINT; + goto rflag; + case 'j': /* C99 */ + flags |= INTMAXT; goto rflag; case 'l': - if (flags&LONGINT) + if (flags & LONGINT) flags |= LONGDBL; /* for 'll' - long long */ else flags |= LONGINT; goto rflag; - case 'j': /* C99 */ - flags |= LONGDBL; /* long long */ + case 't': /* C99 */ + flags |= PTRDIFFT; goto rflag; case 'z': /* C99 */ - flags |= LONGINT; - goto rflag; - case 't': /* C99 */ - /* t => int, which is the default. */ + flags |= SIZET; goto rflag; case 'c': - *(t = buf) = va_arg(argp, int); + *(t = buf) = argument[table_index].int_arg; size = 1; sign = '\0'; goto pforw; @@ -228,37 +821,67 @@ /*FALLTHROUGH*/ case 'd': case 'i': - ARG(signed); - if ((long long)_ulonglong < 0) + if (flags & (INTMAXT | PTRDIFFT | SIZET)) { - _ulonglong = -_ulonglong; - sign = '-'; + SIGNED_TYPEDEF_ARG(); + if ((intmax_t)_uintmax < 0) + { + _uintmax = -_uintmax; + sign = '-'; + } + } + else + { + SIGNED_ARG(); + if ((long long)_ulonglong < 0) + { + _ulonglong = -_ulonglong; + sign = '-'; + } } base = 10; goto number; - case 'e': + case 'A': case 'E': + case 'F': + case 'G': + style = 1; /* NAN/INF will be outputed */ + case 'a': + case 'e': case 'f': case 'g': - case 'G': + if (*fmt == 'A' || *fmt == 'a') flags |= HEXPREFIX; if (flags & LONGDBL) - _ldouble = va_arg(argp, long double); + _ldouble = argument[table_index].longdouble_arg; else - _ldouble = (long double)va_arg(argp, double); + _ldouble = (long double)argument[table_index].double_arg; /* * 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 if (*fmt == 'A' || *fmt == 'a') + /* + * C99 imposes that precision must be sufficient + * for an exact representation of the mantissa. + * Because floating point arguments are promoted + * to long double arguments, there is no way to + * distinguish between float, double and long + * double mantissa. Due to this fact, if no + * precision has been given, then a precision + * sufficient large for exact long double mantissa + * representation will be used as default. + */ + prec = DEFHEXPREC; else prec = DEFPREC; } @@ -274,12 +897,7 @@ } else { - struct IEEExp { - unsigned manl:32; - unsigned manh:32; - unsigned exp:15; - unsigned sign:1; - } ip = *(struct IEEExp *)&_ldouble; + struct IEEExp ip = *(struct IEEExp *)&_ldouble; if (ip.sign) neg_ldouble = 1; @@ -308,21 +926,30 @@ goto pforw; case 'n': if (flags & LONGDBL) - *va_arg(argp, long long *) = cnt; + *argument[table_index].plonglong_arg = cnt; else if (flags & LONGINT) - *va_arg(argp, long *) = cnt; + *argument[table_index].plong_arg = cnt; else if (flags & SHORTINT) - *va_arg(argp, short *) = cnt; + *argument[table_index].pshort_arg = cnt; else if (flags & CHARINT) - *va_arg(argp, char *) = (char)cnt; + *argument[table_index].pchar_arg = cnt; + else if (flags & INTMAXT) + *argument[table_index].pintmax_t_arg = cnt; + else if (flags & PTRDIFFT) + *argument[table_index].pptrdiff_t_arg = cnt; + else if (flags & SIZET) + *argument[table_index].psize_t_arg = cnt; else - *va_arg(argp, int *) = cnt; + *argument[table_index].pint_arg = cnt; break; case 'O': flags |= LONGINT; /*FALLTHROUGH*/ case 'o': - ARG(unsigned); + if (flags & (INTMAXT | PTRDIFFT | SIZET)) + UNSIGNED_TYPEDEF_ARG(); + else + UNSIGNED_ARG(); base = 8; goto nosign; case 'p': @@ -334,11 +961,11 @@ * -- ANSI X3J11 */ /* NOSTRICT */ - _ulonglong = (unsigned long)va_arg(argp, void *); + _ulonglong = (unsigned long)argument[table_index].pvoid_arg; base = 16; goto nosign; case 's': - if (!(t = va_arg(argp, char *))) + if (!(t = argument[table_index].pchar_arg)) t = NULL_REP; if (prec >= 0) { @@ -366,14 +993,20 @@ flags |= LONGINT; /*FALLTHROUGH*/ case 'u': - ARG(unsigned); + if (flags & (INTMAXT | PTRDIFFT | SIZET)) + UNSIGNED_TYPEDEF_ARG(); + else + UNSIGNED_ARG(); base = 10; goto nosign; case 'X': - digs = "0123456789ABCDEF"; + digs = UPPER_DIGITS; /* FALLTHROUGH */ case 'x': - ARG(unsigned); + if (flags & (INTMAXT | PTRDIFFT | SIZET)) + UNSIGNED_TYPEDEF_ARG(); + else + UNSIGNED_ARG(); base = 16; /* leading 0x/X only if non-zero */ if (flags & ALT && _ulonglong != 0) @@ -398,36 +1031,54 @@ */ t = buf + BUF; - if (_ulonglong != 0 || prec != 0) + if (flags & (INTMAXT | PTRDIFFT | SIZET)) { - /* 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 ; */ - else - CONVERT(unsigned long) /* no ; */ -#undef CONVERT - if (flags & ALT && base == 8 && *t != '0') - *--t = '0'; /* octal leading 0 */ + if (_uintmax != 0 || prec != 0) + { + /* conversion is done separately since operations + with long long are much slower */ + if (flags & INTMAXT) + CONVERT(uintmax_t, _uintmax, base, t, *fmt == 'X'); + else if (flags & PTRDIFFT) + CONVERT(unsigned int, _uintmax, base, t, *fmt == 'X'); + else + CONVERT(size_t, _uintmax, base, t, *fmt == 'X'); + } } + else + { + if (_ulonglong != 0 || prec != 0) + { + /* conversion is done separately since operations + with long long are much slower */ + if (flags & LONGDBL) + CONVERT(unsigned long long, _ulonglong, base, t, *fmt == 'X'); + else + CONVERT(unsigned long, _ulonglong, base, t, *fmt == 'X'); + } + } + + if (flags & GROUPING && base == 10 && *t != '0' && thousands_sep && *localeconv()->grouping) + { + char *p, *eob = buf + BUF; + for (p = buf; t < eob; *p++ = *t++) + ; /* reverse integer into beginning of buffer */ + + GROUPING_FORMAT(buf, p, eob); + } + + if (flags & ALT && base == 8 && *t != '0') + *--t = '0'; /* octal leading 0 */ - digs = "0123456789abcdef"; + digs = LOWER_DIGITS; size = buf + BUF - t; pforw: /* * All reasonable formats wind up here. At this point, - * `t' points to a string which (if not flags&LADJUST) + * `t' points to a string which (if not flags & LADJUST) * should be padded out to `width' places. If - * flags&ZEROPAD, it should first be prefixed by any + * flags & ZEROPAD, it should first be prefixed by any * sign or other prefix; otherwise, it should be blank * padded before the prefix is emitted. After any * left-hand padding and prefixing, emit zeroes @@ -449,7 +1100,7 @@ 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 */ @@ -458,10 +1109,10 @@ if (flags & HEXPREFIX) { PUTC('0'); - PUTC((char)*fmt); + PUTC((*fmt == 'A') ? 'X' : (*fmt == 'a') ? 'x' : (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 */ @@ -482,6 +1133,8 @@ cnt += width > realsz ? width : realsz; break; case '\0': /* "%?" prints ?, unless ? is NULL */ + finished: + free(argument); return cnt; default: PUTC((char)*fmt); @@ -489,6 +1142,12 @@ } } /* NOTREACHED */ + +#undef SIGNED_ARG +#undef UNSIGNED_ARG +#undef SIGNED_TYPEDEF_ARG +#undef UNSIGNED_TYPEDEF_ARG +#undef GET_STAR_ARG_VALUE } static long double pten[] = @@ -530,6 +1189,117 @@ if ((expcnt = isspeciall(number, startp))) return(expcnt); + if (fmtch == 'a' || fmtch == 'A') + { + /* + * We are dealing with intel's extended double format. + * The 64-bit mantissa explicitely contains the leading integer digit. + * C99 standard defines hex float format as: 0xh.hhhp+ddd + * where h is a hex digit, d is a decimal digit and p represents base 2. + * The 64 bit of the mantissa can be subdivided into 16 nibbles, + * so exact hex representation of the binary coded mantissa is + * possible. To get a single hex digit in the integer part of + * the mantissa the period must be shifted by three places + * to the right. This makes it necessary to adjust the bias + * from 0x3fff to 0x4002. + */ + +#define IEEE754_LONG_DOUBLE_BIAS 0x3fff +#define LONG_DOUBLE_BIAS (IEEE754_LONG_DOUBLE_BIAS + 0x03) +#define LONG_DOUBLE_DENORMALIZED_BIAS (LONG_DOUBLE_BIAS - 0x01) + + + struct IEEExp *ip = (struct IEEExp *)&number; + char *fraction_part; + unsigned long long int mantissa = (unsigned long long int) ip->manh << 32 | ip->manl; + int mantissa_renormalized = FALSE, positive_exponent, exponent = ip->exp; + + p = endp; + t = startp; + + /* + * Mantissa. + */ + if (prec < (int)(sizeof(mantissa) * 8 / 4 - 1)) + { + /* If requested precision is less than the size of the mantissa's fraction */ + + unsigned int n = sizeof(mantissa) * 8 / 4 - 1 - prec - 1; /* One nibble more than precision. */ + mantissa >>= n * 4; /* The least significant nibble will determinated the rounding. */ + if ((mantissa & 0x0fULL) > 0x07ULL) + mantissa += 0x10ULL; /* Round up. */ + mantissa >>= 4; /* Discard least significant nibble. */ + + n = sizeof(mantissa) * 8 / 4 - 1 - n; + if ((mantissa >> (n * 4)) & 0x01ULL) /* Carry ? */ + { + mantissa >>= 4; + mantissa_renormalized = TRUE; + } + } + + CONVERT(unsigned long long int, mantissa, 16, p, fmtch == 'A'); + *t++ = *p++; + *t++ = decimal_point; + + fraction_part = t; + for (; p < endp; *t++ = *p++) + ; + for (; t - fraction_part < prec && t < endp; *t++ = '0') + ; /* pad with zeros to the right. */ + if (!(flags & ALT)) + t--; /* Do not output decimal point. */ + + /* + * Exponent. + */ + if (exponent >= LONG_DOUBLE_BIAS) + { + positive_exponent = TRUE; /* Normalized number greater one. */ + exponent -= LONG_DOUBLE_BIAS; + } + else if (exponent == 0) + { + if (mantissa) + { + positive_exponent = FALSE; /* Denormalized number. */ + exponent = LONG_DOUBLE_DENORMALIZED_BIAS; + } + else + positive_exponent = TRUE; /* Zero. */ + } + else + { + positive_exponent = FALSE; /* Normalized number smaller one. */ + exponent = LONG_DOUBLE_BIAS - exponent; + } + if (mantissa_renormalized) + { + /* + * A nibble has been shifted into fraction to renormalize mantissa, + * so increment binary exponent by 4. + */ + if (positive_exponent) + exponent += 4; + else + { + exponent = 4 - exponent; + positive_exponent = (exponent >= 0) ? TRUE : FALSE; + } + } + CONVERT(int, exponent, 10, p, fmtch == 'A'); + *--p = (positive_exponent) ? '+' : '-'; + *--p = (fmtch == 'A') ? 'P' : 'p'; + for (; p < endp; *t++ = *p++) + ; + + return t - startp; + +#undef IEEE754_LONG_DOUBLE_BIAS +#undef LONG_DOUBLE_BIAS +#undef LONG_DOUBLE_DENORMALIZED_BIAS + } + dotrim = expcnt = gformat = 0; /* fract = modfl(number, &integer); */ integer = number; @@ -595,17 +1365,23 @@ switch(fmtch) { case 'f': + case 'F': /* reverse integer into beginning of buffer */ if (expcnt) for (; ++p < endp; *t++ = *p); else *t++ = '0'; /* + * if thousands' grouping is requested. + */ + if (flags & GROUPING && expcnt && thousands_sep && *localeconv()->grouping) + GROUPING_FORMAT(startp, t, endp); + /* * 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) { @@ -626,8 +1402,8 @@ 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; @@ -683,14 +1459,14 @@ 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) @@ -708,10 +1484,10 @@ 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; } @@ -757,13 +1533,18 @@ else *t++ = '0'; /* + * if thousands' grouping is requested. + */ + if (flags & GROUPING && expcnt && thousands_sep && *localeconv()->grouping) + GROUPING_FORMAT(startp, t, endp); + /* * 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; @@ -784,17 +1565,19 @@ startp = 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; } } return t - startp; } +#undef GROUPING_FORMAT +#undef CONVERT static char * roundl(long double fract, int *expv, char *start, char *end, char ch, @@ -824,7 +1607,7 @@ if (tmp > 4) for (;; --end) { - if (*end == decimal) + if (*end == decimal_point) --end; if (++*end <= '9') break; @@ -848,7 +1631,7 @@ else if (*signp == '-') for (;; --end) { - if (*end == decimal) + if (*end == decimal_point) --end; if (*end != '0') break; @@ -892,12 +1675,7 @@ static int isspeciall(long double d, char *bufp) { - struct IEEExp { - unsigned manl:32; - unsigned manh:32; - unsigned exp:15; - unsigned sign:1; - } *ip = (struct IEEExp *)&d; + struct IEEExp *ip = (struct IEEExp *)&d; nan_p = 0; /* don't assume the static is 0 (emacs) */ @@ -914,11 +1692,11 @@ return(0); if ((ip->manh & 0x7fffffff) || ip->manl) { - strcpy(bufp, "NaN"); + strcpy(bufp, NAN_REP[style]); nan_p = ip->sign ? -1 : 1; /* kludge: we don't need the sign, it's not nice, but it should work */ } else - (void)strcpy(bufp, "Inf"); + (void)strcpy(bufp, INF_REP[style]); return(3); } Index: djgpp/src/libc/ansi/stdio/printf.txh =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdio/printf.txh,v retrieving revision 1.8 diff -u -r1.8 printf.txh --- djgpp/src/libc/ansi/stdio/printf.txh 29 Jan 2003 12:28:44 -0000 1.8 +++ djgpp/src/libc/ansi/stdio/printf.txh 9 Apr 2003 06:42:50 -0000 @@ -13,8 +13,24 @@ Sends formatted output from the arguments (@dots{}) to @code{stdout}. The format string contains regular characters to print, as well as -conversion specifiers, which begin with a percent symbol. Each -conversion speficier contains the following fields: +conversion specifiers, which begin with a percent symbol. Conversions +can be applied to the nth argument after the format string in the argument +list, rather than to the next unused argument. In this case, the conversion +specifier character @code{%} is replaced by the sequence @code{"%n$"}, where +@code{n} is a decimal integer in the range [@code{1}, @code{NL_ARGMAX}], +giving the position of the argument in the argument list. @code{NL_ARGMAX} +is the number of the last argument in the argument list. The format string +can contain either numbered argument conversion specifiers (that is, +@code{"%n$"} and @code{"*m$"}), or unnumbered argument conversion specifiers +(that is @code{%} and @code{*}), but not both. The only exception to this +is that @code{%%} can be mixed with @code{"%n$"} form. When numbered argument +specifications are used, specifying the Nth argument requires that all the +leading arguments, from the first to the (N-1)th, are specified in the format +string. In format strings containing the @code{"%n$"} form of conversion +specification, numbered arguments in the argument list can be referenced +from the format string as many times as required. + +Each conversion specifier contains the following fields: @itemize @bullet @@ -24,6 +40,13 @@ @table @code +@item ' + +The integer part of the result of a decimal conversion (@code{%i}, +@code{%d}, @code{%u}, @code{%f}, @code{%F}, @code{%g} or @code{%G}) +will be formatted with thousands' grouping characters. +The non-monetary grouping character is used. + @item - left-justify the field. @@ -54,6 +77,11 @@ This may also be an asterisk (@code{*}), which means that the actual width will be obtained from the next argument. If the argument is negative, it supplies a @code{-} flag and a positive width. +In format strings containing the @code{"%n$"} form of a conversion +specification, a field width can be indicated by the sequence @code{"*m$"}, +where m is a decimal integer in the range [@code{1}, @code{NL_ARGMAX}], +giving the position in the argument list (after the format argument) of +an integer argument containing the field width. @item @@ -63,6 +91,11 @@ integer, the number of fraction digits for a floating point number (max for @code{g} or @code{G}, actual for others), or the maximum number of characters for a string. +In format strings containing the @code{"%n$"} form of a conversion +specification, a precision can be indicated by the sequence @code{"*m$"}, +where m is a decimal integer in the range [@code{1}, @code{NL_ARGMAX}], +giving the position in the argument list (after the format argument) of +an integer argument containing the precision. @item @@ -114,6 +147,17 @@ A signed long integer. This is non-standard and obsolete. Please use @code{ld} instead. +@item a +@itemx A + +A floating point number (float or double) printed in the style +@code{"[-]0xh.hhhhp[+|-]d"}, where @code{h} represents a hexadecimal +digit, @code{d} represents a decimal digit and @code{p} stands for the +base 2. For long double, use @code{"La"} or @code{"LA"}. The case of +the exponent, the case of the letters @code{"abcdef"} and the case of +@code{"x"} matches the specifier case. The representation always has +an exponent. + @item e @itemx E @@ -122,6 +166,7 @@ case. The representation always has an exponent. @item f +@itemx F A floating point number (float or double). For long double, use @code{"Lf"}. The representation never has an exponent. @@ -201,4 +246,38 @@ @example printf("%-3d %10.2f%% Percent of %s\n", index, per[index], name[index]); +@end example + +The following statement can be used to print date and time +using language-independent format: + +@example +printf(format, weekday, month, day, hour, precision, min); +@end example + + +For American usage, the format string could look like: + +@example +"%s, %s %d, %d:%.*d\n" +@end example + +The integer precision has the value 2. +The above example will produce the following message: + +@example +Sunday, October 27, 9:09 +@end example + + +For German usage, the format string could look like: + +@example +"%1$s, %3$d. %2$s, %4$d:%6$.*5$d\n" +@end example + +The above example will produce the following message: + +@example +Sonntag, 27. Oktober, 9:09 @end example