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;
81 /*****************************************************************************/
83 /*****************************************************************************/
87 void InfoWarning (const char* Format, ...)
88 /* Print a warning message adding file name and line number of the config file */
93 va_start (ap, Format);
94 xvsprintf (Buf, sizeof (Buf), Format, ap);
97 fprintf (stderr, "%s(%u): Warning: %s\n",
98 InputSrcName, InfoErrorLine, Buf);
103 void InfoError (const char* Format, ...)
104 /* Print an error message adding file name and line number of the config file */
109 va_start (ap, Format);
110 xvsprintf (Buf, sizeof (Buf), Format, ap);
113 fprintf (stderr, "%s(%u): Error: %s\n",
114 InputSrcName, InfoErrorLine, Buf);
121 /*****************************************************************************/
123 /*****************************************************************************/
127 static void NextChar (void)
128 /* Read the next character from the input file */
130 /* Read from the file */
131 C = getc (InputFile);
147 static unsigned DigitVal (int C)
148 /* Return the value for a numeric digit */
153 return toupper (C) - 'A' + 10;
159 static void SkipBlanks (int SingleLine)
161 while (C != EOF && (!SingleLine || C != '\n') && IsSpace (C)) {
166 static long GetDecimalToken (void)
170 while (C != EOF && IsDigit (C)) {
171 Value = Value * 10 + DigitVal (C);
177 static int GetEncodedChar (char *Buf, unsigned *IPtr, unsigned Size)
184 } else if (C != '\\') {
189 NextChar (); /* consume '\\' */
192 } else if (IsODigit (C)) {
195 Decoded = Decoded * 8 + DigitVal (C);
198 } while (Count > 0 && C != EOF && IsODigit (C));
199 } else if (C == 'x') {
200 NextChar (); /* consume 'x' */
202 while (Count > 0 && C != EOF && IsXDigit (C)) {
203 Decoded = Decoded * 16 + DigitVal (C);
209 case '"': case '\'': case '\\':
211 case 't': Decoded = '\t'; break;
212 case 'r': Decoded = '\r'; break;
213 case 'n': Decoded = '\n'; break;
219 if (*IPtr < Size - 1) {
220 Buf [(*IPtr)++] = Decoded;
226 static void LineMarkerOrComment ()
227 /* Handle a line beginning with '#'. Possible interpretations are:
228 ** - #line <lineno> ["<filename>"] (C preprocessor input)
229 ** - # <lineno> "<filename>" [<flag>]... (gcc preprocessor output)
233 unsigned long LineNo = 0;
234 int LineDirective = 0;
235 StrBuf SrcNameBuf = AUTO_STRBUF_INITIALIZER;
237 /* Skip the first "# " */
245 for (I = 0; I < sizeof MaybeLine - 1 && C != EOF && IsAlNum (C); ++I) {
250 if (strcmp (MaybeLine, "line") != 0) {
257 /* Get line number */
258 if (C == EOF || !IsDigit (C)) {
261 LineNo = GetDecimalToken ();
264 /* Get the source file name */
266 /* The source file name is missing */
267 if (LineDirective && C == '\n') {
268 /* got #line <lineno> */
277 while (C != EOF && C != '\n' && C != '\"') {
280 if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0) {
283 SB_AppendBuf (&SrcNameBuf, DecodeBuf, I);
290 /* Ignore until the end of line */
291 while (C != EOF && C != '\n') {
295 /* Accepted a line marker */
296 SB_Terminate (&SrcNameBuf);
297 xfree (InputSrcName);
298 InputSrcName = SB_GetBuf (&SrcNameBuf);
299 SB_Init (&SrcNameBuf);
300 InputLine = (unsigned)LineNo;
305 InfoWarning ("Bad line marker");
307 while (C != EOF && C != '\n') {
312 SB_Done (&SrcNameBuf);
315 void InfoNextTok (void)
316 /* Read the next token from the input stream */
322 /* Skip whitespace */
325 /* Remember the current position */
326 InfoErrorLine = InputLine;
327 InfoErrorCol = InputCol;
330 if (C == '_' || IsAlpha (C)) {
332 /* Read the identifier */
334 while (C == '_' || IsAlNum (C)) {
335 if (I < CFG_MAX_IDENT_LEN) {
341 InfoTok = INFOTOK_IDENT;
349 InfoError ("Hex digit expected");
352 while (IsXDigit (C)) {
353 InfoIVal = InfoIVal * 16 + DigitVal (C);
356 InfoTok = INFOTOK_INTCON;
360 /* Decimal number? */
362 InfoIVal = GetDecimalToken ();
363 InfoTok = INFOTOK_INTCON;
367 /* Other characters */
372 InfoTok = INFOTOK_LCURLY;
377 InfoTok = INFOTOK_RCURLY;
382 InfoTok = INFOTOK_SEMI;
387 InfoTok = INFOTOK_DOT;
392 InfoTok = INFOTOK_COMMA;
397 InfoTok = INFOTOK_EQ;
402 InfoTok = INFOTOK_COLON;
408 while (C != EOF && C != '\"') {
409 if (GetEncodedChar (InfoSVal, &I, sizeof InfoSVal) < 0) {
411 InfoError ("Unterminated string");
413 InfoError ("Invalid escape char: %c", C);
418 InfoError ("Unterminated string");
421 InfoTok = INFOTOK_STRCON;
426 if (C == EOF || IsControl (C) || C == '\'') {
427 InfoError ("Invalid character constant");
429 if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0 || I != 1) {
430 InfoError ("Invalid character constant");
432 InfoIVal = DecodeBuf [0];
434 InfoError ("Unterminated character constant");
437 InfoTok = INFOTOK_CHARCON;
441 /* # lineno "sourcefile" or # comment */
442 if (SyncLines && InputCol == 1) {
443 LineMarkerOrComment ();
447 } while (C != EOF && C != '\n');
453 InfoTok = INFOTOK_EOF;
457 /* C++ style comment */
460 InfoError ("Invalid token `/'");
464 } while (C != '\n' && C != EOF);
468 InfoTok = INFOTOK_EOF;
472 InfoTok = INFOTOK_EOF;
476 InfoError ("Invalid character `%c'", C);
483 void InfoConsume (unsigned T, const char* Msg)
484 /* Skip a token, print an error message if not found */
494 void InfoConsumeLCurly (void)
495 /* Consume a left curly brace */
497 InfoConsume (INFOTOK_LCURLY, "`{' expected");
502 void InfoConsumeRCurly (void)
503 /* Consume a right curly brace */
505 InfoConsume (INFOTOK_RCURLY, "`}' expected");
510 void InfoConsumeSemi (void)
511 /* Consume a semicolon */
513 InfoConsume (INFOTOK_SEMI, "`;' expected");
518 void InfoConsumeColon (void)
519 /* Consume a colon */
521 InfoConsume (INFOTOK_COLON, "`:' expected");
526 void InfoOptionalComma (void)
527 /* Consume a comma if there is one */
529 if (InfoTok == INFOTOK_COMMA) {
536 void InfoOptionalAssign (void)
537 /* Consume an equal sign if there is one */
539 if (InfoTok == INFOTOK_EQ) {
546 void InfoAssureInt (void)
547 /* Make sure the next token is an integer */
549 if (InfoTok != INFOTOK_INTCON) {
550 InfoError ("Integer constant expected");
556 void InfoAssureStr (void)
557 /* Make sure the next token is a string constant */
559 if (InfoTok != INFOTOK_STRCON) {
560 InfoError ("String constant expected");
566 void InfoAssureChar (void)
567 /* Make sure the next token is a char constant */
569 if (InfoTok != INFOTOK_STRCON) {
570 InfoError ("Character constant expected");
576 void InfoAssureIdent (void)
577 /* Make sure the next token is an identifier */
579 if (InfoTok != INFOTOK_IDENT) {
580 InfoError ("Identifier expected");
586 void InfoRangeCheck (long Lo, long Hi)
587 /* Check the range of InfoIVal */
589 if (InfoIVal < Lo || InfoIVal > Hi) {
590 InfoError ("Range error");
596 void InfoSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
597 /* Map an identifier to one of the special tokens in the table */
601 /* We need an identifier */
602 if (InfoTok == INFOTOK_IDENT) {
604 /* Make it upper case */
606 while (InfoSVal [I]) {
607 InfoSVal [I] = toupper (InfoSVal [I]);
612 for (I = 0; I < Size; ++I) {
613 if (strcmp (InfoSVal, Table [I].Ident) == 0) {
614 InfoTok = Table [I].Tok;
621 /* Not found or no identifier */
622 InfoError ("%s expected", Name);
627 void InfoBoolToken (void)
628 /* Map an identifier or integer to a boolean token */
630 static const IdentTok Booleans [] = {
631 { "YES", INFOTOK_TRUE },
632 { "NO", INFOTOK_FALSE },
633 { "TRUE", INFOTOK_TRUE },
634 { "FALSE", INFOTOK_FALSE },
635 { "ON", INFOTOK_TRUE },
636 { "OFF", INFOTOK_FALSE },
639 /* If we have an identifier, map it to a boolean token */
640 if (InfoTok == INFOTOK_IDENT) {
641 InfoSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
643 /* We expected an integer here */
644 if (InfoTok != INFOTOK_INTCON) {
645 InfoError ("Boolean value expected");
647 InfoTok = (InfoIVal == 0)? INFOTOK_FALSE : INFOTOK_TRUE;
653 void InfoSetName (const char* Name)
654 /* Set a name for a config file */
658 InputSrcName = xstrdup(Name);
664 const char* InfoGetName (void)
665 /* Get the name of the config file */
667 return InfoFile? InfoFile : "";
674 /* Return true if we have an info file given */
676 return (InfoFile != 0);
681 void InfoOpenInput (void)
682 /* Open the input file */
685 InputFile = fopen (InfoFile, "r");
686 if (InputFile == 0) {
687 Error ("Cannot open `%s': %s", InfoFile, strerror (errno));
690 /* Initialize variables */
695 /* Start the ball rolling ... */
701 void InfoCloseInput (void)
702 /* Close the input file if we have one */
704 /* Close the input file if we had one */
706 (void) fclose (InputFile);