Mail Archives: djgpp-workers/2003/04/09/02:50:13
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 <libc/stubs.h>
#include <sys/types.h>
#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
#include <stdio.h>
#include <ctype.h>
#include <locale.h>
@@ -18,7 +20,20 @@
#include <libc/file.h>
#include <libc/local.h>
-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
- Raw text -