diff options
Diffstat (limited to 'stdio-common')
-rw-r--r-- | stdio-common/Makefile | 2 | ||||
-rw-r--r-- | stdio-common/printf_fp.c | 10 | ||||
-rw-r--r-- | stdio-common/printf_fphex.c | 440 | ||||
-rw-r--r-- | stdio-common/tstscanf.c | 10 | ||||
-rw-r--r-- | stdio-common/vfprintf.c | 86 | ||||
-rw-r--r-- | stdio-common/vfscanf.c | 4 |
6 files changed, 527 insertions, 25 deletions
diff --git a/stdio-common/Makefile b/stdio-common/Makefile index 814374b8ae..d1d922ed36 100644 --- a/stdio-common/Makefile +++ b/stdio-common/Makefile @@ -25,7 +25,7 @@ headers := stdio_lim.h printf.h routines := \ ctermid cuserid \ - vfprintf vprintf printf_fp reg-printf printf-prs _itoa \ + vfprintf vprintf printf_fp reg-printf printf-prs _itoa printf_fphex \ fprintf printf snprintf sprintf asprintf dprintf \ vfscanf \ fscanf scanf sscanf \ diff --git a/stdio-common/printf_fp.c b/stdio-common/printf_fp.c index dede144b8d..8cff7a4d66 100644 --- a/stdio-common/printf_fp.c +++ b/stdio-common/printf_fp.c @@ -1,5 +1,5 @@ /* Floating point output for `printf'. - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. @@ -291,12 +291,12 @@ __printf_fp (FILE *fp, /* Check for special values: not a number or infinity. */ if (__isnanl (fpnum.ldbl)) { - special = "NaN"; + special = isupper (info->spec) ? "NAN" : "nan"; is_neg = 0; } else if (__isinfl (fpnum.ldbl)) { - special = "Inf"; + special = isupper (info->spec) ? "INF" : "inf"; is_neg = fpnum.ldbl < 0; } else @@ -316,12 +316,12 @@ __printf_fp (FILE *fp, /* Check for special values: not a number or infinity. */ if (__isnan (fpnum.dbl)) { - special = "NaN"; + special = isupper (info->spec) ? "NAN" : "nan"; is_neg = 0; } else if (__isinf (fpnum.dbl)) { - special = "Inf"; + special = isupper (info->spec) ? "INF" : "inf"; is_neg = fpnum.dbl < 0; } else diff --git a/stdio-common/printf_fphex.c b/stdio-common/printf_fphex.c new file mode 100644 index 0000000000..d442b56099 --- /dev/null +++ b/stdio-common/printf_fphex.c @@ -0,0 +1,440 @@ +/* Print floating point number in hexadecimal notation according to + ISO C 9X. + Copyright (C) 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <ctype.h> +#include <ieee754.h> +#include <math.h> +#include <printf.h> +#include <stdlib.h> +#include <stdio.h> +#include "_itoa.h" +#include "../locale/localeinfo.h" + +/* #define NDEBUG 1*/ /* Undefine this for debugging assertions. */ +#include <assert.h> + +/* This defines make it possible to use the same code for GNU C library and + the GNU I/O library. */ +#ifdef USE_IN_LIBIO +# include <libioP.h> +# define PUT(f, s, n) _IO_sputn (f, s, n) +# define PAD(f, c, n) _IO_padn (f, c, n) +/* We use this file GNU C library and GNU I/O library. So make + names equal. */ +# undef putc +# define putc(c, f) _IO_putc_unlocked (c, f) +# define size_t _IO_size_t +# define FILE _IO_FILE +#else /* ! USE_IN_LIBIO */ +# define PUT(f, s, n) fwrite (s, 1, n, f) +# define PAD(f, c, n) __printf_pad (f, c, n) +ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c. */ +#endif /* USE_IN_LIBIO */ + +/* Macros for doing the actual output. */ + +#define outchar(ch) \ + do \ + { \ + register const int outc = (ch); \ + if (putc (outc, fp) == EOF) \ + return -1; \ + ++done; \ + } while (0) + +#define PRINT(ptr, len) \ + do \ + { \ + int outlen = (len); \ + const char *cp = (ptr); \ + while (outlen-- > 0) \ + outchar (*cp++); \ + } while (0) + +#define PADN(ch, len) \ + do \ + { \ + if (PAD (fp, ch, len) != len) \ + return -1; \ + done += len; \ + } \ + while (0) + + +int +__printf_fphex (FILE *fp, + const struct printf_info *info, + const void *const *args) +{ + /* The floating-point value to output. */ + union + { + union ieee754_double dbl; + union ieee854_long_double ldbl; + } + fpnum; + + /* Locale-dependent representation of decimal point. */ + wchar_t decimal; + + /* "NaN" or "Inf" for the special cases. */ + const char *special = NULL; + + /* Buffer for the generated number string for the mantissa. The + maximal size for the mantissa is 64 bits. */ + char numbuf[16]; + char *numstr; + char *numend; + int negative; + + /* The maximal exponent of two in decimal notation has 5 digits. */ + char expbuf[5]; + char *expstr; + int expnegative; + int exponent; + + /* Non-zero is mantissa is zero. */ + int zero_mantissa; + + /* The leading digit before the decimal point. */ + char leading; + + /* Precision. */ + int precision = info->prec; + + /* Width. */ + int width = info->width; + + /* Number of characters written. */ + int done = 0; + + + /* Figure out the decimal point character. */ + if (info->extra == 0) + { + if (mbtowc (&decimal, _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT), + strlen (_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT))) <= 0) + decimal = (wchar_t) *_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT); + } + else + { + if (mbtowc (&decimal, _NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT), + strlen (_NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT))) <= 0) + decimal = (wchar_t) *_NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT); + } + /* Give default value. */ + if (decimal == L'\0') + decimal = L'.'; + + + /* Fetch the argument value. */ + if (info->is_long_double && sizeof (long double) > sizeof (double)) + { + fpnum.ldbl.d = *(const long double *) args[0]; + + /* Check for special values: not a number or infinity. */ + if (__isnanl (fpnum.ldbl.d)) + { + special = isupper (info->spec) ? "NAN" : "nan"; + negative = 0; + } + else + { + if (__isinfl (fpnum.ldbl.d)) + special = isupper (info->spec) ? "INF" : "inf"; + + negative = fpnum.ldbl.d < 0; + } + } + else + { + fpnum.dbl.d = *(const double *) args[0]; + + /* Check for special values: not a number or infinity. */ + if (__isnan (fpnum.dbl.d)) + { + special = isupper (info->spec) ? "NAN" : "nan"; + negative = 0; + } + else + { + if (__isinf (fpnum.dbl.d)) + special = isupper (info->spec) ? "INF" : "inf"; + + negative = fpnum.dbl.d < 0; + } + } + + if (special) + { + int width = info->prec > info->width ? info->prec : info->width; + + if (negative || info->showsign || info->space) + --width; + width -= 3; + + if (!info->left && width > 0) + PADN (' ', width); + + if (negative) + outchar ('-'); + else if (info->showsign) + outchar ('+'); + else if (info->space) + outchar (' '); + + PRINT (special, 3); + + if (info->left && width > 0) + PADN (' ', width); + + return done; + } + + /* We are handling here only 64 and 80 bit IEEE foating point + numbers. */ + if (info->is_long_double == 0 || sizeof (double) == sizeof (long double)) + { + /* We have 52 bits of mantissa plus one implicit digit. Since + 52 bits are representable without rest using hexadecimal + digits we use only the implicit digits for the number before + the decimal point. */ + if (sizeof (unsigned long int) > 6) + { + unsigned long int num; + + num = (((unsigned long int) fpnum.dbl.ieee.mantissa0) << 32 + | fpnum.dbl.ieee.mantissa1); + + zero_mantissa = num == 0; + + numstr = _itoa_word (num, numbuf + sizeof numbuf, 16, + info->spec == 'A'); + } + else + { + unsigned long long int num; + + num = (((unsigned long long int) fpnum.dbl.ieee.mantissa0) << 32 + | fpnum.dbl.ieee.mantissa1); + + zero_mantissa = num == 0; + + numstr = _itoa (num, numbuf + sizeof numbuf, 16, info->spec == 'A'); + } + + /* Fill with zeroes. */ + while (numstr > numbuf + (sizeof numbuf - 13)) /* 52 ÷ 4 = 13 */ + *--numstr = '0'; + + leading = fpnum.dbl.ieee.exponent == 0 ? '0' : '1'; + + exponent = fpnum.dbl.ieee.exponent; + + if (exponent != 0 && exponent < IEEE754_DOUBLE_BIAS) + { + expnegative = 1; + exponent = abs (exponent - IEEE754_DOUBLE_BIAS); + } + else + { + expnegative = 0; + if (exponent != 0) + exponent -= IEEE754_DOUBLE_BIAS; + } + } + else + { + /* The "strange" 80 bit format on ix86 and m68k has an explicit + leading digit in the 64 bit mantissa. */ + assert (sizeof (long double) == 12); + + if (sizeof (unsigned long int) > 6) + { + unsigned long int num; + + num = (((unsigned long int) fpnum.dbl.ieee.mantissa0) << 32 + | fpnum.dbl.ieee.mantissa1); + + zero_mantissa = num == 0; + + numstr = _itoa_word (num, numbuf + sizeof numbuf, 16, + info->spec == 'A'); + } + else + { + unsigned long long int num; + + num = (((unsigned long long int) fpnum.ldbl.ieee.mantissa0) << 32 + | fpnum.ldbl.ieee.mantissa1); + + zero_mantissa = num == 0; + + numstr = _itoa (num, numbuf + sizeof numbuf, 16, info->spec == 'A'); + } + + /* We use a full nibble for the leading digit. */ + leading = *numstr++; + + /* Fill with zeroes. */ + while (numstr > numbuf + (sizeof numbuf - 15)) /* 60 ÷ 4 = 15 */ + *--numstr = '0'; + + /* We have 3 bits from the mantissa in the leading nibble. */ + exponent = fpnum.ldbl.ieee.exponent - 3; + + if (exponent != 0 && exponent < IEEE854_LONG_DOUBLE_BIAS) + { + expnegative = 1; + exponent = abs (exponent - IEEE854_LONG_DOUBLE_BIAS); + } + else + { + expnegative = 0; + if (exponent != 0) + exponent -= IEEE854_LONG_DOUBLE_BIAS; + } + } + + /* Look for trailing zeroes. */ + if (! zero_mantissa) + { + numend = numbuf + sizeof numbuf; + while (numend[-1] == '0') + --numend; + + if (precision == -1) + precision = numend - numstr; + else if (precision < numend - numstr + && (numstr[precision] > 5 + || (numstr[precision] == 5 + && (precision + 1 < numend - numstr + /* Round to even. */ + || (precision > 0 + && ((numstr[precision - 1] & 1) + ^ (isdigit (numstr[precision - 1]) == 0))) + || (precision == 0 + && ((leading & 1) + ^ (isdigit (leading) == 0))))))) + { + /* Round up. */ + int cnt = precision; + while (--cnt >= 0) + { + char ch = numstr[cnt]; + /* We assume that the digits and the letters are ordered + like in ASCII. This is true for the rest of GNU, too. */ + if (ch == '9') + { + numstr[cnt] = info->spec; /* This is tricky, + think about it! */ + break; + } + else if (tolower (ch) < 'f') + { + ++numstr[cnt]; + break; + } + else + numstr[cnt] = '0'; + } + if (cnt < 0) + { + /* The mantissa so far was fff...f Now increment the + leading digit. Here it is again possible that we + get an overflow. */ + if (leading == '9') + leading = info->spec; + else if (tolower (leading) < 'f') + ++leading; + else + { + leading = 1; + if (expnegative) + { + exponent += 4; + if (exponent >= 0) + expnegative = 0; + } + else + exponent += 4; + } + } + } + } + else + numend = numstr; + + /* Now we can compute the exponent string. */ + expstr = _itoa_word (exponent, expbuf + sizeof expbuf, 10, 0); + + /* Now we have all information to compute the size. */ + width -= ((negative || info->showsign || info->space) + /* Sign. */ + + 2 + 1 + 1 + precision + 1 + 1 + /* 0x h . hhh P ExpoSign. */ + + ((expbuf + sizeof expbuf) - expstr)); + /* Exponent. */ + + /* A special case if when the mantissa is zero and the `#' is not + given. In this case we must not print the decimal point. */ + if (zero_mantissa && precision == 0 && !info->alt) + ++width; /* This nihilates the +1 for the decimal-point + character in the following equation. */ + + if (!info->left && width > 0) + PADN (' ', width); + + if (negative) + outchar ('-'); + else if (info->showsign) + outchar ('+'); + else if (info->space) + outchar (' '); + + outchar ('0'); + outchar (info->spec == 'A' ? 'X' : 'x'); + outchar (leading); + + if (!zero_mantissa || precision != 0 || info->alt) + outchar (decimal); + + if (!zero_mantissa || precision != 0) + { + PRINT (numstr, MIN (numend - numstr, precision)); + if (precision > numend - numstr) + PADN ('0', precision - (numend - numstr)); + } + + if (info->left && info->pad == '0' && width > 0) + PADN ('0', width); + + outchar (info->spec == 'A' ? 'P' : 'p'); + + outchar (expnegative ? '-' : '+'); + + PRINT (expstr, (expbuf + sizeof expbuf) - expstr); + + if (info->left && info->pad != '0' && width > 0) + PADN (info->pad, width); + + return done; +} diff --git a/stdio-common/tstscanf.c b/stdio-common/tstscanf.c index d76d95b1b7..85be441b5b 100644 --- a/stdio-common/tstscanf.c +++ b/stdio-common/tstscanf.c @@ -140,5 +140,15 @@ main (int argc, char **argv) if (out != stdout) pclose (out); + fputs ("Test 3:\n", out); + { + int res, val, n; + + res = sscanf ("-242", "%3o%n", &val, &n); + printf ("res = %d, val = %d, n = %d\n", res, val, n); + if (res != 1 || val != -20 || n != 3) + return 1; + } + exit(EXIT_SUCCESS); } diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c index eda0d7bd8e..c206c2f0ac 100644 --- a/stdio-common/vfprintf.c +++ b/stdio-common/vfprintf.c @@ -238,7 +238,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) /* '4' */ 8, /* '5' */ 8, /* '6' */ 8, /* '7' */ 8, /* '8' */ 8, /* '9' */ 8, 0, 0, 0, 0, 0, 0, - 0, 0, 0, /* 'C' */ 25, + 0, /* 'A' */ 26, 0, /* 'C' */ 25, 0, /* 'E' */ 19, 0, /* 'G' */ 19, 0, 0, 0, 0, /* 'L' */ 12, 0, 0, 0, @@ -246,7 +246,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) 0, 0, 0, 0, /* 'X' */ 18, 0, /* 'Z' */ 13, 0, 0, 0, 0, 0, - 0, 0, 0, /* 'c' */ 20, + 0, /* 'a' */ 26, 0, /* 'c' */ 20, /* 'd' */ 15, /* 'e' */ 19, /* 'f' */ 19, /* 'g' */ 19, /* 'h' */ 10, /* 'i' */ 15, 0, 0, /* 'l' */ 11, /* 'm' */ 24, /* 'n' */ 23, /* 'o' */ 17, @@ -270,7 +270,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) #define STEP0_3_TABLE \ /* Step 0: at the beginning. */ \ - static const void *step0_jumps[26] = \ + static const void *step0_jumps[27] = \ { \ REF (form_unknown), \ REF (flag_space), /* for ' ' */ \ @@ -297,10 +297,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) REF (form_pointer), /* for 'p' */ \ REF (form_number), /* for 'n' */ \ REF (form_strerror), /* for 'm' */ \ - REF (form_wcharacter) /* for 'C' */ \ + REF (form_wcharacter), /* for 'C' */ \ + REF (form_floathex) /* for 'A', 'a' */ \ }; \ /* Step 1: after processing width. */ \ - static const void *step1_jumps[26] = \ + static const void *step1_jumps[27] = \ { \ REF (form_unknown), \ REF (form_unknown), /* for ' ' */ \ @@ -327,10 +328,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) REF (form_pointer), /* for 'p' */ \ REF (form_number), /* for 'n' */ \ REF (form_strerror), /* for 'm' */ \ - REF (form_wcharacter) /* for 'C' */ \ + REF (form_wcharacter), /* for 'C' */ \ + REF (form_floathex) /* for 'A', 'a' */ \ }; \ /* Step 2: after processing precision. */ \ - static const void *step2_jumps[26] = \ + static const void *step2_jumps[27] = \ { \ REF (form_unknown), \ REF (form_unknown), /* for ' ' */ \ @@ -357,10 +359,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) REF (form_pointer), /* for 'p' */ \ REF (form_number), /* for 'n' */ \ REF (form_strerror), /* for 'm' */ \ - REF (form_wcharacter) /* for 'C' */ \ + REF (form_wcharacter), /* for 'C' */ \ + REF (form_floathex) /* for 'A', 'a' */ \ }; \ /* Step 3: after processing first 'l' modifier. */ \ - static const void *step3_jumps[26] = \ + static const void *step3_jumps[27] = \ { \ REF (form_unknown), \ REF (form_unknown), /* for ' ' */ \ @@ -387,12 +390,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) REF (form_pointer), /* for 'p' */ \ REF (form_number), /* for 'n' */ \ REF (form_strerror), /* for 'm' */ \ - REF (form_wcharacter) /* for 'C' */ \ + REF (form_wcharacter), /* for 'C' */ \ + REF (form_floathex) /* for 'A', 'a' */ \ } #define STEP4_TABLE \ /* Step 4: processing format specifier. */ \ - static const void *step4_jumps[26] = \ + static const void *step4_jumps[27] = \ { \ REF (form_unknown), \ REF (form_unknown), /* for ' ' */ \ @@ -419,7 +423,8 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) REF (form_pointer), /* for 'p' */ \ REF (form_number), /* for 'n' */ \ REF (form_strerror), /* for 'm' */ \ - REF (form_wcharacter) /* for 'C' */ \ + REF (form_wcharacter), /* for 'C' */ \ + REF (form_floathex) /* for 'A', 'a' */ \ } @@ -702,6 +707,53 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) } \ break; \ \ + LABEL (form_floathex): \ + { \ + /* FLoating point number printed as hexadecimal number. */ \ + extern int __printf_fphex __P ((FILE *, const struct printf_info *, \ + const void **const)); \ + const void *ptr; \ + int function_done; \ + \ + if (fspec == NULL) \ + { \ + struct printf_info info = { prec: prec, \ + width: width, \ + spec: spec, \ + is_long_double: is_long_double, \ + is_short: is_short, \ + is_long: is_long, \ + alt: alt, \ + space: space, \ + left: left, \ + showsign: showsign, \ + group: group, \ + pad: pad, \ + extra: 0 }; \ + \ + if (is_long_double) \ + the_arg.pa_long_double = va_arg (ap, long double); \ + else \ + the_arg.pa_double = va_arg (ap, double); \ + ptr = (const void *) &the_arg; \ + \ + function_done = __printf_fphex (s, &info, &ptr); \ + } \ + else \ + { \ + ptr = (const void *) &args_value[fspec->data_arg]; \ + \ + function_done = __printf_fphex (s, &fspec->info, &ptr); \ + } \ + \ + if (function_done < 0) \ + /* Error in print handler. */ \ + return -1; \ + \ + done += function_done; \ + } \ + break; \ + \ LABEL (form_character): \ /* Character. */ \ if (is_long) \ @@ -881,7 +933,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) done = 0; grouping = (const char *) -1; #ifdef __va_copy - /* This macro will be available soon in gcc's <stdarg.h>. Me need it + /* This macro will be available soon in gcc's <stdarg.h>. We need it since on some systems `va_list' is not an integral type. */ __va_copy (ap_save, ap); #else @@ -1405,16 +1457,16 @@ printf_unknown (FILE *s, const struct printf_info *info, if (info->width != 0) { w = _itoa_word (info->width, workend + 1, 10, 0); - while (++w <= workend) - outchar (*w); + while (w <= workend) + outchar (*w++); } if (info->prec != -1) { outchar ('.'); w = _itoa_word (info->prec, workend + 1, 10, 0); - while (++w <= workend) - outchar (*w); + while (w <= workend) + outchar (*w++); } if (info->spec != '\0') diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c index f201c46775..8653088751 100644 --- a/stdio-common/vfscanf.c +++ b/stdio-common/vfscanf.c @@ -841,9 +841,9 @@ __vfscanf (FILE *s, const char *format, va_list argptr) if (flags & LONGDBL) { if (number_signed) - num.q = __strtoq_internal (wp, &tw, base, flags & GROUP); + num.q = __strtoll_internal (wp, &tw, base, flags & GROUP); else - num.uq = __strtouq_internal (wp, &tw, base, flags & GROUP); + num.uq = __strtoull_internal (wp, &tw, base, flags & GROUP); } else { |