3 /* _PDCLIB_scan( const char *, struct _PDCLIB_status_t * )
5 This file is part of the Public Domain C Library (PDCLib).
6 Permission is granted to use, modify, and / or redistribute at will.
19 /* Using an integer's bits as flags for both the conversion flags and length
22 #define E_suppressed 1<<0
27 #define E_intmax 1<<10
29 #define E_ptrdiff 1<<12
30 #define E_intptr 1<<13
31 #define E_ldouble 1<<14
32 #define E_unsigned 1<<16
35 /* Helper macro for assigning a readily converted integer value to the correct
36 parameter type, used in a switch on status->flags (see E_* flags above).
37 case_cond: combination of the E_* flags above, used for the switch-case
38 type: integer type, used to get the correct type from the parameter
39 stack as well as for cast target.
41 #define ASSIGN_VALUE_TO( case_cond, type ) \
43 *( va_arg( status->arg, type * ) ) = (type)( value * sign ); \
47 /* Helper function to get a character from the string or stream, whatever is
48 used for input. When reading from a string, returns EOF on end-of-string
49 so that handling of the return value can be uniform for both streams and
52 static int GET( struct _PDCLIB_status_t * status )
55 if ( status->stream != NULL )
57 rc = getc( status->stream );
61 rc = ( *status->s == '\0' ) ? EOF : (unsigned char)*((status->s)++);
72 /* Helper function to put a read character back into the string or stream,
73 whatever is used for input.
75 static void UNGET( int c, struct _PDCLIB_status_t * status )
77 if ( status->stream != NULL )
79 ungetc( c, status->stream ); /* TODO: Error? */
90 const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status )
92 /* generic input character */
94 const char * orig_spec = spec;
95 if ( *(++spec) == '%' )
97 /* %% -> match single '%' */
103 if ( status->n == 0 )
115 /* Initializing status structure */
122 /* '*' suppresses assigning parsed value to variable */
125 status->flags |= E_suppressed;
129 /* If a width is given, strtol() will return its value. If not given,
130 strtol() will return zero. In both cases, endptr will point to the
131 rest of the conversion specifier - just what we need.
133 char const * prev_spec = spec;
134 status->width = (int)strtol( spec, (char**)&spec, 10 );
135 if ( spec == prev_spec )
137 status->width = SIZE_MAX;
140 /* Optional length modifier
141 We step one character ahead in any case, and step back only if we find
142 there has been no length modifier (or step ahead another character if it
143 has been "hh" or "ll").
151 status->flags |= E_char;
157 status->flags |= E_short;
163 /* ll -> long long */
164 status->flags |= E_llong;
170 status->flags |= E_long;
174 /* j -> intmax_t, which might or might not be long long */
175 status->flags |= E_intmax;
178 /* z -> size_t, which might or might not be unsigned int */
179 status->flags |= E_size;
182 /* t -> ptrdiff_t, which might or might not be long */
183 status->flags |= E_ptrdiff;
186 /* L -> long double */
187 status->flags |= E_ldouble;
194 /* Conversion specifier */
196 /* whether valid input had been parsed */
197 bool value_parsed = false;
209 status->flags |= E_unsigned;
213 status->flags |= E_unsigned;
217 status->flags |= E_unsigned;
230 char * c = va_arg( status->arg, char * );
231 /* for %c, default width is one */
232 if ( status->width == SIZE_MAX )
236 /* reading until width reached or input exhausted */
237 while ( ( status->this < status->width ) &&
238 ( ( rc = GET( status ) ) != EOF ) )
243 /* width or input exhausted */
251 /* input error, no character read */
252 if ( status->n == 0 )
261 char * c = va_arg( status->arg, char * );
262 while ( ( status->this < status->width ) &&
263 ( ( rc = GET( status ) ) != EOF ) )
269 /* matching sequence terminated by whitespace */
275 /* leading whitespace not counted against width */
285 /* width or input exhausted */
294 /* input error, no character read */
295 if ( status->n == 0 )
304 status->flags |= E_unsigned;
308 int * val = va_arg( status->arg, int * );
313 /* No conversion specifier. Bad conversion. */
317 if ( status->base != -1 )
319 /* integer conversion */
320 uintmax_t value = 0; /* absolute value read */
321 uintmax_t limit; /* max. value allowed */
322 uintmax_t threshold; /* overflow threshold */
323 bool prefix_parsed = false;
325 while ( ( status->this < status->width ) &&
326 ( ( rc = GET( status ) ) != EOF ) )
332 /* matching sequence terminated by whitespace */
338 /* leading whitespace not counted against width */
344 /* no sign parsed yet */
354 /* not a sign; put back character */
359 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax | E_size | E_ptrdiff | E_unsigned ) )
362 limit = ( sign == 1 ) ? CHAR_MAX : ( CHAR_MIN * sign );
364 case E_char | E_unsigned:
368 limit = ( sign == 1 ) ? SHRT_MAX : ( SHRT_MIN * sign );
370 case E_short | E_unsigned:
374 limit = ( sign == 1 ) ? LONG_MAX : ( LONG_MIN * sign );
376 case E_long | E_unsigned:
380 limit = ( sign == 1 ) ? LLONG_MAX : ( LLONG_MIN * sign );
382 case E_llong | E_unsigned:
386 limit = ( sign == 1 ) ? INTMAX_MAX : ( INTMAX_MIN * sign );
388 case E_intmax | E_unsigned:
392 case E_size | E_unsigned:
396 case E_ptrdiff | E_unsigned:
397 limit = ( sign == 1 ) ? PTRDIFF_MAX : ( PTRDIFF_MIN * sign );
403 limit = ( sign == 1 ) ? INT_MAX : ( INT_MIN * sign );
407 else if ( ! prefix_parsed )
409 /* no prefix (0x... for hex, 0... for octal) parsed yet */
410 prefix_parsed = true;
413 /* not a prefix; if base not yet set, set to decimal */
414 if ( status->base == 0 )
422 /* starts with zero, so it might be a prefix. */
423 /* check what follows next (might be 0x...) */
424 if ( ( status->this < status->width ) &&
425 ( ( rc = GET( status ) ) != EOF ) )
427 if ( tolower( rc ) == 'x' )
429 /* 0x... would be prefix for hex base... */
430 if ( ( status->base == 0 ) ||
431 ( status->base == 16 ) )
437 /* ...unless already set to other value */
444 /* 0... but not 0x.... would be octal prefix */
446 if ( status->base == 0 )
450 /* in any case we have read a zero */
456 /* failed to read beyond the initial zero */
464 char * digitptr = memchr( _PDCLIB_digits, tolower( rc ), status->base );
465 if ( digitptr == NULL )
467 /* end of input item */
472 // if ( ( ( limit - ( digitptr - _PDCLIB_digits ) ) / status->base ) >= value )
473 //if ( ( ( limit / status->base ) >= value ) && ( ( limit - ( digitptr - _PDCLIB_digits ) ) >= ( value * status->base ) ) )
476 value *= status->base;
477 value += digitptr - _PDCLIB_digits;
487 /* width or input exhausted, or non-matching character */
488 if ( ! value_parsed )
490 /* out of input before anything could be parsed - input error */
491 /* FIXME: if first character does not match, value_parsed is not set - but it is NOT an input error */
492 if ( ( status->n == 0 ) && ( rc == EOF ) )
498 /* convert value to target type and assign to parameter */
499 switch ( status->flags & ( E_char | E_short | E_long | E_llong |
500 E_intmax | E_size | E_ptrdiff |
503 ASSIGN_VALUE_TO( E_char, char );
504 ASSIGN_VALUE_TO( E_char | E_unsigned, unsigned char );
505 ASSIGN_VALUE_TO( E_short, short );
506 ASSIGN_VALUE_TO( E_short | E_unsigned, unsigned short );
507 ASSIGN_VALUE_TO( 0, int );
508 ASSIGN_VALUE_TO( E_unsigned, unsigned int );
509 ASSIGN_VALUE_TO( E_long, long );
510 ASSIGN_VALUE_TO( E_long | E_unsigned, unsigned long );
511 ASSIGN_VALUE_TO( E_llong, long long );
512 ASSIGN_VALUE_TO( E_llong | E_unsigned, unsigned long long );
513 ASSIGN_VALUE_TO( E_intmax, intmax_t );
514 ASSIGN_VALUE_TO( E_intmax | E_unsigned, uintmax_t );
515 ASSIGN_VALUE_TO( E_size, size_t );
516 /* ASSIGN_VALUE_TO( E_size | E_unsigned, unsigned size_t ); */
517 ASSIGN_VALUE_TO( E_ptrdiff, ptrdiff_t );
518 /* ASSIGN_VALUE_TO( E_ptrdiff | E_unsigned, unsigned ptrdiff_t ); */
520 puts( "UNSUPPORTED SCANF FLAG COMBINATION" );
521 return NULL; /* behaviour unspecified */
532 #include <_PDCLIB_test.h>
539 /* Testing covered by fscanf.c */