From b1d4e1613bf34f846d9a0638d4e7cdb1f9b5ab71 Mon Sep 17 00:00:00 2001 From: cuz Date: Fri, 26 Nov 2004 22:16:54 +0000 Subject: [PATCH] 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 --- libsrc/common/.cvsignore | 5 + libsrc/common/Makefile | 3 + libsrc/common/_scanf.c | 349 ++++++++++++++++++++++----------------- libsrc/common/_scanf.h | 20 +-- libsrc/common/fscanf.c | 36 ++++ libsrc/common/scanf.c | 36 ++++ libsrc/common/vfscanf.c | 39 +++++ libsrc/common/vsscanf.c | 56 +++++-- 8 files changed, 370 insertions(+), 174 deletions(-) create mode 100644 libsrc/common/fscanf.c create mode 100644 libsrc/common/scanf.c create mode 100644 libsrc/common/vfscanf.c diff --git a/libsrc/common/.cvsignore b/libsrc/common/.cvsignore index adb367888..25e50ed2a 100644 --- a/libsrc/common/.cvsignore +++ b/libsrc/common/.cvsignore @@ -14,6 +14,7 @@ fgets.s fputc.s fputs.s freopen.s +fscanf.s fseek.s fsetpos.s ftell.s @@ -28,6 +29,7 @@ puts.s qsort.s realloc.s rewind.s +scanf.s sleep.s sscanf.s strftime.s @@ -35,4 +37,7 @@ strtok.s strxfrm.s system.s timezone.s +vfscanf.s vsscanf.s + + diff --git a/libsrc/common/Makefile b/libsrc/common/Makefile index 7f3babe33..c72f99742 100644 --- a/libsrc/common/Makefile +++ b/libsrc/common/Makefile @@ -44,6 +44,7 @@ C_OBJS = _afailed.o \ fputc.o \ fputs.o \ freopen.o \ + fscanf.o \ fseek.o \ fsetpos.o \ ftell.o \ @@ -58,6 +59,7 @@ C_OBJS = _afailed.o \ qsort.o \ realloc.o \ rewind.o \ + scanf.o \ sleep.o \ sscanf.o \ strftime.o \ @@ -65,6 +67,7 @@ C_OBJS = _afailed.o \ strtok.o \ system.o \ timezone.o \ + vfscanf.o \ vsscanf.o diff --git a/libsrc/common/_scanf.c b/libsrc/common/_scanf.c index 7351a5d93..53d65694e 100644 --- a/libsrc/common/_scanf.c +++ b/libsrc/common/_scanf.c @@ -39,21 +39,20 @@ -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 struct scanfdata* 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 int C; /* Character from input */ +static unsigned Width; /* Maximum field width */ +static long IntVal; /* Converted int value */ +static unsigned Conversions; /* Number of conversions */ +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 unsigned char Invert; /* Do we need to invert the charset? */ -static unsigned char CharSet[32]; /* 32 * 8 bits = 256 bits */ +static unsigned char Positive; /* Flag for positive value */ +static unsigned char NoAssign; /* Supppress assigment */ +static unsigned char Invert; /* Do we need to invert the charset? */ +static unsigned char CharSet[32]; /* 32 * 8 bits = 256 bits */ static const unsigned char Bits[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; @@ -120,7 +119,7 @@ static void InvertCharSet (void) /*****************************************************************************/ -/* Code */ +/* Code */ /*****************************************************************************/ @@ -128,8 +127,21 @@ static void InvertCharSet (void) static void ReadChar (void) /* Get an input character, count characters */ { - C = D->fin (D); - ++D->ccount; + C = D->get (D->data); + if (C != EOF) { + ++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,29 +183,8 @@ static unsigned char HexVal (char C) if (isdigit (C)) { return C - '0'; } else { - return C - 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 (); + return toupper (C) - ('A' - 10); } - - /* One more conversion */ - ++Conversions; } @@ -216,10 +207,7 @@ static void AssignInt (void) asm ("stx ptr1+1"); /* Get the number of bytes-1 to copy */ - asm ("ldy #3"); - asm ("lda %v", IsLong); - asm ("bne L1"); - asm ("ldy #1"); + asm ("ldy %v", IntBytes); /* Assign the integer value */ 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 * 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 */ unsigned char Result; /* setjmp result */ char* S; - unsigned char Base; /* Integer base in %i */ unsigned char HaveWidth; /* True if a width was given */ char Start; /* Start of range */ @@ -263,13 +323,13 @@ int _scanf (struct indesc* D_, register const char* format, va_list ap_) Again: /* Get the next input character */ - ReadChar (); + ReadChar (); /* Walk over the format string */ while (F = *format++) { /* Check for a conversion */ - if (F != '%' || *format == '%') { + if (F != '%' || *format == '%') { /* %% or any char other than % */ if (F == '%') { @@ -279,25 +339,25 @@ Again: /* 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. + /* 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; - } 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 * the number of conversions. */ return Conversions; - } else { - - /* A match. Read the next input character and start over */ - goto Again; - } } else { @@ -305,97 +365,82 @@ Again: /* A conversion. Skip the percent sign. */ F = *format++; - /* Initialize variables */ - NoAssign = 0; - IsShort = 0; - IsLong = 0; - Width = UINT_MAX; - HaveWidth = 0; - - /* Check for flags. */ - while (1) { - if (isdigit (F)) { - HaveWidth = 1; - 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: + /* 1. Assignment suppression */ + if (F == '*') { + F = *format++; + NoAssign = 1; + } else { + NoAssign = 0; + } + + /* 2. Maximum field width */ + Width = UINT_MAX; + HaveWidth = 0; + if (isdigit (F)) { + HaveWidth = 1; + Width = 0; + do { + /* ### Non portable ### */ + Width = Width * 10 + (F & 0x0F); + F = *format++; + } while (isdigit (F)); + } + + /* 3. Length modifier */ + IntBytes = sizeof(int) - 1; + switch (F) { + case 'h': + if (*format == 'h') { + IntBytes = sizeof(char) - 1; + ++format; + } + F = *format++; + break; + + 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; + } - /* Check for the actual conversion character */ + /* 4. Conversion specifier */ switch (F) { - case 'D': - IsLong = 1; - case 'd': - /* Optionally signed decimal integer */ - SkipWhite (); - ReadSign (); - ReadInt (10); - if (!Positive) { - IntVal = -IntVal; - } - AssignInt (); - break; + /* 'd' and 'u' conversions are actually the same, since the + * standard says that evene 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 */ - 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 (); + ScanInt (0); break; case 'o': - /* Unsigned octal integer */ - SkipWhite (); - ReadInt (8); - AssignInt (); + /* Optionally signed octal integer */ + ScanInt (8); break; - case 'u': - /* Unsigned decimal integer */ - SkipWhite (); - ReadInt (10); - AssignInt (); - break; - case 'x': case 'X': - /* Unsigned hexadecimal integer */ - SkipWhite (); - ReadInt (16); - AssignInt (); + /* Optionally signed hexadecimal integer */ + ScanInt (16); break; case 'E': @@ -408,7 +453,7 @@ FlagsDone: case 's': /* Whitespace terminated string */ - SkipWhite (); + SkipWhite (); if (!NoAssign) { S = va_arg (ap, char*); } @@ -416,7 +461,7 @@ FlagsDone: if (!NoAssign) { *S++ = C; } - ReadChar (); + ReadChar (); } /* Terminate the string just read */ if (!NoAssign) { @@ -435,12 +480,12 @@ FlagsDone: S = va_arg (ap, char*); while (Width--) { *S++ = C; - ReadChar (); + ReadCharWithCheck (); } } else { /* Just skip as many chars as given */ while (Width--) { - ReadChar (); + ReadCharWithCheck (); } } ++Conversions; @@ -516,7 +561,7 @@ FlagsDone: break; case 'p': - /* Pointer, format is 0xABCD */ + /* Pointer, format is 0xABCD */ SkipWhite (); if (C != '0') { longjmp (JumpBuf, RC_NOCONV); @@ -526,8 +571,11 @@ FlagsDone: longjmp (JumpBuf, RC_NOCONV); } ReadChar (); - ReadInt (16); + if (ReadInt (16) != 4) { /* 4 chars expected */ + longjmp (JumpBuf, RC_NOCONV); + } AssignInt (); + ++Conversions; break; case 'n': @@ -542,21 +590,24 @@ FlagsDone: 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 */ - if (D->ccount == 0) { + /* Jump via JumpBuf means an error. If this happens at EOF with no + * 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 */ - return -1; - } + Conversions = EOF; + } } diff --git a/libsrc/common/_scanf.h b/libsrc/common/_scanf.h index a385e4ae4..e477c37da 100644 --- a/libsrc/common/_scanf.h +++ b/libsrc/common/_scanf.h @@ -12,13 +12,11 @@ -/* Forward */ -struct indesc; - /* Type of the function that is called to input data. The function will * 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 * level, so check this when altering the structure. */ -struct indesc { - infunc fin; /* Pointer to input routine */ - unsigned ccount; /* Number of chars read */ +struct scanfdata { + getfunc get; /* Pointer to input routine */ + ungetfunc unget; /* Pointer to pushback routine */ + unsigned ccount; /* Number of chars read */ /* Fields used outside of _scanf */ - char* buf; /* Pointer to input buffer */ - unsigned size; /* Size of input buffer */ - unsigned fill; /* Fill mark of input buffer */ - unsigned ridx; /* Read index of input buffer */ + void* data; /* Caller data */ }; /* 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); diff --git a/libsrc/common/fscanf.c b/libsrc/common/fscanf.c new file mode 100644 index 000000000..dae7109a5 --- /dev/null +++ b/libsrc/common/fscanf.c @@ -0,0 +1,36 @@ +/* + * fscanf.c + * + * Ullrich von Bassewitz (uz@cc65.org), 2004-11-26 + * + */ + + + +#include + + + +/*****************************************************************************/ +/* 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); +} + + + + diff --git a/libsrc/common/scanf.c b/libsrc/common/scanf.c new file mode 100644 index 000000000..d92174afc --- /dev/null +++ b/libsrc/common/scanf.c @@ -0,0 +1,36 @@ +/* + * scanf.c + * + * Ullrich von Bassewitz (uz@cc65.org), 2004-11-26 + * + */ + + + +#include + + + +/*****************************************************************************/ +/* 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); +} + + + + diff --git a/libsrc/common/vfscanf.c b/libsrc/common/vfscanf.c new file mode 100644 index 000000000..0f62113f7 --- /dev/null +++ b/libsrc/common/vfscanf.c @@ -0,0 +1,39 @@ +/* + * vfscanf.c + * + * Ullrich von Bassewitz (uz@cc65.org), 2004-11-26 + * + */ + + + +#include +#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); +} + + + diff --git a/libsrc/common/vsscanf.c b/libsrc/common/vsscanf.c index 95fcf5e9b..68d748c2f 100644 --- a/libsrc/common/vsscanf.c +++ b/libsrc/common/vsscanf.c @@ -9,7 +9,20 @@ #include #include "_scanf.h" - + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +struct sscanfdata { + const char* str; /* Pointer to input string */ + unsigned index; /* Read index */ +}; + /*****************************************************************************/ @@ -18,15 +31,29 @@ -static char get (struct indesc* d) +static int __fastcall__ get (struct sscanfdata* d) /* Read a character from the input string and return it */ { char C; - if (C = d->buf[d->ridx]) { - /* Increment index only if end not reached */ - ++d->ridx; + if (C = d->str[d->index]) { + /* Increment index only if end not reached */ + ++d->index; + return C; + } else { + return EOF; } - return C; +} + + + +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) /* Standard C function */ { - struct indesc id; + struct sscanfdata sd; + struct scanfdata d; - /* Initialize the indesc struct. We leave all fields uninitialized that we - * don't need + /* Initialize the data structs. The sscanfdata struct will be passed back + * to the get and unget functions by _scanf. */ - id.fin = (infunc) get; - id.buf = (char*) str; - id.ridx = 0; + d.get = (getfunc) get; + d.unget = (ungetfunc) unget, + d.data = &sd; + sd.str = str; + sd.index = 0; /* Call the internal function and return the result */ - return _scanf (&id, format, ap); + return _scanf (&d, format, ap); } -- 2.39.5