Intermediate code.
[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 <ctype.h>
14
15 /* Using an integer's bits as flags for both the conversion flags and length
16    modifiers.
17 */
18 #define E_suppressed 1<<0
19 #define E_char       1<<6
20 #define E_short      1<<7
21 #define E_long       1<<8
22 #define E_llong      1<<9
23 #define E_intmax     1<<10
24 #define E_size       1<<11
25 #define E_ptrdiff    1<<12
26 #define E_intptr     1<<13
27 #define E_ldouble    1<<14
28 #define E_unsigned   1<<16
29
30
31 #define MATCH_FAIL -1
32 #define MATCH_ERROR -2
33
34 static int MATCH( int c, struct _PDCLIB_status_t * status )
35 {
36     if ( status->stream != NULL )
37     {
38         if ( ! _PDCLIB_prepread( status->stream ) )
39         {
40             return MATCH_ERROR;
41         }
42         if ( tolower( status->stream->buffer[ status->stream->bufidx ] ) == c )
43         {
44             /* recycling parameter */
45             c = getc( status->stream );
46         }
47         else
48         {
49             return MATCH_FAIL;
50         }
51     }
52     else
53     {
54         if ( tolower( *(status->s) ) == c )
55         {
56             /* recycling parameter */
57             c = *((status->s)++); /* TODO: \0 */
58         }
59         else
60         {
61             return MATCH_FAIL;
62         }
63     }
64     ++(status->i);
65     ++(status->this);
66     return c;
67 }
68
69
70 static void UNGET( int c, struct _PDCLIB_status_t * status )
71 {
72     if ( status->stream != NULL )
73     {
74         ungetc( c, status->stream ); /* TODO: Error? */
75     }
76     else
77     {
78         *(--(status->s)) = c;
79     }
80     --(status->i);
81     --(status->this);
82 }
83
84
85 const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status )
86 {
87     const char * orig_spec = spec;
88     if ( *(++spec) == '%' )
89     {
90         /* %% -> match single '%' */
91         MATCH( *spec, status );
92         return ++spec;
93     }
94     /* Initializing status structure */
95     status->flags = 0;
96     status->base = -1;
97     status->this = 0;
98     status->width = 0;
99     status->prec = 0;
100
101     /* '*' suppresses assigning parsed value to variable */
102     if ( *spec == '*' )
103     {
104         status->flags |= E_suppressed;
105         ++spec;
106     }
107
108     /* If a width is given, strtol() will return its value. If not given,
109        strtol() will return zero. In both cases, endptr will point to the
110        rest of the conversion specifier - just what we need.
111     */
112     status->width = (int)strtol( spec, (char**)&spec, 10 );
113
114     /* Optional length modifier
115        We step one character ahead in any case, and step back only if we find
116        there has been no length modifier (or step ahead another character if it
117        has been "hh" or "ll").
118     */
119     switch ( *(spec++) )
120     {
121         case 'h':
122             if ( *spec == 'h' )
123             {
124                 /* hh -> char */
125                 status->flags |= E_char;
126                 ++spec;
127             }
128             else
129             {
130                 /* h -> short */
131                 status->flags |= E_short;
132             }
133             break;
134         case 'l':
135             if ( *spec == 'l' )
136             {
137                 /* ll -> long long */
138                 status->flags |= E_llong;
139                 ++spec;
140             }
141             else
142             {
143                 /* l -> long */
144                 status->flags |= E_long;
145             }
146             break;
147         case 'j':
148             /* j -> intmax_t, which might or might not be long long */
149             status->flags |= E_intmax;
150             break;
151         case 'z':
152             /* z -> size_t, which might or might not be unsigned int */
153             status->flags |= E_size;
154             break;
155         case 't':
156             /* t -> ptrdiff_t, which might or might not be long */
157             status->flags |= E_ptrdiff;
158             break;
159         case 'L':
160             /* L -> long double */
161             status->flags |= E_ldouble;
162             break;
163         default:
164             --spec;
165             break;
166     }
167
168     /* Conversion specifier */
169     switch ( *spec )
170     {
171         case 'd':
172             status->base = 10;
173             break;
174         case 'i':
175             status->base = 0;
176             break;
177         case 'o':
178             status->base = 8;
179             status->flags |= E_unsigned;
180             break;
181         case 'u':
182             status->base = 10;
183             status->flags |= E_unsigned;
184             break;
185         case 'x':
186             status->base = 16;
187             status->flags |= E_unsigned;
188             break;
189         case 'f':
190         case 'F':
191         case 'e':
192         case 'E':
193         case 'g':
194         case 'G':
195         case 'a':
196         case 'A':
197             break;
198         case 'c':
199             /* TODO */
200             break;
201         case 's':
202             /* TODO */
203             break;
204         case 'p':
205             /* TODO */
206             status->base = 16;
207             status->flags |= E_unsigned;
208             break;
209         case 'n':
210         {
211             int * val = va_arg( status->arg, int * );
212             *val = status->i;
213             return ++spec;
214         }
215         default:
216             /* No conversion specifier. Bad conversion. */
217             return orig_spec;
218     }
219     bool zero = false;
220     if ( status->base != -1 )
221     {
222         bool value = false;
223         int rc;
224         if ( ( rc = MATCH( '0', status ) ) >= 0 )
225         {
226             if ( ( rc = MATCH( 'x', status ) ) >= 0 )
227             {
228                 if ( ( status->base == 0 ) || ( status->base == 16 ) )
229                 {
230                     status->base = 16;
231                 }
232                 else
233                 {
234                     UNGET( rc, status );
235                     value = true;
236                 }
237             }
238             else if ( rc == MATCH_FAIL )
239             {
240                 if ( status->base == 0 )
241                 {
242                     status->base = 8;
243                 }
244                 else
245                 {
246                     value = true;
247                 }
248             }
249             else
250             {
251                 /* TODO: MATCH_ERROR */
252             }
253         }
254         else if ( rc == MATCH_FAIL )
255         {
256             if ( status->base == 0 )
257             {
258                 status->base = 10;
259             }
260         }
261         else
262         {
263             /* TODO: MATCH_ERROR */
264         }
265         /* TODO: Integer conversion */
266     }
267     else
268     {
269         /* TODO: Float conversions? */
270     }
271     return NULL;
272 }
273
274 #ifdef TEST
275 #include <_PDCLIB_test.h>
276
277 int main( void )
278 {
279     TESTCASE( NO_TESTDRIVER );
280     return TEST_RESULTS;
281 }
282
283 #endif