1
0
mirror of https://github.com/cc65/cc65.git synced 2024-09-30 08:57:49 +00:00

Rewrote _scanf. It does need some tests and improvements, but it's a more

standard version than before, and it does support the necessary functionality
to support scanf functions for files.
Added vfscanf, fscanf and vfscanf.


git-svn-id: svn://svn.cc65.org/cc65/trunk@3301 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
cuz 2004-11-26 22:16:54 +00:00
parent e35b91ed3b
commit b1d4e1613b
8 changed files with 369 additions and 173 deletions

View File

@ -14,6 +14,7 @@ fgets.s
fputc.s fputc.s
fputs.s fputs.s
freopen.s freopen.s
fscanf.s
fseek.s fseek.s
fsetpos.s fsetpos.s
ftell.s ftell.s
@ -28,6 +29,7 @@ puts.s
qsort.s qsort.s
realloc.s realloc.s
rewind.s rewind.s
scanf.s
sleep.s sleep.s
sscanf.s sscanf.s
strftime.s strftime.s
@ -35,4 +37,7 @@ strtok.s
strxfrm.s strxfrm.s
system.s system.s
timezone.s timezone.s
vfscanf.s
vsscanf.s vsscanf.s

View File

@ -44,6 +44,7 @@ C_OBJS = _afailed.o \
fputc.o \ fputc.o \
fputs.o \ fputs.o \
freopen.o \ freopen.o \
fscanf.o \
fseek.o \ fseek.o \
fsetpos.o \ fsetpos.o \
ftell.o \ ftell.o \
@ -58,6 +59,7 @@ C_OBJS = _afailed.o \
qsort.o \ qsort.o \
realloc.o \ realloc.o \
rewind.o \ rewind.o \
scanf.o \
sleep.o \ sleep.o \
sscanf.o \ sscanf.o \
strftime.o \ strftime.o \
@ -65,6 +67,7 @@ C_OBJS = _afailed.o \
strtok.o \ strtok.o \
system.o \ system.o \
timezone.o \ timezone.o \
vfscanf.o \
vsscanf.o vsscanf.o

View File

