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

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