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 empty string buf */
67 static const StrBuf EmptyStrBuf = STRBUF_INITIALIZER;
69 /* An array of pointers that grows if needed */
70 typedef struct Collection Collection;
72 unsigned Count; /* Number of items in the list */
73 unsigned Size; /* Size of allocated array */
74 void** Items; /* Array with dynamic size */
77 /* Initializer for static collections */
78 #define COLLECTION_INITIALIZER { 0, 0, 0 }
80 /* An empty collection */
81 static const Collection EmptyCollection = COLLECTION_INITIALIZER;
85 /* Data structure containing information from the debug info file. A pointer
86 * to this structure is passed as handle to callers from the outside.
88 typedef struct DbgInfo DbgInfo;
90 Collection FileInfos; /* Collection with file infos */
94 typedef enum Token Token;
97 TOK_INVALID, /* Invalid token */
98 TOK_EOF, /* End of file reached */
100 TOK_INTCON, /* Integer constant */
101 TOK_STRCON, /* String constant */
109 TOK_ABSOLUTE, /* ABSOLUTE keyword */
110 TOK_ADDRSIZE, /* ADDRSIZE keyword */
111 TOK_EQUATE, /* EQUATE keyword */
112 TOK_FILE, /* FILE keyword */
113 TOK_LABEL, /* LABEL keyword */
114 TOK_LINE, /* LINE keyword */
115 TOK_LONG, /* LONG_keyword */
116 TOK_MAJOR, /* MAJOR keyword */
117 TOK_MINOR, /* MINOR keyword */
118 TOK_MTIME, /* MTIME keyword */
119 TOK_RANGE, /* RANGE keyword */
120 TOK_RO, /* RO keyword */
121 TOK_RW, /* RW keyword */
122 TOK_SEGMENT, /* SEGMENT keyword */
123 TOK_SIZE, /* SIZE keyword */
124 TOK_START, /* START keyword */
125 TOK_SYM, /* SYM keyword */
126 TOK_TYPE, /* TYPE keyword */
127 TOK_VALUE, /* VALUE keyword */
128 TOK_VERSION, /* VERSION keyword */
129 TOK_ZEROPAGE, /* ZEROPAGE keyword */
131 TOK_IDENT, /* To catch unknown keywords */
134 /* Data used when parsing the debug info file */
135 typedef struct InputData InputData;
137 const char* FileName; /* Name of input file */
138 cc65_line Line; /* Current line number */
139 unsigned Col; /* Current column number */
140 cc65_line SLine; /* Line number at start of token */
141 unsigned SCol; /* Column number at start of token */
142 unsigned Errors; /* Number of errors */
143 FILE* F; /* Input file */
144 int C; /* Input character */
145 Token Tok; /* Token from input stream */
146 unsigned long IVal; /* Integer constant */
147 StrBuf SVal; /* String constant */
148 cc65_errorfunc Error; /* Function called in case of errors */
149 unsigned MajorVersion; /* Major version number */
150 unsigned MinorVersion; /* Minor version number */
151 Collection LineInfos; /* Line information */
152 DbgInfo* Info; /* Pointer to debug info */
155 /* Internally used file info struct */
156 typedef struct FileInfo FileInfo;
158 unsigned long Size; /* Size of file */
159 unsigned long MTime; /* Modification time */
160 cc65_addr Start; /* Start address of line infos */
161 cc65_addr End; /* End address of line infos */
162 Collection LineInfos; /* Line infos for this file */
163 char FileName[1]; /* Name of file with full path */
166 /* Internally used line info struct */
167 typedef struct LineInfo LineInfo;
169 cc65_addr Start; /* Start of data range */
170 cc65_addr End; /* End of data range */
171 cc65_line Line; /* Line number */
172 FileInfo* FileInfo; /* Pointer to file info */
173 char FileName[1]; /* Name of file */
178 /*****************************************************************************/
180 /*****************************************************************************/
184 static void NextToken (InputData* D);
185 /* Read the next token from the input stream */
189 /*****************************************************************************/
190 /* Memory allocation */
191 /*****************************************************************************/
195 static void* xmalloc (size_t Size)
196 /* Allocate memory, check for out of memory condition. Do some debugging */
200 /* Allow zero sized requests and return NULL in this case */
203 /* Allocate memory */
206 /* Check for errors */
210 /* Return a pointer to the block */
216 static void* xrealloc (void* P, size_t Size)
217 /* Reallocate a memory block, check for out of memory */
219 /* Reallocate the block */
220 void* N = realloc (P, Size);
222 /* Check for errors */
223 assert (N != 0 || Size == 0);
225 /* Return the pointer to the new block */
231 static void xfree (void* Block)
232 /* Free the block, do some debugging */
239 /*****************************************************************************/
240 /* Dynamic strings */
241 /*****************************************************************************/
245 static void SB_Done (StrBuf* B)
246 /* Free the data of a string buffer (but not the struct itself) */
255 static void SB_Realloc (StrBuf* B, unsigned NewSize)
256 /* Reallocate the string buffer space, make sure at least NewSize bytes are
260 /* Get the current size, use a minimum of 8 bytes */
261 unsigned NewAllocated = B->Allocated;
262 if (NewAllocated == 0) {
266 /* Round up to the next power of two */
267 while (NewAllocated < NewSize) {
271 /* Reallocate the buffer. Beware: The allocated size may be zero while the
272 * length is not. This means that we have a buffer that wasn't allocated
276 /* Just reallocate the block */
277 B->Buf = xrealloc (B->Buf, NewAllocated);
279 /* Allocate a new block and copy */
280 B->Buf = memcpy (xmalloc (NewAllocated), B->Buf, B->Len);
283 /* Remember the new block size */
284 B->Allocated = NewAllocated;
289 static unsigned SB_GetLen (const StrBuf* B)
290 /* Return the length of the buffer contents */
297 static const char* SB_GetConstBuf (const StrBuf* B)
298 /* Return a buffer pointer */
305 static void SB_Terminate (StrBuf* B)
306 /* Zero terminate the given string buffer. NOTE: The terminating zero is not
307 * accounted for in B->Len, if you want that, you have to use AppendChar!
310 unsigned NewLen = B->Len + 1;
311 if (NewLen > B->Allocated) {
312 SB_Realloc (B, NewLen);
314 B->Buf[B->Len] = '\0';
319 static void SB_Clear (StrBuf* B)
320 /* Clear the string buffer (make it empty) */
327 static void SB_AppendChar (StrBuf* B, int C)
328 /* Append a character to a string buffer */
330 unsigned NewLen = B->Len + 1;
331 if (NewLen > B->Allocated) {
332 SB_Realloc (B, NewLen);
334 B->Buf[B->Len] = (char) C;
340 /*****************************************************************************/
342 /*****************************************************************************/
346 static Collection* InitCollection (Collection* C)
347 /* Initialize a collection and return it. */
349 /* Intialize the fields. */
354 /* Return the new struct */
360 static void DoneCollection (Collection* C)
361 /* Free the data for a collection. This will not free the data contained in
365 /* Free the pointer array */
371 static unsigned CollCount (const Collection* C)
372 /* Return the number of items in the collection */
379 static void CollGrow (Collection* C, unsigned Size)
380 /* Grow the collection C so it is able to hold Size items without a resize
381 * being necessary. This can be called for performance reasons if the number
382 * of items to be placed in the collection is known in advance.
387 /* Ignore the call if the collection is already large enough */
388 if (Size <= C->Size) {
392 /* Grow the collection */
394 NewItems = xmalloc (C->Size * sizeof (void*));
395 memcpy (NewItems, C->Items, C->Count * sizeof (void*));
402 static void CollInsert (Collection* C, void* Item, unsigned Index)
403 /* Insert the data at the given position in the collection */
405 /* Check for invalid indices */
406 assert (Index <= C->Count);
408 /* Grow the array if necessary */
409 if (C->Count >= C->Size) {
411 CollGrow (C, (C->Size == 0)? 8 : C->Size * 2);
414 /* Move the existing elements if needed */
415 if (C->Count != Index) {
416 memmove (C->Items+Index+1, C->Items+Index, (C->Count-Index) * sizeof (void*));
420 /* Store the new item */
421 C->Items[Index] = Item;
426 static void CollAppend (Collection* C, void* Item)
427 /* Append an item to the end of the collection */
429 /* Insert the item at the end of the current list */
430 CollInsert (C, Item, C->Count);
435 static void* CollAt (Collection* C, unsigned Index)
436 /* Return the item at the given index */
438 /* Check the index */
439 assert (Index < C->Count);
441 /* Return the element */
442 return C->Items[Index];
447 static void* CollFirst (Collection* C)
448 /* Return the first item in a collection */
450 /* We must have at least one entry */
451 assert (C->Count > 0);
453 /* Return the element */
459 static void* CollLast (Collection* C)
460 /* Return the last item in a collection */
462 /* We must have at least one entry */
463 assert (C->Count > 0);
465 /* Return the element */
466 return C->Items[C->Count-1];
471 static void CollDelete (Collection* C, unsigned Index)
472 /* Remove the item with the given index from the collection. This will not
473 * free the item itself, just the pointer. All items with higher indices
474 * will get moved to a lower position.
477 /* Check the index */
478 assert (Index < C->Count);
480 /* Remove the item pointer */
482 memmove (C->Items+Index, C->Items+Index+1, (C->Count-Index) * sizeof (void*));
487 static void CollReplace (Collection* C, void* Item, unsigned Index)
488 /* Replace the item at the given position. The old item will not be freed,
489 * just the pointer will get replaced.
492 /* Check the index */
493 assert (Index < C->Count);
495 /* Replace the item pointer */
496 C->Items[Index] = Item;
501 static void CollQuickSort (Collection* C, int Lo, int Hi,
502 int (*Compare) (const void*, const void*))
503 /* Internal recursive sort function. */
505 /* Get a pointer to the items */
506 void** Items = C->Items;
513 while (I <= J && Compare (Items[Lo], Items[I]) >= 0) {
516 while (I <= J && Compare (Items[Lo], Items[J]) < 0) {
521 void* Tmp = Items[I];
530 void* Tmp = Items[J];
531 Items[J] = Items[Lo];
534 if (J > (Hi + Lo) / 2) {
535 CollQuickSort (C, J + 1, Hi, Compare);
538 CollQuickSort (C, Lo, J - 1, Compare);
546 void CollSort (Collection* C, int (*Compare) (const void*, const void*))
547 /* Sort the collection using the given compare function. */
550 CollQuickSort (C, 0, C->Count-1, Compare);
556 /*****************************************************************************/
558 /*****************************************************************************/
562 static LineInfo* NewLineInfo (const StrBuf* FileName)
563 /* Create a new LineInfo struct and return it */
565 /* Allocate memory */
566 LineInfo* L = xmalloc (sizeof (LineInfo) + SB_GetLen (FileName));
573 memcpy (L->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
581 static void FreeLineInfo (LineInfo* L)
582 /* Free a LineInfo struct */
589 static LineInfo* PreenLineInfo (LineInfo* L, FileInfo* F)
590 /* Replace the name by file information */
592 /* Shrink the LineInfo struct removing the FfileName field */
593 L = xrealloc (L, sizeof (*L) - 1);
595 /* Set the FileInfo pointer instead */
598 /* Return the result */
604 static int CompareLineInfo (const void* L, const void* R)
605 /* Helper function to sort line infos in a collection */
607 /* Sort by start of range */
608 if (((const LineInfo*) L)->Start > ((const LineInfo*) R)->Start) {
610 } else if (((const LineInfo*) L)->Start < ((const LineInfo*) R)->Start) {
619 /*****************************************************************************/
621 /*****************************************************************************/
625 static FileInfo* NewFileInfo (const StrBuf* FileName)
626 /* Create a new FileInfo struct and return it */
628 /* Allocate memory */
629 FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
634 F->Start = ~(cc65_addr)0;
636 InitCollection (&F->LineInfos);
637 memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
645 static void FreeFileInfo (FileInfo* F)
646 /* Free a FileInfo struct */
650 /* Walk through the collection with line infos and delete them */
651 for (I = 0; I < CollCount (&F->LineInfos); ++I) {
652 FreeLineInfo (CollAt (&F->LineInfos, I));
654 DoneCollection (&F->LineInfos);
656 /* Free the file info structure itself */
662 static int CompareFileInfo (const void* L, const void* R)
663 /* Helper function to sort file infos in a collection */
665 /* Sort by file name */
666 return strcmp (((const FileInfo*) L)->FileName,
667 ((const FileInfo*) R)->FileName);
672 /*****************************************************************************/
674 /*****************************************************************************/
678 static DbgInfo* NewDbgInfo (void)
679 /* Create a new DbgInfo struct and return it */
681 /* Allocate memory */
682 DbgInfo* Info = xmalloc (sizeof (DbgInfo));
685 InitCollection (&Info->FileInfos);
693 static void FreeDbgInfo (DbgInfo* Info)
694 /* Free a DbgInfo struct */
699 for (I = 0; I < CollCount (&Info->FileInfos); ++I) {
700 FreeFileInfo (CollAt (&Info->FileInfos, I));
702 DoneCollection (&Info->FileInfos);
704 /* Free the structure itself */
710 /*****************************************************************************/
711 /* Helper functions */
712 /*****************************************************************************/
716 static void ParseError (InputData* D, cc65_error_severity Type, const char* Msg, ...)
717 /* Call the user supplied parse error function */
723 /* Test-format the error message so we know how much space to allocate */
725 MsgSize = vsnprintf (0, 0, Msg, ap);
728 /* Allocate memory */
729 E = xmalloc (sizeof (*E) + MsgSize);
731 /* Write data to E */
733 E->name = D->FileName;
737 vsnprintf (E->errormsg, MsgSize+1, Msg, ap);
740 /* Call the caller:-) */
743 /* Free the data structure */
747 if (Type == CC65_ERROR) {
754 static void SkipLine (InputData* D)
755 /* Error recovery routine. Skip tokens until EOL or EOF is reached */
757 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
764 static void UnexpectedToken (InputData* D)
765 /* Call ParseError with a message about an unexpected input token */
767 ParseError (D, CC65_ERROR, "Unexpected input token %d", D->Tok);
773 static void MissingAttribute (InputData* D, const char* AttrName)
774 /* Print an error about a missing attribute */
776 ParseError (D, CC65_ERROR, "Attribute \"%s\" is mising", AttrName);
781 /*****************************************************************************/
782 /* Scanner and parser */
783 /*****************************************************************************/
787 static int DigitVal (int C)
788 /* Return the value for a numeric digit. Return -1 if C is invalid */
792 } else if (isxdigit (C)) {
793 return toupper (C) - 'A' + 10;
801 static void NextChar (InputData* D)
802 /* Read the next character from the input. Count lines and columns */
804 /* Check if we've encountered EOF before */
818 static void NextToken (InputData* D)
819 /* Read the next token from the input stream */
821 static const struct KeywordEntry {
822 const char Keyword[10];
825 { "absolute", TOK_ABSOLUTE },
826 { "addrsize", TOK_ADDRSIZE },
827 { "equate", TOK_EQUATE },
828 { "file", TOK_FILE },
829 { "label", TOK_LABEL },
830 { "line", TOK_LINE },
831 { "long", TOK_LONG },
832 { "major", TOK_MAJOR },
833 { "minor", TOK_MINOR },
834 { "mtime", TOK_MTIME },
835 { "range", TOK_RANGE },
838 { "segment", TOK_SEGMENT },
839 { "size", TOK_SIZE },
840 { "start", TOK_START },
842 { "type", TOK_TYPE },
843 { "value", TOK_VALUE },
844 { "version", TOK_VERSION },
845 { "zeropage", TOK_ZEROPAGE },
849 /* Skip whitespace */
850 while (D->C == ' ' || D->C == '\t') {
854 /* Remember the current position as start of the next token */
859 if (D->C == '_' || isalpha (D->C)) {
861 const struct KeywordEntry* Entry;
863 /* Read the identifier */
865 while (D->C == '_' || isalnum (D->C)) {
866 SB_AppendChar (&D->SVal, D->C);
869 SB_Terminate (&D->SVal);
871 /* Search the identifier in the keyword table */
872 Entry = bsearch (SB_GetConstBuf (&D->SVal),
874 sizeof (KeywordTable) / sizeof (KeywordTable[0]),
875 sizeof (KeywordTable[0]),
876 (int (*)(const void*, const void*)) strcmp);
886 if (isdigit (D->C)) {
891 if (toupper (D->C) == 'X') {
901 while ((Val = DigitVal (D->C)) >= 0 && Val < Base) {
902 D->IVal = D->IVal * Base + Val;
909 /* Other characters */
936 if (D->C == '\n' || D->C == EOF) {
937 ParseError (D, CC65_ERROR, "Unterminated string constant");
944 SB_AppendChar (&D->SVal, D->C);
947 SB_Terminate (&D->SVal);
961 ParseError (D, CC65_ERROR, "Invalid input character `%c'", D->C);
968 static int TokenFollows (InputData* D, Token Tok, const char* Name)
969 /* Check for a comma */
972 ParseError (D, CC65_ERROR, "%s expected", Name);
982 static int IntConstFollows (InputData* D)
983 /* Check for an integer constant */
985 return TokenFollows (D, TOK_INTCON, "Integer constant");
990 static int StringConstFollows (InputData* D)
991 /* Check for a string literal */
993 return TokenFollows (D, TOK_STRCON, "String literal");
998 static int Consume (InputData* D, Token Tok, const char* Name)
999 /* Check for a token and consume it. Return true if the token was comsumed,
1000 * return false otherwise.
1003 if (TokenFollows (D, Tok, Name)) {
1013 static int ConsumeComma (InputData* D)
1014 /* Consume a comma */
1016 return Consume (D, TOK_COMMA, "','");
1021 static int ConsumeEqual (InputData* D)
1022 /* Consume an equal sign */
1024 return Consume (D, TOK_EQUAL, "'='");
1029 static int ConsumeMinus (InputData* D)
1030 /* Consume a minus sign */
1032 return Consume (D, TOK_MINUS, "'-'");
1037 static void ParseFile (InputData* D)
1038 /* Parse a FILE line */
1041 enum { None = 0x00, Size = 0x01, MTime = 0x02 } InfoBits = None;
1043 /* Skip the FILE token */
1047 if (!StringConstFollows (D)) {
1051 /* Allocate a new file info */
1052 F = NewFileInfo (&D->SVal);
1054 /* Skip the file name */
1057 /* More stuff follows */
1058 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1060 /* Comma follows before next attribute */
1061 if (!ConsumeComma (D)) {
1069 if (!ConsumeEqual (D)) {
1072 if (!IntConstFollows (D)) {
1082 if (!ConsumeEqual (D)) {
1085 if (!IntConstFollows (D)) {
1094 UnexpectedToken (D);
1101 /* Check for required information */
1102 if ((InfoBits & Size) == None) {
1103 MissingAttribute (D, "size");
1106 if ((InfoBits & MTime) == None) {
1107 MissingAttribute (D, "mtime");
1111 /* Remember the file info */
1112 CollAppend (&D->Info->FileInfos, F);
1118 /* Entry point in case of errors */
1124 static void ParseLine (InputData* D)
1125 /* Parse a LINE line */
1128 enum { None = 0x00, Line = 0x01, Range = 0x02 } InfoBits = None;
1130 /* Skip the LINE token */
1133 /* File name follows */
1134 if (!StringConstFollows (D)) {
1138 /* Allocate a new line info */
1139 L = NewLineInfo (&D->SVal);
1141 /* Skip the file name */
1144 /* More stuff follows */
1145 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1147 /* Comma follows before next attribute */
1148 if (!ConsumeComma (D)) {
1156 if (!ConsumeEqual (D)) {
1159 if (!IntConstFollows (D)) {
1169 if (!ConsumeEqual (D)) {
1172 if (!IntConstFollows (D)) {
1175 L->Start = (cc65_addr) D->IVal;
1177 if (!ConsumeMinus (D)) {
1180 if (!IntConstFollows (D)) {
1183 L->End = (cc65_addr) D->IVal;
1189 UnexpectedToken (D);
1196 /* Check for required information */
1197 if ((InfoBits & Line) == None) {
1198 MissingAttribute (D, "line");
1201 if ((InfoBits & Range) == None) {
1202 MissingAttribute (D, "range");
1206 /* Remember the line info */
1207 CollAppend (&D->LineInfos, L);
1213 /* Entry point in case of errors */
1219 static void ParseSegment (InputData* D)
1220 /* Parse a SEGMENT line */
1222 /* Skip the SEGMENT token */
1231 static void ParseSym (InputData* D)
1232 /* Parse a SYM line */
1234 /* Skip the SYM token */
1243 static void ParseVersion (InputData* D)
1244 /* Parse a VERSION line */
1246 enum { None = 0x00, Major = 0x01, Minor = 0x02 } InfoBits = None;
1248 /* Skip the VERSION token */
1251 /* More stuff follows */
1252 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1258 if (!ConsumeEqual (D)) {
1261 if (!IntConstFollows (D)) {
1264 D->MajorVersion = D->IVal;
1271 if (!ConsumeEqual (D)) {
1274 if (!IntConstFollows (D)) {
1277 D->MinorVersion = D->IVal;
1283 UnexpectedToken (D);
1288 /* Comma follows before next attribute */
1289 if (D->Tok == TOK_COMMA) {
1291 } else if (D->Tok == TOK_EOL || D->Tok == TOK_EOF) {
1294 UnexpectedToken (D);
1299 /* Check for required information */
1300 if ((InfoBits & Major) == None) {
1301 MissingAttribute (D, "major");
1304 if ((InfoBits & Minor) == None) {
1305 MissingAttribute (D, "minor");
1310 /* Entry point in case of errors */
1316 /*****************************************************************************/
1317 /* Data processing */
1318 /*****************************************************************************/
1322 static FileInfo* FindFileInfo (InputData* D, const char* FileName)
1323 /* Find the FileInfo for a given file name */
1325 /* Get a pointer to the file info collection */
1326 Collection* FileInfos = &D->Info->FileInfos;
1328 /* Do a binary search */
1330 int Hi = (int) CollCount (FileInfos) - 1;
1334 int Cur = (Lo + Hi) / 2;
1337 FileInfo* CurItem = CollAt (FileInfos, Cur);
1340 int Res = strcmp (CurItem->FileName, FileName);
1345 } else if (Res > 0) {
1359 static void ProcessFileInfo (InputData* D)
1360 /* Postprocess file infos */
1362 /* Get a pointer to the file info collection */
1363 Collection* FileInfos = &D->Info->FileInfos;
1365 /* First, sort the file infos, so we can check for duplicates and do
1368 CollSort (FileInfos, CompareFileInfo);
1370 /* Cannot work on an empty collection */
1371 if (CollCount (FileInfos) > 0) {
1373 /* Walk through the file infos and check for duplicates. If we find
1374 * some, warn and remove them, so the file infos are unique after
1377 FileInfo* F = CollAt (FileInfos, 0);
1379 while (I < CollCount (FileInfos)) {
1380 FileInfo* Next = CollAt (FileInfos, I);
1381 if (strcmp (F->FileName, Next->FileName) == 0) {
1382 /* Warn only if time stamp and/or size is different */
1383 if (F->Size != Next->Size || F->MTime != Next->MTime) {
1386 "Duplicate file entry for \"%s\"",
1389 /* Remove the duplicate entry */
1390 FreeFileInfo (Next);
1391 CollDelete (FileInfos, I);
1393 /* This one is ok, check the next entry */
1403 static void ProcessLineInfo (InputData* D)
1404 /* Postprocess line infos */
1406 /* Get pointers to the collections */
1407 Collection* LineInfos = &D->LineInfos;
1408 Collection* FileInfos = &D->Info->FileInfos;
1410 /* Walk over the line infos and replace the name by a pointer to the
1411 * corresponding file info struct. The LineInfo structs will get shrinked
1412 * in this process. Add the line info to each file where it is defined.
1415 while (I < CollCount (LineInfos)) {
1417 /* Get LineInfo struct */
1418 LineInfo* L = CollAt (LineInfos, I);
1420 /* Find FileInfo that corresponds to name */
1421 FileInfo* F = FindFileInfo (D, L->FileName);
1423 /* If we have no corresponding file info, print a warning and remove
1429 "No file info for file \"%s\"",
1432 CollDelete (LineInfos, I);
1436 /* Shrink the line info struct effectively removing the file name
1437 * but set the pointer to the file info now.
1439 L = PreenLineInfo (L, F);
1440 CollReplace (LineInfos, L, I);
1442 /* Add this line info to the file where it is defined */
1443 CollAppend (&F->LineInfos, L);
1449 /* Walk over all files and sort the line infos for each file by ascending
1450 * start address of the range, so we can do a binary search later.
1452 for (I = 0; I < CollCount (FileInfos); ++I) {
1454 /* Get a pointer to this file info */
1455 FileInfo* F = CollAt (FileInfos, I);
1457 /* Sort the line infos for this file */
1458 CollSort (&F->LineInfos, CompareLineInfo);
1460 /* If there are line info entries, place the first and last address
1461 * of into the FileInfo struct itself, so we can rule out a FileInfo
1462 * quickly when mapping an address to a line info.
1464 if (CollCount (&F->LineInfos) > 0) {
1465 F->Start = ((const LineInfo*) CollFirst (&F->LineInfos))->Start;
1466 F->End = ((const LineInfo*) CollLast (&F->LineInfos))->End;
1474 static LineInfo* FindLineInfo (FileInfo* F, cc65_addr Addr)
1475 /* Find the LineInfo for a given address */
1477 Collection* LineInfos;
1482 /* Each file info contains the first and last address for which line
1483 * info is available, so we can rule out non matching ones quickly.
1485 if (Addr < F->Start || Addr > F->End) {
1489 /* Get a pointer to the line info collection for this file */
1490 LineInfos = &F->LineInfos;
1492 /* Do a binary search */
1494 Hi = (int) CollCount (LineInfos) - 1;
1498 int Cur = (Lo + Hi) / 2;
1501 LineInfo* CurItem = CollAt (LineInfos, Cur);
1504 if (Addr < CurItem->Start) {
1506 } else if (Addr > CurItem->End) {
1520 /*****************************************************************************/
1522 /*****************************************************************************/
1526 cc65_dbginfo cc65_read_dbginfo (const char* filename, cc65_errorfunc errorfunc)
1527 /* Parse the debug info file with the given name. On success, the function
1528 * will return a pointer to an opaque cc65_dbginfo structure, that must be
1529 * passed to the other functions in this module to retrieve information.
1530 * errorfunc is called in case of warnings and errors. If the file cannot be
1531 * read successfully, NULL is returned.
1534 /* Data structure used to control scanning and parsing */
1536 filename, /* Name of input file */
1537 1, /* Line number */
1539 0, /* Line at start of current token */
1540 0, /* Column at start of current token */
1541 0, /* Number of errors */
1543 ' ', /* Input character */
1544 TOK_INVALID, /* Input token */
1545 0, /* Integer constant */
1546 STRBUF_INITIALIZER, /* String constant */
1547 errorfunc, /* Function called in case of errors */
1548 0, /* Major version number */
1549 0, /* Minor version number */
1550 COLLECTION_INITIALIZER, /* Line information */
1551 0, /* Pointer to debug info */
1554 /* Open the input file */
1555 D.F = fopen (D.FileName, "r");
1558 ParseError (&D, CC65_ERROR,
1559 "Cannot open input file \"%s\": %s",
1560 D.FileName, strerror (errno));
1564 /* Create a new debug info struct */
1565 D.Info = NewDbgInfo ();
1567 /* Prime the pump */
1571 while (D.Tok != TOK_EOF) {
1596 UnexpectedToken (&D);
1600 /* EOL or EOF must follow */
1601 if (D.Tok != TOK_EOF) {
1602 if (D.Tok != TOK_EOL) {
1603 ParseError (&D, 1, "Extra tokens in line");
1610 /* Close the file */
1613 /* Free memory allocated for SVal */
1616 /* In case of errors, delete the debug info already allocated and
1620 /* Free allocated stuff */
1622 for (I = 0; I < CollCount (&D.LineInfos); ++I) {
1623 FreeLineInfo (CollAt (&D.LineInfos, I));
1625 DoneCollection (&D.LineInfos);
1626 FreeDbgInfo (D.Info);
1630 /* We do now have all the information from the file. Do postprocessing. */
1631 ProcessFileInfo (&D);
1632 ProcessLineInfo (&D);
1634 /* Free the collection that contained the line info */
1635 DoneCollection (&D.LineInfos);
1637 /* Return the debug info struct that was created */
1643 void cc65_free_dbginfo (cc65_dbginfo Handle)
1644 /* Free debug information read from a file */
1647 FreeDbgInfo (Handle);
1653 cc65_lineinfo* cc65_get_lineinfo (cc65_dbginfo Handle, unsigned long Addr)
1654 /* Return line information for the given address. The function returns 0
1655 * if no line information was found.
1659 Collection* FileInfos;
1660 cc65_lineinfo* D = 0;
1662 /* We will place a list of line infos in a collection */
1663 Collection LineInfos = COLLECTION_INITIALIZER;
1665 /* Check the parameter */
1666 assert (Handle != 0);
1668 /* Walk over all files and search for matching line infos */
1669 FileInfos = &((DbgInfo*) Handle)->FileInfos;
1670 for (I = 0; I < CollCount (FileInfos); ++I) {
1671 /* Check if the file contains line info for this address */
1672 LineInfo* L = FindLineInfo (CollAt (FileInfos, I), Addr);
1674 CollAppend (&LineInfos, L);
1678 /* Do we have line infos? */
1679 if (CollCount (&LineInfos) > 0) {
1681 /* Prepare the struct we will return to the caller */
1682 D = xmalloc (sizeof (*D) +
1683 (CollCount (&LineInfos) - 1) * sizeof (D->data[0]));
1684 D->count = CollCount (&LineInfos);
1685 for (I = 0; I < D->count; ++I) {
1687 /* Pointer to this info */
1688 LineInfo* L = CollAt (&LineInfos, I);
1691 D->data[I].name = L->FileInfo->FileName;
1692 D->data[I].size = L->FileInfo->Size;
1693 D->data[I].mtime = L->FileInfo->MTime;
1694 D->data[I].line = L->Line;
1695 D->data[I].start = L->Start;
1696 D->data[I].end = L->End;
1700 /* Free the line info collection */
1701 DoneCollection (&LineInfos);
1703 /* Return the struct we've created */
1709 void cc65_free_lineinfo (cc65_dbginfo Handle, cc65_lineinfo* Info)
1710 /* Free line info returned by cc65_get_lineinfo() */
1712 /* Just for completeness, check the handle */
1713 assert (Handle != 0);
1715 /* Just free the memory */