2000-09-24 19:06:59 +00:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/* */
|
2013-05-09 11:56:54 +00:00
|
|
|
|
/* scanner.c */
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* */
|
2013-05-09 11:56:54 +00:00
|
|
|
|
/* Configuration file scanner for the da65 disassembler */
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* */
|
|
|
|
|
/* */
|
2005-01-08 20:16:57 +00:00
|
|
|
|
/* (C) 2000-2005 Ullrich von Bassewitz */
|
2003-08-09 08:28:44 +00:00
|
|
|
|
/* R<>merstrasse 52 */
|
|
|
|
|
/* D-70794 Filderstadt */
|
|
|
|
|
/* EMail: uz@cc65.org */
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* */
|
|
|
|
|
/* This software is provided 'as-is', without any expressed or implied */
|
|
|
|
|
/* warranty. In no event will the authors be held liable for any damages */
|
|
|
|
|
/* arising from the use of this software. */
|
|
|
|
|
/* */
|
|
|
|
|
/* Permission is granted to anyone to use this software for any purpose, */
|
|
|
|
|
/* including commercial applications, and to alter it and redistribute it */
|
|
|
|
|
/* freely, subject to the following restrictions: */
|
|
|
|
|
/* */
|
|
|
|
|
/* 1. The origin of this software must not be misrepresented; you must not */
|
|
|
|
|
/* claim that you wrote the original software. If you use this software */
|
|
|
|
|
/* in a product, an acknowledgment in the product documentation would be */
|
|
|
|
|
/* appreciated but is not required. */
|
|
|
|
|
/* 2. Altered source versions must be plainly marked as such, and must not */
|
|
|
|
|
/* be misrepresented as being the original software. */
|
|
|
|
|
/* 3. This notice may not be removed or altered from any source */
|
|
|
|
|
/* distribution. */
|
|
|
|
|
/* */
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
/* common */
|
2002-04-20 11:49:53 +00:00
|
|
|
|
#include "chartype.h"
|
2000-09-24 19:06:59 +00:00
|
|
|
|
#include "xsprintf.h"
|
2018-06-10 16:53:35 +00:00
|
|
|
|
#include "xmalloc.h"
|
|
|
|
|
#include "strbuf.h"
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
/* ld65 */
|
|
|
|
|
#include "global.h"
|
|
|
|
|
#include "error.h"
|
|
|
|
|
#include "scanner.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2013-05-09 11:56:54 +00:00
|
|
|
|
/* Data */
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Current token and attributes */
|
2003-08-09 08:28:44 +00:00
|
|
|
|
unsigned InfoTok;
|
2013-05-09 11:56:54 +00:00
|
|
|
|
char InfoSVal [CFG_MAX_IDENT_LEN+1];
|
2003-08-09 08:28:44 +00:00
|
|
|
|
long InfoIVal;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
/* Error location */
|
2013-05-09 11:56:54 +00:00
|
|
|
|
unsigned InfoErrorLine;
|
|
|
|
|
unsigned InfoErrorCol;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
/* Input sources for the configuration */
|
2013-05-09 11:56:54 +00:00
|
|
|
|
static const char* InfoFile = 0;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
/* Other input stuff */
|
2013-05-09 11:56:54 +00:00
|
|
|
|
static int C = ' ';
|
|
|
|
|
static unsigned InputLine = 1;
|
|
|
|
|
static unsigned InputCol = 0;
|
|
|
|
|
static FILE* InputFile = 0;
|
2018-06-10 16:53:35 +00:00
|
|
|
|
static char* InputSrcName = 0;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2013-05-09 11:56:54 +00:00
|
|
|
|
/* Error handling */
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoWarning (const char* Format, ...)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Print a warning message adding file name and line number of the config file */
|
|
|
|
|
{
|
|
|
|
|
char Buf [512];
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
|
|
va_start (ap, Format);
|
|
|
|
|
xvsprintf (Buf, sizeof (Buf), Format, ap);
|
2001-09-15 13:36:59 +00:00
|
|
|
|
va_end (ap);
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
2018-06-10 16:53:35 +00:00
|
|
|
|
fprintf (stderr, "%s(%u): Warning: %s\n",
|
|
|
|
|
InputSrcName, InfoErrorLine, Buf);
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoError (const char* Format, ...)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Print an error message adding file name and line number of the config file */
|
|
|
|
|
{
|
|
|
|
|
char Buf [512];
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
|
|
va_start (ap, Format);
|
|
|
|
|
xvsprintf (Buf, sizeof (Buf), Format, ap);
|
|
|
|
|
va_end (ap);
|
|
|
|
|
|
2018-06-10 16:53:35 +00:00
|
|
|
|
fprintf (stderr, "%s(%u): Error: %s\n",
|
|
|
|
|
InputSrcName, InfoErrorLine, Buf);
|
|
|
|
|
exit (EXIT_FAILURE);
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
2001-09-15 13:36:59 +00:00
|
|
|
|
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
|
2018-06-11 17:06:01 +00:00
|
|
|
|
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/*****************************************************************************/
|
2013-05-09 11:56:54 +00:00
|
|
|
|
/* Code */
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void NextChar (void)
|
|
|
|
|
/* Read the next character from the input file */
|
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
/* Read from the file */
|
|
|
|
|
C = getc (InputFile);
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
/* Count columns */
|
|
|
|
|
if (C != EOF) {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
++InputCol;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Count lines */
|
|
|
|
|
if (C == '\n') {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
++InputLine;
|
|
|
|
|
InputCol = 0;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned DigitVal (int C)
|
|
|
|
|
/* Return the value for a numeric digit */
|
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
if (IsDigit (C)) {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
return C - '0';
|
2000-09-24 19:06:59 +00:00
|
|
|
|
} else {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
return toupper (C) - 'A' + 10;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-06-10 16:53:35 +00:00
|
|
|
|
static void SkipBlanks (int SingleLine)
|
|
|
|
|
{
|
|
|
|
|
while (C != EOF && (!SingleLine || C != '\n') && IsSpace (C)) {
|
|
|
|
|
NextChar ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 14:50:46 +00:00
|
|
|
|
|
|
|
|
|
|
2018-06-11 17:06:01 +00:00
|
|
|
|
static long GetDecimalToken (void)
|
2018-06-10 16:53:35 +00:00
|
|
|
|
{
|
|
|
|
|
long Value = 0;
|
|
|
|
|
|
|
|
|
|
while (C != EOF && IsDigit (C)) {
|
|
|
|
|
Value = Value * 10 + DigitVal (C);
|
|
|
|
|
NextChar ();
|
|
|
|
|
}
|
|
|
|
|
return Value;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 14:50:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int GetEncodedChar (char* Buf, unsigned* IPtr, unsigned Size)
|
2018-06-10 16:53:35 +00:00
|
|
|
|
{
|
|
|
|
|
char Decoded = 0;
|
|
|
|
|
int Count;
|
|
|
|
|
|
|
|
|
|
if (C == EOF) {
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (C != '\\') {
|
|
|
|
|
Decoded = C;
|
|
|
|
|
NextChar ();
|
|
|
|
|
goto Store;
|
|
|
|
|
}
|
|
|
|
|
NextChar (); /* consume '\\' */
|
|
|
|
|
if (C == EOF) {
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (IsODigit (C)) {
|
|
|
|
|
Count = 3;
|
|
|
|
|
do {
|
|
|
|
|
Decoded = Decoded * 8 + DigitVal (C);
|
|
|
|
|
NextChar ();
|
|
|
|
|
--Count;
|
|
|
|
|
} while (Count > 0 && C != EOF && IsODigit (C));
|
|
|
|
|
} else if (C == 'x') {
|
|
|
|
|
NextChar (); /* consume 'x' */
|
|
|
|
|
Count = 2;
|
|
|
|
|
while (Count > 0 && C != EOF && IsXDigit (C)) {
|
|
|
|
|
Decoded = Decoded * 16 + DigitVal (C);
|
|
|
|
|
NextChar ();
|
|
|
|
|
--Count;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
switch (C) {
|
|
|
|
|
case '"': case '\'': case '\\':
|
2018-06-25 14:50:46 +00:00
|
|
|
|
Decoded = C; break;
|
|
|
|
|
case 't': Decoded = '\t'; break;
|
|
|
|
|
case 'r': Decoded = '\r'; break;
|
|
|
|
|
case 'n': Decoded = '\n'; break;
|
|
|
|
|
default: return -1;
|
2018-06-10 16:53:35 +00:00
|
|
|
|
}
|
|
|
|
|
NextChar ();
|
|
|
|
|
}
|
|
|
|
|
Store:
|
|
|
|
|
if (*IPtr < Size - 1) {
|
|
|
|
|
Buf [(*IPtr)++] = Decoded;
|
|
|
|
|
}
|
|
|
|
|
Buf [*IPtr] = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 14:50:46 +00:00
|
|
|
|
|
|
|
|
|
|
2018-06-10 16:53:35 +00:00
|
|
|
|
static void LineMarkerOrComment ()
|
|
|
|
|
/* Handle a line beginning with '#'. Possible interpretations are:
|
2018-06-25 14:50:46 +00:00
|
|
|
|
** - #line <lineno> ["<filename>"] (C preprocessor input)
|
2018-06-11 17:06:01 +00:00
|
|
|
|
** - # <lineno> "<filename>" [<flag>]... (gcc preprocessor output)
|
|
|
|
|
** - #<comment>
|
|
|
|
|
*/
|
2018-06-10 16:53:35 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned long LineNo = 0;
|
|
|
|
|
int LineDirective = 0;
|
|
|
|
|
StrBuf SrcNameBuf = AUTO_STRBUF_INITIALIZER;
|
2018-06-11 17:06:01 +00:00
|
|
|
|
|
2018-06-10 16:53:35 +00:00
|
|
|
|
/* Skip the first "# " */
|
|
|
|
|
NextChar ();
|
|
|
|
|
SkipBlanks (1);
|
|
|
|
|
|
|
|
|
|
/* Check "line" */
|
|
|
|
|
if (C == 'l') {
|
|
|
|
|
char MaybeLine [6];
|
|
|
|
|
unsigned I;
|
|
|
|
|
for (I = 0; I < sizeof MaybeLine - 1 && C != EOF && IsAlNum (C); ++I) {
|
|
|
|
|
MaybeLine [I] = C;
|
|
|
|
|
NextChar ();
|
|
|
|
|
}
|
|
|
|
|
MaybeLine [I] = 0;
|
|
|
|
|
if (strcmp (MaybeLine, "line") != 0) {
|
|
|
|
|
goto NotMarker;
|
|
|
|
|
}
|
|
|
|
|
LineDirective = 1;
|
|
|
|
|
SkipBlanks (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get line number */
|
|
|
|
|
if (C == EOF || !IsDigit (C)) {
|
|
|
|
|
goto NotMarker;
|
|
|
|
|
}
|
|
|
|
|
LineNo = GetDecimalToken ();
|
|
|
|
|
SkipBlanks (1);
|
|
|
|
|
|
|
|
|
|
/* Get the source file name */
|
|
|
|
|
if (C != '\"') {
|
|
|
|
|
/* The source file name is missing */
|
|
|
|
|
if (LineDirective && C == '\n') {
|
|
|
|
|
/* got #line <lineno> */
|
|
|
|
|
NextChar ();
|
|
|
|
|
InputLine = LineNo;
|
|
|
|
|
goto Last;
|
|
|
|
|
} else {
|
|
|
|
|
goto NotMarker;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NextChar ();
|
|
|
|
|
while (C != EOF && C != '\n' && C != '\"') {
|
|
|
|
|
char DecodeBuf [2];
|
|
|
|
|
unsigned I = 0;
|
|
|
|
|
if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0) {
|
|
|
|
|
goto BadMarker;
|
|
|
|
|
}
|
|
|
|
|
SB_AppendBuf (&SrcNameBuf, DecodeBuf, I);
|
|
|
|
|
}
|
|
|
|
|
if (C != '\"') {
|
|
|
|
|
goto BadMarker;
|
|
|
|
|
}
|
|
|
|
|
NextChar ();
|
|
|
|
|
|
|
|
|
|
/* Ignore until the end of line */
|
|
|
|
|
while (C != EOF && C != '\n') {
|
|
|
|
|
NextChar ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Accepted a line marker */
|
|
|
|
|
SB_Terminate (&SrcNameBuf);
|
|
|
|
|
xfree (InputSrcName);
|
|
|
|
|
InputSrcName = SB_GetBuf (&SrcNameBuf);
|
|
|
|
|
SB_Init (&SrcNameBuf);
|
|
|
|
|
InputLine = (unsigned)LineNo;
|
|
|
|
|
NextChar ();
|
|
|
|
|
goto Last;
|
|
|
|
|
|
|
|
|
|
BadMarker:
|
|
|
|
|
InfoWarning ("Bad line marker");
|
|
|
|
|
NotMarker:
|
|
|
|
|
while (C != EOF && C != '\n') {
|
|
|
|
|
NextChar ();
|
|
|
|
|
}
|
|
|
|
|
NextChar ();
|
|
|
|
|
Last:
|
|
|
|
|
SB_Done (&SrcNameBuf);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 14:50:46 +00:00
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoNextTok (void)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Read the next token from the input stream */
|
|
|
|
|
{
|
|
|
|
|
unsigned I;
|
2018-06-10 16:53:35 +00:00
|
|
|
|
char DecodeBuf [2];
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
Again:
|
|
|
|
|
/* Skip whitespace */
|
2018-06-10 16:53:35 +00:00
|
|
|
|
SkipBlanks (0);
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
/* Remember the current position */
|
2003-08-09 08:28:44 +00:00
|
|
|
|
InfoErrorLine = InputLine;
|
|
|
|
|
InfoErrorCol = InputCol;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
/* Identifier? */
|
2002-04-20 11:49:53 +00:00
|
|
|
|
if (C == '_' || IsAlpha (C)) {
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
2013-05-09 11:56:54 +00:00
|
|
|
|
/* Read the identifier */
|
|
|
|
|
I = 0;
|
|
|
|
|
while (C == '_' || IsAlNum (C)) {
|
|
|
|
|
if (I < CFG_MAX_IDENT_LEN) {
|
|
|
|
|
InfoSVal [I++] = C;
|
|
|
|
|
}
|
|
|
|
|
NextChar ();
|
|
|
|
|
}
|
|
|
|
|
InfoSVal [I] = '\0';
|
|
|
|
|
InfoTok = INFOTOK_IDENT;
|
|
|
|
|
return;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Hex number? */
|
|
|
|
|
if (C == '$') {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
NextChar ();
|
|
|
|
|
if (!IsXDigit (C)) {
|
|
|
|
|
InfoError ("Hex digit expected");
|
|
|
|
|
}
|
|
|
|
|
InfoIVal = 0;
|
|
|
|
|
while (IsXDigit (C)) {
|
|
|
|
|
InfoIVal = InfoIVal * 16 + DigitVal (C);
|
|
|
|
|
NextChar ();
|
|
|
|
|
}
|
|
|
|
|
InfoTok = INFOTOK_INTCON;
|
|
|
|
|
return;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Decimal number? */
|
2003-08-09 08:28:44 +00:00
|
|
|
|
if (IsDigit (C)) {
|
2018-06-10 16:53:35 +00:00
|
|
|
|
InfoIVal = GetDecimalToken ();
|
2013-05-09 11:56:54 +00:00
|
|
|
|
InfoTok = INFOTOK_INTCON;
|
|
|
|
|
return;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Other characters */
|
|
|
|
|
switch (C) {
|
|
|
|
|
|
2013-05-09 11:56:54 +00:00
|
|
|
|
case '{':
|
|
|
|
|
NextChar ();
|
|
|
|
|
InfoTok = INFOTOK_LCURLY;
|
|
|
|
|
break;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
2013-05-09 11:56:54 +00:00
|
|
|
|
case '}':
|
|
|
|
|
NextChar ();
|
|
|
|
|
InfoTok = INFOTOK_RCURLY;
|
|
|
|
|
break;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
2013-05-09 11:56:54 +00:00
|
|
|
|
case ';':
|
|
|
|
|
NextChar ();
|
|
|
|
|
InfoTok = INFOTOK_SEMI;
|
|
|
|
|
break;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
2013-05-09 11:56:54 +00:00
|
|
|
|
case '.':
|
|
|
|
|
NextChar ();
|
|
|
|
|
InfoTok = INFOTOK_DOT;
|
|
|
|
|
break;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
2013-05-09 11:56:54 +00:00
|
|
|
|
case ',':
|
|
|
|
|
NextChar ();
|
|
|
|
|
InfoTok = INFOTOK_COMMA;
|
|
|
|
|
break;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
2013-05-09 11:56:54 +00:00
|
|
|
|
case '=':
|
|
|
|
|
NextChar ();
|
|
|
|
|
InfoTok = INFOTOK_EQ;
|
|
|
|
|
break;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
case ':':
|
2013-05-09 11:56:54 +00:00
|
|
|
|
NextChar ();
|
|
|
|
|
InfoTok = INFOTOK_COLON;
|
|
|
|
|
break;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
case '\"':
|
2013-05-09 11:56:54 +00:00
|
|
|
|
NextChar ();
|
|
|
|
|
I = 0;
|
2018-10-06 15:30:35 +00:00
|
|
|
|
InfoSVal[0] = '\0';
|
2018-06-10 16:53:35 +00:00
|
|
|
|
while (C != EOF && C != '\"') {
|
|
|
|
|
if (GetEncodedChar (InfoSVal, &I, sizeof InfoSVal) < 0) {
|
|
|
|
|
if (C == EOF) {
|
|
|
|
|
InfoError ("Unterminated string");
|
|
|
|
|
} else {
|
|
|
|
|
InfoError ("Invalid escape char: %c", C);
|
2013-05-09 11:56:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-06-10 16:53:35 +00:00
|
|
|
|
if (C != '\"') {
|
|
|
|
|
InfoError ("Unterminated string");
|
|
|
|
|
}
|
2018-06-25 14:50:46 +00:00
|
|
|
|
NextChar ();
|
2013-05-09 11:56:54 +00:00
|
|
|
|
InfoTok = INFOTOK_STRCON;
|
|
|
|
|
break;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
2005-01-08 20:16:57 +00:00
|
|
|
|
case '\'':
|
|
|
|
|
NextChar ();
|
2018-06-10 16:53:35 +00:00
|
|
|
|
if (C == EOF || IsControl (C) || C == '\'') {
|
2005-01-08 20:16:57 +00:00
|
|
|
|
InfoError ("Invalid character constant");
|
|
|
|
|
}
|
2018-06-10 16:53:35 +00:00
|
|
|
|
if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0 || I != 1) {
|
|
|
|
|
InfoError ("Invalid character constant");
|
|
|
|
|
}
|
|
|
|
|
InfoIVal = DecodeBuf [0];
|
2005-01-08 20:16:57 +00:00
|
|
|
|
if (C != '\'') {
|
|
|
|
|
InfoError ("Unterminated character constant");
|
|
|
|
|
}
|
|
|
|
|
NextChar ();
|
|
|
|
|
InfoTok = INFOTOK_CHARCON;
|
|
|
|
|
break;
|
|
|
|
|
|
2000-09-24 19:06:59 +00:00
|
|
|
|
case '#':
|
2018-06-10 16:53:35 +00:00
|
|
|
|
/* # lineno "sourcefile" or # comment */
|
2018-06-11 17:06:01 +00:00
|
|
|
|
if (SyncLines && InputCol == 1) {
|
2018-06-10 16:53:35 +00:00
|
|
|
|
LineMarkerOrComment ();
|
|
|
|
|
} else {
|
|
|
|
|
do {
|
|
|
|
|
NextChar ();
|
|
|
|
|
} while (C != EOF && C != '\n');
|
2013-05-09 11:56:54 +00:00
|
|
|
|
NextChar ();
|
|
|
|
|
}
|
|
|
|
|
if (C != EOF) {
|
|
|
|
|
goto Again;
|
|
|
|
|
}
|
|
|
|
|
InfoTok = INFOTOK_EOF;
|
|
|
|
|
break;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
2018-06-10 16:53:35 +00:00
|
|
|
|
case '/':
|
|
|
|
|
/* C++ style comment */
|
|
|
|
|
NextChar ();
|
|
|
|
|
if (C != '/') {
|
|
|
|
|
InfoError ("Invalid token `/'");
|
|
|
|
|
}
|
|
|
|
|
do {
|
|
|
|
|
NextChar ();
|
|
|
|
|
} while (C != '\n' && C != EOF);
|
|
|
|
|
if (C != EOF) {
|
|
|
|
|
goto Again;
|
|
|
|
|
}
|
|
|
|
|
InfoTok = INFOTOK_EOF;
|
|
|
|
|
break;
|
|
|
|
|
|
2000-09-24 19:06:59 +00:00
|
|
|
|
case EOF:
|
2013-05-09 11:56:54 +00:00
|
|
|
|
InfoTok = INFOTOK_EOF;
|
|
|
|
|
break;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
2013-05-09 11:56:54 +00:00
|
|
|
|
default:
|
|
|
|
|
InfoError ("Invalid character `%c'", C);
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoConsume (unsigned T, const char* Msg)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Skip a token, print an error message if not found */
|
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
if (InfoTok != T) {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
InfoError (Msg);
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
2003-08-09 08:28:44 +00:00
|
|
|
|
InfoNextTok ();
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoConsumeLCurly (void)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Consume a left curly brace */
|
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
InfoConsume (INFOTOK_LCURLY, "`{' expected");
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoConsumeRCurly (void)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Consume a right curly brace */
|
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
InfoConsume (INFOTOK_RCURLY, "`}' expected");
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoConsumeSemi (void)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Consume a semicolon */
|
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
InfoConsume (INFOTOK_SEMI, "`;' expected");
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoConsumeColon (void)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Consume a colon */
|
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
InfoConsume (INFOTOK_COLON, "`:' expected");
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoOptionalComma (void)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Consume a comma if there is one */
|
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
if (InfoTok == INFOTOK_COMMA) {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
InfoNextTok ();
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoOptionalAssign (void)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Consume an equal sign if there is one */
|
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
if (InfoTok == INFOTOK_EQ) {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
InfoNextTok ();
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoAssureInt (void)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Make sure the next token is an integer */
|
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
if (InfoTok != INFOTOK_INTCON) {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
InfoError ("Integer constant expected");
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoAssureStr (void)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Make sure the next token is a string constant */
|
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
if (InfoTok != INFOTOK_STRCON) {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
InfoError ("String constant expected");
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-01-08 20:16:57 +00:00
|
|
|
|
void InfoAssureChar (void)
|
|
|
|
|
/* Make sure the next token is a char constant */
|
|
|
|
|
{
|
|
|
|
|
if (InfoTok != INFOTOK_STRCON) {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
InfoError ("Character constant expected");
|
2005-01-08 20:16:57 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoAssureIdent (void)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Make sure the next token is an identifier */
|
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
if (InfoTok != INFOTOK_IDENT) {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
InfoError ("Identifier expected");
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoRangeCheck (long Lo, long Hi)
|
|
|
|
|
/* Check the range of InfoIVal */
|
2000-09-24 19:06:59 +00:00
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
if (InfoIVal < Lo || InfoIVal > Hi) {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
InfoError ("Range error");
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Map an identifier to one of the special tokens in the table */
|
|
|
|
|
{
|
|
|
|
|
unsigned I;
|
|
|
|
|
|
|
|
|
|
/* We need an identifier */
|
2003-08-09 08:28:44 +00:00
|
|
|
|
if (InfoTok == INFOTOK_IDENT) {
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
2013-05-09 11:56:54 +00:00
|
|
|
|
/* Make it upper case */
|
|
|
|
|
I = 0;
|
|
|
|
|
while (InfoSVal [I]) {
|
|
|
|
|
InfoSVal [I] = toupper (InfoSVal [I]);
|
|
|
|
|
++I;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Linear search */
|
|
|
|
|
for (I = 0; I < Size; ++I) {
|
|
|
|
|
if (strcmp (InfoSVal, Table [I].Ident) == 0) {
|
|
|
|
|
InfoTok = Table [I].Tok;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2000-09-24 19:06:59 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Not found or no identifier */
|
2003-08-09 08:28:44 +00:00
|
|
|
|
InfoError ("%s expected", Name);
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoBoolToken (void)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Map an identifier or integer to a boolean token */
|
|
|
|
|
{
|
|
|
|
|
static const IdentTok Booleans [] = {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
{ "YES", INFOTOK_TRUE },
|
|
|
|
|
{ "NO", INFOTOK_FALSE },
|
2003-08-09 08:28:44 +00:00
|
|
|
|
{ "TRUE", INFOTOK_TRUE },
|
|
|
|
|
{ "FALSE", INFOTOK_FALSE },
|
2013-05-09 11:56:54 +00:00
|
|
|
|
{ "ON", INFOTOK_TRUE },
|
|
|
|
|
{ "OFF", INFOTOK_FALSE },
|
2000-09-24 19:06:59 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* If we have an identifier, map it to a boolean token */
|
2003-08-09 08:28:44 +00:00
|
|
|
|
if (InfoTok == INFOTOK_IDENT) {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
InfoSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
|
2000-09-24 19:06:59 +00:00
|
|
|
|
} else {
|
2013-05-09 11:56:54 +00:00
|
|
|
|
/* We expected an integer here */
|
|
|
|
|
if (InfoTok != INFOTOK_INTCON) {
|
|
|
|
|
InfoError ("Boolean value expected");
|
|
|
|
|
}
|
|
|
|
|
InfoTok = (InfoIVal == 0)? INFOTOK_FALSE : INFOTOK_TRUE;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoSetName (const char* Name)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Set a name for a config file */
|
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
InfoFile = Name;
|
2018-06-10 16:53:35 +00:00
|
|
|
|
xfree(InputSrcName);
|
|
|
|
|
InputSrcName = xstrdup(Name);
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
int InfoAvail ()
|
|
|
|
|
/* Return true if we have an info file given */
|
2000-09-24 19:06:59 +00:00
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
return (InfoFile != 0);
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoOpenInput (void)
|
|
|
|
|
/* Open the input file */
|
2000-09-24 19:06:59 +00:00
|
|
|
|
{
|
2003-08-09 08:28:44 +00:00
|
|
|
|
/* Open the file */
|
|
|
|
|
InputFile = fopen (InfoFile, "r");
|
|
|
|
|
if (InputFile == 0) {
|
|
|
|
|
Error ("Cannot open `%s': %s", InfoFile, strerror (errno));
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Initialize variables */
|
|
|
|
|
C = ' ';
|
|
|
|
|
InputLine = 1;
|
|
|
|
|
InputCol = 0;
|
|
|
|
|
|
|
|
|
|
/* Start the ball rolling ... */
|
2003-08-09 08:28:44 +00:00
|
|
|
|
InfoNextTok ();
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-09 08:28:44 +00:00
|
|
|
|
void InfoCloseInput (void)
|
2000-09-24 19:06:59 +00:00
|
|
|
|
/* Close the input file if we have one */
|
|
|
|
|
{
|
|
|
|
|
/* Close the input file if we had one */
|
|
|
|
|
if (InputFile) {
|
|
|
|
|
(void) fclose (InputFile);
|
2013-05-09 11:56:54 +00:00
|
|
|
|
InputFile = 0;
|
2000-09-24 19:06:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|