X-Git-Url: https://pd.if.org/git/?a=blobdiff_plain;f=functions%2F_PDCLIB%2Fscan.c;h=523b0c81449c5846f50868f09118fcb445c69dba;hb=7f5e4748e1e44b136cbeddc70601bbf2a5a56e1b;hp=d12e8844a06e9eebc712cc68f522a5afbdc026e0;hpb=68525aefa7b7b1f2f769e287e1f2f3a4585005ff;p=pdclib diff --git a/functions/_PDCLIB/scan.c b/functions/_PDCLIB/scan.c index d12e884..523b0c8 100644 --- a/functions/_PDCLIB/scan.c +++ b/functions/_PDCLIB/scan.c @@ -7,10 +7,268 @@ */ #include +#include +#include +#include +#include + +/* Using an integer's bits as flags for both the conversion flags and length + modifiers. +*/ +#define E_suppressed 1<<0 +#define E_char 1<<6 +#define E_short 1<<7 +#define E_long 1<<8 +#define E_llong 1<<9 +#define E_intmax 1<<10 +#define E_size 1<<11 +#define E_ptrdiff 1<<12 +#define E_intptr 1<<13 +#define E_ldouble 1<<14 +#define E_unsigned 1<<16 + + +#define MATCH_FAIL -1 +#define MATCH_ERROR -2 + +static int MATCH( int c, struct _PDCLIB_status_t * status ) +{ + if ( status->stream != NULL ) + { + if ( ! _PDCLIB_prepread( status->stream ) ) + { + return MATCH_ERROR; + } + if ( tolower( status->stream->buffer[ status->stream->bufidx ] ) == c ) + { + /* recycling parameter */ + c = getc( status->stream ); + } + else + { + return MATCH_FAIL; + } + } + else + { + if ( tolower( *(status->s) ) == c ) + { + /* recycling parameter */ + c = *((status->s)++); /* TODO: \0 */ + } + else + { + return MATCH_FAIL; + } + } + ++(status->i); + ++(status->this); + return c; +} + + +static void UNGET( int c, struct _PDCLIB_status_t * status ) +{ + if ( status->stream != NULL ) + { + ungetc( c, status->stream ); /* TODO: Error? */ + } + else + { + *(--(status->s)) = c; + } + --(status->i); + --(status->this); +} + const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) { - return ++spec; + const char * orig_spec = spec; + if ( *(++spec) == '%' ) + { + /* %% -> match single '%' */ + MATCH( *spec, status ); + return ++spec; + } + /* Initializing status structure */ + status->flags = 0; + status->base = -1; + status->this = 0; + status->width = 0; + status->prec = 0; + + /* '*' suppresses assigning parsed value to variable */ + if ( *spec == '*' ) + { + status->flags |= E_suppressed; + ++spec; + } + + /* If a width is given, strtol() will return its value. If not given, + strtol() will return zero. In both cases, endptr will point to the + rest of the conversion specifier - just what we need. + */ + status->width = (int)strtol( spec, (char**)&spec, 10 ); + + /* Optional length modifier + We step one character ahead in any case, and step back only if we find + there has been no length modifier (or step ahead another character if it + has been "hh" or "ll"). + */ + switch ( *(spec++) ) + { + case 'h': + if ( *spec == 'h' ) + { + /* hh -> char */ + status->flags |= E_char; + ++spec; + } + else + { + /* h -> short */ + status->flags |= E_short; + } + break; + case 'l': + if ( *spec == 'l' ) + { + /* ll -> long long */ + status->flags |= E_llong; + ++spec; + } + else + { + /* l -> long */ + status->flags |= E_long; + } + break; + case 'j': + /* j -> intmax_t, which might or might not be long long */ + status->flags |= E_intmax; + break; + case 'z': + /* z -> size_t, which might or might not be unsigned int */ + status->flags |= E_size; + break; + case 't': + /* t -> ptrdiff_t, which might or might not be long */ + status->flags |= E_ptrdiff; + break; + case 'L': + /* L -> long double */ + status->flags |= E_ldouble; + break; + default: + --spec; + break; + } + + /* Conversion specifier */ + switch ( *spec ) + { + case 'd': + status->base = 10; + break; + case 'i': + status->base = 0; + break; + case 'o': + status->base = 8; + status->flags |= E_unsigned; + break; + case 'u': + status->base = 10; + status->flags |= E_unsigned; + break; + case 'x': + status->base = 16; + status->flags |= E_unsigned; + break; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': + break; + case 'c': + /* TODO */ + break; + case 's': + /* TODO */ + break; + case 'p': + /* TODO */ + status->base = 16; + status->flags |= E_unsigned; + break; + case 'n': + { + int * val = va_arg( status->arg, int * ); + *val = status->i; + return ++spec; + } + default: + /* No conversion specifier. Bad conversion. */ + return orig_spec; + } + bool zero = false; + if ( status->base != -1 ) + { + bool value = false; + int rc; + if ( ( rc = MATCH( '0', status ) ) >= 0 ) + { + if ( ( rc = MATCH( 'x', status ) ) >= 0 ) + { + if ( ( status->base == 0 ) || ( status->base == 16 ) ) + { + status->base = 16; + } + else + { + UNGET( rc, status ); + value = true; + } + } + else if ( rc == MATCH_FAIL ) + { + if ( status->base == 0 ) + { + status->base = 8; + } + else + { + value = true; + } + } + else + { + /* TODO: MATCH_ERROR */ + } + } + else if ( rc == MATCH_FAIL ) + { + if ( status->base == 0 ) + { + status->base = 10; + } + } + else + { + /* TODO: MATCH_ERROR */ + } + /* TODO: Integer conversion */ + } + else + { + /* TODO: Float conversions? */ + } + return NULL; } #ifdef TEST