X-Git-Url: https://pd.if.org/git/?a=blobdiff_plain;f=functions%2F_PDCLIB%2Fscan.c;h=15b1d503c32a5fa9481c48f65d625a08922508bb;hb=31fdebd6d624ffce068548cb4d63f4ec583cec04;hp=0d46f9ecfd53efe0f8a13fdd901487f482198c55;hpb=5125969b57fe4d64874b2633adcf007bda2a1847;p=pdclib diff --git a/functions/_PDCLIB/scan.c b/functions/_PDCLIB/scan.c index 0d46f9e..15b1d50 100644 --- a/functions/_PDCLIB/scan.c +++ b/functions/_PDCLIB/scan.c @@ -32,18 +32,6 @@ #define E_unsigned 1<<16 -/* Helper macro for assigning a readily converted integer value to the correct - parameter type, used in a switch on status->flags (see E_* flags above). - case_cond: combination of the E_* flags above, used for the switch-case - type: integer type, used to get the correct type from the parameter - stack as well as for cast target. -*/ -#define ASSIGN_VALUE_TO( case_cond, type ) \ - case case_cond: \ - *( va_arg( status->arg, type * ) ) = (type)( value * sign ); \ - break - - /* Helper function to get a character from the string or stream, whatever is used for input. When reading from a string, returns EOF on end-of-string so that handling of the return value can be uniform for both streams and @@ -63,7 +51,7 @@ static int GET( struct _PDCLIB_status_t * status ) if ( rc != EOF ) { ++(status->i); - ++(status->this); + ++(status->current); } return rc; } @@ -83,7 +71,45 @@ static void UNGET( int c, struct _PDCLIB_status_t * status ) --(status->s); } --(status->i); - --(status->this); + --(status->current); +} + + +/* Helper function to check if a character is part of a given scanset */ +static bool IN_SCANSET( const char * scanlist, const char * end_scanlist, int rc ) +{ + // SOLAR + int previous = -1; + while ( scanlist != end_scanlist ) + { + if ( ( *scanlist == '-' ) && ( previous != -1 ) ) + { + /* possible scangroup ("a-z") */ + if ( ++scanlist == end_scanlist ) + { + /* '-' at end of scanlist does not describe a scangroup */ + return rc == '-'; + } + while ( ++previous <= (unsigned char)*scanlist ) + { + if ( previous == rc ) + { + return true; + } + } + previous = -1; + } + else + { + /* not a scangroup, check verbatim */ + if ( rc == (unsigned char)*scanlist ) + { + return true; + } + previous = (unsigned char)(*scanlist++); + } + } + return false; } @@ -115,7 +141,7 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) /* Initializing status structure */ status->flags = 0; status->base = -1; - status->this = 0; + status->current = 0; status->width = 0; status->prec = 0; @@ -234,7 +260,7 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) status->width = 1; } /* reading until width reached or input exhausted */ - while ( ( status->this < status->width ) && + while ( ( status->current < status->width ) && ( ( rc = GET( status ) ) != EOF ) ) { *(c++) = rc; @@ -259,25 +285,28 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) case 's': { char * c = va_arg( status->arg, char * ); - while ( ( status->this < status->width ) && + while ( ( status->current < status->width ) && ( ( rc = GET( status ) ) != EOF ) ) { if ( isspace( rc ) ) { + UNGET( rc, status ); if ( value_parsed ) { /* matching sequence terminated by whitespace */ *c = '\0'; - return spec; + ++status->n; + return ++spec; } else { - /* leading whitespace not counted against width */ - --(status->this); + /* matching error */ + return NULL; } } else { + /* match */ value_parsed = true; *(c++) = rc; } @@ -299,6 +328,60 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) return NULL; } } + case '[': + { + const char * endspec = spec; + bool negative_scanlist = false; + if ( *(++endspec) == '^' ) + { + negative_scanlist = true; + ++endspec; + } + spec = endspec; + do + { + // TODO: This can run beyond a malformed format string + ++endspec; + } while ( *endspec != ']' ); + // read according to scanlist, equiv. to %s above + char * c = va_arg( status->arg, char * ); + while ( ( status->current < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + if ( negative_scanlist ) + { + if ( IN_SCANSET( spec, endspec, rc ) ) + { + UNGET( rc, status ); + break; + } + } + else + { + if ( ! IN_SCANSET( spec, endspec, rc ) ) + { + UNGET( rc, status ); + break; + } + } + value_parsed = true; + *(c++) = rc; + } + if ( value_parsed ) + { + *c = '\0'; + ++status->n; + return ++endspec; + } + else + { + if ( rc == EOF ) + { + status->n = -1; + } + return NULL; + } + } case 'p': status->base = 16; status->flags |= E_unsigned; @@ -318,11 +401,9 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) { /* integer conversion */ uintmax_t value = 0; /* absolute value read */ - uintmax_t limit; /* max. value allowed */ - uintmax_t threshold; /* overflow threshold */ bool prefix_parsed = false; int sign = 0; - while ( ( status->this < status->width ) && + while ( ( status->current < status->width ) && ( ( rc = GET( status ) ) != EOF ) ) { if ( isspace( rc ) ) @@ -336,7 +417,7 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) else { /* leading whitespace not counted against width */ - status->this--; + status->current--; } } else if ( ! sign ) @@ -356,53 +437,6 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) UNGET( rc, status ); break; } - switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax | E_size | E_ptrdiff | E_unsigned ) ) - { - case E_char: - limit = ( sign == 1 ) ? CHAR_MAX : ( CHAR_MIN * sign ); - break; - case E_char | E_unsigned: - limit = UCHAR_MAX; - break; - case E_short: - limit = ( sign == 1 ) ? SHRT_MAX : ( SHRT_MIN * sign ); - break; - case E_short | E_unsigned: - limit = USHRT_MAX; - break; - case E_long: - limit = ( sign == 1 ) ? LONG_MAX : ( LONG_MIN * sign ); - break; - case E_long | E_unsigned: - limit = ULONG_MAX; - break; - case E_llong: - limit = ( sign == 1 ) ? LLONG_MAX : ( LLONG_MIN * sign ); - break; - case E_llong | E_unsigned: - limit = ULLONG_MAX; - break; - case E_intmax: - limit = ( sign == 1 ) ? INTMAX_MAX : ( INTMAX_MIN * sign ); - break; - case E_intmax | E_unsigned: - limit = UINTMAX_MAX; - break; - case E_size: - case E_size | E_unsigned: - limit = SIZE_MAX; - break; - case E_ptrdiff: - case E_ptrdiff | E_unsigned: - limit = ( sign == 1 ) ? PTRDIFF_MAX : ( PTRDIFF_MIN * sign ); - break; - case E_unsigned: - limit = UINT_MAX; - break; - default: - limit = ( sign == 1 ) ? INT_MAX : ( INT_MIN * sign ); - break; - } } else if ( ! prefix_parsed ) { @@ -421,7 +455,7 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) { /* starts with zero, so it might be a prefix. */ /* check what follows next (might be 0x...) */ - if ( ( status->this < status->width ) && + if ( ( status->current < status->width ) && ( ( rc = GET( status ) ) != EOF ) ) { if ( tolower( rc ) == 'x' ) @@ -468,20 +502,9 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) UNGET( rc, status ); break; } - // SOLAR - // if ( ( ( limit - ( digitptr - _PDCLIB_digits ) ) / status->base ) >= value ) - //if ( ( ( limit / status->base ) >= value ) && ( ( limit - ( digitptr - _PDCLIB_digits ) ) >= ( value * status->base ) ) ) - { - /* no overflow */ - value *= status->base; - value += digitptr - _PDCLIB_digits; - value_parsed = true; - } - //else - //{ - // value = limit; - // threshold = 0; - //} + value *= status->base; + value += digitptr - _PDCLIB_digits; + value_parsed = true; } } /* width or input exhausted, or non-matching character */ @@ -496,31 +519,70 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) return NULL; } /* convert value to target type and assign to parameter */ - switch ( status->flags & ( E_char | E_short | E_long | E_llong | - E_intmax | E_size | E_ptrdiff | - E_unsigned ) ) + if ( ! ( status->flags & E_suppressed ) ) { - ASSIGN_VALUE_TO( E_char, char ); - ASSIGN_VALUE_TO( E_char | E_unsigned, unsigned char ); - ASSIGN_VALUE_TO( E_short, short ); - ASSIGN_VALUE_TO( E_short | E_unsigned, unsigned short ); - ASSIGN_VALUE_TO( 0, int ); - ASSIGN_VALUE_TO( E_unsigned, unsigned int ); - ASSIGN_VALUE_TO( E_long, long ); - ASSIGN_VALUE_TO( E_long | E_unsigned, unsigned long ); - ASSIGN_VALUE_TO( E_llong, long long ); - ASSIGN_VALUE_TO( E_llong | E_unsigned, unsigned long long ); - ASSIGN_VALUE_TO( E_intmax, intmax_t ); - ASSIGN_VALUE_TO( E_intmax | E_unsigned, uintmax_t ); - ASSIGN_VALUE_TO( E_size, size_t ); - /* ASSIGN_VALUE_TO( E_size | E_unsigned, unsigned size_t ); */ - ASSIGN_VALUE_TO( E_ptrdiff, ptrdiff_t ); - /* ASSIGN_VALUE_TO( E_ptrdiff | E_unsigned, unsigned ptrdiff_t ); */ - default: - puts( "UNSUPPORTED SCANF FLAG COMBINATION" ); - return NULL; /* behaviour unspecified */ + switch ( status->flags & ( E_char | E_short | E_long | E_llong | + E_intmax | E_size | E_ptrdiff | + E_unsigned ) ) + { + case E_char: + *( va_arg( status->arg, char * ) ) = (char)( value * sign ); + break; + case E_char | E_unsigned: + *( va_arg( status->arg, unsigned char * ) ) = (unsigned char)( value * sign ); + break; + + case E_short: + *( va_arg( status->arg, short * ) ) = (short)( value * sign ); + break; + case E_short | E_unsigned: + *( va_arg( status->arg, unsigned short * ) ) = (unsigned short)( value * sign ); + break; + + case 0: + *( va_arg( status->arg, int * ) ) = (int)( value * sign ); + break; + case E_unsigned: + *( va_arg( status->arg, unsigned int * ) ) = (unsigned int)( value * sign ); + break; + + case E_long: + *( va_arg( status->arg, long * ) ) = (long)( value * sign ); + break; + case E_long | E_unsigned: + *( va_arg( status->arg, unsigned long * ) ) = (unsigned long)( value * sign ); + break; + + case E_llong: + *( va_arg( status->arg, long long * ) ) = (long long)( value * sign ); + break; + case E_llong | E_unsigned: + *( va_arg( status->arg, unsigned long long * ) ) = (unsigned long long)( value * sign ); + break; + + case E_intmax: + *( va_arg( status->arg, intmax_t * ) ) = (intmax_t)( value * sign ); + break; + case E_intmax | E_unsigned: + *( va_arg( status->arg, uintmax_t * ) ) = (uintmax_t)( value * sign ); + break; + + case E_size: + /* E_size always implies unsigned */ + *( va_arg( status->arg, size_t * ) ) = (size_t)( value * sign ); + break; + + case E_ptrdiff: + /* E_ptrdiff always implies signed */ + *( va_arg( status->arg, ptrdiff_t * ) ) = (ptrdiff_t)( value * sign ); + break; + + default: + puts( "UNSUPPORTED SCANF FLAG COMBINATION" ); + return NULL; /* behaviour unspecified */ + } + ++(status->n); } - ++(status->n); return ++spec; } /* TODO: Floats. */ @@ -529,14 +591,34 @@ const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) #ifdef TEST +#define _PDCLIB_FILEID "_PDCLIB/scan.c" +#define _PDCLIB_STRINGIO + #include <_PDCLIB_test.h> -#include +static int testscanf( char const * s, char const * format, ... ) +{ + struct _PDCLIB_status_t status; + status.n = 0; + status.i = 0; + status.s = (char *)s; + status.stream = NULL; + va_start( status.arg, format ); + if ( *(_PDCLIB_scan( format, &status )) != '\0' ) + { + printf( "_PDCLIB_scan() did not return end-of-specifier on '%s'.\n", format ); + ++TEST_RESULTS; + } + va_end( status.arg ); + return status.n; +} + +#define TEST_CONVERSION_ONLY - int main( void ) { - /* Testing covered by fscanf.c */ + char source[100]; +#include "scanf_testcases.h" return TEST_RESULTS; }