@ -39,19 +39,18 @@
static struct indesc* D; /* Copy of function argument */ static struct scanfdata* D; /* Copy of function argument */
static va_list ap; /* 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 jmp_buf JumpBuf; /* Label that is used in case of EOF */
static char C; /* Character from input */ static int C; /* Character from input */
static unsigned Width; /* Maximum field width */ static unsigned Width; /* Maximum field width */
static long IntVal; /* Converted int value */ static long IntVal; /* Converted int value */
static unsigned Conversions; /* Number of conversions */ static unsigned Conversions; /* Number of conversions */
static unsigned char IntBytes; /* Number of bytes-1 for int conversions */
/* Flags */ /* Flags */
static unsigned char Positive; /* Flag for positive value */ static unsigned char Positive; /* Flag for positive value */
static unsigned char NoAssign; /* Supppress assigment */ static unsigned char NoAssign; /* Supppress assigment */
static unsigned char IsShort; /* Short type */
static unsigned char IsLong; /* Long type */
static unsigned char Invert; /* Do we need to invert the charset? */ static unsigned char Invert; /* Do we need to invert the charset? */
static unsigned char CharSet[32]; /* 32 * 8 bits = 256 bits */ static unsigned char CharSet[32]; /* 32 * 8 bits = 256 bits */
static const unsigned char Bits[8] = { static const unsigned char Bits[8] = {
@ -128,9 +127,22 @@ static void InvertCharSet (void)
static void ReadChar (void) static void ReadChar (void)
/* Get an input character, count characters */ /* Get an input character, count characters */
{ {
C = D->fin (D); C = D->get (D->data);
if (C != EOF) {
++D->ccount; ++D->ccount;
} }
}
static void ReadCharWithCheck (void)
/* Get an input char, use longjmp in case of EOF */
{
ReadChar ();
if (C == EOF) {
longjmp (JumpBuf, RC_EOF);
}
}
@ -171,33 +183,12 @@ static unsigned char HexVal (char C)
if (isdigit (C)) { if (isdigit (C)) {
return C - '0'; return C - '0';
} else { } else {
return C - toupper (C) + ('A' + 10); return toupper (C) - ('A' - 10);
} }
} }
static void 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);
}
/* Read the value */
IntVal = 0;
while (isxdigit (C) && Width-- > 0) {
IntVal = IntVal * Base + HexVal (C);
ReadChar ();
}
/* One more conversion */
++Conversions;
}
static void AssignInt (void) static void AssignInt (void)
/* Assign the integer value in Val to the next argument. The function makes /* Assign the integer value in Val to the next argument. The function makes
* several non portable assumptions to reduce code size: * several non portable assumptions to reduce code size:
@ -216,10 +207,7 @@ static void AssignInt (void)
asm ("stx ptr1+1"); asm ("stx ptr1+1");
/* Get the number of bytes-1 to copy */ /* Get the number of bytes-1 to copy */
asm ("ldy #3"); asm ("ldy %v", IntBytes);
asm ("lda %v", IsLong);
asm ("bne L1");
asm ("ldy #1");
/* Assign the integer value */ /* Assign the integer value */
asm ("L1: lda %v,y", IntVal); asm ("L1: lda %v,y", IntVal);
@ -232,7 +220,80 @@ static void AssignInt (void)
int _scanf (struct indesc* D_, register const char* format, va_list ap_) static unsigned char ReadInt (unsigned char Base)
/* Read an integer and store it into IntVal. Returns the number of chars
* converted. Does NOT bump Conversions.
*/
{
unsigned char Val;
unsigned char CharCount = 0;
/* Read the integer value */
IntVal = 0;
while (isxdigit (C) && Width-- > 0 && (Val = HexVal (C)) < Base) {
++CharCount;
IntVal = IntVal * Base + Val;
ReadChar ();
}
/* If we didn't convert anything, it's an error */
if (CharCount == 0) {
longjmp (JumpBuf, RC_NOCONV);
}
/* Return the number of characters converted */
return CharCount;
}
static void ScanInt (unsigned char Base)
/* Scan an integer including white space, sign and optional base spec,
* and store it into IntVal.
*/
{
/* Skip whitespace */
SkipWhite ();
/* Read an optional sign */
ReadSign ();
/* If Base is unknown (zero), figure it out */
if (Base == 0) {
if (C == '0') {
ReadChar ();
switch (C) {
case 'x':
case 'X':
Base = 16;
ReadChar ();
break;
default:
Base = 8;
}
} else {
Base = 10;
}
}
/* Read the integer value */
ReadInt (Base);
/* Apply the sign */
if (!Positive) {
IntVal = -IntVal;
}
/* Assign the value to the next argument unless suppressed */
AssignInt ();
/* One more conversion */
++Conversions;
}
int _scanf (struct scanfdata* D_, register const char* format, va_list ap_)
/* This is the routine used to do the actual work. It is called from several /* 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.
*/ */
@ -240,7 +301,6 @@ int _scanf (struct indesc* D_, register const char* format, va_list ap_)
char F; /* Character from format string */ char F; /* Character from format string */
unsigned char Result; /* setjmp result */ unsigned char Result; /* setjmp result */
char* S; char* S;
unsigned char Base; /* Integer base in %i */
unsigned char HaveWidth; /* True if a width was given */ unsigned char HaveWidth; /* True if a width was given */
char Start; /* Start of range */ char Start; /* Start of range */
@ -279,25 +339,25 @@ Again:
/* Check for a match */ /* Check for a match */
if (isspace (F)) { if (isspace (F)) {
/* Special white space handling: Any whitespace matches /* Special white space handling: Any whitespace in the
* any amount of whitespace including none(!). So this * format string matches any amount of whitespace including
* match will never fail. * none(!). So this match will never fail.
*/ */
SkipWhite (); SkipWhite ();
continue; continue;
} else if (F != C) { } else if (F == C) {
/* A match. Read the next input character and start over */
goto Again;
} else {
/* A mismatch. We will stop scanning the input and return /* A mismatch. We will stop scanning the input and return
* the number of conversions. * the number of conversions.
*/ */
return Conversions; return Conversions;
} else {
/* A match. Read the next input character and start over */
goto Again;
} }
} else { } else {
@ -305,15 +365,17 @@ Again:
/* A conversion. Skip the percent sign. */ /* A conversion. Skip the percent sign. */
F = *format++; F = *format++;
/* Initialize variables */ /* 1. Assignment suppression */
if (F == '*') {
F = *format++;
NoAssign = 1;
} else {
NoAssign = 0; NoAssign = 0;
IsShort = 0; }
IsLong = 0;
/* 2. Maximum field width */
Width = UINT_MAX; Width = UINT_MAX;
HaveWidth = 0; HaveWidth = 0;
/* Check for flags. */
while (1) {
if (isdigit (F)) { if (isdigit (F)) {
HaveWidth = 1; HaveWidth = 1;
Width = 0; Width = 0;
@ -322,80 +384,63 @@ Again:
Width = Width * 10 + (F & 0x0F); Width = Width * 10 + (F & 0x0F);
F = *format++; F = *format++;
} while (isdigit (F)); } while (isdigit (F));
} else { }
/* 3. Length modifier */
IntBytes = sizeof(int) - 1;
switch (F) { switch (F) {
case '*': NoAssign = 1; break; case 'h':
case 'h': IsShort = 1; break; if (*format == 'h') {
case 'l': IntBytes = sizeof(char) - 1;
case 'L': IsLong = 1; break; ++format;
default: goto FlagsDone;
} }
F = *format++; F = *format++;
} break;
}
FlagsDone:
/* Check for the actual conversion character */ case 'l':
if (*format == 'l') {
/* Treat long long as long */
++format;
}
/* FALLTHROUGH */
case 'j': /* intmax_t */
IntBytes = sizeof(long) - 1;
F = *format++;
break;
case 'z': /* size_t */
case 't': /* ptrdiff_t */
case 'L': /* long double - ignore this one */
F = *format++;
break;
}
/* 4. Conversion specifier */
switch (F) { switch (F) {
case 'D': /* 'd' and 'u' conversions are actually the same, since the
IsLong = 1; * standard says that evene the 'u' modifier allows an
case 'd': * optionally signed integer.
/* Optionally signed decimal integer */ */
SkipWhite (); case 'd': /* Optionally signed decimal integer */
ReadSign (); case 'u':
ReadInt (10); ScanInt (10);
if (!Positive) {
IntVal = -IntVal;
}
AssignInt ();
break; break;
case 'i': case 'i':
/* Optionally signed integer with a base */ /* Optionally signed integer with a base */
SkipWhite (); ScanInt (0);
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; break;
case 'o': case 'o':
/* Unsigned octal integer */ /* Optionally signed octal integer */
SkipWhite (); ScanInt (8);
ReadInt (8);
AssignInt ();
break;
case 'u':
/* Unsigned decimal integer */
SkipWhite ();
ReadInt (10);
AssignInt ();
break; break;
case 'x': case 'x':
case 'X': case 'X':
/* Unsigned hexadecimal integer */ /* Optionally signed hexadecimal integer */
SkipWhite (); ScanInt (16);
ReadInt (16);
AssignInt ();
break; break;
case 'E': case 'E':
@ -435,12 +480,12 @@ FlagsDone:
S = va_arg (ap, char*); S = va_arg (ap, char*);
while (Width--) { while (Width--) {
*S++ = C; *S++ = C;
ReadChar (); ReadCharWithCheck ();
} }
} else { } else {
/* Just skip as many chars as given */ /* Just skip as many chars as given */
while (Width--) { while (Width--) {
ReadChar (); ReadCharWithCheck ();
} }
} }
++Conversions; ++Conversions;
@ -526,8 +571,11 @@ FlagsDone:
longjmp (JumpBuf, RC_NOCONV); longjmp (JumpBuf, RC_NOCONV);
} }
ReadChar (); ReadChar ();
ReadInt (16); if (ReadInt (16) != 4) { /* 4 chars expected */
longjmp (JumpBuf, RC_NOCONV);
}
AssignInt (); AssignInt ();
++Conversions;
break; break;
case 'n': case 'n':
@ -542,20 +590,23 @@ FlagsDone:
break; break;
} }
}
/* Skip the format char */
goto Again;
} }
/* Push back the last unused character, provided it is not EOF */
if (C != EOF) {
D->unget (C, D->data);
} }
} else if (Result == RC_EOF) { } else {
/* Jump via JumpBuf means EOF on input */ /* Jump via JumpBuf means an error. If this happens at EOF with no
if (D->ccount == 0) { * conversions, it is considered an error, otherwise the number
* of conversions is returned (the default behaviour).
*/
if (C == EOF && D->ccount == 0) {
/* Special case: error */ /* Special case: error */
return -1; Conversions = EOF;
} }
} }

