+ /* generic input character */
+ int rc;
+ const char * orig_spec = spec;
+ if ( *(++spec) == '%' )
+ {
+ /* %% -> match single '%' */
+ 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 = -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 */
+
+ /* whether valid input had been parsed */
+ bool value_parsed = false;
+
+ 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':
+ {
+ 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':
+ {
+ 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':
+ 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;
+ }
+
+ 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;