]> git.sur5r.net Git - cc65/blob - libsrc/common/_scanf.c
Fixed geos portion of Makefile
[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
173     /* Place copies of the arguments into global variables. This is not very
174      * nice, but on a 6502 platform it gives better code, since the values
175      * do not have to be passed as parameters.
176      */
177     D   = D_;
178     ap  = ap_;
179
180     /* Initialize variables */
181     Conversions = 0;
182     D->ccount   = 0;
183
184     /* Set up the jump label. The get() routine will use this label when EOF
185      * is reached.
186      */
187     Result = setjmp (JumpBuf);
188     printf ("Result = %u\n", Result);
189     if (Result == RC_OK) {
190
191 Again:
192         /* Get the next input character */
193         ReadChar ();
194
195         /* Walk over the format string */
196         while (F = *format++) {
197
198             /* Check for a conversion */
199             if (F != '%' || *format == '%') {
200
201                 /* %% or any char other than % */
202                 if (F == '%') {
203                     ++format;
204                 }
205
206                 /* Check for a match */
207                 if (isspace (F)) {
208
209                     /* Special white space handling: Any whitespace matches
210                      * any amount of whitespace including none(!). So this
211                      * match will never fail.
212                      */
213                     SkipWhite ();
214                     continue;
215
216                 } else if (F != C) {
217
218                     /* A mismatch. We will stop scanning the input and return
219                      * the number of conversions.
220                      */
221                     printf ("F = '%c', C = '%c' --> mismatch\n", F, C);
222                     return Conversions;
223
224                 } else {
225
226                     /* A match. Read the next input character and start over */
227                     goto Again;
228
229                 }
230
231             } else {
232
233                 /* A conversion. Skip the percent sign. */
234                 F = *format++;
235
236                 /* Initialize variables */
237                 NoAssign    = 0;
238                 IsShort     = 0;
239                 IsLong      = 0;
240                 Width       = UINT_MAX;
241
242                 /* Check for flags. */
243                 while (1) {
244                     if (isdigit (F)) {
245                         Width = 0;
246                         do {
247                             /* ### Non portable ### */
248                             Width = Width * 10 + (F & 0x0F);
249                             F = *format++;
250                         } while (isdigit (F));
251                     } else {
252                         switch (F) {
253                             case '*':   NoAssign = 1;   break;
254                             case 'h':   IsShort = 1;    break;
255                             case 'l':
256                             case 'L':   IsLong = 1;     break;
257                             default:    goto FlagsDone;
258                         }
259                         F = *format++;
260                     }
261                 }
262 FlagsDone:
263
264                 /* Check for the actual conversion character */
265                 printf ("F = '%c'\n", F);
266                 switch (F) {
267
268                     case 'D':
269                         IsLong = 1;
270                     case 'd':
271                         /* Optionally signed decimal integer */
272                         SkipWhite ();
273                         ReadSign ();
274                         ReadInt (10);
275                         if (!Positive) {
276                             IntVal = -IntVal;
277                         }
278                         AssignInt ();
279                         break;
280
281                     case 'i':
282                         /* Optionally signed integer with a base */
283                         SkipWhite ();
284                         ReadSign ();
285                         if (C == '0') {
286                             ReadChar ();
287                             switch (C) {
288                                 case 'x':
289                                 case 'X':
290                                     Base = 16;
291                                     ReadChar();
292                                     break;
293                                 default:
294                                     Base = 8;
295                             }
296                         } else {
297                             Base = 10;
298                         }
299                         ReadInt (Base);
300                         if (!Positive) {
301                             IntVal = -IntVal;
302                         }
303                         AssignInt ();
304                         break;
305
306                     case 'o':
307                         /* Unsigned octal integer */
308                         SkipWhite ();
309                         ReadInt (8);
310                         AssignInt ();
311                         break;
312
313                     case 'u':
314                         /* Unsigned decimal integer */
315                         SkipWhite ();
316                         ReadInt (10);
317                         AssignInt ();
318                         break;
319
320                     case 'x':
321                     case 'X':
322                         /* Unsigned hexadecimal integer */
323                         SkipWhite ();
324                         ReadInt (16);
325                         AssignInt ();
326                         break;
327
328                     case 'E':
329                     case 'e':
330                     case 'f':
331                     case 'g':
332                         /* Optionally signed float */
333                         break;
334
335                     case 's':
336                         /* Whitespace terminated string */
337                         SkipWhite ();
338                         S = NoAssign? 0 : va_arg (ap, char*);
339                         while (C && !isspace (C) && Width--) {
340                             if (S) {
341                                 *S++ = C;
342                             }
343                             ReadChar ();
344                         }
345                         break;
346
347                     case 'c':
348                         /* Fixed length string */
349                         break;
350
351                     case '[':
352                         /* String using characters from a set */
353                         break;
354
355                     case 'p':
356                         /* Pointer */
357                         break;
358
359                     case 'n':
360                         /* Store characters consumed so far */
361                         IntVal = D->ccount;
362                         IsLong = 0;
363                         AssignInt ();
364                         break;
365
366                     default:
367                         /* Invalid conversion */
368                         break;
369
370                 }
371
372                 /* Skip the format char */
373                 goto Again;
374
375             }
376
377         }
378
379     } else if (Result == RC_EOF) {
380
381         /* Jump via JumpBuf means EOF on input */
382         if (D->ccount == 0) {
383             /* Special case: error */
384             return -1;
385         }
386
387     }
388
389     /* Return the number of conversions */
390     return Conversions;
391 }
392
393
394