]> pd.if.org Git - pdclib/blob - functions/_PDCLIB/scan.c
f41079bd78ee98908e98642e14011ff3ac21718a
[pdclib] / functions / _PDCLIB / scan.c
1 /* $Id$ */
2
3 /* _PDCLIB_scan( const char *, struct _PDCLIB_status_t * )
4
5    This file is part of the Public Domain C Library (PDCLib).
6    Permission is granted to use, modify, and / or redistribute at will.
7 */
8
9 #include <stdio.h>
10 #include <stdbool.h>
11 #include <stdlib.h>
12 #include <stdarg.h>
13 #include <stdint.h>
14 #include <ctype.h>
15 #include <string.h>
16 #include <stddef.h>
17
18 /* Using an integer's bits as flags for both the conversion flags and length
19    modifiers.
20 */
21 #define E_suppressed 1<<0
22 #define E_char       1<<6
23 #define E_short      1<<7
24 #define E_long       1<<8
25 #define E_llong      1<<9
26 #define E_intmax     1<<10
27 #define E_size       1<<11
28 #define E_ptrdiff    1<<12
29 #define E_intptr     1<<13
30 #define E_ldouble    1<<14
31 #define E_unsigned   1<<16
32
33
34 #define ASSIGN( case_cond, type ) \
35     case case_cond: \
36         *( va_arg( status->arg, type * ) ) = (type)( value * sign ); \
37         break
38
39
40 static int GET( struct _PDCLIB_status_t * status )
41 {
42     ++(status->i);
43     ++(status->this);
44     if ( status->stream != NULL )
45     {
46         return getc( status->stream );
47     }
48     else
49     {
50         return ( *status->s == '\0' ) ? EOF : *((status->s)++);
51     }
52 }
53
54
55 static void UNGET( int c, struct _PDCLIB_status_t * status )
56 {
57     if ( status->stream != NULL )
58     {
59         ungetc( c, status->stream ); /* TODO: Error? */
60     }
61     else
62     {
63         *(--(status->s)) = c;
64     }
65     --(status->i);
66     --(status->this);
67 }
68
69
70 const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status )
71 {
72     /* generic input character */
73     int rc;
74     const char * orig_spec = spec;
75     if ( *(++spec) == '%' )
76     {
77         /* %% -> match single '%' */
78         rc = GET( status );
79         switch ( rc )
80         {
81             case EOF:
82                 /* matching failure */
83                 return NULL;
84             case '%':
85                 return ++spec;
86             default:
87                 UNGET( rc, status );
88                 break;
89         }
90     }
91     /* Initializing status structure */
92     status->flags = 0;
93     status->base = -1;
94     status->this = 0;
95     status->width = 0;
96     status->prec = 0;
97
98     /* '*' suppresses assigning parsed value to variable */
99     if ( *spec == '*' )
100     {
101         status->flags |= E_suppressed;
102         ++spec;
103     }
104
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.
108     */
109     char const * prev_spec = spec;
110     status->width = (int)strtol( spec, (char**)&spec, 10 );
111     if ( spec == prev_spec )
112     {
113         status->width = SIZE_MAX;
114     }
115
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").
120     */
121     switch ( *(spec++) )
122     {
123         case 'h':
124             if ( *spec == 'h' )
125             {
126                 /* hh -> char */
127                 status->flags |= E_char;
128                 ++spec;
129             }
130             else
131             {
132                 /* h -> short */
133                 status->flags |= E_short;
134             }
135             break;
136         case 'l':
137             if ( *spec == 'l' )
138             {
139                 /* ll -> long long */
140                 status->flags |= E_llong;
141                 ++spec;
142             }
143             else
144             {
145                 /* l -> long */
146                 status->flags |= E_long;
147             }
148             break;
149         case 'j':
150             /* j -> intmax_t, which might or might not be long long */
151             status->flags |= E_intmax;
152             break;
153         case 'z':
154             /* z -> size_t, which might or might not be unsigned int */
155             status->flags |= E_size;
156             break;
157         case 't':
158             /* t -> ptrdiff_t, which might or might not be long */
159             status->flags |= E_ptrdiff;
160             break;
161         case 'L':
162             /* L -> long double */
163             status->flags |= E_ldouble;
164             break;
165         default:
166             --spec;
167             break;
168     }
169
170     /* Conversion specifier */
171
172     /* whether valid input had been parsed */
173     bool value_parsed = false;
174
175     switch ( *spec )
176     {
177         case 'd':
178             status->base = 10;
179             break;
180         case 'i':
181             status->base = 0;
182             break;
183         case 'o':
184             status->base = 8;
185             status->flags |= E_unsigned;
186             break;
187         case 'u':
188             status->base = 10;
189             status->flags |= E_unsigned;
190             break;
191         case 'x':
192             status->base = 16;
193             status->flags |= E_unsigned;
194             break;
195         case 'f':
196         case 'F':
197         case 'e':
198         case 'E':
199         case 'g':
200         case 'G':
201         case 'a':
202         case 'A':
203             break;
204         case 'c':
205         {
206             char * c = va_arg( status->arg, char * );
207             if ( status->width == SIZE_MAX )
208             {
209                 status->width = 1;
210             }
211             while ( ( status->this < status->width ) &&
212                     ( ( rc = GET( status ) ) != EOF ) )
213             {
214                 *(c++) = rc;
215                 value_parsed = true;
216             }
217             if ( value_parsed )
218             {
219                 ++status->n;
220                 return ++spec;
221             }
222             else
223             {
224                 /* FIXME: Need two kinds of "no match" here: zero width, and input error */
225                 return NULL;
226             }
227         }
228         case 's':
229         {
230             char * c = va_arg( status->arg, char * );
231             while ( ( status->this < status->width ) && 
232                     ( ( rc = GET( status ) ) != EOF ) )
233             {
234                 if ( isspace( rc ) )
235                 {
236                     if ( value_parsed )
237                     {
238                         *c = '\0';
239                         return spec;
240                     }
241                     else
242                     {
243                         --(status->this);
244                     }
245                 }
246                 else
247                 {
248                     value_parsed = true;
249                     *(c++) = rc;
250                 }
251             }
252             /* width or input exhausted */
253             if ( value_parsed )
254             {
255                 *c = '\0';
256                 ++status->n;
257                 return ++spec;
258             }
259             else
260             {
261                 return NULL;
262             }
263         }
264         case 'p':
265             status->base = 16;
266             status->flags |= E_unsigned;
267             break;
268         case 'n':
269         {
270             int * val = va_arg( status->arg, int * );
271             *val = status->i;
272             return ++spec;
273         }
274         default:
275             /* No conversion specifier. Bad conversion. */
276             return orig_spec;
277     }
278
279     if ( status->base != -1 )
280     {
281         /* integer conversion */
282         uintmax_t value = 0;
283         bool prefix_parsed = false;
284         int sign = 0;
285         while ( ( status->this < status->width ) &&
286                 ( ( rc = GET( status ) ) != EOF ) )
287         {
288             if ( ! sign )
289             {
290                 switch ( rc )
291                 {
292                     case '-':
293                         sign = -1;
294                         break;
295                     case '+':
296                         sign = 1;
297                         break;
298                     default:
299                         sign = 1;
300                         UNGET( rc, status );
301                         break;
302                 }
303             }
304             else if ( ! prefix_parsed )
305             {
306                 prefix_parsed = true;
307                 if ( rc != '0' )
308                 {
309                     if ( status->base == 0 )
310                     {
311                         status->base = 10;
312                     }
313                     UNGET( rc, status );
314                 }
315                 else
316                 {
317                     if ( ( status->this < status->width ) &&
318                          ( ( rc = GET( status ) ) != EOF ) )
319                     {
320                         if ( tolower( rc ) == 'x' )
321                         {
322                             if ( ( status->base == 0 ) ||
323                                  ( status->base == 16 ) )
324                             {
325                                 status->base = 16;
326                             }
327                             else
328                             {
329                                 UNGET( rc, status );
330                                 value_parsed = true;
331                             }
332                         }
333                         else
334                         {
335                             UNGET( rc, status );
336                             if ( status->base == 0 )
337                             {
338                                 status->base = 8;
339                             }
340                             value_parsed = true;
341                         }
342                     }
343                 }
344             }
345             else
346             {
347                 char * digitptr = memchr( _PDCLIB_digits, tolower( rc ), status->base );
348                 if ( digitptr == NULL )
349                 {
350                     /* end of input item */
351                     break;
352                 }
353                 value *= status->base;
354                 value += digitptr - _PDCLIB_digits;
355                 value_parsed = true;
356             }
357         }
358         /* width exceeded, EOF, read error, non-matching character */
359         if ( ! value_parsed )
360         {
361             /* matching error */
362             return NULL;
363         }
364         switch ( status->flags & ( E_char | E_short | E_long | E_llong |
365                                    E_intmax | E_size | E_ptrdiff |
366                                    E_unsigned ) )
367         {
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 );
372             ASSIGN( 0, int );
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 ); */
384             default:
385                 puts( "UNSUPPORTED SCANF FLAG COMBINATION" );
386                 return NULL;
387         }
388         return ++spec;
389     }
390     /* TODO: Floats. */
391     return NULL;
392 }
393
394
395 #ifdef TEST
396 #include <_PDCLIB_test.h>
397 #include <limits.h>
398
399
400  
401 int main( void )
402 {
403     /* Testing covered by fscanf.c */
404     return TEST_RESULTS;
405 }
406
407 #endif