#include <stddef.h>
#include <limits.h>
+#ifndef REGTEST
+
/* Using an integer's bits as flags for both the conversion flags and length
modifiers.
*/
#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
*/
static int GET( struct _PDCLIB_status_t * status )
{
- int rc;
+ int rc = EOF;
if ( status->stream != NULL )
{
rc = getc( status->stream );
if ( rc != EOF )
{
++(status->i);
- ++(status->this);
+ ++(status->current);
}
return rc;
}
--(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;
}
/* Initializing status structure */
status->flags = 0;
status->base = -1;
- status->this = 0;
+ status->current = 0;
status->width = 0;
status->prec = 0;
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;
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;
}
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;
{
/* 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 ) )
else
{
/* leading whitespace not counted against width */
- status->this--;
+ status->current--;
}
}
else if ( ! sign )
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 )
{
{
/* 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' )
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 */
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. */
return NULL;
}
-
+#endif
#ifdef TEST
+#define _PDCLIB_FILEID "_PDCLIB/scan.c"
+#define _PDCLIB_STRINGIO
+
#include <_PDCLIB_test.h>
-#include <limits.h>
+#ifndef REGTEST
+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;
+}
+#endif
+
+#define TEST_CONVERSION_ONLY
-
int main( void )
{
- /* Testing covered by fscanf.c */
+#ifndef REGTEST
+ char source[100];
+#include "scanf_testcases.h"
+#endif
return TEST_RESULTS;
}