]> git.sur5r.net Git - cc65/blob - src/dbginfo/dbginfo.c
Major changes: Names of structures, fields and subroutine names have changed.
[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 };
93
94 /* Input tokens */
95 typedef enum {
96
97     TOK_INVALID,                        /* Invalid token */
98     TOK_EOF,                            /* End of file reached */
99
100     TOK_INTCON,                         /* Integer constant */
101     TOK_STRCON,                         /* String constant */
102
103     TOK_EQUAL,                          /* = */
104     TOK_COMMA,                          /* , */
105     TOK_MINUS,                          /* - */
106     TOK_PLUS,                           /* + */
107     TOK_EOL,                            /* \n */
108
109     TOK_ABSOLUTE,                       /* ABSOLUTE keyword */
110     TOK_ADDRSIZE,                       /* ADDRSIZE keyword */
111     TOK_EQUATE,                         /* EQUATE keyword */
112     TOK_FILE,                           /* FILE keyword */
113     TOK_ID,                             /* ID keyword */
114     TOK_LABEL,                          /* LABEL keyword */
115     TOK_LINE,                           /* LINE keyword */
116     TOK_LONG,                           /* LONG_keyword */
117     TOK_MAJOR,                          /* MAJOR keyword */
118     TOK_MINOR,                          /* MINOR keyword */
119     TOK_MTIME,                          /* MTIME keyword */
120     TOK_NAME,                           /* NAME keyword */
121     TOK_OUTPUTNAME,                     /* OUTPUTNAME keyword */
122     TOK_OUTPUTOFFS,                     /* OUTPUTOFFS keyword */
123     TOK_RANGE,                          /* RANGE keyword */
124     TOK_RO,                             /* RO keyword */
125     TOK_RW,                             /* RW keyword */
126     TOK_SEGMENT,                        /* SEGMENT keyword */
127     TOK_SIZE,                           /* SIZE keyword */
128     TOK_START,                          /* START keyword */
129     TOK_SYM,                            /* SYM keyword */
130     TOK_TYPE,                           /* TYPE keyword */
131     TOK_VALUE,                          /* VALUE keyword */
132     TOK_VERSION,                        /* VERSION keyword */
133     TOK_ZEROPAGE,                       /* ZEROPAGE keyword */
134
135     TOK_IDENT,                          /* To catch unknown keywords */
136 } Token;
137
138 /* Data used when parsing the debug info file */
139 typedef struct InputData InputData;
140 struct InputData {
141     const char*         FileName;       /* Name of input file */
142     cc65_line           Line;           /* Current line number */
143     unsigned            Col;            /* Current column number */
144     cc65_line           SLine;          /* Line number at start of token */
145     unsigned            SCol;           /* Column number at start of token */
146     unsigned            Errors;         /* Number of errors */
147     FILE*               F;              /* Input file */
148     int                 C;              /* Input character */
149     Token               Tok;            /* Token from input stream */
150     unsigned long       IVal;           /* Integer constant */
151     StrBuf              SVal;           /* String constant */
152     cc65_errorfunc      Error;          /* Function called in case of errors */
153     unsigned            MajorVersion;   /* Major version number */
154     unsigned            MinorVersion;   /* Minor version number */
155     Collection          LineInfos;      /* Line information */
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);
677 }
678
679
680
681 static int CompareSegInfoByName (const void* L, const void* R)
682 /* Helper function to sort segment infos in a collection by name */
683 {
684     /* Sort by file name */
685     return strcmp (((const SegInfo*) L)->SegName,
686                    ((const SegInfo*) R)->SegName);
687 }
688
689
690
691 static int CompareSegInfoById (const void* L, const void* R)
692 /* Helper function to sort segment infos in a collection by id */
693 {
694     if (((const SegInfo*) L)->Id > ((const SegInfo*) R)->Id) {
695         return 1;
696     } else if (((const SegInfo*) L)->Id < ((const SegInfo*) R)->Id) {
697         return -1;
698     } else {
699         return 0;
700     }
701 }
702
703
704
705 /*****************************************************************************/
706 /*                                 Line info                                 */
707 /*****************************************************************************/
708
709
710
711 static LineInfo* NewLineInfo (unsigned File, unsigned Seg, cc65_line Line,
712                               cc65_addr Start, cc65_addr End)
713 /* Create a new LineInfo struct and return it */
714 {
715     /* Allocate memory */
716     LineInfo* L = xmalloc (sizeof (LineInfo));
717
718     /* Initialize it */
719     L->Start    = Start;
720     L->End      = End;
721     L->Line     = Line;
722     L->File.Id  = File;
723     L->Seg.Id   = Seg;
724
725     /* Return it */
726     return L;
727 }
728
729
730
731 static void FreeLineInfo (LineInfo* L)
732 /* Free a LineInfo struct */
733 {
734     xfree (L);
735 }
736
737
738
739 static int CompareLineInfoByAddr (const void* L, const void* R)
740 /* Helper function to sort line infos in a collection by address */
741 {
742     /* Sort by start of range */
743     if (((const LineInfo*) L)->Start > ((const LineInfo*) R)->Start) {
744         return 1;
745     } else if (((const LineInfo*) L)->Start < ((const LineInfo*) R)->Start) {
746         return -1;
747     } else {
748         return 0;
749     }
750 }
751
752
753
754 static int CompareLineInfoByLine (const void* L, const void* R)
755 /* Helper function to sort line infos in a collection by line */
756 {
757     if (((const LineInfo*) L)->Line > ((const LineInfo*) R)->Line) {
758         return 1;
759     } else if (((const LineInfo*) L)->Line < ((const LineInfo*) R)->Line) {
760         return -1;
761     } else {
762         return 0;
763     }
764 }
765
766
767
768 /*****************************************************************************/
769 /*                                 File info                                 */
770 /*****************************************************************************/
771
772
773
774 static FileInfo* NewFileInfo (const StrBuf* FileName, unsigned Id,
775                               unsigned long Size, unsigned long MTime)
776 /* Create a new FileInfo struct and return it */
777 {
778     /* Allocate memory */
779     FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
780
781     /* Initialize it */
782     F->Id    = Id;
783     F->Size  = Size;
784     F->MTime = MTime;
785     F->Start = ~(cc65_addr)0;
786     F->End   = 0;
787     InitCollection (&F->LineInfoByAddr);
788     InitCollection (&F->LineInfoByLine);
789     memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
790
791     /* Return it */
792     return F;
793 }
794
795
796
797 static void FreeFileInfo (FileInfo* F)
798 /* Free a FileInfo struct */
799 {
800     unsigned I;
801
802     /* Walk through the collection with line infos and delete them */
803     for (I = 0; I < CollCount (&F->LineInfoByAddr); ++I) {
804         FreeLineInfo (CollAt (&F->LineInfoByAddr, I));
805     }
806     DoneCollection (&F->LineInfoByAddr);
807     DoneCollection (&F->LineInfoByLine);
808
809     /* Free the file info structure itself */
810     xfree (F);
811 }
812
813
814
815 static int CompareFileInfoByName (const void* L, const void* R)
816 /* Helper function to sort file infos in a collection by name */
817 {
818     /* Sort by file name */
819     return strcmp (((const FileInfo*) L)->FileName,
820                    ((const FileInfo*) R)->FileName);
821 }
822
823
824
825 static int CompareFileInfoById (const void* L, const void* R)
826 /* Helper function to sort file infos in a collection by id */
827 {
828     if (((const FileInfo*) L)->Id > ((const FileInfo*) R)->Id) {
829         return 1;
830     } else if (((const FileInfo*) L)->Id < ((const FileInfo*) R)->Id) {
831         return -1;
832     } else {
833         return 0;
834     }
835 }
836
837
838
839 /*****************************************************************************/
840 /*                                Debug info                                 */
841 /*****************************************************************************/
842
843
844
845 static DbgInfo* NewDbgInfo (void)
846 /* Create a new DbgInfo struct and return it */
847 {
848     /* Allocate memory */
849     DbgInfo* Info = xmalloc (sizeof (DbgInfo));
850
851     /* Initialize it */
852     InitCollection (&Info->SegInfoByName);
853     InitCollection (&Info->SegInfoById);
854     InitCollection (&Info->FileInfoByName);
855     InitCollection (&Info->FileInfoById);
856
857     /* Return it */
858     return Info;
859 }
860
861
862
863 static void FreeDbgInfo (DbgInfo* Info)
864 /* Free a DbgInfo struct */
865 {
866     unsigned I;
867
868     /* Free segment info */
869     for (I = 0; I < CollCount (&Info->SegInfoByName); ++I) {
870         FreeSegInfo (CollAt (&Info->SegInfoByName, I));
871     }
872     DoneCollection (&Info->SegInfoByName);
873     DoneCollection (&Info->SegInfoById);
874
875     /* Free file info */
876     for (I = 0; I < CollCount (&Info->FileInfoByName); ++I) {
877         FreeFileInfo (CollAt (&Info->FileInfoByName, I));
878     }
879     DoneCollection (&Info->FileInfoByName);
880     DoneCollection (&Info->FileInfoById);
881
882     /* Free the structure itself */
883     xfree (Info);
884 }
885
886
887
888 /*****************************************************************************/
889 /*                             Helper functions                              */
890 /*****************************************************************************/
891
892
893
894 static void CopyLineInfo (cc65_linedata* D, const LineInfo* L)
895 /* Copy data from a LineInfo struct to the cc65_linedata struct returned to
896  * the caller.
897  */
898 {
899     D->source_name  = L->File.Info->FileName;
900     D->source_size  = L->File.Info->Size;
901     D->source_mtime = L->File.Info->MTime;
902     D->source_line  = L->Line;
903     D->line_start   = L->Start;
904     D->line_end     = L->End;
905     if (L->Seg.Info->OutputName) {
906         D->output_name  = L->Seg.Info->OutputName;
907         D->output_offs  = L->Seg.Info->OutputOffs + L->Start - L->Seg.Info->Start;
908     } else {
909         D->output_name  = 0;
910         D->output_offs  = 0;
911     }
912 }
913
914
915
916 static void ParseError (InputData* D, cc65_error_severity Type, const char* Msg, ...)
917 /* Call the user supplied parse error function */
918 {
919     va_list             ap;
920     int                 MsgSize;
921     cc65_parseerror*    E;
922
923     /* Test-format the error message so we know how much space to allocate */
924     va_start (ap, Msg);
925     MsgSize = vsnprintf (0, 0, Msg, ap);
926     va_end (ap);
927
928     /* Allocate memory */
929     E = xmalloc (sizeof (*E) + MsgSize);
930
931     /* Write data to E */
932     E->type   = Type;
933     E->name   = D->FileName;
934     E->line   = D->SLine;
935     E->column = D->SCol;
936     va_start (ap, Msg);
937     vsnprintf (E->errormsg, MsgSize+1, Msg, ap);
938     va_end (ap);
939
940     /* Call the caller:-) */
941     D->Error (E);
942
943     /* Free the data structure */
944     xfree (E);
945
946     /* Count errors */
947     if (Type == CC65_ERROR) {
948         ++D->Errors;
949     }
950 }
951
952
953
954 static void SkipLine (InputData* D)
955 /* Error recovery routine. Skip tokens until EOL or EOF is reached */
956 {
957     while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
958         NextToken (D);
959     }
960 }
961
962
963
964 static void UnexpectedToken (InputData* D)
965 /* Call ParseError with a message about an unexpected input token */
966 {
967     ParseError (D, CC65_ERROR, "Unexpected input token %d", D->Tok);
968     SkipLine (D);
969 }
970
971
972
973 static void UnknownKeyword (InputData* D)
974 /* Print a warning about an unknown keyword in the file. Try to do smart
975  * recovery, so if later versions of the debug information add additional
976  * keywords, this code may be able to at least ignore them.
977  */
978 {
979     /* Output a warning */
980     ParseError (D, CC65_WARNING, "Unknown keyword \"%s\" - skipping",
981                 SB_GetConstBuf (&D->SVal));
982
983     /* Skip the identifier */
984     NextToken (D);
985
986     /* If an equal sign follows, ignore anything up to the next line end
987      * or comma. If a comma or line end follows, we're already done. If
988      * we have none of both, we ignore the remainder of the line.
989      */
990     if (D->Tok == TOK_EQUAL) {
991         NextToken (D);
992         while (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
993             NextToken (D);
994         }
995     } else if (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
996         SkipLine (D);
997     }
998 }
999
1000
1001
1002 /*****************************************************************************/
1003 /*                            Scanner and parser                             */
1004 /*****************************************************************************/
1005
1006
1007
1008 static int DigitVal (int C)
1009 /* Return the value for a numeric digit. Return -1 if C is invalid */
1010 {
1011     if (isdigit (C)) {
1012         return C - '0';
1013     } else if (isxdigit (C)) {
1014         return toupper (C) - 'A' + 10;
1015     } else {
1016         return -1;
1017     }
1018 }
1019
1020
1021
1022 static void NextChar (InputData* D)
1023 /* Read the next character from the input. Count lines and columns */
1024 {
1025     /* Check if we've encountered EOF before */
1026     if (D->C >= 0) {
1027         D->C = fgetc (D->F);
1028         if (D->C == '\n') {
1029             ++D->Line;
1030             D->Col = 0;
1031         } else {
1032             ++D->Col;
1033         }
1034     }
1035 }
1036
1037
1038
1039 static void NextToken (InputData* D)
1040 /* Read the next token from the input stream */
1041 {
1042     static const struct KeywordEntry  {
1043         const char      Keyword[16];
1044         Token           Tok;
1045     } KeywordTable[] = {
1046         { "absolute",   TOK_ABSOLUTE    },
1047         { "addrsize",   TOK_ADDRSIZE    },
1048         { "equate",     TOK_EQUATE      },
1049         { "file",       TOK_FILE        },
1050         { "id",         TOK_ID          },
1051         { "label",      TOK_LABEL       },
1052         { "line",       TOK_LINE        },
1053         { "long",       TOK_LONG        },
1054         { "major",      TOK_MAJOR       },
1055         { "minor",      TOK_MINOR       },
1056         { "mtime",      TOK_MTIME       },
1057         { "name",       TOK_NAME        },
1058         { "outputname", TOK_OUTPUTNAME  },
1059         { "outputoffs", TOK_OUTPUTOFFS  },
1060         { "range",      TOK_RANGE       },
1061         { "ro",         TOK_RO          },
1062         { "rw",         TOK_RW          },
1063         { "segment",    TOK_SEGMENT     },
1064         { "size",       TOK_SIZE        },
1065         { "start",      TOK_START       },
1066         { "sym",        TOK_SYM         },
1067         { "type",       TOK_TYPE        },
1068         { "value",      TOK_VALUE       },
1069         { "version",    TOK_VERSION     },
1070         { "zeropage",   TOK_ZEROPAGE    },
1071     };
1072
1073
1074     /* Skip whitespace */
1075     while (D->C == ' ' || D->C == '\t') {
1076         NextChar (D);
1077     }
1078
1079     /* Remember the current position as start of the next token */
1080     D->SLine = D->Line;
1081     D->SCol  = D->Col;
1082
1083     /* Identifier? */
1084     if (D->C == '_' || isalpha (D->C)) {
1085
1086         const struct KeywordEntry* Entry;
1087
1088         /* Read the identifier */
1089         SB_Clear (&D->SVal);
1090         while (D->C == '_' || isalnum (D->C)) {
1091             SB_AppendChar (&D->SVal, D->C);
1092             NextChar (D);
1093         }
1094         SB_Terminate (&D->SVal);
1095
1096         /* Search the identifier in the keyword table */
1097         Entry = bsearch (SB_GetConstBuf (&D->SVal),
1098                          KeywordTable,
1099                          sizeof (KeywordTable) / sizeof (KeywordTable[0]),
1100                          sizeof (KeywordTable[0]),
1101                          (int (*)(const void*, const void*)) strcmp);
1102         if (Entry == 0) {
1103             D->Tok = TOK_IDENT;
1104         } else {
1105             D->Tok = Entry->Tok;
1106         }
1107         return;
1108     }
1109
1110     /* Number? */
1111     if (isdigit (D->C)) {
1112         int Base = 10;
1113         int Val;
1114         if (D->C == '0') {
1115             NextChar (D);
1116             if (toupper (D->C) == 'X') {
1117                 NextChar (D);
1118                 Base = 16;
1119             } else {
1120                 Base = 8;
1121             }
1122         } else {
1123             Base = 10;
1124         }
1125         D->IVal = 0;
1126         while ((Val = DigitVal (D->C)) >= 0 && Val < Base) {
1127             D->IVal = D->IVal * Base + Val;
1128             NextChar (D);
1129         }
1130         D->Tok = TOK_INTCON;
1131         return;
1132     }
1133
1134     /* Other characters */
1135     switch (D->C) {
1136
1137         case '-':
1138             NextChar (D);
1139             D->Tok = TOK_MINUS;
1140             break;
1141
1142         case '+':
1143             NextChar (D);
1144             D->Tok = TOK_PLUS;
1145             break;
1146
1147         case ',':
1148             NextChar (D);
1149             D->Tok = TOK_COMMA;
1150             break;
1151
1152         case '=':
1153             NextChar (D);
1154             D->Tok = TOK_EQUAL;
1155             break;
1156
1157         case '\"':
1158             SB_Clear (&D->SVal);
1159             NextChar (D);
1160             while (1) {
1161                 if (D->C == '\n' || D->C == EOF) {
1162                     ParseError (D, CC65_ERROR, "Unterminated string constant");
1163                     break;
1164                 }
1165                 if (D->C == '\"') {
1166                     NextChar (D);
1167                     break;
1168                 }
1169                 SB_AppendChar (&D->SVal, D->C);
1170                 NextChar (D);
1171             }
1172             SB_Terminate (&D->SVal);
1173             D->Tok = TOK_STRCON;
1174             break;
1175
1176         case '\n':
1177             NextChar (D);
1178             D->Tok = TOK_EOL;
1179             break;
1180
1181         case EOF:
1182             D->Tok = TOK_EOF;
1183             break;
1184
1185         default:
1186             ParseError (D, CC65_ERROR, "Invalid input character `%c'", D->C);
1187
1188     }
1189 }
1190
1191
1192
1193 static int TokenFollows (InputData* D, Token Tok, const char* Name)
1194 /* Check for a comma */
1195 {
1196     if (D->Tok != Tok) {
1197         ParseError (D, CC65_ERROR, "%s expected", Name);
1198         SkipLine (D);
1199         return 0;
1200     } else {
1201         return 1;
1202     }
1203 }
1204
1205
1206
1207 static int IntConstFollows (InputData* D)
1208 /* Check for an integer constant */
1209 {
1210     return TokenFollows (D, TOK_INTCON, "Integer constant");
1211 }
1212
1213
1214
1215 static int StrConstFollows (InputData* D)
1216 /* Check for a string literal */
1217 {
1218     return TokenFollows (D, TOK_STRCON, "String literal");
1219 }
1220
1221
1222
1223 static int Consume (InputData* D, Token Tok, const char* Name)
1224 /* Check for a token and consume it. Return true if the token was comsumed,
1225  * return false otherwise.
1226  */
1227 {
1228     if (TokenFollows (D, Tok, Name)) {
1229         NextToken (D);
1230         return 1;
1231     } else {
1232         return 0;
1233     }
1234 }
1235
1236
1237
1238 static int ConsumeEqual (InputData* D)
1239 /* Consume an equal sign */
1240 {
1241     return Consume (D, TOK_EQUAL, "'='");
1242 }
1243
1244
1245
1246 static int ConsumeMinus (InputData* D)
1247 /* Consume a minus sign */
1248 {
1249     return Consume (D, TOK_MINUS, "'-'");
1250 }
1251
1252
1253
1254 static void ConsumeEOL (InputData* D)
1255 /* Consume an end-of-line token, if we aren't at end-of-file */
1256 {
1257     if (D->Tok != TOK_EOF) {
1258         if (D->Tok != TOK_EOL) {
1259             ParseError (D, CC65_ERROR, "Extra tokens in line");
1260             SkipLine (D);
1261         }
1262         NextToken (D);
1263     }
1264 }
1265
1266
1267
1268 static void ParseFile (InputData* D)
1269 /* Parse a FILE line */
1270 {
1271     unsigned      Id;
1272     unsigned long Size;
1273     unsigned long MTime;
1274     StrBuf        FileName = STRBUF_INITIALIZER;
1275     FileInfo*     F;
1276     enum {
1277         ibNone      = 0x00,
1278         ibId        = 0x01,
1279         ibFileName  = 0x02,
1280         ibSize      = 0x04,
1281         ibMTime     = 0x08,
1282         ibRequired  = ibId | ibFileName | ibSize | ibMTime,
1283     } InfoBits = ibNone;
1284
1285     /* Skip the FILE token */
1286     NextToken (D);
1287
1288     /* More stuff follows */
1289     while (1) {
1290
1291         Token Tok;
1292
1293         /* Check for an unknown keyword */
1294         if (D->Tok == TOK_IDENT) {
1295             UnknownKeyword (D);
1296             continue;
1297         }
1298
1299         /* Something we know? */
1300         if (D->Tok != TOK_ID   && D->Tok != TOK_NAME  &&
1301             D->Tok != TOK_SIZE && D->Tok != TOK_MTIME) {
1302             /* Done */
1303             break;
1304         }
1305
1306         /* Remember the token, skip it, check for equal */
1307         Tok = D->Tok;
1308         NextToken (D);
1309         if (!ConsumeEqual (D)) {
1310             goto ErrorExit;
1311         }
1312
1313         /* Check what the token was */
1314         switch (Tok) {
1315
1316             case TOK_ID:
1317                 if (!IntConstFollows (D)) {
1318                     goto ErrorExit;
1319                 }
1320                 Id = D->IVal;
1321                 InfoBits |= ibId;
1322                 NextToken (D);
1323                 break;
1324
1325             case TOK_NAME:
1326                 if (!StrConstFollows (D)) {
1327                     goto ErrorExit;
1328                 }
1329                 SB_Copy (&FileName, &D->SVal);
1330                 SB_Terminate (&FileName);
1331                 InfoBits |= ibFileName;
1332                 NextToken (D);
1333                 break;
1334
1335             case TOK_SIZE:
1336                 if (!IntConstFollows (D)) {
1337                     goto ErrorExit;
1338                 }
1339                 Size = D->IVal;
1340                 NextToken (D);
1341                 InfoBits |= ibSize;
1342                 break;
1343
1344             case TOK_MTIME:
1345                 if (!IntConstFollows (D)) {
1346                     goto ErrorExit;
1347                 }
1348                 MTime = D->IVal;
1349                 NextToken (D);
1350                 InfoBits |= ibMTime;
1351                 break;
1352
1353             default:
1354                 /* NOTREACHED */
1355                 UnexpectedToken (D);
1356                 goto ErrorExit;
1357
1358         }
1359
1360         /* Comma or done */
1361         if (D->Tok != TOK_COMMA) {
1362             break;
1363         }
1364         NextToken (D);
1365     }
1366
1367     /* Check for end of line */
1368     if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1369         UnexpectedToken (D);
1370         SkipLine (D);
1371         goto ErrorExit;
1372     }
1373
1374     /* Check for required information */
1375     if ((InfoBits & ibRequired) != ibRequired) {
1376         ParseError (D, CC65_ERROR, "Required attributes missing");
1377         goto ErrorExit;
1378     }
1379
1380     /* Create the file info and remember it */
1381     F = NewFileInfo (&FileName, Id, Size, MTime);
1382     CollAppend (&D->Info->FileInfoByName, F);
1383
1384 ErrorExit:
1385     /* Entry point in case of errors */
1386     SB_Done (&FileName);
1387     return;
1388 }
1389
1390
1391
1392 static void ParseLine (InputData* D)
1393 /* Parse a LINE line */
1394 {
1395     unsigned    File;
1396     unsigned    Segment;
1397     cc65_line   Line;
1398     cc65_addr   Start;
1399     cc65_addr   End;
1400     LineInfo*   L;
1401     enum {
1402         ibNone      = 0x00,
1403         ibFile      = 0x01,
1404         ibSegment   = 0x02,
1405         ibLine      = 0x04,
1406         ibRange     = 0x08,
1407         ibRequired  = ibFile | ibSegment | ibLine | ibRange,
1408     } InfoBits = ibNone;
1409
1410     /* Skip the LINE token */
1411     NextToken (D);
1412
1413     /* More stuff follows */
1414     while (1) {
1415
1416         Token Tok;
1417
1418         /* Check for an unknown keyword */
1419         if (D->Tok == TOK_IDENT) {
1420             UnknownKeyword (D);
1421             continue;
1422         }
1423
1424         /* Something we know? */
1425         if (D->Tok != TOK_FILE && D->Tok != TOK_SEGMENT &&
1426             D->Tok != TOK_LINE && D->Tok != TOK_RANGE) {
1427             /* Done */
1428             break;
1429         }
1430
1431         /* Remember the token, skip it, check for equal */
1432         Tok = D->Tok;
1433         NextToken (D);
1434         if (!ConsumeEqual (D)) {
1435             goto ErrorExit;
1436         }
1437
1438         /* Check what the token was */
1439         switch (Tok) {
1440
1441             case TOK_FILE:
1442                 if (!IntConstFollows (D)) {
1443                     goto ErrorExit;
1444                 }
1445                 File = D->IVal;
1446                 InfoBits |= ibFile;
1447                 NextToken (D);
1448                 break;
1449
1450             case TOK_SEGMENT:
1451                 if (!IntConstFollows (D)) {
1452                     goto ErrorExit;
1453                 }
1454                 Segment = D->IVal;
1455                 InfoBits |= ibSegment;
1456                 NextToken (D);
1457                 break;
1458
1459             case TOK_LINE:
1460                 if (!IntConstFollows (D)) {
1461                     goto ErrorExit;
1462                 }
1463                 Line = (cc65_line) D->IVal;
1464                 NextToken (D);
1465                 InfoBits |= ibLine;
1466                 break;
1467
1468             case TOK_RANGE:
1469                 if (!IntConstFollows (D)) {
1470                     goto ErrorExit;
1471                 }
1472                 Start = (cc65_addr) D->IVal;
1473                 NextToken (D);
1474                 if (!ConsumeMinus (D)) {
1475                     goto ErrorExit;
1476                 }
1477                 if (!IntConstFollows (D)) {
1478                     goto ErrorExit;
1479                 }
1480                 End = (cc65_addr) D->IVal;
1481                 NextToken (D);
1482                 InfoBits |= ibRange;
1483                 break;
1484
1485             default:
1486                 /* NOTREACHED */
1487                 UnexpectedToken (D);
1488                 goto ErrorExit;
1489
1490         }
1491
1492         /* Comma or done */
1493         if (D->Tok != TOK_COMMA) {
1494             break;
1495         }
1496         NextToken (D);
1497     }
1498
1499     /* Check for end of line */
1500     if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1501         UnexpectedToken (D);
1502         SkipLine (D);
1503         goto ErrorExit;
1504     }
1505
1506     /* Check for required information */
1507     if ((InfoBits & ibRequired) != ibRequired) {
1508         ParseError (D, CC65_ERROR, "Required attributes missing");
1509         goto ErrorExit;
1510     }
1511
1512     /* Create the line info and remember it */
1513     L = NewLineInfo (File, Segment, Line, Start, End);
1514     CollAppend (&D->LineInfos, L);
1515
1516 ErrorExit:
1517     /* Entry point in case of errors */
1518     return;
1519 }
1520
1521
1522
1523 static void ParseSegment (InputData* D)
1524 /* Parse a SEGMENT line */
1525 {
1526     unsigned        Id;
1527     cc65_addr       Start;
1528     cc65_addr       Size;
1529     StrBuf          SegName = STRBUF_INITIALIZER;
1530     StrBuf          OutputName = STRBUF_INITIALIZER;
1531     unsigned long   OutputOffs;
1532     SegInfo*        S;
1533     enum {
1534         ibNone      = 0x00,
1535         ibId        = 0x01,
1536         ibSegName   = 0x02,
1537         ibStart     = 0x04,
1538         ibSize      = 0x08,
1539         ibAddrSize  = 0x10,
1540         ibType      = 0x20,
1541         ibOutputName= 0x40,
1542         ibOutputOffs= 0x80,
1543         ibRequired  = ibId | ibSegName | ibStart | ibSize | ibAddrSize | ibType,
1544     } InfoBits = ibNone;
1545
1546     /* Skip the SEGMENT token */
1547     NextToken (D);
1548
1549     /* More stuff follows */
1550     while (1) {
1551
1552         Token Tok;
1553
1554         /* Check for an unknown keyword */
1555         if (D->Tok == TOK_IDENT) {
1556             UnknownKeyword (D);
1557             continue;
1558         }
1559
1560         /* Something we know? */
1561         if (D->Tok != TOK_ADDRSIZE      && D->Tok != TOK_ID         &&
1562             D->Tok != TOK_NAME          && D->Tok != TOK_OUTPUTNAME &&
1563             D->Tok != TOK_OUTPUTOFFS    && D->Tok != TOK_SIZE       &&
1564             D->Tok != TOK_START         && D->Tok != TOK_TYPE) {
1565             /* Done */
1566             break;
1567         }
1568
1569         /* Remember the token, skip it, check for equal */
1570         Tok = D->Tok;
1571         NextToken (D);
1572         if (!ConsumeEqual (D)) {
1573             goto ErrorExit;
1574         }
1575
1576         /* Check what the token was */
1577         switch (Tok) {
1578
1579             case TOK_ID:
1580                 if (!IntConstFollows (D)) {
1581                     goto ErrorExit;
1582                 }
1583                 Id = D->IVal;
1584                 InfoBits |= ibId;
1585                 NextToken (D);
1586                 break;
1587
1588             case TOK_NAME:
1589                 if (!StrConstFollows (D)) {
1590                     goto ErrorExit;
1591                 }
1592                 SB_Copy (&SegName, &D->SVal);
1593                 SB_Terminate (&SegName);
1594                 InfoBits |= ibSegName;
1595                 NextToken (D);
1596                 break;
1597
1598             case TOK_OUTPUTNAME:
1599                 if (!StrConstFollows (D)) {
1600                     goto ErrorExit;
1601                 }
1602                 SB_Copy (&OutputName, &D->SVal);
1603                 SB_Terminate (&OutputName);
1604                 InfoBits |= ibOutputName;
1605                 NextToken (D);
1606                 break;
1607
1608             case TOK_OUTPUTOFFS:
1609                 if (!IntConstFollows (D)) {
1610                     goto ErrorExit;
1611                 }
1612                 OutputOffs = D->IVal;
1613                 NextToken (D);
1614                 InfoBits |= ibOutputOffs;
1615                 break;
1616
1617             case TOK_START:
1618                 if (!IntConstFollows (D)) {
1619                     goto ErrorExit;
1620                 }
1621                 Start = (cc65_addr) D->IVal;
1622                 NextToken (D);
1623                 InfoBits |= ibStart;
1624                 break;
1625
1626             case TOK_SIZE:
1627                 if (!IntConstFollows (D)) {
1628                     goto ErrorExit;
1629                 }
1630                 Size = D->IVal;
1631                 NextToken (D);
1632                 InfoBits |= ibSize;
1633                 break;
1634
1635             case TOK_ADDRSIZE:
1636                 NextToken (D);
1637                 InfoBits |= ibAddrSize;
1638                 break;
1639
1640             case TOK_TYPE:
1641                 NextToken (D);
1642                 InfoBits |= ibType;
1643                 break;
1644
1645             default:
1646                 /* NOTREACHED */
1647                 UnexpectedToken (D);
1648                 goto ErrorExit;
1649
1650         }
1651
1652         /* Comma or done */
1653         if (D->Tok != TOK_COMMA) {
1654             break;
1655         }
1656         NextToken (D);
1657     }
1658
1659     /* Check for end of line */
1660     if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1661         UnexpectedToken (D);
1662         SkipLine (D);
1663         goto ErrorExit;
1664     }
1665
1666     /* Check for required information */
1667     if ((InfoBits & ibRequired) != ibRequired) {
1668         ParseError (D, CC65_ERROR, "Required attributes missing");
1669         goto ErrorExit;
1670     }
1671
1672     /* Create the segment info and remember it */
1673     S = NewSegInfo (&SegName, Id, Start, Size, &OutputName, OutputOffs);
1674     CollAppend (&D->Info->SegInfoByName, S);
1675
1676 ErrorExit:
1677     /* Entry point in case of errors */
1678     SB_Done (&SegName);
1679     SB_Done (&OutputName);
1680     return;
1681 }
1682
1683
1684
1685 static void ParseSym (InputData* D)
1686 /* Parse a SYM line */
1687 {
1688     /* Skip the SYM token */
1689     NextToken (D);
1690
1691     /* ### */
1692     SkipLine (D);
1693 }
1694
1695
1696
1697 static void ParseVersion (InputData* D)
1698 /* Parse a VERSION line */
1699 {
1700     enum {
1701         ibNone      = 0x00,
1702         ibMajor     = 0x01,
1703         ibMinor     = 0x02,
1704         ibRequired  = ibMajor | ibMinor,
1705     } InfoBits = ibNone;
1706
1707     /* Skip the VERSION token */
1708     NextToken (D);
1709
1710     /* More stuff follows */
1711     while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1712
1713         switch (D->Tok) {
1714
1715             case TOK_MAJOR:
1716                 NextToken (D);
1717                 if (!ConsumeEqual (D)) {
1718                     goto ErrorExit;
1719                 }
1720                 if (!IntConstFollows (D)) {
1721                     goto ErrorExit;
1722                 }
1723                 D->MajorVersion = D->IVal;
1724                 NextToken (D);
1725                 InfoBits |= ibMajor;
1726                 break;
1727
1728             case TOK_MINOR:
1729                 NextToken (D);
1730                 if (!ConsumeEqual (D)) {
1731                     goto ErrorExit;
1732                 }
1733                 if (!IntConstFollows (D)) {
1734                     goto ErrorExit;
1735                 }
1736                 D->MinorVersion = D->IVal;
1737                 NextToken (D);
1738                 InfoBits |= ibMinor;
1739                 break;
1740
1741             case TOK_IDENT:
1742                 /* Try to skip unknown keywords that may have been added by
1743                  * a later version.
1744                  */
1745                 UnknownKeyword (D);
1746                 break;
1747
1748             default:
1749                 UnexpectedToken (D);
1750                 SkipLine (D);
1751                 goto ErrorExit;
1752         }
1753
1754         /* Comma follows before next attribute */
1755         if (D->Tok == TOK_COMMA) {
1756             NextToken (D);
1757         } else if (D->Tok == TOK_EOL || D->Tok == TOK_EOF) {
1758             break;
1759         } else {
1760             UnexpectedToken (D);
1761             goto ErrorExit;
1762         }
1763     }
1764
1765     /* Check for required information */
1766     if ((InfoBits & ibRequired) != ibRequired) {
1767         ParseError (D, CC65_ERROR, "Required attributes missing");
1768         goto ErrorExit;
1769     }
1770
1771 ErrorExit:
1772     /* Entry point in case of errors */
1773     return;
1774 }
1775
1776
1777
1778 /*****************************************************************************/
1779 /*                              Data processing                              */
1780 /*****************************************************************************/
1781
1782
1783
1784 static SegInfo* FindSegInfoById (InputData* D, unsigned Id)
1785 /* Find the SegInfo with a given Id */
1786 {
1787     /* Get a pointer to the segment info collection */
1788     Collection* SegInfos = &D->Info->SegInfoById;
1789
1790     /* Do a binary search */
1791     int Lo = 0;
1792     int Hi = (int) CollCount (SegInfos) - 1;
1793     while (Lo <= Hi) {
1794
1795         /* Mid of range */
1796         int Cur = (Lo + Hi) / 2;
1797
1798         /* Get item */
1799         SegInfo* CurItem = CollAt (SegInfos, Cur);
1800
1801         /* Found? */
1802         if (Id > CurItem->Id) {
1803             Lo = Cur + 1;
1804         } else if (Id < CurItem->Id) {
1805             Hi = Cur - 1;
1806         } else {
1807             /* Found! */
1808             return CurItem;
1809         }
1810     }
1811
1812     /* Not found */
1813     return 0;
1814 }
1815
1816
1817
1818 static FileInfo* FindFileInfoByName (Collection* FileInfos, const char* FileName)
1819 /* Find the FileInfo for a given file name */
1820 {
1821     /* Do a binary search */
1822     int Lo = 0;
1823     int Hi = (int) CollCount (FileInfos) - 1;
1824     while (Lo <= Hi) {
1825
1826         /* Mid of range */
1827         int Cur = (Lo + Hi) / 2;
1828
1829         /* Get item */
1830         FileInfo* CurItem = CollAt (FileInfos, Cur);
1831
1832         /* Compare */
1833         int Res = strcmp (CurItem->FileName, FileName);
1834
1835         /* Found? */
1836         if (Res < 0) {
1837             Lo = Cur + 1;
1838         } else if (Res > 0) {
1839             Hi = Cur - 1;
1840         } else {
1841             /* Found! */
1842             return CurItem;
1843         }
1844     }
1845
1846     /* Not found */
1847     return 0;
1848 }
1849
1850
1851
1852 static FileInfo* FindFileInfoById (Collection* FileInfos, unsigned Id)
1853 /* Find the FileInfo with a given Id */
1854 {
1855     /* Do a binary search */
1856     int Lo = 0;
1857     int Hi = (int) CollCount (FileInfos) - 1;
1858     while (Lo <= Hi) {
1859
1860         /* Mid of range */
1861         int Cur = (Lo + Hi) / 2;
1862
1863         /* Get item */
1864         FileInfo* CurItem = CollAt (FileInfos, Cur);
1865
1866         /* Found? */
1867         if (Id > CurItem->Id) {
1868             Lo = Cur + 1;
1869         } else if (Id < CurItem->Id) {
1870             Hi = Cur - 1;
1871         } else {
1872             /* Found! */
1873             return CurItem;
1874         }
1875     }
1876
1877     /* Not found */
1878     return 0;
1879 }
1880
1881
1882
1883 static void ProcessSegInfo (InputData* D)
1884 /* Postprocess segment infos */
1885 {
1886     unsigned I;
1887
1888     /* Get pointers to the segment info collections */
1889     Collection* SegInfoByName = &D->Info->SegInfoByName;
1890     Collection* SegInfoById   = &D->Info->SegInfoById;
1891
1892     /* Sort the segment infos by name */
1893     CollSort (SegInfoByName, CompareSegInfoByName);
1894
1895     /* Copy all items over to the collection that will get sorted by id */
1896     for (I = 0; I < CollCount (SegInfoByName); ++I) {
1897         CollAppend (SegInfoById, CollAt (SegInfoByName, I));
1898     }
1899
1900     /* Sort this collection */
1901     CollSort (SegInfoById, CompareSegInfoById);
1902 }
1903
1904
1905
1906 static void ProcessFileInfo (InputData* D)
1907 /* Postprocess file infos */
1908 {
1909     /* Get pointers to the file info collections */
1910     Collection* FileInfoByName = &D->Info->FileInfoByName;
1911     Collection* FileInfoById   = &D->Info->FileInfoById;
1912
1913     /* First, sort the file infos, so we can check for duplicates and do
1914      * binary search.
1915      */
1916     CollSort (FileInfoByName, CompareFileInfoByName);
1917
1918     /* Cannot work on an empty collection */
1919     if (CollCount (FileInfoByName) > 0) {
1920
1921         /* Walk through the file infos sorted by name and check for duplicates.
1922          * If we find some, warn and remove them, so the file infos are unique
1923          * after that step.
1924          */
1925         FileInfo* F = CollAt (FileInfoByName, 0);
1926         unsigned I = 1;
1927         while (I < CollCount (FileInfoByName)) {
1928             FileInfo* Next = CollAt (FileInfoByName, I);
1929             if (strcmp (F->FileName, Next->FileName) == 0) {
1930                 /* Warn only if time stamp and/or size is different */
1931                 if (F->Size != Next->Size || F->MTime != Next->MTime) {
1932                     ParseError (D,
1933                                 CC65_WARNING,
1934                                 "Duplicate file entry for \"%s\"",
1935                                 F->FileName);
1936                 }
1937                 /* Remove the duplicate entry */
1938                 FreeFileInfo (Next);
1939                 CollDelete (FileInfoByName, I);
1940             } else {
1941                 /* This one is ok, check the next entry */
1942                 F = Next;
1943                 ++I;
1944             }
1945         }
1946
1947         /* Copy the file infos to another collection that will be sorted by id */
1948         for (I = 0; I < CollCount (FileInfoByName); ++I) {
1949             CollAppend (FileInfoById, CollAt (FileInfoByName, I));
1950         }
1951
1952         /* Sort this collection */
1953         CollSort (FileInfoById, CompareFileInfoById);
1954     }
1955 }
1956
1957
1958
1959 static void ProcessLineInfo (InputData* D)
1960 /* Postprocess line infos */
1961 {
1962     /* Get pointers to the collections */
1963     Collection* LineInfos = &D->LineInfos;
1964     Collection* FileInfos = &D->Info->FileInfoByName;
1965
1966     /* Walk over the line infos and replace the id numbers of file and segment
1967      * with pointers to the actual structs. Add the line info to each file
1968      * where it is defined.
1969      */
1970     unsigned I = 0;
1971     FileInfo* LastFileInfo = 0;
1972     SegInfo*  LastSegInfo  = 0;
1973     while (I < CollCount (LineInfos)) {
1974
1975         FileInfo* F;
1976         SegInfo*  S;
1977
1978         /* Get LineInfo struct */
1979         LineInfo* L = CollAt (LineInfos, I);
1980
1981         /* Find the FileInfo that corresponds to Id. We cache the last file
1982          * info in LastFileInfo to speedup searching.
1983          */
1984         if (LastFileInfo && LastFileInfo->Id == L->File.Id) {
1985             F = LastFileInfo;
1986         } else {
1987             F = FindFileInfoById (&D->Info->FileInfoById, L->File.Id);
1988
1989             /* If we have no corresponding file info, print a warning and
1990              * remove the line info.
1991              */
1992             if (F == 0) {
1993                 ParseError (D,
1994                             CC65_ERROR,
1995                             "No file info for file with id %u",
1996                             L->File.Id);
1997                 FreeLineInfo (L);
1998                 CollDelete (LineInfos, I);
1999                 continue;
2000             }
2001
2002             /* Otherwise remember it for later */
2003             LastFileInfo = F;
2004         }
2005
2006         /* Replace the file id by a pointer to the file info */
2007         L->File.Info = F;
2008
2009         /* Find the SegInfo that corresponds to Id. We cache the last file
2010          * info in LastSegInfo to speedup searching.
2011          */
2012         if (LastSegInfo && LastSegInfo->Id == L->Seg.Id) {
2013             S = LastSegInfo;
2014         } else {
2015             S = FindSegInfoById (D, L->Seg.Id);
2016
2017             /* If we have no corresponding segment info, print a warning and
2018              * remove the line info.
2019              */
2020             if (S == 0) {
2021                 ParseError (D,
2022                             CC65_ERROR,
2023                             "No segment info for segment with id %u",
2024                             L->Seg.Id);
2025                 FreeLineInfo (L);
2026                 CollDelete (LineInfos, I);
2027                 continue;
2028             }
2029
2030             /* Otherwise remember it for later */
2031             LastSegInfo = S;
2032         }
2033
2034         /* Replace the segment id by a pointer to the segment info */
2035         L->Seg.Info = S;
2036
2037         /* Add this line info to the file where it is defined */
2038         CollAppend (&F->LineInfoByAddr, L);
2039         CollAppend (&F->LineInfoByLine, L);
2040
2041         /* Next one */
2042         ++I;
2043     }
2044
2045     /* Walk over all files and sort the line infos for each file so we can
2046      * do a binary search later.
2047      */
2048     for (I = 0; I < CollCount (FileInfos); ++I) {
2049
2050         /* Get a pointer to this file info */
2051         FileInfo* F = CollAt (FileInfos, I);
2052
2053         /* Sort the line infos for this file */
2054         CollSort (&F->LineInfoByAddr, CompareLineInfoByAddr);
2055         CollSort (&F->LineInfoByLine, CompareLineInfoByLine);
2056
2057         /* If there are line info entries, place the first and last address
2058          * of into the FileInfo struct itself, so we can rule out a FileInfo
2059          * quickly when mapping an address to a line info.
2060          */
2061         if (CollCount (&F->LineInfoByAddr) > 0) {
2062             F->Start = ((const LineInfo*) CollFirst (&F->LineInfoByAddr))->Start;
2063             F->End   = ((const LineInfo*) CollLast (&F->LineInfoByAddr))->End;
2064         }
2065
2066     }
2067 }
2068
2069
2070
2071 static LineInfo* FindLineInfoByAddr (FileInfo* F, cc65_addr Addr)
2072 /* Find the LineInfo for a given address */
2073 {
2074     Collection* LineInfoByAddr;
2075     int         Hi;
2076     int         Lo;
2077
2078
2079     /* Each file info contains the first and last address for which line
2080      * info is available, so we can rule out non matching ones quickly.
2081      */
2082     if (Addr < F->Start || Addr > F->End) {
2083         return 0;
2084     }
2085
2086     /* Get a pointer to the line info collection for this file */
2087     LineInfoByAddr = &F->LineInfoByAddr;
2088
2089     /* Do a binary search */
2090     Lo = 0;
2091     Hi = (int) CollCount (LineInfoByAddr) - 1;
2092     while (Lo <= Hi) {
2093
2094         /* Mid of range */
2095         int Cur = (Lo + Hi) / 2;
2096
2097         /* Get item */
2098         LineInfo* CurItem = CollAt (LineInfoByAddr, Cur);
2099
2100         /* Found? */
2101         if (Addr < CurItem->Start) {
2102             Hi = Cur - 1;
2103         } else if (Addr > CurItem->End) {
2104             Lo = Cur + 1;
2105         } else {
2106             /* Found! */
2107             return CurItem;
2108         }
2109     }
2110
2111     /* Not found */
2112     return 0;
2113 }
2114
2115
2116
2117 static LineInfo* FindLineInfoByLine (FileInfo* F, cc65_line Line)
2118 /* Find the LineInfo for a given line number */
2119 {
2120     int         Hi;
2121     int         Lo;
2122
2123
2124     /* Get a pointer to the line info collection for this file */
2125     Collection* LineInfoByLine = &F->LineInfoByLine;
2126
2127     /* Do a binary search */
2128     Lo = 0;
2129     Hi = (int) CollCount (LineInfoByLine) - 1;
2130     while (Lo <= Hi) {
2131
2132         /* Mid of range */
2133         int Cur = (Lo + Hi) / 2;
2134
2135         /* Get item */
2136         LineInfo* CurItem = CollAt (LineInfoByLine, Cur);
2137
2138         /* Found? */
2139         if (Line < CurItem->Line) {
2140             Hi = Cur - 1;
2141         } else if (Line > CurItem->Line) {
2142             Lo = Cur + 1;
2143         } else {
2144             /* Found! */
2145             return CurItem;
2146         }
2147     }
2148
2149     /* Not found */
2150     return 0;
2151 }
2152
2153
2154
2155 /*****************************************************************************/
2156 /*                                   Code                                    */
2157 /*****************************************************************************/
2158
2159
2160
2161 cc65_dbginfo cc65_read_dbginfo (const char* FileName, cc65_errorfunc ErrFunc)
2162 /* Parse the debug info file with the given name. On success, the function
2163  * will return a pointer to an opaque cc65_dbginfo structure, that must be
2164  * passed to the other functions in this module to retrieve information.
2165  * errorfunc is called in case of warnings and errors. If the file cannot be
2166  * read successfully, NULL is returned.
2167  */
2168 {
2169     /* Data structure used to control scanning and parsing */
2170     InputData D = {
2171         0,                      /* Name of input file */
2172         1,                      /* Line number */
2173         0,                      /* Input file */
2174         0,                      /* Line at start of current token */
2175         0,                      /* Column at start of current token */
2176         0,                      /* Number of errors */
2177         0,                      /* Input file */
2178         ' ',                    /* Input character */
2179         TOK_INVALID,            /* Input token */
2180         0,                      /* Integer constant */
2181         STRBUF_INITIALIZER,     /* String constant */
2182         0,                      /* Function called in case of errors */
2183         0,                      /* Major version number */
2184         0,                      /* Minor version number */
2185         COLLECTION_INITIALIZER, /* Line information */
2186         0,                      /* Pointer to debug info */
2187     };
2188     D.FileName = FileName;
2189     D.Error    = ErrFunc;
2190
2191     /* Open the input file */
2192     D.F = fopen (D.FileName, "r");
2193     if (D.F == 0) {
2194         /* Cannot open */
2195         ParseError (&D, CC65_ERROR,
2196                     "Cannot open input file \"%s\": %s",
2197                      D.FileName, strerror (errno));
2198         return 0;
2199     }
2200
2201     /* Create a new debug info struct */
2202     D.Info = NewDbgInfo ();
2203
2204     /* Prime the pump */
2205     NextToken (&D);
2206
2207     /* The first line in the file must specify version information */
2208     if (D.Tok != TOK_VERSION) {
2209         ParseError (&D, CC65_ERROR,
2210                     "\"version\" keyword missing in first line - this is not "
2211                     "a valid debug info file");
2212     } else {
2213
2214         /* Parse the version directive and check the version */
2215         ParseVersion (&D);
2216         if (D.MajorVersion > VER_MAJOR) {
2217             ParseError (&D, CC65_WARNING,
2218                         "The format of this debug info file is newer than what we "
2219                         "know. Will proceed but probably fail. Version found = %u, "
2220                         "version supported = %u",
2221                         D.MajorVersion, VER_MAJOR);
2222         }
2223         ConsumeEOL (&D);
2224
2225         /* Parse lines */
2226         while (D.Tok != TOK_EOF) {
2227
2228             switch (D.Tok) {
2229
2230                 case TOK_FILE:
2231                     ParseFile (&D);
2232                     break;
2233
2234                 case TOK_LINE:
2235                     ParseLine (&D);
2236                     break;
2237
2238                 case TOK_SEGMENT:
2239                     ParseSegment (&D);
2240                     break;
2241
2242                 case TOK_SYM:
2243                     ParseSym (&D);
2244                     break;
2245
2246                 case TOK_IDENT:
2247                     /* Output a warning, then skip the line with the unknown
2248                      * keyword that may have been added by a later version.
2249                      */
2250                     ParseError (&D, CC65_WARNING,
2251                                 "Unknown keyword \"%s\" - skipping",
2252                                 SB_GetConstBuf (&D.SVal));
2253
2254                     SkipLine (&D);
2255                     break;
2256
2257                 default:
2258                     UnexpectedToken (&D);
2259
2260             }
2261
2262             /* EOL or EOF must follow */
2263             ConsumeEOL (&D);
2264         }
2265     }
2266
2267     /* Close the file */
2268     fclose (D.F);
2269
2270     /* Free memory allocated for SVal */
2271     SB_Done (&D.SVal);
2272
2273     /* In case of errors, delete the debug info already allocated and
2274      * return NULL
2275      */
2276     if (D.Errors > 0) {
2277         /* Free allocated stuff */
2278         unsigned I;
2279         for (I = 0; I < CollCount (&D.LineInfos); ++I) {
2280             FreeLineInfo (CollAt (&D.LineInfos, I));
2281         }
2282         DoneCollection (&D.LineInfos);
2283         FreeDbgInfo (D.Info);
2284         return 0;
2285     }
2286
2287     /* We do now have all the information from the input file. Do
2288      * postprocessing.
2289      */
2290     ProcessSegInfo (&D);
2291     ProcessFileInfo (&D);
2292     ProcessLineInfo (&D);
2293
2294     /* Free the collection that contained the line info */
2295     DoneCollection (&D.LineInfos);
2296
2297     /* Return the debug info struct that was created */
2298     return D.Info;
2299 }
2300
2301
2302
2303 void cc65_free_dbginfo (cc65_dbginfo Handle)
2304 /* Free debug information read from a file */
2305 {
2306     if (Handle) {
2307         FreeDbgInfo (Handle);
2308     }
2309 }
2310
2311
2312
2313 cc65_lineinfo* cc65_lineinfo_byaddr (cc65_dbginfo Handle, unsigned long Addr)
2314 /* Return line information for the given address. The function returns 0
2315  * if no line information was found.
2316  */
2317 {
2318     unsigned        I;
2319     Collection*     FileInfoByName;
2320     cc65_lineinfo*  D = 0;
2321
2322     /* We will place a list of line infos in a collection */
2323     Collection LineInfos = COLLECTION_INITIALIZER;
2324
2325     /* Check the parameter */
2326     assert (Handle != 0);
2327
2328     /* Walk over all files and search for matching line infos */
2329     FileInfoByName = &((DbgInfo*) Handle)->FileInfoByName;
2330     for (I = 0; I < CollCount (FileInfoByName); ++I) {
2331         /* Check if the file contains line info for this address */
2332         LineInfo* L = FindLineInfoByAddr (CollAt (FileInfoByName, I), Addr);
2333         if (L != 0) {
2334             CollAppend (&LineInfos, L);
2335         }
2336     }
2337
2338     /* Do we have line infos? */
2339     if (CollCount (&LineInfos) > 0) {
2340
2341         /* Prepare the struct we will return to the caller */
2342         D = xmalloc (sizeof (*D) +
2343                      (CollCount (&LineInfos) - 1) * sizeof (D->data[0]));
2344         D->count = CollCount (&LineInfos);
2345         for (I = 0; I < D->count; ++I) {
2346             /* Copy data */
2347             CopyLineInfo (D->data + I, CollAt (&LineInfos, I));
2348         }
2349     }
2350
2351     /* Free the line info collection */
2352     DoneCollection (&LineInfos);
2353
2354     /* Return the struct we've created */
2355     return D;
2356 }
2357
2358
2359
2360 cc65_lineinfo* cc65_lineinfo_byname (cc65_dbginfo Handle, const char* FileName,
2361                                      cc65_line Line)
2362 /* Return line information for a file/line number combination. The function
2363  * returns NULL if no line information was found.
2364  */
2365 {
2366     DbgInfo*        Info;
2367     FileInfo*       F;
2368     LineInfo*       L;
2369     cc65_lineinfo*  D;
2370
2371     /* Check the parameter */
2372     assert (Handle != 0);
2373
2374     /* The handle is actually a pointer to a debug info struct */
2375     Info = (DbgInfo*) Handle;
2376
2377     /* Get the file info */
2378     F = FindFileInfoByName (&Info->FileInfoByName, FileName);
2379     if (F == 0) {
2380         /* File not found */
2381         return 0;
2382     }
2383
2384     /* Search in the file for the given line */
2385     L = FindLineInfoByLine (F, Line);
2386     if (L == 0) {
2387         /* Line not found */
2388         return 0;
2389     }
2390
2391     /* Prepare the struct we will return to the caller */
2392     D = xmalloc (sizeof (*D));
2393     D->count = 1;
2394
2395     /* Copy data */
2396     CopyLineInfo (D->data, L);
2397
2398     /* Return the allocated struct */
2399     return D;
2400 }
2401
2402
2403
2404 void cc65_free_lineinfo (cc65_dbginfo Handle, cc65_lineinfo* Info)
2405 /* Free line info returned by one of the other functions */
2406 {
2407     /* Just for completeness, check the handle */
2408     assert (Handle != 0);
2409
2410     /* Just free the memory */
2411     xfree (Info);
2412 }
2413
2414
2415
2416 cc65_sourceinfo* cc65_get_sourcelist (cc65_dbginfo Handle)
2417 /* Return a list of all source files */
2418 {
2419     DbgInfo*            Info;
2420     Collection*         FileInfoByName;
2421     cc65_sourceinfo*    D;
2422     unsigned            I;
2423
2424     /* Check the parameter */
2425     assert (Handle != 0);
2426
2427     /* The handle is actually a pointer to a debug info struct */
2428     Info = (DbgInfo*) Handle;
2429
2430     /* Get a pointer to the file list */
2431     FileInfoByName = &Info->FileInfoByName;
2432
2433     /* Allocate memory for the data structure returned to the caller */
2434     D = xmalloc (sizeof (*D) - sizeof (D->data[0]) +
2435                  CollCount (FileInfoByName) * sizeof (D->data[0]));
2436
2437     /* Fill in the data */
2438     D->count = CollCount (FileInfoByName);
2439     for (I = 0; I < CollCount (FileInfoByName); ++I) {
2440
2441         /* Get this item */
2442         FileInfo* F = CollAt (FileInfoByName, I);
2443
2444         /* Copy the data */
2445         D->data[I].source_name  = F->FileName;
2446         D->data[I].source_size  = F->Size;
2447         D->data[I].source_mtime = F->MTime;
2448     }
2449
2450     /* Return the result */
2451     return D;
2452 }
2453
2454
2455
2456 void cc65_free_sourceinfo (cc65_dbginfo Handle, cc65_sourceinfo* Info)
2457 /* Free a source info record */
2458 {
2459     /* Just for completeness, check the handle */
2460     assert (Handle != 0);
2461
2462     /* Free the memory */
2463     xfree (Info);
2464 }
2465
2466
2467
2468 cc65_segmentinfo* cc65_get_segmentlist (cc65_dbginfo Handle)
2469 /* Return a list of all segments referenced in the debug information */
2470 {
2471     DbgInfo*            Info;
2472     Collection*         SegInfoByName;
2473     cc65_segmentinfo*   D;
2474     unsigned            I;
2475
2476     /* Check the parameter */
2477     assert (Handle != 0);
2478
2479     /* The handle is actually a pointer to a debug info struct */
2480     Info = (DbgInfo*) Handle;
2481
2482     /* Get a pointer to the file list */
2483     SegInfoByName = &Info->SegInfoByName;
2484
2485     /* Allocate memory for the data structure returned to the caller */
2486     D = xmalloc (sizeof (*D) - sizeof (D->data[0]) +
2487                  CollCount (SegInfoByName) * sizeof (D->data[0]));
2488
2489     /* Fill in the data */
2490     D->count = CollCount (SegInfoByName);
2491     for (I = 0; I < CollCount (SegInfoByName); ++I) {
2492
2493         /* Get this item */
2494         SegInfo* S = CollAt (SegInfoByName, I);
2495
2496         /* Copy the data */
2497         D->data[I].segment_name  = S->SegName;
2498         D->data[I].segment_start = S->Start;
2499         D->data[I].segment_size  = S->Size;
2500         D->data[I].output_name   = S->OutputName;
2501         D->data[I].output_offs   = S->OutputOffs;
2502     }
2503
2504     /* Return the result */
2505     return D;
2506 }
2507
2508
2509
2510 void cc65_free_segmentinfo (cc65_dbginfo Handle, cc65_segmentinfo* Info)
2511 /* Free a segment info record */
2512 {
2513     /* Just for completeness, check the handle */
2514     assert (Handle != 0);
2515
2516     /* Free the memory */
2517     xfree (Info);
2518 }
2519
2520
2521