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