X-Git-Url: https://pd.if.org/git/?a=blobdiff_plain;f=functions%2F_PDCLIB%2Fscan.c;h=b6012611b707b290158d4c3585de5a271149790b;hb=0b5f84a52cf54e88b99a44efc6f5f2c46e9b294d;hp=1502f62ba5cc6c489fcc2e94b046cfbefea6987e;hpb=5bcc00c1b85feff1996e3f34a1a9ebf3b624161e;p=pdclib diff --git a/functions/_PDCLIB/scan.c b/functions/_PDCLIB/scan.c index 1502f62..b601261 100644 --- a/functions/_PDCLIB/scan.c +++ b/functions/_PDCLIB/scan.c @@ -10,6 +10,10 @@ #include #include #include +#include +#include +#include +#include /* Using an integer's bits as flags for both the conversion flags and length modifiers. @@ -27,51 +31,66 @@ #define E_unsigned 1<<16 -static bool MATCH( const char x, struct _PDCLIB_status_t * status ) +#define ASSIGN( case_cond, type ) \ + case case_cond: \ + *( va_arg( status->arg, type * ) ) = (type)( value * sign ); \ + break + + +static int GET( struct _PDCLIB_status_t * status ) { + ++(status->i); + ++(status->this); if ( status->stream != NULL ) { - /* Matching against stream */ - if ( status->stream->buffer[ status->stream->bufidx ] == x ) - { - getc( status->stream ); - ++(status->i); - return true; - } - else - { - return false; - } + return getc( status->stream ); } else { - /* Matching against string */ - if ( *(status->s) == x ) - { - ++(status->s); - ++(status->i); - return true; - } - else - { - return false; - } + return *((status->s)++); } } +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 ) { + /* generic input character */ + int rc; const char * orig_spec = spec; if ( *(++spec) == '%' ) { /* %% -> match single '%' */ - MATCH( *spec, status ); - return ++spec; + rc = GET( status ); + switch ( rc ) + { + case EOF: + /* matching failure */ + return NULL; + case '%': + return ++spec; + default: + UNGET( rc, status ); + break; + } } /* Initializing status structure */ status->flags = 0; - status->base = 0; + status->base = -1; status->this = 0; status->width = 0; status->prec = 0; @@ -144,13 +163,17 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) } /* Conversion specifier */ + + /* whether valid input had been parsed */ + bool value_parsed = false; + switch ( *spec ) { case 'd': status->base = 10; break; case 'i': - /* status->base = 0; */ + status->base = 0; break; case 'o': status->base = 8; @@ -174,13 +197,65 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) case 'A': break; case 'c': - /* TODO */ - break; + { + char * c = va_arg( status->arg, char * ); + if ( status->width == SIZE_MAX ) + { + status->width = 1; + } + while ( ( status->this < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + *(c++) = rc; + value_parsed = true; + } + if ( value_parsed ) + { + ++status->n; + return ++spec; + } + else + { + return NULL; + } + } case 's': - /* TODO */ - break; + { + char * c = va_arg( status->arg, char * ); + while ( ( status->this < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + if ( isspace( rc ) ) + { + if ( value_parsed ) + { + *c = '\0'; + return spec; + } + else + { + --(status->this); + } + } + else + { + value_parsed = true; + *(c++) = rc; + } + } + /* width or input exhausted */ + if ( value_parsed ) + { + *c = '\0'; + ++status->n; + return ++spec; + } + else + { + return NULL; + } + } case 'p': - /* TODO */ status->base = 16; status->flags |= E_unsigned; break; @@ -194,16 +269,132 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) /* No conversion specifier. Bad conversion. */ return orig_spec; } - /* TODO: Actual conversions */ + + if ( status->base != -1 ) + { + /* integer conversion */ + uintmax_t value = 0; + bool prefix_parsed = false; + int sign = 0; + while ( ( status->this < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + if ( ! sign ) + { + switch ( rc ) + { + case '-': + sign = -1; + break; + case '+': + sign = 1; + break; + default: + sign = 1; + UNGET( rc, status ); + break; + } + } + else if ( ! prefix_parsed ) + { + prefix_parsed = true; + if ( rc != '0' ) + { + if ( status->base == 0 ) + { + status->base = 10; + } + UNGET( rc, status ); + } + else + { + if ( ( status->this < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + if ( tolower( rc ) == 'x' ) + { + if ( ( status->base == 0 ) || + ( status->base == 16 ) ) + { + status->base = 16; + } + else + { + UNGET( rc, status ); + value_parsed = true; + } + } + else + { + UNGET( rc, status ); + if ( status->base == 0 ) + { + status->base = 8; + } + value_parsed = true; + } + } + } + } + else + { + char * digitptr = memchr( _PDCLIB_digits, tolower( rc ), status->base ); + if ( digitptr == NULL ) + { + /* end of input item */ + break; + } + value *= status->base; + value += digitptr - _PDCLIB_digits; + value_parsed = true; + } + } + /* width exceeded, EOF, read error, non-matching character */ + if ( ! value_parsed ) + { + /* matching error */ + return NULL; + } + switch ( status->flags & ( E_char | E_short | E_long | E_llong | + E_intmax | E_size | E_ptrdiff | + E_unsigned ) ) + { + ASSIGN( E_char, char ); + ASSIGN( E_char | E_unsigned, unsigned char ); + ASSIGN( E_short, short ); + ASSIGN( E_short | E_unsigned, unsigned short ); + ASSIGN( 0, int ); + ASSIGN( E_unsigned, unsigned int ); + ASSIGN( E_long, long ); + ASSIGN( E_long | E_unsigned, unsigned long ); + ASSIGN( E_llong, long long ); + ASSIGN( E_llong | E_unsigned, unsigned long long ); + ASSIGN( E_intmax, intmax_t ); + ASSIGN( E_intmax | E_unsigned, uintmax_t ); + ASSIGN( E_size, size_t ); + /* ASSIGN( E_size | E_unsigned, unsigned size_t ); */ + ASSIGN( E_ptrdiff, ptrdiff_t ); + /* ASSIGN( E_ptrdiff | E_unsigned, unsigned ptrdiff_t ); */ + default: + puts( "UNSUPPORTED SCANF FLAG COMBINATION" ); + return NULL; + } + return ++spec; + } + /* TODO: Floats. */ return NULL; } + #ifdef TEST #include <_PDCLIB_test.h> +#include + + int main( void ) { - TESTCASE( NO_TESTDRIVER ); + /* Testing covered by fscanf.c */ return TEST_RESULTS; }