Started debugging scanf() functions.
[fw/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)++);
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     status->width = (int)strtol( spec, (char**)&spec, 10 );
110
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").
115     */
116     switch ( *(spec++) )
117     {
118         case 'h':
119             if ( *spec == 'h' )
120             {
121                 /* hh -> char */
122                 status->flags |= E_char;
123                 ++spec;
124             }
125             else
126             {
127                 /* h -> short */
128                 status->flags |= E_short;
129             }
130             break;
131         case 'l':
132             if ( *spec == 'l' )
133             {
134                 /* ll -> long long */
135                 status->flags |= E_llong;
136                 ++spec;
137             }
138             else
139             {
140                 /* l -> long */
141                 status->flags |= E_long;
142             }
143             break;
144         case 'j':
145             /* j -> intmax_t, which might or might not be long long */
146             status->flags |= E_intmax;
147             break;
148         case 'z':
149             /* z -> size_t, which might or might not be unsigned int */
150             status->flags |= E_size;
151             break;
152         case 't':
153             /* t -> ptrdiff_t, which might or might not be long */
154             status->flags |= E_ptrdiff;
155             break;
156         case 'L':
157             /* L -> long double */
158             status->flags |= E_ldouble;
159             break;
160         default:
161             --spec;
162             break;
163     }
164
165     /* Conversion specifier */
166
167     /* whether valid input had been parsed */
168     bool value_parsed = false;
169
170     switch ( *spec )
171     {
172         case 'd':
173             status->base = 10;
174             break;
175         case 'i':
176             status->base = 0;
177             break;
178         case 'o':
179             status->base = 8;
180             status->flags |= E_unsigned;
181             break;
182         case 'u':
183             status->base = 10;
184             status->flags |= E_unsigned;
185             break;
186         case 'x':
187             status->base = 16;
188             status->flags |= E_unsigned;
189             break;
190         case 'f':
191         case 'F':
192         case 'e':
193         case 'E':
194         case 'g':
195         case 'G':
196         case 'a':
197         case 'A':
198             break;
199         case 'c':
200         {
201             char * c = va_arg( status->arg, char * );
202             if ( status->width == SIZE_MAX )
203             {
204                 status->width = 1;
205             }
206             while ( ( status->this < status->width ) &&
207                     ( ( rc = GET( status ) ) != EOF ) )
208             {
209                 *(c++) = rc;
210                 value_parsed = true;
211             }
212             if ( value_parsed )
213             {
214                 ++status->n;
215                 return ++spec;
216             }
217             else
218             {
219                 return NULL;
220             }
221         }
222         case 's':
223         {
224             char * c = va_arg( status->arg, char * );
225             while ( ( status->this < status->width ) && 
226                     ( ( rc = GET( status ) ) != EOF ) )
227             {
228                 if ( isspace( rc ) )
229                 {
230                     if ( value_parsed )
231                     {
232                         *c = '\0';
233                         return spec;
234                     }
235                     else
236                     {
237                         --(status->this);
238                     }
239                 }
240                 else
241                 {
242                     value_parsed = true;
243                     *(c++) = rc;
244                 }
245             }
246             /* width or input exhausted */
247             if ( value_parsed )
248             {
249                 *c = '\0';
250                 ++status->n;
251                 return ++spec;
252             }
253             else
254             {
255                 return NULL;
256             }
257         }
258         case 'p':
259             status->base = 16;
260             status->flags |= E_unsigned;
261             break;
262         case 'n':
263         {
264             int * val = va_arg( status->arg, int * );
265             *val = status->i;
266             return ++spec;
267         }
268         default:
269             /* No conversion specifier. Bad conversion. */
270             return orig_spec;
271     }
272
273     if ( status->base != -1 )
274     {
275         /* integer conversion */
276         uintmax_t value = 0;
277         bool prefix_parsed = false;
278         int sign = 0;
279         while ( ( status->this < status->width ) &&
280                 ( ( rc = GET( status ) ) != EOF ) )
281         {
282             if ( ! sign )
283             {
284                 switch ( rc )
285                 {
286                     case '-':
287                         sign = -1;
288                         break;
289                     case '+':
290                         sign = 1;
291                         break;
292                     default:
293                         sign = 1;
294                         UNGET( rc, status );
295                         break;
296                 }
297             }
298             else if ( ! prefix_parsed )
299             {
300                 prefix_parsed = true;
301                 if ( rc != '0' )
302                 {
303                     if ( status->base == 0 )
304                     {
305                         status->base = 10;
306                     }
307                     UNGET( rc, status );
308                 }
309                 else
310                 {
311                     if ( ( status->this < status->width ) &&
312                          ( ( rc = GET( status ) ) != EOF ) )
313                     {
314                         if ( tolower( rc ) == 'x' )
315                         {
316                             if ( ( status->base == 0 ) ||
317                                  ( status->base == 16 ) )
318                             {
319                                 status->base = 16;
320                             }
321                             else
322                             {
323                                 UNGET( rc, status );
324                                 value_parsed = true;
325                             }
326                         }
327                         else
328                         {
329                             UNGET( rc, status );
330                             if ( status->base == 0 )
331                             {
332                                 status->base = 8;
333                             }
334                             value_parsed = true;
335                         }
336                     }
337                 }
338             }
339             else
340             {
341                 char * digitptr = memchr( _PDCLIB_digits, tolower( rc ), status->base );
342                 if ( digitptr == NULL )
343                 {
344                     /* end of input item */
345                     break;
346                 }
347                 value *= status->base;
348                 value += digitptr - _PDCLIB_digits;
349                 value_parsed = true;
350             }
351         }
352         /* width exceeded, EOF, read error, non-matching character */
353         if ( ! value_parsed )
354         {
355             /* matching error */
356             return NULL;
357         }
358         switch ( status->flags & ( E_char | E_short | E_long | E_llong |
359                                    E_intmax | E_size | E_ptrdiff |
360                                    E_unsigned ) )
361         {
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 );
366             ASSIGN( 0, int );
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 ); */
378             default:
379                 puts( "UNSUPPORTED SCANF FLAG COMBINATION" );
380                 return NULL;
381         }
382         return ++spec;
383     }
384     /* TODO: Floats. */
385     return NULL;
386 }
387
388
389 #ifdef TEST
390 #include <_PDCLIB_test.h>
391 #include <limits.h>
392
393
394  
395 int main( void )
396 {
397     /* Testing covered by fscanf.c */
398     return TEST_RESULTS;
399 }
400
401 #endif