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 scanfdata* 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 int 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 */
49 static unsigned char IntBytes; /* Number of bytes-1 for int conversions */
52 static unsigned char Positive; /* Flag for positive value */
53 static unsigned char NoAssign; /* Supppress assigment */
54 static unsigned char Invert; /* Do we need to invert the charset? */
55 static unsigned char CharSet[32]; /* 32 * 8 bits = 256 bits */
56 static const unsigned char Bits[8] = {
57 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
62 /*****************************************************************************/
64 /*****************************************************************************/
68 static void AddCharToSet (unsigned char C)
69 /* Set the given bit in the character set */
80 asm ("lda %v,y", Bits);
81 asm ("ora %v,x", CharSet);
82 asm ("sta %v,x", CharSet);
87 static unsigned char IsCharInSet (unsigned char C)
88 /* Check if the given char is part of the character set */
99 asm ("lda %v,y", Bits);
100 asm ("and %v,x", CharSet);
107 static void InvertCharSet (void)
108 /* Invert the character set */
110 asm ("ldy #%b", sizeof (CharSet) - 1);
112 asm ("lda %v,y", CharSet);
114 asm ("sta %v,y", CharSet);
121 /*****************************************************************************/
123 /*****************************************************************************/
127 static void ReadChar (void)
128 /* Get an input character, count characters */
130 C = D->get (D->data);
138 static void ReadCharWithCheck (void)
139 /* Get an input char, use longjmp in case of EOF */
143 longjmp (JumpBuf, RC_EOF);
149 static void SkipWhite (void)
150 /* Skip white space in the input and return the first non white character */
152 while (isspace (C)) {
159 static void ReadSign (void)
160 /* Read an optional sign and skip it. Store 1 in Positive if the value is
161 * positive, store 0 otherwise.
179 static unsigned char HexVal (char C)
180 /* Convert a digit to a value */
186 return toupper (C) - ('A' - 10);
192 static void AssignInt (void)
193 /* Assign the integer value in Val to the next argument. The function makes
194 * several non portable assumptions to reduce code size:
195 * - int and unsigned types have the same representation
196 * - short and int have the same representation.
197 * - all pointer types have the same representation.
202 /* Get the next argument pointer */
203 __AX__ = (unsigned) va_arg (ap, void*);
205 /* Store the argument pointer into ptr1 */
209 /* Get the number of bytes-1 to copy */
210 asm ("ldy %v", IntBytes);
212 /* Assign the integer value */
213 asm ("L1: lda %v,y", IntVal);
214 asm ("sta (ptr1),y");
223 static unsigned char ReadInt (unsigned char Base)
224 /* Read an integer and store it into IntVal. Returns the number of chars
225 * converted. Does NOT bump Conversions.
229 unsigned char CharCount = 0;
231 /* Read the integer value */
233 while (isxdigit (C) && Width-- > 0 && (Val = HexVal (C)) < Base) {
235 IntVal = IntVal * Base + Val;
239 /* If we didn't convert anything, it's an error */
240 if (CharCount == 0) {
241 longjmp (JumpBuf, RC_NOCONV);
244 /* Return the number of characters converted */
250 static void ScanInt (unsigned char Base)
251 /* Scan an integer including white space, sign and optional base spec,
252 * and store it into IntVal.
255 /* Skip whitespace */
258 /* Read an optional sign */
261 /* If Base is unknown (zero), figure it out */
279 /* Read the integer value */
287 /* Assign the value to the next argument unless suppressed */
290 /* One more conversion */
296 int _scanf (struct scanfdata* D_, register const char* format, va_list ap_)
297 /* This is the routine used to do the actual work. It is called from several
298 * types of wrappers to implement the actual ISO xxscanf functions.
301 char F; /* Character from format string */
302 unsigned char Result; /* setjmp result */
304 unsigned char HaveWidth; /* True if a width was given */
305 char Start; /* Start of range */
307 /* Place copies of the arguments into global variables. This is not very
308 * nice, but on a 6502 platform it gives better code, since the values
309 * do not have to be passed as parameters.
314 /* Initialize variables */
318 /* Set up the jump label. The get() routine will use this label when EOF
321 Result = setjmp (JumpBuf);
322 if (Result == RC_OK) {
325 /* Get the next input character */
328 /* Walk over the format string */
329 while (F = *format++) {
331 /* Check for a conversion */
332 if (F != '%' || *format == '%') {
334 /* %% or any char other than % */
339 /* Check for a match */
342 /* Special white space handling: Any whitespace in the
343 * format string matches any amount of whitespace including
344 * none(!). So this match will never fail.
351 /* A match. Read the next input character and start over */
356 /* A mismatch. We will stop scanning the input and return
357 * the number of conversions.
365 /* A conversion. Skip the percent sign. */
368 /* 1. Assignment suppression */
376 /* 2. Maximum field width */
383 /* ### Non portable ### */
384 Width = Width * 10 + (F & 0x0F);
386 } while (isdigit (F));
389 /* 3. Length modifier */
390 IntBytes = sizeof(int) - 1;
393 if (*format == 'h') {
394 IntBytes = sizeof(char) - 1;
401 if (*format == 'l') {
402 /* Treat long long as long */
406 case 'j': /* intmax_t */
407 IntBytes = sizeof(long) - 1;
411 case 'z': /* size_t */
412 case 't': /* ptrdiff_t */
413 case 'L': /* long double - ignore this one */
418 /* 4. Conversion specifier */
421 /* 'd' and 'u' conversions are actually the same, since the
422 * standard says that evene the 'u' modifier allows an
423 * optionally signed integer.
425 case 'd': /* Optionally signed decimal integer */
431 /* Optionally signed integer with a base */
436 /* Optionally signed octal integer */
442 /* Optionally signed hexadecimal integer */
450 /* Optionally signed float */
451 longjmp (JumpBuf, RC_NOCONV);
455 /* Whitespace terminated string */
458 S = va_arg (ap, char*);
460 while (!isspace (C) && Width--) {
466 /* Terminate the string just read */
474 /* Fixed length string, NOT zero terminated */
476 /* No width given, default is 1 */
480 S = va_arg (ap, char*);
483 ReadCharWithCheck ();
486 /* Just skip as many chars as given */
488 ReadCharWithCheck ();
495 /* String using characters from a set */
498 memset (CharSet, 0, sizeof (CharSet));
508 /* Read the characters that are part of the set */
509 while (F != ']' && F != '\0') {
510 if (*format == '-') {
511 /* A range. Get start and end, skip the '-' */
516 /* '-' as last char means: include '-' */
517 AddCharToSet (Start);
519 } else if (F != '\0') {
520 /* Include all chars in the range */
522 AddCharToSet (Start);
528 /* Get next char after range */
532 /* Just a character */
539 /* Invert the set if requested */
544 /* We have the set in CharSet. Read characters and
545 * store them into a string while they are part of
549 S = va_arg (ap, char*);
550 while (IsCharInSet (C) && Width--) {
556 while (IsCharInSet (C) && Width--) {
564 /* Pointer, format is 0xABCD */
567 longjmp (JumpBuf, RC_NOCONV);
570 if (C != 'x' && C != 'X') {
571 longjmp (JumpBuf, RC_NOCONV);
574 if (ReadInt (16) != 4) { /* 4 chars expected */
575 longjmp (JumpBuf, RC_NOCONV);
582 /* Store characters consumed so far */
588 /* Invalid conversion */
589 longjmp (JumpBuf, RC_NOCONV);
596 /* Push back the last unused character, provided it is not EOF */
598 D->unget (C, D->data);
603 /* Jump via JumpBuf means an error. If this happens at EOF with no
604 * conversions, it is considered an error, otherwise the number
605 * of conversions is returned (the default behaviour).
607 if (C == EOF && D->ccount == 0) {
608 /* Special case: error */
614 /* Return the number of conversions */