diff options
Diffstat (limited to 'libc')
-rw-r--r-- | libc/stdio/scanf.c | 133 |
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 |