scanf: fix handling of %n token Modified: trunk/reactos/lib/crt/wine/scanf.h _____
Modified: trunk/reactos/lib/crt/wine/scanf.h --- trunk/reactos/lib/crt/wine/scanf.h 2005-03-16 23:51:26 UTC (rev 14153) +++ trunk/reactos/lib/crt/wine/scanf.h 2005-03-16 23:59:41 UTC (rev 14154) @@ -98,257 +98,257 @@
if (nch == _EOF_) return _EOF_RET;
while (*format) { - /* a whitespace character in the format string causes scanf to read, - * but not store, all consecutive white-space characters in the input - * up to the next non-white-space character. One white space character - * in the input matches any number (including zero) and combination of - * white-space characters in the input. */ - if (_ISSPACE_(*format)) { + /* a whitespace character in the format string causes scanf to read, + * but not store, all consecutive white-space characters in the input + * up to the next non-white-space character. One white space character + * in the input matches any number (including zero) and combination of + * white-space characters in the input. */ + if (_ISSPACE_(*format)) { /* skip whitespace */ while ((nch!=_EOF_) && _ISSPACE_(nch)) nch = _GETC_(file); } - /* a format specification causes scanf to read and convert characters - * in the input into values of a specified type. The value is assigned - * to an argument in the argument list. Format specifications have - * the form %[*][width][{h | l | I64 | L}]type */ + /* a format specification causes scanf to read and convert characters + * in the input into values of a specified type. The value is assigned + * to an argument in the argument list. Format specifications have + * the form %[*][width][{h | l | I64 | L}]type */ else if (*format == '%') { int st = 0; int suppress = 0; int width = 0; - int base, number_signed; - int h_prefix = 0; - int l_prefix = 0; - int L_prefix = 0; - int w_prefix = 0; - int prefix_finished = 0; - int I64_prefix = 0; + int base, number_signed; + int h_prefix = 0; + int l_prefix = 0; + int L_prefix = 0; + int w_prefix = 0; + int prefix_finished = 0; + int I64_prefix = 0; format++; - /* look for leading asterisk, which means 'suppress assignment of - * this field'. */ - if (*format=='*') { - format++; - suppress=1; - } - /* look for width specification */ - while (_ISDIGIT_(*format)) { - width*=10; - width+=*format++ - '0'; - } - if (width==0) width=-1; /* no width spec seen */ - /* read prefix (if any) */ - while (!prefix_finished) { - switch(*format) { - case 'h': h_prefix = 1; break; - case 'l': l_prefix = 1; break; - case 'w': w_prefix = 1; break; - case 'L': L_prefix = 1; break; - case 'I': - if (*(format + 1) == '6' && - *(format + 2) == '4') { - I64_prefix = 1; - format += 2; - } - break; - default: - prefix_finished = 1; - } - if (!prefix_finished) format++; - } - /* read type */ + /* look for leading asterisk, which means 'suppress assignment of + * this field'. */ + if (*format=='*') { + format++; + suppress=1; + } + /* look for width specification */ + while (_ISDIGIT_(*format)) { + width*=10; + width+=*format++ - '0'; + } + if (width==0) width=-1; /* no width spec seen */ + /* read prefix (if any) */ + while (!prefix_finished) { + switch(*format) { + case 'h': h_prefix = 1; break; + case 'l': l_prefix = 1; break; + case 'w': w_prefix = 1; break; + case 'L': L_prefix = 1; break; + case 'I': + if (*(format + 1) == '6' && + *(format + 2) == '4') { + I64_prefix = 1; + format += 2; + } + break; + default: + prefix_finished = 1; + } + if (!prefix_finished) format++; + } + /* read type */ switch(*format) { - case 'x': - case 'X': /* hexadecimal integer. */ - base = 16; number_signed = 0; - goto number; - case 'o': /* octal integer */ - base = 8; number_signed = 0; - goto number; - case 'u': /* unsigned decimal integer */ - base = 10; number_signed = 0; - goto number; - case 'd': /* signed decimal integer */ - base = 10; number_signed = 1; - goto number; - case 'i': /* generic integer */ - base = 10; number_signed = 1; - number: { - /* read an integer */ - ULONGLONG cur = 0; - int negative = 0; - int seendigit=0; + case 'x': + case 'X': /* hexadecimal integer. */ + base = 16; number_signed = 0; + goto number; + case 'o': /* octal integer */ + base = 8; number_signed = 0; + goto number; + case 'u': /* unsigned decimal integer */ + base = 10; number_signed = 0; + goto number; + case 'd': /* signed decimal integer */ + base = 10; number_signed = 1; + goto number; + case 'i': /* generic integer */ + base = 10; number_signed = 1; + number: { + /* read an integer */ + ULONGLONG cur = 0; + int negative = 0; + int seendigit=0; /* skip initial whitespace */ while ((nch!=_EOF_) && _ISSPACE_(nch)) nch = _GETC_(file); /* get sign */ if (number_signed && (nch == '-' || - nch == '+')) { - negative = (nch=='-'); + nch == '+')) { + negative = (nch=='-'); nch = _GETC_(file); - if (width>0) width--; + if (width>0) width--; } - /* look for leading indication of base */ - if (width!=0 && nch == '0') { + /* look for leading indication of base */ + if (width!=0 && nch == '0') { nch = _GETC_(file); - if (width>0) width--; - seendigit=1; - if (width!=0 && (nch=='x' || nch=='X')) { - if (base==0) - base=16; - if (base==16) { - nch = _GETC_(file); - if (width>0) width--; - seendigit=0; - } - } else if (base==0) - base = 8; - } - /* throw away leading zeros */ - while (width!=0 && nch=='0') { + if (width>0) width--; + seendigit=1; + if (width!=0 && (nch=='x' || nch=='X')) { + if (base==0) + base=16; + if (base==16) { + nch = _GETC_(file); + if (width>0) width--; + seendigit=0; + } + } else if (base==0) + base = 8; + } + /* throw away leading zeros */ + while (width!=0 && nch=='0') { nch = _GETC_(file); - if (width>0) width--; - seendigit=1; - } - if (width!=0 && _CHAR2DIGIT_(nch, base)!=-1) { - cur = _CHAR2DIGIT_(nch, base); - nch = _GETC_(file); - if (width>0) width--; - seendigit=1; - } + if (width>0) width--; + seendigit=1; + } + if (width!=0 && _CHAR2DIGIT_(nch, base)!=-1) { + cur = _CHAR2DIGIT_(nch, base); + nch = _GETC_(file); + if (width>0) width--; + seendigit=1; + } /* read until no more digits */ while (width!=0 && (nch!=_EOF_) && _CHAR2DIGIT_(nch, base)!=-1) { cur = cur*base + _CHAR2DIGIT_(nch, base); nch = _GETC_(file); - if (width>0) width--; - seendigit=1; + if (width>0) width--; + seendigit=1; } - /* okay, done! */ - if (!seendigit) break; /* not a valid number */ + /* okay, done! */ + if (!seendigit) break; /* not a valid number */ st = 1; if (!suppress) { #define _SET_NUMBER_(type) *va_arg(ap, type*) = negative ? -cur : cur - if (number_signed) { - if (I64_prefix) _SET_NUMBER_(LONGLONG); - else if (l_prefix) _SET_NUMBER_(long int); - else if (h_prefix) _SET_NUMBER_(short int); - else _SET_NUMBER_(int); - } else { - if (negative) { - WARN("Dropping sign in reading a negative number into an unsigned value"); - negative = 0; - } - if (I64_prefix) _SET_NUMBER_(ULONGLONG); - else if (l_prefix) _SET_NUMBER_(unsigned long int); - else if (h_prefix) - _SET_NUMBER_(unsigned short int); - else _SET_NUMBER_(unsigned int); - } - } + if (number_signed) { + if (I64_prefix) _SET_NUMBER_(LONGLONG); + else if (l_prefix) _SET_NUMBER_(long int); + else if (h_prefix) _SET_NUMBER_(short int); + else _SET_NUMBER_(int); + } else { + if (negative) { + WARN("Dropping sign in reading a negative number into an unsigned value"); + negative = 0; + } + if (I64_prefix) _SET_NUMBER_(ULONGLONG); + else if (l_prefix) _SET_NUMBER_(unsigned long int); + else if (h_prefix) + _SET_NUMBER_(unsigned short int); + else _SET_NUMBER_(unsigned int); + } + } } break; - case 'e': - case 'E': - case 'f': - case 'g': + case 'e': + case 'E': + case 'f': + case 'g': case 'G': { /* read a float */ long double cur = 0; - int negative = 0; + int negative = 0; /* skip initial whitespace */ while ((nch!=_EOF_) && _ISSPACE_(nch)) nch = _GETC_(file); - /* get sign. */ + /* get sign. */ if (nch == '-' || nch == '+') { - negative = (nch=='-'); - if (width>0) width--; - if (width==0) break; + negative = (nch=='-'); + if (width>0) width--; + if (width==0) break; nch = _GETC_(file); } - /* get first digit. */ - if ('.' != nch) { - if (!_ISDIGIT_(nch)) break; - cur = (nch - '0'); - nch = _GETC_(file); - if (width>0) width--; - /* read until no more digits */ - while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) { + /* get first digit. */ + if ('.' != nch) { + if (!_ISDIGIT_(nch)) break; + cur = (nch - '0'); + nch = _GETC_(file); + if (width>0) width--; + /* read until no more digits */ + while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) { cur = cur*10 + (nch - '0'); nch = _GETC_(file); - if (width>0) width--; - } - } else { - cur = 0; /* MaxPayneDemo Fix: .8 -> 0.8 */ - } - /* handle decimals */ + if (width>0) width--; + } + } else { + cur = 0; /* MaxPayneDemo Fix: .8 -> 0.8 */ + } + /* handle decimals */ if (width!=0 && nch == '.') { float dec = 1; nch = _GETC_(file); - if (width>0) width--; + if (width>0) width--; while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) { dec /= 10; cur += dec * (nch - '0'); nch = _GETC_(file); - if (width>0) width--; + if (width>0) width--; } } - /* handle exponent */ - if (width!=0 && (nch == 'e' || nch == 'E')) { - int exponent = 0, negexp = 0; - float expcnt; + /* handle exponent */ + if (width!=0 && (nch == 'e' || nch == 'E')) { + int exponent = 0, negexp = 0; + float expcnt; nch = _GETC_(file); - if (width>0) width--; - /* possible sign on the exponent */ - if (width!=0 && (nch=='+' || nch=='-')) { - negexp = (nch=='-'); + if (width>0) width--; + /* possible sign on the exponent */ + if (width!=0 && (nch=='+' || nch=='-')) { + negexp = (nch=='-'); nch = _GETC_(file); - if (width>0) width--; - } - /* exponent digits */ - while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) { - exponent *= 10; - exponent += (nch - '0'); + if (width>0) width--; + } + /* exponent digits */ + while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) { + exponent *= 10; + exponent += (nch - '0'); nch = _GETC_(file); - if (width>0) width--; + if (width>0) width--; } - /* update 'cur' with this exponent. */ - expcnt = negexp ? .1 : 10; - while (exponent!=0) { - if (exponent&1) - cur*=expcnt; - exponent/=2; - expcnt=expcnt*expcnt; - } - } + /* update 'cur' with this exponent. */ + expcnt = negexp ? .1 : 10; + while (exponent!=0) { + if (exponent&1) + cur*=expcnt; + exponent/=2; + expcnt=expcnt*expcnt; + } + } st = 1; if (!suppress) { - if (L_prefix) _SET_NUMBER_(long double); - else if (l_prefix) _SET_NUMBER_(double); - else _SET_NUMBER_(float); - } + if (L_prefix) _SET_NUMBER_(long double); + else if (l_prefix) _SET_NUMBER_(double); + else _SET_NUMBER_(float); + } } break; - /* According to - * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/h tml/_crt_scanf_type_field_characters.asp - * 's' reads a character string in a call to fscanf - * and 'S' a wide character string and vice versa in a - * call to fwscanf. The 'h', 'w' and 'l' prefixes override - * this behaviour. 'h' forces reading char * but 'l' and 'w' - * force reading WCHAR. */ - case 's': - if (w_prefix || l_prefix) goto widecharstring; - else if (h_prefix) goto charstring; + /* According to + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/h tml/_crt_scanf_type_field_characters.asp + * 's' reads a character string in a call to fscanf + * and 'S' a wide character string and vice versa in a + * call to fwscanf. The 'h', 'w' and 'l' prefixes override + * this behaviour. 'h' forces reading char * but 'l' and 'w' + * force reading WCHAR. */ + case 's': + if (w_prefix || l_prefix) goto widecharstring; + else if (h_prefix) goto charstring; #ifdef WIDE_SCANF - else goto widecharstring; + else goto widecharstring; #else /* WIDE_SCANF */ - else goto charstring; + else goto charstring; #endif /* WIDE_SCANF */ - case 'S': - if (w_prefix || l_prefix) goto widecharstring; - else if (h_prefix) goto charstring; + case 'S': + if (w_prefix || l_prefix) goto widecharstring; + else if (h_prefix) goto charstring; #ifdef WIDE_SCANF - else goto charstring; + else goto charstring; #else /* WIDE_SCANF */ - else goto widecharstring; + else goto widecharstring; #endif /* WIDE_SCANF */ - charstring: { /* read a word into a char */ - char*str = suppress ? NULL : va_arg(ap, char*); + charstring: { /* read a word into a char */ + char*str = suppress ? NULL : va_arg(ap, char*); char*sptr = str; /* skip initial whitespace */ while ((nch!=_EOF_) && _ISSPACE_(nch)) @@ -356,15 +356,15 @@ /* read until whitespace */ while (width!=0 && (nch!=_EOF_) && !_ISSPACE_(nch)) { if (!suppress) *sptr++ = _CHAR2SUPPORTED_(nch); - st++; + st++; nch = _GETC_(file); - if (width>0) width--; + if (width>0) width--; } /* terminate */ if (!suppress) *sptr = 0; } break; - widecharstring: { /* read a word into a wchar_t* */ + widecharstring: { /* read a word into a wchar_t* */ wchar_t*str = suppress ? NULL : va_arg(ap, wchar_t*); wchar_t*sptr = str; @@ -374,33 +374,33 @@ /* read until whitespace */ while (width!=0 && (nch!=_EOF_) && !_ISSPACE_(nch)) { if (!suppress) *sptr++ = _WIDE2SUPPORTED_(nch); - st++; + st++; nch = _GETC_(file); - if (width>0) width--; + if (width>0) width--; } /* terminate */ if (!suppress) *sptr = 0; } break; /* 'c' and 'C work analogously to 's' and 'S' as described - * above */ - case 'c': - if (w_prefix || l_prefix) goto widecharacter; - else if (h_prefix) goto character; + * above */ + case 'c': + if (w_prefix || l_prefix) goto widecharacter; + else if (h_prefix) goto character; #ifdef WIDE_SCANF - else goto widecharacter; + else goto widecharacter; #else /* WIDE_SCANF */ - else goto character; + else goto character; #endif /* WIDE_SCANF */ - case 'C': - if (w_prefix || l_prefix) goto widecharacter; - else if (h_prefix) goto character; + case 'C': + if (w_prefix || l_prefix) goto widecharacter; + else if (h_prefix) goto character; #ifdef WIDE_SCANF - else goto character; + else goto character; #else /* WIDE_SCANF */ - else goto widecharacter; + else goto widecharacter; #endif /* WIDE_SCANF */ - character: { /* read single character into char */ + character: { /* read single character into char */ if (nch!=_EOF_) { if (!suppress) { char*c = va_arg(ap, char*); @@ -410,8 +410,8 @@ nch = _GETC_(file); } } - break; - widecharacter: { /* read single character into a wchar_t */ + break; + widecharacter: { /* read single character into a wchar_t */ if (nch!=_EOF_) { if (!suppress) { wchar_t*c = va_arg(ap, wchar_t*); @@ -420,80 +420,103 @@ nch = _GETC_(file); st = 1; } - } - break; - case 'n': { - if (!suppress) { - int*n = va_arg(ap, int*); - *n = consumed - (nch!=_EOF_); - } - } - break; - case '[': { + } + break; + case 'n': { + if (!suppress) { + int*n = va_arg(ap, int*); + + /* + *n = consumed - (nch!=_EOF_); + + FIXME: The above is the Wine version and it doesnt work in ros + when %n is at end of input string (return one too many). + But does it fail in Wine too?? If so wine also needs fixin. + -Gunnar + */ + + *n = consumed - 1; + } + /* This is an odd one: according to the standard, + * "Execution of a %n directive does not increment the + * assignment count returned at the completion of + * execution" even if it wasn't suppressed with the + * '*' flag. The Corrigendum to the standard seems + * to contradict this (comment out the assignment to + * suppress below if you want to implement these + * alternate semantics) but the windows program I'm + * looking at expects the behavior I've coded here + * (which happens to be what glibc does as well). + */ + suppress = 1; + st = 1; + } + break; + case '[': { _CHAR_ *str = suppress ? NULL : va_arg(ap, _CHAR_*); _CHAR_ *sptr = str; - RTL_BITMAP bitMask; + RTL_BITMAP bitMask; ULONG *Mask; - int invert = 0; /* Set if we are NOT to find the chars */ + int invert = 0; /* Set if we are NOT to find the chars */
- /* Init our bitmap */ + /* Init our bitmap */ Mask = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, _BITMAPSIZE_/8); - RtlInitializeBitMap(&bitMask, Mask, _BITMAPSIZE_); + RtlInitializeBitMap(&bitMask, Mask, _BITMAPSIZE_);
- /* Read the format */ - format++; - if(*format == '^') { - invert = 1; - format++; - } - if(*format == ']') { - RtlSetBits(&bitMask, ']', 1); - format++; - } + /* Read the format */ + format++; + if(*format == '^') { + invert = 1; + format++; + } + if(*format == ']') { + RtlSetBits(&bitMask, ']', 1); + format++; + } while(*format && (*format != ']')) { - /* According to: - * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore9 8/html/_crt_scanf_width_specification.asp - * "Note that %[a-z] and %[z-a] are interpreted as equivalent to %[abcde...z]." */ - if((*format == '-') && (*(format + 1) != ']')) { - if ((*(format - 1)) < *(format + 1)) - RtlSetBits(&bitMask, *(format - 1) +1 , *(format + 1) - *(format - 1)); - else - RtlSetBits(&bitMask, *(format + 1) , *(format - 1) - *(format + 1)); - format++; - } else - RtlSetBits(&bitMask, *format, 1); - format++; - } + /* According to: + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore9 8/html/_crt_scanf_width_specification.asp + * "Note that %[a-z] and %[z-a] are interpreted as equivalent to %[abcde...z]." */ + if((*format == '-') && (*(format + 1) != ']')) { + if ((*(format - 1)) < *(format + 1)) + RtlSetBits(&bitMask, *(format - 1) +1 , *(format + 1) - *(format - 1)); + else + RtlSetBits(&bitMask, *(format + 1) , *(format - 1) - *(format + 1)); + format++; + } else + RtlSetBits(&bitMask, *format, 1); + format++; + } /* read until char is not suitable */ while ((width != 0) && (nch != _EOF_)) { - if(!invert) { - if(RtlAreBitsSet(&bitMask, nch, 1)) { - if (!suppress) *sptr++ = _CHAR2SUPPORTED_(nch); - } else - break; - } else { - if(RtlAreBitsClear(&bitMask, nch, 1)) { - if (!suppress) *sptr++ = _CHAR2SUPPORTED_(nch); - } else - break; - } + if(!invert) { + if(RtlAreBitsSet(&bitMask, nch, 1)) { + if (!suppress) *sptr++ = _CHAR2SUPPORTED_(nch); + } else + break; + } else { + if(RtlAreBitsClear(&bitMask, nch, 1)) { + if (!suppress) *sptr++ = _CHAR2SUPPORTED_(nch); + } else + break; + } st++; nch = _GETC_(file); if (width>0) width--; } /* terminate */ if (!suppress) *sptr = 0; - HeapFree(GetProcessHeap(), 0, Mask); + HeapFree(GetProcessHeap(), 0, Mask); } break; default: - /* From spec: "if a percent sign is followed by a character - * that has no meaning as a format-control character, that - * character and the following characters are treated as - * an ordinary sequence of characters, that is, a sequence - * of characters that must match the input. For example, - * to specify that a percent-sign character is to be input, - * use %%." */ + /* From spec: "if a percent sign is followed by a character + * that has no meaning as a format-control character, that + * character and the following characters are treated as + * an ordinary sequence of characters, that is, a sequence + * of characters that must match the input. For example, + * to specify that a percent-sign character is to be input, + * use %%." */ while ((nch!=_EOF_) && _ISSPACE_(nch)) nch = _GETC_(file); if (nch==*format) { @@ -506,18 +529,18 @@ if (st && !suppress) rd++; else if (!st) break; } - /* a non-white-space character causes scanf to read, but not store, - * a matching non-white-space character. */ + /* a non-white-space character causes scanf to read, but not store, + * a matching non-white-space character. */ else { /* check for character match */ if (nch == *format) { - nch = _GETC_(file); + nch = _GETC_(file); } else break; } format++; } if (nch!=_EOF_) { - _UNGETC_(nch, file); + _UNGETC_(nch, file); } TRACE("returning %d\n", rd); return rd;