View File

@ -12,13 +12,11 @@
/* Forward */
struct indesc;
/* Type of the function that is called to input data. The function will /* Type of the function that is called to input data. The function will
* return EOF if no more data is available. * return EOF if no more data is available.
*/ */
typedef char (*infunc) (struct indesc* desc); typedef int __fastcall__ (*getfunc) (void* data);
typedef int __fastcall__ (*ungetfunc) (int c, void* data);
@ -26,21 +24,19 @@ typedef char (*infunc) (struct indesc* desc);
* Beware: The low level functions will access the structure on the assembly * Beware: The low level functions will access the structure on the assembly
* level, so check this when altering the structure. * level, so check this when altering the structure.
*/ */
struct indesc { struct scanfdata {
infunc fin; /* Pointer to input routine */ getfunc get; /* Pointer to input routine */
ungetfunc unget; /* Pointer to pushback routine */
unsigned ccount; /* Number of chars read */ unsigned ccount; /* Number of chars read */
/* Fields used outside of _scanf */ /* Fields used outside of _scanf */
char* buf; /* Pointer to input buffer */ void* data; /* Caller data */
unsigned size; /* Size of input buffer */
unsigned fill; /* Fill mark of input buffer */
unsigned ridx; /* Read index of input buffer */
}; };
/* Internal scanning routine */ /* Internal scanning routine */
int _scanf (struct indesc* d, const char* format, va_list ap); int _scanf (struct scanfdata* d, const char* format, va_list ap);

