/*
- * _scanf.c
- *
- * (C) Copyright 2001 Ullrich von Bassewitz (uz@cc65.org)
- *
- * This is the basic layer for all scanf type functions.
- */
+** _scanf.c
+**
+** (c) Copyright 2001-2005, Ullrich von Bassewitz <uz@cc65.org>
+** 2014-09-10, Greg King <greg.king5@verizon.net>
+**
+** This is the basic layer for all scanf-type functions. It should be
+** rewritten in assembly, at some time in the future. So, some of the code
+** is not as elegant as it could be.
+*/
-#include <stdio.h>
+#include <stddef.h>
#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
#include <setjmp.h>
-#include <ctype.h>
#include <limits.h>
+#include <errno.h>
+
+#include <ctype.h>
+/* _scanf() can give EOF to these functions. But, the macroes can't
+** understand it; so, they are removed.
+*/
+#undef isspace
+#undef isxdigit
#include "_scanf.h"
+#pragma static-locals(on)
+
/*****************************************************************************/
-/* SetJmp return codes */
+/* SetJmp return codes */
/*****************************************************************************/
-#define RC_OK 0 /* Regular call */
-#define RC_EOF 1 /* EOF reached */
-#define RC_NOCONV 2 /* No conversion possible */
+enum {
+ RC_OK, /* setjmp() call */
+ RC_NOCONV, /* No conversion possible */
+ RC_EOF /* EOF reached */
+};
/*****************************************************************************/
-/* Data */
+/* Data */
/*****************************************************************************/
-static struct indesc* D; /* Copy of function argument */
-static va_list ap; /* Copy of function argument */
-static jmp_buf JumpBuf; /* Label that is used in case of EOF */
-static char C; /* Character from input */
-static unsigned Width; /* Maximum field width */
-static long IntVal; /* Converted int value */
-static unsigned Conversions; /* Number of conversions */
+static const char* format; /* Copy of function argument */
+static const struct scanfdata* D_; /* Copy of function argument */
+static va_list ap; /* Copy of function argument */
+static jmp_buf JumpBuf; /* "Label" that is used for failures */
+static char F; /* Character from format string */
+static unsigned CharCount; /* Characters read so far */
+static int C; /* Character from input */
+static unsigned Width; /* Maximum field width */
+static long IntVal; /* Converted int value */
+static int Assignments; /* Number of assignments */
+static unsigned char IntBytes; /* Number of bytes-1 for int conversions */
/* Flags */
-static unsigned char Positive; /* Flag for positive value */
-static unsigned char NoAssign; /* Supppress assigment */
-static unsigned char IsShort; /* Short type */
-static unsigned char IsLong; /* Long type */
+static bool Converted; /* Some object was converted */
+static bool Positive; /* Flag for positive value */
+static bool NoAssign; /* Suppress assignment */
+static bool Invert; /* Do we need to invert the charset? */
+static unsigned char CharSet[(1+UCHAR_MAX)/CHAR_BIT];
+static const unsigned char Bits[CHAR_BIT] = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
+};
+
+/* We need C to be 16 bits since we cannot check for EOF otherwise.
+** Unfortunately, this causes the code to be quite larger, even if for most
+** purposes, checking the low byte would be enough, since if C is EOF, the
+** low byte will not match any useful character anyway (at least for the
+** supported platforms - I know that this is not portable). So the following
+** macro is used to access just the low byte of C.
+*/
+#define CHAR(c) (*((unsigned char*)&(c)))
/*****************************************************************************/
-/* Character sets */
+/* Character sets */
/*****************************************************************************/
+/* We don't want the optimizer to ruin our "perfect" ;-)
+** assembly code!
+*/
+#pragma optimize (push, off)
+
+static unsigned FindBit (void)
+/* Locate the character's bit in the charset array.
+** < .A - Argument character
+** > .X - Offset of the byte in the character-set mask
+** > .A - Bit-mask
+*/
+{
+ asm ("pha");
+ asm ("lsr a"); /* Divide by CHAR_BIT */
+ asm ("lsr a");
+ asm ("lsr a");
+ asm ("tax"); /* Byte's offset */
+ asm ("pla");
+ asm ("and #%b", CHAR_BIT-1);
+ asm ("tay"); /* Bit's offset */
+ asm ("lda %v,y", Bits);
+ return (unsigned) __AX__;
+}
+
+#pragma optimize (pop)
+
+
+static void __fastcall__ AddCharToSet (unsigned char /* C */)
+/* Set the given bit in the character set */
+{
+ FindBit();
+ asm ("ora %v,x", CharSet);
+ asm ("sta %v,x", CharSet);
+}
+
+
+
+#pragma optimize (push, off)
+
+static unsigned char IsCharInSet (void)
+/* Check if the char. is part of the character set. */
+{
+ /* Get the character from C. */
+ asm ("lda #$00");
+ asm ("ldx %v+1", C);
+ asm ("bne L1"); /* EOF never is in the set */
+ asm ("lda %v", C);
+ FindBit();
+ asm ("and %v,x", CharSet);
+ asm ("L1:");
+ asm ("ldx #$00");
+ return (unsigned char) __AX__;
+}
+
+#pragma optimize (pop)
+
+
+
+static void InvertCharSet (void)
+/* Invert the character set */
+{
+ asm ("ldy #%b", sizeof (CharSet) - 1);
+ asm ("L1:");
+ asm ("lda %v,y", CharSet);
+ asm ("eor #$FF");
+ asm ("sta %v,y", CharSet);
+ asm ("dey");
+ asm ("bpl L1");
+}
+
+
+
/*****************************************************************************/
-/* Code */
+/* Code */
/*****************************************************************************/
+static void PushBack (void)
+/* Push back the last (unused) character, provided it is not EOF. */
+{
+ /* Get the character from C. */
+ /* Only the high-byte needs to be checked for EOF. */
+ asm ("ldx %v+1", C);
+ asm ("bne %g", Done);
+ asm ("lda %v", C);
+
+ /* Put unget()'s first argument on the stack. */
+ asm ("jsr pushax");
+
+ /* Copy D into the zero-page. */
+ (const struct scanfdata*) __AX__ = D_;
+ asm ("sta ptr1");
+ asm ("stx ptr1+1");
+
+ /* Copy the unget vector to jmpvec. */
+ asm ("ldy #%b", offsetof (struct scanfdata, unget));
+ asm ("lda (ptr1),y");
+ asm ("sta jmpvec+1");
+ asm ("iny");
+ asm ("lda (ptr1),y");
+ asm ("sta jmpvec+2");
+
+ /* Load D->data into __AX__. */
+ asm ("ldy #%b", offsetof (struct scanfdata, data) + 1);
+ asm ("lda (ptr1),y");
+ asm ("tax");
+ asm ("dey");
+ asm ("lda (ptr1),y");
+
+ /* Call the unget routine. */
+ asm ("jsr jmpvec");
+
+ /* Take back that character's count. */
+ asm ("lda %v", CharCount);
+ asm ("bne %g", Yank);
+ asm ("dec %v+1", CharCount);
+Yank:
+ asm ("dec %v", CharCount);
+
+Done:
+ ;
+}
+
+
+
static void ReadChar (void)
/* Get an input character, count characters */
{
- C = D->fin (D);
- ++D->ccount;
+ /* Move D to ptr1 */
+ asm ("lda %v", D_);
+ asm ("ldx %v+1", D_);
+ asm ("sta ptr1");
+ asm ("stx ptr1+1");
+
+ /* Copy the get vector to jmpvec */
+ asm ("ldy #%b", offsetof (struct scanfdata, get));
+ asm ("lda (ptr1),y");
+ asm ("sta jmpvec+1");
+ asm ("iny");
+ asm ("lda (ptr1),y");
+ asm ("sta jmpvec+2");
+
+ /* Load D->data into __AX__ */
+ asm ("ldy #%b", offsetof (struct scanfdata, data) + 1);
+ asm ("lda (ptr1),y");
+ asm ("tax");
+ asm ("dey");
+ asm ("lda (ptr1),y");
+
+ /* Call the get routine */
+ asm ("jsr jmpvec");
+
+ /* Assign the result to C */
+ asm ("sta %v", C);
+ asm ("stx %v+1", C);
+
+ /* If C is EOF, don't bump the character counter.
+ ** Only the high-byte needs to be checked.
+ */
+ asm ("inx");
+ asm ("beq %g", Done);
+
+ /* Must bump CharCount. */
+ asm ("inc %v", CharCount);
+ asm ("bne %g", Done);
+ asm ("inc %v+1", CharCount);
+
+Done:
+ ;
+}
+
+
+
+#pragma optimize (push, off)
+
+static void __fastcall__ Error (unsigned char /* Code */)
+/* Does a longjmp using the given code */
+{
+ asm ("pha");
+ (char*) __AX__ = JumpBuf;
+ asm ("jsr pushax");
+ asm ("pla");
+ asm ("ldx #>$0000");
+ asm ("jmp %v", longjmp);
+}
+
+#pragma optimize (pop)
+
+
+
+static void CheckEnd (void)
+/* Stop a scan if it prematurely reaches the end of a string or a file. */
+{
+ /* Only the high-byte needs to be checked for EOF. */
+ asm ("ldx %v+1", C);
+ asm ("beq %g", Done);
+
+ Error (RC_EOF);
+Done:
+ ;
}
static void SkipWhite (void)
/* Skip white space in the input and return the first non white character */
{
- while (isspace (C)) {
- ReadChar ();
+ while ((bool) isspace (C)) {
+ ReadChar ();
}
}
+#pragma optimize (push, off)
+
static void ReadSign (void)
/* Read an optional sign and skip it. Store 1 in Positive if the value is
- * positive, store 0 otherwise.
- */
+** positive, store 0 otherwise.
+*/
{
- switch (C) {
- case '-':
- ReadChar ();
- Positive = 0;
- case '+':
- ReadChar ();
- /* FALLTHROUGH */
- default:
- Positive = 1;
- }
+ /* We can ignore the high byte of C here, since if it is EOF, the lower
+ ** byte won't match anyway.
+ */
+ asm ("lda %v", C);
+ asm ("cmp #'-'");
+ asm ("bne %g", NotNeg);
+
+ /* Negative value */
+ asm ("sta %v", Converted);
+ asm ("jsr %v", ReadChar);
+ asm ("lda #$00"); /* Flag as negative */
+ asm ("beq %g", Store);
+
+ /* Positive value */
+NotNeg:
+ asm ("cmp #'+'");
+ asm ("bne %g", Pos);
+ asm ("sta %v", Converted);
+ asm ("jsr %v", ReadChar); /* Skip the + sign */
+Pos:
+ asm ("lda #$01"); /* Flag as positive */
+Store:
+ asm ("sta %v", Positive);
}
+#pragma optimize (pop)
-static unsigned char HexVal (char C)
+
+static unsigned char __fastcall__ HexVal (char C)
/* Convert a digit to a value */
{
-
- if (isdigit (C)) {
- return C - '0';
- } else {
- return C - toupper (C) + ('A' + 10);
- }
+ return (bool) isdigit (C) ?
+ C - '0' :
+ (char) tolower ((int) C) - ('a' - 10);
}
-static void ReadInt (unsigned char Base)
-/* Read an integer and store it into IntVal */
+static void __fastcall__ ReadInt (unsigned char Base)
+/* Read an integer, and store it into IntVal. */
{
- /* Value must start with a digit */
- if (!isdigit (C)) {
- longjmp (JumpBuf, RC_NOCONV);
+ unsigned char Val, CharCount = 0;
+
+ /* Read the integer value */
+ IntVal = 0L;
+ while ((bool) isxdigit (C) && ++Width != 0
+ && (Val = HexVal ((char) C)) < Base) {
+ ++CharCount;
+ IntVal = IntVal * (long) Base + (long) Val;
+ ReadChar ();
}
- /* Read the value */
- IntVal = 0;
- while (isxdigit (C) && Width-- > 0) {
- printf ("ReadInt: '%c'\n", C);
- IntVal = IntVal * Base + HexVal (C);
- ReadChar ();
+ /* If we didn't convert anything, it's a failure. */
+ if (CharCount == 0) {
+ Error (RC_NOCONV);
}
- /* One more conversion */
- ++Conversions;
+ /* Another conversion */
+ Converted = true;
}
static void AssignInt (void)
/* Assign the integer value in Val to the next argument. The function makes
- * several non portable assumptions to reduce code size:
- * - int and unsigned types have the same representation
- * - short and int have the same representation.
- * - all pointer types have the same representation.
- */
+** several non-portable assumptions, to reduce code size:
+** - signed and unsigned types have the same representation.
+** - short and int have the same representation.
+** - all pointer types have the same representation.
+*/
+{
+ if (NoAssign == false) {
+
+ /* Get the next argument pointer */
+ (void*) __AX__ = va_arg (ap, void*);
+
+ /* Put the argument pointer into the zero-page. */
+ asm ("sta ptr1");
+ asm ("stx ptr1+1");
+
+ /* Get the number of bytes-1 to copy */
+ asm ("ldy %v", IntBytes);
+
+ /* Assign the integer value */
+Loop: asm ("lda %v,y", IntVal);
+ asm ("sta (ptr1),y");
+ asm ("dey");
+ asm ("bpl %g", Loop);
+
+ /* Another assignment */
+ asm ("inc %v", Assignments);
+ asm ("bne %g", Done);
+ asm ("inc %v+1", Assignments);
+Done: ;
+ }
+}
+
+
+
+static void __fastcall__ ScanInt (unsigned char Base)
+/* Scan an integer including white space, sign and optional base spec,
+** and store it into IntVal.
+*/
{
- if (!NoAssign) {
- /* Get the next argument pointer */
- void* P = va_arg (ap, void*);
-
- /* Assign to the converted value */
- if (IsLong) {
- *(long*)P = IntVal;
- } else {
- *(int*)P = (int) IntVal;
- }
+ /* Skip whitespace */
+ SkipWhite ();
+
+ /* Read an optional sign */
+ ReadSign ();
+
+ /* If Base is unknown (zero), figure it out */
+ if (Base == 0) {
+ if (CHAR (C) == '0') {
+ ReadChar ();
+ switch (CHAR (C)) {
+ case 'x':
+ case 'X':
+ Base = 16;
+ Converted = true;
+ ReadChar ();
+ break;
+ default:
+ Base = 8;
+
+ /* Restart at the beginning of the number because it might
+ ** be only a single zero digit (which already was read).
+ */
+ PushBack ();
+ C = '0';
+ }
+ } else {
+ Base = 10;
+ }
}
+
+ /* Read the integer value */
+ ReadInt (Base);
+
+ /* Apply the sign */
+ if (Positive == false) {
+ IntVal = -IntVal;
+ }
+
+ /* Assign the value to the next argument unless suppressed */
+ AssignInt ();
}
-int _scanf (struct indesc* D_, const char* format, va_list ap_)
+static char GetFormat (void)
+/* Pick up the next character from the format string. */
+{
+/* return (F = *format++); */
+ (const char*) __AX__ = format;
+ asm ("sta regsave");
+ asm ("stx regsave+1");
+ ++format;
+ asm ("ldy #0");
+ asm ("lda (regsave),y");
+ asm ("ldx #>$0000");
+ return (F = (char) __AX__);
+}
+
+
+
+int __fastcall__ _scanf (const struct scanfdata* D,
+ const char* format_, va_list ap_)
/* This is the routine used to do the actual work. It is called from several
- * types of wrappers to implement the actual ISO xxscanf functions.
- */
+** types of wrappers to implement the actual ISO xxscanf functions.
+*/
{
- char F; /* Character from format string */
- unsigned char Result; /* setjmp result */
- char* S;
- unsigned char Base; /* Integer base in %i */
+ register char* S;
+ bool HaveWidth; /* True if a width was given */
+ bool Match; /* True if a character-set has any matches */
+ char Start; /* Walks over a range */
/* Place copies of the arguments into global variables. This is not very
- * nice, but on a 6502 platform it gives better code, since the values
- * do not have to be passed as parameters.
- */
- D = D_;
- ap = ap_;
+ ** nice, but on a 6502 platform it gives better code, since the values
+ ** do not have to be passed as parameters.
+ */
+ D_ = D;
+ format = format_;
+ ap = ap_;
/* Initialize variables */
- Conversions = 0;
- D->ccount = 0;
-
- /* Set up the jump label. The get() routine will use this label when EOF
- * is reached.
- */
- Result = setjmp (JumpBuf);
- printf ("Result = %u\n", Result);
- if (Result == RC_OK) {
-
+ Converted = false;
+ Assignments = 0;
+ CharCount = 0;
+
+ /* Set up the jump "label". CheckEnd() will use that label when EOF
+ ** is reached. ReadInt() will use it when number-conversion fails.
+ */
+ if ((unsigned char) setjmp (JumpBuf) == RC_OK) {
Again:
- /* Get the next input character */
- ReadChar ();
-
- /* Walk over the format string */
- while (F = *format++) {
-
- /* Check for a conversion */
- if (F != '%' || *format == '%') {
-
- /* %% or any char other than % */
- if (F == '%') {
- ++format;
- }
-
- /* Check for a match */
- if (isspace (F)) {
-
- /* Special white space handling: Any whitespace matches
- * any amount of whitespace including none(!). So this
- * match will never fail.
- */
- SkipWhite ();
- continue;
-
- } else if (F != C) {
-
- /* A mismatch. We will stop scanning the input and return
- * the number of conversions.
- */
- printf ("F = '%c', C = '%c' --> mismatch\n", F, C);
- return Conversions;
-
- } else {
-
- /* A match. Read the next input character and start over */
- goto Again;
-
- }
-
- } else {
-
- /* A conversion. Skip the percent sign. */
- F = *format++;
-
- /* Initialize variables */
- NoAssign = 0;
- IsShort = 0;
- IsLong = 0;
- Width = UINT_MAX;
-
- /* Check for flags. */
- while (1) {
- if (isdigit (F)) {
- Width = 0;
- do {
- /* ### Non portable ### */
- Width = Width * 10 + (F & 0x0F);
- F = *format++;
- } while (isdigit (F));
- } else {
- switch (F) {
- case '*': NoAssign = 1; break;
- case 'h': IsShort = 1; break;
- case 'l':
- case 'L': IsLong = 1; break;
- default: goto FlagsDone;
- }
- F = *format++;
- }
- }
-FlagsDone:
-
- /* Check for the actual conversion character */
- printf ("F = '%c'\n", F);
- switch (F) {
-
- case 'D':
- IsLong = 1;
- case 'd':
- /* Optionally signed decimal integer */
- SkipWhite ();
- ReadSign ();
- ReadInt (10);
- if (!Positive) {
- IntVal = -IntVal;
- }
- AssignInt ();
- break;
-
- case 'i':
- /* Optionally signed integer with a base */
- SkipWhite ();
- ReadSign ();
- if (C == '0') {
- ReadChar ();
- switch (C) {
- case 'x':
- case 'X':
- Base = 16;
- ReadChar();
- break;
- default:
- Base = 8;
- }
- } else {
- Base = 10;
- }
- ReadInt (Base);
- if (!Positive) {
- IntVal = -IntVal;
- }
- AssignInt ();
- break;
-
- case 'o':
- /* Unsigned octal integer */
- SkipWhite ();
- ReadInt (8);
- AssignInt ();
- break;
-
- case 'u':
- /* Unsigned decimal integer */
- SkipWhite ();
- ReadInt (10);
- AssignInt ();
- break;
-
- case 'x':
- case 'X':
- /* Unsigned hexadecimal integer */
- SkipWhite ();
- ReadInt (16);
- AssignInt ();
- break;
-
- case 'E':
- case 'e':
- case 'f':
- case 'g':
- /* Optionally signed float */
- break;
-
- case 's':
- /* Whitespace terminated string */
- SkipWhite ();
- S = NoAssign? 0 : va_arg (ap, char*);
- while (C && !isspace (C) && Width--) {
- if (S) {
- *S++ = C;
- }
- ReadChar ();
- }
- break;
-
- case 'c':
- /* Fixed length string */
- break;
-
- case '[':
- /* String using characters from a set */
- break;
-
- case 'p':
- /* Pointer */
- break;
-
- case 'n':
- /* Store characters consumed so far */
- IntVal = D->ccount;
- IsLong = 0;
- AssignInt ();
- break;
-
- default:
- /* Invalid conversion */
- break;
-
- }
-
- /* Skip the format char */
- goto Again;
-
- }
-
- }
-
- } else if (Result == RC_EOF) {
-
- /* Jump via JumpBuf means EOF on input */
- if (D->ccount == 0) {
- /* Special case: error */
- return -1;
- }
+ /* Get the next input character */
+ ReadChar ();
+
+ /* Walk over the format string */
+ while (GetFormat ()) {
+
+ /* Check for a conversion */
+ if (F != '%') {
+
+ /* Check for a match */
+ if ((bool) isspace ((int) F)) {
+
+ /* Special white space handling: Any whitespace in the
+ ** format string matches any amount of whitespace including
+ ** none(!). So this match will never fail.
+ */
+ SkipWhite ();
+ continue;
+ }
+
+Percent:
+ /* ### Note: The opposite test (C == F)
+ ** would be optimized into buggy code!
+ */
+ if (C != (int) F) {
+
+ /* A mismatch -- we will stop scanning the input,
+ ** and return the number of assigned conversions.
+ */
+ goto NoConv;
+ }
+
+ /* A match -- get the next input character, and continue. */
+ goto Again;
+
+ } else {
+
+ /* A conversion. Skip the percent sign. */
+ /* 0. Check for %% */
+ if (GetFormat () == '%') {
+ goto Percent;
+ }
+
+ /* 1. Assignment suppression */
+ NoAssign = (F == '*');
+ if (NoAssign) {
+ GetFormat ();
+ }
+
+ /* 2. Maximum field width */
+ Width = UINT_MAX;
+ HaveWidth = (bool) isdigit (F);
+ if (HaveWidth) {
+ Width = 0;
+ do {
+ /* ### Non portable ### */
+ Width = Width * 10 + (F & 0x0F);
+ } while ((bool) isdigit (GetFormat ()));
+ }
+ if (Width == 0) {
+ /* Invalid specification */
+ /* Note: This method of leaving the function might seem
+ ** to be crude, but it optimizes very well because
+ ** the four exits can share this code.
+ */
+ _seterrno (EINVAL);
+ Assignments = EOF;
+ PushBack ();
+ return Assignments;
+ }
+ /* Increment-and-test makes better code than test-and-decrement
+ ** does. So, change the width into a form that can be used in
+ ** that way.
+ */
+ Width = ~Width;
+
+ /* 3. Length modifier */
+ IntBytes = sizeof(int) - 1;
+ switch (F) {
+ case 'h':
+ if (*format == 'h') {
+ IntBytes = sizeof(char) - 1;
+ ++format;
+ }
+ GetFormat ();
+ break;
+
+ case 'l':
+ if (*format == 'l') {
+ /* Treat long long as long */
+ ++format;
+ }
+ /* FALLTHROUGH */
+ case 'j': /* intmax_t */
+ IntBytes = sizeof(long) - 1;
+ /* FALLTHROUGH */
+
+ case 'z': /* size_t */
+ case 't': /* ptrdiff_t */
+ /* Same size as int */
+
+ case 'L': /* long double - ignore this one */
+ GetFormat ();
+ }
+
+ /* 4. Conversion specifier */
+ switch (F) {
+ /* 'd' and 'u' conversions are actually the same, since the
+ ** standard says that even the 'u' modifier allows an
+ ** optionally signed integer.
+ */
+ case 'd': /* Optionally signed decimal integer */
+ case 'u':
+ ScanInt (10);
+ break;
+
+ case 'i':
+ /* Optionally signed integer with a base */
+ ScanInt (0);
+ break;
+
+ case 'o':
+ /* Optionally signed octal integer */
+ ScanInt (8);
+ break;
+
+ case 'x':
+ case 'X':
+ /* Optionally signed hexadecimal integer */
+ ScanInt (16);
+ break;
+
+ case 's':
+ /* Whitespace-terminated string */
+ SkipWhite ();
+ CheckEnd (); /* Is it an input failure? */
+ Converted = true; /* No, conversion will succeed */
+ if (NoAssign == false) {
+ S = va_arg (ap, char*);
+ }
+ while (C != EOF
+ && (bool) isspace (C) == false
+ && ++Width) {
+ if (NoAssign == false) {
+ *S++ = C;
+ }
+ ReadChar ();
+ }
+ /* Terminate the string just read */
+ if (NoAssign == false) {
+ *S = '\0';
+ ++Assignments;
+ }
+ break;
+
+ case 'c':
+ /* Fixed-length string, NOT zero-terminated */
+ if (HaveWidth == false) {
+ /* No width given, default is 1 */
+ Width = ~1u;
+ }
+ CheckEnd (); /* Is it an input failure? */
+ Converted = true; /* No, at least 1 char. available */
+ if (NoAssign == false) {
+ S = va_arg (ap, char*);
+ /* ## This loop is convenient for us, but it isn't
+ ** standard C. The standard implies that a failure
+ ** shouldn't put anything into the array argument.
+ */
+ while (++Width) {
+ CheckEnd (); /* Is it a matching failure? */
+ *S++ = C;
+ ReadChar ();
+ }
+ ++Assignments;
+ } else {
+ /* Just skip as many chars as given */
+ while (++Width) {
+ CheckEnd (); /* Is it a matching failure? */
+ ReadChar ();
+ }
+ }
+ break;
+
+ case '[':
+ /* String using characters from a set */
+ /* Clear the set */
+ memset (CharSet, 0, sizeof (CharSet));
+ /* Skip the left-bracket, and test for inversion. */
+ Invert = (GetFormat () == '^');
+ if (Invert) {
+ GetFormat ();
+ }
+ if (F == ']') {
+ /* Empty sets aren't allowed; so, a right-bracket
+ ** at the beginning must be a member of the set.
+ */
+ AddCharToSet (F);
+ GetFormat ();
+ }
+ /* Read the characters that are part of the set */
+ while (F != '\0' && F != ']') {
+ if (*format == '-') { /* Look ahead at next char. */
+ /* A range. Get start and end, skip the '-' */
+ Start = F;
+ ++format;
+ switch (GetFormat ()) {
+ case '\0':
+ case ']':
+ /* '-' as last char means: include '-' */
+ AddCharToSet (Start);
+ AddCharToSet ('-');
+ break;
+ default:
+ /* Include all characters
+ ** that are in the range.
+ */
+ while (1) {
+ AddCharToSet (Start);
+ if (Start == F) {
+ break;
+ }
+ ++Start;
+ }
+ /* Get next char after range */
+ GetFormat ();
+ }
+ } else {
+ /* Just a character */
+ AddCharToSet (F);
+ /* Get next char */
+ GetFormat ();
+ }
+ }
+ /* Don't go beyond the end of the format string. */
+ /* (Maybe, this should mean an invalid specification.) */
+ if (F == '\0') {
+ --format;
+ }
+
+ /* Invert the set if requested */
+ if (Invert) {
+ InvertCharSet ();
+ }
+
+ /* We have the set in CharSet. Read characters and
+ ** store them into a string while they are part of
+ ** the set.
+ */
+ Match = false;
+ if (NoAssign == false) {
+ S = va_arg (ap, char*);
+ }
+ while (IsCharInSet () && ++Width) {
+ if (NoAssign == false) {
+ *S++ = C;
+ }
+ Match = Converted = true;
+ ReadChar ();
+ }
+ /* At least one character must match the set. */
+ if (Match == false) {
+ goto NoConv;
+ }
+ if (NoAssign == false) {
+ *S = '\0';
+ ++Assignments;
+ }
+ break;
+
+ case 'p':
+ /* Pointer, general format is 0xABCD.
+ ** %hhp --> zero-page pointer
+ ** %hp --> near pointer
+ ** %lp --> far pointer
+ */
+ SkipWhite ();
+ if (CHAR (C) != '0') {
+ goto NoConv;
+ }
+ Converted = true;
+ ReadChar ();
+ switch (CHAR (C)) {
+ case 'x':
+ case 'X':
+ break;
+ default:
+ goto NoConv;
+ }
+ ReadChar ();
+ ReadInt (16);
+ AssignInt ();
+ break;
+
+ case 'n':
+ /* Store the number of characters consumed so far
+ ** (the read-ahead character hasn't been consumed).
+ */
+ IntVal = (long) (CharCount - (C == EOF ? 0u : 1u));
+ AssignInt ();
+ /* Don't count it. */
+ if (NoAssign == false) {
+ --Assignments;
+ }
+ break;
+
+ case 'S':
+ case 'C':
+ /* Wide characters */
+
+ case 'a':
+ case 'A':
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'F':
+ case 'g':
+ case 'G':
+ /* Optionally signed float */
+
+ /* Those 2 groups aren't implemented. */
+ _seterrno (ENOSYS);
+ Assignments = EOF;
+ PushBack ();
+ return Assignments;
+
+ default:
+ /* Invalid specification */
+ _seterrno (EINVAL);
+ Assignments = EOF;
+ PushBack ();
+ return Assignments;
+ }
+ }
+ }
+ } else {
+NoConv:
+
+ /* Coming here means a failure. If that happens at EOF, with no
+ ** conversion attempts, then it is considered an error; otherwise,
+ ** the number of assignments is returned (the default behaviour).
+ */
+ if (C == EOF && Converted == false) {
+ Assignments = EOF; /* Special case: error */
+ }
}
- /* Return the number of conversions */
- return Conversions;
+ /* Put the read-ahead character back into the input stream. */
+ PushBack ();
+
+ /* Return the number of conversion-and-assignments. */
+ return Assignments;
}