library/stdio_vfscanf.c
/*
* $Id: stdio_vfscanf.c,v 1.22 2015-04-24 13:00:12 obarthel Exp $
*
* :ts=4
*
* Portable ISO 'C' (1994) runtime library for the Amiga computer
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Neither the name of Olaf Barthel nor the names of contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*#define DEBUG*/
#ifndef _STDLIB_NULL_POINTER_CHECK_H
#include "stdlib_null_pointer_check.h"
#endif /* _STDLIB_NULL_POINTER_CHECK_H */
/****************************************************************************/
#ifndef _STDIO_HEADERS_H
#include "stdio_headers.h"
#endif /* _STDIO_HEADERS_H */
#ifndef _LOCALE_HEADERS_H
#include "locale_headers.h"
#endif /* _LOCALE_HEADERS_H */
/****************************************************************************/
#if defined(FLOATING_POINT_SUPPORT) && !defined(_MATH_HEADERS_H)
#include "math_headers.h"
#endif /* FLOATING_POINT_SUPPORT && !_MATH_HEADERS_H */
/****************************************************************************/
/*
* Uncomment this to activate '%lld' support and the like. Better still,
* define this is in the Makefile!
*/
/*#define USE_64_BIT_INTS*/
/****************************************************************************/
int
vfscanf(FILE *stream, const char *format, va_list arg)
{
enum parameter_size_t
{
parameter_size_byte,
parameter_size_long,
parameter_size_short,
parameter_size_size_t,
parameter_size_ptrdiff_t,
parameter_size_long_long,
parameter_size_long_double,
parameter_size_intmax_t,
parameter_size_default
};
enum parameter_size_t parameter_size;
int total_num_chars_read = 0;
int num_chars_processed;
BOOL assignment_suppressed;
int maximum_field_width;
int result = EOF;
int num_conversions = 0;
int num_assignments = 0;
int conversion_type;
int c;
ENTER();
assert( stream != NULL && format != NULL );
if(__check_abort_enabled)
__check_abort();
flockfile(stream);
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(stream == NULL || format == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
if(__fgetc_check(stream) < 0)
goto out;
/* Just so we can detect errors and tell them apart from
an 'end of file' condition. */
clearerr(stream);
while((c = (*(unsigned char *)format)) != '\0')
{
D(("next format character is '%lc'",c));
if(isspace(c))
{
/* Skip all blank spaces in the stream. */
format++;
while((c = __getc(stream)) != EOF)
{
if(isspace(c))
{
total_num_chars_read++;
}
else
{
/* End of the white space; we push the last
* character read back into the stream.
*/
if(ungetc(c,stream) == EOF)
goto out;
/* Resume scanning. */
break;
}
}
if(c == EOF)
{
SHOWMSG("end of file");
/* Hit the end of the stream? */
if(num_conversions == 0)
goto out;
/* Finished... */
break;
}
/* Resume scanning. */
continue;
}
else if (c != '%')
{
int d;
SHOWMSG("next character must match exactly");
/* Match the next character in the stream. */
format++;
d = __getc(stream);
if(d == EOF)
{
SHOWMSG("end of file");
/* Hit the end of the stream. */
if(num_conversions == 0)
goto out;
break;
}
else if (c == d)
{
SHOWMSG("it matches");
total_num_chars_read++;
}
else
{
SHOWMSG("it does not match");
if(ungetc(d,stream) == EOF)
goto out;
break;
}
/* Resume scanning. */
continue;
}
format++;
c = (*format);
if(c == '*')
{
/* If the first letter of the format specification
* is an asterisk, no output will be produced for
* this parameter.
*/
assignment_suppressed = TRUE;
format++;
c = (*format);
}
else
{
assignment_suppressed = FALSE;
}
maximum_field_width = -1;
if('0' <= c && c <= '9')
{
int next_sum;
int sum = 0;
/* This could be the field width specification. */
while(TRUE)
{
c = (*format);
if('0' <= c && c <= '9')
{
next_sum = (10 * sum) + c - '0';
if(next_sum < sum) /* overflow? */
break;
sum = next_sum;
format++;
}
else
{
break;
}
}
maximum_field_width = sum;
}
else if (c == '\0')
{
/* And that's the end of the string. */
break;
}
c = (*format);
if(c == 'h')
{
/* Parameter is a short integer. */
parameter_size = parameter_size_short;
SHOWMSG("short integer");
format++;
}
else if (c == 'j')
{
parameter_size = parameter_size_intmax_t;
SHOWMSG("largest integer");
format++;
}
else if (c == 'l')
{
/* Parameter is a long integer or a double precision floating point value. */
parameter_size = parameter_size_long;
SHOWMSG("long integer");
format++;
}
else if (c == 'L')
{
/* Parameter is a long double floating point value. */
parameter_size = parameter_size_long_double;
SHOWMSG("long double");
format++;
}
else if (c == 't')
{
parameter_size = parameter_size_ptrdiff_t;
SHOWMSG("pointer difference");
format++;
}
else if (c == 'z')
{
parameter_size = parameter_size_size_t;
SHOWMSG("size");
format++;
}
else if (c == '\0')
{
/* And that's the end of the string. */
break;
}
else
{
/* Parameter is a long integer or a single precision floating point value. */
parameter_size = parameter_size_default;
SHOWMSG("default");
}
/* Now for the conversion type. */
c = (*format);
if(c == '\0')
break;
#if defined(USE_64_BIT_INTS) && defined(__GNUC__)
{
D(("c = '%lc'",c));
/* Check for long long parameters. */
if(parameter_size == parameter_size_long && c == 'l')
{
SHOWMSG("this is a long long parameter");
parameter_size = parameter_size_long_long;
format++;
/* The conversion type follows. */
c = (*format);
if(c == '\0')
break;
}
}
#endif /* __GNUC__ */
/* Check for byte parameters. */
if(parameter_size == parameter_size_short && c == 'h')
{
parameter_size = parameter_size_byte;
SHOWMSG("byte-sized integer");
format++;
/* The conversion type follows. */
c = (*format);
if(c == '\0')
break;
}
switch(c)
{
/* It's a pointer. */
case 'p':
conversion_type = 'x';
format++;
break;
/* It's a floating point number. */
case 'a':
case 'A':
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
conversion_type = 'f';
format++;
break;
case 'c': /* character(s) */
case 'd': /* signed integer */
case 'i': /* signed integer in decimal, octal or hexadecimal format */
case 'n': /* number of characters processed */
case 'o': /* integer (octal) */
case 's': /* string */
case 'u': /* unsigned integer */
case 'x': /* unsigned integer in hexadecimal format */
case 'X': /* unsigned integer in hexadecimal format */
case '%': /* the % character */
case '[': /* a range of characters */
conversion_type = c;
format++;
break;
default:
/* Must be something else */
continue;
}
/* Skip any initial whitespace characters. Exceptions are %c, %n and %[. */
if(conversion_type != 'c' &&
conversion_type != 'n' &&
conversion_type != '[')
{
while((c = __getc(stream)) != EOF)
{
if(isspace(c))
{
total_num_chars_read++;
}
else
{
/* End of the white space; we push the last
* character read back into the stream.
*/
if(ungetc(c,stream) == EOF)
goto out;
/* Proceed with the conversion operation. */
break;
}
}
}
num_chars_processed = 0;
D(("conversion type = '%lc'",c));
if(conversion_type == 'c')
{
char * c_ptr;
int i;
/* The maximum field width is actually the number
* of characters that should be read. Default is
* 1 character.
*/
if(maximum_field_width < 0)
maximum_field_width = 1;
if(NOT assignment_suppressed)
{
assert( arg != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(arg == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
c_ptr = va_arg(arg,char *);
assert( c_ptr != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(c_ptr == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
}
else
{
c_ptr = NULL;
}
for(i = 0 ; i < maximum_field_width ; i++)
{
c = __getc(stream);
if(c == EOF)
{
/* Bail out if we hit the end of the stream. */
if(num_conversions == 0)
goto out;
break;
}
total_num_chars_read++;
if(NOT assignment_suppressed)
(*c_ptr++) = c;
num_chars_processed++;
}
/* Did we read as many characters as we wanted? */
if(num_chars_processed != maximum_field_width)
break;
if(NOT assignment_suppressed)
num_assignments++;
num_conversions++;
}
else if (conversion_type == 'f')
{
#if defined(FLOATING_POINT_SUPPORT)
{
__long_double_t sum = 0.0;
__long_double_t new_sum;
BOOL is_negative = FALSE;
BOOL decimal_point_matches = FALSE;
BOOL have_exponent = FALSE;
void * next_parameter = NULL;
/* We boldly try to initialize the parameter to a well-
defined value before we begin the conversion. */
if(NOT assignment_suppressed)
{
assert( arg != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(arg == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
if(parameter_size == parameter_size_default)
{
SHOWMSG("short format (float)");
next_parameter = va_arg(arg,float *);
}
else if (parameter_size == parameter_size_long)
{
SHOWMSG("long format (double)");
next_parameter = va_arg(arg,double *);
}
else
{
SHOWMSG("extended format (long double)");
next_parameter = va_arg(arg,__long_double_t *);
}
assert( next_parameter != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(next_parameter == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
if(parameter_size == parameter_size_default)
{
*((float *)next_parameter) = 0;
}
else if (parameter_size == parameter_size_long)
{
*((double *)next_parameter) = 0;
}
else
{
*((__long_double_t *)next_parameter) = 0;
}
}
if(maximum_field_width != 0)
{
c = __getc(stream);
if(c != EOF)
{
/* Skip the sign. */
if(c == '-')
{
SHOWMSG("negative floating point number");
is_negative = 1;
total_num_chars_read++;
if(maximum_field_width > 0)
maximum_field_width--;
}
else if (c == '+')
{
SHOWMSG("ignoring positive sign");
total_num_chars_read++;
if(maximum_field_width > 0)
maximum_field_width--;
}
else
{
D(("first character '%lc' is not a sign",c));
/* It's not a sign; reread this later. */
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
}
}
}
/* Now it gets complicated. We need to pick up a keyword
such as "inf", "infinity" or "nan" if it's present.
This is tricky because we won't be able to push all
the characters back we read during scanning. */
if(maximum_field_width != 0)
{
const char infinity_string[] = "infinity";
const char infinity_nan[] = "nan";
char chars_read_so_far[80];
size_t num_chars_read_so_far = 0;
size_t infinity_match = 0;
size_t nan_match = 0;
while(maximum_field_width != 0 && num_chars_read_so_far < sizeof(chars_read_so_far) && (c = __getc(stream)) != EOF)
{
D(("c = '%lc'",c));
if(tolower(c) == infinity_string[infinity_match])
{
SHOWVALUE(infinity_match);
nan_match = 0;
chars_read_so_far[num_chars_read_so_far++] = c;
if(maximum_field_width > 0)
maximum_field_width--;
/* Did we match the complete word? */
infinity_match++;
if(infinity_match == sizeof(infinity_string)-1)
{
SHOWMSG("we have a match for infinity");
break;
}
}
else if (infinity_match == 3) /* Did we match the "inf" of "infinity"? */
{
SHOWVALUE(infinity_match);
nan_match = 0;
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
SHOWMSG("we have a match for inf");
break;
}
else if (tolower(c) == infinity_nan[nan_match])
{
SHOWVALUE(nan_match);
infinity_match = 0;
chars_read_so_far[num_chars_read_so_far++] = c;
if(maximum_field_width > 0)
maximum_field_width--;
/* Did we match the complete word? */
nan_match++;
if(nan_match == sizeof(infinity_nan)-1)
{
SHOWMSG("we have a match for nan");
/* Check for the () to follow the nan. */
if(maximum_field_width != 0 && num_chars_read_so_far < sizeof(chars_read_so_far) && (c = __getc(stream)) != EOF)
{
/* Is this the opening parenthesis of the "nan()" keyword? */
if(c == '(')
{
SHOWMSG("there's something following the nan");
nan_match++;
if(maximum_field_width > 0)
maximum_field_width--;
/* Look for the closing parenthesis. */
while(maximum_field_width != 0 && (c = __getc(stream)) != EOF)
{
nan_match++;
if(maximum_field_width > 0)
maximum_field_width--;
if(c == ')')
break;
}
}
else
{
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
}
}
break;
}
}
else
{
SHOWMSG("we didn't find a match for infinity/nan");
nan_match = infinity_match = 0;
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
maximum_field_width += num_chars_read_so_far;
/* Let's try our best here... */
while(num_chars_read_so_far > 0)
ungetc(chars_read_so_far[--num_chars_read_so_far],stream);
break;
}
}
SHOWVALUE(infinity_match);
if(infinity_match >= 3)
{
SHOWMSG("infinity");
sum = __inf();
total_num_chars_read = num_chars_processed = infinity_match;
}
else if (nan_match >= 3)
{
SHOWMSG("not a number");
sum = nan(NULL);
total_num_chars_read = num_chars_processed = nan_match;
}
}
/* If we didn't find a keyword above, look for digits. */
if(num_chars_processed == 0)
{
int radix = 10;
/* Check if there's a hex prefix introducing this number. */
if(maximum_field_width != 0 && (c = __getc(stream)) != EOF)
{
D(("c = '%lc'",c));
if(c == '0')
{
/* Use the leading zero as is. */
total_num_chars_read++;
num_chars_processed++;
if(maximum_field_width > 0)
maximum_field_width--;
if(maximum_field_width != 0 && (c = __getc(stream)) != EOF)
{
if(tolower(c) == 'x')
{
SHOWMSG("found the 0x prefix; setting radix to 16");
/* The floating point number will be encoded
in hexadecimal/binary notation. */
radix = 16;
total_num_chars_read++;
num_chars_processed++;
if(maximum_field_width > 0)
maximum_field_width--;
}
else
{
/* Put this back. */
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
}
}
}
else
{
/* Put this back. */
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
}
}
if(maximum_field_width != 0)
{
int digit;
while(maximum_field_width != 0 && (c = __getc(stream)) != EOF)
{
/* Is this a digit? */
if('0' <= c && c <= '9')
digit = c - '0';
else if ('a' <= c && c <= 'f')
digit = c - 'a' + 10;
else if ('A' <= c && c <= 'F')
digit = c - 'A' + 10;
else
digit = radix;
if(digit < radix)
{
D(("got another digit '%lc'",c));
new_sum = (radix * sum) + digit;
if(new_sum < sum) /* overflow? */
{
/* Put this back. */
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
break;
}
sum = new_sum;
}
else
{
D(("'%lc' is not a digit",c));
/* It's not a digit; reread this later. */
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
break;
}
total_num_chars_read++;
num_chars_processed++;
if(maximum_field_width > 0)
maximum_field_width--;
}
if(c == EOF && num_chars_processed == 0 && num_conversions == 0)
goto out;
}
if(maximum_field_width != 0)
{
SHOWMSG("looking for decimal point");
c = __getc(stream);
if(c != EOF)
{
D(("c = '%lc'",c));
__locale_lock();
/* Did we find the decimal point? We accept both the
* locale configured decimal point and the plain old
* dot.
*/
if(__locale_table[LC_NUMERIC] != NULL)
{
const unsigned char * point;
point = (const unsigned char *)__locale_table[LC_NUMERIC]->loc_DecimalPoint;
if(c == (*point) || c == '.')
{
SHOWMSG("found a decimal point");
decimal_point_matches = TRUE;
}
else
{
D(("'%lc' is not a decimal point",c));
}
}
else
{
if(c == '.')
{
SHOWMSG("found a decimal point");
decimal_point_matches = TRUE;
}
else
{
D(("'%lc' is not a decimal point",c));
}
}
__locale_unlock();
if(decimal_point_matches)
{
total_num_chars_read++;
if(maximum_field_width > 0)
maximum_field_width--;
}
else if ((radix == 10 && (c == 'e' || c == 'E')) ||
(radix == 16 && (c == 'p' || c == 'P')))
{
SHOWMSG("found an exponent specifier");
total_num_chars_read++;
have_exponent = TRUE;
if(maximum_field_width > 0)
maximum_field_width--;
}
else
{
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
}
}
if(decimal_point_matches)
{
double multiplier = 1.0 / radix;
int digit;
SHOWMSG("found a decimal point");
num_chars_processed++;
/* Process all digits following the decimal point. */
while(maximum_field_width != 0 && (c = __getc(stream)) != EOF)
{
/* Is this a digit? */
if('0' <= c && c <= '9')
digit = c - '0';
else if ('a' <= c && c <= 'f')
digit = c - 'a' + 10;
else if ('A' <= c && c <= 'F')
digit = c - 'A' + 10;
else
digit = radix;
if(digit < radix)
{
D(("got another digit '%lc'",c));
if(multiplier != 0.0)
{
new_sum = sum + digit * multiplier;
if(new_sum < sum) /* overflow? */
{
SHOWMSG("got an overflow");
/* Put this back. */
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
break;
}
sum = new_sum;
multiplier = multiplier / radix;
}
total_num_chars_read++;
num_chars_processed++;
if(maximum_field_width > 0)
maximum_field_width--;
}
else if ((radix == 10 && (c == 'e' || c == 'E')) ||
(radix == 16 && (c == 'p' || c == 'P')))
{
SHOWMSG("found an exponent specifier");
total_num_chars_read++;
num_chars_processed++;
if(maximum_field_width > 0)
maximum_field_width--;
have_exponent = TRUE;
break;
}
else
{
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
break;
}
}
}
if(have_exponent)
{
BOOL exponent_is_negative = FALSE;
int new_exponent;
int exponent = 0;
int exponent_radix;
SHOWMSG("processing exponent");
/* The exponent may be a binary number. */
if(radix == 16)
exponent_radix = 2;
else
exponent_radix = 10;
if(maximum_field_width != 0)
{
c = __getc(stream);
if(c != EOF)
{
int digit;
D(("c = '%lc'",c));
/* Skip the sign. */
if(c == '-')
{
exponent_is_negative = TRUE;
total_num_chars_read++;
num_chars_processed++;
if(maximum_field_width > 0)
maximum_field_width--;
}
else if (c == '+')
{
total_num_chars_read++;
num_chars_processed++;
if(maximum_field_width > 0)
maximum_field_width--;
}
else
{
/* It's not a sign; reread this later. */
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
}
while(maximum_field_width != 0 && (c = __getc(stream)) != EOF)
{
if('0' <= c && c <= '9')
digit = c - '0';
else
digit = exponent_radix;
if(digit < exponent_radix)
{
D(("got another digit '%lc'",c));
new_exponent = (exponent_radix * exponent) + digit;
if(new_exponent < exponent) /* overflow? */
{
SHOWMSG("overflow!");
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
break;
}
exponent = new_exponent;
total_num_chars_read++;
num_chars_processed++;
if(maximum_field_width > 0)
maximum_field_width--;
}
else
{
SHOWMSG("invalid digit");
if(ungetc(c,stream) == EOF)
{
SHOWMSG("couldn't push this character back");
goto out;
}
break;
}
}
/* If the exponent is valid, scale the number
* accordingly.
*/
if(exponent != 0)
{
if(exponent_is_negative)
{
double divisor;
/* A negative exponent means division. */
divisor = pow((double)radix,(double)exponent);
if(divisor != 0.0)
sum = sum / divisor;
}
else
{
/* A positive exponent means multiplication. */
new_sum = sum * pow((double)radix,(double)exponent);
if(new_sum >= sum)
sum = new_sum;
else
sum = __get_huge_val();
}
}
}
}
}
}
}
SHOWMSG("conversion finished..");
if(num_chars_processed > 0)
{
if(NOT assignment_suppressed)
{
if(is_negative)
sum = (-sum);
if(parameter_size == parameter_size_default)
{
*((float *)next_parameter) = sum;
}
else if (parameter_size == parameter_size_long)
{
*((double *)next_parameter) = sum;
}
else
{
*((__long_double_t *)next_parameter) = sum;
}
num_assignments++;
D(("number of assignments = %ld\n", num_assignments));
}
num_conversions++;
D(("number of conversions = %ld\n", num_conversions));
}
else
{
SHOWMSG("no characters were actually processed");
}
}
#else
{
if(NOT assignment_suppressed)
{
void * next_parameter;
assert( arg != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(arg == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
if(parameter_size == parameter_size_default)
{
SHOWMSG("short format (float)");
next_parameter = va_arg(arg,float *);
}
else if (parameter_size == parameter_size_long)
{
SHOWMSG("long format (double)");
next_parameter = va_arg(arg,double *);
}
else
{
SHOWMSG("extended format (long double)");
next_parameter = va_arg(arg,__long_double_t *);
}
assert( next_parameter != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(next_parameter == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
}
}
#endif /* FLOATING_POINT_SUPPORT */
}
else if (conversion_type == 'd' ||
conversion_type == 'i' ||
conversion_type == 'o' ||
conversion_type == 'u' ||
conversion_type == 'x')
{
#if defined(USE_64_BIT_INTS) && defined(__GNUC__)
long long next_sum;
long long sum = 0;
#else
int next_sum;
int sum = 0;
#endif /* __GNUC__ */
BOOL is_negative = FALSE;
int radix;
void * next_parameter = NULL;
/* We boldly try to initialize the parameter to a well-
defined value before we begin the conversion. */
if(NOT assignment_suppressed)
{
assert( arg != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(arg == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
if(parameter_size == parameter_size_short)
{
next_parameter = va_arg(arg,short *);
}
else if (parameter_size == parameter_size_byte)
{
next_parameter = va_arg(arg,char *);
}
else
{
#if defined(USE_64_BIT_INTS) && defined(__GNUC__)
{
if(parameter_size == parameter_size_long_long || parameter_size == parameter_size_intmax_t)
next_parameter = va_arg(arg,long long *);
else
next_parameter = va_arg(arg,int *);
}
#else
{
next_parameter = va_arg(arg,int *);
}
#endif /* __GNUC__ */
}
assert( next_parameter != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(next_parameter == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
if(parameter_size == parameter_size_short)
{
*((short *)next_parameter) = 0;
}
else if (parameter_size == parameter_size_byte)
{
*((char *)next_parameter) = 0;
}
else
{
#if defined(USE_64_BIT_INTS) && defined(__GNUC__)
{
if(parameter_size == parameter_size_long_long || parameter_size == parameter_size_intmax_t)
*((long long *)next_parameter) = 0;
else
*((int *)next_parameter) = 0;
}
#else
{
*((int *)next_parameter) = 0;
}
#endif /* __GNUC__ */
}
}
/* Chose a base according to the conversion to be
expected. For the 'x' and 'i' we examine the
incoming data rather than commit ourselves to
a peculiar data format now. */
if(conversion_type == 'd' || conversion_type == 'u')
radix = 10;
else if (conversion_type == 'o')
radix = 8;
else
radix = 0;
if(maximum_field_width != 0)
{
c = __getc(stream);
if(c != EOF)
{
/* Skip the sign. */
if(c == '-')
{
is_negative = TRUE;
total_num_chars_read++;
if(maximum_field_width > 0)
maximum_field_width--;
}
else if (c == '+')
{
total_num_chars_read++;
if(maximum_field_width > 0)
maximum_field_width--;
}
else if (c == '0')
{
/* Keep the leading zero. */
total_num_chars_read++;
num_chars_processed++;
if(maximum_field_width > 0)
maximum_field_width--;
if(radix == 0 && maximum_field_width != 0)
{
/* This could be an octal number, the
* '0x' prefix or just a zero.
*/
c = __getc(stream);
/* This takes care of the '0x' prefix for hexadecimal
numbers ('%x') and also picks the right type of
data for the '%i' type. */
if ((c == 'x' || c == 'X') && (conversion_type == 'x' || conversion_type == 'i'))
{
/* It's the hex prefix. */
radix = 16;
/* The preceding '0' was part of the
hex prefix. So we don't really know
the number yet. */
num_chars_processed--;
total_num_chars_read++;
if(maximum_field_width > 0)
maximum_field_width--;
}
else if (isdigit(c) && (conversion_type == 'i')) /* This could be the octal prefix for the '%i' format. */
{
/* The preceding '0' was part of the
octal prefix. So we don't really know
the number yet. */
num_chars_processed--;
/* It's an octal number. */
radix = 8;
/* Process the rest later. */
if(ungetc(c,stream) == EOF)
goto out;
}
else if (c != EOF)
{
/* It's something else; put it back. */
if(ungetc(c,stream) == EOF)
goto out;
}
}
}
else
{
if(ungetc(c,stream) == EOF)
goto out;
}
}
}
/* Pick a base if none has been chosen yet. */
if(radix == 0)
{
if(conversion_type == 'x')
radix = 16;
else
radix = 10;
}
if(maximum_field_width != 0)
{
int digit;
while(maximum_field_width != 0 && (c = __getc(stream)) != EOF)
{
/* Is this a digit or hexadecimal value? */
if('0' <= c && c <= '9')
digit = c - '0';
else if ('a' <= c && c <= 'f')
digit = c - 'a' + 10;
else if ('A' <= c && c <= 'F')
digit = c - 'A' + 10;
else
digit = radix;
/* Is this a valid digit? */
if(digit >= radix)
{
SHOWMSG("digit is larger than radix");
if(ungetc(c,stream) == EOF)
goto out;
break;
}
next_sum = (radix * sum) + digit;
#if defined(USE_64_BIT_INTS) && defined(__GNUC__)
{
if(parameter_size == parameter_size_long_long || parameter_size == parameter_size_intmax_t)
{
if((unsigned long long)next_sum < (unsigned long long)sum) /* overflow? */
{
SHOWMSG("overflow!");
/* Put this back. */
if(ungetc(c,stream) == EOF)
goto out;
break;
}
}
else if ((unsigned int)next_sum < (unsigned int)sum) /* overflow? */
{
SHOWMSG("overflow!");
/* Put this back. */
if(ungetc(c,stream) == EOF)
goto out;
break;
}
}
#else
{
if((unsigned int)next_sum < (unsigned int)sum) /* overflow? */
{
SHOWMSG("overflow!");
/* Put this back. */
if(ungetc(c,stream) == EOF)
goto out;
break;
}
}
#endif /* __GNUC__ */
total_num_chars_read++;
num_chars_processed++;
sum = next_sum;
if(maximum_field_width > 0)
maximum_field_width--;
}
if(c == EOF && num_chars_processed == 0 && num_conversions == 0)
goto out;
}
if(num_chars_processed > 0)
{
if(NOT assignment_suppressed)
{
if(is_negative)
sum = (-sum);
if(parameter_size == parameter_size_short)
{
*((short *)next_parameter) = sum;
}
else if (parameter_size == parameter_size_byte)
{
*((char *)next_parameter) = sum;
}
else
{
#if defined(USE_64_BIT_INTS) && defined(__GNUC__)
{
if(parameter_size == parameter_size_long_long || parameter_size == parameter_size_intmax_t)
*((long long *)next_parameter) = sum;
else
*((int *)next_parameter) = sum;
}
#else
{
*((int *)next_parameter) = sum;
}
#endif /* __GNUC__ */
}
num_assignments++;
}
num_conversions++;
}
}
else if (conversion_type == 's')
{
char * s_ptr;
if(NOT assignment_suppressed)
{
assert( arg != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(arg == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
s_ptr = va_arg(arg,char *);
assert( s_ptr != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(s_ptr == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
}
else
{
s_ptr = NULL;
}
/* Try to NUL terminate this even in case of failure. */
if(NOT assignment_suppressed)
(*s_ptr) = '\0';
if(maximum_field_width != 0)
{
while(maximum_field_width != 0 && (c = __getc(stream)) != EOF)
{
/* Blank spaces stop the conversion process. */
if(isspace(c))
{
if(ungetc(c,stream) == EOF)
goto out;
break;
}
total_num_chars_read++;
num_chars_processed++;
if(NOT assignment_suppressed)
{
(*s_ptr++) = c;
(*s_ptr) = '\0'; /* Try to NUL terminate this even in case of failure. */
}
if(maximum_field_width > 0)
maximum_field_width--;
}
/* The conversion is considered to have failed if an EOF was
encountered before any non-whitespace characters could be
converted. */
if(c == EOF && num_chars_processed == 0 && num_conversions == 0)
goto out;
}
if(NOT assignment_suppressed)
num_assignments++;
num_conversions++;
}
else if (conversion_type == 'n')
{
if(NOT assignment_suppressed)
{
assert( arg != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(arg == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
if(parameter_size == parameter_size_short)
{
short * short_ptr;
short_ptr = va_arg(arg,short *);
assert( short_ptr != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(short_ptr == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
(*short_ptr) = total_num_chars_read;
}
else if (parameter_size == parameter_size_byte)
{
char * byte_ptr;
byte_ptr = va_arg(arg,char *);
assert( byte_ptr != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(byte_ptr == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
(*byte_ptr) = total_num_chars_read;
}
else
{
#if defined(USE_64_BIT_INTS) && defined(__GNUC__)
{
if(parameter_size == parameter_size_long_long || parameter_size == parameter_size_intmax_t)
{
long long * int_ptr;
int_ptr = va_arg(arg,long long *);
assert( int_ptr != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(int_ptr == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
(*int_ptr) = total_num_chars_read;
}
else
{
int * int_ptr;
int_ptr = va_arg(arg,int *);
assert( int_ptr != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(int_ptr == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
(*int_ptr) = total_num_chars_read;
}
}
#else
{
int * int_ptr;
int_ptr = va_arg(arg,int *);
assert( int_ptr != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(int_ptr == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
(*int_ptr) = total_num_chars_read;
}
#endif /* __GNUC__ */
}
}
}
else if (c == '%')
{
int d;
d = __getc(stream);
if(d == EOF)
{
SHOWMSG("end of file");
/* Hit the end of the stream. */
if(num_conversions == 0)
goto out;
break;
}
else if (c == d)
{
total_num_chars_read++;
}
else
{
/* This is not what we expected. We're finished. */
if(ungetc(d,stream) == EOF)
goto out;
break;
}
}
else if (c == '[')
{
char * s_ptr;
char set[256];
const unsigned char * scanset;
size_t scanset_length,i;
int pick;
if(NOT assignment_suppressed)
{
assert( arg != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(arg == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
s_ptr = va_arg(arg,char *);
assert( s_ptr != NULL );
#if defined(CHECK_FOR_NULL_POINTERS)
{
if(s_ptr == NULL)
{
__set_errno(EFAULT);
goto out;
}
}
#endif /* CHECK_FOR_NULL_POINTERS */
}
else
{
s_ptr = NULL;
}
c = (*format);
if(c == '^')
{
/* Accept only characters which are not found in the range. */
pick = 0;
format++;
}
else
{
/* Accept only characters which are found in the range. */
pick = 1;
}
memset(set,1 - pick,sizeof(set));
/* If the ']' character introduces the range, then
* it is not considered the range termination character.
*/
c = (*format);
if(c == ']')
{
set[c] = pick;
format++;
}
/* Figure out how many characters are in the scanset. */
scanset = (const unsigned char *)format;
for(scanset_length = 0 ; scanset[scanset_length] != '\0' && scanset[scanset_length] != ']' ; scanset_length++)
format++;
/* We already skipped everything but the right bracket. */
if((*format) == ']')
format++;
/* Now have a look at the specification. We support a non-standard
scanf() family feature which permits you to specify ranges of
characters rather than spelling out each character included in
the range. */
for(i = 0 ; i < scanset_length ; i++)
{
c = scanset[i];
/* Could this be a range? It's not a range if it
is the first or the last character in the
specification. */
if(c == '-' && 0 < i && i < scanset_length - 1)
{
int first,last,j;
/* Pick the first and the last character in
the range, e.g. for "%[A-Z]" the first would
be the 'A' and the 'Z' would be the last. */
first = scanset[i-1];
last = scanset[i+1];
/* Everything in the scanset now
goes into the set. */
for(j = first ; j <= last ; j++)
set[j] = pick;
/* Skip the character which marked the
end of the range and resume scanning. */
i++;
continue;
}
assert( 0 <= c && c <= 255 );
set[c] = pick;
}
/* Try to NUL terminate this even in case of failure. */
if(NOT assignment_suppressed)
(*s_ptr) = '\0';
if(maximum_field_width != 0)
{
while(maximum_field_width != 0 && (c = __getc(stream)) != EOF)
{
assert( 0 <= c && c <= 255 );
/* If the character is not part of the
* range we stop further processing.
*/
if(set[c] == 0)
{
if(ungetc(c,stream) == EOF)
goto out;
break;
}
if(NOT assignment_suppressed)
{
(*s_ptr++) = c;
(*s_ptr) = '\0'; /* Try to NUL terminate this even in case of failure. */
}
total_num_chars_read++;
num_chars_processed++;
if(maximum_field_width > 0)
maximum_field_width--;
}
if(c == EOF && num_chars_processed == 0 && num_conversions == 0)
goto out;
}
if(num_chars_processed > 0)
{
if(NOT assignment_suppressed)
num_assignments++;
num_conversions++;
}
}
}
result = num_assignments;
out:
funlockfile(stream);
RETURN(result);
return(result);
}