]> git.sur5r.net Git - cc65/blob - src/dbginfo/dbginfo.c
Allow access to segment information.
[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  SegInfoByName;          /* Segment infos sorted by name */
85     Collection  SegInfoById;            /* Segment infos sorted by id */
86     Collection  FileInfoByName;         /* File infos sorted by name */
87     Collection  FileInfoById;           /* File infos sorted by id */
88 };
89
90 /* Input tokens */
91 typedef enum {
92
93     TOK_INVALID,                        /* Invalid token */
94     TOK_EOF,                            /* End of file reached */
95
96     TOK_INTCON,                         /* Integer constant */
97     TOK_STRCON,                         /* String constant */
98
99     TOK_EQUAL,                          /* = */
100     TOK_COMMA,                          /* , */
101     TOK_MINUS,                          /* - */
102     TOK_PLUS,                           /* + */
103     TOK_EOL,                            /* \n */
104
105     TOK_ABSOLUTE,                       /* ABSOLUTE keyword */
106     TOK_ADDRSIZE,                       /* ADDRSIZE keyword */
107     TOK_EQUATE,                         /* EQUATE keyword */
108     TOK_FILE,                           /* FILE keyword */
109     TOK_ID,                             /* ID keyword */
110     TOK_LABEL,                          /* LABEL keyword */
111     TOK_LINE,                           /* LINE keyword */
112     TOK_LONG,                           /* LONG_keyword */
113     TOK_MAJOR,                          /* MAJOR keyword */
114     TOK_MINOR,                          /* MINOR keyword */
115     TOK_MTIME,                          /* MTIME keyword */
116     TOK_NAME,                           /* NAME keyword */
117     TOK_RANGE,                          /* RANGE keyword */
118     TOK_RO,                             /* RO keyword */
119     TOK_RW,                             /* RW keyword */
120     TOK_SEGMENT,                        /* SEGMENT keyword */
121     TOK_SIZE,                           /* SIZE keyword */
122     TOK_START,                          /* START keyword */
123     TOK_SYM,                            /* SYM keyword */
124     TOK_TYPE,                           /* TYPE keyword */
125     TOK_VALUE,                          /* VALUE keyword */
126     TOK_VERSION,                        /* VERSION keyword */
127     TOK_ZEROPAGE,                       /* ZEROPAGE keyword */
128
129     TOK_IDENT,                          /* To catch unknown keywords */
130 } Token;
131
132 /* Data used when parsing the debug info file */
133 typedef struct InputData InputData;
134 struct InputData {
135     const char*         FileName;       /* Name of input file */
136     cc65_line           Line;           /* Current line number */
137     unsigned            Col;            /* Current column number */
138     cc65_line           SLine;          /* Line number at start of token */
139     unsigned            SCol;           /* Column number at start of token */
140     unsigned            Errors;         /* Number of errors */
141     FILE*               F;              /* Input file */
142     int                 C;              /* Input character */
143     Token               Tok;            /* Token from input stream */
144     unsigned long       IVal;           /* Integer constant */
145     StrBuf              SVal;           /* String constant */
146     cc65_errorfunc      Error;          /* Function called in case of errors */
147     unsigned            MajorVersion;   /* Major version number */
148     unsigned            MinorVersion;   /* Minor version number */
149     Collection          LineInfos;      /* Line information */
150     DbgInfo*            Info;           /* Pointer to debug info */
151 };
152
153 /* Internally used segment info struct */
154 typedef struct SegInfo SegInfo;
155 struct SegInfo {
156     unsigned            Id;             /* Id of segment */
157     cc65_addr           Start;          /* Start address of segment */
158     cc65_addr           Size;           /* Size of segment */
159     char                SegName[1];     /* Name of segment */
160 };
161
162 /* Internally used file info struct */
163 typedef struct FileInfo FileInfo;
164 struct FileInfo {
165     unsigned            Id;             /* Id of file */
166     unsigned long       Size;           /* Size of file */
167     unsigned long       MTime;          /* Modification time */
168     cc65_addr           Start;          /* Start address of line infos */
169     cc65_addr           End;            /* End address of line infos */
170     Collection          LineInfoByAddr; /* Line infos sorted by address */
171     Collection          LineInfoByLine; /* Line infos sorted by line */
172     char                FileName[1];    /* Name of file with full path */
173 };
174
175 /* Internally used line info struct */
176 typedef struct LineInfo LineInfo;
177 struct LineInfo {
178     cc65_addr           Start;          /* Start of data range */
179     cc65_addr           End;            /* End of data range */
180     cc65_line           Line;           /* Line number */
181     union {
182         unsigned        Id;             /* Id of file */
183         FileInfo*       Info;           /* Pointer to file info */
184     } File;
185     union {
186         unsigned        Id;             /* Id of segment */
187         SegInfo*        Info;           /* Pointer to segment info */
188     } Seg;
189 };
190
191
192
193 /*****************************************************************************/
194 /*                                 Forwards                                  */
195 /*****************************************************************************/
196
197
198
199 static void NextToken (InputData* D);
200 /* Read the next token from the input stream */
201
202
203
204 /*****************************************************************************/
205 /*                             Memory allocation                             */
206 /*****************************************************************************/
207
208
209
210 static void* xmalloc (size_t Size)
211 /* Allocate memory, check for out of memory condition. Do some debugging */
212 {
213     void* P = 0;
214
215     /* Allow zero sized requests and return NULL in this case */
216     if (Size) {
217
218         /* Allocate memory */
219         P = malloc (Size);
220
221         /* Check for errors */
222         assert (P != 0);
223     }
224
225     /* Return a pointer to the block */
226     return P;
227 }
228
229
230
231 static void* xrealloc (void* P, size_t Size)
232 /* Reallocate a memory block, check for out of memory */
233 {
234     /* Reallocate the block */
235     void* N = realloc (P, Size);
236
237     /* Check for errors */
238     assert (N != 0 || Size == 0);
239
240     /* Return the pointer to the new block */
241     return N;
242 }
243
244
245
246 static void xfree (void* Block)
247 /* Free the block, do some debugging */
248 {
249     free (Block);
250 }
251
252
253
254 /*****************************************************************************/
255 /*                              Dynamic strings                              */
256 /*****************************************************************************/
257
258
259
260 static void SB_Done (StrBuf* B)
261 /* Free the data of a string buffer (but not the struct itself) */
262 {
263     if (B->Allocated) {
264         xfree (B->Buf);
265     }
266 }
267
268
269
270 static void SB_Realloc (StrBuf* B, unsigned NewSize)
271 /* Reallocate the string buffer space, make sure at least NewSize bytes are
272  * available.
273  */
274 {
275     /* Get the current size, use a minimum of 8 bytes */
276     unsigned NewAllocated = B->Allocated;
277     if (NewAllocated == 0) {
278         NewAllocated = 8;
279     }
280
281     /* Round up to the next power of two */
282     while (NewAllocated < NewSize) {
283         NewAllocated *= 2;
284     }
285
286     /* Reallocate the buffer. Beware: The allocated size may be zero while the
287      * length is not. This means that we have a buffer that wasn't allocated
288      * on the heap.
289      */
290     if (B->Allocated) {
291         /* Just reallocate the block */
292         B->Buf   = xrealloc (B->Buf, NewAllocated);
293     } else {
294         /* Allocate a new block and copy */
295         B->Buf   = memcpy (xmalloc (NewAllocated), B->Buf, B->Len);
296     }
297
298     /* Remember the new block size */
299     B->Allocated = NewAllocated;
300 }
301
302
303
304 static void SB_CheapRealloc (StrBuf* B, unsigned NewSize)
305 /* Reallocate the string buffer space, make sure at least NewSize bytes are
306  * available. This function won't copy the old buffer contents over to the new
307  * buffer and may be used if the old contents are overwritten later.
308  */
309 {
310     /* Get the current size, use a minimum of 8 bytes */
311     unsigned NewAllocated = B->Allocated;
312     if (NewAllocated == 0) {
313         NewAllocated = 8;
314     }
315
316     /* Round up to the next power of two */
317     while (NewAllocated < NewSize) {
318         NewAllocated *= 2;
319     }
320
321     /* Free the old buffer if there is one */
322     if (B->Allocated) {
323         xfree (B->Buf);
324     }
325
326     /* Allocate a fresh block */
327     B->Buf = xmalloc (NewAllocated);
328
329     /* Remember the new block size */
330     B->Allocated = NewAllocated;
331 }
332
333
334
335 static unsigned SB_GetLen (const StrBuf* B)
336 /* Return the length of the buffer contents */
337 {
338     return B->Len;
339 }
340
341
342
343 static const char* SB_GetConstBuf (const StrBuf* B)
344 /* Return a buffer pointer */
345 {
346     return B->Buf;
347 }
348
349
350
351 static void SB_Terminate (StrBuf* B)
352 /* Zero terminate the given string buffer. NOTE: The terminating zero is not
353  * accounted for in B->Len, if you want that, you have to use AppendChar!
354  */
355 {
356     unsigned NewLen = B->Len + 1;
357     if (NewLen > B->Allocated) {
358         SB_Realloc (B, NewLen);
359     }
360     B->Buf[B->Len] = '\0';
361 }
362
363
364
365 static void SB_Clear (StrBuf* B)
366 /* Clear the string buffer (make it empty) */
367 {
368     B->Len = 0;
369 }
370
371
372
373 static void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size)
374 /* Copy Buf to Target, discarding the old contents of Target */
375 {
376     if (Size) {
377         if (Target->Allocated < Size) {
378             SB_CheapRealloc (Target, Size);
379         }
380         memcpy (Target->Buf, Buf, Size);
381     }
382     Target->Len = Size;
383 }
384
385
386
387 static void SB_Copy (StrBuf* Target, const StrBuf* Source)
388 /* Copy Source to Target, discarding the old contents of Target */
389 {
390     SB_CopyBuf (Target, Source->Buf, Source->Len);
391 }
392
393
394
395 static void SB_AppendChar (StrBuf* B, int C)
396 /* Append a character to a string buffer */
397 {
398     unsigned NewLen = B->Len + 1;
399     if (NewLen > B->Allocated) {
400         SB_Realloc (B, NewLen);
401     }
402     B->Buf[B->Len] = (char) C;
403     B->Len = NewLen;
404 }
405
406
407
408 /*****************************************************************************/
409 /*                                Collections                                */
410 /*****************************************************************************/
411
412
413
414 static Collection* InitCollection (Collection* C)
415 /* Initialize a collection and return it. */
416 {
417     /* Intialize the fields. */
418     C->Count = 0;
419     C->Size  = 0;
420     C->Items = 0;
421
422     /* Return the new struct */
423     return C;
424 }
425
426
427
428 static void DoneCollection (Collection* C)
429 /* Free the data for a collection. This will not free the data contained in
430  * the collection.
431  */
432 {
433     /* Free the pointer array */
434     xfree (C->Items);
435 }
436
437
438
439 static unsigned CollCount (const Collection* C)
440 /* Return the number of items in the collection */
441 {
442     return C->Count;
443 }
444
445
446
447 static void CollGrow (Collection* C, unsigned Size)
448 /* Grow the collection C so it is able to hold Size items without a resize
449  * being necessary. This can be called for performance reasons if the number
450  * of items to be placed in the collection is known in advance.
451  */
452 {
453     void** NewItems;
454
455     /* Ignore the call if the collection is already large enough */
456     if (Size <= C->Size) {
457         return;
458     }
459
460     /* Grow the collection */
461     C->Size = Size;
462     NewItems = xmalloc (C->Size * sizeof (void*));
463     memcpy (NewItems, C->Items, C->Count * sizeof (void*));
464     xfree (C->Items);
465     C->Items = NewItems;
466 }
467
468
469
470 static void CollInsert (Collection* C, void* Item, unsigned Index)
471 /* Insert the data at the given position in the collection */
472 {
473     /* Check for invalid indices */
474     assert (Index <= C->Count);
475
476     /* Grow the array if necessary */
477     if (C->Count >= C->Size) {
478         /* Must grow */
479         CollGrow (C, (C->Size == 0)? 8 : C->Size * 2);
480     }
481
482     /* Move the existing elements if needed */
483     if (C->Count != Index) {
484         memmove (C->Items+Index+1, C->Items+Index, (C->Count-Index) * sizeof (void*));
485     }
486     ++C->Count;
487
488     /* Store the new item */
489     C->Items[Index] = Item;
490 }
491
492
493
494 static void CollAppend (Collection* C, void* Item)
495 /* Append an item to the end of the collection */
496 {
497     /* Insert the item at the end of the current list */
498     CollInsert (C, Item, C->Count);
499 }
500
501
502
503 static void* CollAt (Collection* C, unsigned Index)
504 /* Return the item at the given index */
505 {
506     /* Check the index */
507     assert (Index < C->Count);
508
509     /* Return the element */
510     return C->Items[Index];
511 }
512
513
514
515 static void* CollFirst (Collection* C)
516 /* Return the first item in a collection */
517 {
518     /* We must have at least one entry */
519     assert (C->Count > 0);
520
521     /* Return the element */
522     return C->Items[0];
523 }
524
525
526
527 static void* CollLast (Collection* C)
528 /* Return the last item in a collection */
529 {
530     /* We must have at least one entry */
531     assert (C->Count > 0);
532
533     /* Return the element */
534     return C->Items[C->Count-1];
535 }
536
537
538
539 static void CollDelete (Collection* C, unsigned Index)
540 /* Remove the item with the given index from the collection. This will not
541  * free the item itself, just the pointer. All items with higher indices
542  * will get moved to a lower position.
543  */
544 {
545     /* Check the index */
546     assert (Index < C->Count);
547
548     /* Remove the item pointer */
549     --C->Count;
550     memmove (C->Items+Index, C->Items+Index+1, (C->Count-Index) * sizeof (void*));
551 }
552
553
554
555 static void CollQuickSort (Collection* C, int Lo, int Hi,
556                            int (*Compare) (const void*, const void*))
557 /* Internal recursive sort function. */
558 {
559     /* Get a pointer to the items */
560     void** Items = C->Items;
561
562     /* Quicksort */
563     while (Hi > Lo) {
564         int I = Lo + 1;
565         int J = Hi;
566         while (I <= J) {
567             while (I <= J && Compare (Items[Lo], Items[I]) >= 0) {
568                 ++I;
569             }
570             while (I <= J && Compare (Items[Lo], Items[J]) < 0) {
571                 --J;
572             }
573             if (I <= J) {
574                 /* Swap I and J */
575                 void* Tmp = Items[I];
576                 Items[I]  = Items[J];
577                 Items[J]  = Tmp;
578                 ++I;
579                 --J;
580             }
581         }
582         if (J != Lo) {
583             /* Swap J and Lo */
584             void* Tmp = Items[J];
585             Items[J]  = Items[Lo];
586             Items[Lo] = Tmp;
587         }
588         if (J > (Hi + Lo) / 2) {
589             CollQuickSort (C, J + 1, Hi, Compare);
590             Hi = J - 1;
591         } else {
592             CollQuickSort (C, Lo, J - 1, Compare);
593             Lo = J + 1;
594         }
595     }
596 }
597
598
599
600 void CollSort (Collection* C, int (*Compare) (const void*, const void*))
601 /* Sort the collection using the given compare function. */
602 {
603     if (C->Count > 1) {
604         CollQuickSort (C, 0, C->Count-1, Compare);
605     }
606 }
607
608
609
610 /*****************************************************************************/
611 /*                               Segment info                                */
612 /*****************************************************************************/
613
614
615
616 static SegInfo* NewSegInfo (const StrBuf* SegName, unsigned Id,
617                             cc65_addr Start, cc65_addr Size)
618 /* Create a new SegInfo struct and return it */
619 {
620     /* Allocate memory */
621     SegInfo* S = xmalloc (sizeof (SegInfo) + SB_GetLen (SegName));
622
623     /* Initialize it */
624     S->Id    = Id;
625     S->Start = Start;
626     S->Size  = Size;
627     memcpy (S->SegName, SB_GetConstBuf (SegName), SB_GetLen (SegName) + 1);
628
629     /* Return it */
630     return S;
631 }
632
633
634
635 static void FreeSegInfo (SegInfo* S)
636 /* Free a SegInfo struct */
637 {
638     xfree (S);
639 }
640
641
642
643 static int CompareSegInfoByName (const void* L, const void* R)
644 /* Helper function to sort segment infos in a collection by name */
645 {
646     /* Sort by file name */
647     return strcmp (((const SegInfo*) L)->SegName,
648                    ((const SegInfo*) R)->SegName);
649 }
650
651
652
653 static int CompareSegInfoById (const void* L, const void* R)
654 /* Helper function to sort segment infos in a collection by id */
655 {
656     if (((const SegInfo*) L)->Id > ((const SegInfo*) R)->Id) {
657         return 1;
658     } else if (((const SegInfo*) L)->Id < ((const SegInfo*) R)->Id) {
659         return -1;
660     } else {
661         return 0;
662     }
663 }
664
665
666
667 /*****************************************************************************/
668 /*                                 Line info                                 */
669 /*****************************************************************************/
670
671
672
673 static LineInfo* NewLineInfo (unsigned File, unsigned Seg, cc65_line Line,
674                               cc65_addr Start, cc65_addr End)
675 /* Create a new LineInfo struct and return it */
676 {
677     /* Allocate memory */
678     LineInfo* L = xmalloc (sizeof (LineInfo));
679
680     /* Initialize it */
681     L->Start    = Start;
682     L->End      = End;
683     L->Line     = Line;
684     L->File.Id  = File;
685     L->Seg.Id   = Seg;
686
687     /* Return it */
688     return L;
689 }
690
691
692
693 static void FreeLineInfo (LineInfo* L)
694 /* Free a LineInfo struct */
695 {
696     xfree (L);
697 }
698
699
700
701 static int CompareLineInfoByAddr (const void* L, const void* R)
702 /* Helper function to sort line infos in a collection by address */
703 {
704     /* Sort by start of range */
705     if (((const LineInfo*) L)->Start > ((const LineInfo*) R)->Start) {
706         return 1;
707     } else if (((const LineInfo*) L)->Start < ((const LineInfo*) R)->Start) {
708         return -1;
709     } else {
710         return 0;
711     }
712 }
713
714
715
716 static int CompareLineInfoByLine (const void* L, const void* R)
717 /* Helper function to sort line infos in a collection by line */
718 {
719     if (((const LineInfo*) L)->Line > ((const LineInfo*) R)->Line) {
720         return 1;
721     } else if (((const LineInfo*) L)->Line < ((const LineInfo*) R)->Line) {
722         return -1;
723     } else {
724         return 0;
725     }
726 }
727
728
729
730 /*****************************************************************************/
731 /*                                 File info                                 */
732 /*****************************************************************************/
733
734
735
736 static FileInfo* NewFileInfo (const StrBuf* FileName, unsigned Id,
737                               unsigned long Size, unsigned long MTime)
738 /* Create a new FileInfo struct and return it */
739 {
740     /* Allocate memory */
741     FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
742
743     /* Initialize it */
744     F->Id    = Id;
745     F->Size  = Size;
746     F->MTime = MTime;
747     F->Start = ~(cc65_addr)0;
748     F->End   = 0;
749     InitCollection (&F->LineInfoByAddr);
750     InitCollection (&F->LineInfoByLine);
751     memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
752
753     /* Return it */
754     return F;
755 }
756
757
758
759 static void FreeFileInfo (FileInfo* F)
760 /* Free a FileInfo struct */
761 {
762     unsigned I;
763
764     /* Walk through the collection with line infos and delete them */
765     for (I = 0; I < CollCount (&F->LineInfoByAddr); ++I) {
766         FreeLineInfo (CollAt (&F->LineInfoByAddr, I));
767     }
768     DoneCollection (&F->LineInfoByAddr);
769     DoneCollection (&F->LineInfoByLine);
770
771     /* Free the file info structure itself */
772     xfree (F);
773 }
774
775
776
777 static int CompareFileInfoByName (const void* L, const void* R)
778 /* Helper function to sort file infos in a collection by name */
779 {
780     /* Sort by file name */
781     return strcmp (((const FileInfo*) L)->FileName,
782                    ((const FileInfo*) R)->FileName);
783 }
784
785
786
787 static int CompareFileInfoById (const void* L, const void* R)
788 /* Helper function to sort file infos in a collection by id */
789 {
790     if (((const FileInfo*) L)->Id > ((const FileInfo*) R)->Id) {
791         return 1;
792     } else if (((const FileInfo*) L)->Id < ((const FileInfo*) R)->Id) {
793         return -1;
794     } else {
795         return 0;
796     }
797 }
798
799
800
801 /*****************************************************************************/
802 /*                                Debug info                                 */
803 /*****************************************************************************/
804
805
806
807 static DbgInfo* NewDbgInfo (void)
808 /* Create a new DbgInfo struct and return it */
809 {
810     /* Allocate memory */
811     DbgInfo* Info = xmalloc (sizeof (DbgInfo));
812
813     /* Initialize it */
814     InitCollection (&Info->SegInfoByName);
815     InitCollection (&Info->SegInfoById);
816     InitCollection (&Info->FileInfoByName);
817     InitCollection (&Info->FileInfoById);
818
819     /* Return it */
820     return Info;
821 }
822
823
824
825 static void FreeDbgInfo (DbgInfo* Info)
826 /* Free a DbgInfo struct */
827 {
828     unsigned I;
829
830     /* Free segment info */
831     for (I = 0; I < CollCount (&Info->SegInfoByName); ++I) {
832         FreeSegInfo (CollAt (&Info->SegInfoByName, I));
833     }
834     DoneCollection (&Info->SegInfoByName);
835     DoneCollection (&Info->SegInfoById);
836
837     /* Free file info */
838     for (I = 0; I < CollCount (&Info->FileInfoByName); ++I) {
839         FreeFileInfo (CollAt (&Info->FileInfoByName, I));
840     }
841     DoneCollection (&Info->FileInfoByName);
842     DoneCollection (&Info->FileInfoById);
843
844     /* Free the structure itself */
845     xfree (Info);
846 }
847
848
849
850 /*****************************************************************************/
851 /*                             Helper functions                              */
852 /*****************************************************************************/
853
854
855
856 static void ParseError (InputData* D, cc65_error_severity Type, const char* Msg, ...)
857 /* Call the user supplied parse error function */
858 {
859     va_list             ap;
860     int                 MsgSize;
861     cc65_parseerror*    E;
862
863     /* Test-format the error message so we know how much space to allocate */
864     va_start (ap, Msg);
865     MsgSize = vsnprintf (0, 0, Msg, ap);
866     va_end (ap);
867
868     /* Allocate memory */
869     E = xmalloc (sizeof (*E) + MsgSize);
870
871     /* Write data to E */
872     E->type   = Type;
873     E->name   = D->FileName;
874     E->line   = D->SLine;
875     E->column = D->SCol;
876     va_start (ap, Msg);
877     vsnprintf (E->errormsg, MsgSize+1, Msg, ap);
878     va_end (ap);
879
880     /* Call the caller:-) */
881     D->Error (E);
882
883     /* Free the data structure */
884     xfree (E);
885
886     /* Count errors */
887     if (Type == CC65_ERROR) {
888         ++D->Errors;
889     }
890 }
891
892
893
894 static void SkipLine (InputData* D)
895 /* Error recovery routine. Skip tokens until EOL or EOF is reached */
896 {
897     while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
898         NextToken (D);
899     }
900 }
901
902
903
904 static void UnexpectedToken (InputData* D)
905 /* Call ParseError with a message about an unexpected input token */
906 {
907     ParseError (D, CC65_ERROR, "Unexpected input token %d", D->Tok);
908     SkipLine (D);
909 }
910
911
912
913 static void MissingAttribute (InputData* D, const char* AttrName)
914 /* Print an error about a missing attribute */
915 {
916     ParseError (D, CC65_ERROR, "Attribute \"%s\" is mising", AttrName);
917 }
918
919
920
921 static void UnknownKeyword (InputData* D)
922 /* Print a warning about an unknown keyword in the file. Try to do smart
923  * recovery, so if later versions of the debug information add additional
924  * keywords, this code may be able to at least ignore them.
925  */
926 {
927     /* Output a warning */
928     ParseError (D, CC65_WARNING, "Unknown keyword \"%s\" - skipping",
929                 SB_GetConstBuf (&D->SVal));
930
931     /* Skip the identifier */
932     NextToken (D);
933
934     /* If an equal sign follows, ignore anything up to the next line end
935      * or comma. If a comma or line end follows, we're already done. If
936      * we have none of both, we ignore the remainder of the line.
937      */
938     if (D->Tok == TOK_EQUAL) {
939         NextToken (D);
940         while (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
941             NextToken (D);
942         }
943     } else if (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
944         SkipLine (D);
945     }
946 }
947
948
949
950 /*****************************************************************************/
951 /*                            Scanner and parser                             */
952 /*****************************************************************************/
953
954
955
956 static int DigitVal (int C)
957 /* Return the value for a numeric digit. Return -1 if C is invalid */
958 {
959     if (isdigit (C)) {
960         return C - '0';
961     } else if (isxdigit (C)) {
962         return toupper (C) - 'A' + 10;
963     } else {
964         return -1;
965     }
966 }
967
968
969
970 static void NextChar (InputData* D)
971 /* Read the next character from the input. Count lines and columns */
972 {
973     /* Check if we've encountered EOF before */
974     if (D->C >= 0) {
975         D->C = fgetc (D->F);
976         if (D->C == '\n') {
977             ++D->Line;
978             D->Col = 0;
979         } else {
980             ++D->Col;
981         }
982     }
983 }
984
985
986
987 static void NextToken (InputData* D)
988 /* Read the next token from the input stream */
989 {
990     static const struct KeywordEntry  {
991         const char      Keyword[10];
992         Token           Tok;
993     } KeywordTable[] = {
994         { "absolute",   TOK_ABSOLUTE    },
995         { "addrsize",   TOK_ADDRSIZE    },
996         { "equate",     TOK_EQUATE      },
997         { "file",       TOK_FILE        },
998         { "id",         TOK_ID          },
999         { "label",      TOK_LABEL       },
1000         { "line",       TOK_LINE        },
1001         { "long",       TOK_LONG        },
1002         { "major",      TOK_MAJOR       },
1003         { "minor",      TOK_MINOR       },
1004         { "mtime",      TOK_MTIME       },
1005         { "name",       TOK_NAME        },
1006         { "range",      TOK_RANGE       },
1007         { "ro",         TOK_RO          },
1008         { "rw",         TOK_RW          },
1009         { "segment",    TOK_SEGMENT     },
1010         { "size",       TOK_SIZE        },
1011         { "start",      TOK_START       },
1012         { "sym",        TOK_SYM         },
1013         { "type",       TOK_TYPE        },
1014         { "value",      TOK_VALUE       },
1015         { "version",    TOK_VERSION     },
1016         { "zeropage",   TOK_ZEROPAGE    },
1017     };
1018
1019
1020     /* Skip whitespace */
1021     while (D->C == ' ' || D->C == '\t') {
1022         NextChar (D);
1023     }
1024
1025     /* Remember the current position as start of the next token */
1026     D->SLine = D->Line;
1027     D->SCol  = D->Col;
1028
1029     /* Identifier? */
1030     if (D->C == '_' || isalpha (D->C)) {
1031
1032         const struct KeywordEntry* Entry;
1033
1034         /* Read the identifier */
1035         SB_Clear (&D->SVal);
1036         while (D->C == '_' || isalnum (D->C)) {
1037             SB_AppendChar (&D->SVal, D->C);
1038             NextChar (D);
1039         }
1040         SB_Terminate (&D->SVal);
1041
1042         /* Search the identifier in the keyword table */
1043         Entry = bsearch (SB_GetConstBuf (&D->SVal),
1044                          KeywordTable,
1045                          sizeof (KeywordTable) / sizeof (KeywordTable[0]),
1046                          sizeof (KeywordTable[0]),
1047                          (int (*)(const void*, const void*)) strcmp);
1048         if (Entry == 0) {
1049             D->Tok = TOK_IDENT;
1050         } else {
1051             D->Tok = Entry->Tok;
1052         }
1053         return;
1054     }
1055
1056     /* Number? */
1057     if (isdigit (D->C)) {
1058         int Base = 10;
1059         int Val;
1060         if (D->C == '0') {
1061             NextChar (D);
1062             if (toupper (D->C) == 'X') {
1063                 NextChar (D);
1064                 Base = 16;
1065             } else {
1066                 Base = 8;
1067             }
1068         } else {
1069             Base = 10;
1070         }
1071         D->IVal = 0;
1072         while ((Val = DigitVal (D->C)) >= 0 && Val < Base) {
1073             D->IVal = D->IVal * Base + Val;
1074             NextChar (D);
1075         }
1076         D->Tok = TOK_INTCON;
1077         return;
1078     }
1079
1080     /* Other characters */
1081     switch (D->C) {
1082
1083         case '-':
1084             NextChar (D);
1085             D->Tok = TOK_MINUS;
1086             break;
1087
1088         case '+':
1089             NextChar (D);
1090             D->Tok = TOK_PLUS;
1091             break;
1092
1093         case ',':
1094             NextChar (D);
1095             D->Tok = TOK_COMMA;
1096             break;
1097
1098         case '=':
1099             NextChar (D);
1100             D->Tok = TOK_EQUAL;
1101             break;
1102
1103         case '\"':
1104             SB_Clear (&D->SVal);
1105             NextChar (D);
1106             while (1) {
1107                 if (D->C == '\n' || D->C == EOF) {
1108                     ParseError (D, CC65_ERROR, "Unterminated string constant");
1109                     break;
1110                 }
1111                 if (D->C == '\"') {
1112                     NextChar (D);
1113                     break;
1114                 }
1115                 SB_AppendChar (&D->SVal, D->C);
1116                 NextChar (D);
1117             }
1118             SB_Terminate (&D->SVal);
1119             D->Tok = TOK_STRCON;
1120             break;
1121
1122         case '\n':
1123             NextChar (D);
1124             D->Tok = TOK_EOL;
1125             break;
1126
1127         case EOF:
1128             D->Tok = TOK_EOF;
1129             break;
1130
1131         default:
1132             ParseError (D, CC65_ERROR, "Invalid input character `%c'", D->C);
1133
1134     }
1135 }
1136
1137
1138
1139 static int TokenFollows (InputData* D, Token Tok, const char* Name)
1140 /* Check for a comma */
1141 {
1142     if (D->Tok != Tok) {
1143         ParseError (D, CC65_ERROR, "%s expected", Name);
1144         SkipLine (D);
1145         return 0;
1146     } else {
1147         return 1;
1148     }
1149 }
1150
1151
1152
1153 static int IntConstFollows (InputData* D)
1154 /* Check for an integer constant */
1155 {
1156     return TokenFollows (D, TOK_INTCON, "Integer constant");
1157 }
1158
1159
1160
1161 static int StrConstFollows (InputData* D)
1162 /* Check for a string literal */
1163 {
1164     return TokenFollows (D, TOK_STRCON, "String literal");
1165 }
1166
1167
1168
1169 static int Consume (InputData* D, Token Tok, const char* Name)
1170 /* Check for a token and consume it. Return true if the token was comsumed,
1171  * return false otherwise.
1172  */
1173 {
1174     if (TokenFollows (D, Tok, Name)) {
1175         NextToken (D);
1176         return 1;
1177     } else {
1178         return 0;
1179     }
1180 }
1181
1182
1183
1184 static int ConsumeEqual (InputData* D)
1185 /* Consume an equal sign */
1186 {
1187     return Consume (D, TOK_EQUAL, "'='");
1188 }
1189
1190
1191
1192 static int ConsumeMinus (InputData* D)
1193 /* Consume a minus sign */
1194 {
1195     return Consume (D, TOK_MINUS, "'-'");
1196 }
1197
1198
1199
1200 static void ParseFile (InputData* D)
1201 /* Parse a FILE line */
1202 {
1203     unsigned      Id;
1204     unsigned long Size;
1205     unsigned long MTime;
1206     StrBuf        FileName = STRBUF_INITIALIZER;
1207     FileInfo*     F;
1208     enum {
1209         ibNone      = 0x00,
1210         ibId        = 0x01,
1211         ibFileName  = 0x02,
1212         ibSize      = 0x04,
1213         ibMTime     = 0x08,
1214         ibRequired  = ibId | ibFileName | ibSize | ibMTime,
1215     } InfoBits = ibNone;
1216
1217     /* Skip the FILE token */
1218     NextToken (D);
1219
1220     /* More stuff follows */
1221     while (1) {
1222
1223         Token Tok;
1224
1225         /* Check for an unknown keyword */
1226         if (D->Tok == TOK_IDENT) {
1227             UnknownKeyword (D);
1228             continue;
1229         }
1230
1231         /* Something we know? */
1232         if (D->Tok != TOK_ID   && D->Tok != TOK_NAME  &&
1233             D->Tok != TOK_SIZE && D->Tok != TOK_MTIME) {
1234             /* Done */
1235             break;
1236         }
1237
1238         /* Remember the token, skip it, check for equal */
1239         Tok = D->Tok;
1240         NextToken (D);
1241         if (!ConsumeEqual (D)) {
1242             goto ErrorExit;
1243         }
1244
1245         /* Check what the token was */
1246         switch (Tok) {
1247
1248             case TOK_ID:
1249                 if (!IntConstFollows (D)) {
1250                     goto ErrorExit;
1251                 }
1252                 Id = D->IVal;
1253                 InfoBits |= ibId;
1254                 NextToken (D);
1255                 break;
1256
1257             case TOK_NAME:
1258                 if (!StrConstFollows (D)) {
1259                     goto ErrorExit;
1260                 }
1261                 SB_Copy (&FileName, &D->SVal);
1262                 SB_Terminate (&FileName);
1263                 InfoBits |= ibFileName;
1264                 NextToken (D);
1265                 break;
1266
1267             case TOK_SIZE:
1268                 if (!IntConstFollows (D)) {
1269                     goto ErrorExit;
1270                 }
1271                 Size = D->IVal;
1272                 NextToken (D);
1273                 InfoBits |= ibSize;
1274                 break;
1275
1276             case TOK_MTIME:
1277                 if (!IntConstFollows (D)) {
1278                     goto ErrorExit;
1279                 }
1280                 MTime = D->IVal;
1281                 NextToken (D);
1282                 InfoBits |= ibMTime;
1283                 break;
1284
1285             default:
1286                 /* NOTREACHED */
1287                 UnexpectedToken (D);
1288                 goto ErrorExit;
1289
1290         }
1291
1292         /* Comma or done */
1293         if (D->Tok != TOK_COMMA) {
1294             break;
1295         }
1296         NextToken (D);
1297     }
1298
1299     /* Check for end of line */
1300     if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1301         UnexpectedToken (D);
1302         SkipLine (D);
1303         goto ErrorExit;
1304     }
1305
1306     /* Check for required information */
1307     if (InfoBits != ibRequired) {
1308         ParseError (D, CC65_ERROR, "Required attributes missing");
1309         goto ErrorExit;
1310     }
1311
1312     /* Create the file info and remember it */
1313     F = NewFileInfo (&FileName, Id, Size, MTime);
1314     CollAppend (&D->Info->FileInfoByName, F);
1315
1316 ErrorExit:
1317     /* Entry point in case of errors */
1318     SB_Done (&FileName);
1319     return;
1320 }
1321
1322
1323
1324 static void ParseLine (InputData* D)
1325 /* Parse a LINE line */
1326 {
1327     unsigned    File;
1328     unsigned    Segment;
1329     cc65_line   Line;
1330     cc65_addr   Start;
1331     cc65_addr   End;
1332     LineInfo*   L;
1333     enum {
1334         ibNone      = 0x00,
1335         ibFile      = 0x01,
1336         ibSegment   = 0x02,
1337         ibLine      = 0x04,
1338         ibRange     = 0x08,
1339         ibRequired  = ibFile | ibSegment | ibLine | ibRange,
1340     } InfoBits = ibNone;
1341
1342     /* Skip the LINE token */
1343     NextToken (D);
1344
1345     /* More stuff follows */
1346     while (1) {
1347
1348         Token Tok;
1349
1350         /* Check for an unknown keyword */
1351         if (D->Tok == TOK_IDENT) {
1352             UnknownKeyword (D);
1353             continue;
1354         }
1355
1356         /* Something we know? */
1357         if (D->Tok != TOK_FILE && D->Tok != TOK_SEGMENT &&
1358             D->Tok != TOK_LINE && D->Tok != TOK_RANGE) {
1359             /* Done */
1360             break;
1361         }
1362
1363         /* Remember the token, skip it, check for equal */
1364         Tok = D->Tok;
1365         NextToken (D);
1366         if (!ConsumeEqual (D)) {
1367             goto ErrorExit;
1368         }
1369
1370         /* Check what the token was */
1371         switch (Tok) {
1372
1373             case TOK_FILE:
1374                 if (!IntConstFollows (D)) {
1375                     goto ErrorExit;
1376                 }
1377                 File = D->IVal;
1378                 InfoBits |= ibFile;
1379                 NextToken (D);
1380                 break;
1381
1382             case TOK_SEGMENT:
1383                 if (!IntConstFollows (D)) {
1384                     goto ErrorExit;
1385                 }
1386                 Segment = D->IVal;
1387                 InfoBits |= ibSegment;
1388                 NextToken (D);
1389                 break;
1390
1391             case TOK_LINE:
1392                 if (!IntConstFollows (D)) {
1393                     goto ErrorExit;
1394                 }
1395                 Line = (cc65_line) D->IVal;
1396                 NextToken (D);
1397                 InfoBits |= ibLine;
1398                 break;
1399
1400             case TOK_RANGE:
1401                 if (!IntConstFollows (D)) {
1402                     goto ErrorExit;
1403                 }
1404                 Start = (cc65_addr) D->IVal;
1405                 NextToken (D);
1406                 if (!ConsumeMinus (D)) {
1407                     goto ErrorExit;
1408                 }
1409                 if (!IntConstFollows (D)) {
1410                     goto ErrorExit;
1411                 }
1412                 End = (cc65_addr) D->IVal;
1413                 NextToken (D);
1414                 InfoBits |= ibRange;
1415                 break;
1416
1417             default:
1418                 /* NOTREACHED */
1419                 UnexpectedToken (D);
1420                 goto ErrorExit;
1421
1422         }
1423
1424         /* Comma or done */
1425         if (D->Tok != TOK_COMMA) {
1426             break;
1427         }
1428         NextToken (D);
1429     }
1430
1431     /* Check for end of line */
1432     if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1433         UnexpectedToken (D);
1434         SkipLine (D);
1435         goto ErrorExit;
1436     }
1437
1438     /* Check for required information */
1439     if (InfoBits != ibRequired) {
1440         ParseError (D, CC65_ERROR, "Required attributes missing");
1441         goto ErrorExit;
1442     }
1443
1444     /* Create the line info and remember it */
1445     L = NewLineInfo (File, Segment, Line, Start, End);
1446     CollAppend (&D->LineInfos, L);
1447
1448 ErrorExit:
1449     /* Entry point in case of errors */
1450     return;
1451 }
1452
1453
1454
1455 static void ParseSegment (InputData* D)
1456 /* Parse a SEGMENT line */
1457 {
1458     unsigned    Id;
1459     cc65_addr   Start;
1460     cc65_addr   Size;
1461     StrBuf      SegName = STRBUF_INITIALIZER;
1462     SegInfo*    S;
1463     enum {
1464         ibNone      = 0x00,
1465         ibId        = 0x01,
1466         ibSegName   = 0x02,
1467         ibStart     = 0x04,
1468         ibSize      = 0x08,
1469         ibAddrSize  = 0x10,
1470         ibType      = 0x20,
1471         ibRequired  = ibId | ibSegName | ibStart | ibSize | ibAddrSize | ibType,
1472     } InfoBits = ibNone;
1473
1474     /* Skip the SEGMENT token */
1475     NextToken (D);
1476
1477     /* More stuff follows */
1478     while (1) {
1479
1480         Token Tok;
1481
1482         /* Check for an unknown keyword */
1483         if (D->Tok == TOK_IDENT) {
1484             UnknownKeyword (D);
1485             continue;
1486         }
1487
1488         /* Something we know? */
1489         if (D->Tok != TOK_ID       && D->Tok != TOK_NAME  &&
1490             D->Tok != TOK_START    && D->Tok != TOK_SIZE  &&
1491             D->Tok != TOK_ADDRSIZE && D->Tok != TOK_TYPE) {
1492             /* Done */
1493             break;
1494         }
1495
1496         /* Remember the token, skip it, check for equal */
1497         Tok = D->Tok;
1498         NextToken (D);
1499         if (!ConsumeEqual (D)) {
1500             goto ErrorExit;
1501         }
1502
1503         /* Check what the token was */
1504         switch (Tok) {
1505
1506             case TOK_ID:
1507                 if (!IntConstFollows (D)) {
1508                     goto ErrorExit;
1509                 }
1510                 Id = D->IVal;
1511                 InfoBits |= ibId;
1512                 NextToken (D);
1513                 break;
1514
1515             case TOK_NAME:
1516                 if (!StrConstFollows (D)) {
1517                     goto ErrorExit;
1518                 }
1519                 SB_Copy (&SegName, &D->SVal);
1520                 SB_Terminate (&SegName);
1521                 InfoBits |= ibSegName;
1522                 NextToken (D);
1523                 break;
1524
1525             case TOK_START:
1526                 if (!IntConstFollows (D)) {
1527                     goto ErrorExit;
1528                 }
1529                 Start = (cc65_addr) D->IVal;
1530                 NextToken (D);
1531                 InfoBits |= ibStart;
1532                 break;
1533
1534             case TOK_SIZE:
1535                 if (!IntConstFollows (D)) {
1536                     goto ErrorExit;
1537                 }
1538                 Size = D->IVal;
1539                 NextToken (D);
1540                 InfoBits |= ibSize;
1541                 break;
1542
1543             case TOK_ADDRSIZE:
1544                 NextToken (D);
1545                 InfoBits |= ibAddrSize;
1546                 break;
1547
1548             case TOK_TYPE:
1549                 NextToken (D);
1550                 InfoBits |= ibType;
1551                 break;
1552
1553             default:
1554                 /* NOTREACHED */
1555                 UnexpectedToken (D);
1556                 goto ErrorExit;
1557
1558         }
1559
1560         /* Comma or done */
1561         if (D->Tok != TOK_COMMA) {
1562             break;
1563         }
1564         NextToken (D);
1565     }
1566
1567     /* Check for end of line */
1568     if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1569         UnexpectedToken (D);
1570         SkipLine (D);
1571         goto ErrorExit;
1572     }
1573
1574     /* Check for required information */
1575     if (InfoBits != ibRequired) {
1576         ParseError (D, CC65_ERROR, "Required attributes missing");
1577         goto ErrorExit;
1578     }
1579
1580     /* Create the segment info and remember it */
1581     S = NewSegInfo (&SegName, Id, Start, Size);
1582     CollAppend (&D->Info->SegInfoByName, S);
1583
1584 ErrorExit:
1585     /* Entry point in case of errors */
1586     SB_Done (&SegName);
1587     return;
1588 }
1589
1590
1591
1592 static void ParseSym (InputData* D)
1593 /* Parse a SYM line */
1594 {
1595     /* Skip the SYM token */
1596     NextToken (D);
1597
1598     /* ### */
1599     SkipLine (D);
1600 }
1601
1602
1603
1604 static void ParseVersion (InputData* D)
1605 /* Parse a VERSION line */
1606 {
1607     enum { None = 0x00, Major = 0x01, Minor = 0x02 } InfoBits = None;
1608
1609     /* Skip the VERSION token */
1610     NextToken (D);
1611
1612     /* More stuff follows */
1613     while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1614
1615         switch (D->Tok) {
1616
1617             case TOK_MAJOR:
1618                 NextToken (D);
1619                 if (!ConsumeEqual (D)) {
1620                     goto ErrorExit;
1621                 }
1622                 if (!IntConstFollows (D)) {
1623                     goto ErrorExit;
1624                 }
1625                 D->MajorVersion = D->IVal;
1626                 NextToken (D);
1627                 InfoBits |= Major;
1628                 break;
1629
1630             case TOK_MINOR:
1631                 NextToken (D);
1632                 if (!ConsumeEqual (D)) {
1633                     goto ErrorExit;
1634                 }
1635                 if (!IntConstFollows (D)) {
1636                     goto ErrorExit;
1637                 }
1638                 D->MinorVersion = D->IVal;
1639                 NextToken (D);
1640                 InfoBits |= Minor;
1641                 break;
1642
1643             case TOK_IDENT:
1644                 /* Try to skip unknown keywords that may have been added by
1645                  * a later version.
1646                  */
1647                 UnknownKeyword (D);
1648                 break;
1649
1650             default:
1651                 UnexpectedToken (D);
1652                 SkipLine (D);
1653                 goto ErrorExit;
1654         }
1655
1656         /* Comma follows before next attribute */
1657         if (D->Tok == TOK_COMMA) {
1658             NextToken (D);
1659         } else if (D->Tok == TOK_EOL || D->Tok == TOK_EOF) {
1660             break;
1661         } else {
1662             UnexpectedToken (D);
1663             goto ErrorExit;
1664         }
1665     }
1666
1667     /* Check for required information */
1668     if ((InfoBits & Major) == None) {
1669         MissingAttribute (D, "major");
1670         goto ErrorExit;
1671     }
1672     if ((InfoBits & Minor) == None) {
1673         MissingAttribute (D, "minor");
1674         goto ErrorExit;
1675     }
1676
1677 ErrorExit:
1678     /* Entry point in case of errors */
1679     return;
1680 }
1681
1682
1683
1684 /*****************************************************************************/
1685 /*                              Data processing                              */
1686 /*****************************************************************************/
1687
1688
1689
1690 static SegInfo* FindSegInfoById (InputData* D, unsigned Id)
1691 /* Find the SegInfo with a given Id */
1692 {
1693     /* Get a pointer to the segment info collection */
1694     Collection* SegInfos = &D->Info->SegInfoById;
1695
1696     /* Do a binary search */
1697     int Lo = 0;
1698     int Hi = (int) CollCount (SegInfos) - 1;
1699     while (Lo <= Hi) {
1700
1701         /* Mid of range */
1702         int Cur = (Lo + Hi) / 2;
1703
1704         /* Get item */
1705         SegInfo* CurItem = CollAt (SegInfos, Cur);
1706
1707         /* Found? */
1708         if (Id > CurItem->Id) {
1709             Lo = Cur + 1;
1710         } else if (Id < CurItem->Id) {
1711             Hi = Cur - 1;
1712         } else {
1713             /* Found! */
1714             return CurItem;
1715         }
1716     }
1717
1718     /* Not found */
1719     return 0;
1720 }
1721
1722
1723
1724 static FileInfo* FindFileInfoByName (Collection* FileInfos, const char* FileName)
1725 /* Find the FileInfo for a given file name */
1726 {
1727     /* Do a binary search */
1728     int Lo = 0;
1729     int Hi = (int) CollCount (FileInfos) - 1;
1730     while (Lo <= Hi) {
1731
1732         /* Mid of range */
1733         int Cur = (Lo + Hi) / 2;
1734
1735         /* Get item */
1736         FileInfo* CurItem = CollAt (FileInfos, Cur);
1737
1738         /* Compare */
1739         int Res = strcmp (CurItem->FileName, FileName);
1740
1741         /* Found? */
1742         if (Res < 0) {
1743             Lo = Cur + 1;
1744         } else if (Res > 0) {
1745             Hi = Cur - 1;
1746         } else {
1747             /* Found! */
1748             return CurItem;
1749         }
1750     }
1751
1752     /* Not found */
1753     return 0;
1754 }
1755
1756
1757
1758 static FileInfo* FindFileInfoById (Collection* FileInfos, unsigned Id)
1759 /* Find the FileInfo with a given Id */
1760 {
1761     /* Do a binary search */
1762     int Lo = 0;
1763     int Hi = (int) CollCount (FileInfos) - 1;
1764     while (Lo <= Hi) {
1765
1766         /* Mid of range */
1767         int Cur = (Lo + Hi) / 2;
1768
1769         /* Get item */
1770         FileInfo* CurItem = CollAt (FileInfos, Cur);
1771
1772         /* Found? */
1773         if (Id > CurItem->Id) {
1774             Lo = Cur + 1;
1775         } else if (Id < CurItem->Id) {
1776             Hi = Cur - 1;
1777         } else {
1778             /* Found! */
1779             return CurItem;
1780         }
1781     }
1782
1783     /* Not found */
1784     return 0;
1785 }
1786
1787
1788
1789 static void ProcessSegInfo (InputData* D)
1790 /* Postprocess segment infos */
1791 {
1792     unsigned I;
1793
1794     /* Get pointers to the segment info collections */
1795     Collection* SegInfoByName = &D->Info->SegInfoByName;
1796     Collection* SegInfoById   = &D->Info->SegInfoById;
1797
1798     /* Sort the segment infos by name */
1799     CollSort (SegInfoByName, CompareSegInfoByName);
1800
1801     /* Copy all items over to the collection that will get sorted by id */
1802     for (I = 0; I < CollCount (SegInfoByName); ++I) {
1803         CollAppend (SegInfoById, CollAt (SegInfoByName, I));
1804     }
1805
1806     /* Sort this collection */
1807     CollSort (SegInfoById, CompareSegInfoById);
1808 }
1809
1810
1811
1812 static void ProcessFileInfo (InputData* D)
1813 /* Postprocess file infos */
1814 {
1815     /* Get pointers to the file info collections */
1816     Collection* FileInfoByName = &D->Info->FileInfoByName;
1817     Collection* FileInfoById   = &D->Info->FileInfoById;
1818
1819     /* First, sort the file infos, so we can check for duplicates and do
1820      * binary search.
1821      */
1822     CollSort (FileInfoByName, CompareFileInfoByName);
1823
1824     /* Cannot work on an empty collection */
1825     if (CollCount (FileInfoByName) > 0) {
1826
1827         /* Walk through the file infos sorted by name and check for duplicates.
1828          * If we find some, warn and remove them, so the file infos are unique
1829          * after that step.
1830          */
1831         FileInfo* F = CollAt (FileInfoByName, 0);
1832         unsigned I = 1;
1833         while (I < CollCount (FileInfoByName)) {
1834             FileInfo* Next = CollAt (FileInfoByName, I);
1835             if (strcmp (F->FileName, Next->FileName) == 0) {
1836                 /* Warn only if time stamp and/or size is different */
1837                 if (F->Size != Next->Size || F->MTime != Next->MTime) {
1838                     ParseError (D,
1839                                 CC65_WARNING,
1840                                 "Duplicate file entry for \"%s\"",
1841                                 F->FileName);
1842                 }
1843                 /* Remove the duplicate entry */
1844                 FreeFileInfo (Next);
1845                 CollDelete (FileInfoByName, I);
1846             } else {
1847                 /* This one is ok, check the next entry */
1848                 F = Next;
1849                 ++I;
1850             }
1851         }
1852
1853         /* Copy the file infos to another collection that will be sorted by id */
1854         for (I = 0; I < CollCount (FileInfoByName); ++I) {
1855             CollAppend (FileInfoById, CollAt (FileInfoByName, I));
1856         }
1857
1858         /* Sort this collection */
1859         CollSort (FileInfoById, CompareFileInfoById);
1860     }
1861 }
1862
1863
1864
1865 static void ProcessLineInfo (InputData* D)
1866 /* Postprocess line infos */
1867 {
1868     /* Get pointers to the collections */
1869     Collection* LineInfos = &D->LineInfos;
1870     Collection* FileInfos = &D->Info->FileInfoByName;
1871
1872     /* Walk over the line infos and replace the id numbers of file and segment
1873      * with pointers to the actual structs. Add the line info to each file
1874      * where it is defined.
1875      */
1876     unsigned I = 0;
1877     FileInfo* LastFileInfo = 0;
1878     SegInfo*  LastSegInfo  = 0;
1879     while (I < CollCount (LineInfos)) {
1880
1881         FileInfo* F;
1882         SegInfo*  S;
1883
1884         /* Get LineInfo struct */
1885         LineInfo* L = CollAt (LineInfos, I);
1886
1887         /* Find the FileInfo that corresponds to Id. We cache the last file
1888          * info in LastFileInfo to speedup searching.
1889          */
1890         if (LastFileInfo && LastFileInfo->Id == L->File.Id) {
1891             F = LastFileInfo;
1892         } else {
1893             F = FindFileInfoById (&D->Info->FileInfoById, L->File.Id);
1894
1895             /* If we have no corresponding file info, print a warning and
1896              * remove the line info.
1897              */
1898             if (F == 0) {
1899                 ParseError (D,
1900                             CC65_ERROR,
1901                             "No file info for file with id %u",
1902                             L->File.Id);
1903                 FreeLineInfo (L);
1904                 CollDelete (LineInfos, I);
1905                 continue;
1906             }
1907
1908             /* Otherwise remember it for later */
1909             LastFileInfo = F;
1910         }
1911
1912         /* Replace the file id by a pointer to the file info */
1913         L->File.Info = F;
1914
1915         /* Find the SegInfo that corresponds to Id. We cache the last file
1916          * info in LastSegInfo to speedup searching.
1917          */
1918         if (LastSegInfo && LastSegInfo->Id == L->Seg.Id) {
1919             S = LastSegInfo;
1920         } else {
1921             S = FindSegInfoById (D, L->Seg.Id);
1922
1923             /* If we have no corresponding segment info, print a warning and
1924              * remove the line info.
1925              */
1926             if (S == 0) {
1927                 ParseError (D,
1928                             CC65_ERROR,
1929                             "No segment info for segment with id %u",
1930                             L->Seg.Id);
1931                 FreeLineInfo (L);
1932                 CollDelete (LineInfos, I);
1933                 continue;
1934             }
1935
1936             /* Otherwise remember it for later */
1937             LastSegInfo = S;
1938         }
1939
1940         /* Replace the segment id by a pointer to the segment info */
1941         L->Seg.Info = S;
1942
1943         /* Add this line info to the file where it is defined */
1944         CollAppend (&F->LineInfoByAddr, L);
1945         CollAppend (&F->LineInfoByLine, L);
1946
1947         /* Next one */
1948         ++I;
1949     }
1950
1951     /* Walk over all files and sort the line infos for each file so we can
1952      * do a binary search later.
1953      */
1954     for (I = 0; I < CollCount (FileInfos); ++I) {
1955
1956         /* Get a pointer to this file info */
1957         FileInfo* F = CollAt (FileInfos, I);
1958
1959         /* Sort the line infos for this file */
1960         CollSort (&F->LineInfoByAddr, CompareLineInfoByAddr);
1961         CollSort (&F->LineInfoByLine, CompareLineInfoByLine);
1962
1963         /* If there are line info entries, place the first and last address
1964          * of into the FileInfo struct itself, so we can rule out a FileInfo
1965          * quickly when mapping an address to a line info.
1966          */
1967         if (CollCount (&F->LineInfoByAddr) > 0) {
1968             F->Start = ((const LineInfo*) CollFirst (&F->LineInfoByAddr))->Start;
1969             F->End   = ((const LineInfo*) CollLast (&F->LineInfoByAddr))->End;
1970         }
1971
1972     }
1973 }
1974
1975
1976
1977 static LineInfo* FindLineInfoByAddr (FileInfo* F, cc65_addr Addr)
1978 /* Find the LineInfo for a given address */
1979 {
1980     Collection* LineInfoByAddr;
1981     int         Hi;
1982     int         Lo;
1983
1984
1985     /* Each file info contains the first and last address for which line
1986      * info is available, so we can rule out non matching ones quickly.
1987      */
1988     if (Addr < F->Start || Addr > F->End) {
1989         return 0;
1990     }
1991
1992     /* Get a pointer to the line info collection for this file */
1993     LineInfoByAddr = &F->LineInfoByAddr;
1994
1995     /* Do a binary search */
1996     Lo = 0;
1997     Hi = (int) CollCount (LineInfoByAddr) - 1;
1998     while (Lo <= Hi) {
1999
2000         /* Mid of range */
2001         int Cur = (Lo + Hi) / 2;
2002
2003         /* Get item */
2004         LineInfo* CurItem = CollAt (LineInfoByAddr, Cur);
2005
2006         /* Found? */
2007         if (Addr < CurItem->Start) {
2008             Hi = Cur - 1;
2009         } else if (Addr > CurItem->End) {
2010             Lo = Cur + 1;
2011         } else {
2012             /* Found! */
2013             return CurItem;
2014         }
2015     }
2016
2017     /* Not found */
2018     return 0;
2019 }
2020
2021
2022
2023 static LineInfo* FindLineInfoByLine (FileInfo* F, cc65_line Line)
2024 /* Find the LineInfo for a given line number */
2025 {
2026     int         Hi;
2027     int         Lo;
2028
2029
2030     /* Get a pointer to the line info collection for this file */
2031     Collection* LineInfoByLine = &F->LineInfoByLine;
2032
2033     /* Do a binary search */
2034     Lo = 0;
2035     Hi = (int) CollCount (LineInfoByLine) - 1;
2036     while (Lo <= Hi) {
2037
2038         /* Mid of range */
2039         int Cur = (Lo + Hi) / 2;
2040
2041         /* Get item */
2042         LineInfo* CurItem = CollAt (LineInfoByLine, Cur);
2043
2044         /* Found? */
2045         if (Line < CurItem->Line) {
2046             Hi = Cur - 1;
2047         } else if (Line > CurItem->Line) {
2048             Lo = Cur + 1;
2049         } else {
2050             /* Found! */
2051             return CurItem;
2052         }
2053     }
2054
2055     /* Not found */
2056     return 0;
2057 }
2058
2059
2060
2061 /*****************************************************************************/
2062 /*                                   Code                                    */
2063 /*****************************************************************************/
2064
2065
2066
2067 cc65_dbginfo cc65_read_dbginfo (const char* FileName, cc65_errorfunc ErrFunc)
2068 /* Parse the debug info file with the given name. On success, the function
2069  * will return a pointer to an opaque cc65_dbginfo structure, that must be
2070  * passed to the other functions in this module to retrieve information.
2071  * errorfunc is called in case of warnings and errors. If the file cannot be
2072  * read successfully, NULL is returned.
2073  */
2074 {
2075     /* Data structure used to control scanning and parsing */
2076     InputData D = {
2077         0,                      /* Name of input file */
2078         1,                      /* Line number */
2079         0,                      /* Input file */
2080         0,                      /* Line at start of current token */
2081         0,                      /* Column at start of current token */
2082         0,                      /* Number of errors */
2083         0,                      /* Input file */
2084         ' ',                    /* Input character */
2085         TOK_INVALID,            /* Input token */
2086         0,                      /* Integer constant */
2087         STRBUF_INITIALIZER,     /* String constant */
2088         0,                      /* Function called in case of errors */
2089         0,                      /* Major version number */
2090         0,                      /* Minor version number */
2091         COLLECTION_INITIALIZER, /* Line information */
2092         0,                      /* Pointer to debug info */
2093     };
2094     D.FileName = FileName;
2095     D.Error    = ErrFunc;
2096
2097     /* Open the input file */
2098     D.F = fopen (D.FileName, "r");
2099     if (D.F == 0) {
2100         /* Cannot open */
2101         ParseError (&D, CC65_ERROR,
2102                     "Cannot open input file \"%s\": %s",
2103                      D.FileName, strerror (errno));
2104         return 0;
2105     }
2106
2107     /* Create a new debug info struct */
2108     D.Info = NewDbgInfo ();
2109
2110     /* Prime the pump */
2111     NextToken (&D);
2112
2113     /* Parse lines */
2114     while (D.Tok != TOK_EOF) {
2115
2116         switch (D.Tok) {
2117
2118             case TOK_FILE:
2119                 ParseFile (&D);
2120                 break;
2121
2122             case TOK_LINE:
2123                 ParseLine (&D);
2124                 break;
2125
2126             case TOK_SEGMENT:
2127                 ParseSegment (&D);
2128                 break;
2129
2130             case TOK_SYM:
2131                 ParseSym (&D);
2132                 break;
2133
2134             case TOK_VERSION:
2135                 ParseVersion (&D);
2136                 break;
2137
2138             case TOK_IDENT:
2139                 /* Output a warning, then skip the line with the unknown
2140                  * keyword that may have been added by a later version.
2141                  */
2142                 ParseError (&D, CC65_WARNING,
2143                             "Unknown keyword \"%s\" - skipping",
2144                             SB_GetConstBuf (&D.SVal));
2145
2146                 SkipLine (&D);
2147                 break;
2148
2149             default:
2150                 UnexpectedToken (&D);
2151
2152         }
2153
2154         /* EOL or EOF must follow */
2155         if (D.Tok != TOK_EOF) {
2156             if (D.Tok != TOK_EOL) {
2157                 ParseError (&D, 1, "Extra tokens in line");
2158                 SkipLine (&D);
2159             }
2160             NextToken (&D);
2161         }
2162     }
2163
2164     /* Close the file */
2165     fclose (D.F);
2166
2167     /* Free memory allocated for SVal */
2168     SB_Done (&D.SVal);
2169
2170     /* In case of errors, delete the debug info already allocated and
2171      * return NULL
2172      */
2173     if (D.Errors > 0) {
2174         /* Free allocated stuff */
2175         unsigned I;
2176         for (I = 0; I < CollCount (&D.LineInfos); ++I) {
2177             FreeLineInfo (CollAt (&D.LineInfos, I));
2178         }
2179         DoneCollection (&D.LineInfos);
2180         FreeDbgInfo (D.Info);
2181         return 0;
2182     }
2183
2184     /* We do now have all the information from the input file. Do
2185      * postprocessing.
2186      */
2187     ProcessSegInfo (&D);
2188     ProcessFileInfo (&D);
2189     ProcessLineInfo (&D);
2190
2191     /* Free the collection that contained the line info */
2192     DoneCollection (&D.LineInfos);
2193
2194     /* Return the debug info struct that was created */
2195     return D.Info;
2196 }
2197
2198
2199
2200 void cc65_free_dbginfo (cc65_dbginfo Handle)
2201 /* Free debug information read from a file */
2202 {
2203     if (Handle) {
2204         FreeDbgInfo (Handle);
2205     }
2206 }
2207
2208
2209
2210 cc65_lineinfo* cc65_lineinfo_byaddr (cc65_dbginfo Handle, unsigned long Addr)
2211 /* Return line information for the given address. The function returns 0
2212  * if no line information was found.
2213  */
2214 {
2215     unsigned        I;
2216     Collection*     FileInfoByName;
2217     cc65_lineinfo*  D = 0;
2218
2219     /* We will place a list of line infos in a collection */
2220     Collection LineInfos = COLLECTION_INITIALIZER;
2221
2222     /* Check the parameter */
2223     assert (Handle != 0);
2224
2225     /* Walk over all files and search for matching line infos */
2226     FileInfoByName = &((DbgInfo*) Handle)->FileInfoByName;
2227     for (I = 0; I < CollCount (FileInfoByName); ++I) {
2228         /* Check if the file contains line info for this address */
2229         LineInfo* L = FindLineInfoByAddr (CollAt (FileInfoByName, I), Addr);
2230         if (L != 0) {
2231             CollAppend (&LineInfos, L);
2232         }
2233     }
2234
2235     /* Do we have line infos? */
2236     if (CollCount (&LineInfos) > 0) {
2237
2238         /* Prepare the struct we will return to the caller */
2239         D = xmalloc (sizeof (*D) +
2240                      (CollCount (&LineInfos) - 1) * sizeof (D->data[0]));
2241         D->count = CollCount (&LineInfos);
2242         for (I = 0; I < D->count; ++I) {
2243
2244             /* Pointer to this info */
2245             LineInfo* L = CollAt (&LineInfos, I);
2246
2247             /* Copy data */
2248             D->data[I].name  = L->File.Info->FileName;
2249             D->data[I].size  = L->File.Info->Size;
2250             D->data[I].mtime = L->File.Info->MTime;
2251             D->data[I].line  = L->Line;
2252             D->data[I].start = L->Start;
2253             D->data[I].end   = L->End;
2254         }
2255     }
2256
2257     /* Free the line info collection */
2258     DoneCollection (&LineInfos);
2259
2260     /* Return the struct we've created */
2261     return D;
2262 }
2263
2264
2265
2266 cc65_lineinfo* cc65_lineinfo_byname (cc65_dbginfo Handle, const char* FileName,
2267                                      cc65_line Line)
2268 /* Return line information for a file/line number combination. The function
2269  * returns NULL if no line information was found.
2270  */
2271 {
2272     DbgInfo*        Info;
2273     FileInfo*       F;
2274     LineInfo*       L;
2275     cc65_lineinfo*  D;
2276
2277     /* Check the parameter */
2278     assert (Handle != 0);
2279
2280     /* The handle is actually a pointer to a debug info struct */
2281     Info = (DbgInfo*) Handle;
2282
2283     /* Get the file info */
2284     F = FindFileInfoByName (&Info->FileInfoByName, FileName);
2285     if (F == 0) {
2286         /* File not found */
2287         return 0;
2288     }
2289
2290     /* Search in the file for the given line */
2291     L = FindLineInfoByLine (F, Line);
2292     if (L == 0) {
2293         /* Line not found */
2294         return 0;
2295     }
2296
2297     /* Prepare the struct we will return to the caller */
2298     D = xmalloc (sizeof (*D));
2299     D->count = 1;
2300
2301     /* Copy data */
2302     D->data[0].name  = L->File.Info->FileName;
2303     D->data[0].size  = L->File.Info->Size;
2304     D->data[0].mtime = L->File.Info->MTime;
2305     D->data[0].line  = L->Line;
2306     D->data[0].start = L->Start;
2307     D->data[0].end   = L->End;
2308
2309     /* Return the allocated struct */
2310     return D;
2311 }
2312
2313
2314
2315 void cc65_free_lineinfo (cc65_dbginfo Handle, cc65_lineinfo* Info)
2316 /* Free line info returned by one of the other functions */
2317 {
2318     /* Just for completeness, check the handle */
2319     assert (Handle != 0);
2320
2321     /* Just free the memory */
2322     xfree (Info);
2323 }
2324
2325
2326
2327 cc65_filelist* cc65_get_filelist (cc65_dbginfo Handle)
2328 /* Return a list of all files referenced in the debug information */
2329 {
2330     DbgInfo*        Info;
2331     Collection*     FileInfoByName;
2332     cc65_filelist*  D;
2333     unsigned        I;
2334
2335     /* Check the parameter */
2336     assert (Handle != 0);
2337
2338     /* The handle is actually a pointer to a debug info struct */
2339     Info = (DbgInfo*) Handle;
2340
2341     /* Get a pointer to the file list */
2342     FileInfoByName = &Info->FileInfoByName;
2343
2344     /* Allocate memory for the data structure returned to the caller */
2345     D = xmalloc (sizeof (*D) - sizeof (D->data[0]) +
2346                  CollCount (FileInfoByName) * sizeof (D->data[0]));
2347
2348     /* Fill in the data */
2349     D->count = CollCount (FileInfoByName);
2350     for (I = 0; I < CollCount (FileInfoByName); ++I) {
2351
2352         /* Get this item */
2353         FileInfo* F = CollAt (FileInfoByName, I);
2354
2355         /* Copy the data */
2356         D->data[I].name  = F->FileName;
2357         D->data[I].size  = F->Size;
2358         D->data[I].mtime = F->MTime;
2359     }
2360
2361     /* Return the result */
2362     return D;
2363 }
2364
2365
2366
2367 void cc65_free_filelist (cc65_dbginfo Handle, cc65_filelist* List)
2368 /* Free a file list returned by cc65_get_filelist() */
2369 {
2370     /* Just for completeness, check the handle */
2371     assert (Handle != 0);
2372
2373     /* Just free the memory */
2374     xfree (List);
2375 }
2376
2377
2378
2379 cc65_segmentlist* cc65_get_segmentlist (cc65_dbginfo Handle)
2380 /* Return a list of all segments referenced in the debug information */
2381 {
2382     DbgInfo*            Info;
2383     Collection*         SegInfoByName;
2384     cc65_segmentlist*   D;
2385     unsigned            I;
2386
2387     /* Check the parameter */
2388     assert (Handle != 0);
2389
2390     /* The handle is actually a pointer to a debug info struct */
2391     Info = (DbgInfo*) Handle;
2392
2393     /* Get a pointer to the file list */
2394     SegInfoByName = &Info->SegInfoByName;
2395
2396     /* Allocate memory for the data structure returned to the caller */
2397     D = xmalloc (sizeof (*D) - sizeof (D->data[0]) +
2398                  CollCount (SegInfoByName) * sizeof (D->data[0]));
2399
2400     /* Fill in the data */
2401     D->count = CollCount (SegInfoByName);
2402     for (I = 0; I < CollCount (SegInfoByName); ++I) {
2403
2404         /* Get this item */
2405         SegInfo* S = CollAt (SegInfoByName, I);
2406
2407         /* Copy the data */
2408         D->data[I].name  = S->SegName;
2409         D->data[I].start = S->Start;
2410         D->data[I].end   = S->Start + S->Size - 1;
2411     }
2412
2413     /* Return the result */
2414     return D;
2415 }
2416
2417
2418
2419 void cc65_free_segmentlist (cc65_dbginfo Handle, cc65_segmentlist* List)
2420 /* Free a file list returned by cc65_get_filelist() */
2421 {
2422     /* Just for completeness, check the handle */
2423     assert (Handle != 0);
2424
2425     /* Just free the memory */
2426     xfree (List);
2427 }
2428
2429
2430