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;
}