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 static void InvertCharSet (void)
109 /* Invert the character set */
111 asm ("ldy #%b", sizeof (CharSet) - 1);
113 asm ("lda %v,y", CharSet);
115 asm ("sta %v,y", CharSet);
122 /*****************************************************************************/
124 /*****************************************************************************/
128 static void ReadChar (void)
129 /* Get an input character, count characters */
137 static void SkipWhite (void)
138 /* Skip white space in the input and return the first non white character */
140 while (isspace (C)) {
147 static void ReadSign (void)
148 /* Read an optional sign and skip it. Store 1 in Positive if the value is
149 * positive, store 0 otherwise.
167 static unsigned char HexVal (char C)
168 /* Convert a digit to a value */
174 return C - toupper (C) + ('A' + 10);
180 static void ReadInt (unsigned char Base)
181 /* Read an integer and store it into IntVal */
183 /* Value must start with a digit */
185 longjmp (JumpBuf, RC_NOCONV);
190 while (isxdigit (C) && Width-- > 0) {
191 IntVal = IntVal * Base + HexVal (C);
195 /* One more conversion */
201 static void AssignInt (void)
202 /* Assign the integer value in Val to the next argument. The function makes
203 * several non portable assumptions to reduce code size:
204 * - int and unsigned types have the same representation
205 * - short and int have the same representation.
206 * - all pointer types have the same representation.
210 /* Get the next argument pointer */
211 void* P = va_arg (ap, void*);
213 /* Assign to the converted value */
217 *(int*)P = (int) IntVal;
224 int _scanf (struct indesc* D_, register const char* format, va_list ap_)
225 /* This is the routine used to do the actual work. It is called from several
226 * types of wrappers to implement the actual ISO xxscanf functions.
229 char F; /* Character from format string */
230 unsigned char Result; /* setjmp result */
232 unsigned char Base; /* Integer base in %i */
233 unsigned char HaveWidth; /* True if a width was given */
234 char Start; /* Start of range */
236 /* Place copies of the arguments into global variables. This is not very
237 * nice, but on a 6502 platform it gives better code, since the values
238 * do not have to be passed as parameters.
243 /* Initialize variables */
247 /* Set up the jump label. The get() routine will use this label when EOF
250 Result = setjmp (JumpBuf);
251 if (Result == RC_OK) {
254 /* Get the next input character */
257 /* Walk over the format string */
258 while (F = *format++) {
260 /* Check for a conversion */
261 if (F != '%' || *format == '%') {
263 /* %% or any char other than % */
268 /* Check for a match */
271 /* Special white space handling: Any whitespace matches
272 * any amount of whitespace including none(!). So this
273 * match will never fail.
280 /* A mismatch. We will stop scanning the input and return
281 * the number of conversions.
287 /* A match. Read the next input character and start over */
294 /* A conversion. Skip the percent sign. */
297 /* Initialize variables */
304 /* Check for flags. */
310 /* ### Non portable ### */
311 Width = Width * 10 + (F & 0x0F);
313 } while (isdigit (F));
316 case '*': NoAssign = 1; break;
317 case 'h': IsShort = 1; break;
319 case 'L': IsLong = 1; break;
320 default: goto FlagsDone;
327 /* Check for the actual conversion character */
333 /* Optionally signed decimal integer */
344 /* Optionally signed integer with a base */
369 /* Unsigned octal integer */
376 /* Unsigned decimal integer */
384 /* Unsigned hexadecimal integer */
394 /* Optionally signed float */
395 longjmp (JumpBuf, RC_NOCONV);
399 /* Whitespace terminated string */
402 S = va_arg (ap, char*);
404 while (!isspace (C) && Width--) {
410 /* Terminate the string just read */
418 /* Fixed length string, NOT zero terminated */
420 /* No width given, default is 1 */
424 S = va_arg (ap, char*);
430 /* Just skip as many chars as given */
439 /* String using characters from a set */
442 memset (CharSet, 0, sizeof (CharSet));
452 /* Read the characters that are part of the set */
453 while (F != ']' && F != '\0') {
454 if (*format == '-') {
455 /* A range. Get start and end, skip the '-' */
460 /* '-' as last char means: include '-' */
461 AddCharToSet (Start);
463 } else if (F != '\0') {
464 /* Include all chars in the range */
466 AddCharToSet (Start);
472 /* Get next char after range */
476 /* Just a character */
483 /* Invert the set if requested */
488 /* We have the set in CharSet. Read characters and
489 * store them into a string while they are part of
493 S = va_arg (ap, char*);
494 while (IsCharInSet (C) && Width--) {
500 while (IsCharInSet (C) && Width--) {
508 /* Pointer, format is 0xABCD */
511 longjmp (JumpBuf, RC_NOCONV);
514 if (C != 'x' && C != 'X') {
515 longjmp (JumpBuf, RC_NOCONV);
523 /* Store characters consumed so far */
529 /* Invalid conversion */
530 longjmp (JumpBuf, RC_NOCONV);
535 /* Skip the format char */
542 } else if (Result == RC_EOF) {
544 /* Jump via JumpBuf means EOF on input */
545 if (D->ccount == 0) {
546 /* Special case: error */
552 /* Return the number of conversions */