adtools/clib2

View on GitHub
library/stdio_vfscanf.c

Summary

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