1 /*****************************************************************************/
5 /* cc65 debug info handling */
9 /* (C) 2010, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
49 /*****************************************************************************/
51 /*****************************************************************************/
56 typedef struct StrBuf StrBuf;
58 char* Buf; /* Pointer to buffer */
59 unsigned Len; /* Length of the string */
60 unsigned Allocated; /* Size of allocated memory */
63 /* Initializer for a string buffer */
64 #define STRBUF_INITIALIZER { 0, 0, 0 }
66 /* An array of pointers that grows if needed */
67 typedef struct Collection Collection;
69 unsigned Count; /* Number of items in the list */
70 unsigned Size; /* Size of allocated array */
71 void** Items; /* Array with dynamic size */
74 /* Initializer for static collections */
75 #define COLLECTION_INITIALIZER { 0, 0, 0 }
79 /* Data structure containing information from the debug info file. A pointer
80 * to this structure is passed as handle to callers from the outside.
82 typedef struct DbgInfo DbgInfo;
84 Collection FileInfos; /* Collection with file infos */
90 TOK_INVALID, /* Invalid token */
91 TOK_EOF, /* End of file reached */
93 TOK_INTCON, /* Integer constant */
94 TOK_STRCON, /* String constant */
102 TOK_ABSOLUTE, /* ABSOLUTE keyword */
103 TOK_ADDRSIZE, /* ADDRSIZE keyword */
104 TOK_EQUATE, /* EQUATE keyword */
105 TOK_FILE, /* FILE keyword */
106 TOK_LABEL, /* LABEL keyword */
107 TOK_LINE, /* LINE keyword */
108 TOK_LONG, /* LONG_keyword */
109 TOK_MAJOR, /* MAJOR keyword */
110 TOK_MINOR, /* MINOR keyword */
111 TOK_MTIME, /* MTIME keyword */
112 TOK_RANGE, /* RANGE keyword */
113 TOK_RO, /* RO keyword */
114 TOK_RW, /* RW keyword */
115 TOK_SEGMENT, /* SEGMENT keyword */
116 TOK_SIZE, /* SIZE keyword */
117 TOK_START, /* START keyword */
118 TOK_SYM, /* SYM keyword */
119 TOK_TYPE, /* TYPE keyword */
120 TOK_VALUE, /* VALUE keyword */
121 TOK_VERSION, /* VERSION keyword */
122 TOK_ZEROPAGE, /* ZEROPAGE keyword */
124 TOK_IDENT, /* To catch unknown keywords */
127 /* Data used when parsing the debug info file */
128 typedef struct InputData InputData;
130 const char* FileName; /* Name of input file */
131 cc65_line Line; /* Current line number */
132 unsigned Col; /* Current column number */
133 cc65_line SLine; /* Line number at start of token */
134 unsigned SCol; /* Column number at start of token */
135 unsigned Errors; /* Number of errors */
136 FILE* F; /* Input file */
137 int C; /* Input character */
138 Token Tok; /* Token from input stream */
139 unsigned long IVal; /* Integer constant */
140 StrBuf SVal; /* String constant */
141 cc65_errorfunc Error; /* Function called in case of errors */
142 unsigned MajorVersion; /* Major version number */
143 unsigned MinorVersion; /* Minor version number */
144 Collection LineInfos; /* Line information */
145 DbgInfo* Info; /* Pointer to debug info */
148 /* Internally used file info struct */
149 typedef struct FileInfo FileInfo;
151 unsigned long Size; /* Size of file */
152 unsigned long MTime; /* Modification time */
153 cc65_addr Start; /* Start address of line infos */
154 cc65_addr End; /* End address of line infos */
155 Collection LineInfos; /* Line infos for this file */
156 char FileName[1]; /* Name of file with full path */
159 /* Internally used line info struct */
160 typedef struct LineInfo LineInfo;
162 cc65_addr Start; /* Start of data range */
163 cc65_addr End; /* End of data range */
164 cc65_line Line; /* Line number */
165 FileInfo* FileInfo; /* Pointer to file info */
166 char FileName[1]; /* Name of file */
171 /*****************************************************************************/
173 /*****************************************************************************/
177 static void NextToken (InputData* D);
178 /* Read the next token from the input stream */
182 /*****************************************************************************/
183 /* Memory allocation */
184 /*****************************************************************************/
188 static void* xmalloc (size_t Size)
189 /* Allocate memory, check for out of memory condition. Do some debugging */
193 /* Allow zero sized requests and return NULL in this case */
196 /* Allocate memory */
199 /* Check for errors */
203 /* Return a pointer to the block */
209 static void* xrealloc (void* P, size_t Size)
210 /* Reallocate a memory block, check for out of memory */
212 /* Reallocate the block */
213 void* N = realloc (P, Size);
215 /* Check for errors */
216 assert (N != 0 || Size == 0);
218 /* Return the pointer to the new block */
224 static void xfree (void* Block)
225 /* Free the block, do some debugging */
232 /*****************************************************************************/
233 /* Dynamic strings */
234 /*****************************************************************************/
238 static void SB_Done (StrBuf* B)
239 /* Free the data of a string buffer (but not the struct itself) */
248 static void SB_Realloc (StrBuf* B, unsigned NewSize)
249 /* Reallocate the string buffer space, make sure at least NewSize bytes are
253 /* Get the current size, use a minimum of 8 bytes */
254 unsigned NewAllocated = B->Allocated;
255 if (NewAllocated == 0) {
259 /* Round up to the next power of two */
260 while (NewAllocated < NewSize) {
264 /* Reallocate the buffer. Beware: The allocated size may be zero while the
265 * length is not. This means that we have a buffer that wasn't allocated
269 /* Just reallocate the block */
270 B->Buf = xrealloc (B->Buf, NewAllocated);
272 /* Allocate a new block and copy */
273 B->Buf = memcpy (xmalloc (NewAllocated), B->Buf, B->Len);
276 /* Remember the new block size */
277 B->Allocated = NewAllocated;
282 static unsigned SB_GetLen (const StrBuf* B)
283 /* Return the length of the buffer contents */
290 static const char* SB_GetConstBuf (const StrBuf* B)
291 /* Return a buffer pointer */
298 static void SB_Terminate (StrBuf* B)
299 /* Zero terminate the given string buffer. NOTE: The terminating zero is not
300 * accounted for in B->Len, if you want that, you have to use AppendChar!
303 unsigned NewLen = B->Len + 1;
304 if (NewLen > B->Allocated) {
305 SB_Realloc (B, NewLen);
307 B->Buf[B->Len] = '\0';
312 static void SB_Clear (StrBuf* B)
313 /* Clear the string buffer (make it empty) */
320 static void SB_AppendChar (StrBuf* B, int C)
321 /* Append a character to a string buffer */
323 unsigned NewLen = B->Len + 1;
324 if (NewLen > B->Allocated) {
325 SB_Realloc (B, NewLen);
327 B->Buf[B->Len] = (char) C;
333 /*****************************************************************************/
335 /*****************************************************************************/
339 static Collection* InitCollection (Collection* C)
340 /* Initialize a collection and return it. */
342 /* Intialize the fields. */
347 /* Return the new struct */
353 static void DoneCollection (Collection* C)
354 /* Free the data for a collection. This will not free the data contained in
358 /* Free the pointer array */
364 static unsigned CollCount (const Collection* C)
365 /* Return the number of items in the collection */
372 static void CollGrow (Collection* C, unsigned Size)
373 /* Grow the collection C so it is able to hold Size items without a resize
374 * being necessary. This can be called for performance reasons if the number
375 * of items to be placed in the collection is known in advance.
380 /* Ignore the call if the collection is already large enough */
381 if (Size <= C->Size) {
385 /* Grow the collection */
387 NewItems = xmalloc (C->Size * sizeof (void*));
388 memcpy (NewItems, C->Items, C->Count * sizeof (void*));
395 static void CollInsert (Collection* C, void* Item, unsigned Index)
396 /* Insert the data at the given position in the collection */
398 /* Check for invalid indices */
399 assert (Index <= C->Count);
401 /* Grow the array if necessary */
402 if (C->Count >= C->Size) {
404 CollGrow (C, (C->Size == 0)? 8 : C->Size * 2);
407 /* Move the existing elements if needed */
408 if (C->Count != Index) {
409 memmove (C->Items+Index+1, C->Items+Index, (C->Count-Index) * sizeof (void*));
413 /* Store the new item */
414 C->Items[Index] = Item;
419 static void CollAppend (Collection* C, void* Item)
420 /* Append an item to the end of the collection */
422 /* Insert the item at the end of the current list */
423 CollInsert (C, Item, C->Count);
428 static void* CollAt (Collection* C, unsigned Index)
429 /* Return the item at the given index */
431 /* Check the index */
432 assert (Index < C->Count);
434 /* Return the element */
435 return C->Items[Index];
440 static void* CollFirst (Collection* C)
441 /* Return the first item in a collection */
443 /* We must have at least one entry */
444 assert (C->Count > 0);
446 /* Return the element */
452 static void* CollLast (Collection* C)
453 /* Return the last item in a collection */
455 /* We must have at least one entry */
456 assert (C->Count > 0);
458 /* Return the element */
459 return C->Items[C->Count-1];
464 static void CollDelete (Collection* C, unsigned Index)
465 /* Remove the item with the given index from the collection. This will not
466 * free the item itself, just the pointer. All items with higher indices
467 * will get moved to a lower position.
470 /* Check the index */
471 assert (Index < C->Count);
473 /* Remove the item pointer */
475 memmove (C->Items+Index, C->Items+Index+1, (C->Count-Index) * sizeof (void*));
480 static void CollReplace (Collection* C, void* Item, unsigned Index)
481 /* Replace the item at the given position. The old item will not be freed,
482 * just the pointer will get replaced.
485 /* Check the index */
486 assert (Index < C->Count);
488 /* Replace the item pointer */
489 C->Items[Index] = Item;
494 static void CollQuickSort (Collection* C, int Lo, int Hi,
495 int (*Compare) (const void*, const void*))
496 /* Internal recursive sort function. */
498 /* Get a pointer to the items */
499 void** Items = C->Items;
506 while (I <= J && Compare (Items[Lo], Items[I]) >= 0) {
509 while (I <= J && Compare (Items[Lo], Items[J]) < 0) {
514 void* Tmp = Items[I];
523 void* Tmp = Items[J];
524 Items[J] = Items[Lo];
527 if (J > (Hi + Lo) / 2) {
528 CollQuickSort (C, J + 1, Hi, Compare);
531 CollQuickSort (C, Lo, J - 1, Compare);
539 void CollSort (Collection* C, int (*Compare) (const void*, const void*))
540 /* Sort the collection using the given compare function. */
543 CollQuickSort (C, 0, C->Count-1, Compare);
549 /*****************************************************************************/
551 /*****************************************************************************/
555 static LineInfo* NewLineInfo (const StrBuf* FileName)
556 /* Create a new LineInfo struct and return it */
558 /* Allocate memory */
559 LineInfo* L = xmalloc (sizeof (LineInfo) + SB_GetLen (FileName));
566 memcpy (L->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
574 static void FreeLineInfo (LineInfo* L)
575 /* Free a LineInfo struct */
582 static LineInfo* PreenLineInfo (LineInfo* L, FileInfo* F)
583 /* Replace the name by file information */
585 /* Shrink the LineInfo struct removing the FfileName field */
586 L = xrealloc (L, sizeof (*L) - 1);
588 /* Set the FileInfo pointer instead */
591 /* Return the result */
597 static int CompareLineInfo (const void* L, const void* R)
598 /* Helper function to sort line infos in a collection */
600 /* Sort by start of range */
601 if (((const LineInfo*) L)->Start > ((const LineInfo*) R)->Start) {
603 } else if (((const LineInfo*) L)->Start < ((const LineInfo*) R)->Start) {
612 /*****************************************************************************/
614 /*****************************************************************************/
618 static FileInfo* NewFileInfo (const StrBuf* FileName)
619 /* Create a new FileInfo struct and return it */
621 /* Allocate memory */
622 FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
627 F->Start = ~(cc65_addr)0;
629 InitCollection (&F->LineInfos);
630 memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
638 static void FreeFileInfo (FileInfo* F)
639 /* Free a FileInfo struct */
643 /* Walk through the collection with line infos and delete them */
644 for (I = 0; I < CollCount (&F->LineInfos); ++I) {
645 FreeLineInfo (CollAt (&F->LineInfos, I));
647 DoneCollection (&F->LineInfos);
649 /* Free the file info structure itself */
655 static int CompareFileInfo (const void* L, const void* R)
656 /* Helper function to sort file infos in a collection */
658 /* Sort by file name */
659 return strcmp (((const FileInfo*) L)->FileName,
660 ((const FileInfo*) R)->FileName);
665 /*****************************************************************************/
667 /*****************************************************************************/
671 static DbgInfo* NewDbgInfo (void)
672 /* Create a new DbgInfo struct and return it */
674 /* Allocate memory */
675 DbgInfo* Info = xmalloc (sizeof (DbgInfo));
678 InitCollection (&Info->FileInfos);
686 static void FreeDbgInfo (DbgInfo* Info)
687 /* Free a DbgInfo struct */
692 for (I = 0; I < CollCount (&Info->FileInfos); ++I) {
693 FreeFileInfo (CollAt (&Info->FileInfos, I));
695 DoneCollection (&Info->FileInfos);
697 /* Free the structure itself */
703 /*****************************************************************************/
704 /* Helper functions */
705 /*****************************************************************************/
709 static void ParseError (InputData* D, cc65_error_severity Type, const char* Msg, ...)
710 /* Call the user supplied parse error function */
716 /* Test-format the error message so we know how much space to allocate */
718 MsgSize = vsnprintf (0, 0, Msg, ap);
721 /* Allocate memory */
722 E = xmalloc (sizeof (*E) + MsgSize);
724 /* Write data to E */
726 E->name = D->FileName;
730 vsnprintf (E->errormsg, MsgSize+1, Msg, ap);
733 /* Call the caller:-) */
736 /* Free the data structure */
740 if (Type == CC65_ERROR) {
747 static void SkipLine (InputData* D)
748 /* Error recovery routine. Skip tokens until EOL or EOF is reached */
750 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
757 static void UnexpectedToken (InputData* D)
758 /* Call ParseError with a message about an unexpected input token */
760 ParseError (D, CC65_ERROR, "Unexpected input token %d", D->Tok);
766 static void MissingAttribute (InputData* D, const char* AttrName)
767 /* Print an error about a missing attribute */
769 ParseError (D, CC65_ERROR, "Attribute \"%s\" is mising", AttrName);
774 /*****************************************************************************/
775 /* Scanner and parser */
776 /*****************************************************************************/
780 static int DigitVal (int C)
781 /* Return the value for a numeric digit. Return -1 if C is invalid */
785 } else if (isxdigit (C)) {
786 return toupper (C) - 'A' + 10;
794 static void NextChar (InputData* D)
795 /* Read the next character from the input. Count lines and columns */
797 /* Check if we've encountered EOF before */
811 static void NextToken (InputData* D)
812 /* Read the next token from the input stream */
814 static const struct KeywordEntry {
815 const char Keyword[10];
818 { "absolute", TOK_ABSOLUTE },
819 { "addrsize", TOK_ADDRSIZE },
820 { "equate", TOK_EQUATE },
821 { "file", TOK_FILE },
822 { "label", TOK_LABEL },
823 { "line", TOK_LINE },
824 { "long", TOK_LONG },
825 { "major", TOK_MAJOR },
826 { "minor", TOK_MINOR },
827 { "mtime", TOK_MTIME },
828 { "range", TOK_RANGE },
831 { "segment", TOK_SEGMENT },
832 { "size", TOK_SIZE },
833 { "start", TOK_START },
835 { "type", TOK_TYPE },
836 { "value", TOK_VALUE },
837 { "version", TOK_VERSION },
838 { "zeropage", TOK_ZEROPAGE },
842 /* Skip whitespace */
843 while (D->C == ' ' || D->C == '\t') {
847 /* Remember the current position as start of the next token */
852 if (D->C == '_' || isalpha (D->C)) {
854 const struct KeywordEntry* Entry;
856 /* Read the identifier */
858 while (D->C == '_' || isalnum (D->C)) {
859 SB_AppendChar (&D->SVal, D->C);
862 SB_Terminate (&D->SVal);
864 /* Search the identifier in the keyword table */
865 Entry = bsearch (SB_GetConstBuf (&D->SVal),
867 sizeof (KeywordTable) / sizeof (KeywordTable[0]),
868 sizeof (KeywordTable[0]),
869 (int (*)(const void*, const void*)) strcmp);
879 if (isdigit (D->C)) {
884 if (toupper (D->C) == 'X') {
894 while ((Val = DigitVal (D->C)) >= 0 && Val < Base) {
895 D->IVal = D->IVal * Base + Val;
902 /* Other characters */
929 if (D->C == '\n' || D->C == EOF) {
930 ParseError (D, CC65_ERROR, "Unterminated string constant");
937 SB_AppendChar (&D->SVal, D->C);
940 SB_Terminate (&D->SVal);
954 ParseError (D, CC65_ERROR, "Invalid input character `%c'", D->C);
961 static int TokenFollows (InputData* D, Token Tok, const char* Name)
962 /* Check for a comma */
965 ParseError (D, CC65_ERROR, "%s expected", Name);
975 static int IntConstFollows (InputData* D)
976 /* Check for an integer constant */
978 return TokenFollows (D, TOK_INTCON, "Integer constant");
983 static int StringConstFollows (InputData* D)
984 /* Check for a string literal */
986 return TokenFollows (D, TOK_STRCON, "String literal");
991 static int Consume (InputData* D, Token Tok, const char* Name)
992 /* Check for a token and consume it. Return true if the token was comsumed,
993 * return false otherwise.
996 if (TokenFollows (D, Tok, Name)) {
1006 static int ConsumeComma (InputData* D)
1007 /* Consume a comma */
1009 return Consume (D, TOK_COMMA, "','");
1014 static int ConsumeEqual (InputData* D)
1015 /* Consume an equal sign */
1017 return Consume (D, TOK_EQUAL, "'='");
1022 static int ConsumeMinus (InputData* D)
1023 /* Consume a minus sign */
1025 return Consume (D, TOK_MINUS, "'-'");
1030 static void ParseFile (InputData* D)
1031 /* Parse a FILE line */
1034 enum { None = 0x00, Size = 0x01, MTime = 0x02 } InfoBits = None;
1036 /* Skip the FILE token */
1040 if (!StringConstFollows (D)) {
1044 /* Allocate a new file info */
1045 F = NewFileInfo (&D->SVal);
1047 /* Skip the file name */
1050 /* More stuff follows */
1051 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1053 /* Comma follows before next attribute */
1054 if (!ConsumeComma (D)) {
1062 if (!ConsumeEqual (D)) {
1065 if (!IntConstFollows (D)) {
1075 if (!ConsumeEqual (D)) {
1078 if (!IntConstFollows (D)) {
1087 UnexpectedToken (D);
1094 /* Check for required information */
1095 if ((InfoBits & Size) == None) {
1096 MissingAttribute (D, "size");
1099 if ((InfoBits & MTime) == None) {
1100 MissingAttribute (D, "mtime");
1104 /* Remember the file info */
1105 CollAppend (&D->Info->FileInfos, F);
1111 /* Entry point in case of errors */
1117 static void ParseLine (InputData* D)
1118 /* Parse a LINE line */
1121 enum { None = 0x00, Line = 0x01, Range = 0x02 } InfoBits = None;
1123 /* Skip the LINE token */
1126 /* File name follows */
1127 if (!StringConstFollows (D)) {
1131 /* Allocate a new line info */
1132 L = NewLineInfo (&D->SVal);
1134 /* Skip the file name */
1137 /* More stuff follows */
1138 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1140 /* Comma follows before next attribute */
1141 if (!ConsumeComma (D)) {
1149 if (!ConsumeEqual (D)) {
1152 if (!IntConstFollows (D)) {
1162 if (!ConsumeEqual (D)) {
1165 if (!IntConstFollows (D)) {
1168 L->Start = (cc65_addr) D->IVal;
1170 if (!ConsumeMinus (D)) {
1173 if (!IntConstFollows (D)) {
1176 L->End = (cc65_addr) D->IVal;
1182 UnexpectedToken (D);
1189 /* Check for required information */
1190 if ((InfoBits & Line) == None) {
1191 MissingAttribute (D, "line");
1194 if ((InfoBits & Range) == None) {
1195 MissingAttribute (D, "range");
1199 /* Remember the line info */
1200 CollAppend (&D->LineInfos, L);
1206 /* Entry point in case of errors */
1212 static void ParseSegment (InputData* D)
1213 /* Parse a SEGMENT line */
1215 /* Skip the SEGMENT token */
1224 static void ParseSym (InputData* D)
1225 /* Parse a SYM line */
1227 /* Skip the SYM token */
1236 static void ParseVersion (InputData* D)
1237 /* Parse a VERSION line */
1239 enum { None = 0x00, Major = 0x01, Minor = 0x02 } InfoBits = None;
1241 /* Skip the VERSION token */
1244 /* More stuff follows */
1245 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1251 if (!ConsumeEqual (D)) {
1254 if (!IntConstFollows (D)) {
1257 D->MajorVersion = D->IVal;
1264 if (!ConsumeEqual (D)) {
1267 if (!IntConstFollows (D)) {
1270 D->MinorVersion = D->IVal;
1276 UnexpectedToken (D);
1281 /* Comma follows before next attribute */
1282 if (D->Tok == TOK_COMMA) {
1284 } else if (D->Tok == TOK_EOL || D->Tok == TOK_EOF) {
1287 UnexpectedToken (D);
1292 /* Check for required information */
1293 if ((InfoBits & Major) == None) {
1294 MissingAttribute (D, "major");
1297 if ((InfoBits & Minor) == None) {
1298 MissingAttribute (D, "minor");
1303 /* Entry point in case of errors */
1309 /*****************************************************************************/
1310 /* Data processing */
1311 /*****************************************************************************/
1315 static FileInfo* FindFileInfo (InputData* D, const char* FileName)
1316 /* Find the FileInfo for a given file name */
1318 /* Get a pointer to the file info collection */
1319 Collection* FileInfos = &D->Info->FileInfos;
1321 /* Do a binary search */
1323 int Hi = (int) CollCount (FileInfos) - 1;
1327 int Cur = (Lo + Hi) / 2;
1330 FileInfo* CurItem = CollAt (FileInfos, Cur);
1333 int Res = strcmp (CurItem->FileName, FileName);
1338 } else if (Res > 0) {
1352 static void ProcessFileInfo (InputData* D)
1353 /* Postprocess file infos */
1355 /* Get a pointer to the file info collection */
1356 Collection* FileInfos = &D->Info->FileInfos;
1358 /* First, sort the file infos, so we can check for duplicates and do
1361 CollSort (FileInfos, CompareFileInfo);
1363 /* Cannot work on an empty collection */
1364 if (CollCount (FileInfos) > 0) {
1366 /* Walk through the file infos and check for duplicates. If we find
1367 * some, warn and remove them, so the file infos are unique after
1370 FileInfo* F = CollAt (FileInfos, 0);
1372 while (I < CollCount (FileInfos)) {
1373 FileInfo* Next = CollAt (FileInfos, I);
1374 if (strcmp (F->FileName, Next->FileName) == 0) {
1375 /* Warn only if time stamp and/or size is different */
1376 if (F->Size != Next->Size || F->MTime != Next->MTime) {
1379 "Duplicate file entry for \"%s\"",
1382 /* Remove the duplicate entry */
1383 FreeFileInfo (Next);
1384 CollDelete (FileInfos, I);
1386 /* This one is ok, check the next entry */
1396 static void ProcessLineInfo (InputData* D)
1397 /* Postprocess line infos */
1399 /* Get pointers to the collections */
1400 Collection* LineInfos = &D->LineInfos;
1401 Collection* FileInfos = &D->Info->FileInfos;
1403 /* Walk over the line infos and replace the name by a pointer to the
1404 * corresponding file info struct. The LineInfo structs will get shrinked
1405 * in this process. Add the line info to each file where it is defined.
1408 while (I < CollCount (LineInfos)) {
1410 /* Get LineInfo struct */
1411 LineInfo* L = CollAt (LineInfos, I);
1413 /* Find FileInfo that corresponds to name */
1414 FileInfo* F = FindFileInfo (D, L->FileName);
1416 /* If we have no corresponding file info, print a warning and remove
1422 "No file info for file \"%s\"",
1425 CollDelete (LineInfos, I);
1429 /* Shrink the line info struct effectively removing the file name
1430 * but set the pointer to the file info now.
1432 L = PreenLineInfo (L, F);
1433 CollReplace (LineInfos, L, I);
1435 /* Add this line info to the file where it is defined */
1436 CollAppend (&F->LineInfos, L);
1442 /* Walk over all files and sort the line infos for each file by ascending
1443 * start address of the range, so we can do a binary search later.
1445 for (I = 0; I < CollCount (FileInfos); ++I) {
1447 /* Get a pointer to this file info */
1448 FileInfo* F = CollAt (FileInfos, I);
1450 /* Sort the line infos for this file */
1451 CollSort (&F->LineInfos, CompareLineInfo);
1453 /* If there are line info entries, place the first and last address
1454 * of into the FileInfo struct itself, so we can rule out a FileInfo
1455 * quickly when mapping an address to a line info.
1457 if (CollCount (&F->LineInfos) > 0) {
1458 F->Start = ((const LineInfo*) CollFirst (&F->LineInfos))->Start;
1459 F->End = ((const LineInfo*) CollLast (&F->LineInfos))->End;
1467 static LineInfo* FindLineInfo (FileInfo* F, cc65_addr Addr)
1468 /* Find the LineInfo for a given address */
1470 Collection* LineInfos;
1475 /* Each file info contains the first and last address for which line
1476 * info is available, so we can rule out non matching ones quickly.
1478 if (Addr < F->Start || Addr > F->End) {
1482 /* Get a pointer to the line info collection for this file */
1483 LineInfos = &F->LineInfos;
1485 /* Do a binary search */
1487 Hi = (int) CollCount (LineInfos) - 1;
1491 int Cur = (Lo + Hi) / 2;
1494 LineInfo* CurItem = CollAt (LineInfos, Cur);
1497 if (Addr < CurItem->Start) {
1499 } else if (Addr > CurItem->End) {
1513 /*****************************************************************************/
1515 /*****************************************************************************/
1519 cc65_dbginfo cc65_read_dbginfo (const char* FileName, cc65_errorfunc ErrFunc)
1520 /* Parse the debug info file with the given name. On success, the function
1521 * will return a pointer to an opaque cc65_dbginfo structure, that must be
1522 * passed to the other functions in this module to retrieve information.
1523 * errorfunc is called in case of warnings and errors. If the file cannot be
1524 * read successfully, NULL is returned.
1527 /* Data structure used to control scanning and parsing */
1529 0, /* Name of input file */
1530 1, /* Line number */
1532 0, /* Line at start of current token */
1533 0, /* Column at start of current token */
1534 0, /* Number of errors */
1536 ' ', /* Input character */
1537 TOK_INVALID, /* Input token */
1538 0, /* Integer constant */
1539 STRBUF_INITIALIZER, /* String constant */
1540 0, /* Function called in case of errors */
1541 0, /* Major version number */
1542 0, /* Minor version number */
1543 COLLECTION_INITIALIZER, /* Line information */
1544 0, /* Pointer to debug info */
1546 D.FileName = FileName;
1549 /* Open the input file */
1550 D.F = fopen (D.FileName, "r");
1553 ParseError (&D, CC65_ERROR,
1554 "Cannot open input file \"%s\": %s",
1555 D.FileName, strerror (errno));
1559 /* Create a new debug info struct */
1560 D.Info = NewDbgInfo ();
1562 /* Prime the pump */
1566 while (D.Tok != TOK_EOF) {
1591 UnexpectedToken (&D);
1595 /* EOL or EOF must follow */
1596 if (D.Tok != TOK_EOF) {
1597 if (D.Tok != TOK_EOL) {
1598 ParseError (&D, 1, "Extra tokens in line");
1605 /* Close the file */
1608 /* Free memory allocated for SVal */
1611 /* In case of errors, delete the debug info already allocated and
1615 /* Free allocated stuff */
1617 for (I = 0; I < CollCount (&D.LineInfos); ++I) {
1618 FreeLineInfo (CollAt (&D.LineInfos, I));
1620 DoneCollection (&D.LineInfos);
1621 FreeDbgInfo (D.Info);
1625 /* We do now have all the information from the file. Do postprocessing. */
1626 ProcessFileInfo (&D);
1627 ProcessLineInfo (&D);
1629 /* Free the collection that contained the line info */
1630 DoneCollection (&D.LineInfos);
1632 /* Return the debug info struct that was created */
1638 void cc65_free_dbginfo (cc65_dbginfo Handle)
1639 /* Free debug information read from a file */
1642 FreeDbgInfo (Handle);
1648 cc65_lineinfo* cc65_get_lineinfo (cc65_dbginfo Handle, unsigned long Addr)
1649 /* Return line information for the given address. The function returns 0
1650 * if no line information was found.
1654 Collection* FileInfos;
1655 cc65_lineinfo* D = 0;
1657 /* We will place a list of line infos in a collection */
1658 Collection LineInfos = COLLECTION_INITIALIZER;
1660 /* Check the parameter */
1661 assert (Handle != 0);
1663 /* Walk over all files and search for matching line infos */
1664 FileInfos = &((DbgInfo*) Handle)->FileInfos;
1665 for (I = 0; I < CollCount (FileInfos); ++I) {
1666 /* Check if the file contains line info for this address */
1667 LineInfo* L = FindLineInfo (CollAt (FileInfos, I), Addr);
1669 CollAppend (&LineInfos, L);
1673 /* Do we have line infos? */
1674 if (CollCount (&LineInfos) > 0) {
1676 /* Prepare the struct we will return to the caller */
1677 D = xmalloc (sizeof (*D) +
1678 (CollCount (&LineInfos) - 1) * sizeof (D->data[0]));
1679 D->count = CollCount (&LineInfos);
1680 for (I = 0; I < D->count; ++I) {
1682 /* Pointer to this info */
1683 LineInfo* L = CollAt (&LineInfos, I);
1686 D->data[I].name = L->FileInfo->FileName;
1687 D->data[I].size = L->FileInfo->Size;
1688 D->data[I].mtime = L->FileInfo->MTime;
1689 D->data[I].line = L->Line;
1690 D->data[I].start = L->Start;
1691 D->data[I].end = L->End;
1695 /* Free the line info collection */
1696 DoneCollection (&LineInfos);
1698 /* Return the struct we've created */
1704 void cc65_free_lineinfo (cc65_dbginfo Handle, cc65_lineinfo* Info)
1705 /* Free line info returned by cc65_get_lineinfo() */
1707 /* Just for completeness, check the handle */
1708 assert (Handle != 0);
1710 /* Just free the memory */