From ccadf7b5346a3e21c692dfcbfcf38a63433bc36a Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sun, 14 Mar 2004 21:12:06 +0000 Subject: Update. 2004-03-14 Ulrich Drepper Make the non-_l functions wrappers around the _l functions. * include/monetary.h: Declare __vstrmon_l. * include/string.h: Add libc_hidden_proto for __strcoll_l and __strxfrm_l. * include/time.h: Define ptime_locale_status. Declare __strptime_internal. * include/wchar.h: Add libc_hidden_proto for __wcscoll_l and __wcsxfrm_l. * stdlib/strfmon.c: Move the code to strfmon_l.c. Add little wrapper around __vstrfmon_l. * stdlib/strfmon_l.c: Add real implementation. Split into new function __vstrfmon_l to allow calling it from strfmon. * stdlib/strtod.c: Move real code to strtod_l.c and add wrapper. * stdlib/strtod_l.c: Add real implementation. * stdlib/strtof.c: Adjust to changed strtod.c. * stdlib/strtof_l.c: Include strtod_l.c now. * stdlib/strtold.c: New file. * stdlib/strtold_l.c: Removed. * string/strcoll.c: Move real code to strcoll_l.c: Add wrapper. * string/strcoll_l.c: Add real implementation. * string/strxfrm.c: Move real code to strxfrm_l.c: Add wrapper. * string/strxfrm_l.c: Add real implementation. * sysdeps/generic/strtol.c: Move real implementation to strtol_l.c. Add wrappers. * sysdeps/generic/strtol_l.c: Add real implementation. * sysdeps/generic/strtold.c: Removed. * sysdeps/generic/strtold_l.c: New file. * sysdeps/generic/strtoll_l.c: Include strtol_l.c now. Adjust #defines. * sysdeps/generic/strtoul_l.c: Likewise. * sysdeps/generic/strtoull_l.c: Likewise. * sysdeps/generic/wcstol_l.c: Likewise. * sysdeps/generic/wcstoll_l.c: Likewise. * sysdeps/generic/wcstoul_l.c: Likewise. * sysdeps/generic/wcstoull_l.c: Likewise. * sysdeps/ieee754/ldbl-128/strtold.c: Removed. * sysdeps/ieee754/ldbl-128/strtold_l.c: New file. * sysdeps/ieee754/ldbl-96/strtold.c: Removed. * sysdeps/ieee754/ldbl-96/strtold_l.c: New file. * sysdeps/m68k/strtold.c: Removed. * sysdeps/m68k/strtold_l.c: New file. * time/strftime.c: Move real code to strftime_l.c. Add wrapper. * time/strftime_l.c: Add real implementation. * time/strptime.c: Move real code to strptime_l.c. Add wrapper. * time/strptime_l.c: Add real implementation. * time/wcsftime.c: Simplify since only wrappers are defined in strftime.c. * time/wcsftime_l.c: Include strftime_l.c. * wcsmbs/wcscoll.c: Simplify since the file is not used by wcscoll_l.c anymore. * wcsmbs/wcscoll_l.c: Include strcoll_l.c. * wcsmbs/wcsxfrm.c: Simplify since the file is not used by wcsxfrm_l.c anymore. * wcsmbs/wcsxfrm_l.c: Include strxfrm_l.c. * wcsmbs/wcstod.c: Prepare to include new strtod.c. * wcsmbs/wcstod_l.c: Include strtod_l.c. * wcsmbs/wcstof.c: Prepare to include new strtof.c. * wcsmbs/wcstof_l.c: Include strtof_l.c. * wcsmbs/wcstold.c: Prepare to include new strtold.c. * wcsmbs/wcstold_l.c: Include strtold_l.c. * locale/uselocale.c: Use _NL_CURRENT_LOCALE instead of __libc_tsd_get. * sysdeps/generic/strcasecmp.c: Optimize a bit. It's better to get a reference to the current locale and then use the _l functions. * sysdeps/generic/strncase.c: Likewise. --- stdlib/strfmon.c | 632 +-------------------- stdlib/strfmon_l.c | 636 ++++++++++++++++++++- stdlib/strtod.c | 1560 +--------------------------------------------------- stdlib/strtod_l.c | 1551 ++++++++++++++++++++++++++++++++++++++++++++++++++- stdlib/strtof.c | 37 +- stdlib/strtof_l.c | 28 +- stdlib/strtold.c | 35 ++ stdlib/strtold_l.c | 53 -- 8 files changed, 2282 insertions(+), 2250 deletions(-) create mode 100644 stdlib/strtold.c delete mode 100644 stdlib/strtold_l.c (limited to 'stdlib') diff --git a/stdlib/strfmon.c b/stdlib/strfmon.c index b17dc9823c..b11f95c9ac 100644 --- a/stdlib/strfmon.c +++ b/stdlib/strfmon.c @@ -19,645 +19,21 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include -#include -#include -#include #include -#ifdef USE_IN_LIBIO -# include "../libio/libioP.h" -# include "../libio/strfile.h" -#endif -#include #include -#include -#include -#include "../locale/localeinfo.h" +#include -#define out_char(Ch) \ - do { \ - if (dest >= s + maxsize - 1) \ - { \ - __set_errno (E2BIG); \ - va_end (ap); \ - return -1; \ - } \ - *dest++ = (Ch); \ - } while (0) - -#define out_string(String) \ - do { \ - const char *_s = (String); \ - while (*_s) \ - out_char (*_s++); \ - } while (0) - -#define out_nstring(String, N) \ - do { \ - int _n = (N); \ - const char *_s = (String); \ - while (_n-- > 0) \ - out_char (*_s++); \ - } while (0) - -#define to_digit(Ch) ((Ch) - '0') - - -/* We use this code also for the extended locale handling where the - function gets as an additional argument the locale which has to be - used. To access the values we have to redefine the _NL_CURRENT - macro. */ -#ifdef USE_IN_EXTENDED_LOCALE_MODEL -# undef _NL_CURRENT -# define _NL_CURRENT(category, item) \ - (current->values[_NL_ITEM_INDEX (item)].string) -#endif - -extern int __printf_fp (FILE *, const struct printf_info *, - const void *const *); -libc_hidden_proto (__printf_fp) -/* This function determines the number of digit groups in the output. - The definition is in printf_fp.c. */ -extern unsigned int __guess_grouping (unsigned int intdig_max, - const char *grouping, wchar_t sepchar); - - -/* We have to overcome some problems with this implementation. On the - one hand the strfmon() function is specified in XPG4 and of course - it has to follow this. But on the other hand POSIX.2 specifies - some information in the LC_MONETARY category which should be used, - too. Some of the information contradicts the information which can - be specified in format string. */ -#ifndef USE_IN_EXTENDED_LOCALE_MODEL ssize_t strfmon (char *s, size_t maxsize, const char *format, ...) -#else -ssize_t -__strfmon_l (char *s, size_t maxsize, __locale_t loc, const char *format, ...) -#endif { -#ifdef USE_IN_EXTENDED_LOCALE_MODEL - struct locale_data *current = loc->__locales[LC_MONETARY]; -#endif -#ifdef USE_IN_LIBIO - _IO_strfile f; -# ifdef _IO_MTSAFE_IO - _IO_lock_t lock; -# endif -#else - FILE f; -#endif - struct printf_info info; - va_list ap; /* Scan through the varargs. */ - char *dest; /* Pointer so copy the output. */ - const char *fmt; /* Pointer that walks through format. */ + va_list ap; va_start (ap, format); - dest = s; - fmt = format; - - /* Loop through the format-string. */ - while (*fmt != '\0') - { - /* The floating-point value to output. */ - union - { - double dbl; - __long_double_t ldbl; - } - fpnum; - int int_format; - int print_curr_symbol; - int left_prec; - int left_pad; - int right_prec; - int group; - char pad; - int is_long_double; - int p_sign_posn; - int n_sign_posn; - int sign_posn; - int other_sign_posn; - int left; - int is_negative; - int sep_by_space; - int other_sep_by_space; - int cs_precedes; - int other_cs_precedes; - const char *sign_string; - const char *other_sign_string; - int done; - const char *currency_symbol; - size_t currency_symbol_len; - int width; - char *startp; - const void *ptr; - char space_char; - - /* Process all character which do not introduce a format - specification. */ - if (*fmt != '%') - { - out_char (*fmt++); - continue; - } - - /* "%%" means a single '%' character. */ - if (fmt[1] == '%') - { - out_char (*++fmt); - ++fmt; - continue; - } - - /* Defaults for formatting. */ - int_format = 0; /* Use international curr. symbol */ - print_curr_symbol = 1; /* Print the currency symbol. */ - left_prec = -1; /* No left precision specified. */ - right_prec = -1; /* No right precision specified. */ - group = 1; /* Print digits grouped. */ - pad = ' '; /* Fill character is . */ - is_long_double = 0; /* Double argument by default. */ - p_sign_posn = -1; /* This indicates whether the */ - n_sign_posn = -1; /* '(' flag is given. */ - width = -1; /* No width specified so far. */ - left = 0; /* Right justified by default. */ - - /* Parse group characters. */ - while (1) - { - switch (*++fmt) - { - case '=': /* Set fill character. */ - pad = *++fmt; - if (pad == '\0') - { - /* Premature EOS. */ - __set_errno (EINVAL); - va_end (ap); - return -1; - } - continue; - case '^': /* Don't group digits. */ - group = 0; - continue; - case '+': /* Use +/- for sign of number. */ - if (n_sign_posn != -1) - { - __set_errno (EINVAL); - va_end (ap); - return -1; - } - p_sign_posn = *_NL_CURRENT (LC_MONETARY, P_SIGN_POSN); - n_sign_posn = *_NL_CURRENT (LC_MONETARY, N_SIGN_POSN); - continue; - case '(': /* Use ( ) for negative sign. */ - if (n_sign_posn != -1) - { - __set_errno (EINVAL); - va_end (ap); - return -1; - } - p_sign_posn = 0; - n_sign_posn = 0; - continue; - case '!': /* Don't print the currency symbol. */ - print_curr_symbol = 0; - continue; - case '-': /* Print left justified. */ - left = 1; - continue; - default: - /* Will stop the loop. */; - } - break; - } - - if (isdigit (*fmt)) - { - /* Parse field width. */ - width = to_digit (*fmt); - - while (isdigit (*++fmt)) - { - width *= 10; - width += to_digit (*fmt); - } - - /* If we don't have enough room for the demanded width we - can stop now and return an error. */ - if (dest + width >= s + maxsize) - { - __set_errno (E2BIG); - va_end (ap); - return -1; - } - } - - /* Recognize left precision. */ - if (*fmt == '#') - { - if (!isdigit (*++fmt)) - { - __set_errno (EINVAL); - va_end (ap); - return -1; - } - left_prec = to_digit (*fmt); - - while (isdigit (*++fmt)) - { - left_prec *= 10; - left_prec += to_digit (*fmt); - } - } - - /* Recognize right precision. */ - if (*fmt == '.') - { - if (!isdigit (*++fmt)) - { - __set_errno (EINVAL); - va_end (ap); - return -1; - } - right_prec = to_digit (*fmt); - - while (isdigit (*++fmt)) - { - right_prec *= 10; - right_prec += to_digit (*fmt); - } - } - - /* Handle modifier. This is an extension. */ - if (*fmt == 'L') - { - ++fmt; - is_long_double = 1; - } - - /* Handle format specifier. */ - char int_symbol[4]; - switch (*fmt++) - { - case 'i': { /* Use international currency symbol. */ - const char *int_curr_symbol; - - int_curr_symbol = _NL_CURRENT (LC_MONETARY, INT_CURR_SYMBOL); - strncpy(int_symbol, int_curr_symbol, 3); - int_symbol[3] = '\0'; - - currency_symbol_len = 3; - currency_symbol = &int_symbol[0]; - space_char = int_curr_symbol[3]; - int_format = 1; - break; - } - case 'n': /* Use national currency symbol. */ - currency_symbol = _NL_CURRENT (LC_MONETARY, CURRENCY_SYMBOL); - currency_symbol_len = strlen (currency_symbol); - space_char = ' '; - int_format = 0; - break; - default: /* Any unrecognized format is an error. */ - __set_errno (EINVAL); - va_end (ap); - return -1; - } - - /* If not specified by the format string now find the values for - the format specification. */ - if (p_sign_posn == -1) - p_sign_posn = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_SIGN_POSN : P_SIGN_POSN); - if (n_sign_posn == -1) - n_sign_posn = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_SIGN_POSN : N_SIGN_POSN); - - if (right_prec == -1) - { - right_prec = *_NL_CURRENT (LC_MONETARY, int_format ? INT_FRAC_DIGITS : FRAC_DIGITS); - - if (right_prec == CHAR_MAX) - right_prec = 2; - } - - /* If we have to print the digits grouped determine how many - extra characters this means. */ - if (group && left_prec != -1) - left_prec += __guess_grouping (left_prec, - _NL_CURRENT (LC_MONETARY, MON_GROUPING), - *_NL_CURRENT (LC_MONETARY, - MON_THOUSANDS_SEP)); - - /* Now it's time to get the value. */ - if (is_long_double == 1) - { - fpnum.ldbl = va_arg (ap, long double); - is_negative = fpnum.ldbl < 0; - if (is_negative) - fpnum.ldbl = -fpnum.ldbl; - } - else - { - fpnum.dbl = va_arg (ap, double); - is_negative = fpnum.dbl < 0; - if (is_negative) - fpnum.dbl = -fpnum.dbl; - } - - /* We now know the sign of the value and can determine the format. */ - if (is_negative) - { - sign_string = _NL_CURRENT (LC_MONETARY, NEGATIVE_SIGN); - /* If the locale does not specify a character for the - negative sign we use a '-'. */ - if (*sign_string == '\0') - sign_string = (const char *) "-"; - cs_precedes = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_CS_PRECEDES : N_CS_PRECEDES); - sep_by_space = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_SEP_BY_SPACE : N_SEP_BY_SPACE); - sign_posn = n_sign_posn; - - other_sign_string = _NL_CURRENT (LC_MONETARY, POSITIVE_SIGN); - other_cs_precedes = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_CS_PRECEDES : P_CS_PRECEDES); - other_sep_by_space = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_SEP_BY_SPACE : P_SEP_BY_SPACE); - other_sign_posn = p_sign_posn; - } - else - { - sign_string = _NL_CURRENT (LC_MONETARY, POSITIVE_SIGN); - cs_precedes = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_CS_PRECEDES : P_CS_PRECEDES); - sep_by_space = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_SEP_BY_SPACE : P_SEP_BY_SPACE); - sign_posn = p_sign_posn; - - other_sign_string = _NL_CURRENT (LC_MONETARY, NEGATIVE_SIGN); - if (*other_sign_string == '\0') - other_sign_string = (const char *) "-"; - other_cs_precedes = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_CS_PRECEDES : N_CS_PRECEDES); - other_sep_by_space = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_SEP_BY_SPACE : N_SEP_BY_SPACE); - other_sign_posn = n_sign_posn; - } - - /* Set default values for unspecified information. */ - if (cs_precedes != 0) - cs_precedes = 1; - if (other_cs_precedes != 0) - other_cs_precedes = 1; - if (sep_by_space == CHAR_MAX) - sep_by_space = 0; - if (other_sep_by_space == CHAR_MAX) - other_sep_by_space = 0; - if (sign_posn == CHAR_MAX) - sign_posn = 1; - if (other_sign_posn == CHAR_MAX) - other_sign_posn = 1; - - /* Check for degenerate cases */ - if (sep_by_space == 2) - { - if (sign_posn == 0 || - (sign_posn == 1 && !cs_precedes) || - (sign_posn == 2 && cs_precedes)) - /* sign and symbol are not adjacent, so no separator */ - sep_by_space = 0; - } - if (other_sep_by_space == 2) - { - if (other_sign_posn == 0 || - (other_sign_posn == 1 && !other_cs_precedes) || - (other_sign_posn == 2 && other_cs_precedes)) - /* sign and symbol are not adjacent, so no separator */ - other_sep_by_space = 0; - } - - /* Set the left precision and padding needed for alignment */ - if (left_prec == -1) - { - left_prec = 0; - left_pad = 0; - } - else - { - /* Set left_pad to number of spaces needed to align positive - and negative formats */ - - int left_bytes = 0; - int other_left_bytes = 0; - - /* Work out number of bytes for currency string and separator - preceding the value */ - if (cs_precedes) - { - left_bytes += currency_symbol_len; - if (sep_by_space != 0) - ++left_bytes; - } - - if (other_cs_precedes) - { - other_left_bytes += currency_symbol_len; - if (other_sep_by_space != 0) - ++other_left_bytes; - } - - /* Work out number of bytes for the sign (or left parenthesis) - preceding the value */ - if (sign_posn == 0 && is_negative) - ++left_bytes; - else if (sign_posn == 1) - left_bytes += strlen (sign_string); - else if (cs_precedes && (sign_posn == 3 || sign_posn == 4)) - left_bytes += strlen (sign_string); - - if (other_sign_posn == 0 && !is_negative) - ++other_left_bytes; - else if (other_sign_posn == 1) - other_left_bytes += strlen (other_sign_string); - else if (other_cs_precedes && - (other_sign_posn == 3 || other_sign_posn == 4)) - other_left_bytes += strlen (other_sign_string); - - /* Compare the number of bytes preceding the value for - each format, and set the padding accordingly */ - if (other_left_bytes > left_bytes) - left_pad = other_left_bytes - left_bytes; - else - left_pad = 0; - } - - /* Perhaps we'll someday make these things configurable so - better start using symbolic names now. */ -#define left_paren '(' -#define right_paren ')' - - startp = dest; /* Remember start so we can compute length. */ - - while (left_pad-- > 0) - out_char (' '); - - if (sign_posn == 0 && is_negative) - out_char (left_paren); - - if (cs_precedes) - { - if (sign_posn != 0 && sign_posn != 2 && sign_posn != 4 - && sign_posn != 5) - { - out_string (sign_string); - if (sep_by_space == 2) - out_char (' '); - } - - if (print_curr_symbol) - { - out_string (currency_symbol); - - if (sign_posn == 4) - { - if (sep_by_space == 2) - out_char (space_char); - out_string (sign_string); - if (sep_by_space == 1) - /* POSIX.2 and SUS are not clear on this case, but C99 - says a space follows the adjacent-symbol-and-sign */ - out_char (' '); - } - else - if (sep_by_space == 1) - out_char (space_char); - } - } - else - if (sign_posn != 0 && sign_posn != 2 && sign_posn != 3 - && sign_posn != 4 && sign_posn != 5) - out_string (sign_string); - - /* Print the number. */ -#ifdef USE_IN_LIBIO -# ifdef _IO_MTSAFE_IO - f._sbf._f._lock = &lock; -# endif - INTUSE(_IO_init) ((_IO_FILE *) &f, 0); - _IO_JUMPS ((struct _IO_FILE_plus *) &f) = &_IO_str_jumps; - INTUSE(_IO_str_init_static) ((_IO_strfile *) &f, dest, - (s + maxsize) - dest, dest); -#else - memset ((void *) &f, 0, sizeof (f)); - f.__magic = _IOMAGIC; - f.__mode.__write = 1; - /* The buffer size is one less than MAXLEN - so we have space for the null terminator. */ - f.__bufp = f.__buffer = (char *) dest; - f.__bufsize = (s + maxsize) - dest; - f.__put_limit = f.__buffer + f.__bufsize; - f.__get_limit = f.__buffer; - /* After the buffer is full (MAXLEN characters have been written), - any more characters written will go to the bit bucket. */ - f.__room_funcs = __default_room_functions; - f.__io_funcs.__write = NULL; - f.__seen = 1; -#endif - /* We clear the last available byte so we can find out whether - the numeric representation is too long. */ - s[maxsize - 1] = '\0'; - - info.prec = right_prec; - info.width = left_prec + (right_prec ? (right_prec + 1) : 0); - info.spec = 'f'; - info.is_long_double = is_long_double; - info.is_short = 0; - info.is_long = 0; - info.alt = 0; - info.space = 0; - info.left = 0; - info.showsign = 0; - info.group = group; - info.pad = pad; - info.extra = 1; /* This means use values from LC_MONETARY. */ - info.wide = 0; - - ptr = &fpnum; - done = __printf_fp ((FILE *) &f, &info, &ptr); - if (done < 0) - { - va_end (ap); - return -1; - } - - if (s[maxsize - 1] != '\0') - { - __set_errno (E2BIG); - return -1; - } - - dest += done; - - if (!cs_precedes) - { - if (sign_posn == 3) - { - if (sep_by_space == 1) - out_char (' '); - out_string (sign_string); - } - - if (print_curr_symbol) - { - if ((sign_posn == 3 && sep_by_space == 2) - || (sign_posn == 4 && sep_by_space == 1) - || (sign_posn == 2 && sep_by_space == 1) - || (sign_posn == 1 && sep_by_space == 1) - || (sign_posn == 0 && sep_by_space == 1)) - out_char (space_char); - out_nstring (currency_symbol, currency_symbol_len); - if (sign_posn == 4) - { - if (sep_by_space == 2) - out_char (' '); - out_string (sign_string); - } - } - } - - if (sign_posn == 2) - { - if (sep_by_space == 2) - out_char (' '); - out_string (sign_string); - } - - if (sign_posn == 0 && is_negative) - out_char (right_paren); - - /* Now test whether the output width is filled. */ - if (dest - startp < width) - { - if (left) - /* We simply have to fill using spaces. */ - do - out_char (' '); - while (dest - startp < width); - else - { - int dist = width - (dest - startp); - char *cp; - for (cp = dest - 1; cp >= startp; --cp) - cp[dist] = cp[0]; - - dest += dist; - - do - startp[--dist] = ' '; - while (dist > 0); - } - } - } - - /* Terminate the string. */ - *dest = '\0'; + ssize_t res = __vstrfmon_l (s, maxsize, _NL_CURRENT_LOCALE, format, ap); va_end (ap); - return dest - s; + return res; } diff --git a/stdlib/strfmon_l.c b/stdlib/strfmon_l.c index b0c82a337e..90076afc45 100644 --- a/stdlib/strfmon_l.c +++ b/stdlib/strfmon_l.c @@ -1,5 +1,5 @@ /* Formatting a monetary value according to the given locale. - Copyright (C) 1996, 1997, 2002 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper , 1996. @@ -18,7 +18,637 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define USE_IN_EXTENDED_LOCALE_MODEL 1 -#include +#include +#include +#include +#include +#include +#ifdef USE_IN_LIBIO +# include "../libio/libioP.h" +# include "../libio/strfile.h" +#endif +#include +#include +#include +#include +#include "../locale/localeinfo.h" + +#define out_char(Ch) \ + do { \ + if (dest >= s + maxsize - 1) \ + { \ + __set_errno (E2BIG); \ + va_end (ap); \ + return -1; \ + } \ + *dest++ = (Ch); \ + } while (0) + +#define out_string(String) \ + do { \ + const char *_s = (String); \ + while (*_s) \ + out_char (*_s++); \ + } while (0) + +#define out_nstring(String, N) \ + do { \ + int _n = (N); \ + const char *_s = (String); \ + while (_n-- > 0) \ + out_char (*_s++); \ + } while (0) + +#define to_digit(Ch) ((Ch) - '0') + + +/* We use this code also for the extended locale handling where the + function gets as an additional argument the locale which has to be + used. To access the values we have to redefine the _NL_CURRENT + macro. */ +#undef _NL_CURRENT +#define _NL_CURRENT(category, item) \ + (current->values[_NL_ITEM_INDEX (item)].string) + +extern int __printf_fp (FILE *, const struct printf_info *, + const void *const *); +libc_hidden_proto (__printf_fp) +/* This function determines the number of digit groups in the output. + The definition is in printf_fp.c. */ +extern unsigned int __guess_grouping (unsigned int intdig_max, + const char *grouping, wchar_t sepchar); + + +/* We have to overcome some problems with this implementation. On the + one hand the strfmon() function is specified in XPG4 and of course + it has to follow this. But on the other hand POSIX.2 specifies + some information in the LC_MONETARY category which should be used, + too. Some of the information contradicts the information which can + be specified in format string. */ +ssize_t +__vstrfmon_l (char *s, size_t maxsize, __locale_t loc, const char *format, + va_list ap) +{ + struct locale_data *current = loc->__locales[LC_MONETARY]; +#ifdef USE_IN_LIBIO + _IO_strfile f; +# ifdef _IO_MTSAFE_IO + _IO_lock_t lock; +# endif +#else + FILE f; +#endif + struct printf_info info; + char *dest; /* Pointer so copy the output. */ + const char *fmt; /* Pointer that walks through format. */ + + dest = s; + fmt = format; + + /* Loop through the format-string. */ + while (*fmt != '\0') + { + /* The floating-point value to output. */ + union + { + double dbl; + __long_double_t ldbl; + } + fpnum; + int int_format; + int print_curr_symbol; + int left_prec; + int left_pad; + int right_prec; + int group; + char pad; + int is_long_double; + int p_sign_posn; + int n_sign_posn; + int sign_posn; + int other_sign_posn; + int left; + int is_negative; + int sep_by_space; + int other_sep_by_space; + int cs_precedes; + int other_cs_precedes; + const char *sign_string; + const char *other_sign_string; + int done; + const char *currency_symbol; + size_t currency_symbol_len; + int width; + char *startp; + const void *ptr; + char space_char; + + /* Process all character which do not introduce a format + specification. */ + if (*fmt != '%') + { + out_char (*fmt++); + continue; + } + + /* "%%" means a single '%' character. */ + if (fmt[1] == '%') + { + out_char (*++fmt); + ++fmt; + continue; + } + + /* Defaults for formatting. */ + int_format = 0; /* Use international curr. symbol */ + print_curr_symbol = 1; /* Print the currency symbol. */ + left_prec = -1; /* No left precision specified. */ + right_prec = -1; /* No right precision specified. */ + group = 1; /* Print digits grouped. */ + pad = ' '; /* Fill character is . */ + is_long_double = 0; /* Double argument by default. */ + p_sign_posn = -1; /* This indicates whether the */ + n_sign_posn = -1; /* '(' flag is given. */ + width = -1; /* No width specified so far. */ + left = 0; /* Right justified by default. */ + + /* Parse group characters. */ + while (1) + { + switch (*++fmt) + { + case '=': /* Set fill character. */ + pad = *++fmt; + if (pad == '\0') + { + /* Premature EOS. */ + __set_errno (EINVAL); + return -1; + } + continue; + case '^': /* Don't group digits. */ + group = 0; + continue; + case '+': /* Use +/- for sign of number. */ + if (n_sign_posn != -1) + { + __set_errno (EINVAL); + return -1; + } + p_sign_posn = *_NL_CURRENT (LC_MONETARY, P_SIGN_POSN); + n_sign_posn = *_NL_CURRENT (LC_MONETARY, N_SIGN_POSN); + continue; + case '(': /* Use ( ) for negative sign. */ + if (n_sign_posn != -1) + { + __set_errno (EINVAL); + return -1; + } + p_sign_posn = 0; + n_sign_posn = 0; + continue; + case '!': /* Don't print the currency symbol. */ + print_curr_symbol = 0; + continue; + case '-': /* Print left justified. */ + left = 1; + continue; + default: + /* Will stop the loop. */; + } + break; + } + + if (isdigit (*fmt)) + { + /* Parse field width. */ + width = to_digit (*fmt); + + while (isdigit (*++fmt)) + { + width *= 10; + width += to_digit (*fmt); + } + + /* If we don't have enough room for the demanded width we + can stop now and return an error. */ + if (dest + width >= s + maxsize) + { + __set_errno (E2BIG); + return -1; + } + } + + /* Recognize left precision. */ + if (*fmt == '#') + { + if (!isdigit (*++fmt)) + { + __set_errno (EINVAL); + return -1; + } + left_prec = to_digit (*fmt); + + while (isdigit (*++fmt)) + { + left_prec *= 10; + left_prec += to_digit (*fmt); + } + } + + /* Recognize right precision. */ + if (*fmt == '.') + { + if (!isdigit (*++fmt)) + { + __set_errno (EINVAL); + return -1; + } + right_prec = to_digit (*fmt); + + while (isdigit (*++fmt)) + { + right_prec *= 10; + right_prec += to_digit (*fmt); + } + } + + /* Handle modifier. This is an extension. */ + if (*fmt == 'L') + { + ++fmt; + is_long_double = 1; + } + + /* Handle format specifier. */ + char int_symbol[4]; + switch (*fmt++) + { + case 'i': { /* Use international currency symbol. */ + const char *int_curr_symbol; + + int_curr_symbol = _NL_CURRENT (LC_MONETARY, INT_CURR_SYMBOL); + strncpy(int_symbol, int_curr_symbol, 3); + int_symbol[3] = '\0'; + + currency_symbol_len = 3; + currency_symbol = &int_symbol[0]; + space_char = int_curr_symbol[3]; + int_format = 1; + break; + } + case 'n': /* Use national currency symbol. */ + currency_symbol = _NL_CURRENT (LC_MONETARY, CURRENCY_SYMBOL); + currency_symbol_len = strlen (currency_symbol); + space_char = ' '; + int_format = 0; + break; + default: /* Any unrecognized format is an error. */ + __set_errno (EINVAL); + return -1; + } + + /* If not specified by the format string now find the values for + the format specification. */ + if (p_sign_posn == -1) + p_sign_posn = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_SIGN_POSN : P_SIGN_POSN); + if (n_sign_posn == -1) + n_sign_posn = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_SIGN_POSN : N_SIGN_POSN); + + if (right_prec == -1) + { + right_prec = *_NL_CURRENT (LC_MONETARY, int_format ? INT_FRAC_DIGITS : FRAC_DIGITS); + + if (right_prec == CHAR_MAX) + right_prec = 2; + } + + /* If we have to print the digits grouped determine how many + extra characters this means. */ + if (group && left_prec != -1) + left_prec += __guess_grouping (left_prec, + _NL_CURRENT (LC_MONETARY, MON_GROUPING), + *_NL_CURRENT (LC_MONETARY, + MON_THOUSANDS_SEP)); + + /* Now it's time to get the value. */ + if (is_long_double == 1) + { + fpnum.ldbl = va_arg (ap, long double); + is_negative = fpnum.ldbl < 0; + if (is_negative) + fpnum.ldbl = -fpnum.ldbl; + } + else + { + fpnum.dbl = va_arg (ap, double); + is_negative = fpnum.dbl < 0; + if (is_negative) + fpnum.dbl = -fpnum.dbl; + } + + /* We now know the sign of the value and can determine the format. */ + if (is_negative) + { + sign_string = _NL_CURRENT (LC_MONETARY, NEGATIVE_SIGN); + /* If the locale does not specify a character for the + negative sign we use a '-'. */ + if (*sign_string == '\0') + sign_string = (const char *) "-"; + cs_precedes = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_CS_PRECEDES : N_CS_PRECEDES); + sep_by_space = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_SEP_BY_SPACE : N_SEP_BY_SPACE); + sign_posn = n_sign_posn; + + other_sign_string = _NL_CURRENT (LC_MONETARY, POSITIVE_SIGN); + other_cs_precedes = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_CS_PRECEDES : P_CS_PRECEDES); + other_sep_by_space = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_SEP_BY_SPACE : P_SEP_BY_SPACE); + other_sign_posn = p_sign_posn; + } + else + { + sign_string = _NL_CURRENT (LC_MONETARY, POSITIVE_SIGN); + cs_precedes = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_CS_PRECEDES : P_CS_PRECEDES); + sep_by_space = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_SEP_BY_SPACE : P_SEP_BY_SPACE); + sign_posn = p_sign_posn; + + other_sign_string = _NL_CURRENT (LC_MONETARY, NEGATIVE_SIGN); + if (*other_sign_string == '\0') + other_sign_string = (const char *) "-"; + other_cs_precedes = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_CS_PRECEDES : N_CS_PRECEDES); + other_sep_by_space = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_SEP_BY_SPACE : N_SEP_BY_SPACE); + other_sign_posn = n_sign_posn; + } + + /* Set default values for unspecified information. */ + if (cs_precedes != 0) + cs_precedes = 1; + if (other_cs_precedes != 0) + other_cs_precedes = 1; + if (sep_by_space == CHAR_MAX) + sep_by_space = 0; + if (other_sep_by_space == CHAR_MAX) + other_sep_by_space = 0; + if (sign_posn == CHAR_MAX) + sign_posn = 1; + if (other_sign_posn == CHAR_MAX) + other_sign_posn = 1; + + /* Check for degenerate cases */ + if (sep_by_space == 2) + { + if (sign_posn == 0 || + (sign_posn == 1 && !cs_precedes) || + (sign_posn == 2 && cs_precedes)) + /* sign and symbol are not adjacent, so no separator */ + sep_by_space = 0; + } + if (other_sep_by_space == 2) + { + if (other_sign_posn == 0 || + (other_sign_posn == 1 && !other_cs_precedes) || + (other_sign_posn == 2 && other_cs_precedes)) + /* sign and symbol are not adjacent, so no separator */ + other_sep_by_space = 0; + } + + /* Set the left precision and padding needed for alignment */ + if (left_prec == -1) + { + left_prec = 0; + left_pad = 0; + } + else + { + /* Set left_pad to number of spaces needed to align positive + and negative formats */ + + int left_bytes = 0; + int other_left_bytes = 0; + + /* Work out number of bytes for currency string and separator + preceding the value */ + if (cs_precedes) + { + left_bytes += currency_symbol_len; + if (sep_by_space != 0) + ++left_bytes; + } + + if (other_cs_precedes) + { + other_left_bytes += currency_symbol_len; + if (other_sep_by_space != 0) + ++other_left_bytes; + } + + /* Work out number of bytes for the sign (or left parenthesis) + preceding the value */ + if (sign_posn == 0 && is_negative) + ++left_bytes; + else if (sign_posn == 1) + left_bytes += strlen (sign_string); + else if (cs_precedes && (sign_posn == 3 || sign_posn == 4)) + left_bytes += strlen (sign_string); + + if (other_sign_posn == 0 && !is_negative) + ++other_left_bytes; + else if (other_sign_posn == 1) + other_left_bytes += strlen (other_sign_string); + else if (other_cs_precedes && + (other_sign_posn == 3 || other_sign_posn == 4)) + other_left_bytes += strlen (other_sign_string); + + /* Compare the number of bytes preceding the value for + each format, and set the padding accordingly */ + if (other_left_bytes > left_bytes) + left_pad = other_left_bytes - left_bytes; + else + left_pad = 0; + } + + /* Perhaps we'll someday make these things configurable so + better start using symbolic names now. */ +#define left_paren '(' +#define right_paren ')' + + startp = dest; /* Remember start so we can compute length. */ + + while (left_pad-- > 0) + out_char (' '); + + if (sign_posn == 0 && is_negative) + out_char (left_paren); + + if (cs_precedes) + { + if (sign_posn != 0 && sign_posn != 2 && sign_posn != 4 + && sign_posn != 5) + { + out_string (sign_string); + if (sep_by_space == 2) + out_char (' '); + } + + if (print_curr_symbol) + { + out_string (currency_symbol); + + if (sign_posn == 4) + { + if (sep_by_space == 2) + out_char (space_char); + out_string (sign_string); + if (sep_by_space == 1) + /* POSIX.2 and SUS are not clear on this case, but C99 + says a space follows the adjacent-symbol-and-sign */ + out_char (' '); + } + else + if (sep_by_space == 1) + out_char (space_char); + } + } + else + if (sign_posn != 0 && sign_posn != 2 && sign_posn != 3 + && sign_posn != 4 && sign_posn != 5) + out_string (sign_string); + + /* Print the number. */ +#ifdef USE_IN_LIBIO +# ifdef _IO_MTSAFE_IO + f._sbf._f._lock = &lock; +# endif + INTUSE(_IO_init) ((_IO_FILE *) &f, 0); + _IO_JUMPS ((struct _IO_FILE_plus *) &f) = &_IO_str_jumps; + INTUSE(_IO_str_init_static) ((_IO_strfile *) &f, dest, + (s + maxsize) - dest, dest); +#else + memset ((void *) &f, 0, sizeof (f)); + f.__magic = _IOMAGIC; + f.__mode.__write = 1; + /* The buffer size is one less than MAXLEN + so we have space for the null terminator. */ + f.__bufp = f.__buffer = (char *) dest; + f.__bufsize = (s + maxsize) - dest; + f.__put_limit = f.__buffer + f.__bufsize; + f.__get_limit = f.__buffer; + /* After the buffer is full (MAXLEN characters have been written), + any more characters written will go to the bit bucket. */ + f.__room_funcs = __default_room_functions; + f.__io_funcs.__write = NULL; + f.__seen = 1; +#endif + /* We clear the last available byte so we can find out whether + the numeric representation is too long. */ + s[maxsize - 1] = '\0'; + + info.prec = right_prec; + info.width = left_prec + (right_prec ? (right_prec + 1) : 0); + info.spec = 'f'; + info.is_long_double = is_long_double; + info.is_short = 0; + info.is_long = 0; + info.alt = 0; + info.space = 0; + info.left = 0; + info.showsign = 0; + info.group = group; + info.pad = pad; + info.extra = 1; /* This means use values from LC_MONETARY. */ + info.wide = 0; + + ptr = &fpnum; + done = __printf_fp ((FILE *) &f, &info, &ptr); + if (done < 0) + return -1; + + if (s[maxsize - 1] != '\0') + { + __set_errno (E2BIG); + return -1; + } + + dest += done; + + if (!cs_precedes) + { + if (sign_posn == 3) + { + if (sep_by_space == 1) + out_char (' '); + out_string (sign_string); + } + + if (print_curr_symbol) + { + if ((sign_posn == 3 && sep_by_space == 2) + || (sign_posn == 4 && sep_by_space == 1) + || (sign_posn == 2 && sep_by_space == 1) + || (sign_posn == 1 && sep_by_space == 1) + || (sign_posn == 0 && sep_by_space == 1)) + out_char (space_char); + out_nstring (currency_symbol, currency_symbol_len); + if (sign_posn == 4) + { + if (sep_by_space == 2) + out_char (' '); + out_string (sign_string); + } + } + } + + if (sign_posn == 2) + { + if (sep_by_space == 2) + out_char (' '); + out_string (sign_string); + } + + if (sign_posn == 0 && is_negative) + out_char (right_paren); + + /* Now test whether the output width is filled. */ + if (dest - startp < width) + { + if (left) + /* We simply have to fill using spaces. */ + do + out_char (' '); + while (dest - startp < width); + else + { + int dist = width - (dest - startp); + char *cp; + for (cp = dest - 1; cp >= startp; --cp) + cp[dist] = cp[0]; + + dest += dist; + + do + startp[--dist] = ' '; + while (dist > 0); + } + } + } + + /* Terminate the string. */ + *dest = '\0'; + + return dest - s; +} + +ssize_t +__strfmon_l (char *s, size_t maxsize, __locale_t loc, const char *format, ...) +{ + va_list ap; + + va_start (ap, format); + + ssize_t res = __vstrfmon_l (s, maxsize, loc, format, ap); + + va_end (ap); + + return res; +} weak_alias (__strfmon_l, strfmon_l) diff --git a/stdlib/strtod.c b/stdlib/strtod.c index 63d7a4d5bb..1d4e4a4c29 100644 --- a/stdlib/strtod.c +++ b/stdlib/strtod.c @@ -1,6 +1,6 @@ /* Read decimal floating point numbers. This file is part of the GNU C Library. - Copyright (C) 1995-2002, 2003 Free Software Foundation, Inc. + Copyright (C) 1995-2002, 2003, 2004 Free Software Foundation, Inc. Contributed by Ulrich Drepper , 1995. The GNU C Library is free software; you can redistribute it and/or @@ -18,1579 +18,53 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -/* Configuration part. These macros are defined by `strtold.c', - `strtof.c', `wcstod.c', `wcstold.c', and `wcstof.c' to produce the - `long double' and `float' versions of the reader. */ +#include +#include +#include + + #ifndef FLOAT -# define FLOAT double -# define FLT DBL +# define FLOAT double # ifdef USE_WIDE_CHAR -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define STRTOF __wcstod_l -# else -# define STRTOF wcstod -# endif +# define STRTOF wcstod +# define STRTOF_L __wcstod_l # else -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define STRTOF __strtod_l -# else -# define STRTOF strtod -# endif +# define STRTOF strtod +# define STRTOF_L __strtod_l # endif -# define MPN2FLOAT __mpn_construct_double -# define FLOAT_HUGE_VAL HUGE_VAL -# define SET_MANTISSA(flt, mant) \ - do { union ieee754_double u; \ - u.d = (flt); \ - if ((mant & 0xfffffffffffffULL) == 0) \ - mant = 0x8000000000000ULL; \ - u.ieee.mantissa0 = ((mant) >> 32) & 0xfffff; \ - u.ieee.mantissa1 = (mant) & 0xffffffff; \ - (flt) = u.d; \ - } while (0) -#endif -/* End of configuration part. */ - -#include -#include -#include -#include -#include "../locale/localeinfo.h" -#include -#include -#include -#include - -/* The gmp headers need some configuration frobs. */ -#define HAVE_ALLOCA 1 - -/* Include gmp-mparam.h first, such that definitions of _SHORT_LIMB - and _LONG_LONG_LIMB in it can take effect into gmp.h. */ -#include -#include -#include -#include -#include "fpioconst.h" - -#define NDEBUG 1 -#include - - -/* We use this code also for the extended locale handling where the - function gets as an additional argument the locale which has to be - used. To access the values we have to redefine the _NL_CURRENT - macro. */ -#ifdef USE_IN_EXTENDED_LOCALE_MODEL -# undef _NL_CURRENT -# define _NL_CURRENT(category, item) \ - (current->values[_NL_ITEM_INDEX (item)].string) -# define LOCALE_PARAM , loc -# define LOCALE_PARAM_DECL __locale_t loc; -#else -# define LOCALE_PARAM -# define LOCALE_PARAM_DECL -#endif - -#if defined _LIBC || defined HAVE_WCHAR_H -# include #endif #ifdef USE_WIDE_CHAR # include # define STRING_TYPE wchar_t -# define CHAR_TYPE wint_t -# define L_(Ch) L##Ch -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define ISSPACE(Ch) __iswspace_l ((Ch), loc) -# define ISDIGIT(Ch) __iswdigit_l ((Ch), loc) -# define ISXDIGIT(Ch) __iswxdigit_l ((Ch), loc) -# define TOLOWER(Ch) __towlower_l ((Ch), loc) -# define STRNCASECMP(S1, S2, N) __wcsncasecmp_l ((S1), (S2), (N), loc) -# define STRTOULL(S, E, B) ____wcstoull_l_internal ((S), (E), (B), 0, loc) -# else -# define ISSPACE(Ch) iswspace (Ch) -# define ISDIGIT(Ch) iswdigit (Ch) -# define ISXDIGIT(Ch) iswxdigit (Ch) -# define TOLOWER(Ch) towlower (Ch) -# define STRNCASECMP(S1, S2, N) __wcsncasecmp ((S1), (S2), (N)) -# define STRTOULL(S, E, B) __wcstoull_internal ((S), (E), (B), 0) -# endif #else # define STRING_TYPE char -# define CHAR_TYPE char -# define L_(Ch) Ch -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define ISSPACE(Ch) __isspace_l ((Ch), loc) -# define ISDIGIT(Ch) __isdigit_l ((Ch), loc) -# define ISXDIGIT(Ch) __isxdigit_l ((Ch), loc) -# define TOLOWER(Ch) __tolower_l ((Ch), loc) -# define STRNCASECMP(S1, S2, N) __strncasecmp_l ((S1), (S2), (N), loc) -# define STRTOULL(S, E, B) ____strtoull_l_internal ((S), (E), (B), 0, loc) -# else -# define ISSPACE(Ch) isspace (Ch) -# define ISDIGIT(Ch) isdigit (Ch) -# define ISXDIGIT(Ch) isxdigit (Ch) -# define TOLOWER(Ch) tolower (Ch) -# define STRNCASECMP(S1, S2, N) __strncasecmp ((S1), (S2), (N)) -# define STRTOULL(S, E, B) __strtoull_internal ((S), (E), 0, (B)) -# endif -#endif - - -/* Constants we need from float.h; select the set for the FLOAT precision. */ -#define MANT_DIG PASTE(FLT,_MANT_DIG) -#define DIG PASTE(FLT,_DIG) -#define MAX_EXP PASTE(FLT,_MAX_EXP) -#define MIN_EXP PASTE(FLT,_MIN_EXP) -#define MAX_10_EXP PASTE(FLT,_MAX_10_EXP) -#define MIN_10_EXP PASTE(FLT,_MIN_10_EXP) - -/* Extra macros required to get FLT expanded before the pasting. */ -#define PASTE(a,b) PASTE1(a,b) -#define PASTE1(a,b) a##b - -/* Function to construct a floating point number from an MP integer - containing the fraction bits, a base 2 exponent, and a sign flag. */ -extern FLOAT MPN2FLOAT (mp_srcptr mpn, int exponent, int negative); - -/* Definitions according to limb size used. */ -#if BITS_PER_MP_LIMB == 32 -# define MAX_DIG_PER_LIMB 9 -# define MAX_FAC_PER_LIMB 1000000000UL -#elif BITS_PER_MP_LIMB == 64 -# define MAX_DIG_PER_LIMB 19 -# define MAX_FAC_PER_LIMB 10000000000000000000ULL -#else -# error "mp_limb_t size " BITS_PER_MP_LIMB "not accounted for" #endif - -/* Local data structure. */ -static const mp_limb_t _tens_in_limb[MAX_DIG_PER_LIMB + 1] = -{ 0, 10, 100, - 1000, 10000, 100000L, - 1000000L, 10000000L, 100000000L, - 1000000000L -#if BITS_PER_MP_LIMB > 32 - , 10000000000ULL, 100000000000ULL, - 1000000000000ULL, 10000000000000ULL, 100000000000000ULL, - 1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL, - 1000000000000000000ULL, 10000000000000000000ULL -#endif -#if BITS_PER_MP_LIMB > 64 - #error "Need to expand tens_in_limb table to" MAX_DIG_PER_LIMB -#endif -}; - -#ifndef howmany -#define howmany(x,y) (((x)+((y)-1))/(y)) -#endif -#define SWAP(x, y) ({ typeof(x) _tmp = x; x = y; y = _tmp; }) - -#define NDIG (MAX_10_EXP - MIN_10_EXP + 2 * MANT_DIG) -#define HEXNDIG ((MAX_EXP - MIN_EXP + 7) / 8 + 2 * MANT_DIG) -#define RETURN_LIMB_SIZE howmany (MANT_DIG, BITS_PER_MP_LIMB) - -#define RETURN(val,end) \ - do { if (endptr != NULL) *endptr = (STRING_TYPE *) (end); \ - return val; } while (0) - -/* Maximum size necessary for mpn integers to hold floating point numbers. */ -#define MPNSIZE (howmany (MAX_EXP + 2 * MANT_DIG, BITS_PER_MP_LIMB) \ - + 2) -/* Declare an mpn integer variable that big. */ -#define MPN_VAR(name) mp_limb_t name[MPNSIZE]; mp_size_t name##size -/* Copy an mpn integer value. */ -#define MPN_ASSIGN(dst, src) \ - memcpy (dst, src, (dst##size = src##size) * sizeof (mp_limb_t)) - - -/* Return a floating point number of the needed type according to the given - multi-precision number after possible rounding. */ -static FLOAT -round_and_return (mp_limb_t *retval, int exponent, int negative, - mp_limb_t round_limb, mp_size_t round_bit, int more_bits) -{ - if (exponent < MIN_EXP - 1) - { - mp_size_t shift = MIN_EXP - 1 - exponent; - - if (shift > MANT_DIG) - { - __set_errno (EDOM); - return 0.0; - } - - more_bits |= (round_limb & ((((mp_limb_t) 1) << round_bit) - 1)) != 0; - if (shift == MANT_DIG) - /* This is a special case to handle the very seldom case where - the mantissa will be empty after the shift. */ - { - int i; - - round_limb = retval[RETURN_LIMB_SIZE - 1]; - round_bit = (MANT_DIG - 1) % BITS_PER_MP_LIMB; - for (i = 0; i < RETURN_LIMB_SIZE; ++i) - more_bits |= retval[i] != 0; - MPN_ZERO (retval, RETURN_LIMB_SIZE); - } - else if (shift >= BITS_PER_MP_LIMB) - { - int i; - - round_limb = retval[(shift - 1) / BITS_PER_MP_LIMB]; - round_bit = (shift - 1) % BITS_PER_MP_LIMB; - for (i = 0; i < (shift - 1) / BITS_PER_MP_LIMB; ++i) - more_bits |= retval[i] != 0; - more_bits |= ((round_limb & ((((mp_limb_t) 1) << round_bit) - 1)) - != 0); - - (void) __mpn_rshift (retval, &retval[shift / BITS_PER_MP_LIMB], - RETURN_LIMB_SIZE - (shift / BITS_PER_MP_LIMB), - shift % BITS_PER_MP_LIMB); - MPN_ZERO (&retval[RETURN_LIMB_SIZE - (shift / BITS_PER_MP_LIMB)], - shift / BITS_PER_MP_LIMB); - } - else if (shift > 0) - { - round_limb = retval[0]; - round_bit = shift - 1; - (void) __mpn_rshift (retval, retval, RETURN_LIMB_SIZE, shift); - } - /* This is a hook for the m68k long double format, where the - exponent bias is the same for normalized and denormalized - numbers. */ -#ifndef DENORM_EXP -# define DENORM_EXP (MIN_EXP - 2) -#endif - exponent = DENORM_EXP; - } - - if ((round_limb & (((mp_limb_t) 1) << round_bit)) != 0 - && (more_bits || (retval[0] & 1) != 0 - || (round_limb & ((((mp_limb_t) 1) << round_bit) - 1)) != 0)) - { - mp_limb_t cy = __mpn_add_1 (retval, retval, RETURN_LIMB_SIZE, 1); - - if (((MANT_DIG % BITS_PER_MP_LIMB) == 0 && cy) || - ((MANT_DIG % BITS_PER_MP_LIMB) != 0 && - (retval[RETURN_LIMB_SIZE - 1] - & (((mp_limb_t) 1) << (MANT_DIG % BITS_PER_MP_LIMB))) != 0)) - { - ++exponent; - (void) __mpn_rshift (retval, retval, RETURN_LIMB_SIZE, 1); - retval[RETURN_LIMB_SIZE - 1] - |= ((mp_limb_t) 1) << ((MANT_DIG - 1) % BITS_PER_MP_LIMB); - } - else if (exponent == DENORM_EXP - && (retval[RETURN_LIMB_SIZE - 1] - & (((mp_limb_t) 1) << ((MANT_DIG - 1) % BITS_PER_MP_LIMB))) - != 0) - /* The number was denormalized but now normalized. */ - exponent = MIN_EXP - 1; - } - - if (exponent > MAX_EXP) - return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL; - - return MPN2FLOAT (retval, exponent, negative); -} - - -/* Read a multi-precision integer starting at STR with exactly DIGCNT digits - into N. Return the size of the number limbs in NSIZE at the first - character od the string that is not part of the integer as the function - value. If the EXPONENT is small enough to be taken as an additional - factor for the resulting number (see code) multiply by it. */ -static const STRING_TYPE * -str_to_mpn (const STRING_TYPE *str, int digcnt, mp_limb_t *n, mp_size_t *nsize, - int *exponent -#ifndef USE_WIDE_CHAR - , const char *decimal, size_t decimal_len, const char *thousands -#endif - - ) -{ - /* Number of digits for actual limb. */ - int cnt = 0; - mp_limb_t low = 0; - mp_limb_t start; - - *nsize = 0; - assert (digcnt > 0); - do - { - if (cnt == MAX_DIG_PER_LIMB) - { - if (*nsize == 0) - { - n[0] = low; - *nsize = 1; - } - else - { - mp_limb_t cy; - cy = __mpn_mul_1 (n, n, *nsize, MAX_FAC_PER_LIMB); - cy += __mpn_add_1 (n, n, *nsize, low); - if (cy != 0) - { - n[*nsize] = cy; - ++(*nsize); - } - } - cnt = 0; - low = 0; - } - - /* There might be thousands separators or radix characters in - the string. But these all can be ignored because we know the - format of the number is correct and we have an exact number - of characters to read. */ -#ifdef USE_WIDE_CHAR - if (*str < L'0' || *str > L'9') - ++str; -#else - if (*str < '0' || *str > '9') - { - int inner = 0; - if (thousands != NULL && *str == *thousands - && ({ for (inner = 1; thousands[inner] != '\0'; ++inner) - if (thousands[inner] != str[inner]) - break; - thousands[inner] == '\0'; })) - str += inner; - else - str += decimal_len; - } -#endif - low = low * 10 + *str++ - L_('0'); - ++cnt; - } - while (--digcnt > 0); - - if (*exponent > 0 && cnt + *exponent <= MAX_DIG_PER_LIMB) - { - low *= _tens_in_limb[*exponent]; - start = _tens_in_limb[cnt + *exponent]; - *exponent = 0; - } - else - start = _tens_in_limb[cnt]; - - if (*nsize == 0) - { - n[0] = low; - *nsize = 1; - } - else - { - mp_limb_t cy; - cy = __mpn_mul_1 (n, n, *nsize, start); - cy += __mpn_add_1 (n, n, *nsize, low); - if (cy != 0) - n[(*nsize)++] = cy; - } - - return str; -} - - -/* Shift {PTR, SIZE} COUNT bits to the left, and fill the vacated bits - with the COUNT most significant bits of LIMB. - - Tege doesn't like this function so I have to write it here myself. :) - --drepper */ -static inline void -__attribute ((always_inline)) -__mpn_lshift_1 (mp_limb_t *ptr, mp_size_t size, unsigned int count, - mp_limb_t limb) -{ - if (__builtin_constant_p (count) && count == BITS_PER_MP_LIMB) - { - /* Optimize the case of shifting by exactly a word: - just copy words, with no actual bit-shifting. */ - mp_size_t i; - for (i = size - 1; i > 0; --i) - ptr[i] = ptr[i - 1]; - ptr[0] = limb; - } - else - { - (void) __mpn_lshift (ptr, ptr, size, count); - ptr[0] |= limb >> (BITS_PER_MP_LIMB - count); - } -} - - #define INTERNAL(x) INTERNAL1(x) #define INTERNAL1(x) __##x##_internal -/* This file defines a function to check for correct grouping. */ -#include "grouping.h" - -/* Return a floating point number with the value of the given string NPTR. - Set *ENDPTR to the character after the last used one. If the number is - smaller than the smallest representable number, set `errno' to ERANGE and - return 0.0. If the number is too big to be represented, set `errno' to - ERANGE and return HUGE_VAL with the appropriate sign. */ FLOAT -INTERNAL (STRTOF) (nptr, endptr, group LOCALE_PARAM) +INTERNAL (STRTOF) (nptr, endptr, group) const STRING_TYPE *nptr; STRING_TYPE **endptr; int group; - LOCALE_PARAM_DECL { - int negative; /* The sign of the number. */ - MPN_VAR (num); /* MP representation of the number. */ - int exponent; /* Exponent of the number. */ - - /* Numbers starting `0X' or `0x' have to be processed with base 16. */ - int base = 10; - - /* When we have to compute fractional digits we form a fraction with a - second multi-precision number (and we sometimes need a second for - temporary results). */ - MPN_VAR (den); - - /* Representation for the return value. */ - mp_limb_t retval[RETURN_LIMB_SIZE]; - /* Number of bits currently in result value. */ - int bits; - - /* Running pointer after the last character processed in the string. */ - const STRING_TYPE *cp, *tp; - /* Start of significant part of the number. */ - const STRING_TYPE *startp, *start_of_digits; - /* Points at the character following the integer and fractional digits. */ - const STRING_TYPE *expp; - /* Total number of digit and number of digits in integer part. */ - int dig_no, int_no, lead_zero; - /* Contains the last character read. */ - CHAR_TYPE c; - -/* We should get wint_t from , but not all GCC versions define it - there. So define it ourselves if it remains undefined. */ -#ifndef _WINT_T - typedef unsigned int wint_t; -#endif - /* The radix character of the current locale. */ -#ifdef USE_WIDE_CHAR - wchar_t decimal; -#else - const char *decimal; - size_t decimal_len; -#endif - /* The thousands character of the current locale. */ -#ifdef USE_WIDE_CHAR - wchar_t thousands = L'\0'; -#else - const char *thousands = NULL; -#endif - /* The numeric grouping specification of the current locale, - in the format described in . */ - const char *grouping; - /* Used in several places. */ - int cnt; - -#ifdef USE_IN_EXTENDED_LOCALE_MODEL - struct locale_data *current = loc->__locales[LC_NUMERIC]; -#endif - - if (group) - { - grouping = _NL_CURRENT (LC_NUMERIC, GROUPING); - if (*grouping <= 0 || *grouping == CHAR_MAX) - grouping = NULL; - else - { - /* Figure out the thousands separator character. */ -#ifdef USE_WIDE_CHAR - thousands = _NL_CURRENT_WORD (LC_NUMERIC, - _NL_NUMERIC_THOUSANDS_SEP_WC); - if (thousands == L'\0') - grouping = NULL; -#else - thousands = _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP); - if (*thousands == '\0') - { - thousands = NULL; - grouping = NULL; - } -#endif - } - } - else - grouping = NULL; - - /* Find the locale's decimal point character. */ -#ifdef USE_WIDE_CHAR - decimal = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC); - assert (decimal != L'\0'); -# define decimal_len 1 -#else - decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT); - decimal_len = strlen (decimal); - assert (decimal_len > 0); -#endif - - /* Prepare number representation. */ - exponent = 0; - negative = 0; - bits = 0; - - /* Parse string to get maximal legal prefix. We need the number of - characters of the integer part, the fractional part and the exponent. */ - cp = nptr - 1; - /* Ignore leading white space. */ - do - c = *++cp; - while (ISSPACE (c)); - - /* Get sign of the result. */ - if (c == L_('-')) - { - negative = 1; - c = *++cp; - } - else if (c == L_('+')) - c = *++cp; - - /* Return 0.0 if no legal string is found. - No character is used even if a sign was found. */ -#ifdef USE_WIDE_CHAR - if (c == (wint_t) decimal - && (wint_t) cp[1] >= L'0' && (wint_t) cp[1] <= L'9') - { - /* We accept it. This funny construct is here only to indent - the code directly. */ - } -#else - for (cnt = 0; decimal[cnt] != '\0'; ++cnt) - if (cp[cnt] != decimal[cnt]) - break; - if (decimal[cnt] == '\0' && cp[cnt] >= '0' && cp[cnt] <= '9') - { - /* We accept it. This funny construct is here only to indent - the code directly. */ - } -#endif - else if (c < L_('0') || c > L_('9')) - { - /* Check for `INF' or `INFINITY'. */ - if (TOLOWER (c) == L_('i') && STRNCASECMP (cp, L_("inf"), 3) == 0) - { - /* Return +/- infinity. */ - if (endptr != NULL) - *endptr = (STRING_TYPE *) - (cp + (STRNCASECMP (cp + 3, L_("inity"), 5) == 0 - ? 8 : 3)); - - return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL; - } - - if (TOLOWER (c) == L_('n') && STRNCASECMP (cp, L_("nan"), 3) == 0) - { - /* Return NaN. */ - FLOAT retval = NAN; - - cp += 3; - - /* Match `(n-char-sequence-digit)'. */ - if (*cp == L_('(')) - { - const STRING_TYPE *startp = cp; - do - ++cp; - while ((*cp >= L_('0') && *cp <= L_('9')) - || (TOLOWER (*cp) >= L_('a') && TOLOWER (*cp) <= L_('z')) - || *cp == L_('_')); - - if (*cp != L_(')')) - /* The closing brace is missing. Only match the NAN - part. */ - cp = startp; - else - { - /* This is a system-dependent way to specify the - bitmask used for the NaN. We expect it to be - a number which is put in the mantissa of the - number. */ - STRING_TYPE *endp; - unsigned long long int mant; - - mant = STRTOULL (startp + 1, &endp, 0); - if (endp == cp) - SET_MANTISSA (retval, mant); - } - } - - if (endptr != NULL) - *endptr = (STRING_TYPE *) cp; - - return retval; - } - - /* It is really a text we do not recognize. */ - RETURN (0.0, nptr); - } - - /* First look whether we are faced with a hexadecimal number. */ - if (c == L_('0') && TOLOWER (cp[1]) == L_('x')) - { - /* Okay, it is a hexa-decimal number. Remember this and skip - the characters. BTW: hexadecimal numbers must not be - grouped. */ - base = 16; - cp += 2; - c = *cp; - grouping = NULL; - } - - /* Record the start of the digits, in case we will check their grouping. */ - start_of_digits = startp = cp; - - /* Ignore leading zeroes. This helps us to avoid useless computations. */ -#ifdef USE_WIDE_CHAR - while (c == L'0' || ((wint_t) thousands != L'\0' && c == (wint_t) thousands)) - c = *++cp; -#else - if (thousands == NULL) - while (c == '0') - c = *++cp; - else - { - /* We also have the multibyte thousands string. */ - while (1) - { - if (c != '0') - { - for (cnt = 0; thousands[cnt] != '\0'; ++cnt) - if (c != thousands[cnt]) - break; - if (thousands[cnt] != '\0') - break; - } - c = *++cp; - } - } -#endif - - /* If no other digit but a '0' is found the result is 0.0. - Return current read pointer. */ - if ((c < L_('0') || c > L_('9')) - && (base == 16 && (c < (CHAR_TYPE) TOLOWER (L_('a')) - || c > (CHAR_TYPE) TOLOWER (L_('f')))) -#ifdef USE_WIDE_CHAR - && c != (wint_t) decimal -#else - && ({ for (cnt = 0; decimal[cnt] != '\0'; ++cnt) - if (decimal[cnt] != cp[cnt]) - break; - decimal[cnt] != '\0'; }) -#endif - && (base == 16 && (cp == start_of_digits - || (CHAR_TYPE) TOLOWER (c) != L_('p'))) - && (base != 16 && (CHAR_TYPE) TOLOWER (c) != L_('e'))) - { -#ifdef USE_WIDE_CHAR - tp = __correctly_grouped_prefixwc (start_of_digits, cp, thousands, - grouping); -#else - tp = __correctly_grouped_prefixmb (start_of_digits, cp, thousands, - grouping); -#endif - /* If TP is at the start of the digits, there was no correctly - grouped prefix of the string; so no number found. */ - RETURN (0.0, tp == start_of_digits ? (base == 16 ? cp - 1 : nptr) : tp); - } - - /* Remember first significant digit and read following characters until the - decimal point, exponent character or any non-FP number character. */ - startp = cp; - dig_no = 0; - while (1) - { - if ((c >= L_('0') && c <= L_('9')) - || (base == 16 && (wint_t) TOLOWER (c) >= L_('a') - && (wint_t) TOLOWER (c) <= L_('f'))) - ++dig_no; - else - { -#ifdef USE_WIDE_CHAR - if ((wint_t) thousands == L'\0' || c != (wint_t) thousands) - /* Not a digit or separator: end of the integer part. */ - break; -#else - if (thousands == NULL) - break; - else - { - for (cnt = 0; thousands[cnt] != '\0'; ++cnt) - if (thousands[cnt] != cp[cnt]) - break; - if (thousands[cnt] != '\0') - break; - } -#endif - } - c = *++cp; - } - - if (grouping && dig_no > 0) - { - /* Check the grouping of the digits. */ -#ifdef USE_WIDE_CHAR - tp = __correctly_grouped_prefixwc (start_of_digits, cp, thousands, - grouping); -#else - tp = __correctly_grouped_prefixmb (start_of_digits, cp, thousands, - grouping); -#endif - if (cp != tp) - { - /* Less than the entire string was correctly grouped. */ - - if (tp == start_of_digits) - /* No valid group of numbers at all: no valid number. */ - RETURN (0.0, nptr); - - if (tp < startp) - /* The number is validly grouped, but consists - only of zeroes. The whole value is zero. */ - RETURN (0.0, tp); - - /* Recompute DIG_NO so we won't read more digits than - are properly grouped. */ - cp = tp; - dig_no = 0; - for (tp = startp; tp < cp; ++tp) - if (*tp >= L_('0') && *tp <= L_('9')) - ++dig_no; - - int_no = dig_no; - lead_zero = 0; - - goto number_parsed; - } - } - - /* We have the number digits in the integer part. Whether these are all or - any is really a fractional digit will be decided later. */ - int_no = dig_no; - lead_zero = int_no == 0 ? -1 : 0; - - /* Read the fractional digits. A special case are the 'american style' - numbers like `16.' i.e. with decimal but without trailing digits. */ - if ( -#ifdef USE_WIDE_CHAR - c == (wint_t) decimal -#else - ({ for (cnt = 0; decimal[cnt] != '\0'; ++cnt) - if (decimal[cnt] != cp[cnt]) - break; - decimal[cnt] == '\0'; }) -#endif - ) - { - cp += decimal_len; - c = *cp; - while ((c >= L_('0') && c <= L_('9')) || - (base == 16 && TOLOWER (c) >= L_('a') && TOLOWER (c) <= L_('f'))) - { - if (c != L_('0') && lead_zero == -1) - lead_zero = dig_no - int_no; - ++dig_no; - c = *++cp; - } - } - - /* Remember start of exponent (if any). */ - expp = cp; - - /* Read exponent. */ - if ((base == 16 && TOLOWER (c) == L_('p')) - || (base != 16 && TOLOWER (c) == L_('e'))) - { - int exp_negative = 0; - - c = *++cp; - if (c == L_('-')) - { - exp_negative = 1; - c = *++cp; - } - else if (c == L_('+')) - c = *++cp; - - if (c >= L_('0') && c <= L_('9')) - { - int exp_limit; - - /* Get the exponent limit. */ - if (base == 16) - exp_limit = (exp_negative ? - -MIN_EXP + MANT_DIG + 4 * int_no : - MAX_EXP - 4 * int_no + lead_zero); - else - exp_limit = (exp_negative ? - -MIN_10_EXP + MANT_DIG + int_no : - MAX_10_EXP - int_no + lead_zero); - - do - { - exponent *= 10; - - if (exponent > exp_limit) - /* The exponent is too large/small to represent a valid - number. */ - { - FLOAT result; - - /* We have to take care for special situation: a joker - might have written "0.0e100000" which is in fact - zero. */ - if (lead_zero == -1) - result = negative ? -0.0 : 0.0; - else - {