]> git.sur5r.net Git - cc65/blob - src/dbginfo/dbginfo.c
Mark the symbol that is the name of a scope with the size of that scope
[cc65] / src / dbginfo / dbginfo.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 dbginfo.h                                 */
4 /*                                                                           */
5 /*                         cc65 debug info handling                          */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2010-2011, 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 /* Use this for debugging - beware, lots of output */
56 #define DEBUG           0
57
58 /* Version numbers of the debug format we understand */
59 #define VER_MAJOR       1U
60 #define VER_MINOR       2U
61
62 /* Dynamic strings */
63 typedef struct StrBuf StrBuf;
64 struct StrBuf {
65     char*       Buf;                    /* Pointer to buffer */
66     unsigned    Len;                    /* Length of the string */
67     unsigned    Allocated;              /* Size of allocated memory */
68 };
69
70 /* Initializer for a string buffer */
71 #define STRBUF_INITIALIZER      { 0, 0, 0 }
72
73 /* An array of pointers that grows if needed */
74 typedef struct Collection Collection;
75 struct Collection {
76     unsigned            Count;          /* Number of items in the list */
77     unsigned            Size;           /* Size of allocated array */
78     void**              Items;          /* Array with dynamic size */
79 };
80
81 /* Initializer for static collections */
82 #define COLLECTION_INITIALIZER  { 0, 0, 0 }
83
84 /* Line info management */
85 typedef struct LineInfoListEntry LineInfoListEntry;
86 struct LineInfoListEntry {
87     cc65_addr           Addr;           /* Unique address */
88     unsigned            Count;          /* Number of LineInfos for this address */
89     void*               Data;           /* Either LineInfo* or LineInfo** */
90 };
91
92 typedef struct LineInfoList LineInfoList;
93 struct LineInfoList {
94     unsigned            Count;          /* Number of entries */
95     LineInfoListEntry*  List;           /* Dynamic array with entries */
96 };
97
98
99
100 /* Data structure containing information from the debug info file. A pointer
101  * to this structure is passed as handle to callers from the outside.
102  */
103 typedef struct DbgInfo DbgInfo;
104 struct DbgInfo {
105     Collection          SegInfoByName;  /* Segment infos sorted by name */
106     Collection          SegInfoById;    /* Segment infos sorted by id */
107     Collection          FileInfoByName; /* File infos sorted by name */
108     Collection          FileInfoById;   /* File infos sorted by id */
109     Collection          LineInfos;      /* List of all line infos */
110     LineInfoList        LineInfoByAddr; /* Line infos sorted by unique address */
111     Collection          SymInfoByName;  /* Symbol infos sorted by name */
112     Collection          SymInfoByVal;   /* Symbol infos sorted by value */
113 };
114
115 /* Input tokens */
116 typedef enum {
117
118     TOK_INVALID,                        /* Invalid token */
119     TOK_EOF,                            /* End of file reached */
120
121     TOK_INTCON,                         /* Integer constant */
122     TOK_STRCON,                         /* String constant */
123
124     TOK_EQUAL,                          /* = */
125     TOK_COMMA,                          /* , */
126     TOK_MINUS,                          /* - */
127     TOK_PLUS,                           /* + */
128     TOK_EOL,                            /* \n */
129
130     TOK_FIRST_KEYWORD,
131     TOK_ABSOLUTE = TOK_FIRST_KEYWORD,   /* ABSOLUTE keyword */
132     TOK_ADDRSIZE,                       /* ADDRSIZE keyword */
133     TOK_COUNT,                          /* COUNT keyword */
134     TOK_EQUATE,                         /* EQUATE keyword */
135     TOK_FILE,                           /* FILE keyword */
136     TOK_ID,                             /* ID keyword */
137     TOK_LABEL,                          /* LABEL keyword */
138     TOK_LINE,                           /* LINE keyword */
139     TOK_LONG,                           /* LONG_keyword */
140     TOK_MAJOR,                          /* MAJOR keyword */
141     TOK_MINOR,                          /* MINOR keyword */
142     TOK_MTIME,                          /* MTIME keyword */
143     TOK_NAME,                           /* NAME keyword */
144     TOK_OUTPUTNAME,                     /* OUTPUTNAME keyword */
145     TOK_OUTPUTOFFS,                     /* OUTPUTOFFS keyword */
146     TOK_RANGE,                          /* RANGE keyword */
147     TOK_RO,                             /* RO keyword */
148     TOK_RW,                             /* RW keyword */
149     TOK_SEGMENT,                        /* SEGMENT keyword */
150     TOK_SIZE,                           /* SIZE keyword */
151     TOK_START,                          /* START keyword */
152     TOK_SYM,                            /* SYM keyword */
153     TOK_TYPE,                           /* TYPE keyword */
154     TOK_VALUE,                          /* VALUE keyword */
155     TOK_VERSION,                        /* VERSION keyword */
156     TOK_ZEROPAGE,                       /* ZEROPAGE keyword */
157     TOK_LAST_KEYWORD = TOK_ZEROPAGE,
158
159     TOK_IDENT,                          /* To catch unknown keywords */
160 } Token;
161
162 /* Data used when parsing the debug info file */
163 typedef struct InputData InputData;
164 struct InputData {
165     const char*         FileName;       /* Name of input file */
166     cc65_line           Line;           /* Current line number */
167     unsigned            Col;            /* Current column number */
168     cc65_line           SLine;          /* Line number at start of token */
169     unsigned            SCol;           /* Column number at start of token */
170     unsigned            Errors;         /* Number of errors */
171     FILE*               F;              /* Input file */
172     int                 C;              /* Input character */
173     Token               Tok;            /* Token from input stream */
174     unsigned long       IVal;           /* Integer constant */
175     StrBuf              SVal;           /* String constant */
176     cc65_errorfunc      Error;          /* Function called in case of errors */
177     unsigned            MajorVersion;   /* Major version number */
178     unsigned            MinorVersion;   /* Minor version number */
179     DbgInfo*            Info;           /* Pointer to debug info */
180 };
181
182 /* Internally used segment info struct */
183 typedef struct SegInfo SegInfo;
184 struct SegInfo {
185     unsigned            Id;             /* Id of segment */
186     cc65_addr           Start;          /* Start address of segment */
187     cc65_addr           Size;           /* Size of segment */
188     char*               OutputName;     /* Name of output file */
189     unsigned long       OutputOffs;     /* Offset in output file */
190     char                SegName[1];     /* Name of segment */
191 };
192
193 /* Internally used file info struct */
194 typedef struct FileInfo FileInfo;
195 struct FileInfo {
196     unsigned            Id;             /* Id of file */
197     unsigned long       Size;           /* Size of file */
198     unsigned long       MTime;          /* Modification time */
199     Collection          LineInfoByLine; /* Line infos sorted by line */
200     char                FileName[1];    /* Name of file with full path */
201 };
202
203 /* Internally used line info struct */
204 typedef struct LineInfo LineInfo;
205 struct LineInfo {
206     cc65_addr           Start;          /* Start of data range */
207     cc65_addr           End;            /* End of data range */
208     cc65_line           Line;           /* Line number */
209     union {
210         unsigned        Id;             /* Id of file */
211         FileInfo*       Info;           /* Pointer to file info */
212     } File;
213     union {
214         unsigned        Id;             /* Id of segment */
215         SegInfo*        Info;           /* Pointer to segment info */
216     } Seg;
217     cc65_line_type      Type;           /* Type of line */
218     unsigned            Count;          /* Nesting counter for macros */
219 };
220
221 /* Internally used symbol info struct */
222 typedef struct SymInfo SymInfo;
223 struct SymInfo {
224     cc65_symbol_type    Type;           /* Type of symbol */
225     long                Value;          /* Value of symbol */
226     cc65_size           Size;           /* Size of symbol */
227     char                SymName[1];     /* Name of symbol */
228 };
229
230
231
232 /*****************************************************************************/
233 /*                                 Forwards                                  */
234 /*****************************************************************************/
235
236
237
238 static void NextToken (InputData* D);
239 /* Read the next token from the input stream */
240
241
242
243 /*****************************************************************************/
244 /*                             Memory allocation                             */
245 /*****************************************************************************/
246
247
248
249 static void* xmalloc (size_t Size)
250 /* Allocate memory, check for out of memory condition. Do some debugging */
251 {
252     void* P = 0;
253
254     /* Allow zero sized requests and return NULL in this case */
255     if (Size) {
256
257         /* Allocate memory */
258         P = malloc (Size);
259
260         /* Check for errors */
261         assert (P != 0);
262     }
263
264     /* Return a pointer to the block */
265     return P;
266 }
267
268
269
270 static void* xrealloc (void* P, size_t Size)
271 /* Reallocate a memory block, check for out of memory */
272 {
273     /* Reallocate the block */
274     void* N = realloc (P, Size);
275
276     /* Check for errors */
277     assert (N != 0 || Size == 0);
278
279     /* Return the pointer to the new block */
280     return N;
281 }
282
283
284
285 static void xfree (void* Block)
286 /* Free the block, do some debugging */
287 {
288     free (Block);
289 }
290
291
292
293 static cc65_lineinfo* new_cc65_lineinfo (unsigned Count)
294 /* Allocate and return a cc65_lineinfo struct that is able to hold Count
295  * entries. Initialize the count field of the returned struct.
296  */
297 {
298     cc65_lineinfo* L = xmalloc (sizeof (*L) - sizeof (L->data[0]) +
299                                 Count * sizeof (L->data[0]));
300     L->count = Count;
301     return L;
302 }
303
304
305
306 static cc65_sourceinfo* new_cc65_sourceinfo (unsigned Count)
307 /* Allocate and return a cc65_sourceinfo struct that is able to hold Count
308  * entries. Initialize the count field of the returned struct.
309  */
310 {
311     cc65_sourceinfo* S = xmalloc (sizeof (*S) - sizeof (S->data[0]) +
312                                   Count * sizeof (S->data[0]));
313     S->count = Count;
314     return S;
315 }
316
317
318
319 static cc65_segmentinfo* new_cc65_segmentinfo (unsigned Count)
320 /* Allocate and return a cc65_segmentinfo struct that is able to hold Count
321  * entries. Initialize the count field of the returned struct.
322  */
323 {
324     cc65_segmentinfo* S = xmalloc (sizeof (*S) - sizeof (S->data[0]) +
325                                    Count * sizeof (S->data[0]));
326     S->count = Count;
327     return S;
328 }
329
330
331
332 static cc65_symbolinfo* new_cc65_symbolinfo (unsigned Count)
333 /* Allocate and return a cc65_symbolinfo struct that is able to hold Count
334  * entries. Initialize the count field of the returned struct.
335  */
336 {
337     cc65_symbolinfo* S = xmalloc (sizeof (*S) - sizeof (S->data[0]) +
338                                   Count * sizeof (S->data[0]));
339     S->count = Count;
340     return S;
341 }
342
343
344
345 /*****************************************************************************/
346 /*                              Dynamic strings                              */
347 /*****************************************************************************/
348
349
350
351 static void SB_Done (StrBuf* B)
352 /* Free the data of a string buffer (but not the struct itself) */
353 {
354     if (B->Allocated) {
355         xfree (B->Buf);
356     }
357 }
358
359
360
361 static void SB_Realloc (StrBuf* B, unsigned NewSize)
362 /* Reallocate the string buffer space, make sure at least NewSize bytes are
363  * available.
364  */
365 {
366     /* Get the current size, use a minimum of 8 bytes */
367     unsigned NewAllocated = B->Allocated;
368     if (NewAllocated == 0) {
369         NewAllocated = 8;
370     }
371
372     /* Round up to the next power of two */
373     while (NewAllocated < NewSize) {
374         NewAllocated *= 2;
375     }
376
377     /* Reallocate the buffer. Beware: The allocated size may be zero while the
378      * length is not. This means that we have a buffer that wasn't allocated
379      * on the heap.
380      */
381     if (B->Allocated) {
382         /* Just reallocate the block */
383         B->Buf   = xrealloc (B->Buf, NewAllocated);
384     } else {
385         /* Allocate a new block and copy */
386         B->Buf   = memcpy (xmalloc (NewAllocated), B->Buf, B->Len);
387     }
388
389     /* Remember the new block size */
390     B->Allocated = NewAllocated;
391 }
392
393
394
395 static void SB_CheapRealloc (StrBuf* B, unsigned NewSize)
396 /* Reallocate the string buffer space, make sure at least NewSize bytes are
397  * available. This function won't copy the old buffer contents over to the new
398  * buffer and may be used if the old contents are overwritten later.
399  */
400 {
401     /* Get the current size, use a minimum of 8 bytes */
402     unsigned NewAllocated = B->Allocated;
403     if (NewAllocated == 0) {
404         NewAllocated = 8;
405     }
406
407     /* Round up to the next power of two */
408     while (NewAllocated < NewSize) {
409         NewAllocated *= 2;
410     }
411
412     /* Free the old buffer if there is one */
413     if (B->Allocated) {
414         xfree (B->Buf);
415     }
416
417     /* Allocate a fresh block */
418     B->Buf = xmalloc (NewAllocated);
419
420     /* Remember the new block size */
421     B->Allocated = NewAllocated;
422 }
423
424
425
426 static unsigned SB_GetLen (const StrBuf* B)
427 /* Return the length of the buffer contents */
428 {
429     return B->Len;
430 }
431
432
433
434 static const char* SB_GetConstBuf (const StrBuf* B)
435 /* Return a buffer pointer */
436 {
437     return B->Buf;
438 }
439
440
441
442 static void SB_Terminate (StrBuf* B)
443 /* Zero terminate the given string buffer. NOTE: The terminating zero is not
444  * accounted for in B->Len, if you want that, you have to use AppendChar!
445  */
446 {
447     unsigned NewLen = B->Len + 1;
448     if (NewLen > B->Allocated) {
449         SB_Realloc (B, NewLen);
450     }
451     B->Buf[B->Len] = '\0';
452 }
453
454
455
456 static void SB_Clear (StrBuf* B)
457 /* Clear the string buffer (make it empty) */
458 {
459     B->Len = 0;
460 }
461
462
463
464 static void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size)
465 /* Copy Buf to Target, discarding the old contents of Target */
466 {
467     if (Size) {
468         if (Target->Allocated < Size) {
469             SB_CheapRealloc (Target, Size);
470         }
471         memcpy (Target->Buf, Buf, Size);
472     }
473     Target->Len = Size;
474 }
475
476
477
478 static void SB_Copy (StrBuf* Target, const StrBuf* Source)
479 /* Copy Source to Target, discarding the old contents of Target */
480 {
481     SB_CopyBuf (Target, Source->Buf, Source->Len);
482 }
483
484
485
486 static void SB_AppendChar (StrBuf* B, int C)
487 /* Append a character to a string buffer */
488 {
489     unsigned NewLen = B->Len + 1;
490     if (NewLen > B->Allocated) {
491         SB_Realloc (B, NewLen);
492     }
493     B->Buf[B->Len] = (char) C;
494     B->Len = NewLen;
495 }
496
497
498
499 static char* SB_StrDup (const StrBuf* B)
500 /* Return the contents of B as a dynamically allocated string. The string
501  * will always be NUL terminated.
502  */
503 {
504     /* Allocate memory */
505     char* S = xmalloc (B->Len + 1);
506
507     /* Copy the string */
508     memcpy (S, B->Buf, B->Len);
509
510     /* Terminate it */
511     S[B->Len] = '\0';
512
513     /* And return the result */
514     return S;
515 }
516
517
518
519 /*****************************************************************************/
520 /*                                Collections                                */
521 /*****************************************************************************/
522
523
524
525 static Collection* InitCollection (Collection* C)
526 /* Initialize a collection and return it. */
527 {
528     /* Intialize the fields. */
529     C->Count = 0;
530     C->Size  = 0;
531     C->Items = 0;
532
533     /* Return the new struct */
534     return C;
535 }
536
537
538
539 static void DoneCollection (Collection* C)
540 /* Free the data for a collection. This will not free the data contained in
541  * the collection.
542  */
543 {
544     /* Free the pointer array */
545     xfree (C->Items);
546
547     /* Clear the fields, so the collection may be reused (or DoneCollection
548      * called) again
549      */
550     C->Count = 0;
551     C->Size  = 0;
552     C->Items = 0;
553 }
554
555
556
557 static unsigned CollCount (const Collection* C)
558 /* Return the number of items in the collection */
559 {
560     return C->Count;
561 }
562
563
564
565 static void CollGrow (Collection* C, unsigned Size)
566 /* Grow the collection C so it is able to hold Size items without a resize
567  * being necessary. This can be called for performance reasons if the number
568  * of items to be placed in the collection is known in advance.
569  */
570 {
571     void** NewItems;
572
573     /* Ignore the call if the collection is already large enough */
574     if (Size <= C->Size) {
575         return;
576     }
577
578     /* Grow the collection */
579     C->Size = Size;
580     NewItems = xmalloc (C->Size * sizeof (void*));
581     memcpy (NewItems, C->Items, C->Count * sizeof (void*));
582     xfree (C->Items);
583     C->Items = NewItems;
584 }
585
586
587
588 static void CollInsert (Collection* C, void* Item, unsigned Index)
589 /* Insert the data at the given position in the collection */
590 {
591     /* Check for invalid indices */
592     assert (Index <= C->Count);
593
594     /* Grow the array if necessary */
595     if (C->Count >= C->Size) {
596         /* Must grow */
597         CollGrow (C, (C->Size == 0)? 8 : C->Size * 2);
598     }
599
600     /* Move the existing elements if needed */
601     if (C->Count != Index) {
602         memmove (C->Items+Index+1, C->Items+Index, (C->Count-Index) * sizeof (void*));
603     }
604     ++C->Count;
605
606     /* Store the new item */
607     C->Items[Index] = Item;
608 }
609
610
611
612 static void CollAppend (Collection* C, void* Item)
613 /* Append an item to the end of the collection */
614 {
615     /* Insert the item at the end of the current list */
616     CollInsert (C, Item, C->Count);
617 }
618
619
620
621 static void* CollAt (Collection* C, unsigned Index)
622 /* Return the item at the given index */
623 {
624     /* Check the index */
625     assert (Index < C->Count);
626
627     /* Return the element */
628     return C->Items[Index];
629 }
630
631
632
633 static void* CollFirst (Collection* C)
634 /* Return the first item in a collection */
635 {
636     /* We must have at least one entry */
637     assert (C->Count > 0);
638
639     /* Return the element */
640     return C->Items[0];
641 }
642
643
644
645 static void CollDelete (Collection* C, unsigned Index)
646 /* Remove the item with the given index from the collection. This will not
647  * free the item itself, just the pointer. All items with higher indices
648  * will get moved to a lower position.
649  */
650 {
651     /* Check the index */
652     assert (Index < C->Count);
653
654     /* Remove the item pointer */
655     --C->Count;
656     memmove (C->Items+Index, C->Items+Index+1, (C->Count-Index) * sizeof (void*));
657 }
658
659
660
661 static void CollQuickSort (Collection* C, int Lo, int Hi,
662                            int (*Compare) (const void*, const void*))
663 /* Internal recursive sort function. */
664 {
665     /* Get a pointer to the items */
666     void** Items = C->Items;
667
668     /* Quicksort */
669     while (Hi > Lo) {
670         int I = Lo + 1;
671         int J = Hi;
672         while (I <= J) {
673             while (I <= J && Compare (Items[Lo], Items[I]) >= 0) {
674                 ++I;
675             }
676             while (I <= J && Compare (Items[Lo], Items[J]) < 0) {
677                 --J;
678             }
679             if (I <= J) {
680                 /* Swap I and J */
681                 void* Tmp = Items[I];
682                 Items[I]  = Items[J];
683                 Items[J]  = Tmp;
684                 ++I;
685                 --J;
686             }
687         }
688         if (J != Lo) {
689             /* Swap J and Lo */
690             void* Tmp = Items[J];
691             Items[J]  = Items[Lo];
692             Items[Lo] = Tmp;
693         }
694         if (J > (Hi + Lo) / 2) {
695             CollQuickSort (C, J + 1, Hi, Compare);
696             Hi = J - 1;
697         } else {
698             CollQuickSort (C, Lo, J - 1, Compare);
699             Lo = J + 1;
700         }
701     }
702 }
703
704
705
706 void CollSort (Collection* C, int (*Compare) (const void*, const void*))
707 /* Sort the collection using the given compare function. */
708 {
709     if (C->Count > 1) {
710         CollQuickSort (C, 0, C->Count-1, Compare);
711     }
712 }
713
714
715
716 /*****************************************************************************/
717 /*                              Debugging stuff                              */
718 /*****************************************************************************/
719
720
721
722 #if DEBUG
723
724 /* Output */
725 #define DBGPRINT(format, ...)   printf ((format), __VA_ARGS__)
726
727
728
729 static void DumpFileInfo (Collection* FileInfos)
730 /* Dump a list of file infos */
731 {
732     unsigned I;
733
734     /* File info */
735     for (I = 0; I < CollCount (FileInfos); ++I) {
736         const FileInfo* FI = CollAt (FileInfos, I);
737         printf ("File info %u:\n"
738                 "  Name:   %s\n"
739                 "  Size:   %lu\n"
740                 "  MTime:  %lu\n",
741                 FI->Id,
742                 FI->FileName,
743                 (unsigned long) FI->Size,
744                 (unsigned long) FI->MTime);
745     }
746 }
747
748
749
750 static void DumpOneLineInfo (unsigned Num, LineInfo* LI)
751 /* Dump one line info entry */
752 {
753     printf ("  Index:  %u\n"
754             "    File:   %s\n"
755             "    Line:   %lu\n"
756             "    Range:  0x%06lX-0x%06lX\n"
757             "    Type:   %u\n"
758             "    Count:  %u\n",
759             Num,
760             LI->File.Info->FileName,
761             (unsigned long) LI->Line,
762             (unsigned long) LI->Start,
763             (unsigned long) LI->End,
764             LI->Type,
765             LI->Count);
766 }
767
768
769
770 static void DumpLineInfo (LineInfoList* L)
771 /* Dump a list of line infos */
772 {
773     unsigned I, J;
774
775     /* Line info */
776     for (I = 0; I < L->Count; ++I) {
777         const LineInfoListEntry* E = &L->List[I];
778         printf ("Addr:   %lu\n", (unsigned long) E->Addr);
779         if (E->Count == 1) {
780             DumpOneLineInfo (0, E->Data);
781         } else {
782             for (J = 0; J < E->Count; ++J) {
783                 DumpOneLineInfo (J, ((LineInfo**) E->Data)[J]);
784             }
785         }
786     }
787 }
788
789
790
791 static void DumpData (InputData* D)
792 /* Dump internal data to stdout for debugging */
793 {
794     /* Dump data */
795     DumpFileInfo (&D->Info->FileInfoById);
796     DumpLineInfo (&D->Info->LineInfoByAddr);
797 }
798 #else
799
800 /* No output */
801 #define DBGPRINT(format, ...)
802
803
804
805 #endif
806
807
808
809 /*****************************************************************************/
810 /*                               Segment info                                */
811 /*****************************************************************************/
812
813
814
815 static SegInfo* NewSegInfo (const StrBuf* SegName, unsigned Id,
816                             cc65_addr Start, cc65_addr Size,
817                             const StrBuf* OutputName, unsigned long OutputOffs)
818 /* Create a new SegInfo struct and return it */
819 {
820     /* Allocate memory */
821     SegInfo* S = xmalloc (sizeof (SegInfo) + SB_GetLen (SegName));
822
823     /* Initialize it */
824     S->Id         = Id;
825     S->Start      = Start;
826     S->Size       = Size;
827     if (SB_GetLen (OutputName) > 0) {
828         /* Output file given */
829         S->OutputName = SB_StrDup (OutputName);
830         S->OutputOffs = OutputOffs;
831     } else {
832         /* No output file given */
833         S->OutputName = 0;
834         S->OutputOffs = 0;
835     }
836     memcpy (S->SegName, SB_GetConstBuf (SegName), SB_GetLen (SegName) + 1);
837
838     /* Return it */
839     return S;
840 }
841
842
843
844 static void FreeSegInfo (SegInfo* S)
845 /* Free a SegInfo struct */
846 {
847     xfree (S->OutputName);
848     xfree (S);
849 }
850
851
852
853 static int CompareSegInfoByName (const void* L, const void* R)
854 /* Helper function to sort segment infos in a collection by name */
855 {
856     /* Sort by file name */
857     return strcmp (((const SegInfo*) L)->SegName,
858                    ((const SegInfo*) R)->SegName);
859 }
860
861
862
863 static int CompareSegInfoById (const void* L, const void* R)
864 /* Helper function to sort segment infos in a collection by id */
865 {
866     if (((const SegInfo*) L)->Id > ((const SegInfo*) R)->Id) {
867         return 1;
868     } else if (((const SegInfo*) L)->Id < ((const SegInfo*) R)->Id) {
869         return -1;
870     } else {
871         return 0;
872     }
873 }
874
875
876
877 /*****************************************************************************/
878 /*                                 Line info                                 */
879 /*****************************************************************************/
880
881
882
883 static LineInfo* NewLineInfo (unsigned File, unsigned Seg, cc65_line Line,
884                               cc65_addr Start, cc65_addr End,
885                               cc65_line_type Type, unsigned Count)
886 /* Create a new LineInfo struct and return it */
887 {
888     /* Allocate memory */
889     LineInfo* L = xmalloc (sizeof (LineInfo));
890
891     /* Initialize it */
892     L->Start    = Start;
893     L->End      = End;
894     L->Line     = Line;
895     L->File.Id  = File;
896     L->Seg.Id   = Seg;
897     L->Type     = Type;
898     L->Count    = Count;
899
900     /* Return it */
901     return L;
902 }
903
904
905
906 static void FreeLineInfo (LineInfo* L)
907 /* Free a LineInfo struct */
908 {
909     xfree (L);
910 }
911
912
913
914 static int CompareLineInfoByAddr (const void* L, const void* R)
915 /* Helper function to sort line infos in a collection by address. Line infos
916  * with smaller start address are considered smaller. If start addresses are
917  * equal, line infos with smaller end address are considered smaller. This
918  * means, that when CompareLineInfoByAddr is used for sorting, a range with
919  * identical start addresses will have smaller ranges first, followed by
920  * larger ranges.
921  */
922 {
923     /* Sort by start of range */
924     if (((const LineInfo*) L)->Start > ((const LineInfo*) R)->Start) {
925         return 1;
926     } else if (((const LineInfo*) L)->Start < ((const LineInfo*) R)->Start) {
927         return -1;
928     } else if (((const LineInfo*) L)->End > ((const LineInfo*) R)->End) {
929         return 1;
930     } else if (((const LineInfo*) L)->End < ((const LineInfo*) R)->End) {
931         return -1;
932     } else {
933         return 0;
934     }
935 }
936
937
938
939 static int CompareLineInfoByLine (const void* L, const void* R)
940 /* Helper function to sort line infos in a collection by line. If the line
941  * is identical, sort by the address of the range.
942  */
943 {
944     if (((const LineInfo*) L)->Line > ((const LineInfo*) R)->Line) {
945         return 1;
946     } else if (((const LineInfo*) L)->Line < ((const LineInfo*) R)->Line) {
947         return -1;
948     } else {
949         return CompareLineInfoByAddr (L, R);
950     }
951 }
952
953
954
955 /*****************************************************************************/
956 /*                                 File info                                 */
957 /*****************************************************************************/
958
959
960
961 static FileInfo* NewFileInfo (const StrBuf* FileName, unsigned Id,
962                               unsigned long Size, unsigned long MTime)
963 /* Create a new FileInfo struct and return it */
964 {
965     /* Allocate memory */
966     FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
967
968     /* Initialize it */
969     F->Id    = Id;
970     F->Size  = Size;
971     F->MTime = MTime;
972     InitCollection (&F->LineInfoByLine);
973     memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
974
975     /* Return it */
976     return F;
977 }
978
979
980
981 static void FreeFileInfo (FileInfo* F)
982 /* Free a FileInfo struct */
983 {
984     /* Delete the collection with the line infos */
985     DoneCollection (&F->LineInfoByLine);
986
987     /* Free the file info structure itself */
988     xfree (F);
989 }
990
991
992
993 static int CompareFileInfoByName (const void* L, const void* R)
994 /* Helper function to sort file infos in a collection by name */
995 {
996     /* Sort by file name. If names are equal, sort by timestamp,
997      * then sort by size. Which means, identical files will go
998      * together.
999      */
1000     int Res = strcmp (((const FileInfo*) L)->FileName,
1001                       ((const FileInfo*) R)->FileName);
1002     if (Res != 0) {
1003         return Res;
1004     }
1005     if (((const FileInfo*) L)->MTime > ((const FileInfo*) R)->MTime) {
1006         return 1;
1007     } else if (((const FileInfo*) L)->MTime < ((const FileInfo*) R)->MTime) {
1008         return -1;
1009     }
1010     if (((const FileInfo*) L)->Size > ((const FileInfo*) R)->Size) {
1011         return 1;
1012     } else if (((const FileInfo*) L)->Size < ((const FileInfo*) R)->Size) {
1013         return -1;
1014     } else {
1015         return 0;
1016     }
1017 }
1018
1019
1020
1021 static int CompareFileInfoById (const void* L, const void* R)
1022 /* Helper function to sort file infos in a collection by id */
1023 {
1024     if (((const FileInfo*) L)->Id > ((const FileInfo*) R)->Id) {
1025         return 1;
1026     } else if (((const FileInfo*) L)->Id < ((const FileInfo*) R)->Id) {
1027         return -1;
1028     } else {
1029         return 0;
1030     }
1031 }
1032
1033
1034
1035 /*****************************************************************************/
1036 /*                                Symbol info                                */
1037 /*****************************************************************************/
1038
1039
1040
1041 static SymInfo* NewSymInfo (const StrBuf* Name, long Val,
1042                             cc65_symbol_type Type, cc65_size Size)
1043 /* Create a new SymInfo struct, intialize and return it */
1044 {
1045     /* Allocate memory */
1046     SymInfo* S = xmalloc (sizeof (SymInfo) + SB_GetLen (Name));
1047
1048     /* Initialize it */
1049     S->Type  = Type;
1050     S->Value = Val;
1051     S->Size  = Size;
1052     memcpy (S->SymName, SB_GetConstBuf (Name), SB_GetLen (Name) + 1);
1053
1054     /* Return it */
1055     return S;
1056 }
1057
1058
1059
1060 static void FreeSymInfo (SymInfo* S)
1061 /* Free a SymInfo struct */
1062 {
1063     xfree (S);
1064 }
1065
1066
1067
1068 static int CompareSymInfoByName (const void* L, const void* R)
1069 /* Helper function to sort symbol infos in a collection by name */
1070 {
1071     /* Sort by symbol name */
1072     return strcmp (((const SymInfo*) L)->SymName,
1073                    ((const SymInfo*) R)->SymName);
1074 }
1075
1076
1077
1078 static int CompareSymInfoByVal (const void* L, const void* R)
1079 /* Helper function to sort symbol infos in a collection by value */
1080 {
1081     /* Sort by symbol value. If both are equal, sort by symbol name so it
1082      * looks nice when such a list is returned.
1083      */
1084     if (((const SymInfo*) L)->Value > ((const SymInfo*) R)->Value) {
1085         return 1;
1086     } else if (((const SymInfo*) L)->Value < ((const SymInfo*) R)->Value) {
1087         return -1;
1088     } else {
1089         return CompareSymInfoByName (L, R);
1090     }
1091 }
1092
1093
1094
1095 /*****************************************************************************/
1096 /*                               LineInfoList                                */
1097 /*****************************************************************************/
1098
1099
1100
1101 static void InitLineInfoList (LineInfoList* L)
1102 /* Initialize a line info list */
1103 {
1104     L->Count = 0;
1105     L->List  = 0;
1106 }
1107
1108
1109
1110 static void CreateLineInfoList (LineInfoList* L, Collection* LineInfos)
1111 /* Create a LineInfoList from a Collection with line infos. The collection
1112  * must be sorted by ascending start addresses.
1113  */
1114 {
1115     unsigned I, J;
1116     LineInfo* LI;
1117     LineInfoListEntry* List;
1118     unsigned StartIndex;
1119     cc65_addr Start;
1120     cc65_addr Addr;
1121     cc65_addr End;
1122
1123     /* Initialize and check if there's something to do */
1124     L->Count = 0;
1125     L->List  = 0;
1126     if (CollCount (LineInfos) == 0) {
1127         /* No entries */
1128         return;
1129     }
1130
1131     /* Step 1: Determine the number of unique address entries needed */
1132     LI = CollFirst (LineInfos);
1133     L->Count += (LI->End - LI->Start) + 1;
1134     End = LI->End;
1135     for (I = 1; I < CollCount (LineInfos); ++I) {
1136
1137         /* Get next entry */
1138         LI = CollAt (LineInfos, I);
1139
1140         /* Check for additional unique addresses in this line info */
1141         if (LI->Start > End) {
1142             L->Count += (LI->End - LI->Start) + 1;
1143             End = LI->End;
1144         } else if (LI->End > End) {
1145             L->Count += (LI->End - End);
1146             End = LI->End;
1147         }
1148
1149     }
1150
1151     /* Step 2: Allocate memory and initialize it */
1152     L->List = List = xmalloc (L->Count * sizeof (*List));
1153     for (I = 0; I < L->Count; ++I) {
1154         List[I].Count = 0;
1155         List[I].Data  = 0;
1156     }
1157
1158     /* Step 3: Determine the number of entries per unique address */
1159     List = L->List;
1160     LI = CollFirst (LineInfos);
1161     StartIndex = 0;
1162     Start = LI->Start;
1163     End = LI->End;
1164     for (J = StartIndex, Addr = LI->Start; Addr <= LI->End; ++J, ++Addr) {
1165         List[J].Addr = Addr;
1166         ++List[J].Count;
1167     }
1168     for (I = 1; I < CollCount (LineInfos); ++I) {
1169
1170         /* Get next entry */
1171         LI = CollAt (LineInfos, I);
1172
1173         /* Determine the start index of the next range. Line infos are sorted
1174          * by ascending start address, so the start address of the next entry
1175          * is always larger than the previous one - we don't need to check
1176          * that.
1177          */
1178         if (LI->Start <= End) {
1179             /* Range starts within out already known linear range */
1180             StartIndex += (unsigned) (LI->Start - Start);
1181             Start = LI->Start;
1182             if (LI->End > End) {
1183                 End = LI->End;
1184             }
1185         } else {
1186             /* Range starts after the already known */
1187             StartIndex += (unsigned) (End - Start) + 1;
1188             Start = LI->Start;
1189             End = LI->End;
1190         }
1191         for (J = StartIndex, Addr = LI->Start; Addr <= LI->End; ++J, ++Addr) {
1192             List[J].Addr = Addr;
1193             ++List[J].Count;
1194         }
1195     }
1196
1197     /* Step 4: Allocate memory for the indirect tables */
1198     for (I = 0, List = L->List; I < L->Count; ++I, ++List) {
1199
1200         /* For a count of 1, we store the pointer to the lineinfo for this
1201          * address in the Data pointer directly. For counts > 1, we allocate
1202          * an array of pointers and reset the counter, so we can use it as
1203          * an index later. This is dangerous programming since it disables
1204          * all possible checks!
1205          */
1206         if (List->Count > 1) {
1207             List->Data = xmalloc (List->Count * sizeof (LineInfo*));
1208             List->Count = 0;
1209         }
1210     }
1211
1212     /* Step 5: Enter the data into the table */
1213     List = L->List;
1214     LI = CollFirst (LineInfos);
1215     StartIndex = 0;
1216     Start = LI->Start;
1217     End = LI->End;
1218     for (J = StartIndex, Addr = LI->Start; Addr <= LI->End; ++J, ++Addr) {
1219         assert (List[J].Addr == Addr);
1220         if (List[J].Count == 1 && List[J].Data == 0) {
1221             List[J].Data = LI;
1222         } else {
1223             ((LineInfo**) List[J].Data)[List[J].Count++] = LI;
1224         }
1225     }
1226     for (I = 1; I < CollCount (LineInfos); ++I) {
1227
1228         /* Get next entry */
1229         LI = CollAt (LineInfos, I);
1230
1231         /* Determine the start index of the next range. Line infos are sorted
1232          * by ascending start address, so the start address of the next entry
1233          * is always larger than the previous one - we don't need to check
1234          * that.
1235          */
1236         if (LI->Start <= End) {
1237             /* Range starts within out already known linear range */
1238             StartIndex += (unsigned) (LI->Start - Start);
1239             Start = LI->Start;
1240             if (LI->End > End) {
1241                 End = LI->End;
1242             }
1243         } else {
1244             /* Range starts after the already known */
1245             StartIndex += (unsigned) (End - Start) + 1;
1246             Start = LI->Start;
1247             End = LI->End;
1248         }
1249         for (J = StartIndex, Addr = LI->Start; Addr <= LI->End; ++J, ++Addr) {
1250             assert (List[J].Addr == Addr);
1251             if (List[J].Count == 1 && List[J].Data == 0) {
1252                 List[J].Data = LI;
1253             } else {
1254                 ((LineInfo**) List[J].Data)[List[J].Count++] = LI;
1255             }
1256         }
1257     }
1258 }
1259
1260
1261
1262 static void DoneLineInfoList (LineInfoList* L)
1263 /* Delete the contents of a line info list */
1264 {
1265     unsigned I;
1266
1267     /* Delete the line info and the indirect data */
1268     for (I = 0; I < L->Count; ++I) {
1269
1270         /* Get a pointer to the entry */
1271         LineInfoListEntry* E = &L->List[I];
1272
1273         /* Check for indirect memory */
1274         if (E->Count > 1) {
1275             /* LineInfo addressed indirectly */
1276             xfree (E->Data);
1277         }
1278     }
1279
1280     /* Delete the list */
1281     xfree (L->List);
1282 }
1283
1284
1285
1286 /*****************************************************************************/
1287 /*                                Debug info                                 */
1288 /*****************************************************************************/
1289
1290
1291
1292 static DbgInfo* NewDbgInfo (void)
1293 /* Create a new DbgInfo struct and return it */
1294 {
1295     /* Allocate memory */
1296     DbgInfo* Info = xmalloc (sizeof (DbgInfo));
1297
1298     /* Initialize it */
1299     InitCollection (&Info->SegInfoByName);
1300     InitCollection (&Info->SegInfoById);
1301     InitCollection (&Info->FileInfoByName);
1302     InitCollection (&Info->FileInfoById);
1303     InitCollection (&Info->LineInfos);
1304     InitLineInfoList (&Info->LineInfoByAddr);
1305     InitCollection (&Info->SymInfoByName);
1306     InitCollection (&Info->SymInfoByVal);
1307
1308     /* Return it */
1309     return Info;
1310 }
1311
1312
1313
1314 static void FreeDbgInfo (DbgInfo* Info)
1315 /* Free a DbgInfo struct */
1316 {
1317     unsigned I;
1318
1319     /* Free segment info */
1320     for (I = 0; I < CollCount (&Info->SegInfoByName); ++I) {
1321         FreeSegInfo (CollAt (&Info->SegInfoByName, I));
1322     }
1323     DoneCollection (&Info->SegInfoByName);
1324     DoneCollection (&Info->SegInfoById);
1325
1326     /* Free file info */
1327     for (I = 0; I < CollCount (&Info->FileInfoByName); ++I) {
1328         FreeFileInfo (CollAt (&Info->FileInfoByName, I));
1329     }
1330     DoneCollection (&Info->FileInfoByName);
1331     DoneCollection (&Info->FileInfoById);
1332
1333     /* Free line info */
1334     for (I = 0; I < CollCount (&Info->LineInfos); ++I) {
1335         FreeLineInfo (CollAt (&Info->LineInfos, I));
1336     }
1337     DoneCollection (&Info->LineInfos);
1338     DoneLineInfoList (&Info->LineInfoByAddr);
1339
1340     /* Free symbol info */
1341     for (I = 0; I < CollCount (&Info->SymInfoByName); ++I) {
1342         FreeSymInfo (CollAt (&Info->SymInfoByName, I));
1343     }
1344     DoneCollection (&Info->SymInfoByName);
1345     DoneCollection (&Info->SymInfoByVal);
1346
1347     /* Free the structure itself */
1348     xfree (Info);
1349 }
1350
1351
1352
1353 /*****************************************************************************/
1354 /*                             Helper functions                              */
1355 /*****************************************************************************/
1356
1357
1358
1359 static void CopyLineInfo (cc65_linedata* D, const LineInfo* L)
1360 /* Copy data from a LineInfo struct to the cc65_linedata struct returned to
1361  * the caller.
1362  */
1363 {
1364     D->source_name  = L->File.Info->FileName;
1365     D->source_size  = L->File.Info->Size;
1366     D->source_mtime = L->File.Info->MTime;
1367     D->source_line  = L->Line;
1368     D->line_start   = L->Start;
1369     D->line_end     = L->End;
1370     if (L->Seg.Info->OutputName) {
1371         D->output_name  = L->Seg.Info->OutputName;
1372         D->output_offs  = L->Seg.Info->OutputOffs + L->Start - L->Seg.Info->Start;
1373     } else {
1374         D->output_name  = 0;
1375         D->output_offs  = 0;
1376     }
1377     D->line_type    = L->Type;
1378     D->count        = L->Count;
1379 }
1380
1381
1382
1383 static void CopySymInfo (cc65_symboldata* D, const SymInfo* S)
1384 /* Copy data from a SymInfo struct to the cc65_symboldata struct returned to
1385  * the caller.
1386  */
1387 {
1388     D->symbol_name  = S->SymName;
1389     D->symbol_type  = S->Type;
1390     D->symbol_size  = S->Size;
1391     D->symbol_value = S->Value;
1392 }
1393
1394
1395
1396 static void ParseError (InputData* D, cc65_error_severity Type, const char* Msg, ...)
1397 /* Call the user supplied parse error function */
1398 {
1399     va_list             ap;
1400     int                 MsgSize;
1401     cc65_parseerror*    E;
1402
1403     /* Test-format the error message so we know how much space to allocate */
1404     va_start (ap, Msg);
1405     MsgSize = vsnprintf (0, 0, Msg, ap);
1406     va_end (ap);
1407
1408     /* Allocate memory */
1409     E = xmalloc (sizeof (*E) + MsgSize);
1410
1411     /* Write data to E */
1412     E->type   = Type;
1413     E->name   = D->FileName;
1414     E->line   = D->SLine;
1415     E->column = D->SCol;
1416     va_start (ap, Msg);
1417     vsnprintf (E->errormsg, MsgSize+1, Msg, ap);
1418     va_end (ap);
1419
1420     /* Call the caller:-) */
1421     D->Error (E);
1422
1423     /* Free the data structure */
1424     xfree (E);
1425
1426     /* Count errors */
1427     if (Type == CC65_ERROR) {
1428         ++D->Errors;
1429     }
1430 }
1431
1432
1433
1434 static void SkipLine (InputData* D)
1435 /* Error recovery routine. Skip tokens until EOL or EOF is reached */
1436 {
1437     while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1438         NextToken (D);
1439     }
1440 }
1441
1442
1443
1444 static void UnexpectedToken (InputData* D)
1445 /* Call ParseError with a message about an unexpected input token */
1446 {
1447     ParseError (D, CC65_ERROR, "Unexpected input token %d", D->Tok);
1448     SkipLine (D);
1449 }
1450
1451
1452
1453 static void UnknownKeyword (InputData* D)
1454 /* Print a warning about an unknown keyword in the file. Try to do smart
1455  * recovery, so if later versions of the debug information add additional
1456  * keywords, this code may be able to at least ignore them.
1457  */
1458 {
1459     /* Output a warning */
1460     ParseError (D, CC65_WARNING, "Unknown keyword \"%s\" - skipping",
1461                 SB_GetConstBuf (&D->SVal));
1462
1463     /* Skip the identifier */
1464     NextToken (D);
1465
1466     /* If an equal sign follows, ignore anything up to the next line end
1467      * or comma. If a comma or line end follows, we're already done. If
1468      * we have none of both, we ignore the remainder of the line.
1469      */
1470     if (D->Tok == TOK_EQUAL) {
1471         NextToken (D);
1472         while (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1473             NextToken (D);
1474         }
1475     } else if (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1476         SkipLine (D);
1477     }
1478 }
1479
1480
1481
1482 /*****************************************************************************/
1483 /*                            Scanner and parser                             */
1484 /*****************************************************************************/
1485
1486
1487
1488 static int DigitVal (int C)
1489 /* Return the value for a numeric digit. Return -1 if C is invalid */
1490 {
1491     if (isdigit (C)) {
1492         return C - '0';
1493     } else if (isxdigit (C)) {
1494         return toupper (C) - 'A' + 10;
1495     } else {
1496         return -1;
1497     }
1498 }
1499
1500
1501
1502 static void NextChar (InputData* D)
1503 /* Read the next character from the input. Count lines and columns */
1504 {
1505     /* Check if we've encountered EOF before */
1506     if (D->C >= 0) {
1507         D->C = fgetc (D->F);
1508         if (D->C == '\n') {
1509             ++D->Line;
1510             D->Col = 0;
1511         } else {
1512             ++D->Col;
1513         }
1514     }
1515 }
1516
1517
1518
1519 static void NextToken (InputData* D)
1520 /* Read the next token from the input stream */
1521 {
1522     static const struct KeywordEntry  {
1523         const char      Keyword[16];
1524         Token           Tok;
1525     } KeywordTable[] = {
1526         { "absolute",   TOK_ABSOLUTE    },
1527         { "addrsize",   TOK_ADDRSIZE    },
1528         { "count",      TOK_COUNT       },
1529         { "equate",     TOK_EQUATE      },
1530         { "file",       TOK_FILE        },
1531         { "id",         TOK_ID          },
1532         { "label",      TOK_LABEL       },
1533         { "line",       TOK_LINE        },
1534         { "long",       TOK_LONG        },
1535         { "major",      TOK_MAJOR       },
1536         { "minor",      TOK_MINOR       },
1537         { "mtime",      TOK_MTIME       },
1538         { "name",       TOK_NAME        },
1539         { "outputname", TOK_OUTPUTNAME  },
1540         { "outputoffs", TOK_OUTPUTOFFS  },
1541         { "range",      TOK_RANGE       },
1542         { "ro",         TOK_RO          },
1543         { "rw",         TOK_RW          },
1544         { "seg",        TOK_SEGMENT     },
1545         { "segment",    TOK_SEGMENT     },
1546         { "size",       TOK_SIZE        },
1547         { "start",      TOK_START       },
1548         { "sym",        TOK_SYM         },
1549         { "type",       TOK_TYPE        },
1550         { "value",      TOK_VALUE       },
1551         { "version",    TOK_VERSION     },
1552         { "zeropage",   TOK_ZEROPAGE    },
1553     };
1554
1555
1556     /* Skip whitespace */
1557     while (D->C == ' ' || D->C == '\t' || D->C == '\r') {
1558         NextChar (D);
1559     }
1560
1561     /* Remember the current position as start of the next token */
1562     D->SLine = D->Line;
1563     D->SCol  = D->Col;
1564
1565     /* Identifier? */
1566     if (D->C == '_' || isalpha (D->C)) {
1567
1568         const struct KeywordEntry* Entry;
1569
1570         /* Read the identifier */
1571         SB_Clear (&D->SVal);
1572         while (D->C == '_' || isalnum (D->C)) {
1573             SB_AppendChar (&D->SVal, D->C);
1574             NextChar (D);
1575         }
1576         SB_Terminate (&D->SVal);
1577
1578         /* Search the identifier in the keyword table */
1579         Entry = bsearch (SB_GetConstBuf (&D->SVal),
1580                          KeywordTable,
1581                          sizeof (KeywordTable) / sizeof (KeywordTable[0]),
1582                          sizeof (KeywordTable[0]),
1583                          (int (*)(const void*, const void*)) strcmp);
1584         if (Entry == 0) {
1585             D->Tok = TOK_IDENT;
1586         } else {
1587             D->Tok = Entry->Tok;
1588         }
1589         return;
1590     }
1591
1592     /* Number? */
1593     if (isdigit (D->C)) {
1594         int Base = 10;
1595         int Val;
1596         if (D->C == '0') {
1597             NextChar (D);
1598             if (toupper (D->C) == 'X') {
1599                 NextChar (D);
1600                 Base = 16;
1601             } else {
1602                 Base = 8;
1603             }
1604         } else {
1605             Base = 10;
1606         }
1607         D->IVal = 0;
1608         while ((Val = DigitVal (D->C)) >= 0 && Val < Base) {
1609             D->IVal = D->IVal * Base + Val;
1610             NextChar (D);
1611         }
1612         D->Tok = TOK_INTCON;
1613         return;
1614     }
1615
1616     /* Other characters */
1617     switch (D->C) {
1618
1619         case '-':
1620             NextChar (D);
1621             D->Tok = TOK_MINUS;
1622             break;
1623
1624         case '+':
1625             NextChar (D);
1626             D->Tok = TOK_PLUS;
1627             break;
1628
1629         case ',':
1630             NextChar (D);
1631             D->Tok = TOK_COMMA;
1632             break;
1633
1634         case '=':
1635             NextChar (D);
1636             D->Tok = TOK_EQUAL;
1637             break;
1638
1639         case '\"':
1640             SB_Clear (&D->SVal);
1641             NextChar (D);
1642             while (1) {
1643                 if (D->C == '\n' || D->C == EOF) {
1644                     ParseError (D, CC65_ERROR, "Unterminated string constant");
1645                     break;
1646                 }
1647                 if (D->C == '\"') {
1648                     NextChar (D);
1649                     break;
1650                 }
1651                 SB_AppendChar (&D->SVal, D->C);
1652                 NextChar (D);
1653             }
1654             SB_Terminate (&D->SVal);
1655             D->Tok = TOK_STRCON;
1656             break;
1657
1658         case '\n':
1659             NextChar (D);
1660             D->Tok = TOK_EOL;
1661             break;
1662
1663         case EOF:
1664             D->Tok = TOK_EOF;
1665             break;
1666
1667         default:
1668             ParseError (D, CC65_ERROR, "Invalid input character `%c'", D->C);
1669
1670     }
1671 }
1672
1673
1674
1675 static int TokenIsKeyword (Token Tok)
1676 /* Return true if the given token is a keyword */
1677 {
1678     return (Tok >= TOK_FIRST_KEYWORD && Tok <= TOK_LAST_KEYWORD);
1679 }
1680
1681
1682
1683 static int TokenFollows (InputData* D, Token Tok, const char* Name)
1684 /* Check for a specific token that follows. */
1685 {
1686     if (D->Tok != Tok) {
1687         ParseError (D, CC65_ERROR, "%s expected", Name);
1688         SkipLine (D);
1689         return 0;
1690     } else {
1691         return 1;
1692     }
1693 }
1694
1695
1696
1697 static int IntConstFollows (InputData* D)
1698 /* Check for an integer constant */
1699 {
1700     return TokenFollows (D, TOK_INTCON, "Integer constant");
1701 }
1702
1703
1704
1705 static int StrConstFollows (InputData* D)
1706 /* Check for a string literal */
1707 {
1708     return TokenFollows (D, TOK_STRCON, "String literal");
1709 }
1710
1711
1712
1713 static int Consume (InputData* D, Token Tok, const char* Name)
1714 /* Check for a token and consume it. Return true if the token was comsumed,
1715  * return false otherwise.
1716  */
1717 {
1718     if (TokenFollows (D, Tok, Name)) {
1719         NextToken (D);
1720         return 1;
1721     } else {
1722         return 0;
1723     }
1724 }
1725
1726
1727
1728 static int ConsumeEqual (InputData* D)
1729 /* Consume an equal sign */
1730 {
1731     return Consume (D, TOK_EQUAL, "'='");
1732 }
1733
1734
1735
1736 static int ConsumeMinus (InputData* D)
1737 /* Consume a minus sign */
1738 {
1739     return Consume (D, TOK_MINUS, "'-'");
1740 }
1741
1742
1743
1744 static void ConsumeEOL (InputData* D)
1745 /* Consume an end-of-line token, if we aren't at end-of-file */
1746 {
1747     if (D->Tok != TOK_EOF) {
1748         if (D->Tok != TOK_EOL) {
1749             ParseError (D, CC65_ERROR, "Extra tokens in line");
1750             SkipLine (D);
1751         }
1752         NextToken (D);
1753     }
1754 }
1755
1756
1757
1758 static void ParseFile (InputData* D)
1759 /* Parse a FILE line */
1760 {
1761     unsigned      Id = 0;
1762     unsigned long Size = 0;
1763     unsigned long MTime = 0;
1764     StrBuf        FileName = STRBUF_INITIALIZER;
1765     FileInfo*     F;
1766     enum {
1767         ibNone      = 0x00,
1768         ibId        = 0x01,
1769         ibFileName  = 0x02,
1770         ibSize      = 0x04,
1771         ibMTime     = 0x08,
1772         ibRequired  = ibId | ibFileName | ibSize | ibMTime,
1773     } InfoBits = ibNone;
1774
1775     /* Skip the FILE token */
1776     NextToken (D);
1777
1778     /* More stuff follows */
1779     while (1) {
1780
1781         Token Tok;
1782
1783         /* Something we know? */
1784         if (D->Tok != TOK_ID   && D->Tok != TOK_MTIME &&
1785             D->Tok != TOK_NAME && D->Tok != TOK_SIZE) {
1786
1787             /* Try smart error recovery */
1788             if (D->Tok == TOK_IDENT || TokenIsKeyword (D->Tok)) {
1789                 UnknownKeyword (D);
1790                 continue;
1791             }
1792
1793             /* Done */
1794             break;
1795         }
1796
1797         /* Remember the token, skip it, check for equal */
1798         Tok = D->Tok;
1799         NextToken (D);
1800         if (!ConsumeEqual (D)) {
1801             goto ErrorExit;
1802         }
1803
1804         /* Check what the token was */
1805         switch (Tok) {
1806
1807             case TOK_ID:
1808                 if (!IntConstFollows (D)) {
1809                     goto ErrorExit;
1810                 }
1811                 Id = D->IVal;
1812                 InfoBits |= ibId;
1813                 NextToken (D);
1814                 break;
1815
1816             case TOK_MTIME:
1817                 if (!IntConstFollows (D)) {
1818                     goto ErrorExit;
1819                 }
1820                 MTime = D->IVal;
1821                 NextToken (D);
1822                 InfoBits |= ibMTime;
1823                 break;
1824
1825             case TOK_NAME:
1826                 if (!StrConstFollows (D)) {
1827                     goto ErrorExit;
1828                 }
1829                 SB_Copy (&FileName, &D->SVal);
1830                 SB_Terminate (&FileName);
1831                 InfoBits |= ibFileName;
1832                 NextToken (D);
1833                 break;
1834
1835             case TOK_SIZE:
1836                 if (!IntConstFollows (D)) {
1837                     goto ErrorExit;
1838                 }
1839                 Size = D->IVal;
1840                 NextToken (D);
1841                 InfoBits |= ibSize;
1842                 break;
1843
1844             default:
1845                 /* NOTREACHED */
1846                 UnexpectedToken (D);
1847                 goto ErrorExit;
1848
1849         }
1850
1851         /* Comma or done */
1852         if (D->Tok != TOK_COMMA) {
1853             break;
1854         }
1855         NextToken (D);
1856     }
1857
1858     /* Check for end of line */
1859     if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1860         UnexpectedToken (D);
1861         SkipLine (D);
1862         goto ErrorExit;
1863     }
1864
1865     /* Check for required information */
1866     if ((InfoBits & ibRequired) != ibRequired) {
1867         ParseError (D, CC65_ERROR, "Required attributes missing");
1868         goto ErrorExit;
1869     }
1870
1871     /* Create the file info and remember it */
1872     F = NewFileInfo (&FileName, Id, Size, MTime);
1873     CollAppend (&D->Info->FileInfoByName, F);
1874
1875 ErrorExit:
1876     /* Entry point in case of errors */
1877     SB_Done (&FileName);
1878     return;
1879 }
1880
1881
1882
1883 static void ParseLine (InputData* D)
1884 /* Parse a LINE line */
1885 {
1886     unsigned        File = 0;
1887     unsigned        Segment = 0;
1888     cc65_line       Line = 0;
1889     cc65_addr       Start = 0;
1890     cc65_addr       End = 0;
1891     cc65_line_type  Type = CC65_LINE_ASM;
1892     unsigned        Count = 0;
1893     LineInfo*   L;
1894     enum {
1895         ibNone      = 0x00,
1896         ibFile      = 0x01,
1897         ibSegment   = 0x02,
1898         ibLine      = 0x04,
1899         ibRange     = 0x08,
1900         ibType      = 0x10,
1901         ibCount     = 0x20,
1902         ibRequired  = ibFile | ibSegment | ibLine | ibRange,
1903     } InfoBits = ibNone;
1904
1905     /* Skip the LINE token */
1906     NextToken (D);
1907
1908     /* More stuff follows */
1909     while (1) {
1910
1911         Token Tok;
1912
1913         /* Something we know? */
1914         if (D->Tok != TOK_COUNT   && D->Tok != TOK_FILE  &&
1915             D->Tok != TOK_LINE    && D->Tok != TOK_RANGE &&
1916             D->Tok != TOK_SEGMENT && D->Tok != TOK_TYPE) {
1917
1918             /* Try smart error recovery */
1919             if (D->Tok == TOK_IDENT || TokenIsKeyword (D->Tok)) {
1920                 UnknownKeyword (D);
1921                 continue;
1922             }
1923
1924             /* Done */
1925             break;
1926         }
1927
1928         /* Remember the token, skip it, check for equal */
1929         Tok = D->Tok;
1930         NextToken (D);
1931         if (!ConsumeEqual (D)) {
1932             goto ErrorExit;
1933         }
1934
1935         /* Check what the token was */
1936         switch (Tok) {
1937
1938             case TOK_FILE:
1939                 if (!IntConstFollows (D)) {
1940                     goto ErrorExit;
1941                 }
1942                 File = D->IVal;
1943                 InfoBits |= ibFile;
1944                 NextToken (D);
1945                 break;
1946
1947             case TOK_LINE:
1948                 if (!IntConstFollows (D)) {
1949                     goto ErrorExit;
1950                 }
1951                 Line = (cc65_line) D->IVal;
1952                 NextToken (D);
1953                 InfoBits |= ibLine;
1954                 break;
1955
1956             case TOK_RANGE:
1957                 if (!IntConstFollows (D)) {
1958                     goto ErrorExit;
1959                 }
1960                 Start = (cc65_addr) D->IVal;
1961                 NextToken (D);
1962                 if (!ConsumeMinus (D)) {
1963                     goto ErrorExit;
1964                 }
1965                 if (!IntConstFollows (D)) {
1966                     goto ErrorExit;
1967                 }
1968                 End = (cc65_addr) D->IVal;
1969                 NextToken (D);
1970                 InfoBits |= ibRange;
1971                 break;
1972
1973             case TOK_SEGMENT:
1974                 if (!IntConstFollows (D)) {
1975                     goto ErrorExit;
1976                 }
1977                 Segment = D->IVal;
1978                 InfoBits |= ibSegment;
1979                 NextToken (D);
1980                 break;
1981
1982             case TOK_TYPE:
1983                 if (!IntConstFollows (D)) {
1984                     goto ErrorExit;
1985                 }
1986                 Type = (cc65_line_type) D->IVal;
1987                 InfoBits |= ibType;
1988                 NextToken (D);
1989                 break;
1990
1991             case TOK_COUNT:
1992                 if (!IntConstFollows (D)) {
1993                     goto ErrorExit;
1994                 }
1995                 Count = D->IVal;
1996                 InfoBits |= ibCount;
1997                 NextToken (D);
1998                 break;
1999
2000             default:
2001                 /* NOTREACHED */
2002                 UnexpectedToken (D);
2003                 goto ErrorExit;
2004
2005         }
2006
2007         /* Comma or done */
2008         if (D->Tok != TOK_COMMA) {
2009             break;
2010         }
2011         NextToken (D);
2012     }
2013
2014     /* Check for end of line */
2015     if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
2016         UnexpectedToken (D);
2017         SkipLine (D);
2018         goto ErrorExit;
2019     }
2020
2021     /* Check for required information */
2022     if ((InfoBits & ibRequired) != ibRequired) {
2023         ParseError (D, CC65_ERROR, "Required attributes missing");
2024         goto ErrorExit;
2025     }
2026
2027     /* Create the line info and remember it */
2028     L = NewLineInfo (File, Segment, Line, Start, End, Type, Count);
2029     CollAppend (&D->Info->LineInfos, L);
2030
2031 ErrorExit:
2032     /* Entry point in case of errors */
2033     return;
2034 }
2035
2036
2037
2038 static void ParseSegment (InputData* D)
2039 /* Parse a SEGMENT line */
2040 {
2041     unsigned        Id = 0;
2042     cc65_addr       Start = 0;
2043     cc65_addr       Size = 0;
2044     StrBuf          SegName = STRBUF_INITIALIZER;
2045     StrBuf          OutputName = STRBUF_INITIALIZER;
2046     unsigned long   OutputOffs = 0;
2047     SegInfo*        S;
2048     enum {
2049         ibNone      = 0x00,
2050         ibId        = 0x01,
2051         ibSegName   = 0x02,
2052         ibStart     = 0x04,
2053         ibSize      = 0x08,
2054         ibAddrSize  = 0x10,
2055         ibType      = 0x20,
2056         ibOutputName= 0x40,
2057         ibOutputOffs= 0x80,
2058         ibRequired  = ibId | ibSegName | ibStart | ibSize | ibAddrSize | ibType,
2059     } InfoBits = ibNone;
2060
2061     /* Skip the SEGMENT token */
2062     NextToken (D);
2063
2064     /* More stuff follows */
2065     while (1) {
2066
2067         Token Tok;
2068
2069         /* Something we know? */
2070         if (D->Tok != TOK_ADDRSIZE      && D->Tok != TOK_ID         &&
2071             D->Tok != TOK_NAME          && D->Tok != TOK_OUTPUTNAME &&
2072             D->Tok != TOK_OUTPUTOFFS    && D->Tok != TOK_SIZE       &&
2073             D->Tok != TOK_START         && D->Tok != TOK_TYPE) {
2074
2075             /* Try smart error recovery */
2076             if (D->Tok == TOK_IDENT || TokenIsKeyword (D->Tok)) {
2077                 UnknownKeyword (D);
2078                 continue;
2079             }
2080             /* Done */
2081             break;
2082         }
2083
2084         /* Remember the token, skip it, check for equal */
2085         Tok = D->Tok;
2086         NextToken (D);
2087         if (!ConsumeEqual (D)) {
2088             goto ErrorExit;
2089         }
2090
2091         /* Check what the token was */
2092         switch (Tok) {
2093
2094             case TOK_ADDRSIZE:
2095                 NextToken (D);
2096                 InfoBits |= ibAddrSize;
2097                 break;
2098
2099             case TOK_ID:
2100                 if (!IntConstFollows (D)) {
2101                     goto ErrorExit;
2102                 }
2103                 Id = D->IVal;
2104                 InfoBits |= ibId;
2105                 NextToken (D);
2106                 break;
2107
2108             case TOK_NAME:
2109                 if (!StrConstFollows (D)) {
2110                     goto ErrorExit;
2111                 }
2112                 SB_Copy (&SegName, &D->SVal);
2113                 SB_Terminate (&SegName);
2114                 InfoBits |= ibSegName;
2115                 NextToken (D);
2116                 break;
2117
2118             case TOK_OUTPUTNAME:
2119                 if (!StrConstFollows (D)) {
2120                     goto ErrorExit;
2121                 }
2122                 SB_Copy (&OutputName, &D->SVal);
2123                 SB_Terminate (&OutputName);
2124                 InfoBits |= ibOutputName;
2125                 NextToken (D);
2126                 break;
2127
2128             case TOK_OUTPUTOFFS:
2129                 if (!IntConstFollows (D)) {
2130                     goto ErrorExit;
2131                 }
2132                 OutputOffs = D->IVal;
2133                 NextToken (D);
2134                 InfoBits |= ibOutputOffs;
2135                 break;
2136
2137             case TOK_SIZE:
2138                 if (!IntConstFollows (D)) {
2139                     goto ErrorExit;
2140                 }
2141                 Size = D->IVal;
2142                 NextToken (D);
2143                 InfoBits |= ibSize;
2144                 break;
2145
2146             case TOK_START:
2147                 if (!IntConstFollows (D)) {
2148                     goto ErrorExit;
2149                 }
2150                 Start = (cc65_addr) D->IVal;
2151                 NextToken (D);
2152                 InfoBits |= ibStart;
2153                 break;
2154
2155             case TOK_TYPE:
2156                 NextToken (D);
2157                 InfoBits |= ibType;
2158                 break;
2159
2160             default:
2161                 /* NOTREACHED */
2162                 UnexpectedToken (D);
2163                 goto ErrorExit;
2164
2165         }
2166
2167         /* Comma or done */
2168         if (D->Tok != TOK_COMMA) {
2169             break;
2170         }
2171         NextToken (D);
2172     }
2173
2174     /* Check for end of line */
2175     if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
2176         UnexpectedToken (D);
2177         SkipLine (D);
2178         goto ErrorExit;
2179     }
2180
2181     /* Check for required and/or matched information */
2182     if ((InfoBits & ibRequired) != ibRequired) {
2183         ParseError (D, CC65_ERROR, "Required attributes missing");
2184         goto ErrorExit;
2185     }
2186     InfoBits &= (ibOutputName | ibOutputOffs);
2187     if (InfoBits != ibNone && InfoBits != (ibOutputName | ibOutputOffs)) {
2188         ParseError (D, CC65_ERROR,
2189                     "Attributes \"outputname\" and \"outputoffs\" must be paired");
2190         goto ErrorExit;
2191     }
2192
2193     /* Fix OutputOffs if not given */
2194     if (InfoBits == ibNone) {
2195         OutputOffs = 0;
2196     }
2197
2198     /* Create the segment info and remember it */
2199     S = NewSegInfo (&SegName, Id, Start, Size, &OutputName, OutputOffs);
2200     CollAppend (&D->Info->SegInfoByName, S);
2201
2202 ErrorExit:
2203     /* Entry point in case of errors */
2204     SB_Done (&SegName);
2205     SB_Done (&OutputName);
2206     return;
2207 }
2208
2209
2210
2211 static void ParseSym (InputData* D)
2212 /* Parse a SYM line */
2213 {
2214     cc65_symbol_type    Type;
2215     long                Value;
2216     cc65_size           Size = 0;
2217     StrBuf              SymName = STRBUF_INITIALIZER;
2218     SymInfo*            S;
2219     enum {
2220         ibNone          = 0x00,
2221         ibSymName       = 0x01,
2222         ibValue         = 0x02,
2223         ibAddrSize      = 0x04,
2224         ibType          = 0x08,
2225         ibSize          = 0x10,
2226         ibRequired      = ibSymName | ibValue | ibAddrSize | ibType,
2227     } InfoBits = ibNone;
2228
2229     /* Skip the SYM token */
2230     NextToken (D);
2231
2232     /* More stuff follows */
2233     while (1) {
2234
2235         Token Tok;
2236
2237         /* Something we know? */
2238         if (D->Tok != TOK_ADDRSIZE      && D->Tok != TOK_NAME   &&
2239             D->Tok != TOK_SIZE          && D->Tok != TOK_TYPE   &&
2240             D->Tok != TOK_VALUE) {
2241
2242             /* Try smart error recovery */
2243             if (D->Tok == TOK_IDENT || TokenIsKeyword (D->Tok)) {
2244                 UnknownKeyword (D);
2245                 continue;
2246             }
2247
2248             /* Done */
2249             break;
2250         }
2251
2252         /* Remember the token, skip it, check for equal */
2253         Tok = D->Tok;
2254         NextToken (D);
2255         if (!ConsumeEqual (D)) {
2256             goto ErrorExit;
2257         }
2258
2259         /* Check what the token was */
2260         switch (Tok) {
2261
2262             case TOK_ADDRSIZE:
2263                 NextToken (D);
2264                 InfoBits |= ibAddrSize;
2265                 break;
2266
2267             case TOK_NAME:
2268                 if (!StrConstFollows (D)) {
2269                     goto ErrorExit;
2270                 }
2271                 SB_Copy (&SymName, &D->SVal);
2272                 SB_Terminate (&SymName);
2273                 InfoBits |= ibSymName;
2274                 NextToken (D);
2275                 break;
2276
2277             case TOK_SIZE:
2278                 if (!IntConstFollows (D)) {
2279                     goto ErrorExit;
2280                 }
2281                 Size = (cc65_size) D->IVal;
2282                 InfoBits |= ibSize;
2283                 NextToken (D);
2284                 break;
2285
2286             case TOK_TYPE:
2287                 switch (D->Tok) {
2288                     case TOK_EQUATE:
2289                         Type = CC65_SYM_EQUATE;
2290                         break;
2291                     case TOK_LABEL:
2292                         Type = CC65_SYM_LABEL;
2293                         break;
2294                     default:
2295                         ParseError (D, CC65_ERROR,
2296                                     "Unknown value for attribute \"type\"");
2297                         SkipLine (D);
2298                         goto ErrorExit;
2299                 }
2300                 NextToken (D);
2301                 InfoBits |= ibType;
2302                 break;
2303
2304             case TOK_VALUE:
2305                 if (!IntConstFollows (D)) {
2306                     goto ErrorExit;
2307                 }
2308                 Value = D->IVal;
2309                 InfoBits |= ibValue;
2310                 NextToken (D);
2311                 break;
2312
2313             default:
2314                 /* NOTREACHED */
2315                 UnexpectedToken (D);
2316                 goto ErrorExit;
2317
2318         }
2319
2320         /* Comma or done */
2321         if (D->Tok != TOK_COMMA) {
2322             break;
2323         }
2324         NextToken (D);
2325     }
2326
2327     /* Check for end of line */
2328     if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
2329         UnexpectedToken (D);
2330         SkipLine (D);
2331         goto ErrorExit;
2332     }
2333
2334     /* Check for required and/or matched information */
2335     if ((InfoBits & ibRequired) != ibRequired) {
2336         ParseError (D, CC65_ERROR, "Required attributes missing");
2337         goto ErrorExit;
2338     }
2339
2340     /* Create the symbol info and remember it */
2341     S = NewSymInfo (&SymName, Value, Type, Size);
2342     CollAppend (&D->Info->SymInfoByName, S);
2343     CollAppend (&D->Info->SymInfoByVal, S);
2344
2345 ErrorExit:
2346     /* Entry point in case of errors */
2347     SB_Done (&SymName);
2348     return;
2349 }
2350
2351
2352
2353 static void ParseVersion (InputData* D)
2354 /* Parse a VERSION line */
2355 {
2356     enum {
2357         ibNone      = 0x00,
2358         ibMajor     = 0x01,
2359         ibMinor     = 0x02,
2360         ibRequired  = ibMajor | ibMinor,
2361     } InfoBits = ibNone;
2362
2363     /* Skip the VERSION token */
2364     NextToken (D);
2365
2366     /* More stuff follows */
2367     while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
2368
2369         switch (D->Tok) {
2370
2371             case TOK_MAJOR:
2372                 NextToken (D);
2373                 if (!ConsumeEqual (D)) {
2374                     goto ErrorExit;
2375                 }
2376                 if (!IntConstFollows (D)) {
2377                     goto ErrorExit;
2378                 }
2379                 D->MajorVersion = D->IVal;
2380                 NextToken (D);
2381                 InfoBits |= ibMajor;
2382                 break;
2383
2384             case TOK_MINOR:
2385                 NextToken (D);
2386                 if (!ConsumeEqual (D)) {
2387                     goto ErrorExit;
2388                 }
2389                 if (!IntConstFollows (D)) {
2390                     goto ErrorExit;
2391                 }
2392                 D->MinorVersion = D->IVal;
2393                 NextToken (D);
2394                 InfoBits |= ibMinor;
2395                 break;
2396
2397             case TOK_IDENT:
2398                 /* Try to skip unknown keywords that may have been added by
2399                  * a later version.
2400                  */
2401                 UnknownKeyword (D);
2402                 break;
2403
2404             default:
2405                 UnexpectedToken (D);
2406                 SkipLine (D);
2407                 goto ErrorExit;
2408         }
2409
2410         /* Comma follows before next attribute */
2411         if (D->Tok == TOK_COMMA) {
2412             NextToken (D);
2413         } else if (D->Tok == TOK_EOL || D->Tok == TOK_EOF) {
2414             break;
2415         } else {
2416             UnexpectedToken (D);
2417             goto ErrorExit;
2418         }
2419     }
2420
2421     /* Check for required information */
2422     if ((InfoBits & ibRequired) != ibRequired) {
2423         ParseError (D, CC65_ERROR, "Required attributes missing");
2424         goto ErrorExit;
2425     }
2426
2427 ErrorExit:
2428     /* Entry point in case of errors */
2429     return;
2430 }
2431
2432
2433
2434 /*****************************************************************************/
2435 /*                              Data processing                              */
2436 /*****************************************************************************/
2437
2438
2439
2440 static SegInfo* FindSegInfoById (InputData* D, unsigned Id)
2441 /* Find the SegInfo with a given Id */
2442 {
2443     /* Get a pointer to the segment info collection */
2444     Collection* SegInfos = &D->Info->SegInfoById;
2445
2446     /* Do a binary search */
2447     int Lo = 0;
2448     int Hi = (int) CollCount (SegInfos) - 1;
2449     while (Lo <= Hi) {
2450
2451         /* Mid of range */
2452         int Cur = (Lo + Hi) / 2;
2453
2454         /* Get item */
2455         SegInfo* CurItem = CollAt (SegInfos, Cur);
2456
2457         /* Found? */
2458         if (Id > CurItem->Id) {
2459             Lo = Cur + 1;
2460         } else if (Id < CurItem->Id) {
2461             Hi = Cur - 1;
2462         } else {
2463             /* Found! */
2464             return CurItem;
2465         }
2466     }
2467
2468     /* Not found */
2469     return 0;
2470 }
2471
2472
2473
2474 static int FindFileInfoByName (Collection* FileInfos, const char* FileName,
2475                                unsigned* Index)
2476 /* Find the FileInfo for a given file name. The function returns true if the
2477  * name was found. In this case, Index contains the index of the first item
2478  * that matches. If the item wasn't found, the function returns false and
2479  * Index contains the insert position for FileName.
2480  */
2481 {
2482     /* Do a binary search */
2483     int Lo = 0;
2484     int Hi = (int) CollCount (FileInfos) - 1;
2485     int Found = 0;
2486     while (Lo <= Hi) {
2487
2488         /* Mid of range */
2489         int Cur = (Lo + Hi) / 2;
2490
2491         /* Get item */
2492         FileInfo* CurItem = CollAt (FileInfos, Cur);
2493
2494         /* Compare */
2495         int Res = strcmp (CurItem->FileName, FileName);
2496
2497         /* Found? */
2498         if (Res < 0) {
2499             Lo = Cur + 1;
2500         } else {
2501             Hi = Cur - 1;
2502             /* Since we may have duplicates, repeat the search until we've
2503              * the first item that has a match.
2504              */
2505             if (Res == 0) {
2506                 Found = 1;
2507             }
2508         }
2509     }
2510
2511     /* Pass back the index. This is also the insert position */
2512     *Index = Lo;
2513     return Found;
2514 }
2515
2516
2517
2518 static FileInfo* FindFileInfoById (Collection* FileInfos, unsigned Id)
2519 /* Find the FileInfo with a given Id */
2520 {
2521     /* Do a binary search */
2522     int Lo = 0;
2523     int Hi = (int) CollCount (FileInfos) - 1;
2524     while (Lo <= Hi) {
2525
2526         /* Mid of range */
2527         int Cur = (Lo + Hi) / 2;
2528
2529         /* Get item */
2530         FileInfo* CurItem = CollAt (FileInfos, Cur);
2531
2532         /* Found? */
2533         if (Id > CurItem->Id) {
2534             Lo = Cur + 1;
2535         } else if (Id < CurItem->Id) {
2536             Hi = Cur - 1;
2537         } else {
2538             /* Found! */
2539             return CurItem;
2540         }
2541     }
2542
2543     /* Not found */
2544     return 0;
2545 }
2546
2547
2548
2549 static void ProcessSegInfo (InputData* D)
2550 /* Postprocess segment infos */
2551 {
2552     unsigned I;
2553
2554     /* Get pointers to the segment info collections */
2555     Collection* SegInfoByName = &D->Info->SegInfoByName;
2556     Collection* SegInfoById   = &D->Info->SegInfoById;
2557
2558     /* Sort the segment infos by name */
2559     CollSort (SegInfoByName, CompareSegInfoByName);
2560
2561     /* Copy all items over to the collection that will get sorted by id */
2562     for (I = 0; I < CollCount (SegInfoByName); ++I) {
2563         CollAppend (SegInfoById, CollAt (SegInfoByName, I));
2564     }
2565
2566     /* Sort this collection */
2567     CollSort (SegInfoById, CompareSegInfoById);
2568 }
2569
2570
2571
2572 static void ProcessFileInfo (InputData* D)
2573 /* Postprocess file infos */
2574 {
2575     unsigned I;
2576
2577     /* Get pointers to the file info collections */
2578     Collection* FileInfoByName = &D->Info->FileInfoByName;
2579     Collection* FileInfoById   = &D->Info->FileInfoById;
2580
2581     /* First, sort the file infos, so we can do a binary search */
2582     CollSort (FileInfoByName, CompareFileInfoByName);
2583
2584     /* Copy the file infos to another collection that will be sorted by id */
2585     for (I = 0; I < CollCount (FileInfoByName); ++I) {
2586         CollAppend (FileInfoById, CollAt (FileInfoByName, I));
2587     }
2588
2589     /* Sort this collection */
2590     CollSort (FileInfoById, CompareFileInfoById);
2591 }
2592
2593
2594
2595 static void ProcessLineInfo (InputData* D)
2596 /* Postprocess line infos */
2597 {
2598     /* Get pointers to the collections */
2599     Collection* LineInfos = &D->Info->LineInfos;
2600     Collection* FileInfos = &D->Info->FileInfoByName;
2601
2602     /* Walk over the line infos and replace the id numbers of file and segment
2603      * with pointers to the actual structs. Add the line info to each file
2604      * where it is defined.
2605      */
2606     unsigned I = 0;
2607     FileInfo* LastFileInfo = 0;
2608     SegInfo*  LastSegInfo  = 0;
2609     while (I < CollCount (LineInfos)) {
2610
2611         FileInfo* F;
2612         SegInfo*  S;
2613
2614         /* Get LineInfo struct */
2615         LineInfo* L = CollAt (LineInfos, I);
2616
2617         /* Find the FileInfo that corresponds to Id. We cache the last file
2618          * info in LastFileInfo to speedup searching.
2619          */
2620         if (LastFileInfo && LastFileInfo->Id == L->File.Id) {
2621             F = LastFileInfo;
2622         } else {
2623             F = FindFileInfoById (&D->Info->FileInfoById, L->File.Id);
2624
2625             /* If we have no corresponding file info, print a warning and
2626              * remove the line info.
2627              */
2628             if (F == 0) {
2629                 ParseError (D,
2630                             CC65_ERROR,
2631                             "No file info for file with id %u",
2632                             L->File.Id);
2633                 FreeLineInfo (L);
2634                 CollDelete (LineInfos, I);
2635                 continue;
2636             }
2637
2638             /* Otherwise remember it for later */
2639             LastFileInfo = F;
2640         }
2641
2642         /* Replace the file id by a pointer to the file info */
2643         L->File.Info = F;
2644
2645         /* Find the SegInfo that corresponds to Id. We cache the last file
2646          * info in LastSegInfo to speedup searching.
2647          */
2648         if (LastSegInfo && LastSegInfo->Id == L->Seg.Id) {
2649             S = LastSegInfo;
2650         } else {
2651             S = FindSegInfoById (D, L->Seg.Id);
2652
2653             /* If we have no corresponding segment info, print a warning and
2654              * remove the line info.
2655              */
2656             if (S == 0) {
2657                 ParseError (D,
2658                             CC65_ERROR,
2659                             "No segment info for segment with id %u",
2660                             L->Seg.Id);
2661                 FreeLineInfo (L);
2662                 CollDelete (LineInfos, I);
2663                 continue;
2664             }
2665
2666             /* Otherwise remember it for later */
2667             LastSegInfo = S;
2668         }
2669
2670         /* Replace the segment id by a pointer to the segment info */
2671         L->Seg.Info = S;
2672
2673         /* Add this line info to the file where it is defined */
2674         CollAppend (&F->LineInfoByLine, L);
2675
2676         /* Next one */
2677         ++I;
2678     }
2679
2680     /* Walk over all files and sort the line infos for each file so we can
2681      * do a binary search later.
2682      */
2683     for (I = 0; I < CollCount (FileInfos); ++I) {
2684
2685         /* Get a pointer to this file info */
2686         FileInfo* F = CollAt (FileInfos, I);
2687
2688         /* Sort the line infos for this file */
2689         CollSort (&F->LineInfoByLine, CompareLineInfoByLine);
2690     }
2691
2692     /* Sort the collection with all line infos by address */
2693     CollSort (LineInfos, CompareLineInfoByAddr);
2694
2695     /* Create the line info list from the line info collection */
2696     CreateLineInfoList (&D->Info->LineInfoByAddr, LineInfos);
2697 }
2698
2699
2700
2701 static LineInfoListEntry* FindLineInfoByAddr (const LineInfoList* L, cc65_addr Addr)
2702 /* Find the index of a LineInfo for a given address. Returns 0 if no such
2703  * lineinfo was found.
2704  */
2705 {
2706     /* Do a binary search */
2707     int Lo = 0;
2708     int Hi = (int) L->Count - 1;
2709     while (Lo <= Hi) {
2710
2711         /* Mid of range */
2712         int Cur = (Lo + Hi) / 2;
2713
2714         /* Get item */
2715         LineInfoListEntry* CurItem = &L->List[Cur];
2716
2717         /* Found? */
2718         if (CurItem->Addr > Addr) {
2719             Hi = Cur - 1;
2720         } else if (CurItem->Addr < Addr) {
2721             Lo = Cur + 1;
2722         } else {
2723             /* Found */
2724             return CurItem;
2725         }
2726     }
2727
2728     /* Not found */
2729     return 0;
2730 }
2731
2732
2733
2734 static LineInfo* FindLineInfoByLine (FileInfo* F, cc65_line Line)
2735 /* Find the LineInfo for a given line number */
2736 {
2737     int         Hi;
2738     int         Lo;
2739
2740
2741     /* Get a pointer to the line info collection for this file */
2742     Collection* LineInfoByLine = &F->LineInfoByLine;
2743
2744     /* Do a binary search */
2745     Lo = 0;
2746     Hi = (int) CollCount (LineInfoByLine) - 1;
2747     while (Lo <= Hi) {
2748
2749         /* Mid of range */
2750         int Cur = (Lo + Hi) / 2;
2751
2752         /* Get item */
2753         LineInfo* CurItem = CollAt (LineInfoByLine, Cur);
2754
2755         /* Found? */
2756         if (Line < CurItem->Line) {
2757             Hi = Cur - 1;
2758         } else if (Line > CurItem->Line) {
2759             Lo = Cur + 1;
2760         } else {
2761             /* Found! */
2762             return CurItem;
2763         }
2764     }
2765
2766     /* Not found */
2767     return 0;
2768 }
2769
2770
2771
2772 static void ProcessSymInfo (InputData* D)
2773 /* Postprocess symbol infos */
2774 {
2775     /* Get pointers to the symbol info collections */
2776     Collection* SymInfoByName = &D->Info->SymInfoByName;
2777     Collection* SymInfoByVal  = &D->Info->SymInfoByVal;
2778
2779     /* Sort the symbol infos */
2780     CollSort (SymInfoByName, CompareSymInfoByName);
2781     CollSort (SymInfoByVal,  CompareSymInfoByVal);
2782 }
2783
2784
2785
2786 static int FindSymInfoByName (Collection* SymInfos, const char* SymName, unsigned* Index)
2787 /* Find the SymInfo for a given file name. The function returns true if the
2788  * name was found. In this case, Index contains the index of the first item
2789  * that matches. If the item wasn't found, the function returns false and
2790  * Index contains the insert position for SymName.
2791  */
2792 {
2793     /* Do a binary search */
2794     int Lo = 0;
2795     int Hi = (int) CollCount (SymInfos) - 1;
2796     int Found = 0;
2797     while (Lo <= Hi) {
2798
2799         /* Mid of range */
2800         int Cur = (Lo + Hi) / 2;
2801
2802         /* Get item */
2803         SymInfo* CurItem = CollAt (SymInfos, Cur);
2804
2805         /* Compare */
2806         int Res = strcmp (CurItem->SymName, SymName);
2807
2808         /* Found? */
2809         if (Res < 0) {
2810             Lo = Cur + 1;
2811         } else {
2812             Hi = Cur - 1;
2813             /* Since we may have duplicates, repeat the search until we've
2814              * the first item that has a match.
2815              */
2816             if (Res == 0) {
2817                 Found = 1;
2818             }
2819         }
2820     }
2821
2822     /* Pass back the index. This is also the insert position */
2823     *Index = Lo;
2824     return Found;
2825 }
2826
2827
2828
2829 static int FindSymInfoByValue (Collection* SymInfos, long Value, unsigned* Index)
2830 /* Find the SymInfo for a given value. The function returns true if the
2831  * value was found. In this case, Index contains the index of the first item
2832  * that matches. If the item wasn't found, the function returns false and
2833  * Index contains the insert position for the given value.
2834  */
2835 {
2836     /* Do a binary search */
2837     int Lo = 0;
2838     int Hi = (int) CollCount (SymInfos) - 1;
2839     int Found = 0;
2840     while (Lo <= Hi) {
2841
2842         /* Mid of range */
2843         int Cur = (Lo + Hi) / 2;
2844
2845         /* Get item */
2846         SymInfo* CurItem = CollAt (SymInfos, Cur);
2847
2848         /* Found? */
2849         if (Value > CurItem->Value) {
2850             Lo = Cur + 1;
2851         } else {
2852             Hi = Cur - 1;
2853             /* Since we may have duplicates, repeat the search until we've
2854              * the first item that has a match.
2855              */
2856             if (Value == CurItem->Value) {
2857                 Found = 1;
2858             }
2859         }
2860     }
2861
2862     /* Pass back the index. This is also the insert position */
2863     *Index = Lo;
2864     return Found;
2865 }
2866
2867
2868
2869 /*****************************************************************************/
2870 /*                                   Code                                    */
2871 /*****************************************************************************/
2872
2873
2874
2875 cc65_dbginfo cc65_read_dbginfo (const char* FileName, cc65_errorfunc ErrFunc)
2876 /* Parse the debug info file with the given name. On success, the function
2877  * will return a pointer to an opaque cc65_dbginfo structure, that must be
2878  * passed to the other functions in this module to retrieve information.
2879  * errorfunc is called in case of warnings and errors. If the file cannot be
2880  * read successfully, NULL is returned.
2881  */
2882 {
2883     /* Data structure used to control scanning and parsing */
2884     InputData D = {
2885         0,                      /* Name of input file */
2886         1,                      /* Line number */
2887         0,                      /* Input file */
2888         0,                      /* Line at start of current token */
2889         0,                      /* Column at start of current token */
2890         0,                      /* Number of errors */
2891         0,                      /* Input file */
2892         ' ',                    /* Input character */
2893         TOK_INVALID,            /* Input token */
2894         0,                      /* Integer constant */
2895         STRBUF_INITIALIZER,     /* String constant */
2896         0,                      /* Function called in case of errors */
2897         0,                      /* Major version number */
2898         0,                      /* Minor version number */
2899         0,                      /* Pointer to debug info */
2900     };
2901     D.FileName = FileName;
2902     D.Error    = ErrFunc;
2903
2904     /* Open the input file */
2905     D.F = fopen (D.FileName, "rt");
2906     if (D.F == 0) {
2907         /* Cannot open */
2908         ParseError (&D, CC65_ERROR,
2909                     "Cannot open input file \"%s\": %s",
2910                      D.FileName, strerror (errno));
2911         return 0;
2912     }
2913
2914     /* Create a new debug info struct */
2915     D.Info = NewDbgInfo ();
2916
2917     /* Prime the pump */
2918     NextToken (&D);
2919
2920     /* The first line in the file must specify version information */
2921     if (D.Tok != TOK_VERSION) {
2922         ParseError (&D, CC65_ERROR,
2923                     "\"version\" keyword missing in first line - this is not "
2924                     "a valid debug info file");
2925         goto CloseAndExit;
2926     }
2927
2928     /* Parse the version directive */
2929     ParseVersion (&D);
2930
2931     /* Do several checks on the version number */
2932     if (D.MajorVersion < VER_MAJOR) {
2933         ParseError (
2934             &D, CC65_ERROR,
2935             "This is an old version of the debug info format that is no "
2936             "longer supported. Version found = %u.%u, version supported "
2937             "= %u.%u",
2938              D.MajorVersion, D.MinorVersion, VER_MAJOR, VER_MINOR
2939         );
2940         goto CloseAndExit;
2941     } else if (D.MajorVersion == VER_MAJOR && D.MinorVersion > VER_MINOR) {
2942         ParseError (
2943             &D, CC65_ERROR,
2944             "This is a slightly newer version of the debug info format. "
2945             "It might work, but you may get errors about unknown keywords "
2946             "and similar. Version found = %u.%u, version supported = %u.%u",
2947              D.MajorVersion, D.MinorVersion, VER_MAJOR, VER_MINOR
2948         );
2949     } else if (D.MajorVersion > VER_MAJOR) {
2950         ParseError (
2951             &D, CC65_WARNING,
2952             "The format of this debug info file is newer than what we "
2953             "know. Will proceed but probably fail. Version found = %u.%u, "
2954             "version supported = %u.%u",
2955              D.MajorVersion, D.MinorVersion, VER_MAJOR, VER_MINOR
2956         );
2957     }
2958     ConsumeEOL (&D);
2959
2960     /* Parse lines */
2961     while (D.Tok != TOK_EOF) {
2962
2963         switch (D.Tok) {
2964
2965             case TOK_FILE:
2966                 ParseFile (&D);
2967                 break;
2968
2969             case TOK_LINE:
2970                 ParseLine (&D);
2971                 break;
2972
2973             case TOK_SEGMENT:
2974                 ParseSegment (&D);
2975                 break;
2976
2977             case TOK_SYM:
2978                 ParseSym (&D);
2979                 break;
2980
2981             case TOK_IDENT:
2982                 /* Output a warning, then skip the line with the unknown
2983                  * keyword that may have been added by a later version.
2984                  */
2985                 ParseError (&D, CC65_WARNING,
2986                             "Unknown keyword \"%s\" - skipping",
2987                             SB_GetConstBuf (&D.SVal));
2988
2989                 SkipLine (&D);
2990                 break;
2991
2992             default:
2993                 UnexpectedToken (&D);
2994
2995         }
2996
2997         /* EOL or EOF must follow */
2998         ConsumeEOL (&D);
2999     }
3000
3001 CloseAndExit:
3002     /* Close the file */
3003     fclose (D.F);
3004
3005     /* Free memory allocated for SVal */
3006     SB_Done (&D.SVal);
3007
3008     /* In case of errors, delete the debug info already allocated and
3009      * return NULL
3010      */
3011     if (D.Errors > 0) {
3012         /* Free allocated stuff */
3013         unsigned I;
3014         for (I = 0; I < CollCount (&D.Info->LineInfos); ++I) {
3015             FreeLineInfo (CollAt (&D.Info->LineInfos, I));
3016         }
3017         DoneCollection (&D.Info->LineInfos);
3018         FreeDbgInfo (D.Info);
3019         return 0;
3020     }
3021
3022     /* We do now have all the information from the input file. Do
3023      * postprocessing.
3024      */
3025     ProcessSegInfo (&D);
3026     ProcessFileInfo (&D);
3027     ProcessLineInfo (&D);
3028     ProcessSymInfo (&D);
3029
3030 #if DEBUG
3031     /* Debug output */
3032     DumpData (&D);
3033 #endif
3034
3035     /* Return the debug info struct that was created */
3036     return D.Info;
3037 }
3038
3039
3040
3041 void cc65_free_dbginfo (cc65_dbginfo Handle)
3042 /* Free debug information read from a file */
3043 {
3044     if (Handle) {
3045         FreeDbgInfo (Handle);
3046     }
3047 }
3048
3049
3050
3051 cc65_lineinfo* cc65_lineinfo_byaddr (cc65_dbginfo Handle, unsigned long Addr)
3052 /* Return line information for the given address. The function returns 0
3053  * if no line information was found.
3054  */
3055 {
3056     LineInfoListEntry* E;
3057     cc65_lineinfo*  D = 0;
3058
3059     /* Check the parameter */
3060     assert (Handle != 0);
3061
3062     /* Search in the line infos for address */
3063     E = FindLineInfoByAddr (&((DbgInfo*) Handle)->LineInfoByAddr, Addr);
3064
3065     /* Do we have line infos? */
3066     if (E != 0) {
3067
3068         unsigned I;
3069
3070         /* Prepare the struct we will return to the caller */
3071         D = new_cc65_lineinfo (E->Count);
3072         if (E->Count == 1) {
3073             CopyLineInfo (D->data, E->Data);
3074         } else {
3075             for (I = 0; I < D->count; ++I) {
3076                 /* Copy data */
3077                 CopyLineInfo (D->data + I, ((LineInfo**) E->Data)[I]);
3078             }
3079         }
3080     }
3081
3082     /* Return the struct we've created */
3083     return D;
3084 }
3085
3086
3087
3088 cc65_lineinfo* cc65_lineinfo_byname (cc65_dbginfo Handle, const char* FileName,
3089                                      cc65_line Line)
3090 /* Return line information for a file/line number combination. The function
3091  * returns NULL if no line information was found.
3092  */
3093 {
3094     DbgInfo*        Info;
3095     FileInfo*       F;
3096     cc65_lineinfo*  D;
3097     int             Found;
3098     unsigned        Index;
3099     Collection      LineInfoList = COLLECTION_INITIALIZER;
3100
3101     /* Check the parameter */
3102     assert (Handle != 0);
3103
3104     /* The handle is actually a pointer to a debug info struct */
3105     Info = (DbgInfo*) Handle;
3106
3107     /* Search for the first file with this name */
3108     Found = FindFileInfoByName (&Info->FileInfoByName, FileName, &Index);
3109     if (!Found) {
3110         return 0;
3111     }
3112
3113     /* Loop over all files with this name */
3114     F = CollAt (&Info->FileInfoByName, Index);
3115     while (Found) {
3116
3117         /* Search in the file for the given line */
3118         LineInfo* L = FindLineInfoByLine (F, Line);
3119         if (L) {
3120             /* Remember the line info */
3121             CollAppend (&LineInfoList, L);
3122         }
3123
3124         /* Next entry */
3125         ++Index;
3126
3127         /* If the index is valid, check if the next entry is a file with the
3128          * same name.
3129          */
3130         if (Index < CollCount (&Info->FileInfoByName)) {
3131             F = CollAt (&Info->FileInfoByName, Index);
3132             Found = (strcmp (F->FileName, FileName) == 0);
3133         } else {
3134             Found = 0;
3135         }
3136     }
3137
3138     /* Check if we have entries */
3139     if (CollCount (&LineInfoList) == 0) {
3140         /* Nope */
3141         return 0;
3142     }
3143
3144     /* Prepare the struct we will return to the caller */
3145     D = new_cc65_lineinfo (CollCount (&LineInfoList));
3146
3147     /* Copy the data */
3148     for (Index = 0; Index < CollCount (&LineInfoList); ++Index) {
3149         CopyLineInfo (D->data + Index, CollAt (&LineInfoList, Index));
3150     }
3151
3152     /* Delete the temporary data collection */
3153     DoneCollection (&LineInfoList);
3154
3155     /* Return the allocated struct */
3156     return D;
3157 }
3158
3159
3160
3161 void cc65_free_lineinfo (cc65_dbginfo Handle, cc65_lineinfo* Info)
3162 /* Free line info returned by one of the other functions */
3163 {
3164     /* Just for completeness, check the handle */
3165     assert (Handle != 0);
3166
3167     /* Just free the memory */
3168     xfree (Info);
3169 }
3170
3171
3172
3173 cc65_sourceinfo* cc65_get_sourcelist (cc65_dbginfo Handle)
3174 /* Return a list of all source files */
3175 {
3176     DbgInfo*            Info;
3177     Collection*         FileInfoByName;
3178     cc65_sourceinfo*    D;
3179     unsigned            I;
3180
3181     /* Check the parameter */
3182     assert (Handle != 0);
3183
3184     /* The handle is actually a pointer to a debug info struct */
3185     Info = (DbgInfo*) Handle;
3186
3187     /* Get a pointer to the file list */
3188     FileInfoByName = &Info->FileInfoByName;
3189
3190     /* Allocate memory for the data structure returned to the caller.
3191      * Note: To simplify things, we will allocate the maximum amount of
3192      * memory, we may need later. This saves us the overhead of walking
3193      * the list twice.
3194      */
3195     D = new_cc65_sourceinfo (CollCount (FileInfoByName));
3196
3197     /* Fill in the data, skipping duplicate entries */
3198     D->count = 0;
3199     for (I = 0; I < CollCount (FileInfoByName); ++I) {
3200
3201         /* Get this item */
3202         const FileInfo* F = CollAt (FileInfoByName, I);
3203
3204         /* If this is not the first entry, compare it to the last one and
3205          * don't add it if it is identical.
3206          */
3207         if (I > 0 && CompareFileInfoByName (F, CollAt (FileInfoByName, I-1)) == 0) {
3208             continue;
3209         }
3210
3211         /* Copy the data */
3212         D->data[D->count].source_name  = F->FileName;
3213         D->data[D->count].source_size  = F->Size;
3214         D->data[D->count].source_mtime = F->MTime;
3215
3216         /* One more valid entry */
3217         ++D->count;
3218     }
3219
3220     /* Return the result */
3221     return D;
3222 }
3223
3224
3225
3226 void cc65_free_sourceinfo (cc65_dbginfo Handle, cc65_sourceinfo* Info)
3227 /* Free a source info record */
3228 {
3229     /* Just for completeness, check the handle */
3230     assert (Handle != 0);
3231
3232     /* Free the memory */
3233     xfree (Info);
3234 }
3235
3236
3237
3238 cc65_segmentinfo* cc65_get_segmentlist (cc65_dbginfo Handle)
3239 /* Return a list of all segments referenced in the debug information */
3240 {
3241     DbgInfo*            Info;
3242     Collection*         SegInfoByName;
3243     cc65_segmentinfo*   D;
3244     unsigned            I;
3245
3246     /* Check the parameter */
3247     assert (Handle != 0);
3248
3249     /* The handle is actually a pointer to a debug info struct */
3250     Info = (DbgInfo*) Handle;
3251
3252     /* Get a pointer to the segment list */
3253     SegInfoByName = &Info->SegInfoByName;
3254
3255     /* Allocate memory for the data structure returned to the caller */
3256     D = new_cc65_segmentinfo (CollCount (SegInfoByName));
3257
3258     /* Fill in the data */
3259     D->count = CollCount (SegInfoByName);
3260     for (I = 0; I < CollCount (SegInfoByName); ++I) {
3261
3262         /* Get this item */
3263         const SegInfo* S = CollAt (SegInfoByName, I);
3264
3265         /* Copy the data */
3266         D->data[I].segment_name  = S->SegName;
3267         D->data[I].segment_start = S->Start;
3268         D->data[I].segment_size  = S->Size;
3269         D->data[I].output_name   = S->OutputName;
3270         D->data[I].output_offs   = S->OutputOffs;
3271     }
3272
3273     /* Return the result */
3274     return D;
3275 }
3276
3277
3278
3279 void cc65_free_segmentinfo (cc65_dbginfo Handle, cc65_segmentinfo* Info)
3280 /* Free a segment info record */
3281 {
3282     /* Just for completeness, check the handle */
3283     assert (Handle != 0);
3284
3285     /* Free the memory */
3286     xfree (Info);
3287 }
3288
3289
3290
3291 cc65_symbolinfo* cc65_symbol_byname (cc65_dbginfo Handle, const char* Name)
3292 /* Return a list of symbols with a given name. The function returns NULL if
3293  * no symbol with this name was found.
3294  */
3295 {
3296     DbgInfo*            Info;
3297     Collection*         SymInfoByName;
3298     cc65_symbolinfo*    D;
3299     unsigned            I;
3300     unsigned            Index;
3301     unsigned            Count;
3302
3303     /* Check the parameter */
3304     assert (Handle != 0);
3305
3306     /* The handle is actually a pointer to a debug info struct */
3307     Info = (DbgInfo*) Handle;
3308
3309     /* Get a pointer to the symbol list */
3310     SymInfoByName = &Info->SymInfoByName;
3311
3312     /* Search for the symbol */
3313     if (!FindSymInfoByName (SymInfoByName, Name, &Index)) {
3314         /* Not found */
3315         return 0;
3316     }
3317
3318     /* Index contains the position. Count how many symbols with this name
3319      * we have. Skip the first one, since we have at least one.
3320      */
3321     Count = 1;
3322     while ((unsigned) Index + Count < CollCount (SymInfoByName)) {
3323         const SymInfo* S = CollAt (SymInfoByName, (unsigned) Index + Count);
3324         if (strcmp (S->SymName, Name) != 0) {
3325             break;
3326         }
3327         ++Count;
3328     }
3329
3330     /* Allocate memory for the data structure returned to the caller */
3331     D = new_cc65_symbolinfo (Count);
3332
3333     /* Fill in the data */
3334     D->count = Count;
3335     for (I = 0; I < Count; ++I) {
3336         /* Copy the data */
3337         CopySymInfo (D->data + I, CollAt (SymInfoByName, Index++));
3338     }
3339
3340     /* Return the result */
3341     return D;
3342 }
3343
3344
3345
3346 cc65_symbolinfo* cc65_symbol_inrange (cc65_dbginfo Handle, cc65_addr Start, cc65_addr End)
3347 /* Return a list of labels in the given range. End is inclusive. The function
3348  * return NULL if no symbols within the given range are found. Non label
3349  * symbols are ignored and not returned.
3350  */
3351 {
3352     DbgInfo*            Info;
3353     Collection*         SymInfoByVal;
3354     Collection          SymInfoList = COLLECTION_INITIALIZER;
3355     cc65_symbolinfo*    D;
3356     unsigned            I;
3357     unsigned            Index;
3358
3359     /* Check the parameter */
3360     assert (Handle != 0);
3361
3362     /* The handle is actually a pointer to a debug info struct */
3363     Info = (DbgInfo*) Handle;
3364
3365     /* Get a pointer to the symbol list */
3366     SymInfoByVal = &Info->SymInfoByVal;
3367
3368     /* Search for the symbol. Because we're searching for a range, we cannot
3369      * make use of the function result.
3370      */
3371     FindSymInfoByValue (SymInfoByVal, Start, &Index);
3372
3373     /* Start from the given index, check all symbols until the end address is
3374      * reached. Place all symbols into SymInfoList for later.
3375      */
3376     for (I = Index; I < CollCount (SymInfoByVal); ++I) {
3377
3378         /* Get the item */
3379         SymInfo* Item = CollAt (SymInfoByVal, I);
3380
3381         /* The collection is sorted by address, so if we get a value larger
3382          * than the end address, we're done.
3383          */
3384         if (Item->Value > (long) End) {
3385             break;
3386         }
3387
3388         /* Ignore non-labels */
3389         if (Item->Type != CC65_SYM_LABEL) {
3390             continue;
3391         }
3392
3393         /* Ok, remember this one */
3394         CollAppend (&SymInfoList, Item);
3395     }
3396
3397     /* If we don't have any labels within the range, bail out. No memory has
3398      * been allocated for SymInfoList.
3399      */
3400     if (CollCount (&SymInfoList) == 0) {
3401         return 0;
3402     }
3403
3404     /* Allocate memory for the data structure returned to the caller */
3405     D = new_cc65_symbolinfo (CollCount (&SymInfoList));
3406
3407     /* Fill in the data */
3408     D->count = CollCount (&SymInfoList);
3409     for (I = 0; I < CollCount (&SymInfoList); ++I) {
3410         /* Copy the data */
3411         CopySymInfo (D->data + I, CollAt (&SymInfoList, I));
3412     }
3413
3414     /* Free the collection */
3415     DoneCollection (&SymInfoList);
3416
3417     /* Return the result */
3418     return D;
3419 }
3420
3421
3422
3423 void cc65_free_symbolinfo (cc65_dbginfo Handle, cc65_symbolinfo* Info)
3424 /* Free a symbol info record */
3425 {
3426     /* Just for completeness, check the handle */
3427     assert (Handle != 0);
3428
3429     /* Free the memory */
3430     xfree (Info);
3431 }
3432
3433
3434