]> git.sur5r.net Git - cc65/commitdiff
First version of a module that reads debug files generated by ld65.
authoruz <uz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Tue, 3 Aug 2010 18:46:50 +0000 (18:46 +0000)
committeruz <uz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Tue, 3 Aug 2010 18:46:50 +0000 (18:46 +0000)
git-svn-id: svn://svn.cc65.org/cc65/trunk@4779 b7a2c559-68d2-44c3-8de9-860c34a00d81

src/dbginfo/dbginfo.c [new file with mode: 0644]
src/dbginfo/dbginfo.h [new file with mode: 0644]
src/dbginfo/dbgtest.c [new file with mode: 0644]
src/dbginfo/make/gcc.mak [new file with mode: 0644]

diff --git a/src/dbginfo/dbginfo.c b/src/dbginfo/dbginfo.c
new file mode 100644 (file)
index 0000000..0b6b720
--- /dev/null
@@ -0,0 +1,1479 @@
+/*****************************************************************************/
+/*                                                                           */
+/*                                 dbginfo.h                                 */
+/*                                                                           */
+/*                         cc65 debug info handling                          */
+/*                                                                           */
+/*                                                                           */
+/*                                                                           */
+/* (C) 2010,      Ullrich von Bassewitz                                      */
+/*                Roemerstrasse 52                                           */
+/*                D-70794 Filderstadt                                        */
+/* EMail:         uz@cc65.org                                                */
+/*                                                                           */
+/*                                                                           */
+/* This software is provided 'as-is', without any expressed or implied       */
+/* warranty.  In no event will the authors be held liable for any damages    */
+/* arising from the use of this software.                                    */
+/*                                                                           */
+/* Permission is granted to anyone to use this software for any purpose,     */
+/* including commercial applications, and to alter it and redistribute it    */
+/* freely, subject to the following restrictions:                            */
+/*                                                                           */
+/* 1. The origin of this software must not be misrepresented; you must not   */
+/*    claim that you wrote the original software. If you use this software   */
+/*    in a product, an acknowledgment in the product documentation would be  */
+/*    appreciated but is not required.                                       */
+/* 2. Altered source versions must be plainly marked as such, and must not   */
+/*    be misrepresented as being the original software.                      */
+/* 3. This notice may not be removed or altered from any source              */
+/*    distribution.                                                          */
+/*                                                                           */
+/*****************************************************************************/
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "dbginfo.h"
+
+
+
+/*****************************************************************************/
+/*                                          Data                                    */
+/*****************************************************************************/
+
+
+
+/* Dynamic strings */
+typedef struct StrBuf StrBuf;
+struct StrBuf {
+    char*       Buf;                    /* Pointer to buffer */
+    unsigned    Len;                    /* Length of the string */
+    unsigned    Allocated;              /* Size of allocated memory */
+};
+
+/* Initializer for a string buffer */
+#define STRBUF_INITIALIZER      { 0, 0, 0 }
+
+/* An empty string buf */
+static const StrBuf EmptyStrBuf = STRBUF_INITIALIZER;
+
+/* An array of pointers that grows if needed */
+typedef struct Collection Collection;
+struct Collection {
+    unsigned                   Count;          /* Number of items in the list */
+    unsigned                   Size;           /* Size of allocated array */
+    void**             Items;          /* Array with dynamic size */
+};
+
+/* Initializer for static collections */
+#define COLLECTION_INITIALIZER  { 0, 0, 0 }
+
+/* An empty collection */
+static const Collection EmptyCollection = COLLECTION_INITIALIZER;
+
+
+
+/* ### Parseerror */
+enum {
+    CC65_WARNING,
+    CC65_ERROR,
+};
+
+/* Data structure containing information from the debug info file. A pointer
+ * to this structure is passed as handle to callers from the outside.
+ */
+typedef struct DbgInfo DbgInfo;
+struct DbgInfo {
+    Collection          FileInfo;       /* Collection with file infos */
+    Collection          LineInfo;       /* Collection with line infos */
+
+};
+
+/* Input tokens */
+typedef enum Token Token;
+enum Token {
+
+    TOK_INVALID,                        /* Invalid token */
+    TOK_EOF,                            /* End of file reached */
+
+    TOK_INTCON,                         /* Integer constant */
+    TOK_STRCON,                         /* String constant */
+
+    TOK_EQUAL,                          /* = */
+    TOK_COMMA,                          /* , */
+    TOK_MINUS,                          /* - */
+    TOK_PLUS,                           /* + */
+    TOK_EOL,                            /* \n */
+
+    TOK_ABSOLUTE,                       /* ABSOLUTE keyword */
+    TOK_ADDRSIZE,                       /* ADDRSIZE keyword */
+    TOK_EQUATE,                         /* EQUATE keyword */
+    TOK_FILE,                           /* FILE keyword */
+    TOK_LABEL,                          /* LABEL keyword */
+    TOK_LINE,                           /* LINE keyword */
+    TOK_MTIME,                          /* MTIME keyword */
+    TOK_RANGE,                          /* RANGE keyword */
+    TOK_RO,                             /* RO keyword */
+    TOK_RW,                             /* RW keyword */
+    TOK_SEGMENT,                        /* SEGMENT keyword */
+    TOK_SIZE,                           /* SIZE keyword */
+    TOK_START,                          /* START keyword */
+    TOK_SYM,                            /* SYM keyword */
+    TOK_TYPE,                           /* TYPE keyword */
+    TOK_VALUE,                          /* VALUE keyword */
+    TOK_VERSION,                        /* VERSION keyword */
+    TOK_ZEROPAGE,                       /* ZEROPAGE keyword */
+};
+
+/* Data used when parsing the debug info file */
+typedef struct InputData InputData;
+struct InputData {
+    const char*         FileName;       /* Name of input file */
+    unsigned long       Line;           /* Current line number */
+    unsigned            Col;            /* Current column number */
+    unsigned long       SLine;          /* Line number at start of token */
+    unsigned            SCol;           /* Column number at start of token */
+    unsigned            Errors;         /* Number of errors */
+    FILE*               F;              /* Input file */
+    int                 C;              /* Input character */
+    Token               Tok;            /* Token from input stream */
+    unsigned long       IVal;           /* Integer constant */
+    StrBuf              SVal;           /* String constant */
+    cc65_errorfunc      Error;          /* Function called in case of errors */
+    DbgInfo*            Info;           /* Pointer to debug info */
+};
+
+/* Internally used file info struct */
+typedef struct FileInfo FileInfo;
+struct FileInfo {
+    unsigned long       Size;           /* Size of file */
+    unsigned long       MTime;          /* Modification time */
+    char                FileName[1];    /* Name of file with full path */
+};
+
+/* Internally used line info struct */
+typedef struct LineInfo LineInfo;
+struct LineInfo {
+    unsigned long       Start;          /* Start of data range */
+    unsigned long       End;            /* End of data range */
+    unsigned long       Line;           /* Line number */
+    FileInfo*           FileInfo;       /* Pointer to file info */
+    char                FileName[1];    /* Name of file */
+};
+
+
+
+/*****************************************************************************/
+/*                                 Forwards                                  */
+/*****************************************************************************/
+
+
+
+static void NextToken (InputData* D);
+/* Read the next token from the input stream */
+
+
+
+/*****************************************************************************/
+/*                             Memory allocation                             */
+/*****************************************************************************/
+
+
+
+static void* xmalloc (size_t Size)
+/* Allocate memory, check for out of memory condition. Do some debugging */
+{
+    void* P = 0;
+
+    /* Allow zero sized requests and return NULL in this case */
+    if (Size) {
+
+        /* Allocate memory */
+        P = malloc (Size);
+
+        /* Check for errors */
+        if (P == 0) {
+            /* ####### */
+        }
+    }
+
+    /* Return a pointer to the block */
+    return P;
+}
+
+
+
+static void* xrealloc (void* P, size_t Size)
+/* Reallocate a memory block, check for out of memory */
+{
+    /* Reallocate the block */
+    void* N = realloc (P, Size);
+
+    /* Check for errors */
+    assert (N != 0 || Size == 0);
+
+    /* Return the pointer to the new block */
+    return N;
+}
+
+
+
+static void xfree (void* Block)
+/* Free the block, do some debugging */
+{
+    free (Block);
+}
+
+
+
+/*****************************************************************************/
+/*                              Dynamic strings                              */
+/*****************************************************************************/
+
+
+
+static void SB_Done (StrBuf* B)
+/* Free the data of a string buffer (but not the struct itself) */
+{
+    if (B->Allocated) {
+        xfree (B->Buf);
+    }
+}
+
+
+
+static void SB_Realloc (StrBuf* B, unsigned NewSize)
+/* Reallocate the string buffer space, make sure at least NewSize bytes are
+ * available.
+ */
+{
+    /* Get the current size, use a minimum of 8 bytes */
+    unsigned NewAllocated = B->Allocated;
+    if (NewAllocated == 0) {
+               NewAllocated = 8;
+    }
+
+    /* Round up to the next power of two */
+    while (NewAllocated < NewSize) {
+               NewAllocated *= 2;
+    }
+
+    /* Reallocate the buffer. Beware: The allocated size may be zero while the
+     * length is not. This means that we have a buffer that wasn't allocated
+     * on the heap.
+     */
+    if (B->Allocated) {
+        /* Just reallocate the block */
+        B->Buf   = xrealloc (B->Buf, NewAllocated);
+    } else {
+        /* Allocate a new block and copy */
+        B->Buf   = memcpy (xmalloc (NewAllocated), B->Buf, B->Len);
+    }
+
+    /* Remember the new block size */
+    B->Allocated = NewAllocated;
+}
+
+
+
+static unsigned SB_GetLen (const StrBuf* B)
+/* Return the length of the buffer contents */
+{
+    return B->Len;
+}
+
+
+
+static const char* SB_GetConstBuf (const StrBuf* B)
+/* Return a buffer pointer */
+{
+    return B->Buf;
+}
+
+
+
+static void SB_Terminate (StrBuf* B)
+/* Zero terminate the given string buffer. NOTE: The terminating zero is not
+ * accounted for in B->Len, if you want that, you have to use AppendChar!
+ */
+{
+    unsigned NewLen = B->Len + 1;
+    if (NewLen > B->Allocated) {
+               SB_Realloc (B, NewLen);
+    }
+    B->Buf[B->Len] = '\0';
+}
+
+
+
+static void SB_Clear (StrBuf* B)
+/* Clear the string buffer (make it empty) */
+{
+    B->Len = 0;
+}
+
+
+
+static void SB_AppendChar (StrBuf* B, int C)
+/* Append a character to a string buffer */
+{
+    unsigned NewLen = B->Len + 1;
+    if (NewLen > B->Allocated) {
+               SB_Realloc (B, NewLen);
+    }
+    B->Buf[B->Len] = (char) C;
+    B->Len = NewLen;
+}
+
+
+
+/*****************************************************************************/
+/*                                Collections                                */
+/*****************************************************************************/
+
+
+
+static Collection* InitCollection (Collection* C)
+/* Initialize a collection and return it. */
+{
+    /* Intialize the fields. */
+    C->Count = 0;
+    C->Size  = 0;
+    C->Items = 0;
+
+    /* Return the new struct */
+    return C;
+}
+
+
+
+static void DoneCollection (Collection* C)
+/* Free the data for a collection. This will not free the data contained in
+ * the collection.
+ */
+{
+    /* Free the pointer array */
+    xfree (C->Items);
+}
+
+
+
+static unsigned CollCount (const Collection* C)
+/* Return the number of items in the collection */
+{
+    return C->Count;
+}
+
+
+
+static void CollGrow (Collection* C, unsigned Size)
+/* Grow the collection C so it is able to hold Size items without a resize
+ * being necessary. This can be called for performance reasons if the number
+ * of items to be placed in the collection is known in advance.
+ */
+{
+    void** NewItems;
+
+    /* Ignore the call if the collection is already large enough */
+    if (Size <= C->Size) {
+        return;
+    }
+
+    /* Grow the collection */
+    C->Size = Size;
+    NewItems = xmalloc (C->Size * sizeof (void*));
+    memcpy (NewItems, C->Items, C->Count * sizeof (void*));
+    xfree (C->Items);
+    C->Items = NewItems;
+}
+
+
+
+static void CollInsert (Collection* C, void* Item, unsigned Index)
+/* Insert the data at the given position in the collection */
+{
+    /* Check for invalid indices */
+    assert (Index <= C->Count);
+
+    /* Grow the array if necessary */
+    if (C->Count >= C->Size) {
+               /* Must grow */
+        CollGrow (C, (C->Size == 0)? 8 : C->Size * 2);
+    }
+
+    /* Move the existing elements if needed */
+    if (C->Count != Index) {
+               memmove (C->Items+Index+1, C->Items+Index, (C->Count-Index) * sizeof (void*));
+    }
+    ++C->Count;
+
+    /* Store the new item */
+    C->Items[Index] = Item;
+}
+
+
+
+static void CollAppend (Collection* C, void* Item)
+/* Append an item to the end of the collection */
+{
+    /* Insert the item at the end of the current list */
+    CollInsert (C, Item, C->Count);
+}
+
+
+
+static void* CollAt (Collection* C, unsigned Index)
+/* Return the item at the given index */
+{
+    /* Check the index */
+    assert (Index < C->Count);
+
+    /* Return the element */
+    return C->Items[Index];
+}
+
+
+
+static void CollDelete (Collection* C, unsigned Index)
+/* Remove the item with the given index from the collection. This will not
+ * free the item itself, just the pointer. All items with higher indices
+ * will get moved to a lower position.
+ */
+{
+    /* Check the index */
+    assert (Index < C->Count);
+
+    /* Remove the item pointer */
+    --C->Count;
+    memmove (C->Items+Index, C->Items+Index+1, (C->Count-Index) * sizeof (void*));
+}
+
+
+
+static void CollReplace (Collection* C, void* Item, unsigned Index)
+/* Replace the item at the given position. The old item will not be freed,
+ * just the pointer will get replaced.
+ */
+{
+    /* Check the index */
+    assert (Index < C->Count);
+
+    /* Replace the item pointer */
+    C->Items[Index] = Item;
+}
+
+
+
+static void CollQuickSort (Collection* C, int Lo, int Hi,
+                          int (*Compare) (const void*, const void*))
+/* Internal recursive sort function. */
+{
+    /* Get a pointer to the items */
+    void** Items = C->Items;
+
+    /* Quicksort */
+    while (Hi > Lo) {
+       int I = Lo + 1;
+       int J = Hi;
+       while (I <= J) {
+           while (I <= J && Compare (Items[Lo], Items[I]) >= 0) {
+               ++I;
+           }
+           while (I <= J && Compare (Items[Lo], Items[J]) < 0) {
+               --J;
+           }
+           if (I <= J) {
+               /* Swap I and J */
+               void* Tmp = Items[I];
+               Items[I]  = Items[J];
+               Items[J]  = Tmp;
+               ++I;
+               --J;
+           }
+       }
+       if (J != Lo) {
+           /* Swap J and Lo */
+           void* Tmp = Items[J];
+           Items[J]  = Items[Lo];
+           Items[Lo] = Tmp;
+       }
+       if (J > (Hi + Lo) / 2) {
+           CollQuickSort (C, J + 1, Hi, Compare);
+           Hi = J - 1;
+       } else {
+           CollQuickSort (C, Lo, J - 1, Compare);
+           Lo = J + 1;
+       }
+    }
+}
+
+
+
+void CollSort (Collection* C, int (*Compare) (const void*, const void*))
+/* Sort the collection using the given compare function. */
+{
+    if (C->Count > 1) {
+        CollQuickSort (C, 0, C->Count-1, Compare);
+    }
+}
+
+
+
+/*****************************************************************************/
+/*                                 File info                                 */
+/*****************************************************************************/
+
+
+
+static FileInfo* NewFileInfo (const StrBuf* FileName)
+/* Create a new FileInfo struct and return it */
+{
+    /* Allocate memory */
+    FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
+
+    /* Initialize it */
+    F->Size  = 0;
+    F->MTime = 0;
+    memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
+
+    /* Return it */
+    return F;
+}
+
+
+
+static void FreeFileInfo (FileInfo* F)
+/* Free a FileInfo struct */
+{
+    xfree (F);
+}
+
+
+
+static int CompareFileInfo (const void* L, const void* R)
+/* Helper function to sort file infos in a collection */
+{
+    /* Sort by file name */
+    return strcmp (((const FileInfo*) L)->FileName,
+                   ((const FileInfo*) R)->FileName);
+}
+
+
+
+/*****************************************************************************/
+/*                                 Line info                                 */
+/*****************************************************************************/
+
+
+
+static LineInfo* NewLineInfo (const StrBuf* FileName)
+/* Create a new LineInfo struct and return it */
+{
+    /* Allocate memory */
+    LineInfo* L = xmalloc (sizeof (LineInfo) + SB_GetLen (FileName));
+
+    /* Initialize it */
+    L->Start    = 0;
+    L->End      = 0;
+    L->Line     = 0;
+    L->FileInfo = 0;
+    memcpy (L->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
+
+    /* Return it */
+    return L;
+}
+
+
+
+static void FreeLineInfo (LineInfo* L)
+/* Free a LineInfo struct */
+{
+    xfree (L);
+}
+
+
+
+static LineInfo* PreenLineInfo (LineInfo* L, FileInfo* F)
+/* Replace the name by file information */
+{
+    /* Shrink the LineInfo struct removing the FfileName field */
+    L = xrealloc (L, sizeof (*L) - 1);
+
+    /* Set the FileInfo pointer instead */
+    L->FileInfo = F;
+
+    /* Return the result */
+    return L;
+}
+
+
+
+/*****************************************************************************/
+/*                                Debug info                                 */
+/*****************************************************************************/
+
+
+
+static DbgInfo* NewDbgInfo (void)
+/* Create a new DbgInfo struct and return it */
+{
+    /* Allocate memory */
+    DbgInfo* Info = xmalloc (sizeof (DbgInfo));
+
+    /* Initialize it */
+    InitCollection (&Info->FileInfo);
+    InitCollection (&Info->LineInfo);
+
+    /* Return it */
+    return Info;
+}
+
+
+
+static void FreeDbgInfo (DbgInfo* Info)
+/* Free a DbgInfo struct */
+{
+    unsigned I;
+
+    /* Free file info */
+    for (I = 0; I < CollCount (&Info->FileInfo); ++I) {
+        FreeFileInfo (CollAt (&Info->FileInfo, I));
+    }
+    DoneCollection (&Info->FileInfo);
+
+    /* Free line info */
+    for (I = 0; I < CollCount (&Info->LineInfo); ++I) {
+        FreeLineInfo (CollAt (&Info->LineInfo, I));
+    }
+    DoneCollection (&Info->LineInfo);
+
+    /* Free the structure itself */
+    xfree (Info);
+}
+
+
+
+/*****************************************************************************/
+/*                             Helper functions                              */
+/*****************************************************************************/
+
+
+
+static void ParseError (InputData* D, unsigned Type, const char* Msg, ...)
+/* Call the user supplied parse error function */
+{
+    va_list             ap;
+    int                 MsgSize;
+    cc65_parseerror*    E;
+
+    /* Test-format the error message so we know how much space to allocate */
+    va_start (ap, Msg);
+    MsgSize = vsnprintf (0, 0, Msg, ap);
+    va_end (ap);
+
+    /* Allocate memory */
+    E = xmalloc (sizeof (*E) + MsgSize);
+
+    /* Write data to E */
+    E->type   = Type;
+    E->name   = D->FileName;
+    E->line   = D->SLine;
+    E->column = D->SCol;
+    va_start (ap, Msg);
+    vsnprintf (E->errormsg, MsgSize+1, Msg, ap);
+    va_end (ap);
+
+    /* Call the caller:-) */
+    D->Error (E);
+
+    /* Free the data structure */
+    xfree (E);
+
+    /* Count errors */
+    if (Type == CC65_ERROR) {
+        ++D->Errors;
+    }
+}
+
+
+
+static void SkipLine (InputData* D)
+/* Error recovery routine. Skip tokens until EOL or EOF is reached */
+{
+    while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
+        NextToken (D);
+    }
+}
+
+
+
+static void UnexpectedToken (InputData* D)
+/* Call ParseError with a message about an unexpected input token */
+{
+    ParseError (D, CC65_ERROR, "Unexpected input token %d", D->Tok);
+    SkipLine (D);
+}
+
+
+
+static void MissingAttribute (InputData* D, const char* AttrName)
+/* Print an error about a missing attribute */
+{
+    ParseError (D, CC65_ERROR, "Attribute \"%s\" is mising", AttrName);
+}
+
+
+
+/*****************************************************************************/
+/*                            Scanner and parser                             */
+/*****************************************************************************/
+
+
+
+static int DigitVal (int C)
+/* Return the value for a numeric digit. Return -1 if C is invalid */
+{
+    if (isdigit (C)) {
+       return C - '0';
+    } else if (isxdigit (C)) {
+       return toupper (C) - 'A' + 10;
+    } else {
+        return -1;
+    }
+}
+
+
+
+static void NextChar (InputData* D)
+/* Read the next character from the input. Count lines and columns */
+{
+    /* Check if we've encountered EOF before */
+    if (D->C >= 0) {
+        D->C = fgetc (D->F);
+        if (D->C == '\n') {
+            ++D->Line;
+            D->Col = 0;
+        } else {
+            ++D->Col;
+        }
+    }
+}
+
+
+
+static void NextToken (InputData* D)
+/* Read the next token from the input stream */
+{
+    static const struct KeywordEntry  {
+        const char      Keyword[10];
+        Token           Tok;
+    } KeywordTable[] = {
+        { "absolute",   TOK_ABSOLUTE    },
+        { "addrsize",   TOK_ADDRSIZE    },
+        { "equate",     TOK_EQUATE      },
+        { "file",       TOK_FILE        },
+        { "label",      TOK_LABEL       },
+        { "line",       TOK_LINE        },
+        { "mtime",      TOK_MTIME       },
+        { "range",      TOK_RANGE       },
+        { "ro",         TOK_RO          },
+        { "rw",         TOK_RW          },
+        { "segment",    TOK_SEGMENT     },
+        { "size",       TOK_SIZE        },
+        { "start",      TOK_START       },
+        { "sym",        TOK_SYM         },
+        { "type",       TOK_TYPE        },
+        { "value",      TOK_VALUE       },
+        { "version",    TOK_VERSION     },
+        { "zeropage",   TOK_ZEROPAGE    },
+    };
+
+
+    /* Skip whitespace */
+    while (D->C == ' ' || D->C == '\t') {
+       NextChar (D);
+    }
+
+    /* Remember the current position as start of the next token */
+    D->SLine = D->Line;
+    D->SCol  = D->Col;
+
+    /* Identifier? */
+    if (D->C == '_' || isalpha (D->C)) {
+
+        const struct KeywordEntry* Entry;
+
+       /* Read the identifier */
+        SB_Clear (&D->SVal);
+       while (D->C == '_' || isalnum (D->C)) {
+            SB_AppendChar (&D->SVal, D->C);
+           NextChar (D);
+       }
+               SB_Terminate (&D->SVal);
+
+        /* Search the identifier in the keyword table */
+        Entry = bsearch (SB_GetConstBuf (&D->SVal),
+                         KeywordTable,
+                         sizeof (KeywordTable) / sizeof (KeywordTable[0]),
+                         sizeof (KeywordTable[0]),
+                         (int (*)(const void*, const void*)) strcmp);
+        if (Entry == 0) {
+            ParseError (D, CC65_ERROR, "Unknown keyword `%s'", SB_GetConstBuf (&D->SVal));
+            D->Tok = TOK_INVALID;
+        } else {
+            D->Tok = Entry->Tok;
+        }
+       return;
+    }
+
+    /* Number? */
+    if (isdigit (D->C)) {
+        int Base = 10;
+        int Val;
+        if (D->C == '0') {
+            NextChar (D);
+            if (toupper (D->C) == 'X') {
+                NextChar (D);
+                Base = 16;
+            } else {
+                Base = 8;
+            }
+        } else {
+            Base = 10;
+        }
+               D->IVal = 0;
+        while ((Val = DigitVal (D->C)) >= 0 && Val < Base) {
+                   D->IVal = D->IVal * Base + Val;
+           NextChar (D);
+       }
+       D->Tok = TOK_INTCON;
+       return;
+    }
+
+    /* Other characters */
+    switch (D->C) {
+
+        case '-':
+            NextChar (D);
+            D->Tok = TOK_MINUS;
+            break;
+
+        case '+':
+            NextChar (D);
+            D->Tok = TOK_PLUS;
+            break;
+
+       case ',':
+           NextChar (D);
+           D->Tok = TOK_COMMA;
+           break;
+
+       case '=':
+           NextChar (D);
+           D->Tok = TOK_EQUAL;
+           break;
+
+        case '\"':
+            SB_Clear (&D->SVal);
+            NextChar (D);
+            while (1) {
+                if (D->C == '\n' || D->C == EOF) {
+                    ParseError (D, CC65_ERROR, "Unterminated string constant");
+                    break;
+                }
+                if (D->C == '\"') {
+                    NextChar (D);
+                    break;
+                }
+                SB_AppendChar (&D->SVal, D->C);
+                NextChar (D);
+            }
+            SB_Terminate (&D->SVal);
+            D->Tok = TOK_STRCON;
+                   break;
+
+        case '\n':
+            NextChar (D);
+            D->Tok = TOK_EOL;
+            break;
+
+        case EOF:
+                   D->Tok = TOK_EOF;
+           break;
+
+       default:
+                   ParseError (D, CC65_ERROR, "Invalid input character `%c'", D->C);
+
+    }
+}
+
+
+
+static int TokenFollows (InputData* D, Token Tok, const char* Name)
+/* Check for a comma */
+{
+    if (D->Tok != Tok) {
+        ParseError (D, CC65_ERROR, "%s expected", Name);
+        SkipLine (D);
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+
+
+static int IntConstFollows (InputData* D)
+/* Check for an integer constant */
+{
+    return TokenFollows (D, TOK_INTCON, "Integer constant");
+}
+
+
+
+static int StringConstFollows (InputData* D)
+/* Check for a string literal */
+{
+    return TokenFollows (D, TOK_STRCON, "String literal");
+}
+
+
+
+static int Consume (InputData* D, Token Tok, const char* Name)
+/* Check for a token and consume it. Return true if the token was comsumed,
+ * return false otherwise.
+ */
+{
+    if (TokenFollows (D, Tok, Name)) {
+        NextToken (D);
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+
+
+static int ConsumeComma (InputData* D)
+/* Consume a comma */
+{
+    return Consume (D, TOK_COMMA, "','");
+}
+
+
+
+static int ConsumeEqual (InputData* D)
+/* Consume an equal sign */
+{
+    return Consume (D, TOK_EQUAL, "'='");
+}
+
+
+
+static int ConsumeMinus (InputData* D)
+/* Consume a minus sign */
+{
+    return Consume (D, TOK_MINUS, "'-'");
+}
+
+
+
+static void ParseFile (InputData* D)
+/* Parse a FILE line */
+{
+    FileInfo* F;
+    enum { None = 0x00, Size = 0x01, MTime = 0x02 } InfoBits = None;
+
+    /* Skip the FILE token */
+    NextToken (D);
+
+    /* Name follows */
+    if (!StringConstFollows (D)) {
+        return;
+    }
+
+    /* Allocate a new file info */
+    F = NewFileInfo (&D->SVal);
+
+    /* Skip the file name */
+    NextToken (D);
+
+    /* More stuff follows */
+    while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
+
+        /* Comma follows before next attribute */
+        if (!ConsumeComma (D)) {
+            goto ErrorExit;
+        }
+
+        switch (D->Tok) {
+
+            case TOK_SIZE:
+                NextToken (D);
+                if (!ConsumeEqual (D)) {
+                    goto ErrorExit;
+                }
+                if (!IntConstFollows (D)) {
+                    goto ErrorExit;
+                }
+                F->Size = D->IVal;
+                NextToken (D);
+                InfoBits |= Size;
+                break;
+
+            case TOK_MTIME:
+                NextToken (D);
+                if (!ConsumeEqual (D)) {
+                    goto ErrorExit;
+                }
+                if (!IntConstFollows (D)) {
+                    goto ErrorExit;
+                }
+                F->MTime = D->IVal;
+                NextToken (D);
+                InfoBits |= MTime;
+                break;
+
+            default:
+                UnexpectedToken (D);
+                SkipLine (D);
+                goto ErrorExit;
+        }
+
+    }
+
+    /* Check for required information */
+    if ((InfoBits & Size) == None) {
+        MissingAttribute (D, "size");
+        goto ErrorExit;
+    }
+    if ((InfoBits & MTime) == None) {
+        MissingAttribute (D, "mtime");
+        goto ErrorExit;
+    }
+
+    /* Remember the file info */
+    CollAppend (&D->Info->FileInfo, F);
+
+    /* Done */
+    return;
+
+ErrorExit:
+    /* Entry point in case of errors */
+    FreeFileInfo (F);
+}
+
+
+
+static void ParseLine (InputData* D)
+/* Parse a LINE line */
+{
+    LineInfo* L;
+    enum { None = 0x00, Line = 0x01, Range = 0x02 } InfoBits = None;
+
+    /* Skip the LINE token */
+    NextToken (D);
+
+    /* File name follows */
+    if (!StringConstFollows (D)) {
+        return;
+    }
+
+    /* Allocate a new line info */
+    L = NewLineInfo (&D->SVal);
+
+    /* Skip the file name */
+    NextToken (D);
+
+    /* More stuff follows */
+    while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
+
+        /* Comma follows before next attribute */
+        if (!ConsumeComma (D)) {
+            goto ErrorExit;
+        }
+
+        switch (D->Tok) {
+
+            case TOK_LINE:
+                NextToken (D);
+                if (!ConsumeEqual (D)) {
+                    goto ErrorExit;
+                }
+                if (!IntConstFollows (D)) {
+                    goto ErrorExit;
+                }
+                L->Line = D->IVal;
+                NextToken (D);
+                InfoBits |= Line;
+                break;
+
+            case TOK_RANGE:
+                NextToken (D);
+                if (!ConsumeEqual (D)) {
+                    goto ErrorExit;
+                }
+                if (!IntConstFollows (D)) {
+                    goto ErrorExit;
+                }
+                L->Start = D->IVal;
+                NextToken (D);
+                if (!ConsumeMinus (D)) {
+                    goto ErrorExit;
+                }
+                if (!IntConstFollows (D)) {
+                    goto ErrorExit;
+                }
+                L->Start = D->IVal;
+                NextToken (D);
+                InfoBits |= Range;
+                break;
+
+            default:
+                UnexpectedToken (D);
+                SkipLine (D);
+                goto ErrorExit;
+        }
+
+    }
+
+    /* Check for required information */
+    if ((InfoBits & Line) == None) {
+        MissingAttribute (D, "line");
+        goto ErrorExit;
+    }
+    if ((InfoBits & Range) == None) {
+        MissingAttribute (D, "range");
+        goto ErrorExit;
+    }
+
+    /* Remember the line info */
+    CollAppend (&D->Info->LineInfo, L);
+
+    /* Done */
+    return;
+
+ErrorExit:
+    /* Entry point in case of errors */
+    FreeLineInfo (L);
+}
+
+
+
+static void ParseSegment (InputData* D)
+/* Parse a SEGMENT line */
+{
+    /* Skip the SEGMENT token */
+    NextToken (D);
+
+    /* ### */
+    SkipLine (D);
+}
+
+
+
+static void ParseSym (InputData* D)
+/* Parse a SYM line */
+{
+    /* Skip the SYM token */
+    NextToken (D);
+
+    /* ### */
+    SkipLine (D);
+}
+
+
+
+static void ParseVersion (InputData* D)
+/* Parse a VERSION line */
+{
+    /* Skip the VERSION token */
+    NextToken (D);
+
+    /* ### */
+    SkipLine (D);
+}
+
+
+
+/*****************************************************************************/
+/*                              Data processing                              */
+/*****************************************************************************/
+
+
+
+static FileInfo* FindFileInfo (InputData* D, const char* FileName)
+/* Find the FileInfo for a given file name */
+{
+    /* Get a pointer to the file info collection */
+    Collection* FileInfos = &D->Info->FileInfo;
+
+    /* Do a binary search */
+    int Lo = 0;
+    int Hi = (int) CollCount (FileInfos) - 1;
+    while (Lo <= Hi) {
+
+        /* Mid of range */
+        int Cur = (Lo + Hi) / 2;
+
+        /* Get item */
+        FileInfo* CurItem = CollAt (FileInfos, Cur);
+
+        /* Compare */
+        int Res = strcmp (CurItem->FileName, FileName);
+
+        /* Found? */
+        if (Res < 0) {
+            Lo = Cur + 1;
+        } else if (Res > 0) {
+            Hi = Cur - 1;
+        } else {
+            /* Found! */
+            return CurItem;
+        }
+    }
+
+    /* Not found */
+    return 0;
+}
+
+
+
+static void ProcessFileInfo (InputData* D)
+/* Postprocess file infos */
+{
+    /* Get a pointer to the file info collection */
+    Collection* FileInfos = &D->Info->FileInfo;
+
+    /* First, sort the file infos, so we can check for duplicates and do
+     * binary search.
+     */
+    CollSort (FileInfos, CompareFileInfo);
+
+    /* Cannot work on an empty collection */
+    if (CollCount (FileInfos) > 0) {
+
+        /* Walk through the file infos and check for duplicates. If we find
+         * some, remove all but the first, so the file infos are unique after
+         * that step.
+         */
+        FileInfo* F = CollAt (FileInfos, 0);
+        unsigned I = 1;
+        while (I < CollCount (FileInfos)) {
+            FileInfo* Next = CollAt (FileInfos, I);
+            if (strcmp (F->FileName, Next->FileName) == 0) {
+                /* Warn only if time stamp and/or size is different */
+                if (F->Size != Next->Size || F->MTime != Next->MTime) {
+                    ParseError (D,
+                                CC65_WARNING,
+                                "Duplicate file entry for \"%s\"",
+                                F->FileName);
+                }
+                /* Remove the duplicate entry */
+                FreeFileInfo (Next);
+                CollDelete (FileInfos, I);
+            } else {
+                /* This one is ok, check the next entry */
+                F = Next;
+                ++I;
+            }
+        }
+    }
+}
+
+
+
+static void ProcessLineInfo (InputData* D)
+/* Postprocess line infos */
+{
+    /* Get a pointer to the line info collection */
+    Collection* LineInfos = &D->Info->LineInfo;
+
+    /* Walk over the line infos and replace the name by a pointer to the
+     * corresponding file info struct. The LineInfo structs will get shrinked
+     * in this process.
+     */
+    unsigned I = 0;
+    while (I < CollCount (LineInfos)) {
+
+        /* Get LineInfo struct */
+        LineInfo* L = CollAt (LineInfos, I);
+
+        /* Find FileInfo that corresponds to name */
+        FileInfo* F = FindFileInfo (D, L->FileName);
+
+        /* If we have no corresponding file info, print a warning and remove
+         * the line info.
+         */
+        if (F == 0) {
+            ParseError (D,
+                        CC65_ERROR,
+                        "No file info for file \"%s\"",
+                        L->FileName);
+            FreeLineInfo (L);
+            CollDelete (LineInfos, I);
+            continue;
+        }
+
+        /* Shrink the line info struct effectively removing the file name
+         * but set the pointer to the file info now.
+         */
+        CollReplace (LineInfos, PreenLineInfo (L, F), I);
+
+        /* Next one */
+        ++I;
+    }
+}
+
+
+
+/*****************************************************************************/
+/*                                          Code                                    */
+/*****************************************************************************/
+
+
+
+cc65_dbginfo cc65_read_dbginfo (const char* filename, cc65_errorfunc errorfunc)
+/* Parse the debug info file with the given name. On success, the function
+ * will return a pointer to an opaque cc65_dbginfo structure, that must be
+ * passed to the other functions in this module to retrieve information.
+ * errorfunc is called in case of warnings and errors. If the file cannot be
+ * read successfully, NULL is returned.
+ */
+{
+    /* Data structure used to control scanning and parsing */
+    InputData D = {
+        filename,               /* Name of input file */
+        1,                      /* Line number */
+        0,                      /* Input file */
+        0,                      /* Line at start of current token */
+        0,                      /* Column at start of current token */
+        0,                      /* Number of errors */
+        0,                      /* Input file */
+        ' ',                    /* Input character */
+        TOK_INVALID,            /* Input token */
+        0,                      /* Integer constant */
+        STRBUF_INITIALIZER,     /* String constant */
+        errorfunc,              /* Function called in case of errors */
+        0,                      /* Pointer to debug info */
+    };
+
+    /* Open the input file */
+    D.F = fopen (D.FileName, "r");
+    if (D.F == 0) {
+        /* Cannot open */
+        ParseError (&D, CC65_ERROR,
+                    "Cannot open input file \"%s\": %s",
+                     D.FileName, strerror (errno));
+        return 0;
+    }
+
+    /* Create a new debug info struct */
+    D.Info = NewDbgInfo ();
+
+    /* Prime the pump */
+    NextToken (&D);
+
+    /* Parse lines */
+    while (D.Tok != TOK_EOF) {
+
+        switch (D.Tok) {
+
+            case TOK_FILE:
+                ParseFile (&D);
+                break;
+
+            case TOK_LINE:
+                ParseLine (&D);
+                break;
+
+            case TOK_SEGMENT:
+                ParseSegment (&D);
+                break;
+
+            case TOK_SYM:
+                ParseSym (&D);
+                break;
+
+            case TOK_VERSION:
+                ParseVersion (&D);
+                break;
+
+            default:
+                UnexpectedToken (&D);
+
+        }
+
+        /* EOL or EOF must follow */
+        if (D.Tok != TOK_EOF) {
+            if (D.Tok != TOK_EOL) {
+                ParseError (&D, 1, "Extra tokens in line");
+                SkipLine (&D);
+            }
+            NextToken (&D);
+        }
+    }
+
+    /* Close the file */
+    fclose (D.F);
+
+    /* Free memory allocated for SVal */
+    SB_Done (&D.SVal);
+
+    /* In case of errors, delete the debug info already allocated and
+     * return NULL
+     */
+    if (D.Errors > 0) {
+        /* Free the allocated debug info */
+        FreeDbgInfo (D.Info);
+        return 0;
+    }
+
+    /* We do now have all the information from the file. Do postprocessing. */
+    ProcessFileInfo (&D);
+    ProcessLineInfo (&D);
+
+    /* Return the debug info struct that was created */
+    return D.Info;
+}
+
+
+
+cc65_lineinfo* cc65_get_lineinfo (cc65_dbginfo handle, unsigned long addr)
+/* Return line information for the given address */
+{
+    /* The passed handle is actually a pointer to a DbgInfo struct */
+    DbgInfo* Info = handle;
+    assert (Info != 0);
+
+
+}
+
+
+
+void cc65_free_lineinfo (cc65_dbginfo handle, cc65_lineinfo* info)
+/* Free line info returned by cc65_get_lineinfo() */
+{
+    /* The passed handle is actually a pointer to a DbgInfo struct */
+    DbgInfo* Info = handle;
+    assert (Info != 0);
+
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/dbginfo/dbginfo.h b/src/dbginfo/dbginfo.h
new file mode 100644 (file)
index 0000000..1f7f603
--- /dev/null
@@ -0,0 +1,113 @@
+/*****************************************************************************/
+/*                                                                           */
+/*                                 dbginfo.h                                 */
+/*                                                                           */
+/*                         cc65 debug info handling                          */
+/*                                                                           */
+/*                                                                           */
+/*                                                                           */
+/* (C) 2010,      Ullrich von Bassewitz                                      */
+/*                Roemerstrasse 52                                           */
+/*                D-70794 Filderstadt                                        */
+/* EMail:         uz@cc65.org                                                */
+/*                                                                           */
+/*                                                                           */
+/* This software is provided 'as-is', without any expressed or implied       */
+/* warranty.  In no event will the authors be held liable for any damages    */
+/* arising from the use of this software.                                    */
+/*                                                                           */
+/* Permission is granted to anyone to use this software for any purpose,     */
+/* including commercial applications, and to alter it and redistribute it    */
+/* freely, subject to the following restrictions:                            */
+/*                                                                           */
+/* 1. The origin of this software must not be misrepresented; you must not   */
+/*    claim that you wrote the original software. If you use this software   */
+/*    in a product, an acknowledgment in the product documentation would be  */
+/*    appreciated but is not required.                                       */
+/* 2. Altered source versions must be plainly marked as such, and must not   */
+/*    be misrepresented as being the original software.                      */
+/* 3. This notice may not be removed or altered from any source              */
+/*    distribution.                                                          */
+/*                                                                           */
+/*****************************************************************************/
+
+
+
+#ifndef DBGINFO_H
+#define DBGINFO_H
+
+
+
+/*****************************************************************************/
+/*                                          Data                                    */
+/*****************************************************************************/
+
+
+
+/* Pointer to an opaque data structure containing information from the debug
+ * info file. Actually a handle to the data in the file.
+ */
+typedef void* cc65_dbginfo;
+
+/* Warnings/errors in cc65_read_dbginfo are passed via this struct */
+typedef struct cc65_parseerror cc65_parseerror;
+struct cc65_parseerror {
+    unsigned            type;           /* 0 = warning, 1 = error */
+    const char*         name;           /* Name of input file */
+    unsigned long       line;           /* Error line */
+    unsigned            column;         /* Error column */
+    char                errormsg[1];    /* Error message */
+};
+
+/* Function that is called in case of parse errors */
+typedef void (*cc65_errorfunc) (const struct cc65_parseerror*);
+
+/* File information */
+typedef struct cc65_fileinfo cc65_fileinfo;
+struct cc65_fileinfo {
+    char*               name;           /* Name of file with full path */
+    unsigned long       size;           /* Size of file */
+    unsigned long       mtime;          /* Modification time */
+};
+
+/* Line information */
+typedef struct cc65_lineinfo cc65_lineinfo;
+struct cc65_lineinfo {
+    unsigned            count;          /* Count of data sets that follow */
+    struct {
+        cc65_fileinfo*  fileinfo;       /* File information including name */
+        unsigned long   line;           /* Line number */
+    }                   data[1];
+};
+
+
+
+/*****************************************************************************/
+/*                                          Code                                    */
+/*****************************************************************************/
+
+
+
+cc65_dbginfo cc65_read_dbginfo (const char* filename, cc65_errorfunc errorfunc);
+/* Parse the debug info file with the given name. On success, the function
+ * will return a pointer to an opaque cc65_dbginfo structure, that must be
+ * passed to the other functions in this module to retrieve information.
+ * errorfunc is called in case of warnings and errors. If the file cannot be
+ * read successfully, NULL is returned.
+ */
+
+cc65_lineinfo* cc65_get_lineinfo (cc65_dbginfo handle, unsigned long addr);
+/* Return line information for the given address */
+
+void cc65_free_lineinfo (cc65_dbginfo handle, cc65_lineinfo* info);
+/* Free line info returned by cc65_get_lineinfo() */
+
+
+
+/* End of dbginfo.h */
+
+#endif
+
+
+
+
diff --git a/src/dbginfo/dbgtest.c b/src/dbginfo/dbgtest.c
new file mode 100644 (file)
index 0000000..ac4e52c
--- /dev/null
@@ -0,0 +1,85 @@
+/*****************************************************************************/
+/*                                                                           */
+/*                                 dbgtest.c                                 */
+/*                                                                           */
+/*                   Test file for the cc65 dbginfo module                   */
+/*                                                                           */
+/*                                                                           */
+/*                                                                           */
+/* (C) 2010,      Ullrich von Bassewitz                                      */
+/*                Roemerstrasse 52                                           */
+/*                D-70794 Filderstadt                                        */
+/* EMail:         uz@cc65.org                                                */
+/*                                                                           */
+/*                                                                           */
+/* This software is provided 'as-is', without any expressed or implied       */
+/* warranty.  In no event will the authors be held liable for any damages    */
+/* arising from the use of this software.                                    */
+/*                                                                           */
+/* Permission is granted to anyone to use this software for any purpose,     */
+/* including commercial applications, and to alter it and redistribute it    */
+/* freely, subject to the following restrictions:                            */
+/*                                                                           */
+/* 1. The origin of this software must not be misrepresented; you must not   */
+/*    claim that you wrote the original software. If you use this software   */
+/*    in a product, an acknowledgment in the product documentation would be  */
+/*    appreciated but is not required.                                       */
+/* 2. Altered source versions must be plainly marked as such, and must not   */
+/*    be misrepresented as being the original software.                      */
+/* 3. This notice may not be removed or altered from any source              */
+/*    distribution.                                                          */
+/*                                                                           */
+/*****************************************************************************/
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "dbginfo.h"
+
+
+
+static void ErrorFunc (const struct cc65_parseerror* E)
+/* Handle errors or warnings that occur while parsing a debug file */
+{
+    fprintf (stderr,
+             "%s:%s(%lu): %s\n",
+             E->type? "Error" : "Warning",
+             E->name,
+             E->line,
+             E->errormsg);
+}
+
+
+
+static void Usage (void)
+/* Print usage information and exit */
+{
+    printf ("Usage: dbgtest debug-file\n");
+    exit (1);
+}
+
+
+
+int main (int argc, char** argv)
+{
+    const char* Input;
+    cc65_dbginfo Handle;
+
+
+    /* Input file is argument */
+    if (argc != 2) {
+        Usage ();
+    }
+    Input = argv[1];
+
+
+    Handle = cc65_read_dbginfo (Input, ErrorFunc);
+    if (Handle == 0) {
+        fprintf (stderr, "No handle\n");
+    }
+    return 0;
+}
+
+
+
diff --git a/src/dbginfo/make/gcc.mak b/src/dbginfo/make/gcc.mak
new file mode 100644 (file)
index 0000000..06618cf
--- /dev/null
@@ -0,0 +1,58 @@
+#
+# Makefile for debug info test executable
+#
+
+
+
+
+# ------------------------------------------------------------------------------
+
+# The executable to build
+EXE    = dbgtest
+
+#
+CC      = gcc
+CFLAGS  = -O2 -g -Wall -W 
+EBIND   = emxbind
+LDFLAGS =
+
+# ------------------------------------------------------------------------------
+# Object files and libraries to link
+
+OBJS =         dbginfo.o       \
+       dbgtest.o
+
+
+# ------------------------------------------------------------------------------
+# Makefile targets
+
+# Main target - must be first
+.PHONY: all
+ifeq (.depend,$(wildcard .depend))
+all:           $(EXE)
+include .depend
+else
+all:   depend
+       @$(MAKE) -f make/gcc.mak all
+endif
+
+$(EXE):        $(OBJS)
+       $(CC) $(OBJS) $(LDFLAGS) -o $@
+       @if [ $(OS2_SHELL) ] ;  then $(EBIND) $(EXE) ; fi
+
+clean:
+       $(RM) *~ core.* *.map
+
+zap:   clean
+       $(RM) $(OBJS) $(EXE) .depend
+
+# ------------------------------------------------------------------------------
+# Make the dependencies
+
+.PHONY: depend dep
+depend dep:    $(OBJS:.o=.c)
+       @echo "Creating dependency information"
+       $(CC) -I$(COMMON) -MM $^ > .depend
+
+
+