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.
18 /* Using an integer's bits as flags for both the conversion flags and length
21 #define E_suppressed 1<<0
26 #define E_intmax 1<<10
28 #define E_ptrdiff 1<<12
29 #define E_intptr 1<<13
30 #define E_ldouble 1<<14
31 #define E_unsigned 1<<16
34 #define ASSIGN( case_cond, type ) \
36 *( va_arg( status->arg, type * ) ) = (type)( value * sign ); \
40 static int GET( struct _PDCLIB_status_t * status )
44 if ( status->stream != NULL )
46 return getc( status->stream );
50 return ( *status->s == '\0' ) ? EOF : *((status->s)++);
55 static void UNGET( int c, struct _PDCLIB_status_t * status )
57 if ( status->stream != NULL )
59 ungetc( c, status->stream ); /* TODO: Error? */
70 const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status )
72 /* generic input character */
74 const char * orig_spec = spec;
75 if ( *(++spec) == '%' )
77 /* %% -> match single '%' */
82 /* matching failure */
91 /* Initializing status structure */
98 /* '*' suppresses assigning parsed value to variable */
101 status->flags |= E_suppressed;
105 /* If a width is given, strtol() will return its value. If not given,
106 strtol() will return zero. In both cases, endptr will point to the
107 rest of the conversion specifier - just what we need.
109 char const * prev_spec = spec;
110 status->width = (int)strtol( spec, (char**)&spec, 10 );
111 if ( spec == prev_spec )
113 status->width = SIZE_MAX;
116 /* Optional length modifier
117 We step one character ahead in any case, and step back only if we find
118 there has been no length modifier (or step ahead another character if it
119 has been "hh" or "ll").
127 status->flags |= E_char;
133 status->flags |= E_short;
139 /* ll -> long long */
140 status->flags |= E_llong;
146 status->flags |= E_long;
150 /* j -> intmax_t, which might or might not be long long */
151 status->flags |= E_intmax;
154 /* z -> size_t, which might or might not be unsigned int */
155 status->flags |= E_size;
158 /* t -> ptrdiff_t, which might or might not be long */
159 status->flags |= E_ptrdiff;
162 /* L -> long double */
163 status->flags |= E_ldouble;
170 /* Conversion specifier */
172 /* whether valid input had been parsed */
173 bool value_parsed = false;
185 status->flags |= E_unsigned;
189 status->flags |= E_unsigned;
193 status->flags |= E_unsigned;
206 char * c = va_arg( status->arg, char * );
207 if ( status->width == SIZE_MAX )
211 while ( ( status->this < status->width ) &&
212 ( ( rc = GET( status ) ) != EOF ) )
224 /* FIXME: Need two kinds of "no match" here: zero width, and input error */
230 char * c = va_arg( status->arg, char * );
231 while ( ( status->this < status->width ) &&
232 ( ( rc = GET( status ) ) != EOF ) )
252 /* width or input exhausted */
266 status->flags |= E_unsigned;
270 int * val = va_arg( status->arg, int * );
275 /* No conversion specifier. Bad conversion. */
279 if ( status->base != -1 )
281 /* integer conversion */
283 bool prefix_parsed = false;
285 while ( ( status->this < status->width ) &&
286 ( ( rc = GET( status ) ) != EOF ) )
304 else if ( ! prefix_parsed )
306 prefix_parsed = true;
309 if ( status->base == 0 )
317 if ( ( status->this < status->width ) &&
318 ( ( rc = GET( status ) ) != EOF ) )
320 if ( tolower( rc ) == 'x' )
322 if ( ( status->base == 0 ) ||
323 ( status->base == 16 ) )
336 if ( status->base == 0 )
347 char * digitptr = memchr( _PDCLIB_digits, tolower( rc ), status->base );
348 if ( digitptr == NULL )
350 /* end of input item */
353 value *= status->base;
354 value += digitptr - _PDCLIB_digits;
358 /* width exceeded, EOF, read error, non-matching character */
359 if ( ! value_parsed )
364 switch ( status->flags & ( E_char | E_short | E_long | E_llong |
365 E_intmax | E_size | E_ptrdiff |
368 ASSIGN( E_char, char );
369 ASSIGN( E_char | E_unsigned, unsigned char );
370 ASSIGN( E_short, short );
371 ASSIGN( E_short | E_unsigned, unsigned short );
373 ASSIGN( E_unsigned, unsigned int );
374 ASSIGN( E_long, long );
375 ASSIGN( E_long | E_unsigned, unsigned long );
376 ASSIGN( E_llong, long long );
377 ASSIGN( E_llong | E_unsigned, unsigned long long );
378 ASSIGN( E_intmax, intmax_t );
379 ASSIGN( E_intmax | E_unsigned, uintmax_t );
380 ASSIGN( E_size, size_t );
381 /* ASSIGN( E_size | E_unsigned, unsigned size_t ); */
382 ASSIGN( E_ptrdiff, ptrdiff_t );
383 /* ASSIGN( E_ptrdiff | E_unsigned, unsigned ptrdiff_t ); */
385 puts( "UNSUPPORTED SCANF FLAG COMBINATION" );
396 #include <_PDCLIB_test.h>
403 /* Testing covered by fscanf.c */