]> git.sur5r.net Git - cc65/blob - src/dbginfo/dbginfo.c
Several fixes detected when using another C compiler.
[cc65] / src / dbginfo / dbginfo.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 dbginfo.h                                 */
4 /*                                                                           */
5 /*                         cc65 debug info handling                          */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2010,      Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 52                                           */
11 /*                D-70794 Filderstadt                                        */
12 /* EMail:         uz@cc65.org                                                */
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 <stdio.h>
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <limits.h>
42 #include <assert.h>
43 #include <errno.h>
44
45 #include "dbginfo.h"
46
47
48
49 /*****************************************************************************/
50 /*                                   Data                                    */
51 /*****************************************************************************/
52
53
54
55 /* Dynamic strings */
56 typedef struct StrBuf StrBuf;
57 struct StrBuf {
58     char*       Buf;                    /* Pointer to buffer */
59     unsigned    Len;                    /* Length of the string */
60     unsigned    Allocated;              /* Size of allocated memory */
61 };
62
63 /* Initializer for a string buffer */
64 #define STRBUF_INITIALIZER      { 0, 0, 0 }
65
66 /* An array of pointers that grows if needed */
67 typedef struct Collection Collection;
68 struct Collection {
69     unsigned            Count;          /* Number of items in the list */
70     unsigned            Size;           /* Size of allocated array */
71     void**              Items;          /* Array with dynamic size */
72 };
73
74 /* Initializer for static collections */
75 #define COLLECTION_INITIALIZER  { 0, 0, 0 }
76
77
78
79 /* Data structure containing information from the debug info file. A pointer
80  * to this structure is passed as handle to callers from the outside.
81  */
82 typedef struct DbgInfo DbgInfo;
83 struct DbgInfo {
84     Collection          FileInfos;      /* Collection with file infos */
85 };
86
87 /* Input tokens */
88 typedef enum {
89
90     TOK_INVALID,                        /* Invalid token */
91     TOK_EOF,                            /* End of file reached */
92
93     TOK_INTCON,                         /* Integer constant */
94     TOK_STRCON,                         /* String constant */
95
96     TOK_EQUAL,                          /* = */
97     TOK_COMMA,                          /* , */
98     TOK_MINUS,                          /* - */
99     TOK_PLUS,                           /* + */
100     TOK_EOL,                            /* \n */
101
102     TOK_ABSOLUTE,                       /* ABSOLUTE keyword */
103     TOK_ADDRSIZE,                       /* ADDRSIZE keyword */
104     TOK_EQUATE,                         /* EQUATE keyword */
105     TOK_FILE,                           /* FILE keyword */
106     TOK_LABEL,                          /* LABEL keyword */
107     TOK_LINE,                           /* LINE keyword */
108     TOK_LONG,                           /* LONG_keyword */
109     TOK_MAJOR,                          /* MAJOR keyword */
110     TOK_MINOR,                          /* MINOR keyword */
111     TOK_MTIME,                          /* MTIME keyword */
112     TOK_RANGE,                          /* RANGE keyword */
113     TOK_RO,                             /* RO keyword */
114     TOK_RW,                             /* RW keyword */
115     TOK_SEGMENT,                        /* SEGMENT keyword */
116     TOK_SIZE,                           /* SIZE keyword */
117     TOK_START,                          /* START keyword */
118     TOK_SYM,                            /* SYM keyword */
119     TOK_TYPE,                           /* TYPE keyword */
120     TOK_VALUE,                          /* VALUE keyword */
121     TOK_VERSION,                        /* VERSION keyword */
122     TOK_ZEROPAGE,                       /* ZEROPAGE keyword */
123
124     TOK_IDENT,                          /* To catch unknown keywords */
125 } Token;
126
127 /* Data used when parsing the debug info file */
128 typedef struct InputData InputData;
129 struct InputData {
130     const char*         FileName;       /* Name of input file */
131     cc65_line           Line;           /* Current line number */
132     unsigned            Col;            /* Current column number */
133     cc65_line           SLine;          /* Line number at start of token */
134     unsigned            SCol;           /* Column number at start of token */
135     unsigned            Errors;         /* Number of errors */
136     FILE*               F;              /* Input file */
137     int                 C;              /* Input character */
138     Token               Tok;            /* Token from input stream */
139     unsigned long       IVal;           /* Integer constant */
140     StrBuf              SVal;           /* String constant */
141     cc65_errorfunc      Error;          /* Function called in case of errors */
142     unsigned            MajorVersion;   /* Major version number */
143     unsigned            MinorVersion;   /* Minor version number */
144     Collection          LineInfos;      /* Line information */
145     DbgInfo*            Info;           /* Pointer to debug info */
146 };
147
148 /* Internally used file info struct */
149 typedef struct FileInfo FileInfo;
150 struct FileInfo {
151     unsigned long       Size;           /* Size of file */
152     unsigned long       MTime;          /* Modification time */
153     cc65_addr           Start;          /* Start address of line infos */
154     cc65_addr           End;            /* End address of line infos */
155     Collection          LineInfos;      /* Line infos for this file */
156     char                FileName[1];    /* Name of file with full path */
157 };
158
159 /* Internally used line info struct */
160 typedef struct LineInfo LineInfo;
161 struct LineInfo {
162     cc65_addr           Start;          /* Start of data range */
163     cc65_addr           End;            /* End of data range */
164     cc65_line           Line;           /* Line number */
165     FileInfo*           FileInfo;       /* Pointer to file info */
166     char                FileName[1];    /* Name of file */
167 };
168
169
170
171 /*****************************************************************************/
172 /*                                 Forwards                                  */
173 /*****************************************************************************/
174
175
176
177 static void NextToken (InputData* D);
178 /* Read the next token from the input stream */
179
180
181
182 /*****************************************************************************/
183 /*                             Memory allocation                             */
184 /*****************************************************************************/
185
186
187
188 static void* xmalloc (size_t Size)
189 /* Allocate memory, check for out of memory condition. Do some debugging */
190 {
191     void* P = 0;
192
193     /* Allow zero sized requests and return NULL in this case */
194     if (Size) {
195
196         /* Allocate memory */
197         P = malloc (Size);
198
199         /* Check for errors */
200         assert (P != 0);
201     }
202
203     /* Return a pointer to the block */
204     return P;
205 }
206
207
208
209 static void* xrealloc (void* P, size_t Size)
210 /* Reallocate a memory block, check for out of memory */
211 {
212     /* Reallocate the block */
213     void* N = realloc (P, Size);
214
215     /* Check for errors */
216     assert (N != 0 || Size == 0);
217
218     /* Return the pointer to the new block */
219     return N;
220 }
221
222
223
224 static void xfree (void* Block)
225 /* Free the block, do some debugging */
226 {
227     free (Block);
228 }
229
230
231
232 /*****************************************************************************/
233 /*                              Dynamic strings                              */
234 /*****************************************************************************/
235
236
237
238 static void SB_Done (StrBuf* B)
239 /* Free the data of a string buffer (but not the struct itself) */
240 {
241     if (B->Allocated) {
242         xfree (B->Buf);
243     }
244 }
245
246
247
248 static void SB_Realloc (StrBuf* B, unsigned NewSize)
249 /* Reallocate the string buffer space, make sure at least NewSize bytes are
250  * available.
251  */
252 {
253     /* Get the current size, use a minimum of 8 bytes */
254     unsigned NewAllocated = B->Allocated;
255     if (NewAllocated == 0) {
256         NewAllocated = 8;
257     }
258
259     /* Round up to the next power of two */
260     while (NewAllocated < NewSize) {
261         NewAllocated *= 2;
262     }
263
264     /* Reallocate the buffer. Beware: The allocated size may be zero while the
265      * length is not. This means that we have a buffer that wasn't allocated
266      * on the heap.
267      */
268     if (B->Allocated) {
269         /* Just reallocate the block */
270         B->Buf   = xrealloc (B->Buf, NewAllocated);
271     } else {
272         /* Allocate a new block and copy */
273         B->Buf   = memcpy (xmalloc (NewAllocated), B->Buf, B->Len);
274     }
275
276     /* Remember the new block size */
277     B->Allocated = NewAllocated;
278 }
279
280
281
282 static unsigned SB_GetLen (const StrBuf* B)
283 /* Return the length of the buffer contents */
284 {
285     return B->Len;
286 }
287
288
289
290 static const char* SB_GetConstBuf (const StrBuf* B)
291 /* Return a buffer pointer */
292 {
293     return B->Buf;
294 }
295
296
297
298 static void SB_Terminate (StrBuf* B)
299 /* Zero terminate the given string buffer. NOTE: The terminating zero is not
300  * accounted for in B->Len, if you want that, you have to use AppendChar!
301  */
302 {
303     unsigned NewLen = B->Len + 1;
304     if (NewLen > B->Allocated) {
305         SB_Realloc (B, NewLen);
306     }
307     B->Buf[B->Len] = '\0';
308 }
309
310
311
312 static void SB_Clear (StrBuf* B)
313 /* Clear the string buffer (make it empty) */
314 {
315     B->Len = 0;
316 }
317
318
319
320 static void SB_AppendChar (StrBuf* B, int C)
321 /* Append a character to a string buffer */
322 {
323     unsigned NewLen = B->Len + 1;
324     if (NewLen > B->Allocated) {
325         SB_Realloc (B, NewLen);
326     }
327     B->Buf[B->Len] = (char) C;
328     B->Len = NewLen;
329 }
330
331
332
333 /*****************************************************************************/
334 /*                                Collections                                */
335 /*****************************************************************************/
336
337
338
339 static Collection* InitCollection (Collection* C)
340 /* Initialize a collection and return it. */
341 {
342     /* Intialize the fields. */
343     C->Count = 0;
344     C->Size  = 0;
345     C->Items = 0;
346
347     /* Return the new struct */
348     return C;
349 }
350
351
352
353 static void DoneCollection (Collection* C)
354 /* Free the data for a collection. This will not free the data contained in
355  * the collection.
356  */
357 {
358     /* Free the pointer array */
359     xfree (C->Items);
360 }
361
362
363
364 static unsigned CollCount (const Collection* C)
365 /* Return the number of items in the collection */
366 {
367     return C->Count;
368 }
369
370
371
372 static void CollGrow (Collection* C, unsigned Size)
373 /* Grow the collection C so it is able to hold Size items without a resize
374  * being necessary. This can be called for performance reasons if the number
375  * of items to be placed in the collection is known in advance.
376  */
377 {
378     void** NewItems;
379
380     /* Ignore the call if the collection is already large enough */
381     if (Size <= C->Size) {
382         return;
383     }
384
385     /* Grow the collection */
386     C->Size = Size;
387     NewItems = xmalloc (C->Size * sizeof (void*));
388     memcpy (NewItems, C->Items, C->Count * sizeof (void*));
389     xfree (C->Items);
390     C->Items = NewItems;
391 }
392
393
394
395 static void CollInsert (Collection* C, void* Item, unsigned Index)
396 /* Insert the data at the given position in the collection */
397 {
398     /* Check for invalid indices */
399     assert (Index <= C->Count);
400
401     /* Grow the array if necessary */
402     if (C->Count >= C->Size) {
403         /* Must grow */
404         CollGrow (C, (C->Size == 0)? 8 : C->Size * 2);
405     }
406
407     /* Move the existing elements if needed */
408     if (C->Count != Index) {
409         memmove (C->Items+Index+1, C->Items+Index, (C->Count-Index) * sizeof (void*));
410     }
411     ++C->Count;
412
413     /* Store the new item */
414     C->Items[Index] = Item;
415 }
416
417
418
419 static void CollAppend (Collection* C, void* Item)
420 /* Append an item to the end of the collection */
421 {
422     /* Insert the item at the end of the current list */
423     CollInsert (C, Item, C->Count);
424 }
425
426
427
428 static void* CollAt (Collection* C, unsigned Index)
429 /* Return the item at the given index */
430 {
431     /* Check the index */
432     assert (Index < C->Count);
433
434     /* Return the element */
435     return C->Items[Index];
436 }
437
438
439
440 static void* CollFirst (Collection* C)
441 /* Return the first item in a collection */
442 {
443     /* We must have at least one entry */
444     assert (C->Count > 0);
445
446     /* Return the element */
447     return C->Items[0];
448 }
449
450
451
452 static void* CollLast (Collection* C)
453 /* Return the last item in a collection */
454 {
455     /* We must have at least one entry */
456     assert (C->Count > 0);
457
458     /* Return the element */
459     return C->Items[C->Count-1];
460 }
461
462
463
464 static void CollDelete (Collection* C, unsigned Index)
465 /* Remove the item with the given index from the collection. This will not
466  * free the item itself, just the pointer. All items with higher indices
467  * will get moved to a lower position.
468  */
469 {
470     /* Check the index */
471     assert (Index < C->Count);
472
473     /* Remove the item pointer */
474     --C->Count;
475     memmove (C->Items+Index, C->Items+Index+1, (C->Count-Index) * sizeof (void*));
476 }
477
478
479
480 static void CollReplace (Collection* C, void* Item, unsigned Index)
481 /* Replace the item at the given position. The old item will not be freed,
482  * just the pointer will get replaced.
483  */
484 {
485     /* Check the index */
486     assert (Index < C->Count);
487
488     /* Replace the item pointer */
489     C->Items[Index] = Item;
490 }
491
492
493
494 static void CollQuickSort (Collection* C, int Lo, int Hi,
495                            int (*Compare) (const void*, const void*))
496 /* Internal recursive sort function. */
497 {
498     /* Get a pointer to the items */
499     void** Items = C->Items;
500
501     /* Quicksort */
502     while (Hi > Lo) {
503         int I = Lo + 1;
504         int J = Hi;
505         while (I <= J) {
506             while (I <= J && Compare (Items[Lo], Items[I]) >= 0) {
507                 ++I;
508             }
509             while (I <= J && Compare (Items[Lo], Items[J]) < 0) {
510                 --J;
511             }
512             if (I <= J) {
513                 /* Swap I and J */
514                 void* Tmp = Items[I];
515                 Items[I]  = Items[J];
516                 Items[J]  = Tmp;
517                 ++I;
518                 --J;
519             }
520         }
521         if (J != Lo) {
522             /* Swap J and Lo */
523             void* Tmp = Items[J];
524             Items[J]  = Items[Lo];
525             Items[Lo] = Tmp;
526         }
527         if (J > (Hi + Lo) / 2) {
528             CollQuickSort (C, J + 1, Hi, Compare);
529             Hi = J - 1;
530         } else {
531             CollQuickSort (C, Lo, J - 1, Compare);
532             Lo = J + 1;
533         }
534     }
535 }
536
537
538
539 void CollSort (Collection* C, int (*Compare) (const void*, const void*))
540 /* Sort the collection using the given compare function. */
541 {
542     if (C->Count > 1) {
543         CollQuickSort (C, 0, C->Count-1, Compare);
544     }
545 }
546
547
548
549 /*****************************************************************************/
550 /*                                 Line info                                 */
551 /*****************************************************************************/
552
553
554
555 static LineInfo* NewLineInfo (const StrBuf* FileName)
556 /* Create a new LineInfo struct and return it */
557 {
558     /* Allocate memory */
559     LineInfo* L = xmalloc (sizeof (LineInfo) + SB_GetLen (FileName));
560
561     /* Initialize it */
562     L->Start    = 0;
563     L->End      = 0;
564     L->Line     = 0;
565     L->FileInfo = 0;
566     memcpy (L->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
567
568     /* Return it */
569     return L;
570 }
571
572
573
574 static void FreeLineInfo (LineInfo* L)
575 /* Free a LineInfo struct */
576 {
577     xfree (L);
578 }
579
580
581
582 static LineInfo* PreenLineInfo (LineInfo* L, FileInfo* F)
583 /* Replace the name by file information */
584 {
585     /* Shrink the LineInfo struct removing the FfileName field */
586     L = xrealloc (L, sizeof (*L) - 1);
587
588     /* Set the FileInfo pointer instead */
589     L->FileInfo = F;
590
591     /* Return the result */
592     return L;
593 }
594
595
596
597 static int CompareLineInfo (const void* L, const void* R)
598 /* Helper function to sort line infos in a collection */
599 {
600     /* Sort by start of range */
601     if (((const LineInfo*) L)->Start > ((const LineInfo*) R)->Start) {
602         return 1;
603     } else if (((const LineInfo*) L)->Start < ((const LineInfo*) R)->Start) {
604         return -1;
605     } else {
606         return 0;
607     }
608 }
609
610
611
612 /*****************************************************************************/
613 /*                                 File info                                 */
614 /*****************************************************************************/
615
616
617
618 static FileInfo* NewFileInfo (const StrBuf* FileName)
619 /* Create a new FileInfo struct and return it */
620 {
621     /* Allocate memory */
622     FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
623
624     /* Initialize it */
625     F->Size  = 0;
626     F->MTime = 0;
627     F->Start = ~(cc65_addr)0;
628     F->End   = 0;
629     InitCollection (&F->LineInfos);
630     memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
631
632     /* Return it */
633     return F;
634 }
635
636
637
638 static void FreeFileInfo (FileInfo* F)
639 /* Free a FileInfo struct */
640 {
641     unsigned I;
642
643     /* Walk through the collection with line infos and delete them */
644     for (I = 0; I < CollCount (&F->LineInfos); ++I) {
645         FreeLineInfo (CollAt (&F->LineInfos, I));
646     }
647     DoneCollection (&F->LineInfos);
648
649     /* Free the file info structure itself */
650     xfree (F);
651 }
652
653
654
655 static int CompareFileInfo (const void* L, const void* R)
656 /* Helper function to sort file infos in a collection */
657 {
658     /* Sort by file name */
659     return strcmp (((const FileInfo*) L)->FileName,
660                    ((const FileInfo*) R)->FileName);
661 }
662
663
664
665 /*****************************************************************************/
666 /*                                Debug info                                 */
667 /*****************************************************************************/
668
669
670
671 static DbgInfo* NewDbgInfo (void)
672 /* Create a new DbgInfo struct and return it */
673 {
674     /* Allocate memory */
675     DbgInfo* Info = xmalloc (sizeof (DbgInfo));
676
677     /* Initialize it */
678     InitCollection (&Info->FileInfos);
679
680     /* Return it */
681     return Info;
682 }
683
684
685
686 static void FreeDbgInfo (DbgInfo* Info)
687 /* Free a DbgInfo struct */
688 {
689     unsigned I;
690
691     /* Free file info */
692     for (I = 0; I < CollCount (&Info->FileInfos); ++I) {
693         FreeFileInfo (CollAt (&Info->FileInfos, I));
694     }
695     DoneCollection (&Info->FileInfos);
696
697     /* Free the structure itself */
698     xfree (Info);
699 }
700
701
702
703 /*****************************************************************************/
704 /*                             Helper functions                              */
705 /*****************************************************************************/
706
707
708
709 static void ParseError (InputData* D, cc65_error_severity Type, const char* Msg, ...)
710 /* Call the user supplied parse error function */
711 {
712     va_list             ap;
713     int                 MsgSize;
714     cc65_parseerror*    E;
715
716     /* Test-format the error message so we know how much space to allocate */
717     va_start (ap, Msg);
718     MsgSize = vsnprintf (0, 0, Msg, ap);
719     va_end (ap);
720
721     /* Allocate memory */
722     E = xmalloc (sizeof (*E) + MsgSize);
723
724     /* Write data to E */
725     E->type   = Type;
726     E->name   = D->FileName;
727     E->line   = D->SLine;
728     E->column = D->SCol;
729     va_start (ap, Msg);
730     vsnprintf (E->errormsg, MsgSize+1, Msg, ap);
731     va_end (ap);
732
733     /* Call the caller:-) */
734     D->Error (E);
735
736     /* Free the data structure */
737     xfree (E);
738
739     /* Count errors */
740     if (Type == CC65_ERROR) {
741         ++D->Errors;
742     }
743 }
744
745
746
747 static void SkipLine (InputData* D)
748 /* Error recovery routine. Skip tokens until EOL or EOF is reached */
749 {
750     while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
751         NextToken (D);
752     }
753 }
754
755
756
757 static void UnexpectedToken (InputData* D)
758 /* Call ParseError with a message about an unexpected input token */
759 {
760     ParseError (D, CC65_ERROR, "Unexpected input token %d", D->Tok);
761     SkipLine (D);
762 }
763
764
765
766 static void MissingAttribute (InputData* D, const char* AttrName)
767 /* Print an error about a missing attribute */
768 {
769     ParseError (D, CC65_ERROR, "Attribute \"%s\" is mising", AttrName);
770 }
771
772
773
774 /*****************************************************************************/
775 /*                            Scanner and parser                             */
776 /*****************************************************************************/
777
778
779
780 static int DigitVal (int C)
781 /* Return the value for a numeric digit. Return -1 if C is invalid */
782 {
783     if (isdigit (C)) {
784         return C - '0';
785     } else if (isxdigit (C)) {
786         return toupper (C) - 'A' + 10;
787     } else {
788         return -1;
789     }
790 }
791
792
793
794 static void NextChar (InputData* D)
795 /* Read the next character from the input. Count lines and columns */
796 {
797     /* Check if we've encountered EOF before */
798     if (D->C >= 0) {
799         D->C = fgetc (D->F);
800         if (D->C == '\n') {
801             ++D->Line;
802             D->Col = 0;
803         } else {
804             ++D->Col;
805         }
806     }
807 }
808
809
810
811 static void NextToken (InputData* D)
812 /* Read the next token from the input stream */
813 {
814     static const struct KeywordEntry  {
815         const char      Keyword[10];
816         Token           Tok;
817     } KeywordTable[] = {
818         { "absolute",   TOK_ABSOLUTE    },
819         { "addrsize",   TOK_ADDRSIZE    },
820         { "equate",     TOK_EQUATE      },
821         { "file",       TOK_FILE        },
822         { "label",      TOK_LABEL       },
823         { "line",       TOK_LINE        },
824         { "long",       TOK_LONG        },
825         { "major",      TOK_MAJOR       },
826         { "minor",      TOK_MINOR       },
827         { "mtime",      TOK_MTIME       },
828         { "range",      TOK_RANGE       },
829         { "ro",         TOK_RO          },
830         { "rw",         TOK_RW          },
831         { "segment",    TOK_SEGMENT     },
832         { "size",       TOK_SIZE        },
833         { "start",      TOK_START       },
834         { "sym",        TOK_SYM         },
835         { "type",       TOK_TYPE        },
836         { "value",      TOK_VALUE       },
837         { "version",    TOK_VERSION     },
838         { "zeropage",   TOK_ZEROPAGE    },
839     };
840
841
842     /* Skip whitespace */
843     while (D->C == ' ' || D->C == '\t') {
844         NextChar (D);
845     }
846
847     /* Remember the current position as start of the next token */
848     D->SLine = D->Line;
849     D->SCol  = D->Col;
850
851     /* Identifier? */
852     if (D->C == '_' || isalpha (D->C)) {
853
854         const struct KeywordEntry* Entry;
855
856         /* Read the identifier */
857         SB_Clear (&D->SVal);
858         while (D->C == '_' || isalnum (D->C)) {
859             SB_AppendChar (&D->SVal, D->C);
860             NextChar (D);
861         }
862         SB_Terminate (&D->SVal);
863
864         /* Search the identifier in the keyword table */
865         Entry = bsearch (SB_GetConstBuf (&D->SVal),
866                          KeywordTable,
867                          sizeof (KeywordTable) / sizeof (KeywordTable[0]),
868                          sizeof (KeywordTable[0]),
869                          (int (*)(const void*, const void*)) strcmp);
870         if (Entry == 0) {
871             D->Tok = TOK_IDENT;
872         } else {
873             D->Tok = Entry->Tok;
874         }
875         return;
876     }
877
878     /* Number? */
879     if (isdigit (D->C)) {
880         int Base = 10;
881         int Val;
882         if (D->C == '0') {
883             NextChar (D);
884             if (toupper (D->C) == 'X') {
885                 NextChar (D);
886                 Base = 16;
887             } else {
888                 Base = 8;
889             }
890         } else {
891             Base = 10;
892         }
893         D->IVal = 0;
894         while ((Val = DigitVal (D->C)) >= 0 && Val < Base) {
895             D->IVal = D->IVal * Base + Val;
896             NextChar (D);
897         }
898         D->Tok = TOK_INTCON;
899         return;
900     }
901
902     /* Other characters */
903     switch (D->C) {
904
905         case '-':
906             NextChar (D);
907             D->Tok = TOK_MINUS;
908             break;
909
910         case '+':
911             NextChar (D);
912             D->Tok = TOK_PLUS;
913             break;
914
915         case ',':
916             NextChar (D);
917             D->Tok = TOK_COMMA;
918             break;
919
920         case '=':
921             NextChar (D);
922             D->Tok = TOK_EQUAL;
923             break;
924
925         case '\"':
926             SB_Clear (&D->SVal);
927             NextChar (D);
928             while (1) {
929                 if (D->C == '\n' || D->C == EOF) {
930                     ParseError (D, CC65_ERROR, "Unterminated string constant");
931                     break;
932                 }
933                 if (D->C == '\"') {
934                     NextChar (D);
935                     break;
936                 }
937                 SB_AppendChar (&D->SVal, D->C);
938                 NextChar (D);
939             }
940             SB_Terminate (&D->SVal);
941             D->Tok = TOK_STRCON;
942             break;
943
944         case '\n':
945             NextChar (D);
946             D->Tok = TOK_EOL;
947             break;
948
949         case EOF:
950             D->Tok = TOK_EOF;
951             break;
952
953         default:
954             ParseError (D, CC65_ERROR, "Invalid input character `%c'", D->C);
955
956     }
957 }
958
959
960
961 static int TokenFollows (InputData* D, Token Tok, const char* Name)
962 /* Check for a comma */
963 {
964     if (D->Tok != Tok) {
965         ParseError (D, CC65_ERROR, "%s expected", Name);
966         SkipLine (D);
967         return 0;
968     } else {
969         return 1;
970     }
971 }
972
973
974
975 static int IntConstFollows (InputData* D)
976 /* Check for an integer constant */
977 {
978     return TokenFollows (D, TOK_INTCON, "Integer constant");
979 }
980
981
982
983 static int StringConstFollows (InputData* D)
984 /* Check for a string literal */
985 {
986     return TokenFollows (D, TOK_STRCON, "String literal");
987 }
988
989
990
991 static int Consume (InputData* D, Token Tok, const char* Name)
992 /* Check for a token and consume it. Return true if the token was comsumed,
993  * return false otherwise.
994  */
995 {
996     if (TokenFollows (D, Tok, Name)) {
997         NextToken (D);
998         return 1;
999     } else {
1000         return 0;
1001     }
1002 }
1003
1004
1005
1006 static int ConsumeComma (InputData* D)
1007 /* Consume a comma */
1008 {
1009     return Consume (D, TOK_COMMA, "','");
1010 }
1011
1012
1013
1014 static int ConsumeEqual (InputData* D)
1015 /* Consume an equal sign */
1016 {
1017     return Consume (D, TOK_EQUAL, "'='");
1018 }
1019
1020
1021
1022 static int ConsumeMinus (InputData* D)
1023 /* Consume a minus sign */
1024 {
1025     return Consume (D, TOK_MINUS, "'-'");
1026 }
1027
1028
1029
1030 static void ParseFile (InputData* D)
1031 /* Parse a FILE line */
1032 {
1033     FileInfo* F;
1034     enum { None = 0x00, Size = 0x01, MTime = 0x02 } InfoBits = None;
1035
1036     /* Skip the FILE token */
1037     NextToken (D);
1038
1039     /* Name follows */
1040     if (!StringConstFollows (D)) {
1041         return;
1042     }
1043
1044     /* Allocate a new file info */
1045     F = NewFileInfo (&D->SVal);
1046
1047     /* Skip the file name */
1048     NextToken (D);
1049
1050     /* More stuff follows */
1051     while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1052
1053         /* Comma follows before next attribute */
1054         if (!ConsumeComma (D)) {
1055             goto ErrorExit;
1056         }
1057
1058         switch (D->Tok) {
1059
1060             case TOK_SIZE:
1061                 NextToken (D);
1062                 if (!ConsumeEqual (D)) {
1063                     goto ErrorExit;
1064                 }
1065                 if (!IntConstFollows (D)) {
1066                     goto ErrorExit;
1067                 }
1068                 F->Size = D->IVal;
1069                 NextToken (D);
1070                 InfoBits |= Size;
1071                 break;
1072
1073             case TOK_MTIME:
1074                 NextToken (D);
1075                 if (!ConsumeEqual (D)) {
1076                     goto ErrorExit;
1077                 }
1078                 if (!IntConstFollows (D)) {
1079                     goto ErrorExit;
1080                 }
1081                 F->MTime = D->IVal;
1082                 NextToken (D);
1083                 InfoBits |= MTime;
1084                 break;
1085
1086             default:
1087                 UnexpectedToken (D);
1088                 SkipLine (D);
1089                 goto ErrorExit;
1090         }
1091
1092     }
1093
1094     /* Check for required information */
1095     if ((InfoBits & Size) == None) {
1096         MissingAttribute (D, "size");
1097         goto ErrorExit;
1098     }
1099     if ((InfoBits & MTime) == None) {
1100         MissingAttribute (D, "mtime");
1101         goto ErrorExit;
1102     }
1103
1104     /* Remember the file info */
1105     CollAppend (&D->Info->FileInfos, F);
1106
1107     /* Done */
1108     return;
1109
1110 ErrorExit:
1111     /* Entry point in case of errors */
1112     FreeFileInfo (F);
1113 }
1114
1115
1116
1117 static void ParseLine (InputData* D)
1118 /* Parse a LINE line */
1119 {
1120     LineInfo* L;
1121     enum { None = 0x00, Line = 0x01, Range = 0x02 } InfoBits = None;
1122
1123     /* Skip the LINE token */
1124     NextToken (D);
1125
1126     /* File name follows */
1127     if (!StringConstFollows (D)) {
1128         return;
1129     }
1130
1131     /* Allocate a new line info */
1132     L = NewLineInfo (&D->SVal);
1133
1134     /* Skip the file name */
1135     NextToken (D);
1136
1137     /* More stuff follows */
1138     while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1139
1140         /* Comma follows before next attribute */
1141         if (!ConsumeComma (D)) {
1142             goto ErrorExit;
1143         }
1144
1145         switch (D->Tok) {
1146
1147             case TOK_LINE:
1148                 NextToken (D);
1149                 if (!ConsumeEqual (D)) {
1150                     goto ErrorExit;
1151                 }
1152                 if (!IntConstFollows (D)) {
1153                     goto ErrorExit;
1154                 }
1155                 L->Line = D->IVal;
1156                 NextToken (D);
1157                 InfoBits |= Line;
1158                 break;
1159
1160             case TOK_RANGE:
1161                 NextToken (D);
1162                 if (!ConsumeEqual (D)) {
1163                     goto ErrorExit;
1164                 }
1165                 if (!IntConstFollows (D)) {
1166                     goto ErrorExit;
1167                 }
1168                 L->Start = (cc65_addr) D->IVal;
1169                 NextToken (D);
1170                 if (!ConsumeMinus (D)) {
1171                     goto ErrorExit;
1172                 }
1173                 if (!IntConstFollows (D)) {
1174                     goto ErrorExit;
1175                 }
1176                 L->End = (cc65_addr) D->IVal;
1177                 NextToken (D);
1178                 InfoBits |= Range;
1179                 break;
1180
1181             default:
1182                 UnexpectedToken (D);
1183                 SkipLine (D);
1184                 goto ErrorExit;
1185         }
1186
1187     }
1188
1189     /* Check for required information */
1190     if ((InfoBits & Line) == None) {
1191         MissingAttribute (D, "line");
1192         goto ErrorExit;
1193     }
1194     if ((InfoBits & Range) == None) {
1195         MissingAttribute (D, "range");
1196         goto ErrorExit;
1197     }
1198
1199     /* Remember the line info */
1200     CollAppend (&D->LineInfos, L);
1201
1202     /* Done */
1203     return;
1204
1205 ErrorExit:
1206     /* Entry point in case of errors */
1207     FreeLineInfo (L);
1208 }
1209
1210
1211
1212 static void ParseSegment (InputData* D)
1213 /* Parse a SEGMENT line */
1214 {
1215     /* Skip the SEGMENT token */
1216     NextToken (D);
1217
1218     /* ### */
1219     SkipLine (D);
1220 }
1221
1222
1223
1224 static void ParseSym (InputData* D)
1225 /* Parse a SYM line */
1226 {
1227     /* Skip the SYM token */
1228     NextToken (D);
1229
1230     /* ### */
1231     SkipLine (D);
1232 }
1233
1234
1235
1236 static void ParseVersion (InputData* D)
1237 /* Parse a VERSION line */
1238 {
1239     enum { None = 0x00, Major = 0x01, Minor = 0x02 } InfoBits = None;
1240
1241     /* Skip the VERSION token */
1242     NextToken (D);
1243
1244     /* More stuff follows */
1245     while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1246
1247         switch (D->Tok) {
1248
1249             case TOK_MAJOR:
1250                 NextToken (D);
1251                 if (!ConsumeEqual (D)) {
1252                     goto ErrorExit;
1253                 }
1254                 if (!IntConstFollows (D)) {
1255                     goto ErrorExit;
1256                 }
1257                 D->MajorVersion = D->IVal;
1258                 NextToken (D);
1259                 InfoBits |= Major;
1260                 break;
1261
1262             case TOK_MINOR:
1263                 NextToken (D);
1264                 if (!ConsumeEqual (D)) {
1265                     goto ErrorExit;
1266                 }
1267                 if (!IntConstFollows (D)) {
1268                     goto ErrorExit;
1269                 }
1270                 D->MinorVersion = D->IVal;
1271                 NextToken (D);
1272                 InfoBits |= Minor;
1273                 break;
1274
1275             default:
1276                 UnexpectedToken (D);
1277                 SkipLine (D);
1278                 goto ErrorExit;
1279         }
1280
1281         /* Comma follows before next attribute */
1282         if (D->Tok == TOK_COMMA) {
1283             NextToken (D);
1284         } else if (D->Tok == TOK_EOL || D->Tok == TOK_EOF) {
1285             break;
1286         } else {
1287             UnexpectedToken (D);
1288             goto ErrorExit;
1289         }
1290     }
1291
1292     /* Check for required information */
1293     if ((InfoBits & Major) == None) {
1294         MissingAttribute (D, "major");
1295         goto ErrorExit;
1296     }
1297     if ((InfoBits & Minor) == None) {
1298         MissingAttribute (D, "minor");
1299         goto ErrorExit;
1300     }
1301
1302 ErrorExit:
1303     /* Entry point in case of errors */
1304     return;
1305 }
1306
1307
1308
1309 /*****************************************************************************/
1310 /*                              Data processing                              */
1311 /*****************************************************************************/
1312
1313
1314
1315 static FileInfo* FindFileInfo (InputData* D, const char* FileName)
1316 /* Find the FileInfo for a given file name */
1317 {
1318     /* Get a pointer to the file info collection */
1319     Collection* FileInfos = &D->Info->FileInfos;
1320
1321     /* Do a binary search */
1322     int Lo = 0;
1323     int Hi = (int) CollCount (FileInfos) - 1;
1324     while (Lo <= Hi) {
1325
1326         /* Mid of range */
1327         int Cur = (Lo + Hi) / 2;
1328
1329         /* Get item */
1330         FileInfo* CurItem = CollAt (FileInfos, Cur);
1331
1332         /* Compare */
1333         int Res = strcmp (CurItem->FileName, FileName);
1334
1335         /* Found? */
1336         if (Res < 0) {
1337             Lo = Cur + 1;
1338         } else if (Res > 0) {
1339             Hi = Cur - 1;
1340         } else {
1341             /* Found! */
1342             return CurItem;
1343         }
1344     }
1345
1346     /* Not found */
1347     return 0;
1348 }
1349
1350
1351
1352 static void ProcessFileInfo (InputData* D)
1353 /* Postprocess file infos */
1354 {
1355     /* Get a pointer to the file info collection */
1356     Collection* FileInfos = &D->Info->FileInfos;
1357
1358     /* First, sort the file infos, so we can check for duplicates and do
1359      * binary search.
1360      */
1361     CollSort (FileInfos, CompareFileInfo);
1362
1363     /* Cannot work on an empty collection */
1364     if (CollCount (FileInfos) > 0) {
1365
1366         /* Walk through the file infos and check for duplicates. If we find
1367          * some, warn and remove them, so the file infos are unique after
1368          * that step.
1369          */
1370         FileInfo* F = CollAt (FileInfos, 0);
1371         unsigned I = 1;
1372         while (I < CollCount (FileInfos)) {
1373             FileInfo* Next = CollAt (FileInfos, I);
1374             if (strcmp (F->FileName, Next->FileName) == 0) {
1375                 /* Warn only if time stamp and/or size is different */
1376                 if (F->Size != Next->Size || F->MTime != Next->MTime) {
1377                     ParseError (D,
1378                                 CC65_WARNING,
1379                                 "Duplicate file entry for \"%s\"",
1380                                 F->FileName);
1381                 }
1382                 /* Remove the duplicate entry */
1383                 FreeFileInfo (Next);
1384                 CollDelete (FileInfos, I);
1385             } else {
1386                 /* This one is ok, check the next entry */
1387                 F = Next;
1388                 ++I;
1389             }
1390         }
1391     }
1392 }
1393
1394
1395
1396 static void ProcessLineInfo (InputData* D)
1397 /* Postprocess line infos */
1398 {
1399     /* Get pointers to the collections */
1400     Collection* LineInfos = &D->LineInfos;
1401     Collection* FileInfos = &D->Info->FileInfos;
1402
1403     /* Walk over the line infos and replace the name by a pointer to the
1404      * corresponding file info struct. The LineInfo structs will get shrinked
1405      * in this process. Add the line info to each file where it is defined.
1406      */
1407     unsigned I = 0;
1408     while (I < CollCount (LineInfos)) {
1409
1410         /* Get LineInfo struct */
1411         LineInfo* L = CollAt (LineInfos, I);
1412
1413         /* Find FileInfo that corresponds to name */
1414         FileInfo* F = FindFileInfo (D, L->FileName);
1415
1416         /* If we have no corresponding file info, print a warning and remove
1417          * the line info.
1418          */
1419         if (F == 0) {
1420             ParseError (D,
1421                         CC65_ERROR,
1422                         "No file info for file \"%s\"",
1423                         L->FileName);
1424             FreeLineInfo (L);
1425             CollDelete (LineInfos, I);
1426             continue;
1427         }
1428
1429         /* Shrink the line info struct effectively removing the file name
1430          * but set the pointer to the file info now.
1431          */
1432         L = PreenLineInfo (L, F);
1433         CollReplace (LineInfos, L, I);
1434
1435         /* Add this line info to the file where it is defined */
1436         CollAppend (&F->LineInfos, L);
1437
1438         /* Next one */
1439         ++I;
1440     }
1441
1442     /* Walk over all files and sort the line infos for each file by ascending
1443      * start address of the range, so we can do a binary search later.
1444      */
1445     for (I = 0; I < CollCount (FileInfos); ++I) {
1446
1447         /* Get a pointer to this file info */
1448         FileInfo* F = CollAt (FileInfos, I);
1449
1450         /* Sort the line infos for this file */
1451         CollSort (&F->LineInfos, CompareLineInfo);
1452
1453         /* If there are line info entries, place the first and last address
1454          * of into the FileInfo struct itself, so we can rule out a FileInfo
1455          * quickly when mapping an address to a line info.
1456          */
1457         if (CollCount (&F->LineInfos) > 0) {
1458             F->Start = ((const LineInfo*) CollFirst (&F->LineInfos))->Start;
1459             F->End   = ((const LineInfo*) CollLast (&F->LineInfos))->End;
1460         }
1461
1462     }
1463 }
1464
1465
1466
1467 static LineInfo* FindLineInfo (FileInfo* F, cc65_addr Addr)
1468 /* Find the LineInfo for a given address */
1469 {
1470     Collection* LineInfos;
1471     int         Hi;
1472     int         Lo;
1473
1474
1475     /* Each file info contains the first and last address for which line
1476      * info is available, so we can rule out non matching ones quickly.
1477      */
1478     if (Addr < F->Start || Addr > F->End) {
1479         return 0;
1480     }
1481
1482     /* Get a pointer to the line info collection for this file */
1483     LineInfos = &F->LineInfos;
1484
1485     /* Do a binary search */
1486     Lo = 0;
1487     Hi = (int) CollCount (LineInfos) - 1;
1488     while (Lo <= Hi) {
1489
1490         /* Mid of range */
1491         int Cur = (Lo + Hi) / 2;
1492
1493         /* Get item */
1494         LineInfo* CurItem = CollAt (LineInfos, Cur);
1495
1496         /* Found? */
1497         if (Addr < CurItem->Start) {
1498             Hi = Cur - 1;
1499         } else if (Addr > CurItem->End) {
1500             Lo = Cur + 1;
1501         } else {
1502             /* Found! */
1503             return CurItem;
1504         }
1505     }
1506
1507     /* Not found */
1508     return 0;
1509 }
1510
1511
1512
1513 /*****************************************************************************/
1514 /*                                   Code                                    */
1515 /*****************************************************************************/
1516
1517
1518
1519 cc65_dbginfo cc65_read_dbginfo (const char* FileName, cc65_errorfunc ErrFunc)
1520 /* Parse the debug info file with the given name. On success, the function
1521  * will return a pointer to an opaque cc65_dbginfo structure, that must be
1522  * passed to the other functions in this module to retrieve information.
1523  * errorfunc is called in case of warnings and errors. If the file cannot be
1524  * read successfully, NULL is returned.
1525  */
1526 {
1527     /* Data structure used to control scanning and parsing */
1528     InputData D = {
1529         0,                      /* Name of input file */
1530         1,                      /* Line number */
1531         0,                      /* Input file */
1532         0,                      /* Line at start of current token */
1533         0,                      /* Column at start of current token */
1534         0,                      /* Number of errors */
1535         0,                      /* Input file */
1536         ' ',                    /* Input character */
1537         TOK_INVALID,            /* Input token */
1538         0,                      /* Integer constant */
1539         STRBUF_INITIALIZER,     /* String constant */
1540         0,                      /* Function called in case of errors */
1541         0,                      /* Major version number */
1542         0,                      /* Minor version number */
1543         COLLECTION_INITIALIZER, /* Line information */
1544         0,                      /* Pointer to debug info */
1545     };
1546     D.FileName = FileName;
1547     D.Error    = ErrFunc;
1548
1549     /* Open the input file */
1550     D.F = fopen (D.FileName, "r");
1551     if (D.F == 0) {
1552         /* Cannot open */
1553         ParseError (&D, CC65_ERROR,
1554                     "Cannot open input file \"%s\": %s",
1555                      D.FileName, strerror (errno));
1556         return 0;
1557     }
1558
1559     /* Create a new debug info struct */
1560     D.Info = NewDbgInfo ();
1561
1562     /* Prime the pump */
1563     NextToken (&D);
1564
1565     /* Parse lines */
1566     while (D.Tok != TOK_EOF) {
1567
1568         switch (D.Tok) {
1569
1570             case TOK_FILE:
1571                 ParseFile (&D);
1572                 break;
1573
1574             case TOK_LINE:
1575                 ParseLine (&D);
1576                 break;
1577
1578             case TOK_SEGMENT:
1579                 ParseSegment (&D);
1580                 break;
1581
1582             case TOK_SYM:
1583                 ParseSym (&D);
1584                 break;
1585
1586             case TOK_VERSION:
1587                 ParseVersion (&D);
1588                 break;
1589
1590             default:
1591                 UnexpectedToken (&D);
1592
1593         }
1594
1595         /* EOL or EOF must follow */
1596         if (D.Tok != TOK_EOF) {
1597             if (D.Tok != TOK_EOL) {
1598                 ParseError (&D, 1, "Extra tokens in line");
1599                 SkipLine (&D);
1600             }
1601             NextToken (&D);
1602         }
1603     }
1604
1605     /* Close the file */
1606     fclose (D.F);
1607
1608     /* Free memory allocated for SVal */
1609     SB_Done (&D.SVal);
1610
1611     /* In case of errors, delete the debug info already allocated and
1612      * return NULL
1613      */
1614     if (D.Errors > 0) {
1615         /* Free allocated stuff */
1616         unsigned I;
1617         for (I = 0; I < CollCount (&D.LineInfos); ++I) {
1618             FreeLineInfo (CollAt (&D.LineInfos, I));
1619         }
1620         DoneCollection (&D.LineInfos);
1621         FreeDbgInfo (D.Info);
1622         return 0;
1623     }
1624
1625     /* We do now have all the information from the file. Do postprocessing. */
1626     ProcessFileInfo (&D);
1627     ProcessLineInfo (&D);
1628
1629     /* Free the collection that contained the line info */
1630     DoneCollection (&D.LineInfos);
1631
1632     /* Return the debug info struct that was created */
1633     return D.Info;
1634 }
1635
1636
1637
1638 void cc65_free_dbginfo (cc65_dbginfo Handle)
1639 /* Free debug information read from a file */
1640 {
1641     if (Handle) {
1642         FreeDbgInfo (Handle);
1643     }
1644 }
1645
1646
1647
1648 cc65_lineinfo* cc65_get_lineinfo (cc65_dbginfo Handle, unsigned long Addr)
1649 /* Return line information for the given address. The function returns 0
1650  * if no line information was found.
1651  */
1652 {
1653     unsigned        I;
1654     Collection*     FileInfos;
1655     cc65_lineinfo*  D = 0;
1656
1657     /* We will place a list of line infos in a collection */
1658     Collection LineInfos = COLLECTION_INITIALIZER;
1659
1660     /* Check the parameter */
1661     assert (Handle != 0);
1662
1663     /* Walk over all files and search for matching line infos */
1664     FileInfos = &((DbgInfo*) Handle)->FileInfos;
1665     for (I = 0; I < CollCount (FileInfos); ++I) {
1666         /* Check if the file contains line info for this address */
1667         LineInfo* L = FindLineInfo (CollAt (FileInfos, I), Addr);
1668         if (L != 0) {
1669             CollAppend (&LineInfos, L);
1670         }
1671     }
1672
1673     /* Do we have line infos? */
1674     if (CollCount (&LineInfos) > 0) {
1675
1676         /* Prepare the struct we will return to the caller */
1677         D = xmalloc (sizeof (*D) +
1678                      (CollCount (&LineInfos) - 1) * sizeof (D->data[0]));
1679         D->count = CollCount (&LineInfos);
1680         for (I = 0; I < D->count; ++I) {
1681
1682             /* Pointer to this info */
1683             LineInfo* L = CollAt (&LineInfos, I);
1684
1685             /* Copy data */
1686             D->data[I].name  = L->FileInfo->FileName;
1687             D->data[I].size  = L->FileInfo->Size;
1688             D->data[I].mtime = L->FileInfo->MTime;
1689             D->data[I].line  = L->Line;
1690             D->data[I].start = L->Start;
1691             D->data[I].end   = L->End;
1692         }
1693     }
1694
1695     /* Free the line info collection */
1696     DoneCollection (&LineInfos);
1697
1698     /* Return the struct we've created */
1699     return D;
1700 }
1701
1702
1703
1704 void cc65_free_lineinfo (cc65_dbginfo Handle, cc65_lineinfo* Info)
1705 /* Free line info returned by cc65_get_lineinfo() */
1706 {
1707     /* Just for completeness, check the handle */
1708     assert (Handle != 0);
1709
1710     /* Just free the memory */
1711     xfree (Info);
1712 }
1713
1714
1715