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