delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2003/04/02/15:49:28

Message-ID: <3E8B4CC9.E21C27CF@hrz1.hrz.tu-darmstadt.de>
Date: Wed, 02 Apr 2003 22:49:13 +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: %n$, *m$ and some other c99 support for doprnt.c
X-MailScanner: Found to be clean
Reply-To: djgpp-workers AT delorie DOT com

This is a patch to implement %n$, *m$ and some other c99 support for _doprnt.c.

1) New flag added:
     ': (thousands' grouping flag) The code checks if locale support is available
        or not. If not available this flag has no influence at all on output.

2) New length modifiers added:
     hh: conversion specifiers: d, i, o, ,u, x, or X applies to a signed/unsigned char.
     ll: conversion specifiers: d, i, o, ,u, x, or X applies to a signed/unsigned long long int.
     j:  conversion specifiers: d, i, o, ,u, x, or X applies to a intmax_t or uintmax_t.
     t:  conversion specifiers: d, i, o, ,u, x, or X applies to a ptrdiff_t.
     z:  conversion specifiers: d, i, o, ,u, x, or X applies to a size_t.

3) New conversion specifier added:
     a, A: A double argument is converted in the style: [-]0xh.hhhhp[+-]d, where h is a
           hexadecimal digit, d is a decimal digit and p stands for the base 2.

For all conversion specifiers, if the argument represents infinity or NaN then
for [aefg] the strings "inf" or "nan" will be outputed and for [AEFG] the strings
"INF" or "NAN" will be printed.

To implement %n$ and *m$ support the functions __args_amount and __load_arg_array
have been added. To implement this funtionality the format string is parsed three
times. First the amount of distinct arguments to be retired from stack is determinated
by parsing the string for the first time. After knowing the amount, an array of unions
is allocated and the arguments are retired from stack and stored in the array.
When the format string is parsed for the last time to print the arguments, instead of
accesing the stack, the array is accesed. No matter if numbered conversions specifiers
(this is %n$ and *m$) are used or if unnumbered conversion specifiers (this is % and *)
are used, it is always the array of unions that is accesed to retrive the argument to be
printed.

I have written the patch 6 or 7 months ago and have recompiled almost every /v2gnu appliction
with this new code. I have been using them since then and I have never experienced any crash
due to the use of this code.
Anyway and as usual, suggestions, objections and comments are welcome.

The patch will modify the files: doprnt.c and printf.txh.


Regards,
Guerrero, Juan Manuel



diff -acprNC3 djgpp.orig/src/libc/ansi/stdio/doprnt.c djgpp/src/libc/ansi/stdio/doprnt.c
*** djgpp.orig/src/libc/ansi/stdio/doprnt.c	Fri Oct 18 01:00:24 2002
--- djgpp/src/libc/ansi/stdio/doprnt.c	Sun Oct 27 20:34:02 2002
***************
*** 8,13 ****
--- 8,15 ----
  #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>
***************
*** 19,24 ****
--- 21,32 ----
  #include <libc/local.h>
  
  static char decimal = '.';
+ 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
*************** static char decimal = '.';
*** 33,43 ****
  
  #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) : \
! 		(basetype)va_arg(argp, int)
  
  static int nan_p = 0;
  
--- 41,88 ----
  
  #define	PUTC(ch)	(void) putc(ch, fp)
  
! 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;
  
*************** static __inline__ char tochar(int n)
*** 59,68 ****
  #define	LONGINT		0x01		/* long integer */
  #define	LONGDBL		0x02		/* long double */
  #define	SHORTINT	0x04		/* short integer */
! #define	ALT		0x08		/* alternate form */
! #define	LADJUST		0x10		/* left adjustment */
! #define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
! #define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
  
  static int cvtl(long double number, int prec, int flags, char *signp,
  	        unsigned char fmtch, char *startp, char *endp);
--- 104,118 ----
  #define	LONGINT		0x01		/* long integer */
  #define	LONGDBL		0x02		/* long double */
  #define	SHORTINT	0x04		/* short integer */
