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 ) )
224 char * c = va_arg( status->arg, char * );
225 while ( ( status->this < status->width ) &&
226 ( ( rc = GET( status ) ) != EOF ) )
246 /* width or input exhausted */
260 status->flags |= E_unsigned;
264 int * val = va_arg( status->arg, int * );
269 /* No conversion specifier. Bad conversion. */
273 if ( status->base != -1 )
275 /* integer conversion */
277 bool prefix_parsed = false;
279 while ( ( status->this < status->width ) &&
280 ( ( rc = GET( status ) ) != EOF ) )
298 else if ( ! prefix_parsed )
300 prefix_parsed = true;
303 if ( status->base == 0 )
311 if ( ( status->this < status->width ) &&
312 ( ( rc = GET( status ) ) != EOF ) )
314 if ( tolower( rc ) == 'x' )
316 if ( ( status->base == 0 ) ||
317 ( status->base == 16 ) )
330 if ( status->base == 0 )
341 char * digitptr = memchr( _PDCLIB_digits, tolower( rc ), status->base );
342 if ( digitptr == NULL )
344 /* end of input item */
347 value *= status->base;
348 value += digitptr - _PDCLIB_digits;
352 /* width exceeded, EOF, read error, non-matching character */
353 if ( ! value_parsed )
358 switch ( status->flags & ( E_char | E_short | E_long | E_llong |
359 E_intmax | E_size | E_ptrdiff |
362 ASSIGN( E_char, char );
363 ASSIGN( E_char | E_unsigned, unsigned char );
364 ASSIGN( E_short, short );
365 ASSIGN( E_short | E_unsigned, unsigned short );
367 ASSIGN( E_unsigned, unsigned int );
368 ASSIGN( E_long, long );
369 ASSIGN( E_long | E_unsigned, unsigned long );
370 ASSIGN( E_llong, long long );
371 ASSIGN( E_llong | E_unsigned, unsigned long long );
372 ASSIGN( E_intmax, intmax_t );
373 ASSIGN( E_intmax | E_unsigned, uintmax_t );
374 ASSIGN( E_size, size_t );
375 /* ASSIGN( E_size | E_unsigned, unsigned size_t ); */
376 ASSIGN( E_ptrdiff, ptrdiff_t );
377 /* ASSIGN( E_ptrdiff | E_unsigned, unsigned ptrdiff_t ); */
379 puts( "UNSUPPORTED SCANF FLAG COMBINATION" );
390 #include <_PDCLIB_test.h>
397 /* Testing covered by fscanf.c */