4 * (C) Copyright 2001-2002 Ullrich von Bassewitz (uz@cc65.org)
6 * This is the basic layer for all scanf type functions. It should get
7 * rewritten in assembler at some time in the future, so most of the code
8 * is not as elegant as it could be.
24 /*****************************************************************************/
25 /* SetJmp return codes */
26 /*****************************************************************************/
30 #define RC_OK 0 /* Regular call */
31 #define RC_EOF 1 /* EOF reached */
32 #define RC_NOCONV 2 /* No conversion possible */
36 /*****************************************************************************/
38 /*****************************************************************************/
42 static struct indesc* D; /* Copy of function argument */
43 static va_list ap; /* Copy of function argument */
44 static jmp_buf JumpBuf; /* Label that is used in case of EOF */
45 static char C; /* Character from input */
46 static unsigned Width; /* Maximum field width */
47 static long IntVal; /* Converted int value */
48 static unsigned Conversions; /* Number of conversions */
51 static unsigned char Positive; /* Flag for positive value */
52 static unsigned char NoAssign; /* Supppress assigment */
53 static unsigned char IsShort; /* Short type */
54 static unsigned char IsLong; /* Long type */
55 static unsigned char Invert; /* Do we need to invert the charset? */
56 static unsigned char CharSet[32]; /* 32 * 8 bits = 256 bits */
57 static const unsigned char Bits[8] = {
58 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
63 /*****************************************************************************/
65 /*****************************************************************************/
69 static void AddCharToSet (unsigned char C)
70 /* Set the given bit in the character set */
81 asm ("lda %v,y", Bits);
82 asm ("ora %v,x", CharSet);
83 asm ("sta %v,x", CharSet);
88 static unsigned char IsCharInSet (unsigned char C)
89 /* Check if the given char is part of the character set */
100 asm ("lda %v,y", Bits);
101 asm ("and %v,x", CharSet);
108 /*****************************************************************************/
110 /*****************************************************************************/
114 static void ReadChar (void)
115 /* Get an input character, count characters */
123 static void SkipWhite (void)
124 /* Skip white space in the input and return the first non white character */
126 while (isspace (C)) {
133 static void ReadSign (void)
134 /* Read an optional sign and skip it. Store 1 in Positive if the value is
135 * positive, store 0 otherwise.
152 static unsigned char HexVal (char C)
153 /* Convert a digit to a value */
159 return C - toupper (C) + ('A' + 10);
165 static void ReadInt (unsigned char Base)
166 /* Read an integer and store it into IntVal */
168 /* Value must start with a digit */
170 longjmp (JumpBuf, RC_NOCONV);
175 while (isxdigit (C) && Width-- > 0) {
176 IntVal = IntVal * Base + HexVal (C);
180 /* One more conversion */
186 static void AssignInt (void)
187 /* Assign the integer value in Val to the next argument. The function makes
188 * several non portable assumptions to reduce code size:
189 * - int and unsigned types have the same representation
190 * - short and int have the same representation.
191 * - all pointer types have the same representation.
195 /* Get the next argument pointer */
196 void* P = va_arg (ap, void*);
198 /* Assign to the converted value */
202 *(int*)P = (int) IntVal;
209 int _scanf (struct indesc* D_, const char* format, va_list ap_)
210 /* This is the routine used to do the actual work. It is called from several
211 * types of wrappers to implement the actual ISO xxscanf functions.
214 char F; /* Character from format string */
215 unsigned char Result; /* setjmp result */
217 unsigned char Base; /* Integer base in %i */
218 unsigned char HaveWidth; /* True if a width was given */
219 char Start; /* Start of range */
221 /* Place copies of the arguments into global variables. This is not very
222 * nice, but on a 6502 platform it gives better code, since the values
223 * do not have to be passed as parameters.
228 /* Initialize variables */
232 /* Set up the jump label. The get() routine will use this label when EOF
235 Result = setjmp (JumpBuf);
236 if (Result == RC_OK) {
239 /* Get the next input character */
242 /* Walk over the format string */
243 while (F = *format++) {
245 /* Check for a conversion */
246 if (F != '%' || *format == '%') {
248 /* %% or any char other than % */
253 /* Check for a match */
256 /* Special white space handling: Any whitespace matches
257 * any amount of whitespace including none(!). So this
258 * match will never fail.
265 /* A mismatch. We will stop scanning the input and return
266 * the number of conversions.
272 /* A match. Read the next input character and start over */
279 /* A conversion. Skip the percent sign. */
282 /* Initialize variables */
289 /* Check for flags. */
295 /* ### Non portable ### */
296 Width = Width * 10 + (F & 0x0F);
298 } while (isdigit (F));
301 case '*': NoAssign = 1; break;
302 case 'h': IsShort = 1; break;
304 case 'L': IsLong = 1; break;
305 default: goto FlagsDone;
312 /* Check for the actual conversion character */
318 /* Optionally signed decimal integer */
329 /* Optionally signed integer with a base */
354 /* Unsigned octal integer */
361 /* Unsigned decimal integer */
369 /* Unsigned hexadecimal integer */
379 /* Optionally signed float */
380 longjmp (JumpBuf, RC_NOCONV);
384 /* Whitespace terminated string */
387 S = va_arg (ap, char*);
389 while (!isspace (C) && Width--) {
395 /* Terminate the string just read */
403 /* Fixed length string, NOT zero terminated */
405 /* No width given, default is 1 */
409 S = va_arg (ap, char*);
415 /* Just skip as many chars as given */
424 /* String using characters from a set */
427 memset (CharSet, 0, sizeof (CharSet));
437 /* Read the characters that are part of the set */
438 while (F != ']' && F != '\0') {
439 if (*format == '-') {
440 /* A range. Get start and end, skip the '-' */
445 /* '-' as last char means: include '-' */
446 AddCharToSet (Start);
448 } else if (F != '\0') {
449 /* Include all chars in the range */
451 AddCharToSet (Start);
457 /* Get next char after range */
461 /* Just a character */
468 /* Invert the set if requested */
470 for (Start = 0; Start < sizeof (CharSet); ++Start) {
471 CharSet[Start] ^= 0xFF;
475 /* We have the set in CharSet. Read characters and
476 * store them into a string while they are part of
480 S = va_arg (ap, char*);
481 while (IsCharInSet (C) && Width--) {
487 while (IsCharInSet (C) && Width--) {
495 /* Pointer, format is 0xABCD */
498 longjmp (JumpBuf, RC_NOCONV);
501 if (C != 'x' && C != 'X') {
502 longjmp (JumpBuf, RC_NOCONV);
510 /* Store characters consumed so far */
516 /* Invalid conversion */
517 longjmp (JumpBuf, RC_NOCONV);
522 /* Skip the format char */
529 } else if (Result == RC_EOF) {
531 /* Jump via JumpBuf means EOF on input */
532 if (D->ccount == 0) {
533 /* Special case: error */
539 /* Return the number of conversions */