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)++);
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 status->width = (int)strtol( spec, (char**)&spec, 10 );
111 /* Optional length modifier
112 We step one character ahead in any case, and step back only if we find
113 there has been no length modifier (or step ahead another character if it
114 has been "hh" or "ll").
122 status->flags |= E_char;
128 status->flags |= E_short;
134 /* ll -> long long */
135 status->flags |= E_llong;
141 status->flags |= E_long;
145 /* j -> intmax_t, which might or might not be long long */
146 status->flags |= E_intmax;
149 /* z -> size_t, which might or might not be unsigned int */
150 status->flags |= E_size;
153 /* t -> ptrdiff_t, which might or might not be long */
154 status->flags |= E_ptrdiff;
157 /* L -> long double */
158 status->flags |= E_ldouble;
165 /* Conversion specifier */
167 /* whether valid input had been parsed */
168 bool value_parsed = false;
180 status->flags |= E_unsigned;
184 status->flags |= E_unsigned;
188 status->flags |= E_unsigned;
201 char * c = va_arg( status->arg, char * );
202 if ( status->width == SIZE_MAX )
206 while ( ( status->this < status->width ) &&
207 ( ( rc = GET( status ) ) != EOF ) )
212 return value_parsed ? spec : NULL;
216 char * c = va_arg( status->arg, char * );
217 while ( ( status->this < status->width ) &&
218 ( ( rc = GET( status ) ) != EOF ) )
238 /* width or input exhausted */
251 status->flags |= E_unsigned;
255 int * val = va_arg( status->arg, int * );
260 /* No conversion specifier. Bad conversion. */
264 if ( status->base != -1 )
266 /* integer conversion */
268 bool prefix_parsed = false;
270 while ( ( status->this < status->width ) &&
271 ( ( rc = GET( status ) ) != EOF ) )
289 else if ( ! prefix_parsed )
291 prefix_parsed = true;
294 if ( status->base == 0 )
302 if ( ( status->this < status->width ) &&
303 ( ( rc = GET( status ) ) != EOF ) )
305 if ( tolower( rc ) == 'x' )
307 if ( ( status->base == 0 ) ||
308 ( status->base == 16 ) )
321 if ( status->base == 0 )
332 char * digitptr = memchr( _PDCLIB_digits, tolower( rc ), status->base );
333 if ( digitptr == NULL )
335 /* end of input item */
338 value *= status->base;
339 value += digitptr - _PDCLIB_digits;
343 /* width exceeded, EOF, read error, non-matching character */
344 if ( ! value_parsed )
349 switch ( status->flags & ( E_char | E_short | E_long | E_llong |
350 E_intmax | E_size | E_ptrdiff |
353 ASSIGN( E_char, char );
354 ASSIGN( E_char | E_unsigned, unsigned char );
355 ASSIGN( E_short, short );
356 ASSIGN( E_short | E_unsigned, unsigned short );
358 ASSIGN( E_unsigned, unsigned int );
359 ASSIGN( E_long, long );
360 ASSIGN( E_long | E_unsigned, unsigned long );
361 ASSIGN( E_llong, long long );
362 ASSIGN( E_llong | E_unsigned, unsigned long long );
363 ASSIGN( E_intmax, intmax_t );
364 ASSIGN( E_intmax | E_unsigned, uintmax_t );
365 ASSIGN( E_size, size_t );
366 /* ASSIGN( E_size | E_unsigned, unsigned size_t ); */
367 ASSIGN( E_ptrdiff, ptrdiff_t );
368 /* ASSIGN( E_ptrdiff | E_unsigned, unsigned ptrdiff_t ); */
370 puts( "UNSUPPORTED SCANF FLAG COMBINATION" );
381 #include <_PDCLIB_test.h>
385 TESTCASE( NO_TESTDRIVER );