delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2003/04/09/02:50:13

Message-ID: <3E93C1ED.32C6571D@hrz1.hrz.tu-darmstadt.de>
Date: Wed, 09 Apr 2003 08:47:09 +0200
From: Juan Manuel Guerrero <st001906 AT HRZ1 DOT HRZ DOT TU-Darmstadt DOT De>
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
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 <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 -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019