! #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);
*************** static int isspeciall(long double d, cha
*** 75,80 ****
--- 125,535 ----
  
  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 format; /* selects case of INF/NAN representation to be used*/
+ 
+ static __inline__ int
+ __args_amount(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:%nn$*n$.*m$. 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
+ }
  
  int
  _doprnt(const char *fmt0, va_list argp, FILE *fp)
*************** _doprnt(const char *fmt0, va_list argp, 
*** 87,92 ****
--- 542,548 ----
    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 */
*************** _doprnt(const char *fmt0, va_list argp, 
*** 99,108 ****
    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] */
    int neg_ldouble = 0;		/* non-zero if _ldouble is negative */
  
    decimal = localeconv()->decimal_point[0];
  
    if (fp->_flag & _IORW)
    {
--- 555,618 ----
    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], %[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  ? (unsigned long long)argument[table_index].longlong_arg :             \
+   flags & LONGINT  ? (unsigned long long)argument[table_index].long_arg :                 \
+   flags & SHORTINT ? (unsigned long long)(short)argument[table_index].int_arg :           \
+   flags & CHARINT  ? (unsigned long long)(char)argument[table_index].int_arg :            \
+   (unsigned 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  ? (uintmax_t)argument[table_index].intmax_t_arg :                      \
+   flags & PTRDIFFT ? (uintmax_t)argument[table_index].ptrdiff_t_arg :                     \
+   flags & SIZET    ? (uintmax_t)argument[table_index].size_t_arg :                        \
+   (uintmax_t)argument[table_index].longlong_arg
+ 
+ #define UNSIGNED_TYPEDEF_ARG()  _uintmax =                                                \
+   flags & INTMAXT  ? argument[table_index].uintmax_t_arg :                                \
+   flags & PTRDIFFT ? (uintmax_t)argument[table_index].ptrdiff_t_arg :                     \
+   flags & SIZET    ? (uintmax_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];
+   thousands_sep = localeconv()->thousands_sep[0];
  
    if (fp->_flag & _IORW)
    {
*************** _doprnt(const char *fmt0, va_list argp, 
*** 112,121 ****
    if ((fp->_flag & _IOWRT) == 0)
      return (EOF);
  
    fmt = fmt0;
    digs = "0123456789abcdef";
!   for (cnt = 0;; ++fmt)
    {
      while ((ch = *fmt) && ch != '%')
      {
        PUTC (ch);
--- 622,657 ----
    if ((fp->_flag & _IOWRT) == 0)
      return (EOF);
  
+ 
+   /* Determinate the amount of arguments (objects) on the stack. */
+   fmt = fmt0;
+   number_of_args = __args_amount(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 = "0123456789abcdef";
!   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.
+     */
+ 
+     format = 0; /* nan/inf will be outputed */
+ 
      while ((ch = *fmt) && ch != '%')
      {
        PUTC (ch);
*************** _doprnt(const char *fmt0, va_list argp, 
*** 123,135 ****
        cnt++;
      }
      if (!ch)
!       return cnt;
      flags = 0; dprec = 0; fpprec = 0; width = 0;
      prec = -1;
      sign = '\0';
    rflag:
      switch (*++fmt)
      {
      case ' ':
        /*
         * ``If the space and + flags both appear, the space
--- 659,674 ----
        cnt++;
      }
      if (!ch)
!       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
*************** _doprnt(const char *fmt0, va_list argp, 
*** 137,143 ****
         *	-- ANSI X3J11
         */
        if (!sign)
! 	sign = ' ';
        goto rflag;
      case '#':
        flags |= ALT;
--- 676,682 ----
         *	-- ANSI X3J11
         */
        if (!sign)
!         sign = ' ';
        goto rflag;
      case '#':
        flags |= ALT;
*************** _doprnt(const char *fmt0, va_list argp, 
*** 149,156 ****
         *	-- ANSI X3J11
         * They don't exclude field widths read from args.
         */
!       if ((width = va_arg(argp, int)) >= 0)
! 	goto rflag;
        width = -width;
        /* FALLTHROUGH */
      case '-':
--- 688,696 ----
         *	-- ANSI X3J11
         * They don't exclude field widths read from args.
         */
!       GET_STAR_ARG_VALUE(width);
!       if (width >= 0)
!         goto rflag;
        width = -width;
        /* FALLTHROUGH */
      case '-':
*************** _doprnt(const char *fmt0, va_list argp, 
*** 161,173 ****
        goto rflag;
      case '.':
        if (*++fmt == '*')
! 	n = va_arg(argp, int);
        else
        {
! 	n = 0;
! 	while (isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt))
! 	  n = 10 * n + todigit(*fmt++);
! 	--fmt;
        }
        prec = n < 0 ? -1 : n;
        goto rflag;
--- 701,713 ----
        goto rflag;
      case '.':
        if (*++fmt == '*')
!         GET_STAR_ARG_VALUE(n);
        else
        {
!         n = 0;
!         while (isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt))
!           n = 10 * n + todigit(*fmt++);
!         --fmt;
        }
        prec = n < 0 ? -1 : n;
        goto rflag;
*************** _doprnt(const char *fmt0, va_list argp, 
*** 183,207 ****
      case '5': case '6': case '7': case '8': case '9':
        n = 0;
        do {
! 	n = 10 * n + todigit(*fmt);
        } while (isascii((unsigned char)*++fmt) && isdigit((unsigned char)*fmt));
!       width = n;
!       --fmt;
        goto rflag;
      case 'L':
        flags |= LONGDBL;
        goto rflag;
      case 'h':
!       flags |= SHORTINT;
        goto rflag;
      case 'l':
!       if (flags&LONGINT)
! 	flags |= LONGDBL; /* for 'll' - long long */
        else
! 	flags |= LONGINT;
        goto rflag;
      case 'c':
!       *(t = buf) = va_arg(argp, int);
        size = 1;
        sign = '\0';
        goto pforw;
--- 723,767 ----
      case '5': case '6': case '7': case '8': case '9':
        n = 0;
        do {
!         n = 10 * n + todigit(*fmt);
        } while (isascii((unsigned char)*++fmt) && isdigit((unsigned char)*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)
!       {
!         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;
      case 'c':
!       *(t = buf) = argument[table_index].int_arg;
        size = 1;
        sign = '\0';
        goto pforw;
*************** _doprnt(const char *fmt0, va_list argp, 
*** 210,245 ****
        /*FALLTHROUGH*/
      case 'd':
      case 'i':
!       ARG(int);
!       if ((long long)_ulonglong < 0)
        {
!         _ulonglong = -_ulonglong;
! 	sign = '-';
        }
        base = 10;
        goto number;
!     case 'e':
      case 'E':
      case 'f':
      case 'g':
!     case 'G':
        if (flags & LONGDBL)
! 	_ldouble = va_arg(argp, long double);
        else
! 	_ldouble = (long double)va_arg(argp, double);
        /*
         * 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)))
  	  fpprec = prec - MAXFRACT;
  	prec = MAXFRACT;
        }
        else if (prec == -1)
        {
! 	if (flags&LONGINT)
  	  prec = DEFLPREC;
  	else
  	  prec = DEFPREC;
--- 770,822 ----
        /*FALLTHROUGH*/
      case 'd':
      case 'i':
!       if (flags & (INTMAXT | PTRDIFFT | SIZET))
        {
!         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 'A':
      case 'E':
+     case 'F':
+     case 'G':
+       format = 1; /* NAN/INF will be outputed */
+     case 'a':
+     case 'e':
      case 'f':
      case 'g':
!       if (*fmt == 'A' || *fmt == 'a') flags |= HEXPREFIX;
        if (flags & LONGDBL)
!         _ldouble = argument[table_index].longdouble_arg;
        else
!         _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)))
  	  fpprec = prec - MAXFRACT;
  	prec = MAXFRACT;
        }
        else if (prec == -1)
        {
! 	if (flags & LONGINT)
  	  prec = DEFLPREC;
  	else
  	  prec = DEFPREC;
*************** _doprnt(const char *fmt0, va_list argp, 
*** 271,277 ****
        }
        /*
         * cvt may have to round up past the "start" of the
!        * buffer, i.e. ``intf("%.2f", (double)9.999);'';
         * if the first char isn't NULL, it did.
         */
        *buf = NULL;
--- 848,854 ----
        }
        /*
         * cvt may have to round up past the "start" of the
!        * buffer, i.e. ``printf("%.2f", (double)9.999);'';
         * if the first char isn't NULL, it did.
         */
        *buf = NULL;
*************** _doprnt(const char *fmt0, va_list argp, 
*** 290,308 ****
        goto pforw;
      case 'n':
        if (flags & LONGDBL)
!         *va_arg(argp, long long *) = cnt;
        else if (flags & LONGINT)
! 	*va_arg(argp, long *) = cnt;
        else if (flags & SHORTINT)
! 	*va_arg(argp, short *) = cnt;
        else
! 	*va_arg(argp, int *) = cnt;
        break;
      case 'O':
        flags |= LONGINT;
        /*FALLTHROUGH*/
      case 'o':
!       ARG(unsigned);
        base = 8;
        goto nosign;
      case 'p':
--- 867,893 ----
        goto pforw;
      case 'n':
        if (flags & LONGDBL)
!         *argument[table_index].plonglong_arg = cnt;
        else if (flags & LONGINT)
!         *argument[table_index].plong_arg = cnt;
        else if (flags & SHORTINT)
!         *argument[table_index].pshort_arg = cnt;
!       else if (flags & CHARINT)
!         *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
!         *argument[table_index].pint_arg = cnt;
        break;
      case 'O':
        flags |= LONGINT;
        /*FALLTHROUGH*/
      case 'o':
!       UNSIGNED_ARG();
        base = 8;
        goto nosign;
      case 'p':
*************** _doprnt(const char *fmt0, va_list argp, 
*** 314,324 ****
         *	-- ANSI X3J11
         */
        /* NOSTRICT */
!       _ulonglong = (unsigned long)va_arg(argp, void *);
        base = 16;
        goto nosign;
      case 's':
!       if (!(t = va_arg(argp, char *)))
  	t = NULL_REP;
        if (prec >= 0)
        {
--- 899,909 ----
         *	-- ANSI X3J11
         */
        /* NOSTRICT */
!       _ulonglong = (unsigned long)argument[table_index].pvoid_arg;
        base = 16;
        goto nosign;
      case 's':
!       if (!(t = argument[table_index].pchar_arg))
  	t = NULL_REP;
        if (prec >= 0)
        {
*************** _doprnt(const char *fmt0, va_list argp, 
*** 346,359 ****
        flags |= LONGINT;
        /*FALLTHROUGH*/
      case 'u':
!       ARG(unsigned);
        base = 10;
        goto nosign;
      case 'X':
        digs = "0123456789ABCDEF";
        /* FALLTHROUGH */
      case 'x':
!       ARG(unsigned);
        base = 16;
        /* leading 0x/X only if non-zero */
        if (flags & ALT && _ulonglong != 0)
--- 931,944 ----
        flags |= LONGINT;
        /*FALLTHROUGH*/
      case 'u':
!       UNSIGNED_ARG();
        base = 10;
        goto nosign;
      case 'X':
        digs = "0123456789ABCDEF";
        /* FALLTHROUGH */
      case 'x':
!       UNSIGNED_ARG();
        base = 16;
        /* leading 0x/X only if non-zero */
        if (flags & ALT && _ulonglong != 0)
*************** _doprnt(const char *fmt0, va_list argp, 
*** 378,413 ****
         */
        t = buf + BUF;
  
!       if (_ulonglong != 0 || prec != 0)
        {
!         /* conversion is done separately since operations
! 	  with long long are much slower */
! #define CONVERT(type) \
! 	{ \
! 	  register type _n = (type)_ulonglong; \
! 	  do { \
! 	    *--t = digs[_n % base]; \
! 	    _n /= base; \
! 	  } while (_n); \
! 	}
! 	if (flags&LONGDBL)
! 	  CONVERT(unsigned long long) /* no ; */
! 	else
! 	  CONVERT(unsigned long) /* no ; */
  #undef CONVERT
!         if (flags & ALT && base == 8 && *t != '0')
!           *--t = '0';		/* octal leading 0 */
        }
  
        digs = "0123456789abcdef";
        size = buf + BUF - t;
  
      pforw:
        /*
         * All reasonable formats wind up here.  At this point,
!        * `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
         * sign or other prefix; otherwise, it should be blank
         * padded before the prefix is emitted.  After any
         * left-hand padding and prefixing, emit zeroes
--- 963,1042 ----
         */
        t = buf + BUF;
  
! #define CONVERT(type, argument) \
!       do { \
!         register type _n = (type)(argument); \
!         do { \
!           *--t = digs[_n % base]; \
!           _n /= base; \
!         } while (_n); \
!       } while (0)
! 
!       if (flags & (INTMAXT | PTRDIFFT | SIZET))
        {
!         if (_uintmax != 0 || prec != 0)
!         {
!           /* conversion is done separately since operations
!              with long long are much slower */
!           if (flags & INTMAXT)
!             CONVERT(uintmax_t, _uintmax);
!           else if (flags & PTRDIFFT)
!             CONVERT(ptrdiff_t, _uintmax);
!           else
!             CONVERT(size_t, _uintmax);
!         }
!       }
!       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);
!           else
!             CONVERT(unsigned long, _ulonglong);
!         }
!       }
  #undef CONVERT
! 
!       if (flags & GROUPING && base == 10 && *t != '0' && thousands_sep)
!       {
!         char *p, *pos, *src, *dst, *eob, *eos;
!         long int grouping_size;
! 
!         eob = buf + BUF;
!         for (p = buf; t < eob; *p++ = *t++);
! 
!         eos = p;
!         p = localeconv()->grouping;
!         grouping_size = strtol(p,&p,10);
!         for (src = eos, dst = pos = eob; src > buf; *--dst = *--src)
!           if (pos - dst == grouping_size)
!           {
!             *--dst = thousands_sep;
!             pos = dst;
!             if (*p)
!             {
!               p++;  /* Get next size of group of digits to be formatted. */
!               grouping_size = strtol(p, &p, 10);
!             }
!           }
!         t = (*dst == thousands_sep) ? dst + 1 : dst;  /* Remove leading thousands separator character. */
        }
  
+       if (flags & ALT && base == 8 && *t != '0')
+         *--t = '0';		/* octal leading 0 */
+ 
        digs = "0123456789abcdef";
        size = buf + BUF - t;
  
      pforw:
        /*
         * All reasonable formats wind up here.  At this point,
!        * `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
         * sign or other prefix; otherwise, it should be blank
         * padded before the prefix is emitted.  After any
         * left-hand padding and prefixing, emit zeroes
*************** _doprnt(const char *fmt0, va_list argp, 
*** 429,435 ****
  	realsz += 2;
  
        /* right-adjusting blank padding */
!       if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
  	for (n = realsz; n < width; n++)
  	  PUTC(' ');
        /* prefix */
--- 1058,1064 ----
  	realsz += 2;
  
        /* right-adjusting blank padding */
!       if ((flags & (LADJUST | ZEROPAD)) == 0 && width)
  	for (n = realsz; n < width; n++)
  	  PUTC(' ');
        /* prefix */
*************** _doprnt(const char *fmt0, va_list argp, 
*** 438,447 ****
        if (flags & HEXPREFIX)
        {
  	PUTC('0');
! 	PUTC((char)*fmt);
        }
        /* right-adjusting zero padding */
!       if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
  	for (n = realsz; n < width; n++)
  	  PUTC('0');
        /* leading zeroes from decimal precision */
--- 1067,1076 ----
        if (flags & HEXPREFIX)
        {
  	PUTC('0');
! 	PUTC((*fmt == 'A') ? 'X' : (*fmt == 'a') ? 'x' : (char)*fmt);
        }
        /* right-adjusting zero padding */
!       if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD)
  	for (n = realsz; n < width; n++)
  	  PUTC('0');
        /* leading zeroes from decimal precision */
*************** _doprnt(const char *fmt0, va_list argp, 
*** 462,474 ****
        cnt += width > realsz ? width : realsz;
        break;
      case '\0':			/* "%?" prints ?, unless ? is NULL */
!       return cnt;
      default:
        PUTC((char)*fmt);
        cnt++;
      }
    }
    /* NOTREACHED */
  }
  
  static long double pten[] =
--- 1091,1107 ----
        cnt += width > realsz ? width : realsz;
        break;
      case '\0':			/* "%?" prints ?, unless ? is NULL */
!       goto finished;
      default:
        PUTC((char)*fmt);
        cnt++;
      }
    }
    /* NOTREACHED */
+ 
+ finished:
+   free(argument);
+   return cnt;
  }
  
  static long double pten[] =
*************** 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;       \
+                                                             \
+       do {                                                  \
+         *--(string_end) = digit[(value) % (base)];          \
+       } while ((value) /= (base));                          \
+     } while (0)
+ 
+ 
+     struct IEEExp {
+       unsigned manl:32;
+       unsigned manh:32;
+       unsigned exp:15;
+       unsigned sign:1;
+     } *ip = (struct IEEExp *)&number;
+     char *integer_part, *fraction_part;
+     unsigned long long int temp, mantissa = (unsigned long long int) ip->manh << 32 | ip->manl;
+     int rounding = FALSE, positive_exponent, exponent = ip->exp;
+ 
+     p = endp;
+     t = startp;
+ 
+     /*
+      *  Mantissa.
+      */
+     temp = mantissa;
+     ITOA(mantissa, 16, p, fmtch == 'A');
+     mantissa = temp;
+     integer_part = t;
+     *t++ = *p++;
+     *t++ = decimal;
+     if (prec)
+     {
+       fraction_part = t;
+       for (; p < endp; *t++ = *p++)
+         ;
+       for (; t - fraction_part < prec && t < endp; *t++ = '0')
+         ;  /* pad with zeros to the right. */
+      }
+      else
+      {
+        fraction_part = p;
+        p = endp;
+        if (*fraction_part > '7')
+        {
+          if (*integer_part < 'F' || *integer_part < 'f')
+            *integer_part += (char)1;
+          else
+          {
+            *integer_part = '1';
+            rounding = TRUE;
+          }
+        }
+        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 (rounding)
+     {
+       /* Normalizing the mantissa. */
+       if (positive_exponent)
+         exponent += 4;  /* Right shifting a nibble in mantissa.  */
+       else
+       {
+         exponent = 4 - exponent;  /* Right shifting a nibble in mantissa.  */
+         positive_exponent = (exponent >= 0) ? TRUE : FALSE;
+       }
+     }
+     ITOA(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
+ #undef ITOA
+   }
+ 
    dotrim = expcnt = gformat = 0;
    /* fract = modfl(number, &integer); */
    integer = number;
*************** cvtl(long double number, int prec, int f
*** 572,590 ****
      tmp = modfl(integer * 0.1L , &integer);
      *p-- = tochar((int)((tmp + .01L) * 10));
    }
    switch(fmtch)
    {
    case 'f':
      /* reverse integer into beginning of buffer */
      if (expcnt)
        for (; ++p < endp; *t++ = *p);
      else
        *t++ = '0';
      /*
       * if precision required or alternate flag set, add in a
       * decimal point.
       */
!     if (prec || flags&ALT)
        *t++ = decimal;
      /* if requires more precision and some fraction left */
      if (fract)
--- 1341,1387 ----
      tmp = modfl(integer * 0.1L , &integer);
      *p-- = tochar((int)((tmp + .01L) * 10));
    }
+ 
    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)
+     {
+       char *pos, *src, *dst;
+       long int grouping_size;
+ 
+       p = localeconv()->grouping;
+       grouping_size = strtol(p,&p,10);
+       for (src = t, dst = pos = endp; src > startp; *--dst = *--src)
+         if (pos - dst == grouping_size)
+         {
+           *--dst = thousands_sep;
+           pos = dst;
+           if (*p)
+           {
+             p++;  /* Get next size of group of digits to be formatted. */
+             grouping_size = strtol(p, &p, 10);
+           }
+         }
+       src = (*dst == thousands_sep) ? dst + 1 : dst;  /* Remove leading thousands separator character. */
+       for (dst = startp; src < endp; *dst++ = *src++)
+         ;
+       t = dst;
+     }
+     /*
       * if precision required or alternate flag set, add in a
       * decimal point.
       */
!     if (prec || flags & ALT)
        *t++ = decimal;
      /* if requires more precision and some fraction left */
      if (fract)
*************** cvtl(long double number, int prec, int f
*** 606,612 ****
      if (expcnt)
      {
        *t++ = *++p;
!       if (prec || flags&ALT)
  	*t++ = decimal;
        /* if requires more precision and some integer left */
        for (; prec && ++p < endp; --prec)
--- 1403,1409 ----
      if (expcnt)
      {
        *t++ = *++p;
!       if (prec || flags & ALT)
  	*t++ = decimal;
        /* if requires more precision and some integer left */
        for (; prec && ++p < endp; --prec)
*************** cvtl(long double number, int prec, int f
*** 663,675 ****
  	  break;
        }
        *t++ = tochar((int)tmp);
!       if (prec || flags&ALT)
  	*t++ = decimal;
      }
      else
      {
        *t++ = '0';
!       if (prec || flags&ALT)
  	*t++ = decimal;
      }
      /* if requires more precision and some fraction left */
--- 1460,1472 ----
  	  break;
        }
        *t++ = tochar((int)tmp);
!       if (prec || flags & ALT)
  	*t++ = decimal;
      }
      else
      {
        *t++ = '0';
!       if (prec || flags & ALT)
  	*t++ = decimal;
      }
      /* if requires more precision and some fraction left */
*************** cvtl(long double number, int prec, int f
*** 688,694 ****
      for (; prec--; *t++ = '0');
  
      /* unless alternate flag, trim any g/G format trailing 0's */
!     if (gformat && !(flags&ALT))
      {
        while (t > startp && *--t == '0');
        if (*t == decimal)
--- 1485,1491 ----
      for (; prec--; *t++ = '0');
  
      /* unless alternate flag, trim any g/G format trailing 0's */
!     if (gformat && !(flags & ALT))
      {
        while (t > startp && *--t == '0');
        if (*t == decimal)
*************** cvtl(long double number, int prec, int f
*** 737,746 ****
      else
        *t++ = '0';
      /*
       * if precision required or alternate flag set, add in a
       * decimal point.  If no digits yet, add in leading 0.
       */
!     if (prec || flags&ALT)
      {
        dotrim = 1;
        *t++ = decimal;
--- 1534,1569 ----
      else
        *t++ = '0';
      /*
+      * if thousands' grouping is requested.
+      */
+     if (flags & GROUPING && expcnt && thousands_sep)
+     {
+       char *pos, *src, *dst;
+       long int grouping_size;
+ 
+       p = localeconv()->grouping;
+       grouping_size = strtol(p,&p,10);
+       for (src = t, dst = pos = endp; src > startp; *--dst = *--src)
+         if (pos - dst == grouping_size)
+         {
+           *--dst = thousands_sep;
+           pos = dst;
+           if (*p)
+           {
+             p++;  /* Get next size of group of digits to be formatted. */
+             grouping_size = strtol(p, &p, 10);
+           }
+         }
+       src = (*dst == thousands_sep) ? dst + 1 : dst;  /* Remove leading thousands separator character. */
+       for (dst = startp; src < endp; *dst++ = *src++)
+         ;
+       t = dst;
+     }
+     /*
       * if precision required or alternate flag set, add in a
       * decimal point.  If no digits yet, add in leading 0.
       */
!     if (prec || flags & ALT)
      {
        dotrim = 1;
        *t++ = decimal;
*************** cvtl(long double number, int prec, int f
*** 764,770 ****
        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)
        for (; prec--; *t++ = '0');
      else if (dotrim)
      {
--- 1587,1593 ----
        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)
        for (; prec--; *t++ = '0');
      else if (dotrim)
      {
*************** isspeciall(long double d, char *bufp)
*** 894,904 ****
      return(0);
    if ((ip->manh & 0x7fffffff) || ip->manl)
    {
!     strcpy(bufp, "NaN");
      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");
    return(3);
  }
--- 1717,1727 ----
      return(0);
    if ((ip->manh & 0x7fffffff) || ip->manl)
    {
!     strcpy(bufp, NAN_REP[format]);
      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_REP[format]);
    return(3);
  }
diff -acprNC3 djgpp.orig/src/libc/ansi/stdio/printf.txh djgpp/src/libc/ansi/stdio/printf.txh
*** djgpp.orig/src/libc/ansi/stdio/printf.txh	Thu Oct 17 12:17:18 2002
--- djgpp/src/libc/ansi/stdio/printf.txh	Sun Oct 27 20:42:40 2002
*************** int printf(const char *format, @dots{});
*** 12,19 ****
  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:
  
  @itemize @bullet
  
--- 12,35 ----
  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.  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 containig 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 speficier contains the following fields:
  
  @itemize @bullet
  
*************** an optional flag, which may alter the co
*** 23,31 ****
  
  @table @code
  
  @item -
  
! left-justify the field.
  
  @item +
  
--- 39,54 ----
  
  @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.
  
  @item +
  
*************** To pad numbers with leading zeros.
*** 52,58 ****
  A field width specifier, which specifies the minimum width of the field. 
  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. 
  
  @item
  
--- 75,86 ----
  A field width specifier, which specifies the minimum width of the field. 
  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
  
*************** The precision specifies the minimum numb
*** 62,73 ****
  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. 
  
  @item
  
! An optional conversion qualifier, which may be @code{h} to specify
! @code{short}, @code{l} to specify long ints, or @code{L} to specify
! long doubles. Long long type can be specified by @code{L} or @code{ll}.
  
  @item
  
--- 90,109 ----
  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
  
! An optional conversion qualifier, which may be @code{hh} to specify
! @code{char},  @code{h} to specify @code{short}, @code{l} to specify
! @code{long int}, @code{ll} (ell-ell) to specify @code{long long int},
! @code{j} to specify @code{intmax_t}, @code{t} to specify @code{ptrdiff_t},
! @code{z} to specify @code{size_t} or @code{L} to specify @code{long double}.
! Long long type can be specified by @code{L} or @code{ll}.
  
  @item
  
*************** A signed integer.
*** 88,93 ****
--- 124,139 ----
  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[+|-]dd"}, 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 exponent
+ case, the letters @code{"abcdef"} case and the @code{"x"} case matches
+ the specifier case.  The representation always has an exponent.
+ 
  @item e
  @itemx E
  
*************** A floating point number (float or double
*** 96,101 ****
--- 142,148 ----
  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.
*************** gcc may generate warnings, if you use th
*** 173,175 ****
--- 220,255 ----
  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$, %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