]> git.sur5r.net Git - cc65/blob - libsrc/common/_scanf.c
Working on the _scanf implementation
[cc65] / libsrc / common / _scanf.c
1 /*
2  * _scanf.c
3  *
4  * (C) Copyright 2001 Ullrich von Bassewitz (uz@cc65.org)
5  *
6  * This is the basic layer for all scanf type functions.
7  */
8
9
10
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <setjmp.h>
14 #include <ctype.h>
15 #include <limits.h>
16
17 #include "_scanf.h"
18
19
20
21 /*****************************************************************************/
22 /*                            SetJmp return codes                            */
23 /*****************************************************************************/
24
25
26
27 #define RC_OK           0               /* Regular call */
28 #define RC_EOF          1               /* EOF reached */
29 #define RC_NOCONV       2               /* No conversion possible */
30
31
32
33 /*****************************************************************************/
34 /*                                   Data                                    */
35 /*****************************************************************************/
36
37
38
39 static struct indesc*   D;              /* Copy of function argument */
40 static va_list          ap;             /* Copy of function argument */
41 static jmp_buf          JumpBuf;        /* Label that is used in case of EOF */
42 static char             C;              /* Character from input */
43 static unsigned         Width;          /* Maximum field width */
44 static long             IntVal;         /* Converted int value */
45 static unsigned         Conversions;    /* Number of conversions */
46
47 /* Flags */
48 static unsigned char    Positive;       /* Flag for positive value */
49 static unsigned char    NoAssign;       /* Supppress assigment */
50 static unsigned char    IsShort;        /* Short type */
51 static unsigned char    IsLong;         /* Long type */
52
53
54
55 /*****************************************************************************/
56 /*                              Character sets                               */
57 /*****************************************************************************/
58
59
60
61 /*****************************************************************************/
62 /*                                   Code                                    */
63 /*****************************************************************************/
64
65
66
67 static void ReadChar (void)
68 /* Get an input character, count characters */
69 {
70     C = D->fin (D);
71     ++D->ccount;
72 }
73
74
75
76 static void SkipWhite (void)
77 /* Skip white space in the input and return the first non white character */
78 {
79     while (isspace (C)) {
80         ReadChar ();
81     }
82 }
83
84
85
86 static void ReadSign (void)
87 /* Read an optional sign and skip it. Store 1 in Positive if the value is
88  * positive, store 0 otherwise.
89  */
90 {
91     switch (C) {
92         case '-':
93             ReadChar ();
94             Positive = 0;
95         case '+':
96             ReadChar ();
97             /* FALLTHROUGH */
98         default:
99             Positive = 1;
100     }
101 }
102
103
104
105 static unsigned char HexVal (char C)
106 /* Convert a digit to a value */
107 {
108
109     if (isdigit (C)) {
110         return C - '0';
111     } else {
112         return C - toupper (C) + ('A' + 10);
113     }
114 }
115
116
117
118 static void ReadInt (unsigned char Base)
119 /* Read an integer and store it into IntVal */
120 {
121     /* Value must start with a digit */
122     if (!isdigit (C)) {
123         longjmp (JumpBuf, RC_NOCONV);
124     }
125
126     /* Read the value */
127     IntVal = 0;
128     while (isxdigit (C) && Width-- > 0) {
129         printf ("ReadInt: '%c'\n", C);
130         IntVal = IntVal * Base + HexVal (C);
131         ReadChar ();
132     }
133
134     /* One more conversion */
135     ++Conversions;
136 }
137
138
139
140 static void AssignInt (void)
141 /* Assign the integer value in Val to the next argument. The function makes
142  * several non portable assumptions to reduce code size:
143  *   - int and unsigned types have the same representation
144  *   - short and int have the same representation.
145  *   - all pointer types have the same representation.
146  */
147 {
148     if (!NoAssign) {
149         /* Get the next argument pointer */
150         void* P = va_arg (ap, void*);
151
152         /* Assign to the converted value */
153         if (IsLong) {
154             *(long*)P = IntVal;
155         } else {
156             *(int*)P = (int) IntVal;
157         }
158     }
159 }
160
161
162
163 int _scanf (struct indesc* D_, const char* format, va_list ap_)
164 /* This is the routine used to do the actual work. It is called from several
165  * types of wrappers to implement the actual ISO xxscanf functions.
166  */
167 {
168     char          F;            /* Character from format string */
169     unsigned char Result;       /* setjmp result */
170     char*         S;
171     unsigned char Base;         /* Integer base in %i */
172     unsigned char HaveWidth;    /* True if a width was given */
173
174     /* Place copies of the arguments into global variables. This is not very
175      * nice, but on a 6502 platform it gives better code, since the values
176      * do not have to be passed as parameters.
177      */
178     D   = D_;
179     ap  = ap_;
180
181     /* Initialize variables */
182     Conversions = 0;
183     D->ccount   = 0;
184
185     /* Set up the jump label. The get() routine will use this label when EOF
186      * is reached.
187      */
188     Result = setjmp (JumpBuf);
189     printf ("Result = %u\n", Result);
190     if (Result == RC_OK) {
191
192 Again:
193         /* Get the next input character */
194         ReadChar ();
195
196         /* Walk over the format string */
197         while (F = *format++) {
198
199             /* Check for a conversion */
200             if (F != '%' || *format == '%') {
201
202                 /* %% or any char other than % */
203                 if (F == '%') {
204                     ++format;
205                 }
206
207                 /* Check for a match */
208                 if (isspace (F)) {
209
210                     /* Special white space handling: Any whitespace matches
211                      * any amount of whitespace including none(!). So this
212                      * match will never fail.
213                      */
214                     SkipWhite ();
215                     continue;
216
217                 } else if (F != C) {
218
219                     /* A mismatch. We will stop scanning the input and return
220                      * the number of conversions.
221                      */
222                     printf ("F = '%c', C = '%c' --> mismatch\n", F, C);
223                     return Conversions;
224
225                 } else {
226
227                     /* A match. Read the next input character and start over */
228                     goto Again;
229
230                 }
231
232             } else {
233
234                 /* A conversion. Skip the percent sign. */
235                 F = *format++;
236
237                 /* Initialize variables */
238                 NoAssign    = 0;
239                 IsShort     = 0;
240                 IsLong      = 0;
241                 Width       = UINT_MAX;
242                 HaveWidth   = 0;
243
244                 /* Check for flags. */
245                 while (1) {
246                     if (isdigit (F)) {
247                         HaveWidth = 1;
248                         Width     = 0;
249                         do {
250                             /* ### Non portable ### */
251                             Width = Width * 10 + (F & 0x0F);
252                             F = *format++;
253                         } while (isdigit (F));
254                     } else {
255                         switch (F) {
256                             case '*':   NoAssign = 1;   break;
257                             case 'h':   IsShort = 1;    break;
258                             case 'l':
259                             case 'L':   IsLong = 1;     break;
260                             default:    goto FlagsDone;
261                         }
262                         F = *format++;
263                     }
264                 }
265 FlagsDone:
266
267                 /* Check for the actual conversion character */
268                 printf ("F = '%c'\n", F);
269                 switch (F) {
270
271                     case 'D':
272                         IsLong = 1;
273                     case 'd':
274                         /* Optionally signed decimal integer */
275                         SkipWhite ();
276                         ReadSign ();
277                         ReadInt (10);
278                         if (!Positive) {
279                             IntVal = -IntVal;
280                         }
281                         AssignInt ();
282                         break;
283
284                     case 'i':
285                         /* Optionally signed integer with a base */
286                         SkipWhite ();
287                         ReadSign ();
288                         if (C == '0') {
289                             ReadChar ();
290                             switch (C) {
291                                 case 'x':
292                                 case 'X':
293                                     Base = 16;
294                                     ReadChar();
295                                     break;
296                                 default:
297                                     Base = 8;
298                             }
299                         } else {
300                             Base = 10;
301                         }
302                         ReadInt (Base);
303                         if (!Positive) {
304                             IntVal = -IntVal;
305                         }
306                         AssignInt ();
307                         break;
308
309                     case 'o':
310                         /* Unsigned octal integer */
311                         SkipWhite ();
312                         ReadInt (8);
313                         AssignInt ();
314                         break;
315
316                     case 'u':
317                         /* Unsigned decimal integer */
318                         SkipWhite ();
319                         ReadInt (10);
320                         AssignInt ();
321                         break;
322
323                     case 'x':
324                     case 'X':
325                         /* Unsigned hexadecimal integer */
326                         SkipWhite ();
327                         ReadInt (16);
328                         AssignInt ();
329                         break;
330
331                     case 'E':
332                     case 'e':
333                     case 'f':
334                     case 'g':
335                         /* Optionally signed float */
336                         longjmp (JumpBuf, RC_NOCONV);
337                         break;
338
339                     case 's':
340                         /* Whitespace terminated string */
341                         SkipWhite ();
342                         if (!NoAssign) {
343                             S = va_arg (ap, char*);
344                         }
345                         while (!isspace (C) && Width--) {
346                             if (!NoAssign) {
347                                 *S++ = C;
348                             }
349                             ReadChar ();
350                         }
351                         /* Terminate the string just read */
352                         if (!NoAssign) {
353                             *S = '\0';
354                         }
355                         break;
356
357                     case 'c':
358                         /* Fixed length string, NOT zero terminated */
359                         if (!HaveWidth) {
360                             /* No width given, default is 1 */
361                             Width = 1;
362                         }
363                         if (!NoAssign) {
364                             S = va_arg (ap, char*);
365                         }
366                         while (Width--) {
367                             if (!NoAssign) {
368                                 *S++ = C;
369                             }
370                             ReadChar ();
371                         }
372                         ++Conversions;
373                         break;
374
375                     case '[':
376                         /* String using characters from a set */
377                         break;
378
379                     case 'p':
380                         /* Pointer */
381                         break;
382
383                     case 'n':
384                         /* Store characters consumed so far */
385                         IntVal = D->ccount;
386                         IsLong = 0;
387                         AssignInt ();
388                         break;
389
390                     default:
391                         /* Invalid conversion */
392                         longjmp (JumpBuf, RC_NOCONV);
393                         break;
394
395                 }
396
397                 /* Skip the format char */
398                 goto Again;
399
400             }
401
402         }
403
404     } else if (Result == RC_EOF) {
405
406         /* Jump via JumpBuf means EOF on input */
407         if (D->ccount == 0) {
408             /* Special case: error */
409             return -1;
410         }
411
412     }
413
414     /* Return the number of conversions */
415     return Conversions;
416 }
417
418
419