Usermode/libc - Fixing errors from clang, disabled heap for native build
[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                         int invert = 0;
331                         if( *format++ == '^' ) {
332                                 // Invert
333                                 invert = 1;
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)) && invert == !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                         }
358                 case 'p': // read back printf("%p")
359                         valtype = _VCSCANF_NOTYPE;
360                         break;
361                 case 'n': // number of read characters to this point
362                         if(ptr._int) {
363                                 *ptr._int = nch;
364                                 ret --; // negates the ret ++ at the end
365                         }
366                         valtype = _VCSCANF_NOTYPE;
367                         break;
368                 default:
369                         // Implimentation defined
370                         valtype = _VCSCANF_NOTYPE;
371                         break;
372                 }
373
374                 if(fail)
375                         break;          
376
377                 switch(valtype)
378                 {
379                 case _VCSCANF_NOTYPE:
380                         // Used when assignment is done above
381                         break;
382                 case _VCSCANF_INT:
383                         switch(size)
384                         {
385                         case _VCSCANF_UNDEF:    *ptr._int = ival;       break;
386                         case _VCSCANF_CHAR:     *ptr._char = ival;      break;
387                         case _VCSCANF_SHORT:    *ptr._short = ival;     break;
388                         case _VCSCANF_LONG:     *ptr._long = ival;      break;
389                         case _VCSCANF_LONGLONG: *ptr._longlong = ival;  break;
390                         default:        // ???
391                                 break;
392                         }
393                         break;
394                 case _VCSCANF_REAL:
395                         break;
396                 }
397                 
398                 ret ++;
399         }
400         
401         return ret;
402 }
403
404 int _vsscanf_getc(void *h)
405 {
406         const char      **ibuf = h;
407         return *(*ibuf)++;      // Dereference, read for return, increment
408 }
409
410 void _vsscanf_rewind(void *h)
411 {
412         const char      **ibuf = h;
413         (*ibuf) --;     // NOTE: Breaks if there's a rewind before a getc, but that shouldn't happen
414 }
415
416 int vscanf(const char *format, va_list ap)
417 {
418         return vfscanf(stdin, format, ap);
419 }
420
421 int vsscanf(const char *str, const char *format, va_list ap)
422 {
423         return _vcscanf(_vsscanf_getc, _vsscanf_rewind, &str, format, ap);
424 }
425
426 int _vfscanf_getc(void *h)
427 {
428         int ch = fgetc(h);
429         return ch == -1 ? 0 : ch;
430 }
431 void _vfscanf_rewind(void *h)
432 {
433         fseek(h, -1, SEEK_CUR);
434 }
435
436 int vfscanf(FILE *stream, const char *format, va_list ap)
437 {
438         return _vcscanf(_vfscanf_getc, _vfscanf_rewind, stream, format, ap);
439 }
440
441 int scanf(const char *format, ...)
442 {
443         va_list args;
444          int    rv;
445         va_start(args, format);
446         rv = vfscanf(stdin, format, args);
447         va_end(args);
448         return rv;
449 }
450 int fscanf(FILE *stream, const char *format, ...)
451 {
452         va_list args;
453          int    rv;
454         va_start(args, format);
455         rv = vfscanf(stream, format, args);
456         va_end(args);
457         return rv;
458 }
459 int sscanf(const char *str, const char *format, ...)
460 {
461         va_list args;
462          int    rv;
463         va_start(args, format);
464         rv = vsscanf(str, format, args);
465         va_end(args);
466         return rv;
467 }
468

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