/* * $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 * 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); }