1 /*****************************************************************************/
5 /* Configuration file scanner for the da65 disassembler */
9 /* (C) 2000-2005 Ullrich von Bassewitz */
10 /* Römerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
54 /*****************************************************************************/
56 /*****************************************************************************/
60 /* Current token and attributes */
62 char InfoSVal [CFG_MAX_IDENT_LEN+1];
66 unsigned InfoErrorLine;
67 unsigned InfoErrorCol;
69 /* Input sources for the configuration */
70 static const char* InfoFile = 0;
72 /* Other input stuff */
74 static unsigned InputLine = 1;
75 static unsigned InputCol = 0;
76 static FILE* InputFile = 0;
77 static char* InputSrcName = 0;
80 unsigned char InfoSyncLines = 0;
83 /*****************************************************************************/
85 /*****************************************************************************/
89 void InfoWarning (const char* Format, ...)
90 /* Print a warning message adding file name and line number of the config file */
95 va_start (ap, Format);
96 xvsprintf (Buf, sizeof (Buf), Format, ap);
99 fprintf (stderr, "%s(%u): Warning: %s\n",
100 InputSrcName, InfoErrorLine, Buf);
105 void InfoError (const char* Format, ...)
106 /* Print an error message adding file name and line number of the config file */
111 va_start (ap, Format);
112 xvsprintf (Buf, sizeof (Buf), Format, ap);
115 fprintf (stderr, "%s(%u): Error: %s\n",
116 InputSrcName, InfoErrorLine, Buf);
122 /*****************************************************************************/
124 /*****************************************************************************/
128 static void NextChar (void)
129 /* Read the next character from the input file */
131 /* Read from the file */
132 C = getc (InputFile);
148 static unsigned DigitVal (int C)
149 /* Return the value for a numeric digit */
154 return toupper (C) - 'A' + 10;
160 static void SkipBlanks (int SingleLine)
162 while (C != EOF && (!SingleLine || C != '\n') && IsSpace (C)) {
167 static long GetDecimalToken ()
171 while (C != EOF && IsDigit (C)) {
172 Value = Value * 10 + DigitVal (C);
178 static int GetEncodedChar (char *Buf, unsigned *IPtr, unsigned Size)
185 } else if (C != '\\') {
190 NextChar (); /* consume '\\' */
193 } else if (IsODigit (C)) {
196 Decoded = Decoded * 8 + DigitVal (C);
199 } while (Count > 0 && C != EOF && IsODigit (C));
200 } else if (C == 'x') {
201 NextChar (); /* consume 'x' */
203 while (Count > 0 && C != EOF && IsXDigit (C)) {
204 Decoded = Decoded * 16 + DigitVal (C);
210 case '"': case '\'': case '\\':
212 case 't': Decoded = '\t'; break;
213 case 'r': Decoded = '\r'; break;
214 case 'n': Decoded = '\n'; break;
220 if (*IPtr < Size - 1) {
221 Buf [(*IPtr)++] = Decoded;
227 static void LineMarkerOrComment ()
228 /* Handle a line beginning with '#'. Possible interpretations are:
229 * - #line <lineno> ["<filename>"] (C preprocessor input)
230 * - # <lineno> "<filename>" [<flag>]... (gcc preprocessor output)
234 unsigned long LineNo = 0;
235 int LineDirective = 0;
236 StrBuf SrcNameBuf = AUTO_STRBUF_INITIALIZER;
238 /* Skip the first "# " */
246 for (I = 0; I < sizeof MaybeLine - 1 && C != EOF && IsAlNum (C); ++I) {
251 if (strcmp (MaybeLine, "line") != 0) {
258 /* Get line number */
259 if (C == EOF || !IsDigit (C)) {
262 LineNo = GetDecimalToken ();
265 /* Get the source file name */
267 /* The source file name is missing */
268 if (LineDirective && C == '\n') {
269 /* got #line <lineno> */
278 while (C != EOF && C != '\n' && C != '\"') {
281 if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0) {
284 SB_AppendBuf (&SrcNameBuf, DecodeBuf, I);
291 /* Ignore until the end of line */
292 while (C != EOF && C != '\n') {
296 /* Accepted a line marker */
297 SB_Terminate (&SrcNameBuf);
298 xfree (InputSrcName);
299 InputSrcName = SB_GetBuf (&SrcNameBuf);
300 SB_Init (&SrcNameBuf);
301 InputLine = (unsigned)LineNo;
306 InfoWarning ("Bad line marker");
308 while (C != EOF && C != '\n') {
313 SB_Done (&SrcNameBuf);
316 void InfoNextTok (void)
317 /* Read the next token from the input stream */
323 /* Skip whitespace */
326 /* Remember the current position */
327 InfoErrorLine = InputLine;
328 InfoErrorCol = InputCol;
331 if (C == '_' || IsAlpha (C)) {
333 /* Read the identifier */
335 while (C == '_' || IsAlNum (C)) {
336 if (I < CFG_MAX_IDENT_LEN) {
342 InfoTok = INFOTOK_IDENT;
350 InfoError ("Hex digit expected");
353 while (IsXDigit (C)) {
354 InfoIVal = InfoIVal * 16 + DigitVal (C);
357 InfoTok = INFOTOK_INTCON;
361 /* Decimal number? */
363 InfoIVal = GetDecimalToken ();
364 InfoTok = INFOTOK_INTCON;
368 /* Other characters */
373 InfoTok = INFOTOK_LCURLY;
378 InfoTok = INFOTOK_RCURLY;
383 InfoTok = INFOTOK_SEMI;
388 InfoTok = INFOTOK_DOT;
393 InfoTok = INFOTOK_COMMA;
398 InfoTok = INFOTOK_EQ;
403 InfoTok = INFOTOK_COLON;
409 while (C != EOF && C != '\"') {
410 if (GetEncodedChar (InfoSVal, &I, sizeof InfoSVal) < 0) {
412 InfoError ("Unterminated string");
414 InfoError ("Invalid escape char: %c", C);
419 InfoError ("Unterminated string");
422 InfoTok = INFOTOK_STRCON;
427 if (C == EOF || IsControl (C) || C == '\'') {
428 InfoError ("Invalid character constant");
430 if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0 || I != 1) {
431 InfoError ("Invalid character constant");
433 InfoIVal = DecodeBuf [0];
435 InfoError ("Unterminated character constant");
438 InfoTok = INFOTOK_CHARCON;
442 /* # lineno "sourcefile" or # comment */
443 if (InfoSyncLines && InputCol == 1) {
444 LineMarkerOrComment ();
448 } while (C != EOF && C != '\n');
454 InfoTok = INFOTOK_EOF;
458 /* C++ style comment */
461 InfoError ("Invalid token `/'");
465 } while (C != '\n' && C != EOF);
469 InfoTok = INFOTOK_EOF;
473 InfoTok = INFOTOK_EOF;
477 InfoError ("Invalid character `%c'", C);
484 void InfoConsume (unsigned T, const char* Msg)
485 /* Skip a token, print an error message if not found */
495 void InfoConsumeLCurly (void)
496 /* Consume a left curly brace */
498 InfoConsume (INFOTOK_LCURLY, "`{' expected");
503 void InfoConsumeRCurly (void)
504 /* Consume a right curly brace */
506 InfoConsume (INFOTOK_RCURLY, "`}' expected");
511 void InfoConsumeSemi (void)
512 /* Consume a semicolon */
514 InfoConsume (INFOTOK_SEMI, "`;' expected");
519 void InfoConsumeColon (void)
520 /* Consume a colon */
522 InfoConsume (INFOTOK_COLON, "`:' expected");
527 void InfoOptionalComma (void)
528 /* Consume a comma if there is one */
530 if (InfoTok == INFOTOK_COMMA) {
537 void InfoOptionalAssign (void)
538 /* Consume an equal sign if there is one */
540 if (InfoTok == INFOTOK_EQ) {
547 void InfoAssureInt (void)
548 /* Make sure the next token is an integer */
550 if (InfoTok != INFOTOK_INTCON) {
551 InfoError ("Integer constant expected");
557 void InfoAssureStr (void)
558 /* Make sure the next token is a string constant */
560 if (InfoTok != INFOTOK_STRCON) {
561 InfoError ("String constant expected");
567 void InfoAssureChar (void)
568 /* Make sure the next token is a char constant */
570 if (InfoTok != INFOTOK_STRCON) {
571 InfoError ("Character constant expected");
577 void InfoAssureIdent (void)
578 /* Make sure the next token is an identifier */
580 if (InfoTok != INFOTOK_IDENT) {
581 InfoError ("Identifier expected");
587 void InfoRangeCheck (long Lo, long Hi)
588 /* Check the range of InfoIVal */
590 if (InfoIVal < Lo || InfoIVal > Hi) {
591 InfoError ("Range error");
597 void InfoSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
598 /* Map an identifier to one of the special tokens in the table */
602 /* We need an identifier */
603 if (InfoTok == INFOTOK_IDENT) {
605 /* Make it upper case */
607 while (InfoSVal [I]) {
608 InfoSVal [I] = toupper (InfoSVal [I]);
613 for (I = 0; I < Size; ++I) {
614 if (strcmp (InfoSVal, Table [I].Ident) == 0) {
615 InfoTok = Table [I].Tok;
622 /* Not found or no identifier */
623 InfoError ("%s expected", Name);
628 void InfoBoolToken (void)
629 /* Map an identifier or integer to a boolean token */
631 static const IdentTok Booleans [] = {
632 { "YES", INFOTOK_TRUE },
633 { "NO", INFOTOK_FALSE },
634 { "TRUE", INFOTOK_TRUE },
635 { "FALSE", INFOTOK_FALSE },
636 { "ON", INFOTOK_TRUE },
637 { "OFF", INFOTOK_FALSE },
640 /* If we have an identifier, map it to a boolean token */
641 if (InfoTok == INFOTOK_IDENT) {
642 InfoSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
644 /* We expected an integer here */
645 if (InfoTok != INFOTOK_INTCON) {
646 InfoError ("Boolean value expected");
648 InfoTok = (InfoIVal == 0)? INFOTOK_FALSE : INFOTOK_TRUE;
654 void InfoSetName (const char* Name)
655 /* Set a name for a config file */
659 InputSrcName = xstrdup(Name);
665 const char* InfoGetName (void)
666 /* Get the name of the config file */
668 return InfoFile? InfoFile : "";
675 /* Return true if we have an info file given */
677 return (InfoFile != 0);
682 void InfoOpenInput (void)
683 /* Open the input file */
686 InputFile = fopen (InfoFile, "r");
687 if (InputFile == 0) {
688 Error ("Cannot open `%s': %s", InfoFile, strerror (errno));
691 /* Initialize variables */
696 /* Start the ball rolling ... */
702 void InfoCloseInput (void)
703 /* Close the input file if we have one */
705 /* Close the input file if we had one */
707 (void) fclose (InputFile);