1 /*****************************************************************************/
5 /* Configuration file scanner for the ld65 linker */
9 /* (C) 1998-2009, Ullrich von Bassewitz */
10 /* Roemerstrasse 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 StrBuf CfgSVal = STATIC_STRBUF_INITIALIZER;
63 unsigned long CfgIVal;
66 unsigned CfgErrorLine;
69 /* Input sources for the configuration */
70 static const char* CfgName = 0;
71 static const char* CfgBuf = 0;
73 /* Other input stuff */
75 static unsigned InputLine = 1;
76 static unsigned InputCol = 0;
77 static FILE* InputFile = 0;
81 /*****************************************************************************/
83 /*****************************************************************************/
87 void CfgWarning (const char* Format, ...)
88 /* Print a warning message adding file name and line number of the config file */
90 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
93 va_start (ap, Format);
94 SB_VPrintf (&Buf, Format, ap);
97 Warning ("%s(%u): %s", CfgGetName(), CfgErrorLine, SB_GetConstBuf (&Buf));
103 void CfgError (const char* Format, ...)
104 /* Print an error message adding file name and line number of the config file */
106 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
109 va_start (ap, Format);
110 SB_VPrintf (&Buf, Format, ap);
113 Error ("%s(%u): %s", CfgGetName(), CfgErrorLine, SB_GetConstBuf (&Buf));
119 /*****************************************************************************/
121 /*****************************************************************************/
125 static void NextChar (void)
126 /* Read the next character from the input file */
129 /* Read from buffer */
130 C = (unsigned char)(*CfgBuf);
137 /* Read from the file */
138 C = getc (InputFile);
155 static unsigned DigitVal (int C)
156 /* Return the value for a numeric digit */
161 return toupper (C) - 'A' + 10;
167 static void StrVal (void)
168 /* Parse a string value and expand escape sequences */
170 /* Skip the starting double quotes */
173 /* Read input chars */
180 CfgError ("Unterminated string");
190 CfgError ("Unterminated '%%' escape sequence");
194 SB_AppendChar (&CfgSVal, '%');
199 /* Replace by output file */
201 SB_AppendStr (&CfgSVal, OutputName);
207 CfgWarning ("Unkown escape sequence `%%%c'", C);
208 SB_AppendChar (&CfgSVal, '%');
209 SB_AppendChar (&CfgSVal, C);
216 SB_AppendChar (&CfgSVal, C);
221 /* Skip the terminating double quotes */
224 /* Terminate the string */
225 SB_Terminate (&CfgSVal);
227 /* We've read a string value */
228 CfgTok = CFGTOK_STRCON;
233 void CfgNextTok (void)
234 /* Read the next token from the input stream */
237 /* Skip whitespace */
238 while (isspace (C)) {
242 /* Remember the current position */
243 CfgErrorLine = InputLine;
244 CfgErrorCol = InputCol;
247 if (C == '_' || IsAlpha (C)) {
249 /* Read the identifier */
251 while (C == '_' || IsAlNum (C)) {
252 SB_AppendChar (&CfgSVal, C);
255 SB_Terminate (&CfgSVal);
256 CfgTok = CFGTOK_IDENT;
264 CfgError ("Hex digit expected");
267 while (isxdigit (C)) {
268 CfgIVal = CfgIVal * 16 + DigitVal (C);
271 CfgTok = CFGTOK_INTCON;
275 /* Decimal number? */
278 while (isdigit (C)) {
279 CfgIVal = CfgIVal * 10 + DigitVal (C);
282 CfgTok = CFGTOK_INTCON;
286 /* Other characters */
291 CfgTok = CFGTOK_MINUS;
296 CfgTok = CFGTOK_PLUS;
311 CfgTok = CFGTOK_LPAR;
316 CfgTok = CFGTOK_RPAR;
321 CfgTok = CFGTOK_LCURLY;
326 CfgTok = CFGTOK_RCURLY;
331 CfgTok = CFGTOK_SEMI;
341 CfgTok = CFGTOK_COMMA;
351 CfgTok = CFGTOK_COLON;
360 while (C != '\n' && C != EOF) {
376 SB_CopyStr (&CfgSVal, OutputName);
380 SB_Terminate (&CfgSVal);
381 CfgTok = CFGTOK_STRCON;
387 CfgTok = CFGTOK_INTCON;
391 CfgError ("Invalid format specification");
400 CfgError ("Invalid character `%c'", C);
407 void CfgConsume (cfgtok_t T, const char* Msg)
408 /* Skip a token, print an error message if not found */
411 CfgError ("%s", Msg);
418 void CfgConsumeSemi (void)
419 /* Consume a semicolon */
421 CfgConsume (CFGTOK_SEMI, "`;' expected");
426 void CfgConsumeColon (void)
427 /* Consume a colon */
429 CfgConsume (CFGTOK_COLON, "`:' expected");
434 void CfgOptionalComma (void)
435 /* Consume a comma if there is one */
437 if (CfgTok == CFGTOK_COMMA) {
444 void CfgOptionalAssign (void)
445 /* Consume an equal sign if there is one */
447 if (CfgTok == CFGTOK_EQ) {
454 void CfgAssureInt (void)
455 /* Make sure the next token is an integer */
457 if (CfgTok != CFGTOK_INTCON) {
458 CfgError ("Integer constant expected");
464 void CfgAssureStr (void)
465 /* Make sure the next token is a string constant */
467 if (CfgTok != CFGTOK_STRCON) {
468 CfgError ("String constant expected");
474 void CfgAssureIdent (void)
475 /* Make sure the next token is an identifier */
477 if (CfgTok != CFGTOK_IDENT) {
478 CfgError ("Identifier expected");
484 void CfgRangeCheck (unsigned long Lo, unsigned long Hi)
485 /* Check the range of CfgIVal */
487 if (CfgIVal < Lo || CfgIVal > Hi) {
488 CfgError ("Range error");
494 void CfgSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
495 /* Map an identifier to one of the special tokens in the table */
499 /* We need an identifier */
500 if (CfgTok == CFGTOK_IDENT) {
502 /* Make it upper case */
503 SB_ToUpper (&CfgSVal);
506 for (I = 0; I < Size; ++I) {
507 if (SB_CompareStr (&CfgSVal, Table[I].Ident) == 0) {
508 CfgTok = Table[I].Tok;
515 /* Not found or no identifier */
516 CfgError ("%s expected", Name);
521 void CfgBoolToken (void)
522 /* Map an identifier or integer to a boolean token */
524 static const IdentTok Booleans [] = {
525 { "YES", CFGTOK_TRUE },
526 { "NO", CFGTOK_FALSE },
527 { "TRUE", CFGTOK_TRUE },
528 { "FALSE", CFGTOK_FALSE },
531 /* If we have an identifier, map it to a boolean token */
532 if (CfgTok == CFGTOK_IDENT) {
533 CfgSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
535 /* We expected an integer here */
536 if (CfgTok != CFGTOK_INTCON) {
537 CfgError ("Boolean value expected");
539 CfgTok = (CfgIVal == 0)? CFGTOK_FALSE : CFGTOK_TRUE;
545 void CfgSetName (const char* Name)
546 /* Set a name for a config file */
553 const char* CfgGetName (void)
554 /* Get the name of the config file */
559 return "[builtin config]";
567 void CfgSetBuf (const char* Buf)
568 /* Set a memory buffer for the config */
576 /* Return true if we have a configuration available */
578 return CfgName != 0 || CfgBuf != 0;
583 void CfgOpenInput (void)
584 /* Open the input file if we have one */
586 /* If we have a config name given, open the file, otherwise we will read
592 InputFile = fopen (CfgName, "r");
593 if (InputFile == 0) {
594 Error ("Cannot open `%s': %s", CfgName, strerror (errno));
599 /* Initialize variables */
604 /* Start the ball rolling ... */
610 void CfgCloseInput (void)
611 /* Close the input file if we have one */
613 /* Close the input file if we had one */
615 (void) fclose (InputFile);