diff options
author | fenner <fenner@FreeBSD.org> | 2001-11-30 06:12:15 +0000 |
---|---|---|
committer | fenner <fenner@FreeBSD.org> | 2001-11-30 06:12:15 +0000 |
commit | 5f34351719ae589baa319e74d4a5432cc99775ac (patch) | |
tree | 64b30cdc0268a462bfb03bb9181400291f7b96d2 /lib | |
parent | 5dc107c9938a36ea720fe815c81da168e892d4b0 (diff) | |
download | FreeBSD-src-5f34351719ae589baa319e74d4a5432cc99775ac.zip FreeBSD-src-5f34351719ae589baa319e74d4a5432cc99775ac.tar.gz |
Implement several of the c99 updates to printf(3):
- New length modifiers: hh, j, t, z.
- New flag: '. Note that %'f is not yet implemented.
- Use "inf"/"nan" for efg formats, "INF"/"NAN" for EFG formats.
- Implemented %q in terms of %ll; if "quad_t" is not "long long"
%q will break.
Still to do:
- %C, %S, %lc, %ls (wide character support)
- %'f (thousands in integer portion of %f)
- %a/%A (exact hex representation of floating-point numbers)
Garrett Wollman wrote the first version of the vfprintf.c update;
Mike Barcroft wrote the first version of the printf.3 changes.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libc/stdio/printf.3 | 321 | ||||
-rw-r--r-- | lib/libc/stdio/vfprintf.c | 348 |
2 files changed, 458 insertions, 211 deletions
diff --git a/lib/libc/stdio/printf.3 b/lib/libc/stdio/printf.3 index 784bb77..d9cbd95 100644 --- a/lib/libc/stdio/printf.3 +++ b/lib/libc/stdio/printf.3 @@ -36,7 +36,7 @@ .\" @(#)printf.3 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd June 4, 1993 +.Dd November 8, 2001 .Dt PRINTF 3 .Os .Sh NAME @@ -191,12 +191,9 @@ If unaccessed arguments in the format string are interspersed with ones that are accessed the results will be indeterminate. .It Zero or more of the following flags: -.Bl -hyphen -.It -A -.Cm # -character -specifying that the value should be converted to an +.Bl -tag -width ".So \&\ \& Sc (space)" -compact -offset indent +.It Sq Cm # +The value should be converted to an .Dq alternate form . For .Cm c , d , i , n , p , s , @@ -220,7 +217,7 @@ for .Cm X conversions) prepended to it. For -.Cm e , E , f , g , +.Cm a , A , e , E , f , F , g , and .Cm G conversions, the result will always contain a decimal point, even if no @@ -232,11 +229,8 @@ and .Cm G conversions, trailing zeros are not removed from the result as they would otherwise be. -.It -A -.Cm 0 -(zero) -character specifying zero padding. +.It So Cm 0 Sc (zero) +Zero padding. For all conversions except .Cm n , the converted value is padded on the left with zeros rather than blanks. @@ -247,10 +241,9 @@ and the .Cm 0 flag is ignored. -.It -A negative field width flag -.Cm \- -indicates the converted value is to be left adjusted on the field boundary. +.It Sq Cm \&\- +A negative field width flag; +the converted value is to be left adjusted on the field boundary. Except for .Cm n conversions, the converted value is padded on the right with blanks, @@ -260,20 +253,30 @@ A overrides a .Cm 0 if both are given. -.It -A space, specifying that a blank should be left before a positive number +.It So \&\ \& Sc (space) +A blank should be left before a positive number produced by a signed conversion -.Cm ( d , e , E , f , g , G , +.Cm ( a , A , d , e , E , f , F , g , G , or .Cm i ) . -.It -A -.Cm + -character specifying that a sign always be placed before a +.It Sq Cm + +A sign must always be placed before a number produced by a signed conversion. A .Cm + overrides a space if both are used. +.It Sq Cm ' +Decimal conversions +.Cm ( d , u , +or +.Cm i ) +or the integral portion of a floating point conversion +.Cm ( f +or +.Cm F ) +should be grouped and separated by thousands using +the non-monetary seperator returned by +.Xr localeconv 3 . .El .It An optional decimal digit string specifying a minimum field width. @@ -292,9 +295,9 @@ This gives the minimum number of digits to appear for and .Cm X conversions, the number of digits to appear after the decimal-point for -.Cm e , E , +.Cm a , A , e , E , f , and -.Cm f +.Cm F conversions, the maximum number of significant digits for .Cm g and @@ -304,79 +307,69 @@ string for .Cm s conversions. .It -The optional character -.Cm h , -specifying that a following -.Cm d , i , o , u , x , +An optional length modifier, that specifies the size of the argument. +The following length modifiers are valid for the +.Cm d , i , n , o , u , x , or .Cm X -conversion corresponds to a -.Vt short int -or -.Vt unsigned short int -argument, or that a following -.Cm n -conversion corresponds to a pointer to a -.Vt short int -argument. -.It -The optional character -.Cm l -(ell) specifying that a following -.Cm d , i , o , u , x , -or -.Cm X -conversion applies to a pointer to a -.Vt long int -or -.Vt unsigned long int -argument, or that a following -.Cm n -conversion corresponds to a pointer to a -.Vt long int -argument. -.It -The optional characters -.Cm ll -(ell ell) specifying that a following -.Cm d , i , o , u , x , -or -.Cm X -conversion applies to a pointer to a -.Vt long long int -or -.Vt unsigned long long int -argument, or that a following -.Cm n -conversion corresponds to a pointer to a -.Vt long long int -argument. -.It -The optional character -.Cm q , -specifying that a following -.Cm d , i , o , u , x , +conversion: +.Bl -column ".Cm q Em (deprecated)" ".Vt signed char" ".Vt unsigned long long" ".Vt unsigned long long *" +.It Sy Modifier Ta Cm d , i Ta Cm o , u , x , X Ta Cm n +.It Cm hh Ta Vt signed char Ta Vt unsigned char Ta Vt signed char * +.It Cm h Ta Vt short Ta Vt unsigned short Ta Vt short * +.It Cm l No (ell) Ta Vt long Ta Vt unsigned long Ta Vt long * +.It Cm ll No (ell ell) Ta Vt long long Ta Vt unsigned long long Ta Vt long long * +.It Cm j Ta Vt intmax_t Ta Vt uintmax_t Ta Vt intmax_t * +.It Cm t Ta Vt ptrdiff_t Ta Sy * Ta Vt ptrdiff_t * +.It Cm z Ta Sy * Ta Vt size_t Ta Sy * +.It Cm q Em (deprecated) Ta Vt quad_t Ta Vt u_quad_t Ta Vt quad_t * +.El +.Pp +.Bl -tag -width ".Cm * No -" +.It Cm * No - +The +.Cm t +modifier, when applied to a +.Cm o , u , x , or .Cm X -conversion corresponds to a -.Vt quad int -or -.Vt unsigned quad int -argument, or that a following +conversion, indicates that the argument is of an unsigned type +equivalent in size to a +.Vt ptrdiff_t . +The +.Cm z +modifier, when applied to a +.Cm d or +.Cm i +conversion, indicates that the argument is of a signed type equivalent in +size to a +.Vt size_t . +Similarly, when applied to an .Cm n -conversion corresponds to a pointer to a -.Vt quad int -argument. -.It -The character -.Cm L -specifying that a following -.Cm e , E , f , g , +conversion, it indicates that the argument is a pointer to a signed type +equivalent in size to a +.Vt size_t . +.El +.Pp +The following length modifier is valid for the +.Cm a , A , e , E , f , F , g , or .Cm G -conversion corresponds to a -.Vt long double -argument. +conversion: +.Bl -column ".Sy Modifier" ".Cm a , A , e , E , f , F , g , G" +.It Sy Modifier Ta Cm a , A , e , E , f , F , g , G +.It Cm L Ta Vt long double +.El +.Pp +The following length modifier is valid for the +.Cm c +or +.Cm s +conversion: +.Bl -column ".Sy Modifier" ".Vt wint_t" ".Vt wchar_t *" +.It Sy Modifier Ta Cm c Ta Cm s +.It Cm l No (ell) Ta Vt wint_t Ta Vt wchar_t * +.El .It A character that specifies the type of conversion to be applied. .El @@ -457,7 +450,26 @@ conversion uses the letter to introduce the exponent. The exponent always contains at least two digits; if the value is zero, the exponent is 00. -.It Cm f +.Pp +For +.Cm a , A , e , E , f , F , g , +and +.Cm G +conversions, positive and negative infinity are represented as +.Li inf +and +.Li -inf +respectively when using the lowercase conversion character, and +.Li INF +and +.Li -INF +respectively when using the uppercase conversion character. +Similarly, NaN is represented as +.Li nan +when using the lowercase conversion, and +.Li NAN +when using the uppercase conversion. +.It Cm fF The .Vt double argument is rounded and converted to decimal notation in the style @@ -475,6 +487,8 @@ argument is converted in style or .Cm e (or +.Cm F +or .Cm E for .Cm G @@ -488,12 +502,74 @@ is used if the exponent from its conversion is less than -4 or greater than or equal to the precision. Trailing zeros are removed from the fractional part of the result; a decimal point appears only if it is followed by at least one digit. +.It Cm aA +The +.Vt double +argument is converted to hexadecimal notation in the style +.Oo \- Oc Ns 0xh Ns Cm \&. Ns hhhp Ns Oo +- Oc Ns d , +where the number of digits after the hexadecimal-point character +is equal to the precision specification. +If the precision is missing, it is taken as enough to exactly +represent the floating-point number; if the precision is +explicitly zero, no hexadecimal-point character appears. +This is an exact coversion of the mantissa+exponent internal +floating point representation; the +.Oo \- Oc Ns 0xh Ns Cm \&. Ns hhh +portion represents exactly the mantissa; only denormalized +mantissas have a zero value to the left of the hexadecimal +point. +The +.Cm p +is a literal character +.Qq p ; +the exponent is preceded by a positive or negative sign +and is represented in decimal, using only enough characters +to represent the exponent. +The +.Cm A +conversion uses the prefix +.Cm 0X +(rather than +.Cm 0x ) , +the letters +.Cm ABCDEF +(rather than +.Cm abcdef ) +to represent the hex digits, and the letter +.Cm P +(rather than +.Cm p ) +to seperate the mantissa and exponent. + +.It Cm C +Treated as +.Cm c +with the +.Cm l +(ell) modifier. .It Cm c The .Vt int argument is converted to an .Vt unsigned char , and the resulting character is written. +.Pp +If the +.Cm l +(ell) modifier is used, the +.Vt wint_t +argument shall be converted to a +.Vt wchar_t , +and the (potentially multi-byte) sequence representing the +single wide character is written, including any shift sequences. +If a shift sequence is used, the shift state is also restored +to the original state after the character. +.It Cm S +Treated as +.Cm s +with the +.Cm l +(ell) modifier. .It Cm s The .Vt char * @@ -510,6 +586,30 @@ need be present; if the precision is not specified, or is greater than the size of the array, the array must contain a terminating .Dv NUL character. +.Pp +If the +.Cm l +(ell) modifier is used, the +.Vt wchar_t * +argument is expected to be a pointer to an array of wide characters +(pointer to a wide string). +For each wide character in the string, the (potentially multi-byte) +sequence representing the +wide character is written, including any shift sequences. +If any shift sequence is used, the shift state is also restored +to the original state after the string. +Wide characters from the array are written up to (but not including) +a terminating wide +.Dv NUL +character; +if a precision is specified, no more than the number of bytes specified are +written (including shift sequences). Partial characters are never written. +If a precision is given, no null character +need be present; if the precision is not specified, or is greater than +the number of bytes required to render the multibyte representation of +the string, the array must contain a terminating wide +.Dv NUL +character. .It Cm p The .Vt void * @@ -538,7 +638,8 @@ character is defined in the program's locale (category .Dv LC_NUMERIC ) . .Pp In no case does a non-existent or small field width cause truncation of -a field; if the result of a conversion is wider than the field width, the +a numeric field; if the result of a conversion is wider than the field +width, the field is expanded to contain the conversion result. .Sh EXAMPLES To print a date and time in the form @@ -581,7 +682,8 @@ char *newfmt(const char *fmt, ...) .Ed .Sh SEE ALSO .Xr printf 1 , -.Xr scanf 3 +.Xr scanf 3 , +.Xr setlocale 3 .Sh STANDARDS The .Fn fprintf , @@ -675,4 +777,31 @@ for later interpolation by .Pp Always use the proper secure idiom: .Pp -.Dl snprintf(buffer, sizeof(buffer), "%s", string); +.Dl snprintf(buffer, sizeof(buffer), \*q%s\*q, string); +.Pp +The +.Nm +family of functions currently lack the ability to use the +.Qq ' +flag in conjunction with the +.Qq f +conversion specifier. The +.Qq a +and +.Qq A +conversion specifiers have not yet been implemented. +The +.Qq l +(ell) modifier for the +.Qq c +and +.Qq s +conversion specifiers, for wide characters and strings, have not yet +been implemented. +The +.Qq L +modifier for floating point formats simply round the +.Vt long double +argument to +.Vt double , +providing no additional precision. diff --git a/lib/libc/stdio/vfprintf.c b/lib/libc/stdio/vfprintf.c index bc0e06e..904fcb0 100644 --- a/lib/libc/stdio/vfprintf.c +++ b/lib/libc/stdio/vfprintf.c @@ -51,7 +51,10 @@ static const char rcsid[] = #include "namespace.h" #include <sys/types.h> +#include <ctype.h> #include <limits.h> +#include <stddef.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -75,26 +78,45 @@ union arg { u_int uintarg; long longarg; u_long ulongarg; - quad_t quadarg; - u_quad_t uquadarg; + long long longlongarg; + unsigned long long ulonglongarg; + ptrdiff_t ptrdiffarg; + size_t sizearg; + intmax_t intmaxarg; + uintmax_t uintmaxarg; void *pvoidarg; char *pchararg; + signed char *pschararg; short *pshortarg; int *pintarg; long *plongarg; - quad_t *pquadarg; + long long *plonglongarg; + ptrdiff_t *pptrdiffarg; + size_t *psizearg; + intmax_t *pintmaxarg; #ifdef FLOATING_POINT double doublearg; long double longdoublearg; #endif }; +/* + * Type ids for argument type table. + */ +enum typeid { + T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT, + T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG, + T_PTRDIFFT, TP_PTRDIFFT, T_SIZET, TP_SIZET, + T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, TP_CHAR, TP_SCHAR, + T_DOUBLE, T_LONG_DOUBLE +}; + static int __sprint __P((FILE *, struct __suio *)); static int __sbprintf __P((FILE *, const char *, va_list)) __printflike(2, 0); -static char * __ultoa __P((u_long, char *, int, int, char *)); -static char * __uqtoa __P((u_quad_t, char *, int, int, char *)); +static char * __ujtoa __P((uintmax_t, char *, int, int, char *, const char *)); +static char * __ultoa __P((u_long, char *, int, int, char *, const char *)); static void __find_arguments __P((const char *, va_list, union arg **)); -static void __grow_type_table __P((int, unsigned char **, int *)); +static void __grow_type_table __P((int, enum typeid **, int *)); /* * Flush out all the vectors defined by the given uio, @@ -161,10 +183,12 @@ __sbprintf(FILE *fp, const char *fmt, va_list ap) * use the given digits. */ static char * -__ultoa(u_long val, char *endp, int base, int octzero, char *xdigs) +__ultoa(u_long val, char *endp, int base, int octzero, char *xdigs, + const char *thousep) { register char *cp = endp; register long sval; + int ndig; /* * Handle the three cases separately, in the hope of getting @@ -176,6 +200,7 @@ __ultoa(u_long val, char *endp, int base, int octzero, char *xdigs) *--cp = to_char(val); return (cp); } + ndig = 0; /* * On many machines, unsigned arithmetic is harder than * signed arithmetic, so we do at most one unsigned mod and @@ -184,11 +209,16 @@ __ultoa(u_long val, char *endp, int base, int octzero, char *xdigs) */ if (val > LONG_MAX) { *--cp = to_char(val % 10); + ndig++; sval = val / 10; } else sval = val; do { *--cp = to_char(sval % 10); + if (++ndig == 3 && thousep && *thousep != '\0') { + *--cp = *thousep; + ndig = 0; + } sval /= 10; } while (sval != 0); break; @@ -215,30 +245,39 @@ __ultoa(u_long val, char *endp, int base, int octzero, char *xdigs) return (cp); } -/* Identical to __ultoa, but for quads. */ +/* Identical to __ultoa, but for intmax_t. */ static char * -__uqtoa(u_quad_t val, char *endp, int base, int octzero, char *xdigs) +__ujtoa(uintmax_t val, char *endp, int base, int octzero, char *xdigs, + const char *thousep) { char *cp = endp; - quad_t sval; + intmax_t sval; + int ndig; /* quick test for small values; __ultoa is typically much faster */ /* (perhaps instead we should run until small, then call __ultoa?) */ if (val <= ULONG_MAX) - return (__ultoa((u_long)val, endp, base, octzero, xdigs)); + return (__ultoa((u_long)val, endp, base, octzero, xdigs, + thousep)); switch (base) { case 10: if (val < 10) { *--cp = to_char(val % 10); return (cp); } - if (val > QUAD_MAX) { + ndig = 0; + if (val > INTMAX_MAX) { *--cp = to_char(val % 10); + ndig++; sval = val / 10; } else sval = val; do { *--cp = to_char(sval % 10); + if (++ndig == 3 && thousep && *thousep != '\0') { + *--cp = *thousep; + ndig = 0; + } sval /= 10; } while (sval != 0); break; @@ -284,7 +323,7 @@ vfprintf(FILE *fp, const char *fmt0, va_list ap) #include <math.h> #include "floatio.h" -#define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ +#define BUF ((MAXEXP*4/3)+MAXFRACT+1) /* + decimal point */ #define DEFPREC 6 static char *cvt __P((double, int, int, char *, int *, int, int *, char **)); @@ -292,7 +331,7 @@ static int exponent __P((char *, int, int)); #else /* no FLOATING_POINT */ -#define BUF 68 +#define BUF 90 #endif /* FLOATING_POINT */ @@ -306,10 +345,16 @@ static int exponent __P((char *, int, int)); #define LADJUST 0x004 /* left adjustment */ #define LONGDBL 0x008 /* long double */ #define LONGINT 0x010 /* long integer */ -#define QUADINT 0x020 /* quad integer */ +#define LLONGINT 0x020 /* long long integer */ #define SHORTINT 0x040 /* short integer */ #define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ #define FPT 0x100 /* Floating point number */ + /* C99 additional size modifiers: */ +#define SIZET 0x200 /* size_t */ +#define PTRDIFFT 0x400 /* ptrdiff_t */ +#define INTMAXT 0x800 /* intmax_t */ +#define CHARINT 0x1000 /* print char using int format */ + /* * Non-MT-safe version */ @@ -326,8 +371,9 @@ __vfprintf(FILE *fp, const char *fmt0, va_list ap) int width; /* width from format (%8d), or 0 */ int prec; /* precision from format (%.3d), or -1 */ char sign; /* sign prefix (' ', '+', '-', or \0) */ + const char *thousands_sep; #ifdef FLOATING_POINT - char *decimal_point = localeconv()->decimal_point; + char *decimal_point; char softsign; /* temporary negative sign for floats */ double _double; /* double precision arguments %[eEfgG] */ int expt; /* integer value of exponent */ @@ -337,7 +383,7 @@ __vfprintf(FILE *fp, const char *fmt0, va_list ap) char *dtoaresult; /* buffer allocated by dtoa */ #endif u_long ulval; /* integer arguments %[diouxX] */ - u_quad_t uqval; /* %q integers */ + uintmax_t ujval; /* %j, %ll, %q, %t, %z integers */ int base; /* base for [diouxX] conversion */ int dprec; /* a copy of prec if [diouxX], 0 otherwise */ int realsz; /* field size expanded by dprec, sign, etc */ @@ -347,9 +393,9 @@ __vfprintf(FILE *fp, const char *fmt0, va_list ap) #define NIOV 8 struct __suio uio; /* output information: summary */ struct __siov iov[NIOV];/* ... and individual io vectors */ - char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ + char buf[BUF]; /* space for %c, %[diouxX], %[eEfFgG] */ char ox[2]; /* space for 0x hex-prefix */ - union arg *argtable; /* args, built due to positional arg */ + union arg *argtable; /* args, built due to positional arg */ union arg statargtable [STATIC_ARG_TBL_SIZE]; int nextarg; /* 1-based argument index */ va_list orgap; /* original argument pointer */ @@ -411,11 +457,24 @@ __vfprintf(FILE *fp, const char *fmt0, va_list ap) #define SARG() \ (flags&LONGINT ? GETARG(long) : \ flags&SHORTINT ? (long)(short)GETARG(int) : \ + flags&CHARINT ? (long)(signed char)GETARG(int) : \ (long)GETARG(int)) #define UARG() \ (flags&LONGINT ? GETARG(u_long) : \ flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ + flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ (u_long)GETARG(u_int)) +#define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT) +#define SJARG() \ + (flags&INTMAXT ? GETARG(intmax_t) : \ + flags&SIZET ? (intmax_t)GETARG(size_t) : \ + flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ + (intmax_t)GETARG(long long)) +#define UJARG() \ + (flags&INTMAXT ? GETARG(uintmax_t) : \ + flags&SIZET ? (uintmax_t)GETARG(size_t) : \ + flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ + (uintmax_t)GETARG(unsigned long long)) /* * Get * arguments, including the form *nn$. Preserve the nextarg @@ -443,8 +502,10 @@ __vfprintf(FILE *fp, const char *fmt0, va_list ap) } + thousands_sep = NULL; #ifdef FLOATING_POINT dtoaresult = NULL; + decimal_point = localeconv()->decimal_point; #endif /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ if (cantwrite(fp)) @@ -520,6 +581,9 @@ reswitch: switch (ch) { case '+': sign = '+'; goto rflag; + case '\'': + thousands_sep = localeconv()->thousands_sep; + goto rflag; case '.': if ((ch = *fmt++) == '*') { GETASTER (n); @@ -565,16 +629,30 @@ reswitch: switch (ch) { goto rflag; #endif case 'h': - flags |= SHORTINT; + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; goto rflag; case 'l': - if (flags & LONGINT) - flags |= QUADINT; - else + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else flags |= LONGINT; goto rflag; case 'q': - flags |= QUADINT; + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; goto rflag; case 'c': *(cp = buf) = GETARG(int); @@ -586,10 +664,10 @@ reswitch: switch (ch) { /*FALLTHROUGH*/ case 'd': case 'i': - if (flags & QUADINT) { - uqval = GETARG(quad_t); - if ((quad_t)uqval < 0) { - uqval = -uqval; + if (flags & INTMAX_SIZE) { + ujval = SJARG(); + if ((intmax_t)ujval < 0) { + ujval = -ujval; sign = '-'; } } else { @@ -602,9 +680,14 @@ reswitch: switch (ch) { base = 10; goto number; #ifdef FLOATING_POINT +#ifdef HEXFLOAT + case 'a': + case 'A': +#endif case 'e': case 'E': case 'f': + case 'F': goto fp_begin; case 'g': case 'G': @@ -621,12 +704,18 @@ fp_begin: if (prec == -1) if (isinf(_double)) { if (_double < 0) sign = '-'; - cp = "Inf"; + if (isupper(ch)) + cp = "INF"; + else + cp = "inf"; size = 3; break; } if (isnan(_double)) { - cp = "NaN"; + if (isupper(ch)) + cp = "NAN"; + else + cp = "nan"; size = 3; break; } @@ -643,13 +732,13 @@ fp_begin: if (prec == -1) else ch = 'g'; } - if (ch <= 'e') { /* 'e' or 'E' fmt */ + if (ch == 'e' || ch == 'E') { --expt; expsize = exponent(expstr, expt, ch); size = expsize + ndig; if (ndig > 1 || flags & ALT) ++size; - } else if (ch == 'f') { /* f fmt */ + } else if (ch == 'f' || ch == 'F') { if (expt > 0) { size = expt; if (prec || flags & ALT) @@ -669,12 +758,25 @@ fp_begin: if (prec == -1) break; #endif /* FLOATING_POINT */ case 'n': - if (flags & QUADINT) - *GETARG(quad_t *) = ret; + /* + * Assignment-like behavior is specified if the + * value overflows or is otherwise unrepresentable. + * C99 says to use `signed char' for %hhn conversions. + */ + if (flags & LLONGINT) + *GETARG(long long *) = ret; + else if (flags & SIZET) + *GETARG(ssize_t *) = (ssize_t)ret; + else if (flags & PTRDIFFT) + *GETARG(ptrdiff_t *) = ret; + else if (flags & INTMAXT) + *GETARG(intmax_t *) = ret; else if (flags & LONGINT) *GETARG(long *) = ret; else if (flags & SHORTINT) *GETARG(short *) = ret; + else if (flags & CHARINT) + *GETARG(signed char *) = ret; else *GETARG(int *) = ret; continue; /* no output */ @@ -682,8 +784,8 @@ fp_begin: if (prec == -1) flags |= LONGINT; /*FALLTHROUGH*/ case 'o': - if (flags & QUADINT) - uqval = GETARG(u_quad_t); + if (flags & INTMAX_SIZE) + ujval = UJARG(); else ulval = UARG(); base = 8; @@ -696,10 +798,10 @@ fp_begin: if (prec == -1) * defined manner.'' * -- ANSI X3J11 */ - ulval = (u_long)GETARG(void *); + ujval = (uintmax_t)(uintptr_t)GETARG(void *); base = 16; xdigs = "0123456789abcdef"; - flags = (flags & ~QUADINT) | HEXPREFIX; + flags = flags | INTMAXT | HEXPREFIX; ch = 'x'; goto nosign; case 's': @@ -727,8 +829,8 @@ fp_begin: if (prec == -1) flags |= LONGINT; /*FALLTHROUGH*/ case 'u': - if (flags & QUADINT) - uqval = GETARG(u_quad_t); + if (flags & INTMAX_SIZE) + ujval = UJARG(); else ulval = UARG(); base = 10; @@ -738,14 +840,15 @@ fp_begin: if (prec == -1) goto hex; case 'x': xdigs = "0123456789abcdef"; -hex: if (flags & QUADINT) - uqval = GETARG(u_quad_t); +hex: + if (flags & INTMAX_SIZE) + ujval = UJARG(); else ulval = UARG(); base = 16; /* leading 0x/X only if non-zero */ if (flags & ALT && - (flags & QUADINT ? uqval != 0 : ulval != 0)) + (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) flags |= HEXPREFIX; /* unsigned conversions */ @@ -764,14 +867,14 @@ number: if ((dprec = prec) >= 0) * -- ANSI X3J11 */ cp = buf + BUF; - if (flags & QUADINT) { - if (uqval != 0 || prec != 0) - cp = __uqtoa(uqval, cp, base, - flags & ALT, xdigs); + if (flags & INTMAX_SIZE) { + if (ujval != 0 || prec != 0) + cp = __ujtoa(ujval, cp, base, + flags & ALT, xdigs, thousands_sep); } else { if (ulval != 0 || prec != 0) cp = __ultoa(ulval, cp, base, - flags & ALT, xdigs); + flags & ALT, xdigs, thousands_sep); } size = buf + BUF - cp; break; @@ -904,27 +1007,6 @@ error: } /* - * Type ids for argument type table. - */ -#define T_UNUSED 0 -#define T_SHORT 1 -#define T_U_SHORT 2 -#define TP_SHORT 3 -#define T_INT 4 -#define T_U_INT 5 -#define TP_INT 6 -#define T_LONG 7 -#define T_U_LONG 8 -#define TP_LONG 9 -#define T_QUAD 10 -#define T_U_QUAD 11 -#define TP_QUAD 12 -#define T_DOUBLE 13 -#define T_LONG_DOUBLE 14 -#define TP_CHAR 15 -#define TP_VOID 16 - -/* * Find all arguments when a positional parameter is encountered. Returns a * table, indexed by argument number, of pointers to each arguments. The * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. @@ -939,8 +1021,8 @@ __find_arguments (const char *fmt0, va_list ap, union arg **argtable) char *cp; /* handy char pointer (short term usage) */ int flags; /* flags as above */ int width; /* width from format (%8d), or 0 */ - unsigned char *typetable; /* table of types */ - unsigned char stattypetable [STATIC_ARG_TBL_SIZE]; + enum typeid *typetable; /* table of types */ + enum typeid stattypetable [STATIC_ARG_TBL_SIZE]; int tablesize; /* current size of type table */ int tablemax; /* largest used index in table */ int nextarg; /* 1-based argument index */ @@ -955,12 +1037,18 @@ __find_arguments (const char *fmt0, va_list ap, union arg **argtable) typetable[nextarg++] = type) #define ADDSARG() \ - ((flags&LONGINT) ? ADDTYPE(T_LONG) : \ - ((flags&SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT))) + ((flags&INTMAXT) ? ADDTYPE(T_INTMAXT) : \ + ((flags&SIZET) ? ADDTYPE(T_SIZET) : \ + ((flags&PTRDIFFT) ? ADDTYPE(T_PTRDIFFT) : \ + ((flags&LLONGINT) ? ADDTYPE(T_LLONG) : \ + ((flags&LONGINT) ? ADDTYPE(T_LONG) : ADDTYPE(T_INT)))))) #define ADDUARG() \ - ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \ - ((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT))) + ((flags&INTMAXT) ? ADDTYPE(T_UINTMAXT) : \ + ((flags&SIZET) ? ADDTYPE(T_SIZET) : \ + ((flags&PTRDIFFT) ? ADDTYPE(T_PTRDIFFT) : \ + ((flags&LLONGINT) ? ADDTYPE(T_U_LLONG) : \ + ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : ADDTYPE(T_U_INT)))))) /* * Add * arguments to the type array. @@ -1011,6 +1099,7 @@ reswitch: switch (ch) { goto rflag; case '-': case '+': + case '\'': goto rflag; case '.': if ((ch = *fmt++) == '*') { @@ -1042,16 +1131,30 @@ reswitch: switch (ch) { goto rflag; #endif case 'h': - flags |= SHORTINT; + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; goto rflag; case 'l': - if (flags & LONGINT) - flags |= QUADINT; - else + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else flags |= LONGINT; goto rflag; case 'q': - flags |= QUADINT; + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; goto rflag; case 'c': ADDTYPE(T_INT); @@ -1061,13 +1164,13 @@ reswitch: switch (ch) { /*FALLTHROUGH*/ case 'd': case 'i': - if (flags & QUADINT) { - ADDTYPE(T_QUAD); - } else { - ADDSARG(); - } + ADDSARG(); break; #ifdef FLOATING_POINT +#ifdef HEXFLOAT + case 'a': + case 'A': +#endif case 'e': case 'E': case 'f': @@ -1080,12 +1183,20 @@ reswitch: switch (ch) { break; #endif /* FLOATING_POINT */ case 'n': - if (flags & QUADINT) - ADDTYPE(TP_QUAD); + if (flags & INTMAXT) + ADDTYPE(TP_INTMAXT); + else if (flags & PTRDIFFT) + ADDTYPE(TP_PTRDIFFT); + else if (flags & SIZET) + ADDTYPE(TP_SIZET); + else if (flags & LLONGINT) + ADDTYPE(TP_LLONG); else if (flags & LONGINT) ADDTYPE(TP_LONG); else if (flags & SHORTINT) ADDTYPE(TP_SHORT); + else if (flags & CHARINT) + ADDTYPE(TP_SCHAR); else ADDTYPE(TP_INT); continue; /* no output */ @@ -1093,10 +1204,7 @@ reswitch: switch (ch) { flags |= LONGINT; /*FALLTHROUGH*/ case 'o': - if (flags & QUADINT) - ADDTYPE(T_U_QUAD); - else - ADDUARG(); + ADDUARG(); break; case 'p': ADDTYPE(TP_VOID); @@ -1108,17 +1216,9 @@ reswitch: switch (ch) { flags |= LONGINT; /*FALLTHROUGH*/ case 'u': - if (flags & QUADINT) - ADDTYPE(T_U_QUAD); - else - ADDUARG(); - break; case 'X': case 'x': - if (flags & QUADINT) - ADDTYPE(T_U_QUAD); - else - ADDUARG(); + ADDUARG(); break; default: /* "%?" prints ?, unless ? is NUL */ if (ch == '\0') @@ -1138,14 +1238,11 @@ done: (*argtable) [0].intarg = 0; for (n = 1; n <= tablemax; n++) { switch (typetable [n]) { - case T_UNUSED: - (*argtable) [n].intarg = va_arg (ap, int); - break; - case T_SHORT: + case T_UNUSED: /* whoops! */ (*argtable) [n].intarg = va_arg (ap, int); break; - case T_U_SHORT: - (*argtable) [n].intarg = va_arg (ap, int); + case TP_SCHAR: + (*argtable) [n].pschararg = va_arg (ap, signed char *); break; case TP_SHORT: (*argtable) [n].pshortarg = va_arg (ap, short *); @@ -1168,14 +1265,35 @@ done: case TP_LONG: (*argtable) [n].plongarg = va_arg (ap, long *); break; - case T_QUAD: - (*argtable) [n].quadarg = va_arg (ap, quad_t); + case T_LLONG: + (*argtable) [n].longlongarg = va_arg (ap, long long); + break; + case T_U_LLONG: + (*argtable) [n].ulonglongarg = va_arg (ap, unsigned long long); + break; + case TP_LLONG: + (*argtable) [n].plonglongarg = va_arg (ap, long long *); + break; + case T_PTRDIFFT: + (*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t); + break; + case TP_PTRDIFFT: + (*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *); + break; + case T_SIZET: + (*argtable) [n].sizearg = va_arg (ap, size_t); + break; + case TP_SIZET: + (*argtable) [n].psizearg = va_arg (ap, ssize_t *); + break; + case T_INTMAXT: + (*argtable) [n].intmaxarg = va_arg (ap, intmax_t); break; - case T_U_QUAD: - (*argtable) [n].uquadarg = va_arg (ap, u_quad_t); + case T_UINTMAXT: + (*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t); break; - case TP_QUAD: - (*argtable) [n].pquadarg = va_arg (ap, quad_t *); + case TP_INTMAXT: + (*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *); break; #ifdef FLOATING_POINT case T_DOUBLE: @@ -1202,11 +1320,11 @@ done: * Increase the size of the type table. */ static void -__grow_type_table (int nextarg, unsigned char **typetable, int *tablesize) +__grow_type_table (int nextarg, enum typeid **typetable, int *tablesize) { - unsigned char *const oldtable = *typetable; + enum typeid *const oldtable = *typetable; const int oldsize = *tablesize; - unsigned char *newtable; + enum typeid *newtable; int newsize = oldsize * 2; if (newsize < nextarg + 1) |