]> git.sur5r.net Git - cc65/blob - src/ld65/scanner.c
This commit was generated by cvs2svn to compensate for changes in r2,
[cc65] / src / ld65 / scanner.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 scanner.c                                 */
4 /*                                                                           */
5 /*              Configuration file scanner for the ld65 linker               */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2000 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@musoftware.de                                            */
13 /*                                                                           */
14 /*                                                                           */
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.                                    */
18 /*                                                                           */
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:                            */
22 /*                                                                           */
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              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <ctype.h>
41
42 #include "global.h"
43 #include "error.h"
44 #include "scanner.h"
45
46
47
48 /*****************************************************************************/
49 /*                                   Data                                    */
50 /*****************************************************************************/
51
52
53
54 /* Current token and attributes */
55 unsigned        CfgTok;
56 char            CfgSVal [CFG_MAX_IDENT_LEN+1];
57 unsigned long   CfgIVal;
58
59 /* Error location */
60 unsigned                CfgErrorLine;
61 unsigned                CfgErrorCol;
62
63 /* Input sources for the configuration */
64 static const char*      CfgName         = 0;
65 static const char*      CfgBuf          = 0;
66
67 /* Other input stuff */
68 static int              C               = ' ';
69 static unsigned         InputLine       = 1;
70 static unsigned         InputCol        = 0;
71 static FILE*            InputFile       = 0;
72
73
74
75 /*****************************************************************************/
76 /*                              Error handling                               */
77 /*****************************************************************************/
78
79
80
81 void CfgWarning (const char* Format, ...)
82 /* Print a warning message adding file name and line number of the config file */
83 {
84     char Buf [512];
85     va_list ap;
86     va_start (ap, Format);
87 #ifdef __WATCOMC__
88     _vbprintf (Buf, sizeof (Buf), Format, ap);
89 #else
90     vsnprintf (Buf, sizeof (Buf), Format, ap);
91 #endif
92     Warning ("%s(%u): %s", CfgName, CfgErrorLine, Buf);
93 }
94
95
96
97 void CfgError (const char* Format, ...)
98 /* Print an error message adding file name and line number of the config file */
99 {
100     char Buf [512];
101     va_list ap;
102     va_start (ap, Format);
103 #ifdef __WATCOMC__
104     _vbprintf (Buf, sizeof (Buf), Format, ap);
105 #else
106     vsnprintf (Buf, sizeof (Buf), Format, ap);
107 #endif
108     Error ("%s(%u): %s", CfgName, CfgErrorLine, Buf);
109 }
110
111
112
113 /*****************************************************************************/
114 /*                                   Code                                    */
115 /*****************************************************************************/
116
117
118
119 static void NextChar (void)
120 /* Read the next character from the input file */
121 {
122     if (CfgBuf) {
123         /* Read from buffer */
124         C = (unsigned char)(*CfgBuf);
125         if (C == 0) {
126             C = EOF;
127         } else {
128             ++CfgBuf;
129         }
130     } else {
131         /* Read from the file */
132         C = getc (InputFile);
133     }
134
135     /* Count columns */
136     if (C != EOF) {
137         ++InputCol;
138     }
139
140     /* Count lines */
141     if (C == '\n') {
142         ++InputLine;
143         InputCol = 0;
144     }
145 }
146
147
148
149 static unsigned DigitVal (int C)
150 /* Return the value for a numeric digit */
151 {
152     if (isdigit (C)) {
153         return C - '0';
154     } else {
155         return toupper (C) - 'A' + 10;
156     }
157 }
158
159
160
161 void CfgNextTok (void)
162 /* Read the next token from the input stream */
163 {
164     unsigned I;
165
166
167 Again:
168     /* Skip whitespace */
169     while (isspace (C)) {
170         NextChar ();
171     }
172
173     /* Remember the current position */
174     CfgErrorLine = InputLine;
175     CfgErrorCol  = InputCol;
176
177     /* Identifier? */
178     if (C == '_' || isalpha (C)) {
179
180         /* Read the identifier */
181         I = 0;
182         while (C == '_' || isalnum (C)) {
183             if (I < CFG_MAX_IDENT_LEN) {
184                 CfgSVal [I++] = C;
185             }
186             NextChar ();
187         }
188         CfgSVal [I] = '\0';
189         CfgTok = CFGTOK_IDENT;
190         return;
191     }
192
193     /* Hex number? */
194     if (C == '$') {
195         NextChar ();
196         if (!isxdigit (C)) {
197             Error ("%s(%u): Hex digit expected", CfgName, InputLine);
198         }
199         CfgIVal = 0;
200         while (isxdigit (C)) {
201             CfgIVal = CfgIVal * 16 + DigitVal (C);
202             NextChar ();
203         }
204         CfgTok = CFGTOK_INTCON;
205         return;
206     }
207
208     /* Decimal number? */
209     if (isdigit (C)) {
210         CfgIVal = 0;
211         while (isdigit (C)) {
212             CfgIVal = CfgIVal * 10 + DigitVal (C);
213             NextChar ();
214         }
215         CfgTok = CFGTOK_INTCON;
216         return;
217     }
218
219     /* Other characters */
220     switch (C) {
221
222         case '{':
223             NextChar ();
224             CfgTok = CFGTOK_LCURLY;
225             break;
226
227         case '}':
228             NextChar ();
229             CfgTok = CFGTOK_RCURLY;
230             break;
231
232         case ';':
233             NextChar ();
234             CfgTok = CFGTOK_SEMI;
235             break;
236
237         case '.':
238             NextChar ();
239             CfgTok = CFGTOK_DOT;
240             break;
241
242         case ',':
243             NextChar ();
244             CfgTok = CFGTOK_COMMA;
245             break;
246
247         case '=':
248             NextChar ();
249             CfgTok = CFGTOK_EQ;
250             break;
251
252         case ':':
253             NextChar ();
254             CfgTok = CFGTOK_COLON;
255             break;
256
257         case '\"':
258             NextChar ();
259             I = 0;
260             while (C != '\"') {
261                 if (C == EOF || C == '\n') {
262                     Error ("%s(%u): Unterminated string", CfgName, InputLine);
263                 }
264                 if (I < CFG_MAX_IDENT_LEN) {
265                     CfgSVal [I++] = C;
266                 }
267                 NextChar ();
268             }
269             NextChar ();
270             CfgSVal [I] = '\0';
271             CfgTok = CFGTOK_STRCON;
272             break;
273
274         case '#':
275             /* Comment */
276             while (C != '\n' && C != EOF) {
277                 NextChar ();
278             }
279             if (C != EOF) {
280                 goto Again;
281             }
282             CfgTok = CFGTOK_EOF;
283             break;
284
285         case '%':
286             NextChar ();
287             switch (C) {
288
289                 case 'O':
290                     NextChar ();
291                     if (OutputName) {
292                         strncpy (CfgSVal, OutputName, CFG_MAX_IDENT_LEN);
293                         CfgSVal [CFG_MAX_IDENT_LEN] = '\0';
294                     } else {
295                         CfgSVal [0] = '\0';
296                     }
297                     CfgTok = CFGTOK_STRCON;
298                     break;
299
300                 case 'S':
301                     NextChar ();
302                     CfgIVal = StartAddr;
303                     CfgTok = CFGTOK_INTCON;
304                     break;
305
306                 default:
307                     CfgError ("Invalid format specification");
308             }
309             break;
310
311         case EOF:
312             CfgTok = CFGTOK_EOF;
313             break;
314
315         default:
316             Error ("%s(%u): Invalid character `%c'", CfgName, InputLine, C);
317
318     }
319 }
320
321
322
323 void CfgConsume (unsigned T, const char* Msg)
324 /* Skip a token, print an error message if not found */
325 {
326     if (CfgTok != T) {
327         CfgError (Msg);
328     }
329     CfgNextTok ();
330 }
331
332
333
334 void CfgConsumeSemi (void)
335 /* Consume a semicolon */
336 {
337     CfgConsume (CFGTOK_SEMI, "`;' expected");
338 }
339
340
341
342 void CfgConsumeColon (void)
343 /* Consume a colon */
344 {
345     CfgConsume (CFGTOK_COLON, "`:' expected");
346 }
347
348
349
350 void CfgOptionalComma (void)
351 /* Consume a comma if there is one */
352 {
353     if (CfgTok == CFGTOK_COMMA) {
354         CfgNextTok ();
355     }
356 }
357
358
359
360 void CfgOptionalAssign (void)
361 /* Consume an equal sign if there is one */
362 {
363     if (CfgTok == CFGTOK_EQ) {
364         CfgNextTok ();
365     }
366 }
367
368
369
370 void CfgAssureInt (void)
371 /* Make sure the next token is an integer */
372 {
373     if (CfgTok != CFGTOK_INTCON) {
374         CfgError ("Integer constant expected");
375     }
376 }
377
378
379
380 void CfgAssureStr (void)
381 /* Make sure the next token is a string constant */
382 {
383     if (CfgTok != CFGTOK_STRCON) {
384         CfgError ("String constant expected");
385     }
386 }
387
388
389
390 void CfgAssureIdent (void)
391 /* Make sure the next token is an identifier */
392 {
393     if (CfgTok != CFGTOK_IDENT) {
394         CfgError ("Identifier expected");
395     }
396 }
397
398
399
400 void CfgRangeCheck (unsigned long Lo, unsigned long Hi)
401 /* Check the range of CfgIVal */
402 {
403     if (CfgIVal < Lo || CfgIVal > Hi) {
404         CfgError ("Range error");
405     }
406 }
407
408
409
410 void CfgSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
411 /* Map an identifier to one of the special tokens in the table */
412 {
413     unsigned I;
414
415     /* We need an identifier */
416     if (CfgTok == CFGTOK_IDENT) {
417
418         /* Make it upper case */
419         I = 0;
420         while (CfgSVal [I]) {
421             CfgSVal [I] = toupper (CfgSVal [I]);
422             ++I;
423         }
424
425         /* Linear search */
426         for (I = 0; I < Size; ++I) {
427             if (strcmp (CfgSVal, Table [I].Ident) == 0) {
428                 CfgTok = Table [I].Tok;
429                 return;
430             }
431         }
432
433     }
434
435     /* Not found or no identifier */
436     Error ("%s(%u): %s expected", CfgName, InputLine, Name);
437 }
438
439
440
441 void CfgBoolToken (void)
442 /* Map an identifier or integer to a boolean token */
443 {
444     static const IdentTok Booleans [] = {
445         {   "YES",      CFGTOK_TRUE     },
446         {   "NO",       CFGTOK_FALSE    },
447         {   "TRUE",     CFGTOK_TRUE     },
448         {   "FALSE",    CFGTOK_FALSE    },
449     };
450
451     /* If we have an identifier, map it to a boolean token */
452     if (CfgTok == CFGTOK_IDENT) {
453         CfgSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
454     } else {
455         /* We expected an integer here */
456         if (CfgTok != CFGTOK_INTCON) {
457             CfgError ("Boolean value expected");
458         }
459         CfgTok = (CfgIVal == 0)? CFGTOK_FALSE : CFGTOK_TRUE;
460     }
461 }
462
463
464
465 void CfgSetName (const char* Name)
466 /* Set a name for a config file */
467 {
468     CfgName = Name;
469 }
470
471
472
473 const char* CfgGetName (void)
474 /* Get the name of the config file */
475 {
476     return CfgName? CfgName : "";
477 }
478
479
480
481 void CfgSetBuf (const char* Buf)
482 /* Set a memory buffer for the config */
483 {
484     CfgBuf = Buf;
485 }
486
487
488
489 int CfgAvail (void)
490 /* Return true if we have a configuration available */
491 {
492     return CfgName != 0 || CfgBuf != 0;
493 }
494
495
496
497 void CfgOpenInput (void)
498 /* Open the input file if we have one */
499 {
500     /* If we have a config name given, open the file, otherwise we will read
501      * from a buffer.
502      */
503     if (!CfgBuf) {
504
505         /* Open the file */
506         InputFile = fopen (CfgName, "r");
507         if (InputFile == 0) {
508             Error ("Cannot open `%s': %s", CfgName, strerror (errno));
509         }
510
511     }
512
513     /* Initialize variables */
514     C         = ' ';
515     InputLine = 1;
516     InputCol  = 0;
517
518     /* Start the ball rolling ... */
519     CfgNextTok ();
520 }
521
522
523
524 void CfgCloseInput (void)
525 /* Close the input file if we have one */
526 {
527     /* Close the input file if we had one */
528     if (InputFile) {
529         (void) fclose (InputFile);
530         InputFile = 0;
531     }
532 }
533
534
535