static bool GetFormatMessageArgsA()

in source/shared/FormattedPrint.cpp [1017:1212]


static bool GetFormatMessageArgsA( const char * format, std::vector< vararg_t > * argcache, va_list * Arguments )
{
    if ( NULL == format )
    {
        errno = EINVAL;
        return false;
    }

    const char *p = format;
    char fmt_ch;

    while( '\0' != (fmt_ch = *p++) )
    {
        if ( '%' != fmt_ch )
        {
            // continue to next format spec
        }
        else if ( '0' == *p || '\0' == *p )
        {
            // %0 or null term means end formatting
            break;
        }
        else if ( *p < '1' || '9' < *p )
        {
            // Escaped char, skip and keep going
            ++p;
        }
        else
        {
            // Integer must be [1..99]
            size_t argPos = *p++ - '0';
            if ( '0' <= *p && *p <= '9' )
            {
                argPos *= 10;
                argPos += *p++ - '0';
            }
            assert( 0 < argPos && argPos < 100 );

            if ( argcache->size() < argPos )
            {
                // Haven't processed this arg, yet
                argcache->resize( argPos );
            }

            if ( vararg_t::Unknown == argcache->at(argPos-1).Type() )
            {
                if ( '!' != *p )
                {
                    // Assume %s as per spec
                    argcache->at(argPos-1).SetForPtr();
                }
                else
                {
                    // Step over the initial '!' and process format specification
                    ++p;

                    char ch;
                    int flags = 0;
                    int advance = 0;
                    enum CHARTYPE chclass;
                    enum STATE state = ST_PERCENT;
                    bool found_terminator = false;
                    while ( !found_terminator && ('\0' != (ch = *p++)) )
                    {
                        chclass = GetCharType( ch );
                        state = GetState( chclass, state );

                        switch ( state )
                        {
                        case ST_DOT:
                        case ST_FLAG:
                            break;

                        case ST_WIDTH:
                        case ST_PRECIS:
                            if ( '*' == ch )
                            {
                                argcache->at(argPos-1).SetForInt32();
                                ++argPos;
                                if ( argcache->size() < argPos )
                                {
                                    argcache->resize( argPos );
                                }
                            }
                            break;

                        case ST_SIZE:
                            state = ProcessSize( ch, p, &advance, &flags );
                            p += advance;
                            if ( ST_SIZE != state )
                            {
                                // Size and type flags were inconsistent
                                errno = EINVAL;
                                return false;
                            }
                            break;

                        case ST_TYPE:
                            // Group into 32-bit and 64-bit sized args
                            assert( vararg_t::Unknown == argcache->at(argPos-1).Type() );
                            switch ( ch )
                            {
                            case 'C': // chars
                            case 'c':
                                argcache->at(argPos-1).SetForInt32();
                                break;

                            case 'd': // ints
                            case 'i':
                            case 'u':
                            case 'X':
                            case 'x':
                            case 'o':
                                // INT args
                                if ( (flags & FL_I64) || (flags & FL_LONGLONG) )
                                    argcache->at(argPos-1).SetForInt64();
                                else
                                    argcache->at(argPos-1).SetForInt32();
                                break;

                            case 'S': // strings
                            case 's':
                            case 'p': // pointer
                                argcache->at(argPos-1).SetForPtr();
                                break;

                            case 'E': // doubles (not supported as per spec)
                            case 'e':
                            case 'G':
                            case 'g':
                            case 'A':
                            case 'a':
                            case 'f':
                            default:
                                errno = EINVAL;
                                return false;
                            }
                            break;

                        case ST_NORMAL:
                            if ( '!' == ch )
                            {
                                found_terminator = true;
                                break;
                            }
                            // Fall thru to error, missing terminating '!'

                        default:
                            errno = EINVAL;
                            return false;
                        }
                    }

                    if ( !found_terminator )
                    {
                        // End of string before trailing '!' was found
                        errno = EINVAL;
                        return false;
                    }
                }
            }
        }
    }

    if ( 0 < argcache->size() && NULL == Arguments )
    {
        errno = EINVAL;
        return false;
    }

    // Cache var arg values now that we know the number and sizes
    for ( std::vector< vararg_t >::iterator arg = argcache->begin(); arg != argcache->end(); ++arg )
    {
        if ( vararg_t::Unknown == arg->Type() )
        {
            // Arg not referenced in format string so assume ptr sized.
            // This is a decent assumption since every arg gets ptr-size bytes to ensure alignment
            // of later arg values.  Verified this behavior with both Windows and Linux.
            arg->SetForPtr();
        }

        vararg_t::ArgType_e argtype = arg->Type();
        assert( vararg_t::ShouldBeInt32 == argtype || vararg_t::ShouldBeInt64 == argtype );

        if ( vararg_t::ShouldBeInt32 == argtype )
        {
            arg->Int32Value( (INT)va_arg(*Arguments, INT) );
        }
        else
        {
            arg->Int64Value( (LONGLONG)va_arg(*Arguments, LONGLONG) );
        }
    }

    return true;
}