summaryrefslogtreecommitdiff
path: root/libc/stdio
diff options
context:
space:
mode:
authorManuel Novoa III <mjn3@codepoet.org>2003-09-22 02:57:18 +0000
committerManuel Novoa III <mjn3@codepoet.org>2003-09-22 02:57:18 +0000
commit755c973ce29b2da548470143151a10d5d85ad7d9 (patch)
tree5ff472d338a8e329ad0c3c9beca31c00dcda527e /libc/stdio
parent17ab889ed12ac34f38d602998a55470645814324 (diff)
Ugh... EOF handling by scanf was completely broken. :-( Regretably,
I got my mind fixed in one mode and didn't comply with the standards. Things should be fixed now, but comparision testing is difficult when glibc's scanf is broken and they stubbornly refuse to even acknowledge that it is... even when confronted by specific examples from the C99 standards and from an official C standard defect report.
Diffstat (limited to 'libc/stdio')
-rw-r--r--libc/stdio/scanf.c133
1 files changed, 66 insertions, 67 deletions
diff --git a/libc/stdio/scanf.c b/libc/stdio/scanf.c
index d472c2676..ac5bf9931 100644
--- a/libc/stdio/scanf.c
+++ b/libc/stdio/scanf.c
@@ -33,6 +33,14 @@
* Sep 13, 2003
* Bug fix: Fix a problem reported by Atsushi Nemoto <anemo@mba.ocn.ne.jp>
* for environments where long and long long are the same.
+ *
+ * Sep 21, 2003
+ * Ugh... EOF handling by scanf was completely broken. :-( Regretably,
+ * I got my mind fixed in one mode and didn't comply with the standards.
+ * Things should be fixed now, but comparision testing is difficult when
+ * glibc's scanf is broken and they stubbornly refuse to even acknowledge
+ * that it is... even when confronted by specific examples from the C99
+ * standards and from an official C standard defect report.
*/
@@ -884,7 +892,7 @@ int __psfs_parse_spec(register psfs_t *psfs)
#ifdef L_vfscanf
static int sc_getc(register struct scan_cookie *sc)
{
- return getc(sc->fp);
+ return (getc_unlocked)(sc->fp); /* Disable the macro. */
}
static int scan_getwc(register struct scan_cookie *sc)
@@ -1101,6 +1109,7 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
unsigned char invert; /* Careful! Meaning changes. */
#endif /* L_vfscanf */
unsigned char fail;
+ unsigned char zero_conversions = 1;
#ifdef __UCLIBC_MJN3_ONLY__
#warning TODO: Make checking of the format string in C locale an option.
@@ -1188,6 +1197,8 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
if (*fmt == '%') { /* Conversion specification. */
if (*++fmt == '%') { /* Remember, '%' eats whitespace too. */
+ /* Note: The standard says no conversion occurs.
+ * So do not reset zero_conversions flag. */
psfs.conv_num = CONV_percent;
goto DO_CONVERSION;
}
@@ -1267,6 +1278,10 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
}
if (psfs.conv_num == CONV_n) {
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning Should %n count as a conversion as far as EOF return value?
+#endif
+/* zero_conversions = 0; */
if (psfs.store) {
_store_inttype(psfs.cur_ptr, psfs.dataargtype,
(uintmax_t) sc.nread);
@@ -1275,21 +1290,19 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
}
if (psfs.conv_num <= CONV_A) { /* pointer, integer, or float spec */
-#ifdef L_vfscanf
- if (__psfs_do_numeric(&psfs, &sc) < 0) { /* Num conv failed! */
- goto DONE;
- }
- goto NEXT_FMT;
-#else
int r = __psfs_do_numeric(&psfs, &sc);
+#ifndef L_vfscanf
if (sc.ungot_wflag == 1) { /* fix up '?', '.', and ',' hacks */
sc.cc = sc.ungot_char = sc.ungot_wchar;
}
+#endif
+ if (r != -1) { /* Either success or a matching failure. */
+ zero_conversions = 0;
+ }
if (r < 0) {
goto DONE;
}
goto NEXT_FMT;
-#endif
}
/* Do string conversions here since they are not common code. */
@@ -1314,6 +1327,7 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
}
while (__scan_getc(&sc) >= 0) {
+ zero_conversions = 0;
*b = sc.cc;
b += psfs.store;
}
@@ -1328,6 +1342,7 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
if (psfs.conv_num == CONV_s) {
/* Yes, believe it or not, a %s conversion can store nuls. */
while ((__scan_getc(&sc) >= 0) && !isspace(sc.cc)) {
+ zero_conversions = 0;
*b = sc.cc;
b += psfs.store;
fail = 0;
@@ -1383,6 +1398,7 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
while (__scan_getc(&sc) >= 0) {
+ zero_conversions = 0;
if (!scanset[sc.cc]) {
break;
}
@@ -1419,6 +1435,7 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
}
while (scan_getwc(&sc) >= 0) {
+ zero_conversions = 0;
assert(sc.width >= 0);
*wb = sc.wc;
wb += psfs.store;
@@ -1435,10 +1452,11 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
if (psfs.conv_num == CONV_S) {
/* Yes, believe it or not, a %s conversion can store nuls. */
- while ((scan_getwc(&sc) >= 0)
- && ((((__uwchar_t)(sc.wc)) > UCHAR_MAX)
- || !isspace(sc.wc))
- ) {
+ while (scan_getwc(&sc) >= 0) {
+ zero_conversions = 0;
+ if ((((__uwchar_t)(sc.wc)) <= UCHAR_MAX) && isspace(sc.wc)) {
+ break;
+ }
*wb = sc.wc;
wb += psfs.store;
fail = 0;
@@ -1447,6 +1465,7 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
assert(psfs.conv_num == CONV_LEFTBRACKET);
while (scan_getwc(&sc) >= 0) {
+ zero_conversions = 0;
if (((__uwchar_t) sc.wc) <= UCHAR_MAX) {
if (!scanset[sc.wc]) {
break;
@@ -1496,6 +1515,7 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
}
while (scan_getwc(&sc) >= 0) {
+ zero_conversions = 0;
if (psfs.conv_num == CONV_C) {
*wb = sc.wc;
wb += psfs.store;
@@ -1519,7 +1539,11 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
if ((psfs.conv_num == CONV_S) || (psfs.conv_num == CONV_s)) {
/* Yes, believe it or not, a %s conversion can store nuls. */
- while ((scan_getwc(&sc) >= 0) && !iswspace(sc.wc)) {
+ while (scan_getwc(&sc) >= 0) {
+ zero_conversions = 0;
+ if (iswspace(sc.wc)) {
+ break;
+ }
if (psfs.conv_num == CONV_S) {
*wb = sc.wc;
wb += psfs.store;
@@ -1564,6 +1588,7 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
/* Ok... a valid scanset spec. */
while (scan_getwc(&sc) >= 0) {
+ zero_conversions = 0;
ssp = sss;
do { /* We know sss < fmt. */
if (*ssp == '-') { /* possible range... */
@@ -1637,16 +1662,18 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
NEXT_FMT:
++fmt;
+ if (__FERROR(fp)) {
+ break;
+ }
}
DONE:
- if ((psfs.cnt == 0) && (*fmt) && __FEOF_OR_FERROR(fp)) {
+ if (__FERROR(fp) || (*fmt && zero_conversions && __FEOF(fp))) {
psfs.cnt = EOF; /* Yes, vfwscanf also returns EOF. */
}
kill_scan_cookie(&sc);
-/* RETURN_cnt: */
__STDIO_THREADUNLOCK(fp);
return psfs.cnt;
@@ -1662,6 +1689,7 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
{
unsigned char *b;
const unsigned char *p;
+
#ifdef __UCLIBC_HAS_FLOATS__
int exp_adjust = 0;
#endif
@@ -1674,6 +1702,7 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
unsigned char seendigit = 0;
+#warning what should be returned for an invalid conversion specifier?
#ifndef __UCLIBC_HAS_FLOATS__
if (psfs->conv_num > CONV_i) { /* floating point */
goto DONE;
@@ -1690,10 +1719,10 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
do {
if ((__scan_getc(sc) < 0) || (*p != sc->cc)) {
__scan_ungetc(sc);
- if (p > nil_string) { /* failed */
+ if (p > nil_string) {
/* We matched at least the '(' so even if we
* are at eof, we can not match a pointer. */
- goto DONE;
+ return -2; /* Matching failure */
}
break;
}
@@ -1713,6 +1742,10 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
}
__scan_getc(sc);
+ if (sc->cc < 0) {
+ return -1; /* Input failure (nothing read yet). */
+ }
+
if ((sc->cc == '+') || (sc->cc == '-')) { /* Handle leading sign.*/
*b++ = sc->cc;
__scan_getc(sc);
@@ -1722,17 +1755,13 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
if (sc->cc == '0') { /* Possibly set base and handle prefix. */
__scan_getc(sc);
if ((sc->cc|0x20) == 'x') { /* Assumes ascii.. x or X. */
- if ((__scan_getc(sc) < 0)
-#ifdef __UCLIBC_HAS_WCHAR__
- && !sc->ungot_wflag /* wc outside char range */
-#endif /* __UCLIBC_HAS_WCHAR__ */
- ) {
- /* Note! 'x' at end of file|field is special.
- * While this looks like I'm 'unget'ing twice,
- * EOF and end of field are handled specially
- * by the scan_* funcs. */
- __scan_ungetc(sc);
- goto DO_NO_0X;
+ if (__scan_getc(sc) < 0) {
+ /* Either EOF or error (including wc outside char range).
+ * If EOF or error, this is a matching failure (we read 0x).
+ * If wc outside char range, this is also a matching failure.
+ * Hence, we do an unget (although not really necessary here
+ * and fail. */
+ goto DONE_DO_UNGET; /* matching failure */
}
base = 16; /* Base 16 for sure now. */
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
@@ -1741,7 +1770,6 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
*b++ = 'x';
#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
} else { /* oops... back up */
- DO_NO_0X:
__scan_ungetc(sc);
sc->cc = '0'; /* NASTY HACK! */
@@ -1776,7 +1804,7 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
if (nbmax < nblk2) {
nbmax = nblk2;
}
- assert(!*++p);
+ assert(!p[1]);
}
/* Note: for printf, if 0 and \' flags appear then
@@ -1823,7 +1851,7 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
}
#endif
if (nbmax > nblk1) {
- goto DONE_DO_UNGET;
+ goto DONE_DO_UNGET; /* matching failure */
}
goto DONE_GROUPING_DO_UNGET; /* nbmax == nblk1 */
}
@@ -1904,33 +1932,11 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
/* got an inner group */
goto DONE_GROUPING_DO_UNGET;
}
- if (i > nblk1) {
- /* An inner group if we can back up a bit. */
- if ((i - nblk1) <= (sc->ungot_flag ^ 1)) {
- assert(sc->cc < 0);
- --b;
- goto DO_RECOVER_GROUP;
- }
- }
-
- /* (0 < i < nblk1) && (pass > 0) so prev group char
- * So we have an unrecoverable situation. */
- goto DONE_DO_UNGET;
+ goto DONE_DO_UNGET; /* Matching failure. */
} /* i != 0 */
assert(pass);
- /* No next group. Can we back up past grouping mb char? */
- if ((pass == 1) || (nblk1 == nblk2)) {
- if (!i && (sc->tslen == 1) && (sc->cc < 0)) {
- /* No digits, grouping mb char is len 1, and EOF*/
- DO_RECOVER_GROUP:
- if (sc->ungot_flag & 2) {
- __scan_ungetc(sc);
- }
- goto DONE_GROUPING_DO_UNGET;
- }
- }
goto DONE_DO_UNGET;
} while (1);
@@ -2005,12 +2011,7 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
}
if (*p != sc->cc) {
if (p > sc->fake_decpt) {
- if ((sc->cc >= 0) && (p > sc->fake_decpt + 1)) {
- goto DONE_DO_UNGET; /* failed */
- }
-
- __scan_ungetc(sc);
-
+ goto DONE_DO_UNGET; /* matching failure (read some of decpt) */
}
goto DO_DIGIT_CHECK;
}
@@ -2048,7 +2049,7 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
static const unsigned char nan_inf_str[] = "an\0nfinity";
if (base == 16) { /* We had a prefix, but no digits! */
- goto DONE_DO_UNGET;
+ goto DONE_DO_UNGET; /* matching failure */
}
/* Avoid tolower problems for INFINITY in the tr_TR locale. (yuk)*/
@@ -2095,9 +2096,8 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
*b++ = sc->cc;
__scan_getc(sc);
- if (sc->cc < 0) { /* EOF... recoverable */
- --b;
- goto GOT_FLOAT;
+ if (sc->cc < 0) {
+ goto DONE_DO_UNGET; /* matching failure.. no exponent digits */
}
if ((sc->cc == '+') || (sc->cc == '-')) { /* Signed exponent? */
@@ -2118,7 +2118,7 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
__scan_getc(sc);
} while (sc->cc == '0');
}
-
+
while (__isdigit_char_or_EOF(sc->cc)) { /* Exponent digits (base 10).*/
if (seendigit < MAX_EXP_DIGITS) {
++seendigit;
@@ -2134,7 +2134,6 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
GOT_FLOAT:
-
*b = 0;
{
__fpmax_t x;
@@ -2159,7 +2158,7 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
DONE_DO_UNGET:
__scan_ungetc(sc);
DONE:
- return -1;
+ return -2; /* Matching failure. */
}
#endif