chiark / gitweb /
[PATCH] Shut up wait_for_sysfs class/net failure messages, as it's not possible
[elogind.git] / klibc / klibc / vsscanf.c
1 /*
2  * vsscanf.c
3  *
4  * vsscanf(), from which the rest of the scanf()
5  * family is built
6  */
7
8 #include <ctype.h>
9 #include <stdarg.h>
10 #include <stddef.h>
11 #include <inttypes.h>
12 #include <string.h>
13 #include <limits.h>
14 #include <stdio.h>
15
16 #ifndef LONG_BIT
17 #define LONG_BIT (CHAR_BIT*sizeof(long))
18 #endif
19
20 enum flags {
21   FL_SPLAT  = 0x01,             /* Drop the value, do not assign */
22   FL_INV    = 0x02,             /* Character-set with inverse */
23   FL_WIDTH  = 0x04,             /* Field width specified */
24   FL_MINUS  = 0x08,             /* Negative number */
25 };
26
27 enum ranks {
28   rank_char     = -2,
29   rank_short    = -1,
30   rank_int      = 0,
31   rank_long     = 1,
32   rank_longlong = 2,
33   rank_ptr      = INT_MAX       /* Special value used for pointers */
34 };
35
36 #define MIN_RANK        rank_char
37 #define MAX_RANK        rank_longlong
38
39 #define INTMAX_RANK     rank_longlong
40 #define SIZE_T_RANK     rank_long
41 #define PTRDIFF_T_RANK  rank_long
42
43 enum bail {
44   bail_none = 0,                /* No error condition */
45   bail_eof,                     /* Hit EOF */
46   bail_err                      /* Conversion mismatch */
47 };
48
49 static inline const char *
50 skipspace(const char *p)
51 {
52   while ( isspace((unsigned char)*p) ) p++;
53   return p;
54 }
55
56 #undef set_bit
57 static inline void
58 set_bit(unsigned long *bitmap, unsigned int bit)
59 {
60   bitmap[bit/LONG_BIT] |= 1UL << (bit%LONG_BIT);
61 }
62
63 #undef test_bit
64 static inline int
65 test_bit(unsigned long *bitmap, unsigned int bit)
66 {
67   return (int)(bitmap[bit/LONG_BIT] >> (bit%LONG_BIT)) & 1;
68 }
69
70 int vsscanf(const char *buffer, const char *format, va_list ap)
71 {
72   const char *p = format;
73   char ch;
74   const char *q = buffer;
75   const char *qq;
76   uintmax_t val = 0;
77   int rank = rank_int;          /* Default rank */
78   unsigned int width = UINT_MAX;
79   int base;
80   enum flags flags = 0;
81   enum {
82     st_normal,                  /* Ground state */
83     st_flags,                   /* Special flags */
84     st_width,                   /* Field width */
85     st_modifiers,               /* Length or conversion modifiers */
86     st_match_init,              /* Initial state of %[ sequence */
87     st_match,                   /* Main state of %[ sequence */
88     st_match_range,             /* After - in a %[ sequence */
89   } state = st_normal;
90   char *sarg = NULL;            /* %s %c or %[ string argument */
91   enum bail bail = bail_none;
92   int sign;
93   int converted = 0;            /* Successful conversions */
94   unsigned long matchmap[((1 << CHAR_BIT)+(LONG_BIT-1))/LONG_BIT];
95   int matchinv = 0;             /* Is match map inverted? */
96   unsigned char range_start = 0;
97
98   while ( (ch = *p++) && !bail ) {
99     switch ( state ) {
100     case st_normal:
101       if ( ch == '%' ) {
102         state = st_flags;
103         flags = 0; rank = rank_int; width = UINT_MAX;
104       } else if ( isspace((unsigned char)ch) ) {
105         q = skipspace(q);
106       } else {
107         if ( *q == ch )
108           q++;
109         else
110           bail = bail_err;      /* Match failure */
111       }
112       break;
113
114     case st_flags:
115       switch ( ch ) {
116       case '*':
117         flags |= FL_SPLAT;
118         break;
119       case '0' ... '9':
120         width = (ch-'0');
121         state = st_width;
122         flags |= FL_WIDTH;
123         break;
124       default:
125         state = st_modifiers;
126         p--;                    /* Process this character again */
127         break;
128       }
129       break;
130
131     case st_width:
132       if ( ch >= '0' && ch <= '9' ) {
133         width = width*10+(ch-'0');
134       } else {
135         state = st_modifiers;
136         p--;                    /* Process this character again */
137       }
138       break;
139
140     case st_modifiers:
141       switch ( ch ) {
142         /* Length modifiers - nonterminal sequences */
143       case 'h':
144         rank--;                 /* Shorter rank */
145         break;
146       case 'l':
147         rank++;                 /* Longer rank */
148         break;
149       case 'j':
150         rank = INTMAX_RANK;
151         break;
152       case 'z':
153         rank = SIZE_T_RANK;
154         break;
155       case 't':
156         rank = PTRDIFF_T_RANK;
157         break;
158       case 'L':
159       case 'q':
160         rank = rank_longlong;   /* long double/long long */
161         break;
162
163       default:
164         /* Output modifiers - terminal sequences */
165         state = st_normal;      /* Next state will be normal */
166         if ( rank < MIN_RANK )  /* Canonicalize rank */
167           rank = MIN_RANK;
168         else if ( rank > MAX_RANK )
169           rank = MAX_RANK;
170
171         switch ( ch ) {
172         case 'P':               /* Upper case pointer */
173         case 'p':               /* Pointer */
174 #if 0   /* Enable this to allow null pointers by name */
175           q = skipspace(q);
176           if ( !isdigit((unsigned char)*q) ) {
177             static const char * const nullnames[] =
178             { "null", "nul", "nil", "(null)", "(nul)", "(nil)", 0 };
179             const char * const *np;
180
181             /* Check to see if it's a null pointer by name */
182             for ( np = nullnames ; *np ; np++ ) {
183               if ( !strncasecmp(q, *np, strlen(*np)) ) {
184                 val = (uintmax_t)((void *)NULL);
185                 goto set_integer;
186               }
187             }
188             /* Failure */
189             bail = bail_err;
190             break;
191           }
192           /* else */
193 #endif
194           rank = rank_ptr;
195           base = 0; sign = 0;
196           goto scan_int;
197
198         case 'i':               /* Base-independent integer */
199           base = 0; sign = 1;
200           goto scan_int;
201
202         case 'd':               /* Decimal integer */
203           base = 10; sign = 1;
204           goto scan_int;
205
206         case 'o':               /* Octal integer */
207           base = 8; sign = 0;
208           goto scan_int;
209
210         case 'u':               /* Unsigned decimal integer */
211           base = 10; sign = 0;
212           goto scan_int;
213           
214         case 'x':               /* Hexadecimal integer */
215         case 'X':
216           base = 16; sign = 0;
217           goto scan_int;
218
219         case 'n':               /* Number of characters consumed */
220           val = (q-buffer);
221           goto set_integer;
222
223         scan_int:
224           q = skipspace(q);
225           if ( !*q ) {
226             bail = bail_eof;
227             break;
228           }
229           val = strntoumax(q, (char **)&qq, base, width);
230           if ( qq == q ) {
231             bail = bail_err;
232             break;
233           }
234           q = qq;
235           converted++;
236           /* fall through */
237
238         set_integer:
239           if ( !(flags & FL_SPLAT) ) {
240             switch(rank) {
241             case rank_char:
242               *va_arg(ap, unsigned char *) = (unsigned char)val;
243               break;
244             case rank_short:
245               *va_arg(ap, unsigned short *) = (unsigned short)val;
246               break;
247             case rank_int:
248               *va_arg(ap, unsigned int *) = (unsigned int)val;
249               break;
250             case rank_long:
251               *va_arg(ap, unsigned long *) = (unsigned long)val;
252               break;
253             case rank_longlong:
254               *va_arg(ap, unsigned long long *) = (unsigned long long)val;
255               break;
256             case rank_ptr:
257               *va_arg(ap, void **) = (void *)(uintptr_t)val;
258               break;
259             }
260           }
261           break;
262           
263         case 'c':               /* Character */
264           width = (flags & FL_WIDTH) ? width : 1; /* Default width == 1 */
265           sarg = va_arg(ap, char *);
266           while ( width-- ) {
267             if ( !*q ) {
268               bail = bail_eof;
269               break;
270             }
271             *sarg++ = *q++;
272           }
273           if ( !bail )
274             converted++;
275           break;
276
277         case 's':               /* String */
278           {
279             char *sp;
280             sp = sarg = va_arg(ap, char *);
281             while ( width-- && *q && !isspace((unsigned char)*q) ) {
282               *sp++ = *q++;
283             }
284             if ( sarg != sp ) {
285               *sp = '\0';       /* Terminate output */
286               converted++;
287             } else {
288               bail = bail_eof;
289             }
290           }
291           break;
292           
293         case '[':               /* Character range */
294           sarg = va_arg(ap, char *);
295           state = st_match_init;
296           matchinv = 0;
297           memset(matchmap, 0, sizeof matchmap);
298           break;
299
300         case '%':               /* %% sequence */
301           if ( *q == '%' )
302             q++;
303           else
304             bail = bail_err;
305           break;
306
307         default:                /* Anything else */
308           bail = bail_err;      /* Unknown sequence */
309           break;
310         }
311       }
312       break;
313     
314     case st_match_init:         /* Initial state for %[ match */
315       if ( ch == '^' && !(flags & FL_INV) ) {
316         matchinv = 1;
317       } else {
318         set_bit(matchmap, (unsigned char)ch);
319         state = st_match;
320       }
321       break;
322       
323     case st_match:              /* Main state for %[ match */
324       if ( ch == ']' ) {
325         goto match_run;
326       } else if ( ch == '-' ) {
327         range_start = (unsigned char)ch;
328         state = st_match_range;
329       } else {
330         set_bit(matchmap, (unsigned char)ch);
331       }
332       break;
333       
334     case st_match_range:                /* %[ match after - */
335       if ( ch == ']' ) {
336         set_bit(matchmap, (unsigned char)'-'); /* - was last character */
337         goto match_run;
338       } else {
339         int i;
340         for ( i = range_start ; i < (unsigned char)ch ; i++ )
341           set_bit(matchmap, i);
342         state = st_match;
343       }
344       break;
345
346     match_run:                  /* Match expression finished */
347       qq = q;
348       while ( width && *q && test_bit(matchmap, (unsigned char)*q)^matchinv ) {
349         *sarg++ = *q++;
350       }
351       if ( q != qq ) {
352         *sarg = '\0';
353         converted++;
354       } else {
355         bail = *q ? bail_err : bail_eof;
356       }
357       break;
358     }
359   }
360
361   if ( bail == bail_eof && !converted )
362     converted = -1;             /* Return EOF (-1) */
363
364   return converted;
365 }