summaryrefslogtreecommitdiff
path: root/libc/stdio/scanf.c
diff options
context:
space:
mode:
Diffstat (limited to 'libc/stdio/scanf.c')
-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