36
libsrc/common/fscanf.c Normal file
View File

@ -0,0 +1,36 @@
/*
* fscanf.c
*
* Ullrich von Bassewitz (uz@cc65.org), 2004-11-26
*
*/
#include <stdio.h>
/*****************************************************************************/
/* Code */
/*****************************************************************************/
int fscanf (FILE* f, const char* format, ...)
/* Standard C function */
{
va_list ap;
/* Setup for variable arguments */
va_start (ap, format);
/* Call vfscanf(). Since we know that va_end won't do anything, we will
* save the call and return the value directly.
*/
return vfscanf (f, format, ap);
}

36
libsrc/common/scanf.c Normal file
View File

@ -0,0 +1,36 @@
/*
* scanf.c
*
* Ullrich von Bassewitz (uz@cc65.org), 2004-11-26
*
*/
#include <stdio.h>
/*****************************************************************************/
/* Code */
/*****************************************************************************/
int scanf (const char* format, ...)
/* Standard C function */
{
va_list ap;
/* Setup for variable arguments */
va_start (ap, format);
/* Call vfscanf(). Since we know that va_end won't do anything, we will
* save the call and return the value directly.
*/
return vfscanf (stdin, format, ap);
}

39
libsrc/common/vfscanf.c Normal file
View File

@ -0,0 +1,39 @@
/*
* vfscanf.c
*
* Ullrich von Bassewitz (uz@cc65.org), 2004-11-26
*
*/
#include <stdio.h>
#include "_scanf.h"
/*****************************************************************************/
/* Code */
/*****************************************************************************/
int __fastcall__ vfscanf (FILE* f, const char* format, va_list ap)
/* Standard C function */
{
struct scanfdata d;
/* Initialize the data struct. We do only need the given file as user data,
* since the get and ungetc are crafted so they match the standard fgetc
* and ungetc functions.
*/
d.get = (getfunc) fgetc,
d.unget = (ungetfunc) ungetc,
d.data = f;
/* Call the internal function and return the result */
return _scanf (&d, format, ap);
}

View File

@ -12,21 +12,48 @@
/*****************************************************************************/
/* Data */
/*****************************************************************************/
struct sscanfdata {
const char* str; /* Pointer to input string */
unsigned index; /* Read index */
};
/*****************************************************************************/ /*****************************************************************************/
/* Code */ /* Code */
/*****************************************************************************/ /*****************************************************************************/
static char get (struct indesc* d) static int __fastcall__ get (struct sscanfdata* d)
/* Read a character from the input string and return it */ /* Read a character from the input string and return it */
{ {
char C; char C;
if (C = d->buf[d->ridx]) { if (C = d->str[d->index]) {
/* Increment index only if end not reached */ /* Increment index only if end not reached */
++d->ridx; ++d->index;
}
return C; return C;
} else {
return EOF;
}
}
static int __fastcall__ unget (int c, struct sscanfdata* d)
/* Push back a character onto the input stream */
{
/* We do assume here that the _scanf routine will not push back anything
* not read, so we can ignore c safely and won't check the index.
*/
--d->index;
return c;
} }
@ -34,17 +61,20 @@ static char get (struct indesc* d)
int __fastcall__ vsscanf (const char* str, const char* format, va_list ap) int __fastcall__ vsscanf (const char* str, const char* format, va_list ap)
/* Standard C function */ /* Standard C function */
{ {
struct indesc id; struct sscanfdata sd;
struct scanfdata d;
/* Initialize the indesc struct. We leave all fields uninitialized that we /* Initialize the data structs. The sscanfdata struct will be passed back
* don't need * to the get and unget functions by _scanf.
*/ */
id.fin = (infunc) get; d.get = (getfunc) get;
id.buf = (char*) str; d.unget = (ungetfunc) unget,
id.ridx = 0; d.data = &sd;
sd.str = str;
sd.index = 0;
/* Call the internal function and return the result */ /* Call the internal function and return the result */
return _scanf (&id, format, ap); return _scanf (&d, format, ap);
} }