diff options
author | Ulrich Drepper <drepper@redhat.com> | 2004-03-14 21:12:06 +0000 |
---|---|---|
committer | Ulrich Drepper <drepper@redhat.com> | 2004-03-14 21:12:06 +0000 |
commit | ccadf7b5346a3e21c692dfcbfcf38a63433bc36a (patch) | |
tree | a1e826af3345eee581925387513b1b6c76629c31 | |
parent | 3bc9b83f49805969b7d139b52ef6b003e5136354 (diff) | |
download | glibc-ccadf7b5346a3e21c692dfcbfcf38a63433bc36a.tar.gz |
Update.
2004-03-14 Ulrich Drepper <drepper@redhat.com>
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.
48 files changed, 6501 insertions, 6412 deletions
@@ -1,3 +1,72 @@ +2004-03-14 Ulrich Drepper <drepper@redhat.com> + + 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. + 2004-03-11 Jeroen Dekkers <jeroen@dekkers.cx> * cppflags-iterator.mk: Change libof-$(cpp-src) to diff --git a/include/monetary.h b/include/monetary.h index 4735dc6c8a..98b3dbf294 100644 --- a/include/monetary.h +++ b/include/monetary.h @@ -1 +1,5 @@ #include <stdlib/monetary.h> +#include <stdarg.h> + +extern ssize_t __vstrfmon_l (char *s, size_t maxsize, __locale_t loc, + const char *format, va_list ap); diff --git a/include/string.h b/include/string.h index 041590b1db..42d9362dfc 100644 --- a/include/string.h +++ b/include/string.h @@ -78,6 +78,8 @@ libc_hidden_proto (__strerror_r) libc_hidden_proto (__strverscmp) libc_hidden_proto (basename) libc_hidden_proto (strcoll) +libc_hidden_proto (__strcoll_l) +libc_hidden_proto (__strxfrm_l) libc_hidden_builtin_proto (memchr) libc_hidden_builtin_proto (memcpy) diff --git a/include/time.h b/include/time.h index 87e0f08f8d..5a95be5a4e 100644 --- a/include/time.h +++ b/include/time.h @@ -3,6 +3,7 @@ # include <time/time.h> #else # include <time/time.h> +# include <xlocale.h> extern __typeof (strftime_l) __strftime_l; libc_hidden_proto (__strftime_l) @@ -84,6 +85,18 @@ extern int __getdate_r (__const char *__string, struct tm *__resbufp); extern int __getclktck (void); +/* strptime support. */ +/* Status of lookup: do we use the locale data or the raw data? */ +enum ptime_locale_status { not, loc, raw }; + +extern char * __strptime_internal (const char *rp, const char *fmt, + struct tm *tm, + enum ptime_locale_status *decided, + int era_cnt, __locale_t loc) + internal_function; + + + /* Use in the clock_* functions. Size of the field representing the actual clock ID. */ #ifndef _ISOMAC diff --git a/include/wchar.h b/include/wchar.h index f69463f57a..73bd4a8fef 100644 --- a/include/wchar.h +++ b/include/wchar.h @@ -29,6 +29,9 @@ libc_hidden_proto (__wcstoull_internal) libc_hidden_proto (__wcscasecmp_l) libc_hidden_proto (__wcsncasecmp_l) +libc_hidden_proto (__wcscoll_l) +libc_hidden_proto (__wcsxfrm_l) + libc_hidden_proto (fputws_unlocked) libc_hidden_proto (putwc_unlocked) libc_hidden_proto (putwc) diff --git a/locale/uselocale.c b/locale/uselocale.c index e2f38c10ba..4e63dabd8e 100644 --- a/locale/uselocale.c +++ b/locale/uselocale.c @@ -1,5 +1,5 @@ /* uselocale -- fetch and set the current per-thread locale - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -29,7 +29,7 @@ locale_t __uselocale (locale_t newloc) { - locale_t oldloc = __libc_tsd_get (LOCALE); + locale_t oldloc = _NL_CURRENT_LOCALE; if (newloc != NULL) { 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 <ctype.h> -#include <errno.h> -#include <langinfo.h> -#include <locale.h> #include <monetary.h> -#ifdef USE_IN_LIBIO -# include "../libio/libioP.h" -# include "../libio/strfile.h" -#endif -#include <printf.h> #include <stdarg.h> -#include <stdio.h> -#include <string.h> -#include "../locale/localeinfo.h" +#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. */ -#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 <SP>. */ - 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 <drepper@cygnus.com>, 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 <strfmon.c> +#include <ctype.h> +#include <errno.h> +#include <langinfo.h> +#include <locale.h> +#include <monetary.h> +#ifdef USE_IN_LIBIO +# include "../libio/libioP.h" +# include "../libio/strfile.h" +#endif +#include <printf.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#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 <SP>. */ + 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 <drepper@gnu.org>, 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 <stdlib.h> +#include <wchar.h> +#include <locale/localeinfo.h> + + #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 <ctype.h> -#include <errno.h> -#include <float.h> -#include <ieee754.h> -#include "../locale/localeinfo.h" -#include <locale.h> -#include <math.h> -#include <stdlib.h> -#include <string.h> - -/* 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 <gmp-mparam.h> -#include <gmp.h> -#include <gmp-impl.h> -#include <longlong.h> -#include "fpioconst.h" - -#define NDEBUG 1 -#include <assert.h> - - -/* 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 <wchar.h> #endif #ifdef USE_WIDE_CHAR # include <wctype.h> # 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 <stddef.h>, 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 <locale.h>. */ - 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 - { - /* Overflow or underflow. */ - __set_errno (ERANGE); - result = (exp_negative ? 0.0 : - negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL); - } - - /* Accept all following digits as part of the exponent. */ - do - ++cp; - while (*cp >= L_('0') && *cp <= L_('9')); - - RETURN (result, cp); - /* NOTREACHED */ - } - - exponent += c - L_('0'); - c = *++cp; - } - while (c >= L_('0') && c <= L_('9')); - - if (exp_negative) - exponent = -exponent; - } - else - cp = expp; - } - - /* We don't want to have to work with trailing zeroes after the radix. */ - if (dig_no > int_no) - { - while (expp[-1] == L_('0')) - { - --expp; - --dig_no; - } - assert (dig_no >= int_no); - } - - if (dig_no == int_no && dig_no > 0 && exponent < 0) - do - { - while (! (base == 16 ? ISXDIGIT (expp[-1]) : ISDIGIT (expp[-1]))) - --expp; - - if (expp[-1] != L_('0')) - break; - - --expp; - --dig_no; - --int_no; - ++exponent; - } - while (dig_no > 0 && exponent < 0); - - number_parsed: - - /* The whole string is parsed. Store the address of the next character. */ - if (endptr) - *endptr = (STRING_TYPE *) cp; - - if (dig_no == 0) - return negative ? -0.0 : 0.0; - - if (lead_zero) - { - /* Find the decimal point */ -#ifdef USE_WIDE_CHAR - while (*startp != decimal) - ++startp; -#else - while (1) - { - if (*startp == decimal[0]) - { - for (cnt = 1; decimal[cnt] != '\0'; ++cnt) - if (decimal[cnt] != startp[cnt]) - break; - if (decimal[cnt] == '\0') - break; - } - ++startp; - } -#endif - startp += lead_zero + decimal_len; - exponent -= base == 16 ? 4 * lead_zero : lead_zero; - dig_no -= lead_zero; - } - - /* If the BASE is 16 we can use a simpler algorithm. */ - if (base == 16) - { - static const int nbits[16] = { 0, 1, 2, 2, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4 }; - int idx = (MANT_DIG - 1) / BITS_PER_MP_LIMB; - int pos = (MANT_DIG - 1) % BITS_PER_MP_LIMB; - mp_limb_t val; - - while (!ISXDIGIT (*startp)) - ++startp; - while (*startp == L_('0')) - ++startp; - if (ISDIGIT (*startp)) - val = *startp++ - L_('0'); - else - val = 10 + TOLOWER (*startp++) - L_('a'); - bits = nbits[val]; - /* We cannot have a leading zero. */ - assert (bits != 0); - - if (pos + 1 >= 4 || pos + 1 >= bits) - { - /* We don't have to care for wrapping. This is the normal - case so we add the first clause in the `if' expression as - an optimization. It is a compile-time constant and so does - not cost anything. */ - retval[idx] = val << (pos - bits + 1); - pos -= bits; - } - else - { - retval[idx--] = val >> (bits - pos - 1); - retval[idx] = val << (BITS_PER_MP_LIMB - (bits - pos - 1)); - pos = BITS_PER_MP_LIMB - 1 - (bits - pos - 1); - } - - /* Adjust the exponent for the bits we are shifting in. */ - exponent += bits - 1 + (int_no - 1) * 4; - - while (--dig_no > 0 && idx >= 0) - { - if (!ISXDIGIT (*startp)) - startp += decimal_len; - if (ISDIGIT (*startp)) - val = *startp++ - L_('0'); - else - val = 10 + TOLOWER (*startp++) - L_('a'); - - if (pos + 1 >= 4) - { - retval[idx] |= val << (pos - 4 + 1); - pos -= 4; - } - else - { - retval[idx--] |= val >> (4 - pos - 1); - val <<= BITS_PER_MP_LIMB - (4 - pos - 1); - if (idx < 0) - return round_and_return (retval, exponent, negative, val, - BITS_PER_MP_LIMB - 1, dig_no > 0); - - retval[idx] = val; - pos = BITS_PER_MP_LIMB - 1 - (4 - pos - 1); - } - } - - /* We ran out of digits. */ - MPN_ZERO (retval, idx); - - return round_and_return (retval, exponent, negative, 0, 0, 0); - } - - /* Now we have the number of digits in total and the integer digits as well - as the exponent and its sign. We can decide whether the read digits are - really integer digits or belong to the fractional part; i.e. we normalize - 123e-2 to 1.23. */ - { - register int incr = (exponent < 0 ? MAX (-int_no, exponent) - : MIN (dig_no - int_no, exponent)); - int_no += incr; - exponent -= incr; - } - - if (int_no + exponent > MAX_10_EXP + 1) - { - __set_errno (ERANGE); - return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL; - } - - if (exponent < MIN_10_EXP - (DIG + 1)) - { - __set_errno (ERANGE); - return 0.0; - } - - if (int_no > 0) - { - /* Read the integer part as a multi-precision number to NUM. */ - startp = str_to_mpn (startp, int_no, num, &numsize, &exponent -#ifndef USE_WIDE_CHAR - , decimal, decimal_len, thousands -#endif - ); - - if (exponent > 0) - { - /* We now multiply the gained number by the given power of ten. */ - mp_limb_t *psrc = num; - mp_limb_t *pdest = den; - int expbit = 1; - const struct mp_power *ttab = &_fpioconst_pow10[0]; - - do - { - if ((exponent & expbit) != 0) - { - size_t size = ttab->arraysize - _FPIO_CONST_OFFSET; - mp_limb_t cy; - exponent ^= expbit; - - /* FIXME: not the whole multiplication has to be - done. If we have the needed number of bits we - only need the information whether more non-zero - bits follow. */ - if (numsize >= ttab->arraysize - _FPIO_CONST_OFFSET) - cy = __mpn_mul (pdest, psrc, numsize, - &__tens[ttab->arrayoff - + _FPIO_CONST_OFFSET], - size); - else - cy = __mpn_mul (pdest, &__tens[ttab->arrayoff - + _FPIO_CONST_OFFSET], - size, psrc, numsize); - numsize += size; - if (cy == 0) - --numsize; - (void) SWAP (psrc, pdest); - } - expbit <<= 1; - ++ttab; - } - while (exponent != 0); - - if (psrc == den) - memcpy (num, den, numsize * sizeof (mp_limb_t)); - } - - /* Determine how many bits of the result we already have. */ - count_leading_zeros (bits, num[numsize - 1]); - bits = numsize * BITS_PER_MP_LIMB - bits; - - /* Now we know the exponent of the number in base two. - Check it against the maximum possible exponent. */ - if (bits > MAX_EXP) - { - __set_errno (ERANGE); - return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL; - } - - /* We have already the first BITS bits of the result. Together with - the information whether more non-zero bits follow this is enough - to determine the result. */ - if (bits > MANT_DIG) - { - int i; - const mp_size_t least_idx = (bits - MANT_DIG) / BITS_PER_MP_LIMB; - const mp_size_t least_bit = (bits - MANT_DIG) % BITS_PER_MP_LIMB; - const mp_size_t round_idx = least_bit == 0 ? least_idx - 1 - : least_idx; - const mp_size_t round_bit = least_bit == 0 ? BITS_PER_MP_LIMB - 1 - : least_bit - 1; - - if (least_bit == 0) - memcpy (retval, &num[least_idx], - RETURN_LIMB_SIZE * sizeof (mp_limb_t)); - else - { - for (i = least_idx; i < numsize - 1; ++i) - retval[i - least_idx] = (num[i] >> least_bit) - | (num[i + 1] - << (BITS_PER_MP_LIMB - least_bit)); - if (i - least_idx < RETURN_LIMB_SIZE) - retval[RETURN_LIMB_SIZE - 1] = num[i] >> least_bit; - } - - /* Check whether any limb beside the ones in RETVAL are non-zero. */ - for (i = 0; num[i] == 0; ++i) - ; - - return round_and_return (retval, bits - 1, negative, - num[round_idx], round_bit, - int_no < dig_no || i < round_idx); - /* NOTREACHED */ - } - else if (dig_no == int_no) - { - const mp_size_t target_bit = (MANT_DIG - 1) % BITS_PER_MP_LIMB; - const mp_size_t is_bit = (bits - 1) % BITS_PER_MP_LIMB; - - if (target_bit == is_bit) - { - memcpy (&retval[RETURN_LIMB_SIZE - numsize], num, - numsize * sizeof (mp_limb_t)); - /* FIXME: the following loop can be avoided if we assume a - maximal MANT_DIG value. */ - MPN_ZERO (retval, RETURN_LIMB_SIZE - numsize); - } - else if (target_bit > is_bit) - { - (void) __mpn_lshift (&retval[RETURN_LIMB_SIZE - numsize], - num, numsize, target_bit - is_bit); - /* FIXME: the following loop can be avoided if we assume a - maximal MANT_DIG value. */ - MPN_ZERO (retval, RETURN_LIMB_SIZE - numsize); - } - else - { - mp_limb_t cy; - assert (numsize < RETURN_LIMB_SIZE); - - cy = __mpn_rshift (&retval[RETURN_LIMB_SIZE - numsize], - num, numsize, is_bit - target_bit); - retval[RETURN_LIMB_SIZE - numsize - 1] = cy; - /* FIXME: the following loop can be avoided if we assume a - maximal MANT_DIG value. */ - MPN_ZERO (retval, RETURN_LIMB_SIZE - numsize - 1); - } - - return round_and_return (retval, bits - 1, negative, 0, 0, 0); - /* NOTREACHED */ - } - - /* Store the bits we already have. */ - memcpy (retval, num, numsize * sizeof (mp_limb_t)); -#if RETURN_LIMB_SIZE > 1 - if (numsize < RETURN_LIMB_SIZE) - retval[numsize] = 0; -#endif - } - - /* We have to compute at least some of the fractional digits. */ - { - /* We construct a fraction and the result of the division gives us - the needed digits. The denominator is 1.0 multiplied by the - exponent of the lowest digit; i.e. 0.123 gives 123 / 1000 and - 123e-6 gives 123 / 1000000. */ - - int expbit; - int neg_exp; - int more_bits; - mp_limb_t cy; - mp_limb_t *psrc = den; - mp_limb_t *pdest = num; - const struct mp_power *ttab = &_fpioconst_pow10[0]; - - assert (dig_no > int_no && exponent <= 0); - - - /* For the fractional part we need not process too many digits. One - decimal digits gives us log_2(10) ~ 3.32 bits. If we now compute - ceil(BITS / 3) =: N - digits we should have enough bits for the result. The remaining - decimal digits give us the information that more bits are following. - This can be used while rounding. (Two added as a safety margin.) */ - if (dig_no - int_no > (MANT_DIG - bits + 2) / 3 + 2) - { - dig_no = int_no + (MANT_DIG - bits + 2) / 3 + 2; - more_bits = 1; - } - else - more_bits = 0; - - neg_exp = dig_no - int_no - exponent; - - /* Construct the denominator. */ - densize = 0; - expbit = 1; - do - { - if ((neg_exp & expbit) != 0) - { - mp_limb_t cy; - neg_exp ^= expbit; - - if (densize == 0) - { - densize = ttab->arraysize - _FPIO_CONST_OFFSET; - memcpy (psrc, &__tens[ttab->arrayoff + _FPIO_CONST_OFFSET], - densize * sizeof (mp_limb_t)); - } - else - { - cy = __mpn_mul (pdest, &__tens[ttab->arrayoff - + _FPIO_CONST_OFFSET], - ttab->arraysize - _FPIO_CONST_OFFSET, - psrc, densize); - densize += ttab->arraysize - _FPIO_CONST_OFFSET; - if (cy == 0) - --densize; - (void) SWAP (psrc, pdest); - } - } - expbit <<= 1; - ++ttab; - } - while (neg_exp != 0); - - if (psrc == num) - memcpy (den, num, densize * sizeof (mp_limb_t)); - - /* Read the fractional digits from the string. */ - (void) str_to_mpn (startp, dig_no - int_no, num, &numsize, &exponent -#ifndef USE_WIDE_CHAR - , decimal, decimal_len, thousands -#endif - ); - - /* We now have to shift both numbers so that the highest bit in the - denominator is set. In the same process we copy the numerator to - a high place in the array so that the division constructs the wanted - digits. This is done by a "quasi fix point" number representation. - - num: ddddddddddd . 0000000000000000000000 - |--- m ---| - den: ddddddddddd n >= m - |--- n ---| - */ - - count_leading_zeros (cnt, den[densize - 1]); - - if (cnt > 0) - { - /* Don't call `mpn_shift' with a count of zero since the specification - does not allow this. */ - (void) __mpn_lshift (den, den, densize, cnt); - cy = __mpn_lshift (num, num, numsize, cnt); - if (cy != 0) - num[numsize++] = cy; - } - - /* Now we are ready for the division. But it is not necessary to - do a full multi-precision division because we only need a small - number of bits for the result. So we do not use __mpn_divmod - here but instead do the division here by hand and stop whenever - the needed number of bits is reached. The code itself comes - from the GNU MP Library by Torbj\"orn Granlund. */ - - exponent = bits; - - switch (densize) - { - case 1: - { - mp_limb_t d, n, quot; - int used = 0; - - n = num[0]; - d = den[0]; - assert (numsize == 1 && n < d); - - do - { - udiv_qrnnd (quot, n, n, 0, d); - -#define got_limb \ - if (bits == 0) \ - { \ - register int cnt; \ - if (quot == 0) \ - cnt = BITS_PER_MP_LIMB; \ - else \ - count_leading_zeros (cnt, quot); \ - exponent -= cnt; \ - if (BITS_PER_MP_LIMB - cnt > MANT_DIG) \ - { \ - used = MANT_DIG + cnt; \ - retval[0] = quot >> (BITS_PER_MP_LIMB - used); \ - bits = MANT_DIG + 1; \ - } \ - else \ - { \ - /* Note that we only clear the second element. */ \ - /* The conditional is determined at compile time. */ \ - if (RETURN_LIMB_SIZE > 1) \ - retval[1] = 0; \ - retval[0] = quot; \ - bits = -cnt; \ - } \ - } \ - else if (bits + BITS_PER_MP_LIMB <= MANT_DIG) \ - __mpn_lshift_1 (retval, RETURN_LIMB_SIZE, BITS_PER_MP_LIMB, \ - quot); \ - else \ - { \ - used = MANT_DIG - bits; \ - if (used > 0) \ - __mpn_lshift_1 (retval, RETURN_LIMB_SIZE, used, quot); \ - } \ - bits += BITS_PER_MP_LIMB - - got_limb; - } - while (bits <= MANT_DIG); - - return round_and_return (retval, exponent - 1, negative, - quot, BITS_PER_MP_LIMB - 1 - used, - more_bits || n != 0); - } - case 2: - { - mp_limb_t d0, d1, n0, n1; - mp_limb_t quot = 0; - int used = 0; - - d0 = den[0]; - d1 = den[1]; - - if (numsize < densize) - { - if (num[0] >= d1) - { - /* The numerator of the number occupies fewer bits than - the denominator but the one limb is bigger than the - high limb of the numerator. */ - n1 = 0; - n0 = num[0]; - } - else - { - if (bits <= 0) - exponent -= BITS_PER_MP_LIMB; - else - { - if (bits + BITS_PER_MP_LIMB <= MANT_DIG) - __mpn_lshift_1 (retval, RETURN_LIMB_SIZE, - BITS_PER_MP_LIMB, 0); - else - { - used = MANT_DIG - bits; - if (used > 0) - __mpn_lshift_1 (retval, RETURN_LIMB_SIZE, used, 0); - } - bits += BITS_PER_MP_LIMB; - } - n1 = num[0]; - n0 = 0; - } - } - else - { - n1 = num[1]; - n0 = num[0]; - } - - while (bits <= MANT_DIG) - { - mp_limb_t r; - - if (n1 == d1) - { - /* QUOT should be either 111..111 or 111..110. We need - special treatment of this rare case as normal division - would give overflow. */ - quot = ~(mp_limb_t) 0; - - r = n0 + d1; - if (r < d1) /* Carry in the addition? */ - { - add_ssaaaa (n1, n0, r - d0, 0, 0, d0); - goto have_quot; - } - n1 = d0 - (d0 != 0); - n0 = -d0; - } - else - { - udiv_qrnnd (quot, r, n1, n0, d1); - umul_ppmm (n1, n0, d0, quot); - } - - q_test: - if (n1 > r || (n1 == r && n0 > 0)) - { - /* The estimated QUOT was too large. */ - --quot; - - sub_ddmmss (n1, n0, n1, n0, 0, d0); - r += d1; - if (r >= d1) /* If not carry, test QUOT again. */ - goto q_test; - } - sub_ddmmss (n1, n0, r, 0, n1, n0); - - have_quot: - got_limb; - } - - return round_and_return (retval, exponent - 1, negative, - quot, BITS_PER_MP_LIMB - 1 - used, - more_bits || n1 != 0 || n0 != 0); - } - default: - { - int i; - mp_limb_t cy, dX, d1, n0, n1; - mp_limb_t quot = 0; - int used = 0; - - dX = den[densize - 1]; - d1 = den[densize - 2]; - - /* The division does not work if the upper limb of the two-limb - numerator is greater than the denominator. */ - if (__mpn_cmp (num, &den[densize - numsize], numsize) > 0) - num[numsize++] = 0; - - if (numsize < densize) - { - mp_size_t empty = densize - numsize; - - if (bits <= 0) - { - register int i; - for (i = numsize; i > 0; --i) - num[i + empty] = num[i - 1]; - MPN_ZERO (num, empty + 1); - exponent -= empty * BITS_PER_MP_LIMB; - } - else - { - if (bits + empty * BITS_PER_MP_LIMB <= MANT_DIG) - { - /* We make a difference here because the compiler - cannot optimize the `else' case that good and - this reflects all currently used FLOAT types - and GMP implementations. */ - register int i; -#if RETURN_LIMB_SIZE <= 2 - assert (empty == 1); - __mpn_lshift_1 (retval, RETURN_LIMB_SIZE, - BITS_PER_MP_LIMB, 0); -#else - for (i = RETURN_LIMB_SIZE; i > empty; --i) - retval[i] = retval[i - empty]; -#endif - for (i = numsize; i > 0; --i) - num[i + empty] = num[i - 1]; - MPN_ZERO (num, empty + 1); - } - else - { - used = MANT_DIG - bits; - if (used >= BITS_PER_MP_LIMB) - { - register int i; - (void) __mpn_lshift (&retval[used - / BITS_PER_MP_LIMB], - retval, RETURN_LIMB_SIZE, - used % BITS_PER_MP_LIMB); - for (i = used / BITS_PER_MP_LIMB; i >= 0; --i) - retval[i] = 0; - } - else if (used > 0) - __mpn_lshift_1 (retval, RETURN_LIMB_SIZE, used, 0); - } - bits += empty * BITS_PER_MP_LIMB; - } - } - else - { - int i; - assert (numsize == densize); - for (i = numsize; i > 0; --i) - num[i] = num[i - 1]; - } - - den[densize] = 0; - n0 = num[densize]; - - while (bits <= MANT_DIG) - { - if (n0 == dX) - /* This might over-estimate QUOT, but it's probably not - worth the extra code here to find out. */ - quot = ~(mp_limb_t) 0; - else - { - mp_limb_t r; - - udiv_qrnnd (quot, r, n0, num[densize - 1], dX); - umul_ppmm (n1, n0, d1, quot); - - while (n1 > r || (n1 == r && n0 > num[densize - 2])) - { - --quot; - r += dX; - if (r < dX) /* I.e. "carry in previous addition?" */ - break; - n1 -= n0 < d1; - n0 -= d1; - } - } - - /* Possible optimization: We already have (q * n0) and (1 * n1) - after the calculation of QUOT. Taking advantage of this, we - could make this loop make two iterations less. */ - - cy = __mpn_submul_1 (num, den, densize + 1, quot); - - if (num[densize] != cy) - { - cy = __mpn_add_n (num, num, den, densize); - assert (cy != 0); - --quot; - } - n0 = num[densize] = num[densize - 1]; - for (i = densize - 1; i > 0; --i) - num[i] = num[i - 1]; - - got_limb; - } - - for (i = densize; num[i] == 0 && i >= 0; --i) - ; - return round_and_return (retval, exponent - 1, negative, - quot, BITS_PER_MP_LIMB - 1 - used, - more_bits || i >= 0); - } - } - } - - /* NOTREACHED */ + return INTERNAL(STRTOF_L) (nptr, endptr, group, _NL_CURRENT_LOCALE); } -#if defined _LIBC \ - && !(defined USE_IN_EXTENDED_LOCALE_MODEL && defined USE_WIDE_CHAR) +#if defined _LIBC libc_hidden_def (INTERNAL (STRTOF)) #endif - -/* External user entry point. */ + FLOAT #ifdef weak_function weak_function #endif -STRTOF (nptr, endptr LOCALE_PARAM) +STRTOF (nptr, endptr) const STRING_TYPE *nptr; STRING_TYPE **endptr; - LOCALE_PARAM_DECL { - return INTERNAL (STRTOF) (nptr, endptr, 0 LOCALE_PARAM); + return INTERNAL(STRTOF_L) (nptr, endptr, 0, _NL_CURRENT_LOCALE); } diff --git a/stdlib/strtod_l.c b/stdlib/strtod_l.c index e8449050d3..89d30b435b 100644 --- a/stdlib/strtod_l.c +++ b/stdlib/strtod_l.c @@ -1,5 +1,5 @@ /* Convert string representing a number to float value, using given locale. - Copyright (C) 1997,98,2002 Free Software Foundation, Inc. + Copyright (C) 1997,98,2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -18,14 +18,1555 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define USE_IN_EXTENDED_LOCALE_MODEL 1 - #include <xlocale.h> extern double ____strtod_l_internal (const char *, char **, int, __locale_t); extern unsigned long long int ____strtoull_l_internal (const char *, char **, int, int, __locale_t); -#include <strtod.c> +/* 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. */ +#ifndef FLOAT +# define FLOAT double +# define FLT DBL +# ifdef USE_WIDE_CHAR +# define STRTOF wcstod_l +# define __STRTOF __wcstod_l +# else +# define STRTOF strtod_l +# define __STRTOF __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 <ctype.h> +#include <errno.h> +#include <float.h> +#include <ieee754.h> +#include "../locale/localeinfo.h" +#include <locale.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> + +/* 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 <gmp-mparam.h> +#include <gmp.h> +#include <gmp-impl.h> +#include <longlong.h> +#include "fpioconst.h" + +#define NDEBUG 1 +#include <assert.h> + + +/* We use this code 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 and + _NL_CURRENT_WORD macros. */ +#undef _NL_CURRENT +#define _NL_CURRENT(category, item) \ + (current->values[_NL_ITEM_INDEX (item)].string) +#undef _NL_CURRENT_WORD +#define _NL_CURRENT_WORD(category, item) \ + ((uint32_t) current->values[_NL_ITEM_INDEX (item)].word) + +#if defined _LIBC || defined HAVE_WCHAR_H +# include <wchar.h> +#endif + +#ifdef USE_WIDE_CHAR +# include <wctype.h> +# define STRING_TYPE wchar_t +# define CHAR_TYPE wint_t +# define L_(Ch) L##Ch +# 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 STRING_TYPE char +# define CHAR_TYPE char +# define L_(Ch) Ch +# 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) +#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, loc) + const STRING_TYPE *nptr; + STRING_TYPE **endptr; + int group; + __locale_t loc; +{ + 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 <stddef.h>, 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 <locale.h>. */ + const char *grouping; + /* Used in several places. */ + int cnt; + + struct locale_data *current = loc->__locales[LC_NUMERIC]; + + 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 + { + /* Overflow or underflow. */ + __set_errno (ERANGE); + result = (exp_negative ? 0.0 : + negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL); + } + + /* Accept all following digits as part of the exponent. */ + do + ++cp; + while (*cp >= L_('0') && *cp <= L_('9')); + + RETURN (result, cp); + /* NOTREACHED */ + } + + exponent += c - L_('0'); + c = *++cp; + } + while (c >= L_('0') && c <= L_('9')); + + if (exp_negative) + exponent = -exponent; + } + else + cp = expp; + } + + /* We don't want to have to work with trailing zeroes after the radix. */ + if (dig_no > int_no) + { + while (expp[-1] == L_('0')) + { + --expp; + --dig_no; + } + assert (dig_no >= int_no); + } + + if (dig_no == int_no && dig_no > 0 && exponent < 0) + do + { + while (! (base == 16 ? ISXDIGIT (expp[-1]) : ISDIGIT (expp[-1]))) + --expp; + + if (expp[-1] != L_('0')) + break; + + --expp; + --dig_no; + --int_no; + ++exponent; + } + while (dig_no > 0 && exponent < 0); + + number_parsed: + + /* The whole string is parsed. Store the address of the next character. */ + if (endptr) + *endptr = (STRING_TYPE *) cp; + + if (dig_no == 0) + return negative ? -0.0 : 0.0; + + if (lead_zero) + { + /* Find the decimal point */ +#ifdef USE_WIDE_CHAR + while (*startp != decimal) + ++startp; +#else + while (1) + { + if (*startp == decimal[0]) + { + for (cnt = 1; decimal[cnt] != '\0'; ++cnt) + if (decimal[cnt] != startp[cnt]) + break; + if (decimal[cnt] == '\0') + break; + } + ++startp; + } +#endif + startp += lead_zero + decimal_len; + exponent -= base == 16 ? 4 * lead_zero : lead_zero; + dig_no -= lead_zero; + } + + /* If the BASE is 16 we can use a simpler algorithm. */ + if (base == 16) + { + static const int nbits[16] = { 0, 1, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4 }; + int idx = (MANT_DIG - 1) / BITS_PER_MP_LIMB; + int pos = (MANT_DIG - 1) % BITS_PER_MP_LIMB; + mp_limb_t val; + + while (!ISXDIGIT (*startp)) + ++startp; + while (*startp == L_('0')) + ++startp; + if (ISDIGIT (*startp)) + val = *startp++ - L_('0'); + else + val = 10 + TOLOWER (*startp++) - L_('a'); + bits = nbits[val]; + /* We cannot have a leading zero. */ + assert (bits != 0); + + if (pos + 1 >= 4 || pos + 1 >= bits) + { + /* We don't have to care for wrapping. This is the normal + case so we add the first clause in the `if' expression as + an optimization. It is a compile-time constant and so does + not cost anything. */ + retval[idx] = val << (pos - bits + 1); + pos -= bits; + } + else + { + retval[idx--] = val >> (bits - pos - 1); + retval[idx] = val << (BITS_PER_MP_LIMB - (bits - pos - 1)); + pos = BITS_PER_MP_LIMB - 1 - (bits - pos - 1); + } + + /* Adjust the exponent for the bits we are shifting in. */ + exponent += bits - 1 + (int_no - 1) * 4; + + while (--dig_no > 0 && idx >= 0) + { + if (!ISXDIGIT (*startp)) + startp += decimal_len; + if (ISDIGIT (*startp)) + val = *startp++ - L_('0'); + else + val = 10 + TOLOWER (*startp++) - L_('a'); + + if (pos + 1 >= 4) + { + retval[idx] |= val << (pos - 4 + 1); + pos -= 4; + } + else + { + retval[idx--] |= val >> (4 - pos - 1); + val <<= BITS_PER_MP_LIMB - (4 - pos - 1); + if (idx < 0) + return round_and_return (retval, exponent, negative, val, + BITS_PER_MP_LIMB - 1, dig_no > 0); + + retval[idx] = val; + pos = BITS_PER_MP_LIMB - 1 - (4 - pos - 1); + } + } + + /* We ran out of digits. */ + MPN_ZERO (retval, idx); + + return round_and_return (retval, exponent, negative, 0, 0, 0); + } + + /* Now we have the number of digits in total and the integer digits as well + as the exponent and its sign. We can decide whether the read digits are + really integer digits or belong to the fractional part; i.e. we normalize + 123e-2 to 1.23. */ + { + register int incr = (exponent < 0 ? MAX (-int_no, exponent) + : MIN (dig_no - int_no, exponent)); + int_no += incr; + exponent -= incr; + } + + if (int_no + exponent > MAX_10_EXP + 1) + { + __set_errno (ERANGE); + return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL; + } + + if (exponent < MIN_10_EXP - (DIG + 1)) + { + __set_errno (ERANGE); + return 0.0; + } + + if (int_no > 0) + { + /* Read the integer part as a multi-precision number to NUM. */ + startp = str_to_mpn (startp, int_no, num, &numsize, &exponent +#ifndef USE_WIDE_CHAR + , decimal, decimal_len, thousands +#endif + ); + + if (exponent > 0) + { + /* We now multiply the gained number by the given power of ten. */ + mp_limb_t *psrc = num; + mp_limb_t *pdest = den; + int expbit = 1; + const struct mp_power *ttab = &_fpioconst_pow10[0]; + + do + { + if ((exponent & expbit) != 0) + { + size_t size = ttab->arraysize - _FPIO_CONST_OFFSET; + mp_limb_t cy; + exponent ^= expbit; + + /* FIXME: not the whole multiplication has to be + done. If we have the needed number of bits we + only need the information whether more non-zero + bits follow. */ + if (numsize >= ttab->arraysize - _FPIO_CONST_OFFSET) + cy = __mpn_mul (pdest, psrc, numsize, + &__tens[ttab->arrayoff + + _FPIO_CONST_OFFSET], + size); + else + cy = __mpn_mul (pdest, &__tens[ttab->arrayoff + + _FPIO_CONST_OFFSET], + size, psrc, numsize); + numsize += size; + if (cy == 0) + --numsize; + (void) SWAP (psrc, pdest); + } + expbit <<= 1; + ++ttab; + } + while (exponent != 0); + + if (psrc == den) + memcpy (num, den, numsize * sizeof (mp_limb_t)); + } + + /* Determine how many bits of the result we already have. */ + count_leading_zeros (bits, num[numsize - 1]); + bits = numsize * BITS_PER_MP_LIMB - bits; + + /* Now we know the exponent of the number in base two. + Check it against the maximum possible exponent. */ + if (bits > MAX_EXP) + { + __set_errno (ERANGE); + return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL; + } + + /* We have already the first BITS bits of the result. Together with + the information whether more non-zero bits follow this is enough + to determine the result. */ + if (bits > MANT_DIG) + { + int i; + const mp_size_t least_idx = (bits - MANT_DIG) / BITS_PER_MP_LIMB; + const mp_size_t least_bit = (bits - MANT_DIG) % BITS_PER_MP_LIMB; + const mp_size_t round_idx = least_bit == 0 ? least_idx - 1 + : least_idx; + const mp_size_t round_bit = least_bit == 0 ? BITS_PER_MP_LIMB - 1 + : least_bit - 1; + + if (least_bit == 0) + memcpy (retval, &num[least_idx], + RETURN_LIMB_SIZE * sizeof (mp_limb_t)); + else + { + for (i = least_idx; i < numsize - 1; ++i) + retval[i - least_idx] = (num[i] >> least_bit) + | (num[i + 1] + << (BITS_PER_MP_LIMB - least_bit)); + if (i - least_idx < RETURN_LIMB_SIZE) + retval[RETURN_LIMB_SIZE - 1] = num[i] >> least_bit; + } + + /* Check whether any limb beside the ones in RETVAL are non-zero. */ + for (i = 0; num[i] == 0; ++i) + ; + + return round_and_return (retval, bits - 1, negative, + num[round_idx], round_bit, + int_no < dig_no || i < round_idx); + /* NOTREACHED */ + } + else if (dig_no == int_no) + { + const mp_size_t target_bit = (MANT_DIG - 1) % BITS_PER_MP_LIMB; + const mp_size_t is_bit = (bits - 1) % BITS_PER_MP_LIMB; + + if (target_bit == is_bit) + { + memcpy (&retval[RETURN_LIMB_SIZE - numsize], num, + numsize * sizeof (mp_limb_t)); + /* FIXME: the following loop can be avoided if we assume a + maximal MANT_DIG value. */ + MPN_ZERO (retval, RETURN_LIMB_SIZE - numsize); + } + else if (target_bit > is_bit) + { + (void) __mpn_lshift (&retval[RETURN_LIMB_SIZE - numsize], + num, numsize, target_bit - is_bit); + /* FIXME: the following loop can be avoided if we assume a + maximal MANT_DIG value. */ + MPN_ZERO (retval, RETURN_LIMB_SIZE - numsize); + } + else + { + mp_limb_t cy; + assert (numsize < RETURN_LIMB_SIZE); + + cy = __mpn_rshift (&retval[RETURN_LIMB_SIZE - numsize], + num, numsize, is_bit - target_bit); + retval[RETURN_LIMB_SIZE - numsize - 1] = cy; + /* FIXME: the following loop can be avoided if we assume a + maximal MANT_DIG value. */ + MPN_ZERO (retval, RETURN_LIMB_SIZE - numsize - 1); + } + + return round_and_return (retval, bits - 1, negative, 0, 0, 0); + /* NOTREACHED */ + } + + /* Store the bits we already have. */ + memcpy (retval, num, numsize * sizeof (mp_limb_t)); +#if RETURN_LIMB_SIZE > 1 + if (numsize < RETURN_LIMB_SIZE) + retval[numsize] = 0; +#endif + } + + /* We have to compute at least some of the fractional digits. */ + { + /* We construct a fraction and the result of the division gives us + the needed digits. The denominator is 1.0 multiplied by the + exponent of the lowest digit; i.e. 0.123 gives 123 / 1000 and + 123e-6 gives 123 / 1000000. */ + + int expbit; + int neg_exp; + int more_bits; + mp_limb_t cy; + mp_limb_t *psrc = den; + mp_limb_t *pdest = num; + const struct mp_power *ttab = &_fpioconst_pow10[0]; + + assert (dig_no > int_no && exponent <= 0); + + + /* For the fractional part we need not process too many digits. One + decimal digits gives us log_2(10) ~ 3.32 bits. If we now compute + ceil(BITS / 3) =: N + digits we should have enough bits for the result. The remaining + decimal digits give us the information that more bits are following. + This can be used while rounding. (Two added as a safety margin.) */ + if (dig_no - int_no > (MANT_DIG - bits + 2) / 3 + 2) + { + dig_no = int_no + (MANT_DIG - bits + 2) / 3 + 2; + more_bits = 1; + } + else + more_bits = 0; + + neg_exp = dig_no - int_no - exponent; + + /* Construct the denominator. */ + densize = 0; + expbit = 1; + do + { + if ((neg_exp & expbit) != 0) + { + mp_limb_t cy; + neg_exp ^= expbit; + + if (densize == 0) + { + densize = ttab->arraysize - _FPIO_CONST_OFFSET; + memcpy (psrc, &__tens[ttab->arrayoff + _FPIO_CONST_OFFSET], + densize * sizeof (mp_limb_t)); + } + else + { + cy = __mpn_mul (pdest, &__tens[ttab->arrayoff + + _FPIO_CONST_OFFSET], + ttab->arraysize - _FPIO_CONST_OFFSET, + psrc, densize); + densize += ttab->arraysize - _FPIO_CONST_OFFSET; + if (cy == 0) + --densize; + (void) SWAP (psrc, pdest); + } + } + expbit <<= 1; + ++ttab; + } + while (neg_exp != 0); + + if (psrc == num) + memcpy (den, num, densize * sizeof (mp_limb_t)); + + /* Read the fractional digits from the string. */ + (void) str_to_mpn (startp, dig_no - int_no, num, &numsize, &exponent +#ifndef USE_WIDE_CHAR + , decimal, decimal_len, thousands +#endif + ); + + /* We now have to shift both numbers so that the highest bit in the + denominator is set. In the same process we copy the numerator to + a high place in the array so that the division constructs the wanted + digits. This is done by a "quasi fix point" number representation. + + num: ddddddddddd . 0000000000000000000000 + |--- m ---| + den: ddddddddddd n >= m + |--- n ---| + */ + + count_leading_zeros (cnt, den[densize - 1]); + + if (cnt > 0) + { + /* Don't call `mpn_shift' with a count of zero since the specification + does not allow this. */ + (void) __mpn_lshift (den, den, densize, cnt); + cy = __mpn_lshift (num, num, numsize, cnt); + if (cy != 0) + num[numsize++] = cy; + } + + /* Now we are ready for the division. But it is not necessary to + do a full multi-precision division because we only need a small + number of bits for the result. So we do not use __mpn_divmod + here but instead do the division here by hand and stop whenever + the needed number of bits is reached. The code itself comes + from the GNU MP Library by Torbj\"orn Granlund. */ + + exponent = bits; + + switch (densize) + { + case 1: + { + mp_limb_t d, n, quot; + int used = 0; + + n = num[0]; + d = den[0]; + assert (numsize == 1 && n < d); + + do + { + udiv_qrnnd (quot, n, n, 0, d); + +#define got_limb \ + if (bits == 0) \ + { \ + register int cnt; \ + if (quot == 0) \ + cnt = BITS_PER_MP_LIMB; \ + else \ + count_leading_zeros (cnt, quot); \ + exponent -= cnt; \ + if (BITS_PER_MP_LIMB - cnt > MANT_DIG) \ + { \ + used = MANT_DIG + cnt; \ + retval[0] = quot >> (BITS_PER_MP_LIMB - used); \ + bits = MANT_DIG + 1; \ + } \ + else \ + { \ + /* Note that we only clear the second element. */ \ + /* The conditional is determined at compile time. */ \ + if (RETURN_LIMB_SIZE > 1) \ + retval[1] = 0; \ + retval[0] = quot; \ + bits = -cnt; \ + } \ + } \ + else if (bits + BITS_PER_MP_LIMB <= MANT_DIG) \ + __mpn_lshift_1 (retval, RETURN_LIMB_SIZE, BITS_PER_MP_LIMB, \ + quot); \ + else \ + { \ + used = MANT_DIG - bits; \ + if (used > 0) \ + __mpn_lshift_1 (retval, RETURN_LIMB_SIZE, used, quot); \ + } \ + bits += BITS_PER_MP_LIMB + + got_limb; + } + while (bits <= MANT_DIG); + + return round_and_return (retval, exponent - 1, negative, + quot, BITS_PER_MP_LIMB - 1 - used, + more_bits || n != 0); + } + case 2: + { + mp_limb_t d0, d1, n0, n1; + mp_limb_t quot = 0; + int used = 0; + + d0 = den[0]; + d1 = den[1]; + + if (numsize < densize) + { + if (num[0] >= d1) + { + /* The numerator of the number occupies fewer bits than + the denominator but the one limb is bigger than the + high limb of the numerator. */ + n1 = 0; + n0 = num[0]; + } + else + { + if (bits <= 0) + exponent -= BITS_PER_MP_LIMB; + else + { + if (bits + BITS_PER_MP_LIMB <= MANT_DIG) + __mpn_lshift_1 (retval, RETURN_LIMB_SIZE, + BITS_PER_MP_LIMB, 0); + else + { + used = MANT_DIG - bits; + if (used > 0) + __mpn_lshift_1 (retval, RETURN_LIMB_SIZE, used, 0); + } + bits += BITS_PER_MP_LIMB; + } + n1 = num[0]; + n0 = 0; + } + } + else + { + n1 = num[1]; + n0 = num[0]; + } + + while (bits <= MANT_DIG) + { + mp_limb_t r; + + if (n1 == d1) + { + /* QUOT should be either 111..111 or 111..110. We need + special treatment of this rare case as normal division + would give overflow. */ + quot = ~(mp_limb_t) 0; + + r = n0 + d1; + if (r < d1) /* Carry in the addition? */ + { + add_ssaaaa (n1, n0, r - d0, 0, 0, d0); + goto have_quot; + } + n1 = d0 - (d0 != 0); + n0 = -d0; + } + else + { + udiv_qrnnd (quot, r, n1, n0, d1); + umul_ppmm (n1, n0, d0, quot); + } + + q_test: + if (n1 > r || (n1 == r && n0 > 0)) + { + /* The estimated QUOT was too large. */ + --quot; + + sub_ddmmss (n1, n0, n1, n0, 0, d0); + r += d1; + if (r >= d1) /* If not carry, test QUOT again. */ + goto q_test; + } + sub_ddmmss (n1, n0, r, 0, n1, n0); + + have_quot: + got_limb; + } + + return round_and_return (retval, exponent - 1, negative, + quot, BITS_PER_MP_LIMB - 1 - used, + more_bits || n1 != 0 || n0 != 0); + } + default: + { + int i; + mp_limb_t cy, dX, d1, n0, n1; + mp_limb_t quot = 0; + int used = 0; + + dX = den[densize - 1]; + d1 = den[densize - 2]; + + /* The division does not work if the upper limb of the two-limb + numerator is greater than the denominator. */ + if (__mpn_cmp (num, &den[densize - numsize], numsize) > 0) + num[numsize++] = 0; + + if (numsize < densize) + { + mp_size_t empty = densize - numsize; + + if (bits <= 0) + { + register int i; + for (i = numsize; i > 0; --i) + num[i + empty] = num[i - 1]; + MPN_ZERO (num, empty + 1); + exponent -= empty * BITS_PER_MP_LIMB; + } + else + { + if (bits + empty * BITS_PER_MP_LIMB <= MANT_DIG) + { + /* We make a difference here because the compiler + cannot optimize the `else' case that good and + this reflects all currently used FLOAT types + and GMP implementations. */ + register int i; +#if RETURN_LIMB_SIZE <= 2 + assert (empty == 1); + __mpn_lshift_1 (retval, RETURN_LIMB_SIZE, + BITS_PER_MP_LIMB, 0); +#else + for (i = RETURN_LIMB_SIZE; i > empty; --i) + retval[i] = retval[i - empty]; +#endif + for (i = numsize; i > 0; --i) + num[i + empty] = num[i - 1]; + MPN_ZERO (num, empty + 1); + } + else + { + used = MANT_DIG - bits; + if (used >= BITS_PER_MP_LIMB) + { + register int i; + (void) __mpn_lshift (&retval[used + / BITS_PER_MP_LIMB], + retval, RETURN_LIMB_SIZE, + used % BITS_PER_MP_LIMB); + for (i = used / BITS_PER_MP_LIMB; i >= 0; --i) + retval[i] = 0; + } + else if (used > 0) + __mpn_lshift_1 (retval, RETURN_LIMB_SIZE, used, 0); + } + bits += empty * BITS_PER_MP_LIMB; + } + } + else + { + int i; + assert (numsize == densize); + for (i = numsize; i > 0; --i) + num[i] = num[i - 1]; + } + + den[densize] = 0; + n0 = num[densize]; + + while (bits <= MANT_DIG) + { + if (n0 == dX) + /* This might over-estimate QUOT, but it's probably not + worth the extra code here to find out. */ + quot = ~(mp_limb_t) 0; + else + { + mp_limb_t r; + + udiv_qrnnd (quot, r, n0, num[densize - 1], dX); + umul_ppmm (n1, n0, d1, quot); + + while (n1 > r || (n1 == r && n0 > num[densize - 2])) + { + --quot; + r += dX; + if (r < dX) /* I.e. "carry in previous addition?" */ + break; + n1 -= n0 < d1; + n0 -= d1; + } + } + + /* Possible optimization: We already have (q * n0) and (1 * n1) + after the calculation of QUOT. Taking advantage of this, we + could make this loop make two iterations less. */ + + cy = __mpn_submul_1 (num, den, densize + 1, quot); + + if (num[densize] != cy) + { + cy = __mpn_add_n (num, num, den, densize); + assert (cy != 0); + --quot; + } + n0 = num[densize] = num[densize - 1]; + for (i = densize - 1; i > 0; --i) + num[i] = num[i - 1]; + + got_limb; + } + + for (i = densize; num[i] == 0 && i >= 0; --i) + ; + return round_and_return (retval, exponent - 1, negative, + quot, BITS_PER_MP_LIMB - 1 - used, + more_bits || i >= 0); + } + } + } + + /* NOTREACHED */ +} +#if defined _LIBC && !defined USE_WIDE_CHAR +libc_hidden_def (INTERNAL (__STRTOF)) +#endif + +/* External user entry point. */ -weak_alias (__strtod_l, strtod_l) +FLOAT +#ifdef weak_function +weak_function +#endif +__STRTOF (nptr, endptr, loc) + const STRING_TYPE *nptr; + STRING_TYPE **endptr; + __locale_t loc; +{ + return INTERNAL (__STRTOF) (nptr, endptr, 0, loc); +} +weak_alias (__STRTOF, STRTOF) diff --git a/stdlib/strtof.c b/stdlib/strtof.c index 9d070279f8..b98cb9bfde 100644 --- a/stdlib/strtof.c +++ b/stdlib/strtof.c @@ -1,22 +1,35 @@ +/* Read decimal floating point numbers. + This file is part of the GNU C Library. + Copyright (C) 1995-2002, 2003, 2004 Free Software Foundation, Inc. + Contributed by Ulrich Drepper <drepper@gnu.org>, 1995. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + /* The actual implementation for all floating point sizes is in strtod.c. These macros tell it to produce the `float' version, `strtof'. */ #define FLOAT float #define FLT FLT -#ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define STRTOF __strtof_l +#ifdef USE_WIDE_CHAR +#define STRTOF wcstof +#define STRTOF_L __wcstof_l #else # define STRTOF strtof +# define STRTOF_L __strtof_l #endif -#define MPN2FLOAT __mpn_construct_float -#define FLOAT_HUGE_VAL HUGE_VALF -#define SET_MANTISSA(flt, mant) \ - do { union ieee754_float u; \ - u.f = (flt); \ - if ((mant & 0x7fffff) == 0) \ - mant = 0x400000; \ - u.ieee.mantissa = (mant) & 0x7fffff; \ - (flt) = u.f; \ - } while (0) + #include "strtod.c" diff --git a/stdlib/strtof_l.c b/stdlib/strtof_l.c index 1187ffc73b..bbc7611e1e 100644 --- a/stdlib/strtof_l.c +++ b/stdlib/strtof_l.c @@ -1,5 +1,5 @@ /* Convert string representing a number to float value, using given locale. - Copyright (C) 1997,98,2002 Free Software Foundation, Inc. + Copyright (C) 1997,98,2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -18,14 +18,30 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define USE_IN_EXTENDED_LOCALE_MODEL 1 - #include <xlocale.h> extern float ____strtof_l_internal (const char *, char **, int, __locale_t); extern unsigned long long int ____strtoull_l_internal (const char *, char **, int, int, __locale_t); -#include <strtof.c> - -weak_alias (__strtof_l, strtof_l) +#define FLOAT float +#define FLT FLT +#ifdef USE_WIDE_CHAR +# define STRTOF wcstof_l +# define __STRTOF __wcstof_l +#else +# define STRTOF strtof_l +# define __STRTOF __strtof_l +#endif +#define MPN2FLOAT __mpn_construct_float +#define FLOAT_HUGE_VAL HUGE_VALF +#define SET_MANTISSA(flt, mant) \ + do { union ieee754_float u; \ + u.f = (flt); \ + if ((mant & 0x7fffff) == 0) \ + mant = 0x400000; \ + u.ieee.mantissa = (mant) & 0x7fffff; \ + (flt) = u.f; \ + } while (0) + +#include "strtod_l.c" diff --git a/stdlib/strtold.c b/stdlib/strtold.c new file mode 100644 index 0000000000..0bb227a84f --- /dev/null +++ b/stdlib/strtold.c @@ -0,0 +1,35 @@ +/* Read decimal floating point numbers. + This file is part of the GNU C Library. + Copyright (C) 1995-2002, 2003, 2004 Free Software Foundation, Inc. + Contributed by Ulrich Drepper <drepper@gnu.org>, 1995. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* The actual implementation for all floating point sizes is in strtod.c. + These macros tell it to produce the `float' version, `strtof'. */ + +#define FLOAT long double +#define FLT LDBL +#ifdef USE_WIDE_CHAR +# define STRTOF wcstold +# define STRTOF_L __wcstold_l +#else +# define STRTOF strtold +# define STRTOF_L __strtold_l +#endif + + +#include "strtod.c" diff --git a/stdlib/strtold_l.c b/stdlib/strtold_l.c deleted file mode 100644 index c3a4e79390..0000000000 --- a/stdlib/strtold_l.c +++ /dev/null @@ -1,53 +0,0 @@ -/* Convert string representing a number to float value, using given locale. - Copyright (C) 1997,98,99,2002 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 Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - -#include <math.h> -#include <xlocale.h> - -#ifndef __NO_LONG_DOUBLE_MATH - -#define USE_IN_EXTENDED_LOCALE_MODEL 1 - -extern long double ____strtold_l_internal (const char *, char **, int, - __locale_t); -extern unsigned long long int ____strtoull_l_internal (const char *, char **, - int, int, __locale_t); - -# include <strtold.c> - -#else -/* There is no `long double' type, use the `double' implementations. */ -extern double ____strtod_l_internal (const char *, char **, int, - __locale_t); -long double -____strtold_l_internal (const char *nptr, char **endptr, int group, - __locale_t loc) -{ - return ____strtod_l_internal (nptr, endptr, group, loc); -} - -long double -__strtold_l (const char *nptr, char **endptr, __locale_t loc) -{ - return ____strtod_l_internal (nptr, endptr, 0, loc); -} -#endif - -weak_alias (__strtold_l, strtold_l) diff --git a/string/strcoll.c b/string/strcoll.c index 26072018ac..8a73cae564 100644 --- a/string/strcoll.c +++ b/string/strcoll.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1995,96,97,98,99,2000,2001,2002 Free Software Foundation, Inc. +/* Copyright (C) 1995-2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Ulrich Drepper <drepper@cygnus.com>, 1995. @@ -17,540 +17,24 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include <assert.h> -#include <langinfo.h> -#include <locale.h> -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> #include <string.h> #ifndef STRING_TYPE # define STRING_TYPE char -# define USTRING_TYPE unsigned char -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define STRCOLL __strcoll_l -# else -# define STRCOLL strcoll -# endif -# define STRCMP strcmp -# define STRLEN strlen -# define WEIGHT_H "../locale/weight.h" -# define SUFFIX MB -# define L(arg) arg +# define STRCOLL strcoll +# define STRCOLL_L __strcoll_l #endif -#define CONCAT(a,b) CONCAT1(a,b) -#define CONCAT1(a,b) a##b - #include "../locale/localeinfo.h" -#ifndef USE_IN_EXTENDED_LOCALE_MODEL + int STRCOLL (s1, s2) const STRING_TYPE *s1; const STRING_TYPE *s2; -#else -int -STRCOLL (s1, s2, l) - const STRING_TYPE *s1; - const STRING_TYPE *s2; - __locale_t l; -#endif { -#ifdef USE_IN_EXTENDED_LOCALE_MODEL - struct locale_data *current = l->__locales[LC_COLLATE]; - uint_fast32_t nrules = current->values[_NL_ITEM_INDEX (_NL_COLLATE_NRULES)].word; -#else - uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); -#endif - /* We don't assign the following values right away since it might be - unnecessary in case there are no rules. */ - const unsigned char *rulesets; - const int32_t *table; - const USTRING_TYPE *weights; - const USTRING_TYPE *extra; - const int32_t *indirect; - uint_fast32_t pass; - int result = 0; - const USTRING_TYPE *us1; - const USTRING_TYPE *us2; - size_t s1len; - size_t s2len; - int32_t *idx1arr; - int32_t *idx2arr; - unsigned char *rule1arr; - unsigned char *rule2arr; - size_t idx1max; - size_t idx2max; - size_t idx1cnt; - size_t idx2cnt; - size_t idx1now; - size_t idx2now; - size_t backw1_stop; - size_t backw2_stop; - size_t backw1; - size_t backw2; - int val1; - int val2; - int position; - int seq1len; - int seq2len; - int use_malloc; - -#include WEIGHT_H - - if (nrules == 0) - return STRCMP (s1, s2); - -#ifdef USE_IN_EXTENDED_LOCALE_MODEL - rulesets = (const unsigned char *) - current->values[_NL_ITEM_INDEX (_NL_COLLATE_RULESETS)].string; - table = (const int32_t *) - current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_TABLE,SUFFIX))].string; - weights = (const USTRING_TYPE *) - current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_WEIGHT,SUFFIX))].string; - extra = (const USTRING_TYPE *) - current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_EXTRA,SUFFIX))].string; - indirect = (const int32_t *) - current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_INDIRECT,SUFFIX))].string; -#else - rulesets = (const unsigned char *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_RULESETS); - table = (const int32_t *) - _NL_CURRENT (LC_COLLATE, CONCAT(_NL_COLLATE_TABLE,SUFFIX)); - weights = (const USTRING_TYPE *) - _NL_CURRENT (LC_COLLATE, CONCAT(_NL_COLLATE_WEIGHT,SUFFIX)); - extra = (const USTRING_TYPE *) - _NL_CURRENT (LC_COLLATE, CONCAT(_NL_COLLATE_EXTRA,SUFFIX)); - indirect = (const int32_t *) - _NL_CURRENT (LC_COLLATE, CONCAT(_NL_COLLATE_INDIRECT,SUFFIX)); -#endif - use_malloc = 0; - - assert (((uintptr_t) table) % __alignof__ (table[0]) == 0); - assert (((uintptr_t) weights) % __alignof__ (weights[0]) == 0); - assert (((uintptr_t) extra) % __alignof__ (extra[0]) == 0); - assert (((uintptr_t) indirect) % __alignof__ (indirect[0]) == 0); - - /* We need this a few times. */ - s1len = STRLEN (s1); - s2len = STRLEN (s2); - - /* Catch empty strings. */ - if (__builtin_expect (s1len == 0, 0) || __builtin_expect (s2len == 0, 0)) - return (s1len != 0) - (s2len != 0); - - /* We need the elements of the strings as unsigned values since they - are used as indeces. */ - us1 = (const USTRING_TYPE *) s1; - us2 = (const USTRING_TYPE *) s2; - - /* Perform the first pass over the string and while doing this find - and store the weights for each character. Since we want this to - be as fast as possible we are using `alloca' to store the temporary - values. But since there is no limit on the length of the string - we have to use `malloc' if the string is too long. We should be - very conservative here. - - Please note that the localedef programs makes sure that `position' - is not used at the first level. */ - if (! __libc_use_alloca (s1len + s2len)) - { - idx1arr = (int32_t *) malloc ((s1len + s2len) * (sizeof (int32_t) + 1)); - idx2arr = &idx1arr[s1len]; - rule1arr = (unsigned char *) &idx2arr[s2len]; - rule2arr = &rule1arr[s1len]; - - if (idx1arr == NULL) - /* No memory. Well, go with the stack then. - - XXX Once this implementation is stable we will handle this - differently. Instead of precomputing the indeces we will - do this in time. This means, though, that this happens for - every pass again. */ - goto try_stack; - use_malloc = 1; - } - else - { - try_stack: - idx1arr = (int32_t *) alloca (s1len * sizeof (int32_t)); - idx2arr = (int32_t *) alloca (s2len * sizeof (int32_t)); - rule1arr = (unsigned char *) alloca (s1len); - rule2arr = (unsigned char *) alloca (s2len); - } - - idx1cnt = 0; - idx2cnt = 0; - idx1max = 0; - idx2max = 0; - idx1now = 0; - idx2now = 0; - backw1_stop = ~0ul; - backw2_stop = ~0ul; - backw1 = ~0ul; - backw2 = ~0ul; - seq1len = 0; - seq2len = 0; - position = rulesets[0] & sort_position; - while (1) - { - val1 = 0; - val2 = 0; - - /* Get the next non-IGNOREd element for string `s1'. */ - if (seq1len == 0) - do - { - ++val1; - - if (backw1_stop != ~0ul) - { - /* The is something pushed. */ - if (backw1 == backw1_stop) - { - /* The last pushed character was handled. Continue - with forward characters. */ - if (idx1cnt < idx1max) - idx1now = idx1cnt; - else - /* Nothing anymore. The backward sequence ended with - the last sequence in the string. Note that seq1len - is still zero. */ - break; - } - else - idx1now = --backw1; - } - else - { - backw1_stop = idx1max; - - while (*us1 != L('\0')) - { - int32_t tmp = findidx (&us1); - rule1arr[idx1max] = tmp >> 24; - idx1arr[idx1max] = tmp & 0xffffff; - idx1cnt = idx1max++; - - if ((rulesets[rule1arr[idx1cnt] * nrules] - & sort_backward) == 0) - /* No more backward characters to push. */ - break; - ++idx1cnt; - } - - if (backw1_stop >= idx1cnt) - { - /* No sequence at all or just one. */ - if (idx1cnt == idx1max || backw1_stop > idx1cnt) - /* Note that seq1len is still zero. */ - break; - - backw1_stop = ~0ul; - idx1now = idx1cnt; - } - else - /* We pushed backward sequences. */ - idx1now = backw1 = idx1cnt - 1; - } - } - while ((seq1len = weights[idx1arr[idx1now]++]) == 0); - - /* And the same for string `s2'. */ - if (seq2len == 0) - do - { - ++val2; - - if (backw2_stop != ~0ul) - { - /* The is something pushed. */ - if (backw2 == backw2_stop) - { - /* The last pushed character was handled. Continue - with forward characters. */ - if (idx2cnt < idx2max) - idx2now = idx2cnt; - else - /* Nothing anymore. The backward sequence ended with - the last sequence in the string. Note that seq2len - is still zero. */ - break; - } - else - idx2now = --backw2; - } - else - { - backw2_stop = idx2max; - - while (*us2 != L('\0')) - { - int32_t tmp = findidx (&us2); - rule2arr[idx2max] = tmp >> 24; - idx2arr[idx2max] = tmp & 0xffffff; - idx2cnt = idx2max++; - - if ((rulesets[rule2arr[idx2cnt] * nrules] - & sort_backward) == 0) - /* No more backward characters to push. */ - break; - ++idx2cnt; - } - - if (backw2_stop >= idx2cnt) - { - /* No sequence at all or just one. */ - if (idx2cnt == idx2max || backw2_stop > idx2cnt) - /* Note that seq1len is still zero. */ - break; - - backw2_stop = ~0ul; - idx2now = idx2cnt; - } - else - /* We pushed backward sequences. */ - idx2now = backw2 = idx2cnt - 1; - } - } - while ((seq2len = weights[idx2arr[idx2now]++]) == 0); - - /* See whether any or both strings are empty. */ - if (seq1len == 0 || seq2len == 0) - { - if (seq1len == seq2len) - /* Both ended. So far so good, both strings are equal at the - first level. */ - break; - - /* This means one string is shorter than the other. Find out - which one and return an appropriate value. */ - result = seq1len == 0 ? -1 : 1; - goto free_and_return; - } - - /* Test for position if necessary. */ - if (position && val1 != val2) - { - result = val1 - val2; - goto free_and_return; - } - - /* Compare the two sequences. */ - do - { - if (weights[idx1arr[idx1now]] != weights[idx2arr[idx2now]]) - { - /* The sequences differ. */ - result = weights[idx1arr[idx1now]] - weights[idx2arr[idx2now]]; - goto free_and_return; - } - - /* Increment the offsets. */ - ++idx1arr[idx1now]; - ++idx2arr[idx2now]; - - --seq1len; - --seq2len; - } - while (seq1len > 0 && seq2len > 0); - - if (position && seq1len != seq2len) - { - result = seq1len - seq2len; - goto free_and_return; - } - } - - /* Now the remaining passes over the weights. We now use the - indeces we found before. */ - for (pass = 1; pass < nrules; ++pass) - { - /* We assume that if a rule has defined `position' in one section - this is true for all of them. */ - idx1cnt = 0; - idx2cnt = 0; - backw1_stop = ~0ul; - backw2_stop = ~0ul; - backw1 = ~0ul; - backw2 = ~0ul; - position = rulesets[rule1arr[0] * nrules + pass] & sort_position; - - while (1) - { - val1 = 0; - val2 = 0; - - /* Get the next non-IGNOREd element for string `s1'. */ - if (seq1len == 0) - do - { - ++val1; - - if (backw1_stop != ~0ul) - { - /* The is something pushed. */ - if (backw1 == backw1_stop) - { - /* The last pushed character was handled. Continue - with forward characters. */ - if (idx1cnt < idx1max) - idx1now = idx1cnt; - else - { - /* Nothing anymore. The backward sequence - ended with the last sequence in the string. */ - idx1now = ~0ul; - break; - } - } - else - idx1now = --backw1; - } - else - { - backw1_stop = idx1cnt; - - while (idx1cnt < idx1max) - { - if ((rulesets[rule1arr[idx1cnt] * nrules + pass] - & sort_backward) == 0) - /* No more backward characters to push. */ - break; - ++idx1cnt; - } - - if (backw1_stop == idx1cnt) - { - /* No sequence at all or just one. */ - if (idx1cnt == idx1max) - /* Note that seq1len is still zero. */ - break; - - backw1_stop = ~0ul; - idx1now = idx1cnt++; - } - else - /* We pushed backward sequences. */ - idx1now = backw1 = idx1cnt - 1; - } - } - while ((seq1len = weights[idx1arr[idx1now]++]) == 0); - - /* And the same for string `s2'. */ - if (seq2len == 0) - do - { - ++val2; - - if (backw2_stop != ~0ul) - { - /* The is something pushed. */ - if (backw2 == backw2_stop) - { - /* The last pushed character was handled. Continue - with forward characters. */ - if (idx2cnt < idx2max) - idx2now = idx2cnt; - else - { - /* Nothing anymore. The backward sequence - ended with the last sequence in the string. */ - idx2now = ~0ul; - break; - } - } - else - idx2now = --backw2; - } - else - { - backw2_stop = idx2cnt; - - while (idx2cnt < idx2max) - { - if ((rulesets[rule2arr[idx2cnt] * nrules + pass] - & sort_backward) == 0) - /* No more backward characters to push. */ - break; - ++idx2cnt; - } - - if (backw2_stop == idx2cnt) - { - /* No sequence at all or just one. */ - if (idx2cnt == idx2max) - /* Note that seq2len is still zero. */ - break; - - backw2_stop = ~0ul; - idx2now = idx2cnt++; - } - else - /* We pushed backward sequences. */ - idx2now = backw2 = idx2cnt - 1; - } - } - while ((seq2len = weights[idx2arr[idx2now]++]) == 0); - - /* See whether any or both strings are empty. */ - if (seq1len == 0 || seq2len == 0) - { - if (seq1len == seq2len) - /* Both ended. So far so good, both strings are equal - at this level. */ - break; - - /* This means one string is shorter than the other. Find out - which one and return an appropriate value. */ - result = seq1len == 0 ? -1 : 1; - goto free_and_return; - } - - /* Test for position if necessary. */ - if (position && val1 != val2) - { - result = val1 - val2; - goto free_and_return; - } - - /* Compare the two sequences. */ - do - { - if (weights[idx1arr[idx1now]] != weights[idx2arr[idx2now]]) - { - /* The sequences differ. */ - result = (weights[idx1arr[idx1now]] - - weights[idx2arr[idx2now]]); - goto free_and_return; - } - - /* Increment the offsets. */ - ++idx1arr[idx1now]; - ++idx2arr[idx2now]; - - --seq1len; - --seq2len; - } - while (seq1len > 0 && seq2len > 0); - - if (position && seq1len != seq2len) - { - result = seq1len - seq2len; - goto free_and_return; - } - } - } - - /* Free the memory if needed. */ - free_and_return: - if (use_malloc) - free (idx1arr); - - return result; + return STRCOLL_L (s1, s2, _NL_CURRENT_LOCALE); } -#if !defined WIDE_CHAR_VERSION && !defined USE_IN_EXTENDED_LOCALE_MODEL +#if !defined WIDE_CHAR_VERSION libc_hidden_def (strcoll) #endif diff --git a/string/strcoll_l.c b/string/strcoll_l.c index 6611589ed0..c46921dcc9 100644 --- a/string/strcoll_l.c +++ b/string/strcoll_l.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1995,96,97,2002 Free Software Foundation, Inc. +/* Copyright (C) 1995,96,97,2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Ulrich Drepper <drepper@gnu.org>, 1995. @@ -17,7 +17,515 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define USE_IN_EXTENDED_LOCALE_MODEL 1 -#include <strcoll.c> +#include <assert.h> +#include <langinfo.h> +#include <locale.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#ifndef STRING_TYPE +# define STRING_TYPE char +# define USTRING_TYPE unsigned char +# define STRCOLL __strcoll_l +# define STRCMP strcmp +# define STRLEN strlen +# define WEIGHT_H "../locale/weight.h" +# define SUFFIX MB +# define L(arg) arg +#endif + +#define CONCAT(a,b) CONCAT1(a,b) +#define CONCAT1(a,b) a##b + +#include "../locale/localeinfo.h" + +int +STRCOLL (s1, s2, l) + const STRING_TYPE *s1; + const STRING_TYPE *s2; + __locale_t l; +{ + struct locale_data *current = l->__locales[LC_COLLATE]; + uint_fast32_t nrules = current->values[_NL_ITEM_INDEX (_NL_COLLATE_NRULES)].word; + /* We don't assign the following values right away since it might be + unnecessary in case there are no rules. */ + const unsigned char *rulesets; + const int32_t *table; + const USTRING_TYPE *weights; + const USTRING_TYPE *extra; + const int32_t *indirect; + uint_fast32_t pass; + int result = 0; + const USTRING_TYPE *us1; + const USTRING_TYPE *us2; + size_t s1len; + size_t s2len; + int32_t *idx1arr; + int32_t *idx2arr; + unsigned char *rule1arr; + unsigned char *rule2arr; + size_t idx1max; + size_t idx2max; + size_t idx1cnt; + size_t idx2cnt; + size_t idx1now; + size_t idx2now; + size_t backw1_stop; + size_t backw2_stop; + size_t backw1; + size_t backw2; + int val1; + int val2; + int position; + int seq1len; + int seq2len; + int use_malloc; + +#include WEIGHT_H + + if (nrules == 0) + return STRCMP (s1, s2); + + rulesets = (const unsigned char *) + current->values[_NL_ITEM_INDEX (_NL_COLLATE_RULESETS)].string; + table = (const int32_t *) + current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_TABLE,SUFFIX))].string; + weights = (const USTRING_TYPE *) + current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_WEIGHT,SUFFIX))].string; + extra = (const USTRING_TYPE *) + current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_EXTRA,SUFFIX))].string; + indirect = (const int32_t *) + current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_INDIRECT,SUFFIX))].string; + use_malloc = 0; + + assert (((uintptr_t) table) % __alignof__ (table[0]) == 0); + assert (((uintptr_t) weights) % __alignof__ (weights[0]) == 0); + assert (((uintptr_t) extra) % __alignof__ (extra[0]) == 0); + assert (((uintptr_t) indirect) % __alignof__ (indirect[0]) == 0); + + /* We need this a few times. */ + s1len = STRLEN (s1); + s2len = STRLEN (s2); + + /* Catch empty strings. */ + if (__builtin_expect (s1len == 0, 0) || __builtin_expect (s2len == 0, 0)) + return (s1len != 0) - (s2len != 0); + + /* We need the elements of the strings as unsigned values since they + are used as indeces. */ + us1 = (const USTRING_TYPE *) s1; + us2 = (const USTRING_TYPE *) s2; + + /* Perform the first pass over the string and while doing this find + and store the weights for each character. Since we want this to + be as fast as possible we are using `alloca' to store the temporary + values. But since there is no limit on the length of the string + we have to use `malloc' if the string is too long. We should be + very conservative here. + + Please note that the localedef programs makes sure that `position' + is not used at the first level. */ + if (! __libc_use_alloca (s1len + s2len)) + { + idx1arr = (int32_t *) malloc ((s1len + s2len) * (sizeof (int32_t) + 1)); + idx2arr = &idx1arr[s1len]; + rule1arr = (unsigned char *) &idx2arr[s2len]; + rule2arr = &rule1arr[s1len]; + + if (idx1arr == NULL) + /* No memory. Well, go with the stack then. + + XXX Once this implementation is stable we will handle this + differently. Instead of precomputing the indeces we will + do this in time. This means, though, that this happens for + every pass again. */ + goto try_stack; + use_malloc = 1; + } + else + { + try_stack: + idx1arr = (int32_t *) alloca (s1len * sizeof (int32_t)); + idx2arr = (int32_t *) alloca (s2len * sizeof (int32_t)); + rule1arr = (unsigned char *) alloca (s1len); + rule2arr = (unsigned char *) alloca (s2len); + } + + idx1cnt = 0; + idx2cnt = 0; + idx1max = 0; + idx2max = 0; + idx1now = 0; + idx2now = 0; + backw1_stop = ~0ul; + backw2_stop = ~0ul; + backw1 = ~0ul; + backw2 = ~0ul; + seq1len = 0; + seq2len = 0; + position = rulesets[0] & sort_position; + while (1) + { + val1 = 0; + val2 = 0; + + /* Get the next non-IGNOREd element for string `s1'. */ + if (seq1len == 0) + do + { + ++val1; + + if (backw1_stop != ~0ul) + { + /* The is something pushed. */ + if (backw1 == backw1_stop) + { + /* The last pushed character was handled. Continue + with forward characters. */ + if (idx1cnt < idx1max) + idx1now = idx1cnt; + else + /* Nothing anymore. The backward sequence ended with + the last sequence in the string. Note that seq1len + is still zero. */ + break; + } + else + idx1now = --backw1; + } + else + { + backw1_stop = idx1max; + + while (*us1 != L('\0')) + { + int32_t tmp = findidx (&us1); + rule1arr[idx1max] = tmp >> 24; + idx1arr[idx1max] = tmp & 0xffffff; + idx1cnt = idx1max++; + + if ((rulesets[rule1arr[idx1cnt] * nrules] + & sort_backward) == 0) + /* No more backward characters to push. */ + break; + ++idx1cnt; + } + + if (backw1_stop >= idx1cnt) + { + /* No sequence at all or just one. */ + if (idx1cnt == idx1max || backw1_stop > idx1cnt) + /* Note that seq1len is still zero. */ + break; + + backw1_stop = ~0ul; + idx1now = idx1cnt; + } + else + /* We pushed backward sequences. */ + idx1now = backw1 = idx1cnt - 1; + } + } + while ((seq1len = weights[idx1arr[idx1now]++]) == 0); + + /* And the same for string `s2'. */ + if (seq2len == 0) + do + { + ++val2; + + if (backw2_stop != ~0ul) + { + /* The is something pushed. */ + if (backw2 == backw2_stop) + { + /* The last pushed character was handled. Continue + with forward characters. */ + if (idx2cnt < idx2max) + idx2now = idx2cnt; + else + /* Nothing anymore. The backward sequence ended with + the last sequence in the string. Note that seq2len + is still zero. */ + break; + } + else + idx2now = --backw2; + } + else + { + backw2_stop = idx2max; + + while (*us2 != L('\0')) + { + int32_t tmp = findidx (&us2); + rule2arr[idx2max] = tmp >> 24; + idx2arr[idx2max] = tmp & 0xffffff; + idx2cnt = idx2max++; + + if ((rulesets[rule2arr[idx2cnt] * nrules] + & sort_backward) == 0) + /* No more backward characters to push. */ + break; + ++idx2cnt; + } + + if (backw2_stop >= idx2cnt) + { + /* No sequence at all or just one. */ + if (idx2cnt == idx2max || backw2_stop > idx2cnt) + /* Note that seq1len is still zero. */ + break; + + backw2_stop = ~0ul; + idx2now = idx2cnt; + } + else + /* We pushed backward sequences. */ + idx2now = backw2 = idx2cnt - 1; + } + } + while ((seq2len = weights[idx2arr[idx2now]++]) == 0); + + /* See whether any or both strings are empty. */ + if (seq1len == 0 || seq2len == 0) + { + if (seq1len == seq2len) + /* Both ended. So far so good, both strings are equal at the + first level. */ + break; + + /* This means one string is shorter than the other. Find out + which one and return an appropriate value. */ + result = seq1len == 0 ? -1 : 1; + goto free_and_return; + } + + /* Test for position if necessary. */ + if (position && val1 != val2) + { + result = val1 - val2; + goto free_and_return; + } + + /* Compare the two sequences. */ + do + { + if (weights[idx1arr[idx1now]] != weights[idx2arr[idx2now]]) + { + /* The sequences differ. */ + result = weights[idx1arr[idx1now]] - weights[idx2arr[idx2now]]; + goto free_and_return; + } + + /* Increment the offsets. */ + ++idx1arr[idx1now]; + ++idx2arr[idx2now]; + + --seq1len; + --seq2len; + } + while (seq1len > 0 && seq2len > 0); + + if (position && seq1len != seq2len) + { + result = seq1len - seq2len; + goto free_and_return; + } + } + + /* Now the remaining passes over the weights. We now use the + indeces we found before. */ + for (pass = 1; pass < nrules; ++pass) + { + /* We assume that if a rule has defined `position' in one section + this is true for all of them. */ + idx1cnt = 0; + idx2cnt = 0; + backw1_stop = ~0ul; + backw2_stop = ~0ul; + backw1 = ~0ul; + backw2 = ~0ul; + position = rulesets[rule1arr[0] * nrules + pass] & sort_position; + + while (1) + { + val1 = 0; + val2 = 0; + + /* Get the next non-IGNOREd element for string `s1'. */ + if (seq1len == 0) + do + { + ++val1; + + if (backw1_stop != ~0ul) + { + /* The is something pushed. */ + if (backw1 == backw1_stop) + { + /* The last pushed character was handled. Continue + with forward characters. */ + if (idx1cnt < idx1max) + idx1now = idx1cnt; + else + { + /* Nothing anymore. The backward sequence + ended with the last sequence in the string. */ + idx1now = ~0ul; + break; + } + } + else + idx1now = --backw1; + } + else + { + backw1_stop = idx1cnt; + + while (idx1cnt < idx1max) + { + if ((rulesets[rule1arr[idx1cnt] * nrules + pass] + & sort_backward) == 0) + /* No more backward characters to push. */ + break; + ++idx1cnt; + } + + if (backw1_stop == idx1cnt) + { + /* No sequence at all or just one. */ + if (idx1cnt == idx1max) + /* Note that seq1len is still zero. */ + break; + + backw1_stop = ~0ul; + idx1now = idx1cnt++; + } + else + /* We pushed backward sequences. */ + idx1now = backw1 = idx1cnt - 1; + } + } + while ((seq1len = weights[idx1arr[idx1now]++]) == 0); + + /* And the same for string `s2'. */ + if (seq2len == 0) + do + { + ++val2; + + if (backw2_stop != ~0ul) + { + /* The is something pushed. */ + if (backw2 == backw2_stop) + { + /* The last pushed character was handled. Continue + with forward characters. */ + if (idx2cnt < idx2max) + idx2now = idx2cnt; + else + { + /* Nothing anymore. The backward sequence + ended with the last sequence in the string. */ + idx2now = ~0ul; + break; + } + } + else + idx2now = --backw2; + } + else + { + backw2_stop = idx2cnt; + + while (idx2cnt < idx2max) + { + if ((rulesets[rule2arr[idx2cnt] * nrules + pass] + & sort_backward) == 0) + /* No more backward characters to push. */ + break; + ++idx2cnt; + } + + if (backw2_stop == idx2cnt) + { + /* No sequence at all or just one. */ + if (idx2cnt == idx2max) + /* Note that seq2len is still zero. */ + break; + + backw2_stop = ~0ul; + idx2now = idx2cnt++; + } + else + /* We pushed backward sequences. */ + idx2now = backw2 = idx2cnt - 1; + } + } + while ((seq2len = weights[idx2arr[idx2now]++]) == 0); + + /* See whether any or both strings are empty. */ + if (seq1len == 0 || seq2len == 0) + { + if (seq1len == seq2len) + /* Both ended. So far so good, both strings are equal + at this level. */ + break; + + /* This means one string is shorter than the other. Find out + which one and return an appropriate value. */ + result = seq1len == 0 ? -1 : 1; + goto free_and_return; + } + + /* Test for position if necessary. */ + if (position && val1 != val2) + { + result = val1 - val2; + goto free_and_return; + } + + /* Compare the two sequences. */ + do + { + if (weights[idx1arr[idx1now]] != weights[idx2arr[idx2now]]) + { + /* The sequences differ. */ + result = (weights[idx1arr[idx1now]] + - weights[idx2arr[idx2now]]); + goto free_and_return; + } + + /* Increment the offsets. */ + ++idx1arr[idx1now]; + ++idx2arr[idx2now]; + + --seq1len; + --seq2len; + } + while (seq1len > 0 && seq2len > 0); + + if (position && seq1len != seq2len) + { + result = seq1len - seq2len; + goto free_and_return; + } + } + } + + /* Free the memory if needed. */ + free_and_return: + if (use_malloc) + free (idx1arr); + + return result; +} +libc_hidden_def (STRCOLL) + +#ifndef WIDE_CHAR_VERSION weak_alias (__strcoll_l, strcoll_l) +#endif diff --git a/string/strxfrm.c b/string/strxfrm.c index 549d68cc1c..840f270675 100644 --- a/string/strxfrm.c +++ b/string/strxfrm.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1995-1999,2000,2001,2002,2003 Free Software Foundation, Inc. +/* Copyright (C) 1995-1999,2003, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Ulrich Drepper <drepper@cygnus.com>, 1995. @@ -17,451 +17,17 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include <assert.h> -#include <langinfo.h> -#include <locale.h> -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> #include <string.h> -#include <sys/param.h> +#include <locale/localeinfo.h> #ifndef STRING_TYPE # define STRING_TYPE char -# define USTRING_TYPE unsigned char -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define STRXFRM __strxfrm_l -# else -# define STRXFRM strxfrm -# endif -# define STRCMP strcmp -# define STRLEN strlen -# define STPNCPY __stpncpy -# define WEIGHT_H "../locale/weight.h" -# define SUFFIX MB -# define L(arg) arg +# define STRXFRM strxfrm +# define STRXFRM_L __strxfrm_l #endif -#define CONCAT(a,b) CONCAT1(a,b) -#define CONCAT1(a,b) a##b - -#include "../locale/localeinfo.h" - - -#ifndef WIDE_CHAR_VERSION - -/* We need UTF-8 encoding of numbers. */ -static int -utf8_encode (char *buf, int val) -{ - int retval; - - if (val < 0x80) - { - *buf++ = (char) val; - retval = 1; - } - else - { - int step; - - for (step = 2; step < 6; ++step) - if ((val & (~(uint32_t)0 << (5 * step + 1))) == 0) - break; - retval = step; - - *buf = (unsigned char) (~0xff >> step); - --step; - do - { - buf[step] = 0x80 | (val & 0x3f); - val >>= 6; - } - while (--step > 0); - *buf |= val; - } - - return retval; -} -#endif - - -#ifndef USE_IN_EXTENDED_LOCALE_MODEL size_t STRXFRM (STRING_TYPE *dest, const STRING_TYPE *src, size_t n) -#else -size_t -STRXFRM (STRING_TYPE *dest, const STRING_TYPE *src, size_t n, __locale_t l) -#endif { -#ifdef USE_IN_EXTENDED_LOCALE_MODEL - struct locale_data *current = l->__locales[LC_COLLATE]; - uint_fast32_t nrules = current->values[_NL_ITEM_INDEX (_NL_COLLATE_NRULES)].word; -#else - uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); -#endif - /* We don't assign the following values right away since it might be - unnecessary in case there are no rules. */ - const unsigned char *rulesets; - const int32_t *table; - const USTRING_TYPE *weights; - const USTRING_TYPE *extra; - const int32_t *indirect; - uint_fast32_t pass; - size_t needed; - const USTRING_TYPE *usrc; - size_t srclen = STRLEN (src); - int32_t *idxarr; - unsigned char *rulearr; - size_t idxmax; - size_t idxcnt; - int use_malloc; - -#include WEIGHT_H - - if (nrules == 0) - { - if (n != 0) - STPNCPY (dest, src, MIN (srclen + 1, n)); - - return srclen; - } - -#ifdef USE_IN_EXTENDED_LOCALE_MODEL - rulesets = (const unsigned char *) - current->values[_NL_ITEM_INDEX (_NL_COLLATE_RULESETS)].string; - table = (const int32_t *) - current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_TABLE,SUFFIX))].string; - weights = (const USTRING_TYPE *) - current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_WEIGHT,SUFFIX))].string; - extra = (const USTRING_TYPE *) - current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_EXTRA,SUFFIX))].string; - indirect = (const int32_t *) - current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_INDIRECT,SUFFIX))].string; -#else - rulesets = (const unsigned char *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_RULESETS); - table = (const int32_t *) - _NL_CURRENT (LC_COLLATE, CONCAT(_NL_COLLATE_TABLE,SUFFIX)); - weights = (const USTRING_TYPE *) - _NL_CURRENT (LC_COLLATE, CONCAT(_NL_COLLATE_WEIGHT,SUFFIX)); - extra = (const USTRING_TYPE *) - _NL_CURRENT (LC_COLLATE, CONCAT(_NL_COLLATE_EXTRA,SUFFIX)); - indirect = (const int32_t *) - _NL_CURRENT (LC_COLLATE, CONCAT(_NL_COLLATE_INDIRECT,SUFFIX)); -#endif - use_malloc = 0; - - assert (((uintptr_t) table) % __alignof__ (table[0]) == 0); - assert (((uintptr_t) weights) % __alignof__ (weights[0]) == 0); - assert (((uintptr_t) extra) % __alignof__ (extra[0]) == 0); - assert (((uintptr_t) indirect) % __alignof__ (indirect[0]) == 0); - - /* Handle an empty string as a special case. */ - if (srclen == 0) - { - if (n != 0) - *dest = L('\0'); - return 0; - } - - /* We need the elements of the string as unsigned values since they - are used as indeces. */ - usrc = (const USTRING_TYPE *) src; - - /* Perform the first pass over the string and while doing this find - and store the weights for each character. Since we want this to - be as fast as possible we are using `alloca' to store the temporary - values. But since there is no limit on the length of the string - we have to use `malloc' if the string is too long. We should be - very conservative here. */ - if (! __libc_use_alloca (srclen)) - { - idxarr = (int32_t *) malloc ((srclen + 1) * (sizeof (int32_t) + 1)); - rulearr = (unsigned char *) &idxarr[srclen]; - - if (idxarr == NULL) - /* No memory. Well, go with the stack then. - - XXX Once this implementation is stable we will handle this - differently. Instead of precomputing the indeces we will - do this in time. This means, though, that this happens for - every pass again. */ - goto try_stack; - use_malloc = 1; - } - else - { - try_stack: - idxarr = (int32_t *) alloca (srclen * sizeof (int32_t)); - rulearr = (unsigned char *) alloca (srclen + 1); - } - - idxmax = 0; - do - { - int32_t tmp = findidx (&usrc); - rulearr[idxmax] = tmp >> 24; - idxarr[idxmax] = tmp & 0xffffff; - - ++idxmax; - } - while (*usrc != L('\0')); - - /* This element is only read, the value never used but to determine - another value which then is ignored. */ - rulearr[idxmax] = '\0'; - - /* Now the passes over the weights. We now use the indeces we found - before. */ - needed = 0; - for (pass = 0; pass < nrules; ++pass) - { - size_t backw_stop = ~0ul; - int rule = rulesets[rulearr[0] * nrules + pass]; - /* We assume that if a rule has defined `position' in one section - this is true for all of them. */ - int position = rule & sort_position; - - if (position == 0) - { - for (idxcnt = 0; idxcnt < idxmax; ++idxcnt) - { - if ((rule & sort_forward) != 0) - { - size_t len; - - if (backw_stop != ~0ul) - { - /* Handle the pushed elements now. */ - size_t backw; - - for (backw = idxcnt - 1; backw >= backw_stop; --backw) - { - len = weights[idxarr[backw]++]; - - if (needed + len < n) - while (len-- > 0) - dest[needed++] = weights[idxarr[backw]++]; - else - { - /* No more characters fit into the buffer. */ - needed += len; - idxarr[backw] += len; - } - } - - backw_stop = ~0ul; - } - - /* Now handle the forward element. */ - len = weights[idxarr[idxcnt]++]; - if (needed + len < n) - while (len-- > 0) - dest[needed++] = weights[idxarr[idxcnt]++]; - else - { - /* No more characters fit into the buffer. */ - needed += len; - idxarr[idxcnt] += len; - } - } - else - { - /* Remember where the backwards series started. */ - if (backw_stop == ~0ul) - backw_stop = idxcnt; - } - - rule = rulesets[rulearr[idxcnt + 1] * nrules + pass]; - } - - - if (backw_stop != ~0ul) - { - /* Handle the pushed elements now. */ - size_t backw; - - backw = idxcnt; - while (backw > backw_stop) - { - size_t len = weights[idxarr[--backw]++]; - - if (needed + len < n) - while (len-- > 0) - dest[needed++] = weights[idxarr[backw]++]; - else - { - /* No more characters fit into the buffer. */ - needed += len; - idxarr[backw] += len; - } - } - } - } - else - { - int val = 1; -#ifndef WIDE_CHAR_VERSION - char buf[7]; - size_t buflen; -#endif - size_t i; - - for (idxcnt = 0; idxcnt < idxmax; ++idxcnt) - { - if ((rule & sort_forward) != 0) - { - size_t len; - - if (backw_stop != ~0ul) - { - /* Handle the pushed elements now. */ - size_t backw; - - for (backw = idxcnt - 1; backw >= backw_stop; --backw) - { - len = weights[idxarr[backw]++]; - if (len != 0) - { -#ifdef WIDE_CHAR_VERSION - if (needed + 1 + len < n) - { - dest[needed] = val; - for (i = 0; i < len; ++i) - dest[needed + 1 + i] = - weights[idxarr[backw] + i]; - } - needed += 1 + len; -#else - buflen = utf8_encode (buf, val); - if (needed + buflen + len < n) - { - for (i = 0; i < buflen; ++i) - dest[needed + i] = buf[i]; - for (i = 0; i < len; ++i) - dest[needed + buflen + i] = - weights[idxarr[backw] + i]; - } - needed += buflen + len; -#endif - idxarr[backw] += len; - val = 1; - } - else - ++val; - } - - backw_stop = ~0ul; - } - - /* Now handle the forward element. */ - len = weights[idxarr[idxcnt]++]; - if (len != 0) - { -#ifdef WIDE_CHAR_VERSION - if (needed + 1+ len < n) - { - dest[needed] = val; - for (i = 0; i < len; ++i) - dest[needed + 1 + i] = - weights[idxarr[idxcnt] + i]; - } - needed += 1 + len; -#else - buflen = utf8_encode (buf, val); - if (needed + buflen + len < n) - { - for (i = 0; i < buflen; ++i) - dest[needed + i] = buf[i]; - for (i = 0; i < len; ++i) - dest[needed + buflen + i] = - weights[idxarr[idxcnt] + i]; - } - needed += buflen + len; -#endif - idxarr[idxcnt] += len; - val = 1; - } - else - /* Note that we don't have to increment `idxarr[idxcnt]' - since the length is zero. */ - ++val; - } - else - { - /* Remember where the backwards series started. */ - if (backw_stop == ~0ul) - backw_stop = idxcnt; - } - - rule = rulesets[rulearr[idxcnt + 1] * nrules + pass]; - } - - if (backw_stop != ~0ul) - { - /* Handle the pushed elements now. */ - size_t backw; - - backw = idxmax - 1; - while (backw > backw_stop) - { - size_t len = weights[idxarr[--backw]++]; - if (len != 0) - { -#ifdef WIDE_CHAR_VERSION - if (needed + 1 + len < n) - { - dest[needed] = val; - for (i = 0; i < len; ++i) - dest[needed + 1 + i] = - weights[idxarr[backw] + i]; - } - needed += 1 + len; -#else - buflen = utf8_encode (buf, val); - if (needed + buflen + len < n) - { - for (i = 0; i < buflen; ++i) - dest[needed + i] = buf[i]; - for (i = 0; i < len; ++i) - dest[needed + buflen + i] = - weights[idxarr[backw] + i]; - } - needed += buflen + len; -#endif - idxarr[backw] += len; - val = 1; - } - else - ++val; - } - } - } - - /* Finally store the byte to separate the passes or terminate - the string. */ - if (needed < n) - dest[needed] = pass + 1 < nrules ? L('\1') : L('\0'); - ++needed; - } - - /* This is a little optimization: many collation specifications have - a `position' rule at the end and if no non-ignored character - is found the last \1 byte is immediately followed by a \0 byte - signalling this. We can avoid the \1 byte(s). */ - if (needed <= n && needed > 2 && dest[needed - 2] == L('\1')) - { - /* Remove the \1 byte. */ - --needed; - dest[needed - 1] = L('\0'); - } - - /* Free the memory if needed. */ - if (use_malloc) - free (idxarr); - - /* Return the number of bytes/words we need, but don't count the NUL - byte/word at the end. */ - return needed - 1; + return STRXFRM_L (dest, src, n, _NL_CURRENT_LOCALE); } diff --git a/string/strxfrm_l.c b/string/strxfrm_l.c index 264ab9bfe3..44b605168a 100644 --- a/string/strxfrm_l.c +++ b/string/strxfrm_l.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1995,96,97,2002 Free Software Foundation, Inc. +/* Copyright (C) 1995,96,97,2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Ulrich Drepper <drepper@gnu.org>, 1995. @@ -17,7 +17,430 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define USE_IN_EXTENDED_LOCALE_MODEL 1 -#include <strxfrm.c> +#include <assert.h> +#include <langinfo.h> +#include <locale.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/param.h> +#ifndef STRING_TYPE +# define STRING_TYPE char +# define USTRING_TYPE unsigned char +# define STRXFRM __strxfrm_l +# define STRCMP strcmp +# define STRLEN strlen +# define STPNCPY __stpncpy +# define WEIGHT_H "../locale/weight.h" +# define SUFFIX MB +# define L(arg) arg +#endif + +#define CONCAT(a,b) CONCAT1(a,b) +#define CONCAT1(a,b) a##b + +#include "../locale/localeinfo.h" + + +#ifndef WIDE_CHAR_VERSION + +/* We need UTF-8 encoding of numbers. */ +static int +utf8_encode (char *buf, int val) +{ + int retval; + + if (val < 0x80) + { + *buf++ = (char) val; + retval = 1; + } + else + { + int step; + + for (step = 2; step < 6; ++step) + if ((val & (~(uint32_t)0 << (5 * step + 1))) == 0) + break; + retval = step; + + *buf = (unsigned char) (~0xff >> step); + --step; + do + { + buf[step] = 0x80 | (val & 0x3f); + val >>= 6; + } + while (--step > 0); + *buf |= val; + } + + return retval; +} +#endif + + +size_t +STRXFRM (STRING_TYPE *dest, const STRING_TYPE *src, size_t n, __locale_t l) +{ + struct locale_data *current = l->__locales[LC_COLLATE]; + uint_fast32_t nrules = current->values[_NL_ITEM_INDEX (_NL_COLLATE_NRULES)].word; + /* We don't assign the following values right away since it might be + unnecessary in case there are no rules. */ + const unsigned char *rulesets; + const int32_t *table; + const USTRING_TYPE *weights; + const USTRING_TYPE *extra; + const int32_t *indirect; + uint_fast32_t pass; + size_t needed; + const USTRING_TYPE *usrc; + size_t srclen = STRLEN (src); + int32_t *idxarr; + unsigned char *rulearr; + size_t idxmax; + size_t idxcnt; + int use_malloc; + +#include WEIGHT_H + + if (nrules == 0) + { + if (n != 0) + STPNCPY (dest, src, MIN (srclen + 1, n)); + + return srclen; + } + + rulesets = (const unsigned char *) + current->values[_NL_ITEM_INDEX (_NL_COLLATE_RULESETS)].string; + table = (const int32_t *) + current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_TABLE,SUFFIX))].string; + weights = (const USTRING_TYPE *) + current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_WEIGHT,SUFFIX))].string; + extra = (const USTRING_TYPE *) + current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_EXTRA,SUFFIX))].string; + indirect = (const int32_t *) + current->values[_NL_ITEM_INDEX (CONCAT(_NL_COLLATE_INDIRECT,SUFFIX))].string; + use_malloc = 0; + + assert (((uintptr_t) table) % __alignof__ (table[0]) == 0); + assert (((uintptr_t) weights) % __alignof__ (weights[0]) == 0); + assert (((uintptr_t) extra) % __alignof__ (extra[0]) == 0); + assert (((uintptr_t) indirect) % __alignof__ (indirect[0]) == 0); + + /* Handle an empty string as a special case. */ + if (srclen == 0) + { + if (n != 0) + *dest = L('\0'); + return 0; + } + + /* We need the elements of the string as unsigned values since they + are used as indeces. */ + usrc = (const USTRING_TYPE *) src; + + /* Perform the first pass over the string and while doing this find + and store the weights for each character. Since we want this to + be as fast as possible we are using `alloca' to store the temporary + values. But since there is no limit on the length of the string + we have to use `malloc' if the string is too long. We should be + very conservative here. */ + if (! __libc_use_alloca (srclen)) + { + idxarr = (int32_t *) malloc ((srclen + 1) * (sizeof (int32_t) + 1)); + rulearr = (unsigned char *) &idxarr[srclen]; + + if (idxarr == NULL) + /* No memory. Well, go with the stack then. + + XXX Once this implementation is stable we will handle this + differently. Instead of precomputing the indeces we will + do this in time. This means, though, that this happens for + every pass again. */ + goto try_stack; + use_malloc = 1; + } + else + { + try_stack: + idxarr = (int32_t *) alloca (srclen * sizeof (int32_t)); + rulearr = (unsigned char *) alloca (srclen + 1); + } + + idxmax = 0; + do + { + int32_t tmp = findidx (&usrc); + rulearr[idxmax] = tmp >> 24; + idxarr[idxmax] = tmp & 0xffffff; + + ++idxmax; + } + while (*usrc != L('\0')); + + /* This element is only read, the value never used but to determine + another value which then is ignored. */ + rulearr[idxmax] = '\0'; + + /* Now the passes over the weights. We now use the indeces we found + before. */ + needed = 0; + for (pass = 0; pass < nrules; ++pass) + { + size_t backw_stop = ~0ul; + int rule = rulesets[rulearr[0] * nrules + pass]; + /* We assume that if a rule has defined `position' in one section + this is true for all of them. */ + int position = rule & sort_position; + + if (position == 0) + { + for (idxcnt = 0; idxcnt < idxmax; ++idxcnt) + { + if ((rule & sort_forward) != 0) + { + size_t len; + + if (backw_stop != ~0ul) + { + /* Handle the pushed elements now. */ + size_t backw; + + for (backw = idxcnt - 1; backw >= backw_stop; --backw) + { + len = weights[idxarr[backw]++]; + + if (needed + len < n) + while (len-- > 0) + dest[needed++] = weights[idxarr[backw]++]; + else + { + /* No more characters fit into the buffer. */ + needed += len; + idxarr[backw] += len; + } + } + + backw_stop = ~0ul; + } + + /* Now handle the forward element. */ + len = weights[idxarr[idxcnt]++]; + if (needed + len < n) + while (len-- > 0) + dest[needed++] = weights[idxarr[idxcnt]++]; + else + { + /* No more characters fit into the buffer. */ + needed += len; + idxarr[idxcnt] += len; + } + } + else + { + /* Remember where the backwards series started. */ + if (backw_stop == ~0ul) + backw_stop = idxcnt; + } + + rule = rulesets[rulearr[idxcnt + 1] * nrules + pass]; + } + + + if (backw_stop != ~0ul) + { + /* Handle the pushed elements now. */ + size_t backw; + + backw = idxcnt; + while (backw > backw_stop) + { + size_t len = weights[idxarr[--backw]++]; + + if (needed + len < n) + while (len-- > 0) + dest[needed++] = weights[idxarr[backw]++]; + else + { + /* No more characters fit into the buffer. */ + needed += len; + idxarr[backw] += len; + } + } + } + } + else + { + int val = 1; +#ifndef WIDE_CHAR_VERSION + char buf[7]; + size_t buflen; +#endif + size_t i; + + for (idxcnt = 0; idxcnt < idxmax; ++idxcnt) + { + if ((rule & sort_forward) != 0) + { + size_t len; + + if (backw_stop != ~0ul) + { + /* Handle the pushed elements now. */ + size_t backw; + + for (backw = idxcnt - 1; backw >= backw_stop; --backw) + { + len = weights[idxarr[backw]++]; + if (len != 0) + { +#ifdef WIDE_CHAR_VERSION + if (needed + 1 + len < n) + { + dest[needed] = val; + for (i = 0; i < len; ++i) + dest[needed + 1 + i] = + weights[idxarr[backw] + i]; + } + needed += 1 + len; +#else + buflen = utf8_encode (buf, val); + if (needed + buflen + len < n) + { + for (i = 0; i < buflen; ++i) + dest[needed + i] = buf[i]; + for (i = 0; i < len; ++i) + dest[needed + buflen + i] = + weights[idxarr[backw] + i]; + } + needed += buflen + len; +#endif + idxarr[backw] += len; + val = 1; + } + else + ++val; + } + + backw_stop = ~0ul; + } + + /* Now handle the forward element. */ + len = weights[idxarr[idxcnt]++]; + if (len != 0) + { +#ifdef WIDE_CHAR_VERSION + if (needed + 1+ len < n) + { + dest[needed] = val; + for (i = 0; i < len; ++i) + dest[needed + 1 + i] = + weights[idxarr[idxcnt] + i]; + } + needed += 1 + len; +#else + buflen = utf8_encode (buf, val); + if (needed + buflen + len < n) + { + for (i = 0; i < buflen; ++i) + dest[needed + i] = buf[i]; + for (i = 0; i < len; ++i) + dest[needed + buflen + i] = + weights[idxarr[idxcnt] + i]; + } + needed += buflen + len; +#endif + idxarr[idxcnt] += len; + val = 1; + } + else + /* Note that we don't have to increment `idxarr[idxcnt]' + since the length is zero. */ + ++val; + } + else + { + /* Remember where the backwards series started. */ + if (backw_stop == ~0ul) + backw_stop = idxcnt; + } + + rule = rulesets[rulearr[idxcnt + 1] * nrules + pass]; + } + + if (backw_stop != ~0ul) + { + /* Handle the pushed elements now. */ + size_t backw; + + backw = idxmax - 1; + while (backw > backw_stop) + { + size_t len = weights[idxarr[--backw]++]; + if (len != 0) + { +#ifdef WIDE_CHAR_VERSION + if (needed + 1 + len < n) + { + dest[needed] = val; + for (i = 0; i < len; ++i) + dest[needed + 1 + i] = + weights[idxarr[backw] + i]; + } + needed += 1 + len; +#else + buflen = utf8_encode (buf, val); + if (needed + buflen + len < n) + { + for (i = 0; i < buflen; ++i) + dest[needed + i] = buf[i]; + for (i = 0; i < len; ++i) + dest[needed + buflen + i] = + weights[idxarr[backw] + i]; + } + needed += buflen + len; +#endif + idxarr[backw] += len; + val = 1; + } + else + ++val; + } + } + } + + /* Finally store the byte to separate the passes or terminate + the string. */ + if (needed < n) + dest[needed] = pass + 1 < nrules ? L('\1') : L('\0'); + ++needed; + } + + /* This is a little optimization: many collation specifications have + a `position' rule at the end and if no non-ignored character + is found the last \1 byte is immediately followed by a \0 byte + signalling this. We can avoid the \1 byte(s). */ + if (needed <= n && needed > 2 && dest[needed - 2] == L('\1')) + { + /* Remove the \1 byte. */ + --needed; + dest[needed - 1] = L('\0'); + } + + /* Free the memory if needed. */ + if (use_malloc) + free (idxarr); + + /* Return the number of bytes/words we need, but don't count the NUL + byte/word at the end. */ + return needed - 1; +} +libc_hidden_def (STRXFRM) + +#ifndef WIDE_CHAR_VERSION weak_alias (__strxfrm_l, strxfrm_l) +#endif diff --git a/sysdeps/generic/strtol.c b/sysdeps/generic/strtol.c index 953c6c4a90..02ec19aabe 100644 --- a/sysdeps/generic/strtol.c +++ b/sysdeps/generic/strtol.c @@ -1,5 +1,5 @@ /* Convert string representation of a number into an integer value. - Copyright (C) 1991,92,94,95,96,97,98,99,2000,2001,2002,2003 + Copyright (C) 1991,92,94,95,96,97,98,99,2000,2001,2002,2003,2004 Free Software Foundation, Inc. This file is part of the GNU C Library. @@ -18,46 +18,10 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#if HAVE_CONFIG_H -# include <config.h> -#endif - -#ifdef _LIBC -# define USE_NUMBER_GROUPING -# define STDC_HEADERS -# define HAVE_LIMITS_H -#endif - -#include <ctype.h> -#include <errno.h> -#ifndef errno -extern int errno; -#endif -#ifndef __set_errno -# define __set_errno(Val) errno = (Val) -#endif - -#ifdef HAVE_LIMITS_H -# include <limits.h> -#endif - -#ifdef STDC_HEADERS -# include <stddef.h> -# include <stdlib.h> -# include <string.h> -# include <locale.h> -#else -# ifndef NULL -# define NULL 0 -# endif -#endif - -#ifdef USE_NUMBER_GROUPING -# include "../locale/localeinfo.h" -#endif +#include <stdlib.h> +#include <wchar.h> +#include <locale/localeinfo.h> -/* Nonzero if we are defining `strtoul' or `strtoull', operating on - unsigned integers. */ #ifndef UNSIGNED # define UNSIGNED 0 # define INT LONG int @@ -65,508 +29,83 @@ extern int errno; # define INT unsigned LONG int #endif -/* Determine the name. */ -#ifdef USE_IN_EXTENDED_LOCALE_MODEL -# if UNSIGNED -# ifdef USE_WIDE_CHAR -# ifdef QUAD -# define strtol __wcstoull_l -# else -# define strtol __wcstoul_l -# endif +#if UNSIGNED +# ifdef USE_WIDE_CHAR +# ifdef QUAD +# define strtol wcstoull +# define __strtol_l __wcstoull_l # else -# ifdef QUAD -# define strtol __strtoull_l -# else -# define strtol __strtoul_l -# endif +# define strtol wcstoul +# define __strtol_l __wcstoul_l # endif # else -# ifdef USE_WIDE_CHAR -# ifdef QUAD -# define strtol __wcstoll_l -# else -# define strtol __wcstol_l -# endif +# ifdef QUAD +# define strtol strtoull +# define __strtol_l __strtoull_l # else -# ifdef QUAD -# define strtol __strtoll_l -# else -# define strtol __strtol_l -# endif +# define strtol strtoul +# define __strtol_l __strtoul_l # endif # endif #else -# if UNSIGNED -# ifdef USE_WIDE_CHAR -# ifdef QUAD -# define strtol wcstoull -# else -# define strtol wcstoul -# endif +# ifdef USE_WIDE_CHAR +# ifdef QUAD +# define strtol wcstoll +# define __strtol_l __wcstoll_l # else -# ifdef QUAD -# define strtol strtoull -# else -# define strtol strtoul -# endif +# define strtol wcstol +# define __strtol_l __wcstol_l # endif # else -# ifdef USE_WIDE_CHAR -# ifdef QUAD -# define strtol wcstoll -# else -# define strtol wcstol -# endif -# else -# ifdef QUAD -# define strtol strtoll -# endif +# ifdef QUAD +# define strtol strtoll +# define __strtol_l __strtoll_l # endif # endif #endif + /* If QUAD is defined, we are defining `strtoll' or `strtoull', operating on `long long int's. */ #ifdef QUAD # define LONG long long -# define STRTOL_LONG_MIN LONG_LONG_MIN -# define STRTOL_LONG_MAX LONG_LONG_MAX -# define STRTOL_ULONG_MAX ULONG_LONG_MAX -# if __GNUC__ == 2 && __GNUC_MINOR__ < 7 - /* Work around gcc bug with using this constant. */ - static const unsigned long long int maxquad = ULONG_LONG_MAX; -# undef STRTOL_ULONG_MAX -# define STRTOL_ULONG_MAX maxquad -# endif #else # define LONG long - -# ifndef ULONG_MAX -# define ULONG_MAX ((unsigned long) ~(unsigned long) 0) -# endif -# ifndef LONG_MAX -# define LONG_MAX ((long int) (ULONG_MAX >> 1)) -# endif -# define STRTOL_LONG_MIN LONG_MIN -# define STRTOL_LONG_MAX LONG_MAX -# define STRTOL_ULONG_MAX ULONG_MAX -#endif - - -/* 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 <wchar.h> -#endif #ifdef USE_WIDE_CHAR -# include <wctype.h> -# define L_(Ch) L##Ch -# define UCHAR_TYPE wint_t # define STRING_TYPE wchar_t -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define ISSPACE(Ch) __iswspace_l ((Ch), loc) -# define ISALPHA(Ch) __iswalpha_l ((Ch), loc) -# define TOUPPER(Ch) __towupper_l ((Ch), loc) -# else -# define ISSPACE(Ch) iswspace (Ch) -# define ISALPHA(Ch) iswalpha (Ch) -# define TOUPPER(Ch) towupper (Ch) -# endif -# else -# if defined _LIBC \ - || defined STDC_HEADERS || (!defined isascii && !defined HAVE_ISASCII) -# define IN_CTYPE_DOMAIN(c) 1 -# else -# define IN_CTYPE_DOMAIN(c) isascii(c) -# endif -# define L_(Ch) Ch -# define UCHAR_TYPE unsigned char -# define STRING_TYPE char -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define ISSPACE(Ch) __isspace_l ((Ch), loc) -# define ISALPHA(Ch) __isalpha_l ((Ch), loc) -# define TOUPPER(Ch) __toupper_l ((Ch), loc) -# else -# define ISSPACE(Ch) (IN_CTYPE_DOMAIN (Ch) && isspace (Ch)) -# define ISALPHA(Ch) (IN_CTYPE_DOMAIN (Ch) && isalpha (Ch)) -# define TOUPPER(Ch) (IN_CTYPE_DOMAIN (Ch) ? toupper (Ch) : (Ch)) -# endif -#endif - -#ifdef __STDC__ -# define INTERNAL(X) INTERNAL1(X) -# define INTERNAL1(X) __##X##_internal -# define WEAKNAME(X) WEAKNAME1(X) #else -# define INTERNAL(X) __/**/X/**/_internal +# define STRING_TYPE char #endif -#ifdef USE_NUMBER_GROUPING -/* This file defines a function to check for correct grouping. */ -# include "grouping.h" -#endif + +#define INTERNAL(X) INTERNAL1(X) +#define INTERNAL1(X) __##X##_internal +extern INT INTERNAL (__strtol_l) (const STRING_TYPE *, STRING_TYPE **, int, + int, __locale_t); -/* Convert NPTR to an `unsigned long int' or `long int' in base BASE. - If BASE is 0 the base is determined by the presence of a leading - zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal. - If BASE is < 2 or > 36, it is reset to 10. - If ENDPTR is not NULL, a pointer to the character after the last - one converted is stored in *ENDPTR. */ INT -INTERNAL (strtol) (nptr, endptr, base, group LOCALE_PARAM) +INTERNAL (strtol) (nptr, endptr, base, group) const STRING_TYPE *nptr; STRING_TYPE **endptr; int base; int group; - LOCALE_PARAM_DECL { - int negative; - register unsigned LONG int cutoff; - register unsigned int cutlim; - register unsigned LONG int i; - register const STRING_TYPE *s; - register UCHAR_TYPE c; - const STRING_TYPE *save, *end; - int overflow; -#ifndef USE_WIDE_CHAR - size_t cnt; -#endif - -#ifdef USE_NUMBER_GROUPING -# ifdef USE_IN_EXTENDED_LOCALE_MODEL - struct locale_data *current = loc->__locales[LC_NUMERIC]; -# endif - /* The thousands character of the current locale. */ -# ifdef USE_WIDE_CHAR - wchar_t thousands = L'\0'; -# else - const char *thousands = NULL; - size_t thousands_len = 0; -# endif - /* The numeric grouping specification of the current locale, - in the format described in <locale.h>. */ - const char *grouping; - - if (__builtin_expect (group, 0)) - { - 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 -# ifdef _LIBC - thousands = _NL_CURRENT_WORD (LC_NUMERIC, - _NL_NUMERIC_THOUSANDS_SEP_WC); -# endif - if (thousands == L'\0') - grouping = NULL; -# else -# ifdef _LIBC - thousands = _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP); -# endif - if (*thousands == '\0') - { - thousands = NULL; - grouping = NULL; - } -# endif - } - } - else - grouping = NULL; -#endif - - if (base < 0 || base == 1 || base > 36) - { - __set_errno (EINVAL); - return 0; - } - - save = s = nptr; - - /* Skip white space. */ - while (ISSPACE (*s)) - ++s; - if (__builtin_expect (*s == L_('\0'), 0)) - goto noconv; - - /* Check for a sign. */ - negative = 0; - if (*s == L_('-')) - { - negative = 1; - ++s; - } - else if (*s == L_('+')) - ++s; - - /* Recognize number prefix and if BASE is zero, figure it out ourselves. */ - if (*s == L_('0')) - { - if ((base == 0 || base == 16) && TOUPPER (s[1]) == L_('X')) - { - s += 2; - base = 16; - } - else if (base == 0) - base = 8; - } - else if (base == 0) - base = 10; - - /* Save the pointer so we can check later if anything happened. */ - save = s; - -#ifdef USE_NUMBER_GROUPING - if (base != 10) - grouping = NULL; - - if (__builtin_expect (grouping != NULL, 0)) - { -# ifndef USE_WIDE_CHAR - thousands_len = strlen (thousands); -# endif - - /* Find the end of the digit string and check its grouping. */ - end = s; - if ( -# ifdef USE_WIDE_CHAR - *s != thousands -# else - ({ for (cnt = 0; cnt < thousands_len; ++cnt) - if (thousands[cnt] != end[cnt]) - break; - cnt < thousands_len; }) -# endif - ) - { - for (c = *end; c != L_('\0'); c = *++end) - if (((STRING_TYPE) c < L_('0') || (STRING_TYPE) c > L_('9')) -# ifdef USE_WIDE_CHAR - && (wchar_t) c != thousands -# else - && ({ for (cnt = 0; cnt < thousands_len; ++cnt) - if (thousands[cnt] != end[cnt]) - break; - cnt < thousands_len; }) -# endif - && (!ISALPHA (c) - || (int) (TOUPPER (c) - L_('A') + 10) >= base)) - break; - -# ifdef USE_WIDE_CHAR - end = __correctly_grouped_prefixwc (s, end, thousands, grouping); -# else - end = __correctly_grouped_prefixmb (s, end, thousands, grouping); -# endif - } - } - else -#endif - end = NULL; - - cutoff = STRTOL_ULONG_MAX / (unsigned LONG int) base; - cutlim = STRTOL_ULONG_MAX % (unsigned LONG int) base; - - overflow = 0; - i = 0; - c = *s; - if (sizeof (long int) != sizeof (LONG int)) - { - unsigned long int j = 0; - unsigned long int jmax = ULONG_MAX / base; - - for (;c != L_('\0'); c = *++s) - { - if (s == end) - break; - if (c >= L_('0') && c <= L_('9')) - c -= L_('0'); -#ifdef USE_NUMBER_GROUPING -# ifdef USE_WIDE_CHAR - else if (grouping && (wchar_t) c == thousands) - continue; -# else - else if (thousands_len) - { - for (cnt = 0; cnt < thousands_len; ++cnt) - if (thousands[cnt] != s[cnt]) - break; - if (cnt == thousands_len) - { - s += thousands_len - 1; - continue; - } - if (ISALPHA (c)) - c = TOUPPER (c) - L_('A') + 10; - else - break; - } -# endif -#endif - else if (ISALPHA (c)) - c = TOUPPER (c) - L_('A') + 10; - else - break; - if ((int) c >= base) - break; - /* Note that we never can have an overflow. */ - else if (j >= jmax) - { - /* We have an overflow. Now use the long representation. */ - i = (unsigned LONG int) j; - goto use_long; - } - else - j = j * (unsigned long int) base + c; - } - - i = (unsigned LONG int) j; - } - else - for (;c != L_('\0'); c = *++s) - { - if (s == end) - break; - if (c >= L_('0') && c <= L_('9')) - c -= L_('0'); -#ifdef USE_NUMBER_GROUPING -# ifdef USE_WIDE_CHAR - else if (grouping && (wchar_t) c == thousands) - continue; -# else - else if (thousands_len) - { - for (cnt = 0; cnt < thousands_len; ++cnt) - if (thousands[cnt] != s[cnt]) - break; - if (cnt == thousands_len) - { - s += thousands_len - 1; - continue; - } - if (ISALPHA (c)) - c = TOUPPER (c) - L_('A') + 10; - else - break; - } -# endif -#endif - else if (ISALPHA (c)) - c = TOUPPER (c) - L_('A') + 10; - else - break; - if ((int) c >= base) - break; - /* Check for overflow. */ - if (i > cutoff || (i == cutoff && c > cutlim)) - overflow = 1; - else - { - use_long: - i *= (unsigned LONG int) base; - i += c; - } - } - - /* Check if anything actually happened. */ - if (s == save) - goto noconv; - - /* Store in ENDPTR the address of one character - past the last character we converted. */ - if (endptr != NULL) - *endptr = (STRING_TYPE *) s; - -#if !UNSIGNED - /* Check for a value that is within the range of - `unsigned LONG int', but outside the range of `LONG int'. */ - if (overflow == 0 - && i > (negative - ? -((unsigned LONG int) (STRTOL_LONG_MIN + 1)) + 1 - : (unsigned LONG int) STRTOL_LONG_MAX)) - overflow = 1; -#endif - - if (__builtin_expect (overflow, 0)) - { - __set_errno (ERANGE); -#if UNSIGNED - return STRTOL_ULONG_MAX; -#else - return negative ? STRTOL_LONG_MIN : STRTOL_LONG_MAX; -#endif - } - - /* Return the result of the appropriate sign. */ - return negative ? -i : i; - -noconv: - /* We must handle a special case here: the base is 0 or 16 and the - first two characters are '0' and 'x', but the rest are no - hexadecimal digits. This is no error case. We return 0 and - ENDPTR points to the `x`. */ - if (endptr != NULL) - { - if (save - nptr >= 2 && TOUPPER (save[-1]) == L_('X') - && save[-2] == L_('0')) - *endptr = (STRING_TYPE *) &save[-1]; - else - /* There was no number to convert. */ - *endptr = (STRING_TYPE *) nptr; - } - - return 0L; + return INTERNAL (__strtol_l) (nptr, endptr, base, group, _NL_CURRENT_LOCALE); } -#if defined _LIBC \ - && !(defined USE_IN_EXTENDED_LOCALE_MODEL && defined USE_WIDE_CHAR) libc_hidden_def (INTERNAL (strtol)) -#endif - -/* External user entry point. */ - -#if _LIBC - 0 == 0 -# undef PARAMS -# if defined (__STDC__) && __STDC__ -# define PARAMS(Args) Args -# else -# define PARAMS(Args) () -# endif - -/* Prototype. */ -INT strtol PARAMS ((const STRING_TYPE *nptr, STRING_TYPE **endptr, int base)); -#endif INT -#ifdef weak_function -weak_function -#endif -strtol (nptr, endptr, base LOCALE_PARAM) +strtol (nptr, endptr, base) const STRING_TYPE *nptr; STRING_TYPE **endptr; int base; - LOCALE_PARAM_DECL { - return INTERNAL (strtol) (nptr, endptr, base, 0 LOCALE_PARAM); + return INTERNAL (__strtol_l) (nptr, endptr, base, 0, _NL_CURRENT_LOCALE); } diff --git a/sysdeps/generic/strtol_l.c b/sysdeps/generic/strtol_l.c index e4c3140a03..a3800d6f78 100644 --- a/sysdeps/generic/strtol_l.c +++ b/sysdeps/generic/strtol_l.c @@ -1,5 +1,5 @@ /* Convert string representing a number to integer value, using given locale. - Copyright (C) 1997, 2002 Free Software Foundation, Inc. + Copyright (C) 1997, 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -18,13 +18,496 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define USE_IN_EXTENDED_LOCALE_MODEL 1 +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef _LIBC +# define USE_NUMBER_GROUPING +# define STDC_HEADERS +# define HAVE_LIMITS_H +#endif + +#include <ctype.h> +#include <errno.h> +#ifndef __set_errno +# define __set_errno(Val) errno = (Val) +#endif + +#ifdef HAVE_LIMITS_H +# include <limits.h> +#endif + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> #include <xlocale.h> -extern long int ____strtol_l_internal (const char *, char **, int, int, - __locale_t); +#ifdef USE_NUMBER_GROUPING +# include "../locale/localeinfo.h" +#endif + +/* Nonzero if we are defining `strtoul' or `strtoull', operating on + unsigned integers. */ +#ifndef UNSIGNED +# define UNSIGNED 0 +# define INT LONG int +#else +# define INT unsigned LONG int +#endif + +/* Determine the name. */ +#if UNSIGNED +# ifdef USE_WIDE_CHAR +# ifdef QUAD +# define strtol_l wcstoull_l +# else +# define strtol_l wcstoul_l +# endif +# else +# ifdef QUAD +# define strtol_l strtoull_l +# else +# define strtol_l strtoul_l +# endif +# endif +#else +# ifdef USE_WIDE_CHAR +# ifdef QUAD +# define strtol_l wcstoll_l +# else +# define strtol_l wcstol_l +# endif +# else +# ifdef QUAD +# define strtol_l strtoll_l +# else +# define strtol_l strtol_l +# endif +# endif +#endif + +#define __strtol_l __strtol_l2(strtol_l) +#define __strtol_l2(name) __strtol_l3(name) +#define __strtol_l3(name) __##name + + +/* If QUAD is defined, we are defining `strtoll' or `strtoull', + operating on `long long int's. */ +#ifdef QUAD +# define LONG long long +# define STRTOL_LONG_MIN LONG_LONG_MIN +# define STRTOL_LONG_MAX LONG_LONG_MAX +# define STRTOL_ULONG_MAX ULONG_LONG_MAX +#else +# define LONG long + +# ifndef ULONG_MAX +# define ULONG_MAX ((unsigned long int) ~(unsigned long int) 0) +# endif +# ifndef LONG_MAX +# define LONG_MAX ((long int) (ULONG_MAX >> 1)) +# endif +# define STRTOL_LONG_MIN LONG_MIN +# define STRTOL_LONG_MAX LONG_MAX +# define STRTOL_ULONG_MAX ULONG_MAX +#endif + + +/* We use this code 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 and + _NL_CURRENT_WORD macros. */ +#undef _NL_CURRENT +#define _NL_CURRENT(category, item) \ + (current->values[_NL_ITEM_INDEX (item)].string) +#undef _NL_CURRENT_WORD +#define _NL_CURRENT_WORD(category, item) \ + ((uint32_t) current->values[_NL_ITEM_INDEX (item)].word) + +#if defined _LIBC || defined HAVE_WCHAR_H +# include <wchar.h> +#endif + +#ifdef USE_WIDE_CHAR +# include <wctype.h> +# define L_(Ch) L##Ch +# define UCHAR_TYPE wint_t +# define STRING_TYPE wchar_t +# define ISSPACE(Ch) __iswspace_l ((Ch), loc) +# define ISALPHA(Ch) __iswalpha_l ((Ch), loc) +# define TOUPPER(Ch) __towupper_l ((Ch), loc) +#else +# if defined _LIBC \ + || defined STDC_HEADERS || (!defined isascii && !defined HAVE_ISASCII) +# define IN_CTYPE_DOMAIN(c) 1 +# else +# define IN_CTYPE_DOMAIN(c) isascii(c) +# endif +# define L_(Ch) Ch +# define UCHAR_TYPE unsigned char +# define STRING_TYPE char +# define ISSPACE(Ch) __isspace_l ((Ch), loc) +# define ISALPHA(Ch) __isalpha_l ((Ch), loc) +# define TOUPPER(Ch) __toupper_l ((Ch), loc) +#endif + +#define INTERNAL(X) INTERNAL1(X) +#define INTERNAL1(X) __##X##_internal +#define WEAKNAME(X) WEAKNAME1(X) + +#ifdef USE_NUMBER_GROUPING +/* This file defines a function to check for correct grouping. */ +# include "grouping.h" +#endif + + + +/* Convert NPTR to an `unsigned long int' or `long int' in base BASE. + If BASE is 0 the base is determined by the presence of a leading + zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal. + If BASE is < 2 or > 36, it is reset to 10. + If ENDPTR is not NULL, a pointer to the character after the last + one converted is stored in *ENDPTR. */ + +INT +INTERNAL (__strtol_l) (nptr, endptr, base, group, loc) + const STRING_TYPE *nptr; + STRING_TYPE **endptr; + int base; + int group; + __locale_t loc; +{ + int negative; + register unsigned LONG int cutoff; + register unsigned int cutlim; + register unsigned LONG int i; + register const STRING_TYPE *s; + register UCHAR_TYPE c; + const STRING_TYPE *save, *end; + int overflow; +#ifndef USE_WIDE_CHAR + size_t cnt; +#endif + +#ifdef USE_NUMBER_GROUPING + struct locale_data *current = loc->__locales[LC_NUMERIC]; + /* The thousands character of the current locale. */ +# ifdef USE_WIDE_CHAR + wchar_t thousands = L'\0'; +# else + const char *thousands = NULL; + size_t thousands_len = 0; +# endif + /* The numeric grouping specification of the current locale, + in the format described in <locale.h>. */ + const char *grouping; + + if (__builtin_expect (group, 0)) + { + 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 +# ifdef _LIBC + thousands = _NL_CURRENT_WORD (LC_NUMERIC, + _NL_NUMERIC_THOUSANDS_SEP_WC); +# endif + if (thousands == L'\0') + grouping = NULL; +# else +# ifdef _LIBC + thousands = _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP); +# endif + if (*thousands == '\0') + { + thousands = NULL; + grouping = NULL; + } +# endif + } + } + else + grouping = NULL; +#endif + + if (base < 0 || base == 1 || base > 36) + { + __set_errno (EINVAL); + return 0; + } + + save = s = nptr; + + /* Skip white space. */ + while (ISSPACE (*s)) + ++s; + if (__builtin_expect (*s == L_('\0'), 0)) + goto noconv; + + /* Check for a sign. */ + negative = 0; + if (*s == L_('-')) + { + negative = 1; + ++s; + } + else if (*s == L_('+')) + ++s; + + /* Recognize number prefix and if BASE is zero, figure it out ourselves. */ + if (*s == L_('0')) + { + if ((base == 0 || base == 16) && TOUPPER (s[1]) == L_('X')) + { + s += 2; + base = 16; + } + else if (base == 0) + base = 8; + } + else if (base == 0) + base = 10; + + /* Save the pointer so we can check later if anything happened. */ + save = s; + +#ifdef USE_NUMBER_GROUPING + if (base != 10) + grouping = NULL; + + if (__builtin_expect (grouping != NULL, 0)) + { +# ifndef USE_WIDE_CHAR + thousands_len = strlen (thousands); +# endif + + /* Find the end of the digit string and check its grouping. */ + end = s; + if ( +# ifdef USE_WIDE_CHAR + *s != thousands +# else + ({ for (cnt = 0; cnt < thousands_len; ++cnt) + if (thousands[cnt] != end[cnt]) + break; + cnt < thousands_len; }) +# endif + ) + { + for (c = *end; c != L_('\0'); c = *++end) + if (((STRING_TYPE) c < L_('0') || (STRING_TYPE) c > L_('9')) +# ifdef USE_WIDE_CHAR + && (wchar_t) c != thousands +# else + && ({ for (cnt = 0; cnt < thousands_len; ++cnt) + if (thousands[cnt] != end[cnt]) + break; + cnt < thousands_len; }) +# endif + && (!ISALPHA (c) + || (int) (TOUPPER (c) - L_('A') + 10) >= base)) + break; + +# ifdef USE_WIDE_CHAR + end = __correctly_grouped_prefixwc (s, end, thousands, grouping); +# else + end = __correctly_grouped_prefixmb (s, end, thousands, grouping); +# endif + } + } + else +#endif + end = NULL; + + cutoff = STRTOL_ULONG_MAX / (unsigned LONG int) base; + cutlim = STRTOL_ULONG_MAX % (unsigned LONG int) base; + + overflow = 0; + i = 0; + c = *s; + if (sizeof (long int) != sizeof (LONG int)) + { + unsigned long int j = 0; + unsigned long int jmax = ULONG_MAX / base; + + for (;c != L_('\0'); c = *++s) + { + if (s == end) + break; + if (c >= L_('0') && c <= L_('9')) + c -= L_('0'); +#ifdef USE_NUMBER_GROUPING +# ifdef USE_WIDE_CHAR + else if (grouping && (wchar_t) c == thousands) + continue; +# else + else if (thousands_len) + { + for (cnt = 0; cnt < thousands_len; ++cnt) + if (thousands[cnt] != s[cnt]) + break; + if (cnt == thousands_len) + { + s += thousands_len - 1; + continue; + } + if (ISALPHA (c)) + c = TOUPPER (c) - L_('A') + 10; + else + break; + } +# endif +#endif + else if (ISALPHA (c)) + c = TOUPPER (c) - L_('A') + 10; + else + break; + if ((int) c >= base) + break; + /* Note that we never can have an overflow. */ + else if (j >= jmax) + { + /* We have an overflow. Now use the long representation. */ + i = (unsigned LONG int) j; + goto use_long; + } + else + j = j * (unsigned long int) base + c; + } + + i = (unsigned LONG int) j; + } + else + for (;c != L_('\0'); c = *++s) + { + if (s == end) + break; + if (c >= L_('0') && c <= L_('9')) + c -= L_('0'); +#ifdef USE_NUMBER_GROUPING +# ifdef USE_WIDE_CHAR + else if (grouping && (wchar_t) c == thousands) + continue; +# else + else if (thousands_len) + { + for (cnt = 0; cnt < thousands_len; ++cnt) + if (thousands[cnt] != s[cnt]) + break; + if (cnt == thousands_len) + { + s += thousands_len - 1; + continue; + } + if (ISALPHA (c)) + c = TOUPPER (c) - L_('A') + 10; + else + break; + } +# endif +#endif + else if (ISALPHA (c)) + c = TOUPPER (c) - L_('A') + 10; + else + break; + if ((int) c >= base) + break; + /* Check for overflow. */ + if (i > cutoff || (i == cutoff && c > cutlim)) + overflow = 1; + else + { + use_long: + i *= (unsigned LONG int) base; + i += c; + } + } + + /* Check if anything actually happened. */ + if (s == save) + goto noconv; + + /* Store in ENDPTR the address of one character + past the last character we converted. */ + if (endptr != NULL) + *endptr = (STRING_TYPE *) s; + +#if !UNSIGNED + /* Check for a value that is within the range of + `unsigned LONG int', but outside the range of `LONG int'. */ + if (overflow == 0 + && i > (negative + ? -((unsigned LONG int) (STRTOL_LONG_MIN + 1)) + 1 + : (unsigned LONG int) STRTOL_LONG_MAX)) + overflow = 1; +#endif + + if (__builtin_expect (overflow, 0)) + { + __set_errno (ERANGE); +#if UNSIGNED + return STRTOL_ULONG_MAX; +#else + return negative ? STRTOL_LONG_MIN : STRTOL_LONG_MAX; +#endif + } + + /* Return the result of the appropriate sign. */ + return negative ? -i : i; + +noconv: + /* We must handle a special case here: the base is 0 or 16 and the + first two characters are '0' and 'x', but the rest are no + hexadecimal digits. This is no error case. We return 0 and + ENDPTR points to the `x`. */ + if (endptr != NULL) + { + if (save - nptr >= 2 && TOUPPER (save[-1]) == L_('X') + && save[-2] == L_('0')) + *endptr = (STRING_TYPE *) &save[-1]; + else + /* There was no number to convert. */ + *endptr = (STRING_TYPE *) nptr; + } + + return 0L; +} +#if defined _LIBC && !defined USE_WIDE_CHAR +libc_hidden_def (INTERNAL (__strtol_l)) +#endif + +/* External user entry point. */ + +#if _LIBC - 0 == 0 +# undef PARAMS +# if defined (__STDC__) && __STDC__ +# define PARAMS(Args) Args +# else +# define PARAMS(Args) () +# endif + +/* Prototype. */ +extern INT __strtol_l PARAMS ((const STRING_TYPE *nptr, STRING_TYPE **endptr, + int base)); +#endif -#include "strtol.c" +INT +#ifdef weak_function +weak_function +#endif +__strtol_l (nptr, endptr, base, loc) + const STRING_TYPE *nptr; + STRING_TYPE **endptr; + int base; + __locale_t loc; +{ + return INTERNAL (__strtol_l) (nptr, endptr, base, 0, loc); +} weak_alias (__strtol_l, strtol_l) diff --git a/sysdeps/generic/strtold.c b/sysdeps/generic/strtold_l.c index 7303025eff..e43654700f 100644 --- a/sysdeps/generic/strtold.c +++ b/sysdeps/generic/strtold_l.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1999, 2002 Free Software Foundation, Inc. +/* Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -18,17 +18,23 @@ #include <math.h> #include <stdlib.h> +#include <xlocale.h> + + +extern double ____strtod_l_internal (const char *, char **, int, __locale_t); + /* There is no `long double' type, use the `double' implementations. */ long double -__strtold_internal (const char *nptr, char **endptr, int group) +____strtold_l_internal (const char *nptr, char **endptr, int group, + __locale_t loc) { - return __strtod_internal (nptr, endptr, group); + return ____strtod_l_internal (nptr, endptr, group, loc); } -libc_hidden_def (__strtold_internal) + long double -strtold (const char *nptr, char **endptr) +strtold (const char *nptr, char **endptr, __locale_t loc) { - return __strtod_internal (nptr, endptr, 0); + return ____strtod_l_internal (nptr, endptr, 0, loc); } diff --git a/sysdeps/generic/strtoll_l.c b/sysdeps/generic/strtoll_l.c index 5e7ae7674c..7725035bd1 100644 --- a/sysdeps/generic/strtoll_l.c +++ b/sysdeps/generic/strtoll_l.c @@ -1,5 +1,5 @@ /* Convert string representing a number to integer value, using given locale. - Copyright (C) 1997, 2002 Free Software Foundation, Inc. + Copyright (C) 1997, 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -18,13 +18,11 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define USE_IN_EXTENDED_LOCALE_MODEL 1 +#define QUAD 1 #include <xlocale.h> extern long long int ____strtoll_l_internal (const char *, char **, int, int, __locale_t); -#include <strtoll.c> - -weak_alias (__strtoll_l, strtoll_l) +#include <strtol_l.c> diff --git a/sysdeps/generic/strtoul_l.c b/sysdeps/generic/strtoul_l.c index a049d2d006..a8b980f48d 100644 --- a/sysdeps/generic/strtoul_l.c +++ b/sysdeps/generic/strtoul_l.c @@ -1,5 +1,5 @@ /* Convert string representing a number to integer value, using given locale. - Copyright (C) 1997, 2002 Free Software Foundation, Inc. + Copyright (C) 1997, 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -18,13 +18,11 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define USE_IN_EXTENDED_LOCALE_MODEL 1 +#define UNSIGNED 1 #include <xlocale.h> extern unsigned long int ____strtoul_l_internal (const char *, char **, int, int, __locale_t); -#include "strtoul.c" - -weak_alias (__strtoul_l, strtoul_l) +#include "strtol_l.c" diff --git a/sysdeps/generic/strtoull_l.c b/sysdeps/generic/strtoull_l.c index 55be504318..68ad0d826e 100644 --- a/sysdeps/generic/strtoull_l.c +++ b/sysdeps/generic/strtoull_l.c @@ -1,5 +1,5 @@ /* Convert string representing a number to integer value, using given locale. - Copyright (C) 1997, 2002 Free Software Foundation, Inc. + Copyright (C) 1997, 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -18,13 +18,12 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define USE_IN_EXTENDED_LOCALE_MODEL 1 +#define QUAD 1 +#define UNSIGNED 1 #include <xlocale.h> extern unsigned long long int ____strtoull_l_internal (const char *, char **, int, int, __locale_t); -#include <strtoull.c> - -weak_alias (__strtoull_l, strtoull_l) +#include <strtol_l.c> diff --git a/sysdeps/generic/wcstol_l.c b/sysdeps/generic/wcstol_l.c index 91b0d3fa7d..f1b4171f18 100644 --- a/sysdeps/generic/wcstol_l.c +++ b/sysdeps/generic/wcstol_l.c @@ -1,5 +1,5 @@ /* Convert string representing a number to integer value, using given locale. - Copyright (C) 1997, 2002 Free Software Foundation, Inc. + Copyright (C) 1997, 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -22,11 +22,9 @@ #include <stddef.h> #include <locale.h> -#define USE_IN_EXTENDED_LOCALE_MODEL 1 +#define USE_WIDE_CHAR 1 extern long int ____wcstol_l_internal (const wchar_t *, wchar_t **, int, int, __locale_t); -#include "wcstol.c" - -weak_alias (__wcstol_l, wcstol_l) +#include "strtol_l.c" diff --git a/sysdeps/generic/wcstoll_l.c b/sysdeps/generic/wcstoll_l.c index 9ed2c5aca7..f1a4ca1623 100644 --- a/sysdeps/generic/wcstoll_l.c +++ b/sysdeps/generic/wcstoll_l.c @@ -1,5 +1,5 @@ /* Convert string representing a number to integer value, using given locale. - Copyright (C) 1997 Free Software Foundation, Inc. + Copyright (C) 1997, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -22,11 +22,9 @@ #include <stddef.h> #include <locale.h> -#define USE_IN_EXTENDED_LOCALE_MODEL 1 +#define QUAD 1 extern long long int ____wcstoll_l_internal (const wchar_t *, wchar_t **, int, int, __locale_t); -#include <wcstoll.c> - -weak_alias (__wcstoll_l, wcstoll_l) +#include <wcstol_l.c> diff --git a/sysdeps/generic/wcstoul_l.c b/sysdeps/generic/wcstoul_l.c index 9d567bbcc5..eeee1f0baa 100644 --- a/sysdeps/generic/wcstoul_l.c +++ b/sysdeps/generic/wcstoul_l.c @@ -1,5 +1,5 @@ /* Convert string representing a number to integer value, using given locale. - Copyright (C) 1997, 2002 Free Software Foundation, Inc. + Copyright (C) 1997, 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -22,11 +22,9 @@ #include <stddef.h> #include <locale.h> -#define USE_IN_EXTENDED_LOCALE_MODEL 1 +#define UNSIGNED 1 extern unsigned long int ____wcstoul_l_internal (const wchar_t *, wchar_t **, int, int, __locale_t); -#include <wcstoul.c> - -weak_alias (__wcstoul_l, wcstoul_l) +#include <wcstol_l.c> diff --git a/sysdeps/generic/wcstoull_l.c b/sysdeps/generic/wcstoull_l.c index f8f05ba8d8..32bc3c4bb9 100644 --- a/sysdeps/generic/wcstoull_l.c +++ b/sysdeps/generic/wcstoull_l.c @@ -1,5 +1,5 @@ /* Convert string representing a number to integer value, using given locale. - Copyright (C) 1997, 2002 Free Software Foundation, Inc. + Copyright (C) 1997, 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -22,12 +22,10 @@ #include <stddef.h> #include <locale.h> -#define USE_IN_EXTENDED_LOCALE_MODEL 1 +#define UNSIGNED 1 extern unsigned long long int ____wcstoull_l_internal (const wchar_t *, wchar_t **, int, int, __locale_t); -#include <wcstoull.c> - -weak_alias (__wcstoull_l, wcstoull_l) +#include <wcstoll_l.c> diff --git a/sysdeps/ieee754/ldbl-128/strtold.c b/sysdeps/ieee754/ldbl-128/strtold_l.c index 45ba361c75..eb227fce61 100644 --- a/sysdeps/ieee754/ldbl-128/strtold.c +++ b/sysdeps/ieee754/ldbl-128/strtold_l.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1999 Free Software Foundation, Inc. +/* Copyright (C) 1999, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -21,16 +21,18 @@ /* The actual implementation for all floating point sizes is in strtod.c. These macros tell it to produce the `long double' version, `strtold'. */ -# define FLOAT long double -# define FLT LDBL -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define STRTOF __strtold_l -# else -# define STRTOF strtold -# endif -# define MPN2FLOAT __mpn_construct_long_double -# define FLOAT_HUGE_VAL HUGE_VALL -# define SET_MANTISSA(flt, mant) \ +#define FLOAT long double +#define FLT LDBL +#ifdef USE_WIDE_CHAR +# define STRTOF wcstold_l +# define __STRTOF __wcstold_l +#else +# define STRTOF strtold_l +# define __STRTOF __strtold_l +#endif +#define MPN2FLOAT __mpn_construct_long_double +#define FLOAT_HUGE_VAL HUGE_VALL +#define SET_MANTISSA(flt, mant) \ do { union ieee854_long_double u; \ u.d = (flt); \ u.ieee.mantissa0 = 0x8000; \ @@ -40,4 +42,4 @@ (flt) = u.d; \ } while (0) -# include "strtod.c" +#include <strtod_l.c> diff --git a/sysdeps/ieee754/ldbl-96/strtold.c b/sysdeps/ieee754/ldbl-96/strtold_l.c index c4f395273b..32bf18084b 100644 --- a/sysdeps/ieee754/ldbl-96/strtold.c +++ b/sysdeps/ieee754/ldbl-96/strtold_l.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1999 Free Software Foundation, Inc. +/* Copyright (C) 1999, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -21,16 +21,18 @@ /* The actual implementation for all floating point sizes is in strtod.c. These macros tell it to produce the `long double' version, `strtold'. */ -# define FLOAT long double -# define FLT LDBL -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define STRTOF __strtold_l -# else -# define STRTOF strtold -# endif -# define MPN2FLOAT __mpn_construct_long_double -# define FLOAT_HUGE_VAL HUGE_VALL -# define SET_MANTISSA(flt, mant) \ +#define FLOAT long double +#define FLT LDBL +#ifdef USE_WIDE_CHAR +# define STRTOF wcstold_l +# define __STRTOF __wcstold_l +#else +# define STRTOF strtold_l +# define __STRTOF __strtold_l +#endif +#define MPN2FLOAT __mpn_construct_long_double +#define FLOAT_HUGE_VAL HUGE_VALL +#define SET_MANTISSA(flt, mant) \ do { union ieee854_long_double u; \ u.d = (flt); \ if ((mant & 0x7fffffffffffffffULL) == 0) \ @@ -40,4 +42,4 @@ (flt) = u.d; \ } while (0) -# include "strtod.c" +#include <strtod_l.c> diff --git a/sysdeps/m68k/strtold.c b/sysdeps/m68k/strtold.c deleted file mode 100644 index f756488275..0000000000 --- a/sysdeps/m68k/strtold.c +++ /dev/null @@ -1,2 +0,0 @@ -#define DENORM_EXP (MIN_EXP - 1) -#include <sysdeps/ieee754/ldbl-96/strtold.c> diff --git a/sysdeps/m68k/strtold_l.c b/sysdeps/m68k/strtold_l.c new file mode 100644 index 0000000000..481d9924c2 --- /dev/null +++ b/sysdeps/m68k/strtold_l.c @@ -0,0 +1,2 @@ +#define DENORM_EXP (MIN_EXP - 1) +#include <sysdeps/ieee754/ldbl-96/strtold_l.c> diff --git a/time/strftime.c b/time/strftime.c index 04250317e3..5a4917e352 100644 --- a/time/strftime.c +++ b/time/strftime.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991-1999, 2000, 2001, 2002, 2003 +/* Copyright (C) 1991-1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. @@ -17,1422 +17,13 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif +#include <time.h> +#include <locale/localeinfo.h> -#ifdef _LIBC -# define HAVE_LIMITS_H 1 -# define HAVE_MBLEN 1 -# define HAVE_MBRLEN 1 -# define HAVE_STRUCT_ERA_ENTRY 1 -# define HAVE_TM_GMTOFF 1 -# define HAVE_TM_ZONE 1 -# define HAVE_TZNAME 1 -# define HAVE_TZSET 1 -# define MULTIBYTE_IS_FORMAT_SAFE 1 -# define STDC_HEADERS 1 -# include "../locale/localeinfo.h" -#endif -#if defined emacs && !defined HAVE_BCOPY -# define HAVE_MEMCPY 1 -#endif - -#include <ctype.h> -#include <sys/types.h> /* Some systems define `time_t' here. */ - -#ifdef TIME_WITH_SYS_TIME -# include <sys/time.h> -# include <time.h> -#else -# ifdef HAVE_SYS_TIME_H -# include <sys/time.h> -# else -# include <time.h> -# endif -#endif -#if HAVE_TZNAME -extern char *tzname[]; -#endif - -/* Do multibyte processing if multibytes are supported, unless - multibyte sequences are safe in formats. Multibyte sequences are - safe if they cannot contain byte sequences that look like format - conversion specifications. The GNU C Library uses UTF8 multibyte - encoding, which is safe for formats, but strftime.c can be used - with other C libraries that use unsafe encodings. */ -#define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE) - -#if DO_MULTIBYTE -# if HAVE_MBRLEN -# include <wchar.h> -# else - /* Simulate mbrlen with mblen as best we can. */ -# define mbstate_t int -# define mbrlen(s, n, ps) mblen (s, n) -# define mbsinit(ps) (*(ps) == 0) -# endif - static const mbstate_t mbstate_zero; -#endif - -#if HAVE_LIMITS_H -# include <limits.h> -#endif - -#if STDC_HEADERS -# include <stddef.h> -# include <stdlib.h> -# include <string.h> -#else -# ifndef HAVE_MEMCPY -# define memcpy(d, s, n) bcopy ((s), (d), (n)) -# endif -#endif - -#ifdef COMPILE_WIDE -# include <endian.h> -# define CHAR_T wchar_t -# define UCHAR_T unsigned int -# define L_(Str) L##Str -# define NLW(Sym) _NL_W##Sym - -# define MEMCPY(d, s, n) __wmemcpy (d, s, n) -# define STRLEN(s) __wcslen (s) - -#else -# define CHAR_T char -# define UCHAR_T unsigned char -# define L_(Str) Str -# define NLW(Sym) Sym - -# if !defined STDC_HEADERS && !defined HAVE_MEMCPY -# define MEMCPY(d, s, n) bcopy ((s), (d), (n)) -# else -# define MEMCPY(d, s, n) memcpy ((d), (s), (n)) -# endif -# define STRLEN(s) strlen (s) - -# ifdef _LIBC -# define MEMPCPY(d, s, n) __mempcpy (d, s, n) -# else -# ifndef HAVE_MEMPCPY -# define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n))) -# endif -# endif -#endif - -#ifndef __P -# if defined __GNUC__ || (defined __STDC__ && __STDC__) -# define __P(args) args -# else -# define __P(args) () -# endif /* GCC. */ -#endif /* Not __P. */ - -#ifndef PTR -# ifdef __STDC__ -# define PTR void * -# else -# define PTR char * -# endif -#endif - -#ifndef CHAR_BIT -# define CHAR_BIT 8 -#endif - -#ifndef NULL -# define NULL 0 -#endif - -#define TYPE_SIGNED(t) ((t) -1 < 0) - -/* Bound on length of the string representing an integer value of type t. - Subtract one for the sign bit if t is signed; - 302 / 1000 is log10 (2) rounded up; - add one for integer division truncation; - add one more for a minus sign if t is signed. */ -#define INT_STRLEN_BOUND(t) \ - ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 + 1 + TYPE_SIGNED (t)) - -#define TM_YEAR_BASE 1900 - -#ifndef __isleap -/* Nonzero if YEAR is a leap year (every 4 years, - except every 100th isn't, and every 400th is). */ -# define __isleap(year) \ - ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) -#endif - - -#ifdef _LIBC -# define tzname __tzname -# define tzset __tzset -#endif - -#if !HAVE_TM_GMTOFF -/* Portable standalone applications should supply a "time_r.h" that - declares a POSIX-compliant localtime_r, for the benefit of older - implementations that lack localtime_r or have a nonstandard one. - Similarly for gmtime_r. See the gnulib time_r module for one way - to implement this. */ -# include "time_r.h" -# undef __gmtime_r -# undef __localtime_r -# define __gmtime_r gmtime_r -# define __localtime_r localtime_r -#endif - - -#if !defined memset && !defined HAVE_MEMSET && !defined _LIBC -/* Some systems lack the `memset' function and we don't want to - introduce additional dependencies. */ -/* The SGI compiler reportedly barfs on the trailing null - if we use a string constant as the initializer. 28 June 1997, rms. */ -static const CHAR_T spaces[16] = /* " " */ -{ - L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '), - L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' ') -}; -static const CHAR_T zeroes[16] = /* "0000000000000000" */ -{ - L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'), - L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0') -}; - -# define memset_space(P, Len) \ - do { \ - int _len = (Len); \ - \ - do \ - { \ - int _this = _len > 16 ? 16 : _len; \ - (P) = MEMPCPY ((P), spaces, _this * sizeof (CHAR_T)); \ - _len -= _this; \ - } \ - while (_len > 0); \ - } while (0) - -# define memset_zero(P, Len) \ - do { \ - int _len = (Len); \ - \ - do \ - { \ - int _this = _len > 16 ? 16 : _len; \ - (P) = MEMPCPY ((P), zeroes, _this * sizeof (CHAR_T)); \ - _len -= _this; \ - } \ - while (_len > 0); \ - } while (0) -#else -# ifdef COMPILE_WIDE -# define memset_space(P, Len) (wmemset ((P), L' ', (Len)), (P) += (Len)) -# define memset_zero(P, Len) (wmemset ((P), L'0', (Len)), (P) += (Len)) -# else -# define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len)) -# define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len)) -# endif -#endif - -#define add(n, f) \ - do \ - { \ - int _n = (n); \ - int _delta = width - _n; \ - int _incr = _n + (_delta > 0 ? _delta : 0); \ - if ((size_t) _incr >= maxsize - i) \ - return 0; \ - if (p) \ - { \ - if (_delta > 0) \ - { \ - if (pad == L_('0')) \ - memset_zero (p, _delta); \ - else \ - memset_space (p, _delta); \ - } \ - f; \ - p += _n; \ - } \ - i += _incr; \ - } while (0) - -#define cpy(n, s) \ - add ((n), \ - if (to_lowcase) \ - memcpy_lowcase (p, (s), _n LOCALE_ARG); \ - else if (to_uppcase) \ - memcpy_uppcase (p, (s), _n LOCALE_ARG); \ - else \ - MEMCPY ((PTR) p, (const PTR) (s), _n)) - -#ifdef COMPILE_WIDE -# ifndef USE_IN_EXTENDED_LOCALE_MODEL -# undef __mbsrtowcs_l -# define __mbsrtowcs_l(d, s, l, st, loc) __mbsrtowcs (d, s, l, st) -# endif -# define widen(os, ws, l) \ - { \ - mbstate_t __st; \ - const char *__s = os; \ - memset (&__st, '\0', sizeof (__st)); \ - l = __mbsrtowcs_l (NULL, &__s, 0, &__st, loc); \ - ws = alloca ((l + 1) * sizeof (wchar_t)); \ - (void) __mbsrtowcs_l (ws, &__s, l, &__st, loc); \ - } -#endif - - -#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL -/* 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. */ -# define strftime __strftime_l -# define wcsftime __wcsftime_l -# undef _NL_CURRENT -# define _NL_CURRENT(category, item) \ - (current->values[_NL_ITEM_INDEX (item)].string) -# define LOCALE_PARAM , loc -# define LOCALE_ARG , loc -# define LOCALE_PARAM_DECL __locale_t loc; -# define LOCALE_PARAM_PROTO , __locale_t loc -# define HELPER_LOCALE_ARG , current -#else -# define LOCALE_PARAM -# define LOCALE_PARAM_PROTO -# define LOCALE_ARG -# define LOCALE_PARAM_DECL -# ifdef _LIBC -# define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME) -# else -# define HELPER_LOCALE_ARG -# endif -#endif - -#ifdef COMPILE_WIDE -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define TOUPPER(Ch, L) __towupper_l (Ch, L) -# define TOLOWER(Ch, L) __towlower_l (Ch, L) -# else -# define TOUPPER(Ch, L) towupper (Ch) -# define TOLOWER(Ch, L) towlower (Ch) -# endif -#else -# ifdef _LIBC -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define TOUPPER(Ch, L) __toupper_l (Ch, L) -# define TOLOWER(Ch, L) __tolower_l (Ch, L) -# else -# define TOUPPER(Ch, L) toupper (Ch) -# define TOLOWER(Ch, L) tolower (Ch) -# endif -# else -# define TOUPPER(Ch, L) (islower (Ch) ? toupper (Ch) : (Ch)) -# define TOLOWER(Ch, L) (isupper (Ch) ? tolower (Ch) : (Ch)) -# endif -#endif -/* We don't use `isdigit' here since the locale dependent - interpretation is not what we want here. We only need to accept - the arabic digits in the ASCII range. One day there is perhaps a - more reliable way to accept other sets of digits. */ -#define ISDIGIT(Ch) ((unsigned int) (Ch) - L_('0') <= 9) - -static CHAR_T *memcpy_lowcase __P ((CHAR_T *dest, const CHAR_T *src, - size_t len LOCALE_PARAM_PROTO)); - -static CHAR_T * -memcpy_lowcase (dest, src, len LOCALE_PARAM) - CHAR_T *dest; - const CHAR_T *src; - size_t len; - LOCALE_PARAM_DECL -{ - while (len-- > 0) - dest[len] = TOLOWER ((UCHAR_T) src[len], loc); - return dest; -} - -static CHAR_T *memcpy_uppcase __P ((CHAR_T *dest, const CHAR_T *src, - size_t len LOCALE_PARAM_PROTO)); - -static CHAR_T * -memcpy_uppcase (dest, src, len LOCALE_PARAM) - CHAR_T *dest; - const CHAR_T *src; - size_t len; - LOCALE_PARAM_DECL -{ - while (len-- > 0) - dest[len] = TOUPPER ((UCHAR_T) src[len], loc); - return dest; -} - - -#if ! HAVE_TM_GMTOFF -/* Yield the difference between *A and *B, - measured in seconds, ignoring leap seconds. */ -# define tm_diff ftime_tm_diff -static int tm_diff __P ((const struct tm *, const struct tm *)); -static int -tm_diff (a, b) - const struct tm *a; - const struct tm *b; -{ - /* Compute intervening leap days correctly even if year is negative. - Take care to avoid int overflow in leap day calculations, - but it's OK to assume that A and B are close to each other. */ - int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3); - int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3); - int a100 = a4 / 25 - (a4 % 25 < 0); - int b100 = b4 / 25 - (b4 % 25 < 0); - int a400 = a100 >> 2; - int b400 = b100 >> 2; - int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); - int years = a->tm_year - b->tm_year; - int days = (365 * years + intervening_leap_days - + (a->tm_yday - b->tm_yday)); - return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) - + (a->tm_min - b->tm_min)) - + (a->tm_sec - b->tm_sec)); -} -#endif /* ! HAVE_TM_GMTOFF */ - - - -/* The number of days from the first day of the first ISO week of this - year to the year day YDAY with week day WDAY. ISO weeks start on - Monday; the first ISO week has the year's first Thursday. YDAY may - be as small as YDAY_MINIMUM. */ -#define ISO_WEEK_START_WDAY 1 /* Monday */ -#define ISO_WEEK1_WDAY 4 /* Thursday */ -#define YDAY_MINIMUM (-366) -static int iso_week_days __P ((int, int)); -#ifdef __GNUC__ -__inline__ -#endif -static int -iso_week_days (yday, wday) - int yday; - int wday; -{ - /* Add enough to the first operand of % to make it nonnegative. */ - int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7; - return (yday - - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7 - + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY); -} - - -#if !(defined _NL_CURRENT || HAVE_STRFTIME) -static CHAR_T const weekday_name[][10] = - { - L_("Sunday"), L_("Monday"), L_("Tuesday"), L_("Wednesday"), - L_("Thursday"), L_("Friday"), L_("Saturday") - }; -static CHAR_T const month_name[][10] = - { - L_("January"), L_("February"), L_("March"), L_("April"), L_("May"), - L_("June"), L_("July"), L_("August"), L_("September"), L_("October"), - L_("November"), L_("December") - }; -#endif - - -#ifdef emacs -# define my_strftime emacs_strftimeu -# define ut_argument , ut -# define ut_argument_spec int ut; -# define ut_argument_spec_iso , int ut -#else -# ifdef COMPILE_WIDE -# define my_strftime wcsftime -# define nl_get_alt_digit _nl_get_walt_digit -# else -# define my_strftime strftime -# define nl_get_alt_digit _nl_get_alt_digit -# endif -# define ut_argument -# define ut_argument_spec -# define ut_argument_spec_iso -/* We don't have this information in general. */ -# define ut 0 -#endif - -#if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET - /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime. - Work around this bug by copying *tp before it might be munged. */ - size_t _strftime_copytm __P ((char *, size_t, const char *, - const struct tm * ut_argument_spec_iso)); - size_t - my_strftime (s, maxsize, format, tp ut_argument) - CHAR_T *s; - size_t maxsize; - const CHAR_T *format; - const struct tm *tp; - ut_argument_spec - { - struct tm tmcopy; - tmcopy = *tp; - return _strftime_copytm (s, maxsize, format, &tmcopy ut_argument); - } -# undef my_strftime -# define my_strftime _strftime_copytm -#endif - - -/* Write information from TP into S according to the format - string FORMAT, writing no more that MAXSIZE characters - (including the terminating '\0') and returning number of - characters written. If S is NULL, nothing will be written - anywhere, so to determine how many characters would be - written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */ -size_t -my_strftime (s, maxsize, format, tp ut_argument LOCALE_PARAM) - CHAR_T *s; - size_t maxsize; - const CHAR_T *format; - const struct tm *tp; - ut_argument_spec - LOCALE_PARAM_DECL -{ -#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL - struct locale_data *const current = loc->__locales[LC_TIME]; -#endif - - int hour12 = tp->tm_hour; -#ifdef _NL_CURRENT - /* We cannot make the following values variables since we must delay - the evaluation of these values until really needed since some - expressions might not be valid in every situation. The `struct tm' - might be generated by a strptime() call that initialized - only a few elements. Dereference the pointers only if the format - requires this. Then it is ok to fail if the pointers are invalid. */ -# define a_wkday \ - ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday)) -# define f_wkday \ - ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday)) -# define a_month \ - ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon)) -# define f_month \ - ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon)) -# define ampm \ - ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11 \ - ? NLW(PM_STR) : NLW(AM_STR))) - -# define aw_len STRLEN (a_wkday) -# define am_len STRLEN (a_month) -# define ap_len STRLEN (ampm) -#else -# if !HAVE_STRFTIME -# define f_wkday (weekday_name[tp->tm_wday]) -# define f_month (month_name[tp->tm_mon]) -# define a_wkday f_wkday -# define a_month f_month -# define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11)) - - size_t aw_len = 3; - size_t am_len = 3; - size_t ap_len = 2; -# endif -#endif - const char *zone; - size_t i = 0; - CHAR_T *p = s; - const CHAR_T *f; -#if DO_MULTIBYTE && !defined COMPILE_WIDE - const char *format_end = NULL; -#endif - - zone = NULL; -#if HAVE_TM_ZONE - /* The POSIX test suite assumes that setting - the environment variable TZ to a new value before calling strftime() - will influence the result (the %Z format) even if the information in - TP is computed with a totally different time zone. - This is bogus: though POSIX allows bad behavior like this, - POSIX does not require it. Do the right thing instead. */ - zone = (const char *) tp->tm_zone; -#endif -#if HAVE_TZNAME - if (ut) - { - if (! (zone && *zone)) - zone = "GMT"; - } - else - { - /* POSIX.1 requires that local time zone information is used as - though strftime called tzset. */ -# if HAVE_TZSET - tzset (); -# endif - } -#endif - - if (hour12 > 12) - hour12 -= 12; - else - if (hour12 == 0) - hour12 = 12; - - for (f = format; *f != '\0'; ++f) - { - int pad = 0; /* Padding for number ('-', '_', or 0). */ - int modifier; /* Field modifier ('E', 'O', or 0). */ - int digits; /* Max digits for numeric format. */ - int number_value; /* Numeric value to be printed. */ - int negative_number; /* 1 if the number is negative. */ - const CHAR_T *subfmt; - CHAR_T *bufp; - CHAR_T buf[1 + (sizeof (int) < sizeof (time_t) - ? INT_STRLEN_BOUND (time_t) - : INT_STRLEN_BOUND (int))]; - int width = -1; - int to_lowcase = 0; - int to_uppcase = 0; - int change_case = 0; - int format_char; - -#if DO_MULTIBYTE && !defined COMPILE_WIDE - switch (*f) - { - case L_('%'): - break; - - case L_('\b'): case L_('\t'): case L_('\n'): - case L_('\v'): case L_('\f'): case L_('\r'): - case L_(' '): case L_('!'): case L_('"'): case L_('#'): case L_('&'): - case L_('\''): case L_('('): case L_(')'): case L_('*'): case L_('+'): - case L_(','): case L_('-'): case L_('.'): case L_('/'): case L_('0'): - case L_('1'): case L_('2'): case L_('3'): case L_('4'): case L_('5'): - case L_('6'): case L_('7'): case L_('8'): case L_('9'): case L_(':'): - case L_(';'): case L_('<'): case L_('='): case L_('>'): case L_('?'): - case L_('A'): case L_('B'): case L_('C'): case L_('D'): case L_('E'): - case L_('F'): case L_('G'): case L_('H'): case L_('I'): case L_('J'): - case L_('K'): case L_('L'): case L_('M'): case L_('N'): case L_('O'): - case L_('P'): case L_('Q'): case L_('R'): case L_('S'): case L_('T'): - case L_('U'): case L_('V'): case L_('W'): case L_('X'): case L_('Y'): - case L_('Z'): case L_('['): case L_('\\'): case L_(']'): case L_('^'): - case L_('_'): case L_('a'): case L_('b'): case L_('c'): case L_('d'): - case L_('e'): case L_('f'): case L_('g'): case L_('h'): case L_('i'): - case L_('j'): case L_('k'): case L_('l'): case L_('m'): case L_('n'): - case L_('o'): case L_('p'): case L_('q'): case L_('r'): case L_('s'): - case L_('t'): case L_('u'): case L_('v'): case L_('w'): case L_('x'): - case L_('y'): case L_('z'): case L_('{'): case L_('|'): case L_('}'): - case L_('~'): - /* The C Standard requires these 98 characters (plus '%') to - be in the basic execution character set. None of these - characters can start a multibyte sequence, so they need - not be analyzed further. */ - add (1, *p = *f); - continue; - - default: - /* Copy this multibyte sequence until we reach its end, find - an error, or come back to the initial shift state. */ - { - mbstate_t mbstate = mbstate_zero; - size_t len = 0; - size_t fsize; - - if (! format_end) - format_end = f + strlen (f) + 1; - fsize = format_end - f; - - do - { - size_t bytes = mbrlen (f + len, fsize - len, &mbstate); - - if (bytes == 0) - break; - - if (bytes == (size_t) -2) - { - len += strlen (f + len); - break; - } - - if (bytes == (size_t) -1) - { - len++; - break; - } - - len += bytes; - } - while (! mbsinit (&mbstate)); - - cpy (len, f); - f += len - 1; - continue; - } - } - -#else /* ! DO_MULTIBYTE */ - - /* Either multibyte encodings are not supported, they are - safe for formats, so any non-'%' byte can be copied through, - or this is the wide character version. */ - if (*f != L_('%')) - { - add (1, *p = *f); - continue; - } - -#endif /* ! DO_MULTIBYTE */ - - /* Check for flags that can modify a format. */ - while (1) - { - switch (*++f) - { - /* This influences the number formats. */ - case L_('_'): - case L_('-'): - case L_('0'): - pad = *f; - continue; - - /* This changes textual output. */ - case L_('^'): - to_uppcase = 1; - continue; - case L_('#'): - change_case = 1; - continue; - - default: - break; - } - break; - } - - /* As a GNU extension we allow to specify the field width. */ - if (ISDIGIT (*f)) - { - width = 0; - do - { - if (width > INT_MAX / 10 - || (width == INT_MAX / 10 && *f - L_('0') > INT_MAX % 10)) - /* Avoid overflow. */ - width = INT_MAX; - else - { - width *= 10; - width += *f - L_('0'); - } - ++f; - } - while (ISDIGIT (*f)); - } - - /* Check for modifiers. */ - switch (*f) - { - case L_('E'): - case L_('O'): - modifier = *f++; - break; - - default: - modifier = 0; - break; - } - - /* Now do the specified format. */ - format_char = *f; - switch (format_char) - { -#define DO_NUMBER(d, v) \ - digits = d > width ? d : width; \ - number_value = v; goto do_number -#define DO_NUMBER_SPACEPAD(d, v) \ - digits = d > width ? d : width; \ - number_value = v; goto do_number_spacepad - - case L_('%'): - if (modifier != 0) - goto bad_format; - add (1, *p = *f); - break; - - case L_('a'): - if (modifier != 0) - goto bad_format; - if (change_case) - { - to_uppcase = 1; - to_lowcase = 0; - } -#if defined _NL_CURRENT || !HAVE_STRFTIME - cpy (aw_len, a_wkday); - break; -#else - goto underlying_strftime; -#endif - - case 'A': - if (modifier != 0) - goto bad_format; - if (change_case) - { - to_uppcase = 1; - to_lowcase = 0; - } -#if defined _NL_CURRENT || !HAVE_STRFTIME - cpy (STRLEN (f_wkday), f_wkday); - break; -#else - goto underlying_strftime; -#endif - - case L_('b'): - case L_('h'): - if (change_case) - { - to_uppcase = 1; - to_lowcase = 0; - } - if (modifier != 0) - goto bad_format; -#if defined _NL_CURRENT || !HAVE_STRFTIME - cpy (am_len, a_month); - break; -#else - goto underlying_strftime; -#endif - - case L_('B'): - if (modifier != 0) - goto bad_format; - if (change_case) - { - to_uppcase = 1; - to_lowcase = 0; - } -#if defined _NL_CURRENT || !HAVE_STRFTIME - cpy (STRLEN (f_month), f_month); - break; -#else - goto underlying_strftime; -#endif - - case L_('c'): - if (modifier == L_('O')) - goto bad_format; -#ifdef _NL_CURRENT - if (! (modifier == 'E' - && (*(subfmt = - (const CHAR_T *) _NL_CURRENT (LC_TIME, - NLW(ERA_D_T_FMT))) - != '\0'))) - subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_T_FMT)); -#else -# if HAVE_STRFTIME - goto underlying_strftime; -# else - subfmt = L_("%a %b %e %H:%M:%S %Y"); -# endif -#endif - - subformat: - { - CHAR_T *old_start = p; - size_t len = my_strftime (NULL, (size_t) -1, subfmt, - tp ut_argument LOCALE_ARG); - add (len, my_strftime (p, maxsize - i, subfmt, - tp ut_argument LOCALE_ARG)); - - if (to_uppcase) - while (old_start < p) - { - *old_start = TOUPPER ((UCHAR_T) *old_start, loc); - ++old_start; - } - } - break; - -#if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY) - underlying_strftime: - { - /* The relevant information is available only via the - underlying strftime implementation, so use that. */ - char ufmt[4]; - char *u = ufmt; - char ubuf[1024]; /* enough for any single format in practice */ - size_t len; - /* Make sure we're calling the actual underlying strftime. - In some cases, config.h contains something like - "#define strftime rpl_strftime". */ -# ifdef strftime -# undef strftime - size_t strftime (); -# endif - - *u++ = '%'; - if (modifier != 0) - *u++ = modifier; - *u++ = format_char; - *u = '\0'; - len = strftime (ubuf, sizeof ubuf, ufmt, tp); - if (len == 0 && ubuf[0] != '\0') - return 0; - cpy (len, ubuf); - } - break; -#endif - - case L_('C'): - if (modifier == L_('O')) - goto bad_format; - if (modifier == L_('E')) - { -#if HAVE_STRUCT_ERA_ENTRY - struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG); - if (era) - { -# ifdef COMPILE_WIDE - size_t len = __wcslen (era->era_wname); - cpy (len, era->era_wname); -# else - size_t len = strlen (era->era_name); - cpy (len, era->era_name); -# endif - break; - } -#else -# if HAVE_STRFTIME - goto underlying_strftime; -# endif -#endif - } - - { - int year = tp->tm_year + TM_YEAR_BASE; - DO_NUMBER (1, year / 100 - (year % 100 < 0)); - } - - case L_('x'): - if (modifier == L_('O')) - goto bad_format; -#ifdef _NL_CURRENT - if (! (modifier == L_('E') - && (*(subfmt = - (const CHAR_T *)_NL_CURRENT (LC_TIME, NLW(ERA_D_FMT))) - != L_('\0')))) - subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_FMT)); - goto subformat; -#else -# if HAVE_STRFTIME - goto underlying_strftime; -# else - /* Fall through. */ -# endif -#endif - case L_('D'): - if (modifier != 0) - goto bad_format; - subfmt = L_("%m/%d/%y"); - goto subformat; - - case L_('d'): - if (modifier == L_('E')) - goto bad_format; - - DO_NUMBER (2, tp->tm_mday); - - case L_('e'): - if (modifier == L_('E')) - goto bad_format; - - DO_NUMBER_SPACEPAD (2, tp->tm_mday); - - /* All numeric formats set DIGITS and NUMBER_VALUE and then - jump to one of these two labels. */ - - do_number_spacepad: - /* Force `_' flag unless overwritten by `0' or '-' flag. */ - if (pad != L_('0') && pad != L_('-')) - pad = L_('_'); - - do_number: - /* Format the number according to the MODIFIER flag. */ - - if (modifier == L_('O') && 0 <= number_value) - { -#ifdef _NL_CURRENT - /* Get the locale specific alternate representation of - the number NUMBER_VALUE. If none exist NULL is returned. */ - const CHAR_T *cp = nl_get_alt_digit (number_value - HELPER_LOCALE_ARG); - - if (cp != NULL) - { - size_t digitlen = STRLEN (cp); - if (digitlen != 0) - { - cpy (digitlen, cp); - break; - } - } -#else -# if HAVE_STRFTIME - goto underlying_strftime; -# endif -#endif - } - { - unsigned int u = number_value; - - bufp = buf + sizeof (buf) / sizeof (buf[0]); - negative_number = number_value < 0; - - if (negative_number) - u = -u; - - do - *--bufp = u % 10 + L_('0'); - while ((u /= 10) != 0); - } - - do_number_sign_and_padding: - if (negative_number) - *--bufp = L_('-'); - - if (pad != L_('-')) - { - int padding = digits - (buf + (sizeof (buf) / sizeof (buf[0])) - - bufp); - - if (padding > 0) - { - if (pad == L_('_')) - { - if ((size_t) padding >= maxsize - i) - return 0; - - if (p) - memset_space (p, padding); - i += padding; - width = width > padding ? width - padding : 0; - } - else - { - if ((size_t) digits >= maxsize - i) - return 0; - - if (negative_number) - { - ++bufp; - - if (p) - *p++ = L_('-'); - ++i; - } - - if (p) - memset_zero (p, padding); - i += padding; - width = 0; - } - } - } - - cpy (buf + sizeof (buf) / sizeof (buf[0]) - bufp, bufp); - break; - - case L_('F'): - if (modifier != 0) - goto bad_format; - subfmt = L_("%Y-%m-%d"); - goto subformat; - - case L_('H'): - if (modifier == L_('E')) - goto bad_format; - - DO_NUMBER (2, tp->tm_hour); - - case L_('I'): - if (modifier == L_('E')) - goto bad_format; - - DO_NUMBER (2, hour12); - - case L_('k'): /* GNU extension. */ - if (modifier == L_('E')) - goto bad_format; - - DO_NUMBER_SPACEPAD (2, tp->tm_hour); - - case L_('l'): /* GNU extension. */ - if (modifier == L_('E')) - goto bad_format; - - DO_NUMBER_SPACEPAD (2, hour12); - - case L_('j'): - if (modifier == L_('E')) - goto bad_format; - - DO_NUMBER (3, 1 + tp->tm_yday); - - case L_('M'): - if (modifier == L_('E')) - goto bad_format; - - DO_NUMBER (2, tp->tm_min); - - case L_('m'): - if (modifier == L_('E')) - goto bad_format; - - DO_NUMBER (2, tp->tm_mon + 1); - - case L_('n'): - add (1, *p = L_('\n')); - break; - - case L_('P'): - to_lowcase = 1; -#if !defined _NL_CURRENT && HAVE_STRFTIME - format_char = L_('p'); -#endif - /* FALLTHROUGH */ - - case L_('p'): - if (change_case) - { - to_uppcase = 0; - to_lowcase = 1; - } -#if defined _NL_CURRENT || !HAVE_STRFTIME - cpy (ap_len, ampm); - break; -#else - goto underlying_strftime; -#endif - - case L_('R'): - subfmt = L_("%H:%M"); - goto subformat; - - case L_('r'): -#if !defined _NL_CURRENT && HAVE_STRFTIME - goto underlying_strftime; -#else -# ifdef _NL_CURRENT - if (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, - NLW(T_FMT_AMPM))) - == L_('\0')) -# endif - subfmt = L_("%I:%M:%S %p"); - goto subformat; -#endif - - case L_('S'): - if (modifier == L_('E')) - goto bad_format; - - DO_NUMBER (2, tp->tm_sec); - - case L_('s'): /* GNU extension. */ - { - struct tm ltm; - time_t t; - - ltm = *tp; - t = mktime (<m); - - /* Generate string value for T using time_t arithmetic; - this works even if sizeof (long) < sizeof (time_t). */ - - bufp = buf + sizeof (buf) / sizeof (buf[0]); - negative_number = t < 0; - - do - { - int d = t % 10; - t /= 10; - - if (negative_number) - { - d = -d; - - /* Adjust if division truncates to minus infinity. */ - if (0 < -1 % 10 && d < 0) - { - t++; - d += 10; - } - } - - *--bufp = d + L_('0'); - } - while (t != 0); - - digits = 1; - goto do_number_sign_and_padding; - } - - case L_('X'): - if (modifier == L_('O')) - goto bad_format; -#ifdef _NL_CURRENT - if (! (modifier == L_('E') - && (*(subfmt = - (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_T_FMT))) - != L_('\0')))) - subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(T_FMT)); - goto subformat; -#else -# if HAVE_STRFTIME - goto underlying_strftime; -# else - /* Fall through. */ -# endif -#endif - case L_('T'): - subfmt = L_("%H:%M:%S"); - goto subformat; - - case L_('t'): - add (1, *p = L_('\t')); - break; - - case L_('u'): - DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1); - - case L_('U'): - if (modifier == L_('E')) - goto bad_format; - - DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7); - - case L_('V'): - case L_('g'): - case L_('G'): - if (modifier == L_('E')) - goto bad_format; - { - int year = tp->tm_year + TM_YEAR_BASE; - int days = iso_week_days (tp->tm_yday, tp->tm_wday); - - if (days < 0) - { - /* This ISO week belongs to the previous year. */ - year--; - days = iso_week_days (tp->tm_yday + (365 + __isleap (year)), - tp->tm_wday); - } - else - { - int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)), - tp->tm_wday); - if (0 <= d) - { - /* This ISO week belongs to the next year. */ - year++; - days = d; - } - } - - switch (*f) - { - case L_('g'): - DO_NUMBER (2, (year % 100 + 100) % 100); - - case L_('G'): - DO_NUMBER (1, year); - - default: - DO_NUMBER (2, days / 7 + 1); - } - } - - case L_('W'): - if (modifier == L_('E')) - goto bad_format; - - DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7); - - case L_('w'): - if (modifier == L_('E')) - goto bad_format; - - DO_NUMBER (1, tp->tm_wday); - - case L_('Y'): - if (modifier == 'E') - { -#if HAVE_STRUCT_ERA_ENTRY - struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG); - if (era) - { -# ifdef COMPILE_WIDE - subfmt = era->era_wformat; -# else - subfmt = era->era_format; -# endif - goto subformat; - } -#else -# if HAVE_STRFTIME - goto underlying_strftime; -# endif -#endif - } - if (modifier == L_('O')) - goto bad_format; - else - DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE); - - case L_('y'): - if (modifier == L_('E')) - { -#if HAVE_STRUCT_ERA_ENTRY - struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG); - if (era) - { - int delta = tp->tm_year - era->start_date[0]; - DO_NUMBER (1, (era->offset - + delta * era->absolute_direction)); - } -#else -# if HAVE_STRFTIME - goto underlying_strftime; -# endif -#endif - } - DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100); - - case L_('Z'): - if (change_case) - { - to_uppcase = 0; - to_lowcase = 1; - } - -#if HAVE_TZNAME - /* The tzset() call might have changed the value. */ - if (!(zone && *zone) && tp->tm_isdst >= 0) - zone = tzname[tp->tm_isdst]; -#endif - if (! zone) - zone = ""; - -#ifdef COMPILE_WIDE - { - /* The zone string is always given in multibyte form. We have - to transform it first. */ - wchar_t *wczone; - size_t len; - widen (zone, wczone, len); - cpy (len, wczone); - } -#else - cpy (strlen (zone), zone); -#endif - break; - - case L_('z'): - if (tp->tm_isdst < 0) - break; - - { - int diff; -#if HAVE_TM_GMTOFF - diff = tp->tm_gmtoff; -#else - if (ut) - diff = 0; - else - { - struct tm gtm; - struct tm ltm; - time_t lt; - - ltm = *tp; - lt = mktime (<m); - - if (lt == (time_t) -1) - { - /* mktime returns -1 for errors, but -1 is also a - valid time_t value. Check whether an error really - occurred. */ - struct tm tm; - - if (! __localtime_r (<, &tm) - || ((ltm.tm_sec ^ tm.tm_sec) - | (ltm.tm_min ^ tm.tm_min) - | (ltm.tm_hour ^ tm.tm_hour) - | (ltm.tm_mday ^ tm.tm_mday) - | (ltm.tm_mon ^ tm.tm_mon) - | (ltm.tm_year ^ tm.tm_year))) - break; - } - - if (! __gmtime_r (<, >m)) - break; - - diff = tm_diff (<m, >m); - } -#endif - - if (diff < 0) - { - add (1, *p = L_('-')); - diff = -diff; - } - else - add (1, *p = L_('+')); - - diff /= 60; - DO_NUMBER (4, (diff / 60) * 100 + diff % 60); - } - - case L_('\0'): /* GNU extension: % at end of format. */ - --f; - /* Fall through. */ - default: - /* Unknown format; output the format, including the '%', - since this is most likely the right thing to do if a - multibyte string has been misparsed. */ - bad_format: - { - int flen; - for (flen = 1; f[1 - flen] != L_('%'); flen++) - continue; - cpy (flen, &f[1 - flen]); - } - break; - } - } - - if (p && maxsize != 0) - *p = L_('\0'); - return i; -} -#ifdef _LIBC -libc_hidden_def (my_strftime) -#endif - - -#ifdef emacs -/* For Emacs we have a separate interface which corresponds to the normal - strftime function and does not have the extra information whether the - TP arguments comes from a `gmtime' call or not. */ size_t -emacs_strftime (s, maxsize, format, tp) - char *s; - size_t maxsize; - const char *format; - const struct tm *tp; +strftime (char *s, size_t maxsize, const char *format, const struct tm *tp) { - return my_strftime (s, maxsize, format, tp, 0); + return __strftime_l (s, maxsize, format, tp, _NL_CURRENT_LOCALE); } -#endif +libc_hidden_def (strftime) diff --git a/time/strftime_l.c b/time/strftime_l.c index c6ecada4ce..6cae85d32f 100644 --- a/time/strftime_l.c +++ b/time/strftime_l.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002 Free Software Foundation, Inc. +/* Copyright (C) 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -16,7 +16,1427 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define USE_IN_EXTENDED_LOCALE_MODEL 1 -#include <strftime.c> +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#ifdef _LIBC +# define USE_IN_EXTENDED_LOCALE_MODEL 1 +# define HAVE_LIMITS_H 1 +# define HAVE_MBLEN 1 +# define HAVE_MBRLEN 1 +# define HAVE_STRUCT_ERA_ENTRY 1 +# define HAVE_TM_GMTOFF 1 +# define HAVE_TM_ZONE 1 +# define HAVE_TZNAME 1 +# define HAVE_TZSET 1 +# define MULTIBYTE_IS_FORMAT_SAFE 1 +# define STDC_HEADERS 1 +# include "../locale/localeinfo.h" +#endif + +#if defined emacs && !defined HAVE_BCOPY +# define HAVE_MEMCPY 1 +#endif + +#include <ctype.h> +#include <sys/types.h> /* Some systems define `time_t' here. */ + +#ifdef TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif +#if HAVE_TZNAME +extern char *tzname[]; +#endif + +/* Do multibyte processing if multibytes are supported, unless + multibyte sequences are safe in formats. Multibyte sequences are + safe if they cannot contain byte sequences that look like format + conversion specifications. The GNU C Library uses UTF8 multibyte + encoding, which is safe for formats, but strftime.c can be used + with other C libraries that use unsafe encodings. */ +#define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE) + +#if DO_MULTIBYTE +# if HAVE_MBRLEN +# include <wchar.h> +# else + /* Simulate mbrlen with mblen as best we can. */ +# define mbstate_t int +# define mbrlen(s, n, ps) mblen (s, n) +# define mbsinit(ps) (*(ps) == 0) +# endif + static const mbstate_t mbstate_zero; +#endif + +#if HAVE_LIMITS_H +# include <limits.h> +#endif + +#if STDC_HEADERS +# include <stddef.h> +# include <stdlib.h> +# include <string.h> +#else +# ifndef HAVE_MEMCPY +# define memcpy(d, s, n) bcopy ((s), (d), (n)) +# endif +#endif + +#ifdef COMPILE_WIDE +# include <endian.h> +# define CHAR_T wchar_t +# define UCHAR_T unsigned int +# define L_(Str) L##Str +# define NLW(Sym) _NL_W##Sym + +# define MEMCPY(d, s, n) __wmemcpy (d, s, n) +# define STRLEN(s) __wcslen (s) + +#else +# define CHAR_T char +# define UCHAR_T unsigned char +# define L_(Str) Str +# define NLW(Sym) Sym + +# if !defined STDC_HEADERS && !defined HAVE_MEMCPY +# define MEMCPY(d, s, n) bcopy ((s), (d), (n)) +# else +# define MEMCPY(d, s, n) memcpy ((d), (s), (n)) +# endif +# define STRLEN(s) strlen (s) + +# ifdef _LIBC +# define MEMPCPY(d, s, n) __mempcpy (d, s, n) +# else +# ifndef HAVE_MEMPCPY +# define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n))) +# endif +# endif +#endif + +#ifndef __P +# if defined __GNUC__ || (defined __STDC__ && __STDC__) +# define __P(args) args +# else +# define __P(args) () +# endif /* GCC. */ +#endif /* Not __P. */ + +#ifndef PTR +# ifdef __STDC__ +# define PTR void * +# else +# define PTR char * +# endif +#endif + +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif + +#ifndef NULL +# define NULL 0 +#endif + +#define TYPE_SIGNED(t) ((t) -1 < 0) + +/* Bound on length of the string representing an integer value of type t. + Subtract one for the sign bit if t is signed; + 302 / 1000 is log10 (2) rounded up; + add one for integer division truncation; + add one more for a minus sign if t is signed. */ +#define INT_STRLEN_BOUND(t) \ + ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 + 1 + TYPE_SIGNED (t)) + +#define TM_YEAR_BASE 1900 + +#ifndef __isleap +/* Nonzero if YEAR is a leap year (every 4 years, + except every 100th isn't, and every 400th is). */ +# define __isleap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) +#endif + + +#ifdef _LIBC +# define tzname __tzname +# define tzset __tzset +#endif + +#if !HAVE_TM_GMTOFF +/* Portable standalone applications should supply a "time_r.h" that + declares a POSIX-compliant localtime_r, for the benefit of older + implementations that lack localtime_r or have a nonstandard one. + Similarly for gmtime_r. See the gnulib time_r module for one way + to implement this. */ +# include "time_r.h" +# undef __gmtime_r +# undef __localtime_r +# define __gmtime_r gmtime_r +# define __localtime_r localtime_r +#endif + + +#if !defined memset && !defined HAVE_MEMSET && !defined _LIBC +/* Some systems lack the `memset' function and we don't want to + introduce additional dependencies. */ +/* The SGI compiler reportedly barfs on the trailing null + if we use a string constant as the initializer. 28 June 1997, rms. */ +static const CHAR_T spaces[16] = /* " " */ +{ + L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '), + L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' ') +}; +static const CHAR_T zeroes[16] = /* "0000000000000000" */ +{ + L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'), + L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0') +}; + +# define memset_space(P, Len) \ + do { \ + int _len = (Len); \ + \ + do \ + { \ + int _this = _len > 16 ? 16 : _len; \ + (P) = MEMPCPY ((P), spaces, _this * sizeof (CHAR_T)); \ + _len -= _this; \ + } \ + while (_len > 0); \ + } while (0) + +# define memset_zero(P, Len) \ + do { \ + int _len = (Len); \ + \ + do \ + { \ + int _this = _len > 16 ? 16 : _len; \ + (P) = MEMPCPY ((P), zeroes, _this * sizeof (CHAR_T)); \ + _len -= _this; \ + } \ + while (_len > 0); \ + } while (0) +#else +# ifdef COMPILE_WIDE +# define memset_space(P, Len) (wmemset ((P), L' ', (Len)), (P) += (Len)) +# define memset_zero(P, Len) (wmemset ((P), L'0', (Len)), (P) += (Len)) +# else +# define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len)) +# define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len)) +# endif +#endif + +#define add(n, f) \ + do \ + { \ + int _n = (n); \ + int _delta = width - _n; \ + int _incr = _n + (_delta > 0 ? _delta : 0); \ + if ((size_t) _incr >= maxsize - i) \ + return 0; \ + if (p) \ + { \ + if (_delta > 0) \ + { \ + if (pad == L_('0')) \ + memset_zero (p, _delta); \ + else \ + memset_space (p, _delta); \ + } \ + f; \ + p += _n; \ + } \ + i += _incr; \ + } while (0) + +#define cpy(n, s) \ + add ((n), \ + if (to_lowcase) \ + memcpy_lowcase (p, (s), _n LOCALE_ARG); \ + else if (to_uppcase) \ + memcpy_uppcase (p, (s), _n LOCALE_ARG); \ + else \ + MEMCPY ((PTR) p, (const PTR) (s), _n)) + +#ifdef COMPILE_WIDE +# ifndef USE_IN_EXTENDED_LOCALE_MODEL +# undef __mbsrtowcs_l +# define __mbsrtowcs_l(d, s, l, st, loc) __mbsrtowcs (d, s, l, st) +# endif +# define widen(os, ws, l) \ + { \ + mbstate_t __st; \ + const char *__s = os; \ + memset (&__st, '\0', sizeof (__st)); \ + l = __mbsrtowcs_l (NULL, &__s, 0, &__st, loc); \ + ws = alloca ((l + 1) * sizeof (wchar_t)); \ + (void) __mbsrtowcs_l (ws, &__s, l, &__st, loc); \ + } +#endif + + +#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL +/* 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. */ +# define strftime __strftime_l +# define wcsftime __wcsftime_l +# undef _NL_CURRENT +# define _NL_CURRENT(category, item) \ + (current->values[_NL_ITEM_INDEX (item)].string) +# define LOCALE_PARAM , loc +# define LOCALE_ARG , loc +# define LOCALE_PARAM_DECL __locale_t loc; +# define LOCALE_PARAM_PROTO , __locale_t loc +# define HELPER_LOCALE_ARG , current +#else +# define LOCALE_PARAM +# define LOCALE_PARAM_PROTO +# define LOCALE_ARG +# define LOCALE_PARAM_DECL +# ifdef _LIBC +# define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME) +# else +# define HELPER_LOCALE_ARG +# endif +#endif + +#ifdef COMPILE_WIDE +# ifdef USE_IN_EXTENDED_LOCALE_MODEL +# define TOUPPER(Ch, L) __towupper_l (Ch, L) +# define TOLOWER(Ch, L) __towlower_l (Ch, L) +# else +# define TOUPPER(Ch, L) towupper (Ch) +# define TOLOWER(Ch, L) towlower (Ch) +# endif +#else +# ifdef _LIBC +# ifdef USE_IN_EXTENDED_LOCALE_MODEL +# define TOUPPER(Ch, L) __toupper_l (Ch, L) +# define TOLOWER(Ch, L) __tolower_l (Ch, L) +# else +# define TOUPPER(Ch, L) toupper (Ch) +# define TOLOWER(Ch, L) tolower (Ch) +# endif +# else +# define TOUPPER(Ch, L) (islower (Ch) ? toupper (Ch) : (Ch)) +# define TOLOWER(Ch, L) (isupper (Ch) ? tolower (Ch) : (Ch)) +# endif +#endif +/* We don't use `isdigit' here since the locale dependent + interpretation is not what we want here. We only need to accept + the arabic digits in the ASCII range. One day there is perhaps a + more reliable way to accept other sets of digits. */ +#define ISDIGIT(Ch) ((unsigned int) (Ch) - L_('0') <= 9) + +static CHAR_T *memcpy_lowcase __P ((CHAR_T *dest, const CHAR_T *src, + size_t len LOCALE_PARAM_PROTO)); + +static CHAR_T * +memcpy_lowcase (dest, src, len LOCALE_PARAM) + CHAR_T *dest; + const CHAR_T *src; + size_t len; + LOCALE_PARAM_DECL +{ + while (len-- > 0) + dest[len] = TOLOWER ((UCHAR_T) src[len], loc); + return dest; +} + +static CHAR_T *memcpy_uppcase __P ((CHAR_T *dest, const CHAR_T *src, + size_t len LOCALE_PARAM_PROTO)); + +static CHAR_T * +memcpy_uppcase (dest, src, len LOCALE_PARAM) + CHAR_T *dest; + const CHAR_T *src; + size_t len; + LOCALE_PARAM_DECL +{ + while (len-- > 0) + dest[len] = TOUPPER ((UCHAR_T) src[len], loc); + return dest; +} + + +#if ! HAVE_TM_GMTOFF +/* Yield the difference between *A and *B, + measured in seconds, ignoring leap seconds. */ +# define tm_diff ftime_tm_diff +static int tm_diff __P ((const struct tm *, const struct tm *)); +static int +tm_diff (a, b) + const struct tm *a; + const struct tm *b; +{ + /* Compute intervening leap days correctly even if year is negative. + Take care to avoid int overflow in leap day calculations, + but it's OK to assume that A and B are close to each other. */ + int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3); + int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3); + int a100 = a4 / 25 - (a4 % 25 < 0); + int b100 = b4 / 25 - (b4 % 25 < 0); + int a400 = a100 >> 2; + int b400 = b100 >> 2; + int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); + int years = a->tm_year - b->tm_year; + int days = (365 * years + intervening_leap_days + + (a->tm_yday - b->tm_yday)); + return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) + + (a->tm_min - b->tm_min)) + + (a->tm_sec - b->tm_sec)); +} +#endif /* ! HAVE_TM_GMTOFF */ + + + +/* The number of days from the first day of the first ISO week of this + year to the year day YDAY with week day WDAY. ISO weeks start on + Monday; the first ISO week has the year's first Thursday. YDAY may + be as small as YDAY_MINIMUM. */ +#define ISO_WEEK_START_WDAY 1 /* Monday */ +#define ISO_WEEK1_WDAY 4 /* Thursday */ +#define YDAY_MINIMUM (-366) +static int iso_week_days __P ((int, int)); +#ifdef __GNUC__ +__inline__ +#endif +static int +iso_week_days (yday, wday) + int yday; + int wday; +{ + /* Add enough to the first operand of % to make it nonnegative. */ + int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7; + return (yday + - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7 + + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY); +} + + +#if !(defined _NL_CURRENT || HAVE_STRFTIME) +static CHAR_T const weekday_name[][10] = + { + L_("Sunday"), L_("Monday"), L_("Tuesday"), L_("Wednesday"), + L_("Thursday"), L_("Friday"), L_("Saturday") + }; +static CHAR_T const month_name[][10] = + { + L_("January"), L_("February"), L_("March"), L_("April"), L_("May"), + L_("June"), L_("July"), L_("August"), L_("September"), L_("October"), + L_("November"), L_("December") + }; +#endif + + +#ifdef emacs +# define my_strftime emacs_strftimeu +# define ut_argument , ut +# define ut_argument_spec int ut; +# define ut_argument_spec_iso , int ut +#else +# ifdef COMPILE_WIDE +# define my_strftime wcsftime +# define nl_get_alt_digit _nl_get_walt_digit +# else +# define my_strftime strftime +# define nl_get_alt_digit _nl_get_alt_digit +# endif +# define ut_argument +# define ut_argument_spec +# define ut_argument_spec_iso +/* We don't have this information in general. */ +# define ut 0 +#endif + +#if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET + /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime. + Work around this bug by copying *tp before it might be munged. */ + size_t _strftime_copytm __P ((char *, size_t, const char *, + const struct tm * ut_argument_spec_iso)); + size_t + my_strftime (s, maxsize, format, tp ut_argument) + CHAR_T *s; + size_t maxsize; + const CHAR_T *format; + const struct tm *tp; + ut_argument_spec + { + struct tm tmcopy; + tmcopy = *tp; + return _strftime_copytm (s, maxsize, format, &tmcopy ut_argument); + } +# undef my_strftime +# define my_strftime _strftime_copytm +#endif + + +/* Write information from TP into S according to the format + string FORMAT, writing no more that MAXSIZE characters + (including the terminating '\0') and returning number of + characters written. If S is NULL, nothing will be written + anywhere, so to determine how many characters would be + written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */ +size_t +my_strftime (s, maxsize, format, tp ut_argument LOCALE_PARAM) + CHAR_T *s; + size_t maxsize; + const CHAR_T *format; + const struct tm *tp; + ut_argument_spec + LOCALE_PARAM_DECL +{ +#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL + struct locale_data *const current = loc->__locales[LC_TIME]; +#endif + + int hour12 = tp->tm_hour; +#ifdef _NL_CURRENT + /* We cannot make the following values variables since we must delay + the evaluation of these values until really needed since some + expressions might not be valid in every situation. The `struct tm' + might be generated by a strptime() call that initialized + only a few elements. Dereference the pointers only if the format + requires this. Then it is ok to fail if the pointers are invalid. */ +# define a_wkday \ + ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday)) +# define f_wkday \ + ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday)) +# define a_month \ + ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon)) +# define f_month \ + ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon)) +# define ampm \ + ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11 \ + ? NLW(PM_STR) : NLW(AM_STR))) + +# define aw_len STRLEN (a_wkday) +# define am_len STRLEN (a_month) +# define ap_len STRLEN (ampm) +#else +# if !HAVE_STRFTIME +# define f_wkday (weekday_name[tp->tm_wday]) +# define f_month (month_name[tp->tm_mon]) +# define a_wkday f_wkday +# define a_month f_month +# define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11)) + + size_t aw_len = 3; + size_t am_len = 3; + size_t ap_len = 2; +# endif +#endif + const char *zone; + size_t i = 0; + CHAR_T *p = s; + const CHAR_T *f; +#if DO_MULTIBYTE && !defined COMPILE_WIDE + const char *format_end = NULL; +#endif + + zone = NULL; +#if HAVE_TM_ZONE + /* The POSIX test suite assumes that setting + the environment variable TZ to a new value before calling strftime() + will influence the result (the %Z format) even if the information in + TP is computed with a totally different time zone. + This is bogus: though POSIX allows bad behavior like this, + POSIX does not require it. Do the right thing instead. */ + zone = (const char *) tp->tm_zone; +#endif +#if HAVE_TZNAME + if (ut) + { + if (! (zone && *zone)) + zone = "GMT"; + } + else + { + /* POSIX.1 requires that local time zone information is used as + though strftime called tzset. */ +# if HAVE_TZSET + tzset (); +# endif + } +#endif + + if (hour12 > 12) + hour12 -= 12; + else + if (hour12 == 0) + hour12 = 12; + + for (f = format; *f != '\0'; ++f) + { + int pad = 0; /* Padding for number ('-', '_', or 0). */ + int modifier; /* Field modifier ('E', 'O', or 0). */ + int digits; /* Max digits for numeric format. */ + int number_value; /* Numeric value to be printed. */ + int negative_number; /* 1 if the number is negative. */ + const CHAR_T *subfmt; + CHAR_T *bufp; + CHAR_T buf[1 + (sizeof (int) < sizeof (time_t) + ? INT_STRLEN_BOUND (time_t) + : INT_STRLEN_BOUND (int))]; + int width = -1; + int to_lowcase = 0; + int to_uppcase = 0; + int change_case = 0; + int format_char; + +#if DO_MULTIBYTE && !defined COMPILE_WIDE + switch (*f) + { + case L_('%'): + break; + + case L_('\b'): case L_('\t'): case L_('\n'): + case L_('\v'): case L_('\f'): case L_('\r'): + case L_(' '): case L_('!'): case L_('"'): case L_('#'): case L_('&'): + case L_('\''): case L_('('): case L_(')'): case L_('*'): case L_('+'): + case L_(','): case L_('-'): case L_('.'): case L_('/'): case L_('0'): + case L_('1'): case L_('2'): case L_('3'): case L_('4'): case L_('5'): + case L_('6'): case L_('7'): case L_('8'): case L_('9'): case L_(':'): + case L_(';'): case L_('<'): case L_('='): case L_('>'): case L_('?'): + case L_('A'): case L_('B'): case L_('C'): case L_('D'): case L_('E'): + case L_('F'): case L_('G'): case L_('H'): case L_('I'): case L_('J'): + case L_('K'): case L_('L'): case L_('M'): case L_('N'): case L_('O'): + case L_('P'): case L_('Q'): case L_('R'): case L_('S'): case L_('T'): + case L_('U'): case L_('V'): case L_('W'): case L_('X'): case L_('Y'): + case L_('Z'): case L_('['): case L_('\\'): case L_(']'): case L_('^'): + case L_('_'): case L_('a'): case L_('b'): case L_('c'): case L_('d'): + case L_('e'): case L_('f'): case L_('g'): case L_('h'): case L_('i'): + case L_('j'): case L_('k'): case L_('l'): case L_('m'): case L_('n'): + case L_('o'): case L_('p'): case L_('q'): case L_('r'): case L_('s'): + case L_('t'): case L_('u'): case L_('v'): case L_('w'): case L_('x'): + case L_('y'): case L_('z'): case L_('{'): case L_('|'): case L_('}'): + case L_('~'): + /* The C Standard requires these 98 characters (plus '%') to + be in the basic execution character set. None of these + characters can start a multibyte sequence, so they need + not be analyzed further. */ + add (1, *p = *f); + continue; + + default: + /* Copy this multibyte sequence until we reach its end, find + an error, or come back to the initial shift state. */ + { + mbstate_t mbstate = mbstate_zero; + size_t len = 0; + size_t fsize; + + if (! format_end) + format_end = f + strlen (f) + 1; + fsize = format_end - f; + + do + { + size_t bytes = mbrlen (f + len, fsize - len, &mbstate); + + if (bytes == 0) + break; + + if (bytes == (size_t) -2) + { + len += strlen (f + len); + break; + } + + if (bytes == (size_t) -1) + { + len++; + break; + } + + len += bytes; + } + while (! mbsinit (&mbstate)); + + cpy (len, f); + f += len - 1; + continue; + } + } + +#else /* ! DO_MULTIBYTE */ + + /* Either multibyte encodings are not supported, they are + safe for formats, so any non-'%' byte can be copied through, + or this is the wide character version. */ + if (*f != L_('%')) + { + add (1, *p = *f); + continue; + } + +#endif /* ! DO_MULTIBYTE */ + + /* Check for flags that can modify a format. */ + while (1) + { + switch (*++f) + { + /* This influences the number formats. */ + case L_('_'): + case L_('-'): + case L_('0'): + pad = *f; + continue; + + /* This changes textual output. */ + case L_('^'): + to_uppcase = 1; + continue; + case L_('#'): + change_case = 1; + continue; + + default: + break; + } + break; + } + + /* As a GNU extension we allow to specify the field width. */ + if (ISDIGIT (*f)) + { + width = 0; + do + { + if (width > INT_MAX / 10 + || (width == INT_MAX / 10 && *f - L_('0') > INT_MAX % 10)) + /* Avoid overflow. */ + width = INT_MAX; + else + { + width *= 10; + width += *f - L_('0'); + } + ++f; + } + while (ISDIGIT (*f)); + } + + /* Check for modifiers. */ + switch (*f) + { + case L_('E'): + case L_('O'): + modifier = *f++; + break; + + default: + modifier = 0; + break; + } + + /* Now do the specified format. */ + format_char = *f; + switch (format_char) + { +#define DO_NUMBER(d, v) \ + digits = d > width ? d : width; \ + number_value = v; goto do_number +#define DO_NUMBER_SPACEPAD(d, v) \ + digits = d > width ? d : width; \ + number_value = v; goto do_number_spacepad + + case L_('%'): + if (modifier != 0) + goto bad_format; + add (1, *p = *f); + break; + + case L_('a'): + if (modifier != 0) + goto bad_format; + if (change_case) + { + to_uppcase = 1; + to_lowcase = 0; + } +#if defined _NL_CURRENT || !HAVE_STRFTIME + cpy (aw_len, a_wkday); + break; +#else + goto underlying_strftime; +#endif + + case 'A': + if (modifier != 0) + goto bad_format; + if (change_case) + { + to_uppcase = 1; + to_lowcase = 0; + } +#if defined _NL_CURRENT || !HAVE_STRFTIME + cpy (STRLEN (f_wkday), f_wkday); + break; +#else + goto underlying_strftime; +#endif + + case L_('b'): + case L_('h'): + if (change_case) + { + to_uppcase = 1; + to_lowcase = 0; + } + if (modifier != 0) + goto bad_format; +#if defined _NL_CURRENT || !HAVE_STRFTIME + cpy (am_len, a_month); + break; +#else + goto underlying_strftime; +#endif + + case L_('B'): + if (modifier != 0) + goto bad_format; + if (change_case) + { + to_uppcase = 1; + to_lowcase = 0; + } +#if defined _NL_CURRENT || !HAVE_STRFTIME + cpy (STRLEN (f_month), f_month); + break; +#else + goto underlying_strftime; +#endif + + case L_('c'): + if (modifier == L_('O')) + goto bad_format; +#ifdef _NL_CURRENT + if (! (modifier == 'E' + && (*(subfmt = + (const CHAR_T *) _NL_CURRENT (LC_TIME, + NLW(ERA_D_T_FMT))) + != '\0'))) + subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_T_FMT)); +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# else + subfmt = L_("%a %b %e %H:%M:%S %Y"); +# endif +#endif + + subformat: + { + CHAR_T *old_start = p; + size_t len = my_strftime (NULL, (size_t) -1, subfmt, + tp ut_argument LOCALE_ARG); + add (len, my_strftime (p, maxsize - i, subfmt, + tp ut_argument LOCALE_ARG)); + + if (to_uppcase) + while (old_start < p) + { + *old_start = TOUPPER ((UCHAR_T) *old_start, loc); + ++old_start; + } + } + break; + +#if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY) + underlying_strftime: + { + /* The relevant information is available only via the + underlying strftime implementation, so use that. */ + char ufmt[4]; + char *u = ufmt; + char ubuf[1024]; /* enough for any single format in practice */ + size_t len; + /* Make sure we're calling the actual underlying strftime. + In some cases, config.h contains something like + "#define strftime rpl_strftime". */ +# ifdef strftime +# undef strftime + size_t strftime (); +# endif + + *u++ = '%'; + if (modifier != 0) + *u++ = modifier; + *u++ = format_char; + *u = '\0'; + len = strftime (ubuf, sizeof ubuf, ufmt, tp); + if (len == 0 && ubuf[0] != '\0') + return 0; + cpy (len, ubuf); + } + break; +#endif + + case L_('C'): + if (modifier == L_('O')) + goto bad_format; + if (modifier == L_('E')) + { +#if HAVE_STRUCT_ERA_ENTRY + struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG); + if (era) + { +# ifdef COMPILE_WIDE + size_t len = __wcslen (era->era_wname); + cpy (len, era->era_wname); +# else + size_t len = strlen (era->era_name); + cpy (len, era->era_name); +# endif + break; + } +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# endif +#endif + } + + { + int year = tp->tm_year + TM_YEAR_BASE; + DO_NUMBER (1, year / 100 - (year % 100 < 0)); + } + + case L_('x'): + if (modifier == L_('O')) + goto bad_format; +#ifdef _NL_CURRENT + if (! (modifier == L_('E') + && (*(subfmt = + (const CHAR_T *)_NL_CURRENT (LC_TIME, NLW(ERA_D_FMT))) + != L_('\0')))) + subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_FMT)); + goto subformat; +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# else + /* Fall through. */ +# endif +#endif + case L_('D'): + if (modifier != 0) + goto bad_format; + subfmt = L_("%m/%d/%y"); + goto subformat; + + case L_('d'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, tp->tm_mday); + + case L_('e'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER_SPACEPAD (2, tp->tm_mday); + + /* All numeric formats set DIGITS and NUMBER_VALUE and then + jump to one of these two labels. */ + + do_number_spacepad: + /* Force `_' flag unless overwritten by `0' or '-' flag. */ + if (pad != L_('0') && pad != L_('-')) + pad = L_('_'); + + do_number: + /* Format the number according to the MODIFIER flag. */ + + if (modifier == L_('O') && 0 <= number_value) + { +#ifdef _NL_CURRENT + /* Get the locale specific alternate representation of + the number NUMBER_VALUE. If none exist NULL is returned. */ + const CHAR_T *cp = nl_get_alt_digit (number_value + HELPER_LOCALE_ARG); + + if (cp != NULL) + { + size_t digitlen = STRLEN (cp); + if (digitlen != 0) + { + cpy (digitlen, cp); + break; + } + } +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# endif +#endif + } + { + unsigned int u = number_value; + + bufp = buf + sizeof (buf) / sizeof (buf[0]); + negative_number = number_value < 0; + + if (negative_number) + u = -u; + + do + *--bufp = u % 10 + L_('0'); + while ((u /= 10) != 0); + } + + do_number_sign_and_padding: + if (negative_number) + *--bufp = L_('-'); + + if (pad != L_('-')) + { + int padding = digits - (buf + (sizeof (buf) / sizeof (buf[0])) + - bufp); + + if (padding > 0) + { + if (pad == L_('_')) + { + if ((size_t) padding >= maxsize - i) + return 0; + + if (p) + memset_space (p, padding); + i += padding; + width = width > padding ? width - padding : 0; + } + else + { + if ((size_t) digits >= maxsize - i) + return 0; + + if (negative_number) + { + ++bufp; + + if (p) + *p++ = L_('-'); + ++i; + } + + if (p) + memset_zero (p, padding); + i += padding; + width = 0; + } + } + } + + cpy (buf + sizeof (buf) / sizeof (buf[0]) - bufp, bufp); + break; + + case L_('F'): + if (modifier != 0) + goto bad_format; + subfmt = L_("%Y-%m-%d"); + goto subformat; + + case L_('H'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, tp->tm_hour); + + case L_('I'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, hour12); + + case L_('k'): /* GNU extension. */ + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER_SPACEPAD (2, tp->tm_hour); + + case L_('l'): /* GNU extension. */ + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER_SPACEPAD (2, hour12); + + case L_('j'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (3, 1 + tp->tm_yday); + + case L_('M'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, tp->tm_min); + + case L_('m'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, tp->tm_mon + 1); + + case L_('n'): + add (1, *p = L_('\n')); + break; + + case L_('P'): + to_lowcase = 1; +#if !defined _NL_CURRENT && HAVE_STRFTIME + format_char = L_('p'); +#endif + /* FALLTHROUGH */ + + case L_('p'): + if (change_case) + { + to_uppcase = 0; + to_lowcase = 1; + } +#if defined _NL_CURRENT || !HAVE_STRFTIME + cpy (ap_len, ampm); + break; +#else + goto underlying_strftime; +#endif + + case L_('R'): + subfmt = L_("%H:%M"); + goto subformat; + + case L_('r'): +#if !defined _NL_CURRENT && HAVE_STRFTIME + goto underlying_strftime; +#else +# ifdef _NL_CURRENT + if (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, + NLW(T_FMT_AMPM))) + == L_('\0')) +# endif + subfmt = L_("%I:%M:%S %p"); + goto subformat; +#endif + + case L_('S'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, tp->tm_sec); + + case L_('s'): /* GNU extension. */ + { + struct tm ltm; + time_t t; + + ltm = *tp; + t = mktime (<m); + + /* Generate string value for T using time_t arithmetic; + this works even if sizeof (long) < sizeof (time_t). */ + + bufp = buf + sizeof (buf) / sizeof (buf[0]); + negative_number = t < 0; + + do + { + int d = t % 10; + t /= 10; + + if (negative_number) + { + d = -d; + + /* Adjust if division truncates to minus infinity. */ + if (0 < -1 % 10 && d < 0) + { + t++; + d += 10; + } + } + + *--bufp = d + L_('0'); + } + while (t != 0); + + digits = 1; + goto do_number_sign_and_padding; + } + + case L_('X'): + if (modifier == L_('O')) + goto bad_format; +#ifdef _NL_CURRENT + if (! (modifier == L_('E') + && (*(subfmt = + (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_T_FMT))) + != L_('\0')))) + subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(T_FMT)); + goto subformat; +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# else + /* Fall through. */ +# endif +#endif + case L_('T'): + subfmt = L_("%H:%M:%S"); + goto subformat; + + case L_('t'): + add (1, *p = L_('\t')); + break; + + case L_('u'): + DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1); + + case L_('U'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7); + + case L_('V'): + case L_('g'): + case L_('G'): + if (modifier == L_('E')) + goto bad_format; + { + int year = tp->tm_year + TM_YEAR_BASE; + int days = iso_week_days (tp->tm_yday, tp->tm_wday); + + if (days < 0) + { + /* This ISO week belongs to the previous year. */ + year--; + days = iso_week_days (tp->tm_yday + (365 + __isleap (year)), + tp->tm_wday); + } + else + { + int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)), + tp->tm_wday); + if (0 <= d) + { + /* This ISO week belongs to the next year. */ + year++; + days = d; + } + } + + switch (*f) + { + case L_('g'): + DO_NUMBER (2, (year % 100 + 100) % 100); + + case L_('G'): + DO_NUMBER (1, year); + + default: + DO_NUMBER (2, days / 7 + 1); + } + } + + case L_('W'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7); + + case L_('w'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (1, tp->tm_wday); + + case L_('Y'): + if (modifier == 'E') + { +#if HAVE_STRUCT_ERA_ENTRY + struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG); + if (era) + { +# ifdef COMPILE_WIDE + subfmt = era->era_wformat; +# else + subfmt = era->era_format; +# endif + goto subformat; + } +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# endif +#endif + } + if (modifier == L_('O')) + goto bad_format; + else + DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE); + + case L_('y'): + if (modifier == L_('E')) + { +#if HAVE_STRUCT_ERA_ENTRY + struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG); + if (era) + { + int delta = tp->tm_year - era->start_date[0]; + DO_NUMBER (1, (era->offset + + delta * era->absolute_direction)); + } +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# endif +#endif + } + DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100); + + case L_('Z'): + if (change_case) + { + to_uppcase = 0; + to_lowcase = 1; + } + +#if HAVE_TZNAME + /* The tzset() call might have changed the value. */ + if (!(zone && *zone) && tp->tm_isdst >= 0) + zone = tzname[tp->tm_isdst]; +#endif + if (! zone) + zone = ""; + +#ifdef COMPILE_WIDE + { + /* The zone string is always given in multibyte form. We have + to transform it first. */ + wchar_t *wczone; + size_t len; + widen (zone, wczone, len); + cpy (len, wczone); + } +#else + cpy (strlen (zone), zone); +#endif + break; + + case L_('z'): + if (tp->tm_isdst < 0) + break; + + { + int diff; +#if HAVE_TM_GMTOFF + diff = tp->tm_gmtoff; +#else + if (ut) + diff = 0; + else + { + struct tm gtm; + struct tm ltm; + time_t lt; + + ltm = *tp; + lt = mktime (<m); + + if (lt == (time_t) -1) + { + /* mktime returns -1 for errors, but -1 is also a + valid time_t value. Check whether an error really + occurred. */ + struct tm tm; + + if (! __localtime_r (<, &tm) + || ((ltm.tm_sec ^ tm.tm_sec) + | (ltm.tm_min ^ tm.tm_min) + | (ltm.tm_hour ^ tm.tm_hour) + | (ltm.tm_mday ^ tm.tm_mday) + | (ltm.tm_mon ^ tm.tm_mon) + | (ltm.tm_year ^ tm.tm_year))) + break; + } + + if (! __gmtime_r (<, >m)) + break; + + diff = tm_diff (<m, >m); + } +#endif + + if (diff < 0) + { + add (1, *p = L_('-')); + diff = -diff; + } + else + add (1, *p = L_('+')); + + diff /= 60; + DO_NUMBER (4, (diff / 60) * 100 + diff % 60); + } + + case L_('\0'): /* GNU extension: % at end of format. */ + --f; + /* Fall through. */ + default: + /* Unknown format; output the format, including the '%', + since this is most likely the right thing to do if a + multibyte string has been misparsed. */ + bad_format: + { + int flen; + for (flen = 1; f[1 - flen] != L_('%'); flen++) + continue; + cpy (flen, &f[1 - flen]); + } + break; + } + } + + if (p && maxsize != 0) + *p = L_('\0'); + return i; +} +#ifdef _LIBC +libc_hidden_def (my_strftime) +#endif + + +#ifdef emacs +/* For Emacs we have a separate interface which corresponds to the normal + strftime function and does not have the extra information whether the + TP arguments comes from a `gmtime' call or not. */ +size_t +emacs_strftime (s, maxsize, format, tp) + char *s; + size_t maxsize; + const char *format; + const struct tm *tp; +{ + return my_strftime (s, maxsize, format, tp, 0); +} +#endif + +#if defined _LIBC && !defined COMPILE_WIDE weak_alias (__strftime_l, strftime_l) +#endif diff --git a/time/strptime.c b/time/strptime.c index 1edd4ac257..fc3526912d 100644 --- a/time/strptime.c +++ b/time/strptime.c @@ -1,5 +1,5 @@ /* Convert a string representation of time to a time value. - Copyright (C) 1996-2000, 2001, 2002 Free Software Foundation, Inc. + Copyright (C) 1996-2000, 2001, 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. @@ -23,1072 +23,18 @@ some of them in the same format (such as year, week and weekday) this is enough information for determining the date. */ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <ctype.h> -#include <langinfo.h> -#include <limits.h> -#include <string.h> #include <time.h> - -#ifdef _LIBC -# include "../locale/localeinfo.h" -#endif - - -#ifndef __P -# if defined __GNUC__ || (defined __STDC__ && __STDC__) -# define __P(args) args -# else -# define __P(args) () -# endif /* GCC. */ -#endif /* Not __P. */ - - -#if ! HAVE_LOCALTIME_R && ! defined localtime_r -# ifdef _LIBC -# define localtime_r __localtime_r -# else -/* Approximate localtime_r as best we can in its absence. */ -# define localtime_r my_localtime_r -static struct tm *localtime_r __P ((const time_t *, struct tm *)); -static struct tm * -localtime_r (t, tp) - const time_t *t; - struct tm *tp; -{ - struct tm *l = localtime (t); - if (! l) - return 0; - *tp = *l; - return tp; -} -# endif /* ! _LIBC */ -#endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */ - - -#define match_char(ch1, ch2) if (ch1 != ch2) return NULL -#if defined __GNUC__ && __GNUC__ >= 2 -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define match_string(cs1, s2) \ - ({ size_t len = strlen (cs1); \ - int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0; \ - if (result) (s2) += len; \ - result; }) -# else -# define match_string(cs1, s2) \ - ({ size_t len = strlen (cs1); \ - int result = strncasecmp ((cs1), (s2), len) == 0; \ - if (result) (s2) += len; \ - result; }) -# endif -#else -/* Oh come on. Get a reasonable compiler. */ -# define match_string(cs1, s2) \ - (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1)) -#endif -/* We intentionally do not use isdigit() for testing because this will - lead to problems with the wide character version. */ -#define get_number(from, to, n) \ - do { \ - int __n = n; \ - val = 0; \ - while (*rp == ' ') \ - ++rp; \ - if (*rp < '0' || *rp > '9') \ - return NULL; \ - do { \ - val *= 10; \ - val += *rp++ - '0'; \ - } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \ - if (val < from || val > to) \ - return NULL; \ - } while (0) -#ifdef _NL_CURRENT -# define get_alt_number(from, to, n) \ - ({ \ - __label__ do_normal; \ - \ - if (*decided != raw) \ - { \ - val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \ - if (val == -1 && *decided != loc) \ - { \ - *decided = loc; \ - goto do_normal; \ - } \ - if (val < from || val > to) \ - return NULL; \ - } \ - else \ - { \ - do_normal: \ - get_number (from, to, n); \ - } \ - 0; \ - }) -#else -# define get_alt_number(from, to, n) \ - /* We don't have the alternate representation. */ \ - get_number(from, to, n) -#endif -#define recursive(new_fmt) \ - (*(new_fmt) != '\0' \ - && (rp = strptime_internal (rp, (new_fmt), tm, \ - decided, era_cnt LOCALE_ARG)) != NULL) - - -#ifdef _LIBC -/* This is defined in locale/C-time.c in the GNU libc. */ -extern const struct locale_data _nl_C_LC_TIME attribute_hidden; - -# define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string) -# define ab_weekday_name \ - (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string) -# define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string) -# define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string) -# define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string) -# define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string) -# define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string) -# define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string) -# define HERE_T_FMT_AMPM \ - (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string) -# define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string) - -# define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n) -#else -static char const weekday_name[][10] = - { - "Sunday", "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday" - }; -static char const ab_weekday_name[][4] = - { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; -static char const month_name[][10] = - { - "January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December" - }; -static char const ab_month_name[][4] = - { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; -# define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y" -# define HERE_D_FMT "%m/%d/%y" -# define HERE_AM_STR "AM" -# define HERE_PM_STR "PM" -# define HERE_T_FMT_AMPM "%I:%M:%S %p" -# define HERE_T_FMT "%H:%M:%S" - -static const unsigned short int __mon_yday[2][13] = - { - /* Normal years. */ - { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, - /* Leap years. */ - { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } - }; -#endif - -#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL -/* 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. */ -# define strptime __strptime_l -# undef _NL_CURRENT -# define _NL_CURRENT(category, item) \ - (current->values[_NL_ITEM_INDEX (item)].string) -# undef _NL_CURRENT_WORD -# define _NL_CURRENT_WORD(category, item) \ - (current->values[_NL_ITEM_INDEX (item)].word) -# define LOCALE_PARAM , locale -# define LOCALE_ARG , locale -# define LOCALE_PARAM_PROTO , __locale_t locale -# define LOCALE_PARAM_DECL __locale_t locale; -# define HELPER_LOCALE_ARG , current -# define ISSPACE(Ch) __isspace_l (Ch, locale) -#else -# define LOCALE_PARAM -# define LOCALE_ARG -# define LOCALE_PARAM_DECL -# define LOCALE_PARAM_PROTO -# ifdef _LIBC -# define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME) -# else -# define HELPER_LOCALE_ARG -# endif -# define ISSPACE(Ch) isspace (Ch) -#endif - - -/* Status of lookup: do we use the locale data or the raw data? */ -enum locale_status { not, loc, raw }; - - -#ifndef __isleap -/* Nonzero if YEAR is a leap year (every 4 years, - except every 100th isn't, and every 400th is). */ -# define __isleap(year) \ - ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) -#endif - -/* Compute the day of the week. */ -static void -day_of_the_week (struct tm *tm) -{ - /* We know that January 1st 1970 was a Thursday (= 4). Compute the - the difference between this data in the one on TM and so determine - the weekday. */ - int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2); - int wday = (-473 - + (365 * (tm->tm_year - 70)) - + (corr_year / 4) - - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0) - + (((corr_year / 4) / 25) / 4) - + __mon_yday[0][tm->tm_mon] - + tm->tm_mday - 1); - tm->tm_wday = ((wday % 7) + 7) % 7; -} - -/* Compute the day of the year. */ -static void -day_of_the_year (struct tm *tm) -{ - tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon] - + (tm->tm_mday - 1)); -} - - -static char * -#ifdef _LIBC -internal_function -#endif -strptime_internal __P ((const char *rp, const char *fmt, struct tm *tm, - enum locale_status *decided, int era_cnt - LOCALE_PARAM_PROTO)); - -static char * -#ifdef _LIBC -internal_function -#endif -strptime_internal (rp, fmt, tm, decided, era_cnt LOCALE_PARAM) - const char *rp; - const char *fmt; - struct tm *tm; - enum locale_status *decided; - int era_cnt; - LOCALE_PARAM_DECL -{ -#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL - struct locale_data *const current = locale->__locales[LC_TIME]; -#endif - - const char *rp_backup; - int cnt; - size_t val; - int have_I, is_pm; - int century, want_century; - int want_era; - int have_wday, want_xday; - int have_yday; - int have_mon, have_mday; - int have_uweek, have_wweek; - int week_no; - size_t num_eras; - struct era_entry *era; - - have_I = is_pm = 0; - century = -1; - want_century = 0; - want_era = 0; - era = NULL; - week_no = 0; - - have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0; - have_wweek = 0; - - while (*fmt != '\0') - { - /* A white space in the format string matches 0 more or white - space in the input string. */ - if (ISSPACE (*fmt)) - { - while (ISSPACE (*rp)) - ++rp; - ++fmt; - continue; - } - - /* Any character but `%' must be matched by the same character - in the iput string. */ - if (*fmt != '%') - { - match_char (*fmt++, *rp++); - continue; - } - - ++fmt; -#ifndef _NL_CURRENT - /* We need this for handling the `E' modifier. */ - start_over: -#endif - - /* Make back up of current processing pointer. */ - rp_backup = rp; - - switch (*fmt++) - { - case '%': - /* Match the `%' character itself. */ - match_char ('%', *rp++); - break; - case 'a': - case 'A': - /* Match day of week. */ - for (cnt = 0; cnt < 7; ++cnt) - { -#ifdef _NL_CURRENT - if (*decided !=raw) - { - if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp)) - { - if (*decided == not - && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt), - weekday_name[cnt])) - *decided = loc; - break; - } - if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp)) - { - if (*decided == not - && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), - ab_weekday_name[cnt])) - *decided = loc; - break; - } - } -#endif - if (*decided != loc - && (match_string (weekday_name[cnt], rp) - || match_string (ab_weekday_name[cnt], rp))) - { - *decided = raw; - break; - } - } - if (cnt == 7) - /* Does not match a weekday name. */ - return NULL; - tm->tm_wday = cnt; - have_wday = 1; - break; - case 'b': - case 'B': - case 'h': - /* Match month name. */ - for (cnt = 0; cnt < 12; ++cnt) - { -#ifdef _NL_CURRENT - if (*decided !=raw) - { - if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp)) - { - if (*decided == not - && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt), - month_name[cnt])) - *decided = loc; - break; - } - if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp)) - { - if (*decided == not - && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), - ab_month_name[cnt])) - *decided = loc; - break; - } - } -#endif - if (match_string (month_name[cnt], rp) - || match_string (ab_month_name[cnt], rp)) - { - *decided = raw; - break; - } - } - if (cnt == 12) - /* Does not match a month name. */ - return NULL; - tm->tm_mon = cnt; - want_xday = 1; - break; - case 'c': - /* Match locale's date and time format. */ -#ifdef _NL_CURRENT - if (*decided != raw) - { - if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT))) - { - if (*decided == loc) - return NULL; - else - rp = rp_backup; - } - else - { - if (*decided == not && - strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT)) - *decided = loc; - want_xday = 1; - break; - } - *decided = raw; - } -#endif - if (!recursive (HERE_D_T_FMT)) - return NULL; - want_xday = 1; - break; - case 'C': - /* Match century number. */ - match_century: - get_number (0, 99, 2); - century = val; - want_xday = 1; - break; - case 'd': - case 'e': - /* Match day of month. */ - get_number (1, 31, 2); - tm->tm_mday = val; - have_mday = 1; - want_xday = 1; - break; - case 'F': - if (!recursive ("%Y-%m-%d")) - return NULL; - want_xday = 1; - break; - case 'x': -#ifdef _NL_CURRENT - if (*decided != raw) - { - if (!recursive (_NL_CURRENT (LC_TIME, D_FMT))) - { - if (*decided == loc) - return NULL; - else - rp = rp_backup; - } - else - { - if (*decided == not - && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT)) - *decided = loc; - want_xday = 1; - break; - } - *decided = raw; - } -#endif - /* Fall through. */ - case 'D': - /* Match standard day format. */ - if (!recursive (HERE_D_FMT)) - return NULL; - want_xday = 1; - break; - case 'k': - case 'H': - /* Match hour in 24-hour clock. */ - get_number (0, 23, 2); - tm->tm_hour = val; - have_I = 0; - break; - case 'l': - /* Match hour in 12-hour clock. GNU extension. */ - case 'I': - /* Match hour in 12-hour clock. */ - get_number (1, 12, 2); - tm->tm_hour = val % 12; - have_I = 1; - break; - case 'j': - /* Match day number of year. */ - get_number (1, 366, 3); - tm->tm_yday = val - 1; - have_yday = 1; - break; - case 'm': - /* Match number of month. */ - get_number (1, 12, 2); - tm->tm_mon = val - 1; - have_mon = 1; - want_xday = 1; - break; - case 'M': - /* Match minute. */ - get_number (0, 59, 2); - tm->tm_min = val; - break; - case 'n': - case 't': - /* Match any white space. */ - while (ISSPACE (*rp)) - ++rp; - break; - case 'p': - /* Match locale's equivalent of AM/PM. */ -#ifdef _NL_CURRENT - if (*decided != raw) - { - if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp)) - { - if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR)) - *decided = loc; - break; - } - if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp)) - { - if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR)) - *decided = loc; - is_pm = 1; - break; - } - *decided = raw; - } -#endif - if (!match_string (HERE_AM_STR, rp)) - if (match_string (HERE_PM_STR, rp)) - is_pm = 1; - else - return NULL; - break; - case 'r': -#ifdef _NL_CURRENT - if (*decided != raw) - { - if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM))) - { - if (*decided == loc) - return NULL; - else - rp = rp_backup; - } - else - { - if (*decided == not && - strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM), - HERE_T_FMT_AMPM)) - *decided = loc; - break; - } - *decided = raw; - } -#endif - if (!recursive (HERE_T_FMT_AMPM)) - return NULL; - break; - case 'R': - if (!recursive ("%H:%M")) - return NULL; - break; - case 's': - { - /* The number of seconds may be very high so we cannot use - the `get_number' macro. Instead read the number - character for character and construct the result while - doing this. */ - time_t secs = 0; - if (*rp < '0' || *rp > '9') - /* We need at least one digit. */ - return NULL; - - do - { - secs *= 10; - secs += *rp++ - '0'; - } - while (*rp >= '0' && *rp <= '9'); - - if (localtime_r (&secs, tm) == NULL) - /* Error in function. */ - return NULL; - } - break; - case 'S': - get_number (0, 61, 2); - tm->tm_sec = val; - break; - case 'X': -#ifdef _NL_CURRENT - if (*decided != raw) - { - if (!recursive (_NL_CURRENT (LC_TIME, T_FMT))) - { - if (*decided == loc) - return NULL; - else - rp = rp_backup; - } - else - { - if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT)) - *decided = loc; - break; - } - *decided = raw; - } -#endif - /* Fall through. */ - case 'T': - if (!recursive (HERE_T_FMT)) - return NULL; - break; - case 'u': - get_number (1, 7, 1); - tm->tm_wday = val % 7; - have_wday = 1; - break; - case 'g': - get_number (0, 99, 2); - /* XXX This cannot determine any field in TM. */ - break; - case 'G': - if (*rp < '0' || *rp > '9') - return NULL; - /* XXX Ignore the number since we would need some more - information to compute a real date. */ - do - ++rp; - while (*rp >= '0' && *rp <= '9'); - break; - case 'U': - get_number (0, 53, 2); - week_no = val; - have_uweek = 1; - break; - case 'W': - get_number (0, 53, 2); - week_no = val; - have_wweek = 1; - break; - case 'V': - get_number (0, 53, 2); - /* XXX This cannot determine any field in TM without some - information. */ - break; - case 'w': - /* Match number of weekday. */ - get_number (0, 6, 1); - tm->tm_wday = val; - have_wday = 1; - break; - case 'y': - match_year_in_century: - /* Match year within century. */ - get_number (0, 99, 2); - /* The "Year 2000: The Millennium Rollover" paper suggests that - values in the range 69-99 refer to the twentieth century. */ - tm->tm_year = val >= 69 ? val : val + 100; - /* Indicate that we want to use the century, if specified. */ - want_century = 1; - want_xday = 1; - break; - case 'Y': - /* Match year including century number. */ - get_number (0, 9999, 4); - tm->tm_year = val - 1900; - want_century = 0; - want_xday = 1; - break; - case 'Z': - /* XXX How to handle this? */ - break; - case 'E': -#ifdef _NL_CURRENT - switch (*fmt++) - { - case 'c': - /* Match locale's alternate date and time format. */ - if (*decided != raw) - { - const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT); - - if (*fmt == '\0') - fmt = _NL_CURRENT (LC_TIME, D_T_FMT); - - if (!recursive (fmt)) - { - if (*decided == loc) - return NULL; - else - rp = rp_backup; - } - else - { - if (strcmp (fmt, HERE_D_T_FMT)) - *decided = loc; - want_xday = 1; - break; - } - *decided = raw; - } - /* The C locale has no era information, so use the - normal representation. */ - if (!recursive (HERE_D_T_FMT)) - return NULL; - want_xday = 1; - break; - case 'C': - if (*decided != raw) - { - if (era_cnt >= 0) - { - era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG); - if (era != NULL && match_string (era->era_name, rp)) - { - *decided = loc; - break; - } - else - return NULL; - } - else - { - num_eras = _NL_CURRENT_WORD (LC_TIME, - _NL_TIME_ERA_NUM_ENTRIES); - for (era_cnt = 0; era_cnt < (int) num_eras; - ++era_cnt, rp = rp_backup) - { - era = _nl_select_era_entry (era_cnt - HELPER_LOCALE_ARG); - if (era != NULL && match_string (era->era_name, rp)) - { - *decided = loc; - break; - } - } - if (era_cnt == (int) num_eras) - { - era_cnt = -1; - if (*decided == loc) - return NULL; - } - else - break; - } - - *decided = raw; - } - /* The C locale has no era information, so use the - normal representation. */ - goto match_century; - case 'y': - if (*decided == raw) - goto match_year_in_century; - - get_number(0, 9999, 4); - tm->tm_year = val; - want_era = 1; - want_xday = 1; - want_century = 1; - break; - case 'Y': - if (*decided != raw) - { - num_eras = _NL_CURRENT_WORD (LC_TIME, - _NL_TIME_ERA_NUM_ENTRIES); - for (era_cnt = 0; era_cnt < (int) num_eras; - ++era_cnt, rp = rp_backup) - { - era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG); - if (era != NULL && recursive (era->era_format)) - break; - } - if (era_cnt == (int) num_eras) - { - era_cnt = -1; - if (*decided == loc) - return NULL; - else - rp = rp_backup; - } - else - { - *decided = loc; - era_cnt = -1; - break; - } - - *decided = raw; - } - get_number (0, 9999, 4); - tm->tm_year = val - 1900; - want_century = 0; - want_xday = 1; - break; - case 'x': - if (*decided != raw) - { - const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT); - - if (*fmt == '\0') - fmt = _NL_CURRENT (LC_TIME, D_FMT); - - if (!recursive (fmt)) - { - if (*decided == loc) - return NULL; - else - rp = rp_backup; - } - else - { - if (strcmp (fmt, HERE_D_FMT)) - *decided = loc; - break; - } - *decided = raw; - } - if (!recursive (HERE_D_FMT)) - return NULL; - break; - case 'X': - if (*decided != raw) - { - const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT); - - if (*fmt == '\0') - fmt = _NL_CURRENT (LC_TIME, T_FMT); - - if (!recursive (fmt)) - { - if (*decided == loc) - return NULL; - else - rp = rp_backup; - } - else - { - if (strcmp (fmt, HERE_T_FMT)) - *decided = loc; - break; - } - *decided = raw; - } - if (!recursive (HERE_T_FMT)) - return NULL; - break; - default: - return NULL; - } - break; -#else - /* We have no information about the era format. Just use - the normal format. */ - if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y' - && *fmt != 'x' && *fmt != 'X') - /* This is an illegal format. */ - return NULL; - - goto start_over; -#endif - case 'O': - switch (*fmt++) - { - case 'd': - case 'e': - /* Match day of month using alternate numeric symbols. */ - get_alt_number (1, 31, 2); - tm->tm_mday = val; - have_mday = 1; - want_xday = 1; - break; - case 'H': - /* Match hour in 24-hour clock using alternate numeric - symbols. */ - get_alt_number (0, 23, 2); - tm->tm_hour = val; - have_I = 0; - break; - case 'I': - /* Match hour in 12-hour clock using alternate numeric - symbols. */ - get_alt_number (1, 12, 2); - tm->tm_hour = val % 12; - have_I = 1; - break; - case 'm': - /* Match month using alternate numeric symbols. */ - get_alt_number (1, 12, 2); - tm->tm_mon = val - 1; - have_mon = 1; - want_xday = 1; - break; - case 'M': - /* Match minutes using alternate numeric symbols. */ - get_alt_number (0, 59, 2); - tm->tm_min = val; - break; - case 'S': - /* Match seconds using alternate numeric symbols. */ - get_alt_number (0, 61, 2); - tm->tm_sec = val; - break; - case 'U': - get_alt_number (0, 53, 2); - week_no = val; - have_uweek = 1; - break; - case 'W': - get_alt_number (0, 53, 2); - week_no = val; - have_wweek = 1; - break; - case 'V': - get_alt_number (0, 53, 2); - /* XXX This cannot determine any field in TM without - further information. */ - break; - case 'w': - /* Match number of weekday using alternate numeric symbols. */ - get_alt_number (0, 6, 1); - tm->tm_wday = val; - have_wday = 1; - break; - case 'y': - /* Match year within century using alternate numeric symbols. */ - get_alt_number (0, 99, 2); - tm->tm_year = val >= 69 ? val : val + 100; - want_xday = 1; - break; - default: - return NULL; - } - break; - default: - return NULL; - } - } - - if (have_I && is_pm) - tm->tm_hour += 12; - - if (century != -1) - { - if (want_century) - tm->tm_year = tm->tm_year % 100 + (century - 19) * 100; - else - /* Only the century, but not the year. Strange, but so be it. */ - tm->tm_year = (century - 19) * 100; - } - - if (era_cnt != -1) - { - era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG); - if (era == NULL) - return NULL; - if (want_era) - tm->tm_year = (era->start_date[0] - + ((tm->tm_year - era->offset) - * era->absolute_direction)); - else - /* Era start year assumed. */ - tm->tm_year = era->start_date[0]; - } - else - if (want_era) - { - /* No era found but we have seen an E modifier. Rectify some - values. */ - if (want_century && century == -1 && tm->tm_year < 69) - tm->tm_year += 100; - } - - if (want_xday && !have_wday) - { - if ( !(have_mon && have_mday) && have_yday) - { - /* We don't have tm_mon and/or tm_mday, compute them. */ - int t_mon = 0; - while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday) - t_mon++; - if (!have_mon) - tm->tm_mon = t_mon - 1; - if (!have_mday) - tm->tm_mday = - (tm->tm_yday - - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); - } - day_of_the_week (tm); - } - - if (want_xday && !have_yday) - day_of_the_year (tm); - - if ((have_uweek || have_wweek) && have_wday) - { - int save_wday = tm->tm_wday; - int save_mday = tm->tm_mday; - int save_mon = tm->tm_mon; - int w_offset = have_uweek ? 0 : 1; - - tm->tm_mday = 1; - tm->tm_mon = 0; - day_of_the_week (tm); - if (have_mday) - tm->tm_mday = save_mday; - if (have_mon) - tm->tm_mon = save_mon; - - if (!have_yday) - tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7 - + (week_no - 1) *7 - + save_wday - w_offset); - - if (!have_mday || !have_mon) - { - int t_mon = 0; - while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] - <= tm->tm_yday) - t_mon++; - if (!have_mon) - tm->tm_mon = t_mon - 1; - if (!have_mday) - tm->tm_mday = - (tm->tm_yday - - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); - } - - tm->tm_wday = save_wday; - } - - return (char *) rp; -} +#include <locale/localeinfo.h> char * -strptime (buf, format, tm LOCALE_PARAM) +strptime (buf, format, tm) const char *buf; const char *format; struct tm *tm; - LOCALE_PARAM_DECL { - enum locale_status decided; - -#ifdef _NL_CURRENT - decided = not; -#else - decided = raw; -#endif - return strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG); + enum ptime_locale_status decided = not; + return __strptime_internal (buf, format, tm, &decided, -1, + _NL_CURRENT_LOCALE); } -#if defined _LIBC && !defined USE_IN_EXTENDED_LOCALE_MODEL libc_hidden_def (strptime) -#endif diff --git a/time/strptime_l.c b/time/strptime_l.c index 681386003d..b95f0982e8 100644 --- a/time/strptime_l.c +++ b/time/strptime_l.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002 Free Software Foundation, Inc. +/* Copyright (C) 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -16,7 +16,1053 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define USE_IN_EXTENDED_LOCALE_MODEL 1 -#include <strptime.c> +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <ctype.h> +#include <langinfo.h> +#include <limits.h> +#include <string.h> +#include <time.h> + +#ifdef _LIBC +# include "../locale/localeinfo.h" +#endif + + +#ifndef __P +# if defined __GNUC__ || (defined __STDC__ && __STDC__) +# define __P(args) args +# else +# define __P(args) () +# endif /* GCC. */ +#endif /* Not __P. */ + + +#if ! HAVE_LOCALTIME_R && ! defined localtime_r +# ifdef _LIBC +# define localtime_r __localtime_r +# else +/* Approximate localtime_r as best we can in its absence. */ +# define localtime_r my_localtime_r +static struct tm *localtime_r __P ((const time_t *, struct tm *)); +static struct tm * +localtime_r (t, tp) + const time_t *t; + struct tm *tp; +{ + struct tm *l = localtime (t); + if (! l) + return 0; + *tp = *l; + return tp; +} +# endif /* ! _LIBC */ +#endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */ + + +#define match_char(ch1, ch2) if (ch1 != ch2) return NULL +#if defined __GNUC__ && __GNUC__ >= 2 +# define match_string(cs1, s2) \ + ({ size_t len = strlen (cs1); \ + int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0; \ + if (result) (s2) += len; \ + result; }) +#else +/* Oh come on. Get a reasonable compiler. */ +# define match_string(cs1, s2) \ + (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1)) +#endif +/* We intentionally do not use isdigit() for testing because this will + lead to problems with the wide character version. */ +#define get_number(from, to, n) \ + do { \ + int __n = n; \ + val = 0; \ + while (*rp == ' ') \ + ++rp; \ + if (*rp < '0' || *rp > '9') \ + return NULL; \ + do { \ + val *= 10; \ + val += *rp++ - '0'; \ + } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \ + if (val < from || val > to) \ + return NULL; \ + } while (0) +#ifdef _NL_CURRENT +# define get_alt_number(from, to, n) \ + ({ \ + __label__ do_normal; \ + \ + if (*decided != raw) \ + { \ + val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \ + if (val == -1 && *decided != loc) \ + { \ + *decided = loc; \ + goto do_normal; \ + } \ + if (val < from || val > to) \ + return NULL; \ + } \ + else \ + { \ + do_normal: \ + get_number (from, to, n); \ + } \ + 0; \ + }) +#else +# define get_alt_number(from, to, n) \ + /* We don't have the alternate representation. */ \ + get_number(from, to, n) +#endif +#define recursive(new_fmt) \ + (*(new_fmt) != '\0' \ + && (rp = __strptime_internal (rp, (new_fmt), tm, \ + decided, era_cnt LOCALE_ARG)) != NULL) + + +#ifdef _LIBC +/* This is defined in locale/C-time.c in the GNU libc. */ +extern const struct locale_data _nl_C_LC_TIME attribute_hidden; + +# define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string) +# define ab_weekday_name \ + (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string) +# define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string) +# define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string) +# define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string) +# define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string) +# define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string) +# define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string) +# define HERE_T_FMT_AMPM \ + (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string) +# define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string) + +# define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n) +#else +static char const weekday_name[][10] = + { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" + }; +static char const ab_weekday_name[][4] = + { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; +static char const month_name[][10] = + { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + }; +static char const ab_month_name[][4] = + { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; +# define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y" +# define HERE_D_FMT "%m/%d/%y" +# define HERE_AM_STR "AM" +# define HERE_PM_STR "PM" +# define HERE_T_FMT_AMPM "%I:%M:%S %p" +# define HERE_T_FMT "%H:%M:%S" + +static const unsigned short int __mon_yday[2][13] = + { + /* Normal years. */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years. */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } + }; +#endif + +#if defined _LIBC +/* 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. */ +# define strptime __strptime_l +# undef _NL_CURRENT +# define _NL_CURRENT(category, item) \ + (current->values[_NL_ITEM_INDEX (item)].string) +# undef _NL_CURRENT_WORD +# define _NL_CURRENT_WORD(category, item) \ + (current->values[_NL_ITEM_INDEX (item)].word) +# define LOCALE_PARAM , locale +# define LOCALE_ARG , locale +# define LOCALE_PARAM_PROTO , __locale_t locale +# define LOCALE_PARAM_DECL __locale_t locale; +# define HELPER_LOCALE_ARG , current +# define ISSPACE(Ch) __isspace_l (Ch, locale) +#else +# define LOCALE_PARAM +# define LOCALE_ARG +# define LOCALE_PARAM_DECL +# define LOCALE_PARAM_PROTO +# define HELPER_LOCALE_ARG +# define ISSPACE(Ch) isspace (Ch) +#endif + + + + +#ifndef __isleap +/* Nonzero if YEAR is a leap year (every 4 years, + except every 100th isn't, and every 400th is). */ +# define __isleap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) +#endif + +/* Compute the day of the week. */ +static void +day_of_the_week (struct tm *tm) +{ + /* We know that January 1st 1970 was a Thursday (= 4). Compute the + the difference between this data in the one on TM and so determine + the weekday. */ + int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2); + int wday = (-473 + + (365 * (tm->tm_year - 70)) + + (corr_year / 4) + - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0) + + (((corr_year / 4) / 25) / 4) + + __mon_yday[0][tm->tm_mon] + + tm->tm_mday - 1); + tm->tm_wday = ((wday % 7) + 7) % 7; +} + +/* Compute the day of the year. */ +static void +day_of_the_year (struct tm *tm) +{ + tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon] + + (tm->tm_mday - 1)); +} + + +#ifdef _LIBC +char * +internal_function +#else +static char * +#endif +__strptime_internal (rp, fmt, tm, decided, era_cnt LOCALE_PARAM) + const char *rp; + const char *fmt; + struct tm *tm; + enum ptime_locale_status *decided; + int era_cnt; + LOCALE_PARAM_DECL +{ +#ifdef _LIBC + struct locale_data *const current = locale->__locales[LC_TIME]; +#endif + + const char *rp_backup; + int cnt; + size_t val; + int have_I, is_pm; + int century, want_century; + int want_era; + int have_wday, want_xday; + int have_yday; + int have_mon, have_mday; + int have_uweek, have_wweek; + int week_no; + size_t num_eras; + struct era_entry *era; + + have_I = is_pm = 0; + century = -1; + want_century = 0; + want_era = 0; + era = NULL; + week_no = 0; + + have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0; + have_wweek = 0; + + while (*fmt != '\0') + { + /* A white space in the format string matches 0 more or white + space in the input string. */ + if (ISSPACE (*fmt)) + { + while (ISSPACE (*rp)) + ++rp; + ++fmt; + continue; + } + + /* Any character but `%' must be matched by the same character + in the iput string. */ + if (*fmt != '%') + { + match_char (*fmt++, *rp++); + continue; + } + + ++fmt; +#ifndef _NL_CURRENT + /* We need this for handling the `E' modifier. */ + start_over: +#endif + + /* Make back up of current processing pointer. */ + rp_backup = rp; + + switch (*fmt++) + { + case '%': + /* Match the `%' character itself. */ + match_char ('%', *rp++); + break; + case 'a': + case 'A': + /* Match day of week. */ + for (cnt = 0; cnt < 7; ++cnt) + { +#ifdef _NL_CURRENT + if (*decided !=raw) + { + if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp)) + { + if (*decided == not + && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt), + weekday_name[cnt])) + *decided = loc; + break; + } + if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp)) + { + if (*decided == not + && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), + ab_weekday_name[cnt])) + *decided = loc; + break; + } + } +#endif + if (*decided != loc + && (match_string (weekday_name[cnt], rp) + || match_string (ab_weekday_name[cnt], rp))) + { + *decided = raw; + break; + } + } + if (cnt == 7) + /* Does not match a weekday name. */ + return NULL; + tm->tm_wday = cnt; + have_wday = 1; + break; + case 'b': + case 'B': + case 'h': + /* Match month name. */ + for (cnt = 0; cnt < 12; ++cnt) + { +#ifdef _NL_CURRENT + if (*decided !=raw) + { + if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp)) + { + if (*decided == not + && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt), + month_name[cnt])) + *decided = loc; + break; + } + if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp)) + { + if (*decided == not + && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), + ab_month_name[cnt])) + *decided = loc; + break; + } + } +#endif + if (match_string (month_name[cnt], rp) + || match_string (ab_month_name[cnt], rp)) + { + *decided = raw; + break; + } + } + if (cnt == 12) + /* Does not match a month name. */ + return NULL; + tm->tm_mon = cnt; + want_xday = 1; + break; + case 'c': + /* Match locale's date and time format. */ +#ifdef _NL_CURRENT + if (*decided != raw) + { + if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT))) + { + if (*decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (*decided == not && + strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT)) + *decided = loc; + want_xday = 1; + break; + } + *decided = raw; + } +#endif + if (!recursive (HERE_D_T_FMT)) + return NULL; + want_xday = 1; + break; + case 'C': + /* Match century number. */ + match_century: + get_number (0, 99, 2); + century = val; + want_xday = 1; + break; + case 'd': + case 'e': + /* Match day of month. */ + get_number (1, 31, 2); + tm->tm_mday = val; + have_mday = 1; + want_xday = 1; + break; + case 'F': + if (!recursive ("%Y-%m-%d")) + return NULL; + want_xday = 1; + break; + case 'x': +#ifdef _NL_CURRENT + if (*decided != raw) + { + if (!recursive (_NL_CURRENT (LC_TIME, D_FMT))) + { + if (*decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (*decided == not + && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT)) + *decided = loc; + want_xday = 1; + break; + } + *decided = raw; + } +#endif + /* Fall through. */ + case 'D': + /* Match standard day format. */ + if (!recursive (HERE_D_FMT)) + return NULL; + want_xday = 1; + break; + case 'k': + case 'H': + /* Match hour in 24-hour clock. */ + get_number (0, 23, 2); + tm->tm_hour = val; + have_I = 0; + break; + case 'l': + /* Match hour in 12-hour clock. GNU extension. */ + case 'I': + /* Match hour in 12-hour clock. */ + get_number (1, 12, 2); + tm->tm_hour = val % 12; + have_I = 1; + break; + case 'j': + /* Match day number of year. */ + get_number (1, 366, 3); + tm->tm_yday = val - 1; + have_yday = 1; + break; + case 'm': + /* Match number of month. */ + get_number (1, 12, 2); + tm->tm_mon = val - 1; + have_mon = 1; + want_xday = 1; + break; + case 'M': + /* Match minute. */ + get_number (0, 59, 2); + tm->tm_min = val; + break; + case 'n': + case 't': + /* Match any white space. */ + while (ISSPACE (*rp)) + ++rp; + break; + case 'p': + /* Match locale's equivalent of AM/PM. */ +#ifdef _NL_CURRENT + if (*decided != raw) + { + if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp)) + { + if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR)) + *decided = loc; + break; + } + if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp)) + { + if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR)) + *decided = loc; + is_pm = 1; + break; + } + *decided = raw; + } +#endif + if (!match_string (HERE_AM_STR, rp)) + if (match_string (HERE_PM_STR, rp)) + is_pm = 1; + else + return NULL; + break; + case 'r': +#ifdef _NL_CURRENT + if (*decided != raw) + { + if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM))) + { + if (*decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (*decided == not && + strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM), + HERE_T_FMT_AMPM)) + *decided = loc; + break; + } + *decided = raw; + } +#endif + if (!recursive (HERE_T_FMT_AMPM)) + return NULL; + break; + case 'R': + if (!recursive ("%H:%M")) + return NULL; + break; + case 's': + { + /* The number of seconds may be very high so we cannot use + the `get_number' macro. Instead read the number + character for character and construct the result while + doing this. */ + time_t secs = 0; + if (*rp < '0' || *rp > '9') + /* We need at least one digit. */ + return NULL; + + do + { + secs *= 10; + secs += *rp++ - '0'; + } + while (*rp >= '0' && *rp <= '9'); + + if (localtime_r (&secs, tm) == NULL) + /* Error in function. */ + return NULL; + } + break; + case 'S': + get_number (0, 61, 2); + tm->tm_sec = val; + break; + case 'X': +#ifdef _NL_CURRENT + if (*decided != raw) + { + if (!recursive (_NL_CURRENT (LC_TIME, T_FMT))) + { + if (*decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT)) + *decided = loc; + break; + } + *decided = raw; + } +#endif + /* Fall through. */ + case 'T': + if (!recursive (HERE_T_FMT)) + return NULL; + break; + case 'u': + get_number (1, 7, 1); + tm->tm_wday = val % 7; + have_wday = 1; + break; + case 'g': + get_number (0, 99, 2); + /* XXX This cannot determine any field in TM. */ + break; + case 'G': + if (*rp < '0' || *rp > '9') + return NULL; + /* XXX Ignore the number since we would need some more + information to compute a real date. */ + do + ++rp; + while (*rp >= '0' && *rp <= '9'); + break; + case 'U': + get_number (0, 53, 2); + week_no = val; + have_uweek = 1; + break; + case 'W': + get_number (0, 53, 2); + week_no = val; + have_wweek = 1; + break; + case 'V': + get_number (0, 53, 2); + /* XXX This cannot determine any field in TM without some + information. */ + break; + case 'w': + /* Match number of weekday. */ + get_number (0, 6, 1); + tm->tm_wday = val; + have_wday = 1; + break; + case 'y': + match_year_in_century: + /* Match year within century. */ + get_number (0, 99, 2); + /* The "Year 2000: The Millennium Rollover" paper suggests that + values in the range 69-99 refer to the twentieth century. */ + tm->tm_year = val >= 69 ? val : val + 100; + /* Indicate that we want to use the century, if specified. */ + want_century = 1; + want_xday = 1; + break; + case 'Y': + /* Match year including century number. */ + get_number (0, 9999, 4); + tm->tm_year = val - 1900; + want_century = 0; + want_xday = 1; + break; + case 'Z': + /* XXX How to handle this? */ + break; + case 'E': +#ifdef _NL_CURRENT + switch (*fmt++) + { + case 'c': + /* Match locale's alternate date and time format. */ + if (*decided != raw) + { + const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT); + + if (*fmt == '\0') + fmt = _NL_CURRENT (LC_TIME, D_T_FMT); + + if (!recursive (fmt)) + { + if (*decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (strcmp (fmt, HERE_D_T_FMT)) + *decided = loc; + want_xday = 1; + break; + } + *decided = raw; + } + /* The C locale has no era information, so use the + normal representation. */ + if (!recursive (HERE_D_T_FMT)) + return NULL; + want_xday = 1; + break; + case 'C': + if (*decided != raw) + { + if (era_cnt >= 0) + { + era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG); + if (era != NULL && match_string (era->era_name, rp)) + { + *decided = loc; + break; + } + else + return NULL; + } + else + { + num_eras = _NL_CURRENT_WORD (LC_TIME, + _NL_TIME_ERA_NUM_ENTRIES); + for (era_cnt = 0; era_cnt < (int) num_eras; + ++era_cnt, rp = rp_backup) + { + era = _nl_select_era_entry (era_cnt + HELPER_LOCALE_ARG); + if (era != NULL && match_string (era->era_name, rp)) + { + *decided = loc; + break; + } + } + if (era_cnt == (int) num_eras) + { + era_cnt = -1; + if (*decided == loc) + return NULL; + } + else + break; + } + + *decided = raw; + } + /* The C locale has no era information, so use the + normal representation. */ + goto match_century; + case 'y': + if (*decided == raw) + goto match_year_in_century; + + get_number(0, 9999, 4); + tm->tm_year = val; + want_era = 1; + want_xday = 1; + want_century = 1; + break; + case 'Y': + if (*decided != raw) + { + num_eras = _NL_CURRENT_WORD (LC_TIME, + _NL_TIME_ERA_NUM_ENTRIES); + for (era_cnt = 0; era_cnt < (int) num_eras; + ++era_cnt, rp = rp_backup) + { + era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG); + if (era != NULL && recursive (era->era_format)) + break; + } + if (era_cnt == (int) num_eras) + { + era_cnt = -1; + if (*decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + *decided = loc; + era_cnt = -1; + break; + } + + *decided = raw; + } + get_number (0, 9999, 4); + tm->tm_year = val - 1900; + want_century = 0; + want_xday = 1; + break; + case 'x': + if (*decided != raw) + { + const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT); + + if (*fmt == '\0') + fmt = _NL_CURRENT (LC_TIME, D_FMT); + + if (!recursive (fmt)) + { + if (*decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (strcmp (fmt, HERE_D_FMT)) + *decided = loc; + break; + } + *decided = raw; + } + if (!recursive (HERE_D_FMT)) + return NULL; + break; + case 'X': + if (*decided != raw) + { + const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT); + + if (*fmt == '\0') + fmt = _NL_CURRENT (LC_TIME, T_FMT); + + if (!recursive (fmt)) + { + if (*decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (strcmp (fmt, HERE_T_FMT)) + *decided = loc; + break; + } + *decided = raw; + } + if (!recursive (HERE_T_FMT)) + return NULL; + break; + default: + return NULL; + } + break; +#else + /* We have no information about the era format. Just use + the normal format. */ + if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y' + && *fmt != 'x' && *fmt != 'X') + /* This is an illegal format. */ + return NULL; + + goto start_over; +#endif + case 'O': + switch (*fmt++) + { + case 'd': + case 'e': + /* Match day of month using alternate numeric symbols. */ + get_alt_number (1, 31, 2); + tm->tm_mday = val; + have_mday = 1; + want_xday = 1; + break; + case 'H': + /* Match hour in 24-hour clock using alternate numeric + symbols. */ + get_alt_number (0, 23, 2); + tm->tm_hour = val; + have_I = 0; + break; + case 'I': + /* Match hour in 12-hour clock using alternate numeric + symbols. */ + get_alt_number (1, 12, 2); + tm->tm_hour = val % 12; + have_I = 1; + break; + case 'm': + /* Match month using alternate numeric symbols. */ + get_alt_number (1, 12, 2); + tm->tm_mon = val - 1; + have_mon = 1; + want_xday = 1; + break; + case 'M': + /* Match minutes using alternate numeric symbols. */ + get_alt_number (0, 59, 2); + tm->tm_min = val; + break; + case 'S': + /* Match seconds using alternate numeric symbols. */ + get_alt_number (0, 61, 2); + tm->tm_sec = val; + break; + case 'U': + get_alt_number (0, 53, 2); + week_no = val; + have_uweek = 1; + break; + case 'W': + get_alt_number (0, 53, 2); + week_no = val; + have_wweek = 1; + break; + case 'V': + get_alt_number (0, 53, 2); + /* XXX This cannot determine any field in TM without + further information. */ + break; + case 'w': + /* Match number of weekday using alternate numeric symbols. */ + get_alt_number (0, 6, 1); + tm->tm_wday = val; + have_wday = 1; + break; + case 'y': + /* Match year within century using alternate numeric symbols. */ + get_alt_number (0, 99, 2); + tm->tm_year = val >= 69 ? val : val + 100; + want_xday = 1; + break; + default: + return NULL; + } + break; + default: + return NULL; + } + } + + if (have_I && is_pm) + tm->tm_hour += 12; + + if (century != -1) + { + if (want_century) + tm->tm_year = tm->tm_year % 100 + (century - 19) * 100; + else + /* Only the century, but not the year. Strange, but so be it. */ + tm->tm_year = (century - 19) * 100; + } + + if (era_cnt != -1) + { + era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG); + if (era == NULL) + return NULL; + if (want_era) + tm->tm_year = (era->start_date[0] + + ((tm->tm_year - era->offset) + * era->absolute_direction)); + else + /* Era start year assumed. */ + tm->tm_year = era->start_date[0]; + } + else + if (want_era) + { + /* No era found but we have seen an E modifier. Rectify some + values. */ + if (want_century && century == -1 && tm->tm_year < 69) + tm->tm_year += 100; + } + + if (want_xday && !have_wday) + { + if ( !(have_mon && have_mday) && have_yday) + { + /* We don't have tm_mon and/or tm_mday, compute them. */ + int t_mon = 0; + while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday) + t_mon++; + if (!have_mon) + tm->tm_mon = t_mon - 1; + if (!have_mday) + tm->tm_mday = + (tm->tm_yday + - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); + } + day_of_the_week (tm); + } + + if (want_xday && !have_yday) + day_of_the_year (tm); + + if ((have_uweek || have_wweek) && have_wday) + { + int save_wday = tm->tm_wday; + int save_mday = tm->tm_mday; + int save_mon = tm->tm_mon; + int w_offset = have_uweek ? 0 : 1; + + tm->tm_mday = 1; + tm->tm_mon = 0; + day_of_the_week (tm); + if (have_mday) + tm->tm_mday = save_mday; + if (have_mon) + tm->tm_mon = save_mon; + + if (!have_yday) + tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7 + + (week_no - 1) *7 + + save_wday - w_offset); + + if (!have_mday || !have_mon) + { + int t_mon = 0; + while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] + <= tm->tm_yday) + t_mon++; + if (!have_mon) + tm->tm_mon = t_mon - 1; + if (!have_mday) + tm->tm_mday = + (tm->tm_yday + - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); + } + + tm->tm_wday = save_wday; + } + + return (char *) rp; +} + + +char * +strptime (buf, format, tm LOCALE_PARAM) + const char *buf; + const char *format; + struct tm *tm; + LOCALE_PARAM_DECL +{ + enum ptime_locale_status decided; + +#ifdef _NL_CURRENT + decided = not; +#else + decided = raw; +#endif + return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG); +} + +#ifdef _LIBC weak_alias (__strptime_l, strptime_l) +#endif diff --git a/time/wcsftime.c b/time/wcsftime.c index 17bb53ede6..dcda6be18e 100644 --- a/time/wcsftime.c +++ b/time/wcsftime.c @@ -1,4 +1,30 @@ -#include <wctype.h> +/* Copyright (C) 1991-1999, 2000, 2001, 2002, 2003, 2004 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + #include <wchar.h> -#define COMPILE_WIDE 1 -#include "strftime.c" +#include <locale/localeinfo.h> + + +size_t +wcsftime (wchar_t *s, size_t maxsize, const wchar_t *format, + const struct tm *tp) +{ + return __wcsftime_l (s, maxsize, format, tp, _NL_CURRENT_LOCALE); +} +libc_hidden_def (wcsftime) diff --git a/time/wcsftime_l.c b/time/wcsftime_l.c index 969289710c..e9443ef64e 100644 --- a/time/wcsftime_l.c +++ b/time/wcsftime_l.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002 Free Software Foundation, Inc. +/* Copyright (C) 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -16,7 +16,11 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#include <wchar.h> +#include <wctype.h> + #define USE_IN_EXTENDED_LOCALE_MODEL 1 -#include <wcsftime.c> +#define COMPILE_WIDE 1 +#include "strftime_l.c" weak_alias (__wcsftime_l, wcsftime_l) diff --git a/wcsmbs/wcscoll.c b/wcsmbs/wcscoll.c index 40bd584e8b..ed6db06e4e 100644 --- a/wcsmbs/wcscoll.c +++ b/wcsmbs/wcscoll.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1996, 1997, 1999, 2000, 2001 Free Software Foundation, Inc. +/* Copyright (C) 1996,1997,1999,2000,2001,2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. @@ -22,17 +22,8 @@ #define STRING_TYPE wchar_t #define USTRING_TYPE wint_t -#ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define STRCOLL __wcscoll_l -#else -# define STRCOLL __wcscoll -#endif -#define STRCMP wcscmp -#define STRLEN __wcslen -#define WEIGHT_H "../locale/weightwc.h" -#define SUFFIX WC -#define L(arg) L##arg -#define WIDE_CHAR_VERSION 1 +#define STRCOLL __wcscoll +#define STRCOLL_L __wcscoll_l #include "../string/strcoll.c" diff --git a/wcsmbs/wcscoll_l.c b/wcsmbs/wcscoll_l.c index 20007b29c9..04b0bf3649 100644 --- a/wcsmbs/wcscoll_l.c +++ b/wcsmbs/wcscoll_l.c @@ -1,4 +1,4 @@ -/* 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 <drepper@gnu.ai.mit.edu>, 1996. @@ -17,7 +17,20 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define USE_IN_EXTENDED_LOCALE_MODEL 1 -#include <wcscoll.c> + +#include <wchar.h> +#include "../locale/coll-lookup.h" + +#define STRING_TYPE wchar_t +#define USTRING_TYPE wint_t +#define STRCOLL __wcscoll_l +#define STRCMP wcscmp +#define STRLEN __wcslen +#define WEIGHT_H "../locale/weightwc.h" +#define SUFFIX WC +#define L(arg) L##arg +#define WIDE_CHAR_VERSION 1 + +#include "../string/strcoll_l.c" weak_alias (__wcscoll_l, wcscoll_l) diff --git a/wcsmbs/wcstod.c b/wcsmbs/wcstod.c index 5a39091c25..74fd557db6 100644 --- a/wcsmbs/wcstod.c +++ b/wcsmbs/wcstod.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1996, 1997 Free Software Foundation, Inc. +/* Copyright (C) 1996, 1997, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1996. @@ -17,9 +17,13 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#include <stddef.h> +#include <xlocale.h> -/* The actual implementation for all floating point sizes is in strtod.c. */ #define USE_WIDE_CHAR 1 +extern double ____wcstod_l_internal (const wchar_t *, wchar_t **, int, + __locale_t); + #include <stdlib/strtod.c> diff --git a/wcsmbs/wcstod_l.c b/wcsmbs/wcstod_l.c index 87f3a8f7bc..86ec18e6d5 100644 --- a/wcsmbs/wcstod_l.c +++ b/wcsmbs/wcstod_l.c @@ -1,5 +1,5 @@ /* Convert string representing a number to integer value, using given locale. - Copyright (C) 1997, 1998, 2002 Free Software Foundation, Inc. + Copyright (C) 1997, 1998, 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -18,11 +18,9 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define __need_wchar_t #include <stddef.h> -#include <locale.h> +#include <xlocale.h> -#define USE_IN_EXTENDED_LOCALE_MODEL 1 extern double ____wcstod_l_internal (const wchar_t *, wchar_t **, int, __locale_t); @@ -30,6 +28,6 @@ extern unsigned long long int ____wcstoull_l_internal (const wchar_t *, wchar_t **, int, int, __locale_t); -#include <wcstod.c> +#define USE_WIDE_CHAR 1 -weak_alias (__wcstod_l, wcstod_l) +#include <stdlib/strtod_l.c> diff --git a/wcsmbs/wcstof.c b/wcsmbs/wcstof.c index 02f91ef904..2d2fca3fde 100644 --- a/wcsmbs/wcstof.c +++ b/wcsmbs/wcstof.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1996, 1997 Free Software Foundation, Inc. +/* Copyright (C) 1996, 1997, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1996. @@ -17,27 +17,12 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#include <stddef.h> +#include <xlocale.h> -/* The actual implementation for all floating point sizes is in strtod.c. - These macros tell it to produce the `float' version, `wcstof'. */ - -#define FLOAT float -#define FLT FLT -#ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define STRTOF __wcstof_l -#else -# define STRTOF wcstof -#endif -#define MPN2FLOAT __mpn_construct_float -#define FLOAT_HUGE_VAL HUGE_VALF #define USE_WIDE_CHAR 1 -#define SET_MANTISSA(flt, mant) \ - do { union ieee754_float u; \ - u.f = (flt); \ - if ((mant & 0x7fffff) == 0) \ - mant = 0x400000; \ - u.ieee.mantissa = (mant) & 0x7fffff; \ - (flt) = u.f; \ - } while (0) -#include <stdlib/strtod.c> +extern float ____wcstof_l_internal (const wchar_t *, wchar_t **, int, + __locale_t); + +#include <stdlib/strtof.c> diff --git a/wcsmbs/wcstof_l.c b/wcsmbs/wcstof_l.c index 542961e1fe..0ed31e0c7b 100644 --- a/wcsmbs/wcstof_l.c +++ b/wcsmbs/wcstof_l.c @@ -1,5 +1,5 @@ /* Convert string representing a number to integer value, using given locale. - Copyright (C) 1997,98,2002 Free Software Foundation, Inc. + Copyright (C) 1997,98,2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -18,11 +18,11 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define __need_wchar_t #include <stddef.h> -#include <locale.h> +#include <xlocale.h> -#define USE_IN_EXTENDED_LOCALE_MODEL 1 + +#define USE_WIDE_CHAR 1 extern float ____wcstof_l_internal (const wchar_t *, wchar_t **, int, __locale_t); @@ -30,6 +30,4 @@ extern unsigned long long int ____wcstoull_l_internal (const wchar_t *, wchar_t **, int, int, __locale_t); -#include <wcstof.c> - -weak_alias (__wcstof_l, wcstof_l) +#include <stdlib/strtof_l.c> diff --git a/wcsmbs/wcstold.c b/wcsmbs/wcstold.c index 5c58aa69e6..d99b7278b9 100644 --- a/wcsmbs/wcstold.c +++ b/wcsmbs/wcstold.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. +/* Copyright (C) 1996, 1997, 1998, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1996. @@ -17,46 +17,12 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include <math.h> -#include <wchar.h> +#include <stddef.h> +#include <xlocale.h> -#ifndef __NO_LONG_DOUBLE_MATH -/* The actual implementation for all floating point sizes is in strtod.c. - These macros tell it to produce the `long double' version, `wcstold'. */ +#define USE_WIDE_CHAR 1 -# define FLOAT long double -# define FLT LDBL -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define STRTOF __wcstold_l -# else -# define STRTOF wcstold -# endif -# define MPN2FLOAT __mpn_construct_long_double -# define FLOAT_HUGE_VAL HUGE_VALL -# define USE_WIDE_CHAR 1 -# define SET_MANTISSA(flt, mant) \ - do { union ieee854_long_double u; \ - u.d = (flt); \ - if ((mant & 0x7fffffffffffffffULL) == 0) \ - mant = 0x4000000000000000ULL; \ - u.ieee.mantissa0 = (((mant) >> 32) & 0x7fffffff) | 0x80000000; \ - u.ieee.mantissa1 = (mant) & 0xffffffff; \ - (flt) = u.d; \ - } while (0) +extern long double ____wcstold_l_internal (const wchar_t *, wchar_t **, int, + __locale_t); -# include <stdlib/strtod.c> -#else -/* There is no `long double' type, use the `double' implementations. */ -long double -__wcstold_internal (const wchar_t *nptr, wchar_t **endptr, int group) -{ - return __wcstod_internal (nptr, endptr, group); -} -libc_hidden_def (__wcstold_internal) - -long double -wcstold (const wchar_t *nptr, wchar_t **endptr) -{ - return __wcstod_internal (nptr, endptr, 0); -} -#endif +#include <stdlib/strtold.c> diff --git a/wcsmbs/wcstold_l.c b/wcsmbs/wcstold_l.c index 91a92124cf..9526645f10 100644 --- a/wcsmbs/wcstold_l.c +++ b/wcsmbs/wcstold_l.c @@ -1,5 +1,5 @@ /* Convert string representing a number to integer value, using given locale. - Copyright (C) 1997,98,99,2002 Free Software Foundation, Inc. + Copyright (C) 1997,98,99,2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -18,15 +18,10 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define __need_wchar_t -#include <math.h> #include <stddef.h> -#include <locale.h> -#include <wchar.h> +#include <xlocale.h> -#define USE_IN_EXTENDED_LOCALE_MODEL 1 - -#ifndef __NO_LONG_DOUBLE_MATH +#define USE_WIDE_CHAR 1 extern long double ____wcstold_l_internal (const wchar_t *, wchar_t **, int, __locale_t); @@ -34,23 +29,4 @@ extern unsigned long long int ____wcstoull_l_internal (const wchar_t *, wchar_t **, int, int, __locale_t); -# include <wcstold.c> -#else -/* There is no `long double' type, use the `double' implementations. */ -extern double ____wcstod_l_internal (const wchar_t *, wchar_t **, int, - __locale_t); -long double -____wcstold_l_internal (const wchar_t *nptr, wchar_t **endptr, int group, - __locale_t loc) -{ - return ____wcstod_l_internal (nptr, endptr, group, loc); -} - -long double -__wcstold_l (const wchar_t *nptr, wchar_t **endptr, __locale_t loc) -{ - return ____wcstod_l_internal (nptr, endptr, 0, loc); -} -#endif - -weak_alias (__wcstold_l, wcstold_l) +#include <strtold_l.c> diff --git a/wcsmbs/wcsxfrm.c b/wcsmbs/wcsxfrm.c index 10f3d72673..9e7d10389b 100644 --- a/wcsmbs/wcsxfrm.c +++ b/wcsmbs/wcsxfrm.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. +/* Copyright (C) 1996-2000, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. @@ -21,18 +21,7 @@ #include "../locale/coll-lookup.h" #define STRING_TYPE wchar_t -#define USTRING_TYPE wint_t -#ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define STRXFRM __wcsxfrm_l -#else -# define STRXFRM wcsxfrm -#endif -#define STRCMP wcscmp -#define STRLEN __wcslen -#define STPNCPY __wcpncpy -#define WEIGHT_H "../locale/weightwc.h" -#define SUFFIX WC -#define L(arg) L##arg -#define WIDE_CHAR_VERSION 1 +#define STRXFRM wcsxfrm +#define STRXFRM_L __wcsxfrm_l #include "../string/strxfrm.c" diff --git a/wcsmbs/wcsxfrm_l.c b/wcsmbs/wcsxfrm_l.c index 13046237a9..de9fc93153 100644 --- a/wcsmbs/wcsxfrm_l.c +++ b/wcsmbs/wcsxfrm_l.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1996,97,2002 Free Software Foundation, Inc. +/* Copyright (C) 1996,97,2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1996. @@ -17,7 +17,20 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define USE_IN_EXTENDED_LOCALE_MODEL 1 -#include <wcsxfrm.c> +#include <wchar.h> +#include "../locale/coll-lookup.h" + +#define STRING_TYPE wchar_t +#define USTRING_TYPE wint_t +#define STRXFRM __wcsxfrm_l +#define STRCMP wcscmp +#define STRLEN __wcslen +#define STPNCPY __wcpncpy +#define WEIGHT_H "../locale/weightwc.h" +#define SUFFIX WC +#define L(arg) L##arg +#define WIDE_CHAR_VERSION 1 + +#include "../string/strxfrm_l.c" weak_alias (__wcsxfrm_l, wcsxfrm_l) |