summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2007-02-19 04:40:58 +0000
committerUlrich Drepper <drepper@redhat.com>2007-02-19 04:40:58 +0000
commitecb72bd5b640497474ceae8d5e991b88c0c032e2 (patch)
tree97a7c41d486c34139416fb2b225854b0d074992d
parentac2ca0229ab39381e9e92e07f929703981f77a90 (diff)
[BZ #2211]
* stdio-common/vfscanf.c: Handle localized digits etc for floating point numbers. Patch mostly by Hamed Malek <hamed@farsiweb.info>.
-rw-r--r--ChangeLog5
-rw-r--r--localedata/ChangeLog4
-rw-r--r--localedata/Makefile6
-rw-r--r--localedata/tst-sscanf.c56
-rw-r--r--stdio-common/vfscanf.c242
5 files changed, 296 insertions, 17 deletions
diff --git a/ChangeLog b/ChangeLog
index d898823f51..be2556b4f2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
2007-02-18 Ulrich Drepper <drepper@redhat.com>
+ [BZ #2211]
+ * stdio-common/vfscanf.c: Handle localized digits etc for floating
+ point numbers.
+ Patch mostly by Hamed Malek <hamed@farsiweb.info>.
+
* stdio-common/vfscanf.c: Fix problems in width accounting.
* stdio-common/tst-sscanf.c (double_tests): New tests.
(main): Hook them up.
diff --git a/localedata/ChangeLog b/localedata/ChangeLog
index 9dfe469503..e1bc145ec3 100644
--- a/localedata/ChangeLog
+++ b/localedata/ChangeLog
@@ -1,5 +1,9 @@
2007-02-18 Ulrich Drepper <drepper@redhat.com>
+ * Makefile (tests): Add tst-sscanf.
+ (LOCALES): Add fa_IR.UTF-8.
+ * tst-sscanf.c: New file.
+
* da_DK.in: Adjust for unified collation.
* locales/vi_VN: Don't define HOK here as well.
diff --git a/localedata/Makefile b/localedata/Makefile
index db7094fabe..e518175b98 100644
--- a/localedata/Makefile
+++ b/localedata/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 1996-2002, 2003, 2005 Free Software Foundation, Inc.
+# Copyright (C) 1996-2002, 2003, 2005, 2007 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
@@ -93,7 +93,7 @@ locale_test_suite := tst_iswalnum tst_iswalpha tst_iswcntrl \
tests = $(locale_test_suite) tst-digits tst-setlocale bug-iconv-trans \
tst-leaks tst-mbswcs6 tst-xlocale1 tst-xlocale2 bug-usesetlocale \
- tst-strfmon1
+ tst-strfmon1 tst-sscanf
ifeq (yes,$(build-shared))
ifneq (no,$(PERL))
tests: $(objpfx)mtrace-tst-leaks
@@ -133,7 +133,7 @@ LOCALES := de_DE.ISO-8859-1 de_DE.UTF-8 en_US.ANSI_X3.4-1968 \
en_US.ISO-8859-1 ja_JP.EUC-JP da_DK.ISO-8859-1 \
hr_HR.ISO-8859-2 sv_SE.ISO-8859-1 ja_JP.SJIS fr_FR.ISO-8859-1 \
vi_VN.TCVN5712-1 nb_NO.ISO-8859-1 nn_NO.ISO-8859-1 \
- tr_TR.UTF-8 cs_CZ.UTF-8 zh_TW.EUC-TW
+ tr_TR.UTF-8 cs_CZ.UTF-8 zh_TW.EUC-TW fa_IR.UTF-8
LOCALE_SRCS := $(shell echo "$(LOCALES)"|sed 's/\([^ .]*\)[^ ]*/\1/g')
CHARMAPS := $(shell echo "$(LOCALES)" | \
sed -e 's/[^ .]*[.]\([^ ]*\)/\1/g' -e s/SJIS/SHIFT_JIS/g)
diff --git a/localedata/tst-sscanf.c b/localedata/tst-sscanf.c
new file mode 100644
index 0000000000..89a77a14f8
--- /dev/null
+++ b/localedata/tst-sscanf.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <locale.h>
+#include <assert.h>
+
+#define P0 "\xDB\xB0"
+#define P1 "\xDB\xB1"
+#define P2 "\xDB\xB2"
+#define P3 "\xDB\xB3"
+#define P4 "\xDB\xB4"
+#define P5 "\xDB\xB5"
+#define P6 "\xDB\xB6"
+#define P7 "\xDB\xB7"
+#define P8 "\xDB\xB8"
+#define P9 "\xDB\xB9"
+#define PD "\xd9\xab"
+#define PT "\xd9\xac"
+
+static int
+check_sscanf (const char *s, const char *format, const float n)
+{
+ float f;
+
+ if (sscanf (s, format, &f) != 1)
+ {
+ printf ("nothing found for \"%s\"\n", s);
+ return 1;
+ }
+ if (f != n)
+ {
+ printf ("got %f expected %f from \"%s\"\n", f, n, s);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+do_test (void)
+{
+ if (setlocale (LC_ALL, "fa_IR") == NULL)
+ {
+ puts ("cannot set fa_IR locale");
+ return 1;
+ }
+
+ int r = check_sscanf (P3 PD P1 P4, "%I8f", 3.14);
+ r |= check_sscanf (P3 PT P1 P4 P5, "%I'f", 3145);
+ r |= check_sscanf (P3 PD P1 P4 P1 P5 P9, "%If", 3.14159);
+ r |= check_sscanf ("-" P3 PD P1 P4 P1 P5, "%If", -3.1415);
+ r |= check_sscanf ("+" PD P1 P4 P1 P5, "%If", +.1415);
+ r |= check_sscanf (P3 PD P1 P4 P1 P5 "e+" P2, "%Ie", 3.1415e+2);
+
+ return r;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c
index 90e7e36a5f..cdb610dc0c 100644
--- a/stdio-common/vfscanf.c
+++ b/stdio-common/vfscanf.c
@@ -1,5 +1,4 @@
-/* Copyright (C) 1991-2002, 2003, 2004, 2005, 2006, 2007
- Free Software Foundation, Inc.
+/* Copyright (C) 1991-2006, 2007 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
@@ -1264,13 +1263,13 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
mbdigits[n] = strchr (mbdigits[n], '\0') + 1;
cmpp = mbdigits[n];
- while ((unsigned char) *cmpp == c && avail > 0)
+ while ((unsigned char) *cmpp == c && avail >= 0)
{
if (*++cmpp == '\0')
break;
else
{
- if ((c = inchar ()) == EOF)
+ if (avail == 0 || inchar () == EOF)
break;
--avail;
}
@@ -1317,13 +1316,13 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
int avail = width > 0 ? width : INT_MAX;
cmpp = mbdigits[n];
- while ((unsigned char) *cmpp == c && avail > 0)
+ while ((unsigned char) *cmpp == c && avail >= 0)
{
if (*++cmpp == '\0')
break;
else
{
- if ((c = inchar ()) == EOF)
+ if (avail == 0 || inchar () == EOF)
break;
--avail;
}
@@ -1378,14 +1377,14 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
const char *cmpp = thousands;
int avail = width > 0 ? width : INT_MAX;
- while ((unsigned char) *cmpp == c && avail > 0)
+ while ((unsigned char) *cmpp == c && avail >= 0)
{
ADDW (c);
if (*++cmpp == '\0')
break;
else
{
- if ((c = inchar ()) == EOF)
+ if (avail == 0 || inchar () == EOF)
break;
--avail;
}
@@ -1450,14 +1449,14 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
const char *cmpp = thousands;
int avail = width > 0 ? width : INT_MAX;
- while ((unsigned char) *cmpp == c && avail > 0)
+ while ((unsigned char) *cmpp == c && avail >= 0)
{
ADDW (c);
if (*++cmpp == '\0')
break;
else
{
- if ((c = inchar ()) == EOF)
+ if (avail == 0 || inchar () == EOF)
break;
--avail;
}
@@ -1753,12 +1752,12 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
if (! got_dot)
{
- while ((unsigned char) *cmpp == c && avail > 0)
+ while ((unsigned char) *cmpp == c && avail >= 0)
if (*++cmpp == '\0')
break;
else
{
- if (inchar () == EOF)
+ if (avail == 0 || inchar () == EOF)
break;
--avail;
}
@@ -1790,12 +1789,12 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
++cmp2p;
if (cmp2p - thousands == cmpp - decimal)
{
- while ((unsigned char) *cmp2p == c && avail > 0)
+ while ((unsigned char) *cmp2p == c && avail >= 0)
if (*++cmp2p == '\0')
break;
else
{
- if (inchar () == EOF)
+ if (avail == 0 || inchar () == EOF)
break;
--avail;
}
@@ -1828,6 +1827,221 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
--width;
}
+ wctrans_t map;
+ if (__builtin_expect ((flags & I18N) != 0, 0)
+ /* Hexadecimal floats make no sense, fixing localized
+ digits with ASCII letters. */
+ && !is_hexa
+ /* Minimum requirement. */
+ && (wpsize == 0 || got_dot)
+ && (map = __wctrans ("to_inpunct")) != NULL)
+ {
+ /* Reget the first character. */
+ inchar ();
+
+ /* Localized digits, decimal points, and thousands
+ separator. */
+ wint_t wcdigits[12];
+
+ /* First get decimal equivalent to check if we read it
+ or not. */
+ wcdigits[11] = __towctrans (L'.', map);
+
+ /* If we have not read any character or have just read
+ locale decimal point which matches the decimal point
+ for localized FP numbers, then we may have localized
+ digits. Note, we test GOT_DOT above. */
+#ifdef COMPILE_WSCANF
+ if (wpsize == 0 || (wpsize == 1 && wcdigits[11] == decimal))
+#else
+ char mbdigits[12][MB_LEN_MAX + 1];
+
+ mbstate_t state;
+ memset (&state, '\0', sizeof (state));
+
+ bool match_so_far = wpsize == 0;
+ size_t mblen = __wcrtomb (mbdigits[11], wcdigits[11], &state);
+ if (mblen != (size_t) -1)
+ {
+ mbdigits[11][mblen] = '\0';
+ match_so_far |= (wpsize == strlen (decimal)
+ && strcmp (decimal, mbdigits[11]) == 0);
+ }
+ else
+ {
+ size_t decimal_len = strlen (decimal);
+ /* This should always be the case but the data comes
+ from a file. */
+ if (decimal_len <= MB_LEN_MAX)
+ {
+ match_so_far |= wpsize == decimal_len;
+ memcpy (mbdigits[11], decimal, decimal_len + 1);
+ }
+ else
+ match_so_far = false;
+ }
+
+ if (match_so_far)
+#endif
+ {
+ int have_locthousands = true;
+ /* Now get the digits and the thousands-sep equivalents. */
+ for (int n = 0; n < 11; ++n)
+ {
+ if (n < 10)
+ wcdigits[n] = __towctrans (L'0' + n, map);
+ else if (n == 10)
+ wcdigits[10] = __towctrans (L',', map);
+
+#ifndef COMPILE_WSCANF
+ memset (&state, '\0', sizeof (state));
+
+ size_t mblen = __wcrtomb (mbdigits[n], wcdigits[n],
+ &state);
+ if (mblen == (size_t) -1)
+ {
+ if (n == 10)
+ {
+ if (thousands == NULL || (flags & GROUP) == 0)
+ have_locthousands = false;
+ else
+ {
+ size_t thousands_len = strlen (thousands);
+ if (thousands_len <= MB_LEN_MAX)
+ memcpy (mbdigits[10], thousands,
+ thousands_len + 1);
+ else
+ have_locthousands = false;
+ }
+ }
+ else
+ /* Ignore checking against localized digits. */
+ goto no_i18nflt;
+ }
+ else
+ mbdigits[n][mblen] = '\0';
+#endif
+ }
+
+ /* Start checking against localized digits, if
+ convertion is done correctly. */
+ while (1)
+ {
+ if (got_e && wp[wpsize - 1] == exp_char
+ && (c == L_('-') || c == L_('+')))
+ ADDW (c);
+ else if (wpsize > 0 && !got_e
+ && (CHAR_T) TOLOWER (c) == exp_char)
+ {
+ ADDW (exp_char);
+ got_e = got_dot = 1;
+ }
+ else
+ {
+ /* Check against localized digits, decimal point,
+ and thousands separator. */
+ int n;
+ for (n = 0; n < 12; ++n)
+ {
+#ifdef COMPILE_WSCANF
+ if (c == wcdigits[n])
+ {
+ if (n < 10)
+ ADDW (L_('0') + n);
+ else if (n == 11 && !got_dot)
+ {
+ ADDW (decimal);
+ got_dot = 1;
+ }
+ else if (n == 10 && have_locthousands
+ && ! got_dot)
+ ADDW (thousands);
+ else
+ /* The last read character is not part
+ of the number anymore. */
+ n = 12;
+
+ break;
+ }
+#else
+ const char *cmpp = mbdigits[n];
+ int avail = width > 0 ? width : INT_MAX;
+
+ while ((unsigned char) *cmpp == c && avail >= 0)
+ if (*++cmpp == '\0')
+ break;
+ else
+ {
+ if (avail == 0 || inchar () == EOF)
+ break;
+ --avail;
+ }
+ if (*cmpp == '\0')
+ {
+ if (width > 0)
+ width = avail;
+
+ if (n < 10)
+ ADDW (L_('0') + n);
+ else if (n == 11 && !got_dot)
+ {
+ /* Add all the characters. */
+ for (cmpp = decimal; *cmpp != '\0';
+ ++cmpp)
+ ADDW ((unsigned char) *cmpp);
+
+ got_dot = 1;
+ }
+ else if (n == 10 && (flags & GROUP) != 0
+ && thousands != NULL && ! got_dot)
+ {
+ /* Add all the characters. */
+ for (cmpp = thousands; *cmpp != '\0';
+ ++cmpp)
+ ADDW ((unsigned char) *cmpp);
+ }
+ else
+ /* The last read character is not part
+ of the number anymore. */
+ n = 12;
+
+ break;
+ }
+
+ /* We are pushing all read characters back. */
+ if (cmpp > mbdigits[n])
+ {
+ ungetc (c, s);
+ while (--cmpp > mbdigits[n])
+ ungetc_not_eof ((unsigned char) *cmpp, s);
+ c = (unsigned char) *cmpp;
+ }
+#endif
+ }
+
+ if (n >= 12)
+ {
+ /* The last read character is not part
+ of the number anymore. */
+ ungetc (c, s);
+ break;
+ }
+ }
+
+ if (width == 0 || inchar () == EOF)
+ break;
+
+ if (width > 0)
+ --width;
+ }
+ }
+
+#ifndef COMPILE_WSCANF
+ no_i18nflt:
+ ;
+#endif
+ }
+
/* Have we read any character? If we try to read a number
in hexadecimal notation and we have read only the `0x'
prefix or no exponent this is an error. */