3a561c4bc21e23e12f6679ef470ea1ba61639caf
[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, offset = -1;
152                 enum e_vcscanf_sizes    size = _VCSCANF_UNDEF;
153                 enum e_vcscanf_types    valtype;
154
155                 const char      *set_start;
156                  int    set_len;                
157
158                 // Whitespace matches any ammount of whitespace (including none)
159                 if( isspace(fch) )
160                 {
161                         while( (ich = __getc(h)) && isspace(ich) )
162                                 nch ++;
163                         continue ;
164                 }
165                 
166                 // Any non-whitespace and non-% characters must match exactly
167                 if( fch != '%' )
168                 {
169                         if( __getc(h) != fch )
170                                 break;
171                         nch ++;
172                         continue ;
173                 }
174                 
175                 // Format specifier
176                 fch = *format++;
177                 if(!fch)        break;
178
179                 // Catch '%%' early
180                 if( fch == '%' ) {
181                         if( __getc(h) != '%' )
182                                 break;
183                         nch ++;
184                         continue ;
185                 }               
186
187                 // %n$ - Direct offset selection, shouldn't be mixed with just %
188                 for( ; isdigit(fch); fch = *format++ )
189                         maxlen = maxlen * 10 + (fch - '0');
190                 if( fch == '$' ) {
191                         offset = maxlen;
192                         maxlen = 0;
193                 }
194                 
195                 // Supress assignemnt?
196                 if( fch == '*' )
197                 {
198                         fch = *format++;
199                         ptr._void = NULL;
200                         ret --;
201                 }
202                 else
203                         ptr._void = va_arg(ap, void*);
204                 
205                 // Max field length
206                 while( isdigit(fch) )
207                 {
208                         maxlen = maxlen * 10 + fch - '0';
209                         fch = *format++;
210                 }
211                 
212                 // Length modifier
213                 switch( fch )
214                 {
215                 case 'h':
216                         // 'short'
217                         size = _VCSCANF_SHORT;
218                         fch = *format++;
219                         if( fch == 'h' ) {
220                                 // 'char'
221                                 size = _VCSCANF_CHAR;
222                                 fch = *format++;
223                         }
224                         break;
225                 case 'l':
226                         // 'long'
227                         size = _VCSCANF_LONG;
228                         fch = *format++;
229                         if( fch == 'l' ) {
230                                 // 'long long'
231                                 size = _VCSCANF_LONGLONG;
232                                 fch = *format++;
233                         }
234                         break;
235                 case 'j':
236                         // '(u)intmax_t'
237                         size = _VCSCANF_INTMAX;
238                         fch = *format++;
239                         break;
240                 case 'z':
241                         // 'size_t'
242                         size = _VCSCANF_SIZET;
243                         fch = *format++;
244                         break;
245                 case 't':
246                         // 'ptrdiff_t'
247                         size = _VCSCANF_PTRDIFF;
248                         fch = *format++;
249                         break;
250                 case 'L':
251                         // 'long double' (a, A, e, E, f, F, g, G)
252                         size = _VCSCANF_LONGDOUBLE;
253                         fch = *format++;
254                         break;
255                 }
256                 
257                 // Format specifiers
258                 switch( fch )
259                 {
260                 // Decimal integer
261                 case 'd':
262                         nch += _vcscanf_int(__getc, __rewind, h, 10, maxlen, &ival);
263                         valtype = _VCSCANF_INT;
264                         break;
265                 // variable-base integer
266                 case 'i':
267                         nch += _vcscanf_int(__getc, __rewind, h, 0, maxlen, &ival);
268                         valtype = _VCSCANF_INT;
269                         break;
270                 // Octal integer
271                 case 'o':
272                         nch += _vcscanf_int(__getc, __rewind, h, 8, maxlen, &ival);
273                         valtype = _VCSCANF_INT;
274                         break;
275                 // Hexadecimal integer
276                 case 'x': case 'X':
277                         nch += _vcscanf_int(__getc, __rewind, h, 16, maxlen, &ival);
278                         valtype = _VCSCANF_INT;
279                         break;
280                 // strtod format float
281                 case 'a': case 'A':
282                 case 'e': case 'E':
283                 case 'f': case 'F':
284                 case 'g': case 'G':
285                         valtype = _VCSCANF_REAL;
286                         break;
287                 // `maxlen` or 1 characters
288                 case 'c':
289                         if( maxlen == 0 )
290                                 maxlen = 1;
291                         while( maxlen -- && (ich = __getc(h)) )
292                         {
293                                 if(ptr._char)   *ptr._char++ = ich;
294                                 nch ++;
295                         }
296                         valtype = _VCSCANF_NOTYPE;
297                         break;
298                 // sequence of non-whitespace characters
299                 case 's':
300                         if( maxlen == 0 )
301                                 maxlen = -1;
302
303                         ich = 0;
304                         while( maxlen -- && (ich = __getc(h)) && !isblank(ich) )
305                         {
306                                 if(ptr._char)   *ptr._char++ = ich;
307                                 nch ++;
308                         }
309                         if( maxlen >= 0 && ich )
310                                 __rewind(h);
311                         valtype = _VCSCANF_NOTYPE;
312                         break;
313                 // match a set of characters
314                 case '[':
315                         fch = *format++;
316                         if( fch == '^' ) {
317                                 // Invert
318                                 fch = *format;
319                         }
320                         set_start = format;
321                         set_len = 0;
322                         // if the first character is ']' it's part of the set
323                         do {
324                                 // permissable character
325                                 set_len ++;
326                                 fch = *format++;
327                         } while( fch && fch != ']' );
328
329                         ich = 0;
330                         while( maxlen -- && (ich = __getc(h)) && memchr(set_start, set_len, ich) )
331                         {
332                                 if(ptr._char)   *ptr._char++ = ich;
333                                 nch ++;
334                         }
335                         if( maxlen >= 0 && ich )
336                                 __rewind(h);
337                         valtype = _VCSCANF_NOTYPE;
338                         break;
339                 case 'p': // read back printf("%p")
340                         valtype = _VCSCANF_NOTYPE;
341                         break;
342                 case 'n': // number of read characters to this point
343                         if(ptr._int) {
344                                 *ptr._int = nch;
345                                 ret --; // negates the ret ++ at the end
346                         }
347                         valtype = _VCSCANF_NOTYPE;
348                         break;
349                 default:
350                         // Implimentation defined
351                         valtype = _VCSCANF_NOTYPE;
352                         break;
353                 }
354                 
355                 switch(valtype)
356                 {
357                 case _VCSCANF_NOTYPE:
358                         // Used when assignment is done above
359                         break;
360                 case _VCSCANF_INT:
361                         switch(size)
362                         {
363                         case _VCSCANF_UNDEF:    *ptr._int = ival;       break;
364                         case _VCSCANF_CHAR:     *ptr._char = ival;      break;
365                         case _VCSCANF_SHORT:    *ptr._short = ival;     break;
366                         case _VCSCANF_LONG:     *ptr._long = ival;      break;
367                         case _VCSCANF_LONGLONG: *ptr._longlong = ival;  break;
368                         default:        // ???
369                                 break;
370                         }
371                         break;
372                 case _VCSCANF_REAL:
373                         break;
374                 }
375                 
376                 ret ++;
377         }
378         
379         return ret;
380 }
381
382 int _vsscanf_getc(void *h)
383 {
384         const char      **ibuf = h;
385         return *(*ibuf)++;      // Dereference, read for return, increment
386 }
387
388 void _vsscanf_rewind(void *h)
389 {
390         const char      **ibuf = h;
391         (*ibuf) --;     // NOTE: Breaks if there's a rewind before a getc, but that shouldn't happen
392 }
393
394 int vscanf(const char *format, va_list ap)
395 {
396         return vfscanf(stdin, format, ap);
397 }
398
399 int vsscanf(const char *str, const char *format, va_list ap)
400 {
401         return _vcscanf(_vsscanf_getc, _vsscanf_rewind, &str, format, ap);
402 }
403
404 int _vfscanf_getc(void *h)
405 {
406         return fgetc(h);        // TODO: Handle -1 -> 0
407 }
408 void _vfscanf_rewind(void *h)
409 {
410         fseek(h, -1, SEEK_CUR);
411 }
412
413 int vfscanf(FILE *stream, const char *format, va_list ap)
414 {
415         return _vcscanf(_vfscanf_getc, _vfscanf_rewind, stream, format, ap);
416 }
417
418 int scanf(const char *format, ...)
419 {
420         va_list args;
421          int    rv;
422         va_start(args, format);
423         rv = vfscanf(stdin, format, args);
424         va_end(args);
425         return rv;
426 }
427 int fscanf(FILE *stream, const char *format, ...)
428 {
429         va_list args;
430          int    rv;
431         va_start(args, format);
432         rv = vfscanf(stream, format, args);
433         va_end(args);
434         return rv;
435 }
436 int sscanf(const char *str, const char *format, ...)
437 {
438         va_list args;
439          int    rv;
440         va_start(args, format);
441         rv = vsscanf(str, format, args);
442         va_end(args);
443         return rv;
444 }
445

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