Merge branch 'master' of git://localhost/acess2
[tpg/acess2.git] / Usermode / Libraries / libc.so_src / scanf.c
1 /*
2  * Acess2 C Library
3  * - By John Hodge (thePowersGang)
4  *
5  * scanf.c
6  * - *scanf family of functions
7  */
8 #include <stdio.h>
9 #include <stdarg.h>
10 #include <ctype.h>
11 #include <string.h>
12
13 extern void     _SysDebug(const char *format, ...);
14
15 enum e_vcscanf_types
16 {
17         _VCSCANF_NOTYPE,
18         _VCSCANF_INT,
19         _VCSCANF_REAL,
20 };
21
22 enum e_vcscanf_sizes
23 {
24         _VCSCANF_UNDEF,
25         _VCSCANF_CHAR,
26         _VCSCANF_SHORT,
27         _VCSCANF_LONG,
28         _VCSCANF_LONGLONG,
29         _VCSCANF_INTMAX,
30         _VCSCANF_SIZET,
31         _VCSCANF_PTRDIFF,
32         _VCSCANF_LONGDOUBLE
33 };
34
35 // === CODE ===
36 int _vcscanf_int(int (*__getc)(void *), void (*__rewind)(void*), void *h, int base, int maxlen, long long *outval)
37 {
38         char    ich;
39          int    sgn = 1;
40         long long int   ret = 0;
41          int    n_read = 0;
42
43         // Initialise output to something sane
44         *outval = 0;
45
46         // First character
47         // - maxlen == 0 means no limit
48         ich = __getc(h);
49         n_read ++;
50         
51         // Sign?
52         if( ich == '-' || ich == '+' ) {
53                 sgn = (ich == '-' ? -1 : 1);
54                 if( n_read == maxlen )
55                         return n_read;
56                 ich = __getc(h);
57                 n_read ++;
58         }
59         
60         // Determine base
61         if( base == 0 ) {
62                 if( ich != '0' ) {
63                         base = 10;
64                 }
65                 else {
66                         if( n_read == maxlen )
67                                 return n_read;
68                         ich = __getc(h);
69                         n_read ++;
70                         if( ich != 'x' ) {
71                                 base = 8;
72                         }
73                         else {
74                                 base = 16;
75                                 if( n_read == maxlen )
76                                         return n_read;
77                                 ich = __getc(h);
78                                 n_read ++;
79                         }
80                 }
81         }
82         
83         if( ich == 0 ) {
84                 // Oops?
85                 return n_read;
86         }
87         
88         while( ich )
89         {
90                  int    next = -1;
91                 
92                 // Get the digit value
93                 if( base <= 10 ) {
94                         if( '0' <= ich && ich <= '0'+base-1 )
95                                 next = ich - '0';
96                 }
97                 else {
98                         if( '0' <= ich && ich <= '9' )
99                                 next = ich - '0';
100                         if( 'A' <= ich && ich <= 'A'+base-10-1 )
101                                 next = ich - 'A' + 10;
102                         if( 'a' <= ich && ich <= 'a'+base-10-1 )
103                                 next = ich - 'a' + 10;
104                 }
105                 // if it's not a digit, rewind and break
106                 if( next < 0 ) {
107                         __rewind(h);
108                         n_read --;
109                         break;
110                 }
111                 
112                 // Add to the result
113                 ret *= base;
114                 ret += next;
115                 //_SysDebug("- %i/%i read, 0x%x val", n_read, maxlen, ret);
116
117                 // Check if we've reached the limit
118                 if( n_read == maxlen )
119                         break ;
120
121                 // Next character
122                 ich = __getc(h);
123                 n_read ++;
124         }
125         
126         // Apply sign
127         if( sgn == -1 )
128                 ret = -ret;
129
130         *outval = ret;
131         return n_read;
132 }
133
134 int _vcscanf(int (*__getc)(void*), void (*__rewind)(void*), void *h, const char *format, va_list ap)
135 {
136         char    fch, ich;
137          int    ret = 0, nch = 0;
138         
139         while( (fch = *format++) )
140         {
141                 union {
142                         void    *_void;
143                         char    *_char;
144                         short   *_short;
145                          int    *_int;
146                         long    *_long;
147                         long long       *_longlong;
148                 }       ptr;
149                 long long       ival;
150                 //long double   rval;
151                  int    maxlen = 0;
152                 // int  offset = -1;
153                 enum e_vcscanf_sizes    size = _VCSCANF_UNDEF;
154                 enum e_vcscanf_types    valtype;
155                  int    fail = 0;
156                  int    nnewch;
157
158                 const char      *set_start;
159                  int    set_len;                
160
161                 // Whitespace matches any ammount of whitespace (including none)
162                 if( isspace(fch) )
163                 {
164                         while( (ich = __getc(h)) && isspace(ich) )
165                                 nch ++;
166                         if(ich) __rewind(h);
167                         continue ;
168                 }
169                 
170                 // Any non-whitespace and non-% characters must match exactly
171                 if( fch != '%' )
172                 {
173                         if( __getc(h) != fch )
174                                 break;
175                         nch ++;
176                         continue ;
177                 }
178                 
179                 // Format specifier
180                 fch = *format++;
181                 if(!fch)        break;
182
183                 // Catch '%%' early
184                 if( fch == '%' ) {
185                         if( __getc(h) != '%' )
186                                 break;
187                         nch ++;
188                         continue ;
189                 }               
190
191                 // %n$ - Direct offset selection, shouldn't be mixed with just %
192                 #if 0
193                 for( ; isdigit(fch); fch = *format++ )
194                         maxlen = maxlen * 10 + (fch - '0');
195                 if( fch == '$' ) {
196                         offset = maxlen;
197                         maxlen = 0;
198                 }
199                 #endif
200                 
201                 // Supress assignemnt?
202                 if( fch == '*' )
203                 {
204                         fch = *format++;
205                         ptr._void = NULL;
206                         ret --;
207                 }
208                 else
209                         ptr._void = va_arg(ap, void*);
210                 
211                 // Max field length
212                 while( isdigit(fch) )
213                 {
214                         maxlen = maxlen * 10 + fch - '0';
215                         fch = *format++;
216                 }
217                 
218                 // Length modifier
219                 switch( fch )
220                 {
221                 case 'h':
222                         // 'short'
223                         size = _VCSCANF_SHORT;
224                         fch = *format++;
225                         if( fch == 'h' ) {
226                                 // 'char'
227                                 size = _VCSCANF_CHAR;
228                                 fch = *format++;
229                         }
230                         break;
231                 case 'l':
232                         // 'long'
233                         size = _VCSCANF_LONG;
234                         fch = *format++;
235                         if( fch == 'l' ) {
236                                 // 'long long'
237                                 size = _VCSCANF_LONGLONG;
238                                 fch = *format++;
239                         }
240                         break;
241                 case 'j':
242                         // '(u)intmax_t'
243                         size = _VCSCANF_INTMAX;
244                         fch = *format++;
245                         break;
246                 case 'z':
247                         // 'size_t'
248                         size = _VCSCANF_SIZET;
249                         fch = *format++;
250                         break;
251                 case 't':
252                         // 'ptrdiff_t'
253                         size = _VCSCANF_PTRDIFF;
254                         fch = *format++;
255                         break;
256                 case 'L':
257                         // 'long double' (a, A, e, E, f, F, g, G)
258                         size = _VCSCANF_LONGDOUBLE;
259                         fch = *format++;
260                         break;
261                 }
262                 
263                 // Format specifiers
264                 switch( fch )
265                 {
266                 // Decimal integer
267                 case 'd':
268                         nnewch = _vcscanf_int(__getc, __rewind, h, 10, maxlen, &ival);
269                         if(nnewch==0)   fail=1;
270                         nch += nnewch;
271                         valtype = _VCSCANF_INT;
272                         break;
273                 // variable-base integer
274                 case 'i':
275                         nnewch = _vcscanf_int(__getc, __rewind, h, 0, maxlen, &ival);
276                         if(nnewch==0)   fail=1;
277                         nch += nnewch;
278                         valtype = _VCSCANF_INT;
279                         break;
280                 // Octal integer
281                 case 'o':
282                         nnewch = _vcscanf_int(__getc, __rewind, h, 8, maxlen, &ival);
283                         if(nnewch==0)   fail=1;
284                         nch += nnewch;
285                         valtype = _VCSCANF_INT;
286                         break;
287                 // Hexadecimal integer
288                 case 'x': case 'X':
289                         nnewch = _vcscanf_int(__getc, __rewind, h, 16, maxlen, &ival);
290                         if(nnewch==0)   fail=1;
291                         nch += nnewch;
292                         valtype = _VCSCANF_INT;
293                         break;
294                 // strtod format float
295                 case 'a': case 'A':
296                 case 'e': case 'E':
297                 case 'f': case 'F':
298                 case 'g': case 'G':
299                         valtype = _VCSCANF_REAL;
300                         break;
301                 // `maxlen` or 1 characters
302                 case 'c':
303                         if( maxlen == 0 )
304                                 maxlen = 1;
305                         while( maxlen -- && (ich = __getc(h)) )
306                         {
307                                 if(ptr._char)   *ptr._char++ = ich;
308                                 nch ++;
309                         }
310                         valtype = _VCSCANF_NOTYPE;
311                         break;
312                 // sequence of non-whitespace characters
313                 case 's':
314                         if( maxlen == 0 )
315                                 maxlen = -1;
316
317                         ich = 0;
318                         while( maxlen -- && (ich = __getc(h)) && !isspace(ich) )
319                         {
320                                 if(ptr._char)   *ptr._char++ = ich;
321                                 nch ++;
322                         }
323                         if( maxlen >= 0 && ich )
324                                 __rewind(h);
325                         if(ptr._char)   *ptr._char++ = 0;
326                         valtype = _VCSCANF_NOTYPE;
327                         break;
328                 // match a set of characters
329                 case '[':
330                         fch = *format++;
331                         if( fch == '^' ) {
332                                 // Invert
333                                 fch = *format;
334                         }
335                         set_start = format;
336                         set_len = 0;
337                         // if the first character is ']' it's part of the set
338                         do {
339                                 // permissable character
340                                 set_len ++;
341                                 fch = *format++;
342                         } while( fch && fch != ']' );
343
344                         if( maxlen == 0 )
345                                 maxlen = -1;
346                         ich = 0;
347                         while( maxlen -- && (ich = __getc(h)) && memchr(set_start, set_len, ich) )
348                         {
349                                 if(ptr._char)   *ptr._char++ = ich;
350                                 nch ++;
351                         }
352                         if( maxlen >= 0 && ich )
353                                 __rewind(h);
354                         if(ptr._char)   *ptr._char++ = 0;
355                         valtype = _VCSCANF_NOTYPE;
356                         break;
357                 case 'p': // read back printf("%p")
358                         valtype = _VCSCANF_NOTYPE;
359                         break;
360                 case 'n': // number of read characters to this point
361                         if(ptr._int) {
362                                 *ptr._int = nch;
363                                 ret --; // negates the ret ++ at the end
364                         }
365                         valtype = _VCSCANF_NOTYPE;
366                         break;
367                 default:
368                         // Implimentation defined
369                         valtype = _VCSCANF_NOTYPE;
370                         break;
371                 }
372
373                 if(fail)
374                         break;          
375
376                 switch(valtype)
377                 {
378                 case _VCSCANF_NOTYPE:
379                         // Used when assignment is done above
380                         break;
381                 case _VCSCANF_INT:
382                         switch(size)
383                         {
384                         case _VCSCANF_UNDEF:    *ptr._int = ival;       break;
385                         case _VCSCANF_CHAR:     *ptr._char = ival;      break;
386                         case _VCSCANF_SHORT:    *ptr._short = ival;     break;
387                         case _VCSCANF_LONG:     *ptr._long = ival;      break;
388                         case _VCSCANF_LONGLONG: *ptr._longlong = ival;  break;
389                         default:        // ???
390                                 break;
391                         }
392                         break;
393                 case _VCSCANF_REAL:
394                         break;
395                 }
396                 
397                 ret ++;
398         }
399         
400         return ret;
401 }
402
403 int _vsscanf_getc(void *h)
404 {
405         const char      **ibuf = h;
406         return *(*ibuf)++;      // Dereference, read for return, increment
407 }
408
409 void _vsscanf_rewind(void *h)
410 {
411         const char      **ibuf = h;
412         (*ibuf) --;     // NOTE: Breaks if there's a rewind before a getc, but that shouldn't happen
413 }
414
415 int vscanf(const char *format, va_list ap)
416 {
417         return vfscanf(stdin, format, ap);
418 }
419
420 int vsscanf(const char *str, const char *format, va_list ap)
421 {
422         return _vcscanf(_vsscanf_getc, _vsscanf_rewind, &str, format, ap);
423 }
424
425 int _vfscanf_getc(void *h)
426 {
427         int ch = fgetc(h);
428         return ch == -1 ? 0 : ch;
429 }
430 void _vfscanf_rewind(void *h)
431 {
432         fseek(h, -1, SEEK_CUR);
433 }
434
435 int vfscanf(FILE *stream, const char *format, va_list ap)
436 {
437         return _vcscanf(_vfscanf_getc, _vfscanf_rewind, stream, format, ap);
438 }
439
440 int scanf(const char *format, ...)
441 {
442         va_list args;
443          int    rv;
444         va_start(args, format);
445         rv = vfscanf(stdin, format, args);
446         va_end(args);
447         return rv;
448 }
449 int fscanf(FILE *stream, const char *format, ...)
450 {
451         va_list args;
452          int    rv;
453         va_start(args, format);
454         rv = vfscanf(stream, format, args);
455         va_end(args);
456         return rv;
457 }
458 int sscanf(const char *str, const char *format, ...)
459 {
460         va_list args;
461          int    rv;
462         va_start(args, format);
463         rv = vsscanf(str, format, args);
464         va_end(args);
465         return rv;
466 }
467

UCC git Repository :: git.ucc.asn.au