/*****************************************************************************/ /* */ /* dbgsh.c */ /* */ /* debug info test shell */ /* */ /* */ /* */ /* (C) 2011, 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 #include #include #include #include #include /* common */ #include "attrib.h" #include "chartype.h" #include "cmdline.h" #include "coll.h" /* dbginfo */ #include "dbginfo.h" /*****************************************************************************/ /* Command handler forwards */ /*****************************************************************************/ static void CmdHelp (Collection* Args attribute ((unused))); /* Output a help text */ static void CmdLoad (Collection* Args); /* Load a debug info file */ static void CmdQuit (Collection* Args attribute ((unused))); /* Terminate the application */ static void CmdShow (Collection* Args); /* Show items from the debug info file */ static void CmdShowHelp (Collection* Args); /* Print help for the show command */ static void CmdShowChildScopes (Collection* Args); /* Show child scopes from the debug info file */ static void CmdShowCSymbol (Collection* Args); /* Show c symbols from the debug info file */ static void CmdShowFunction (Collection* Args); /* Show C functions from the debug info file */ static void CmdShowLibrary (Collection* Args); /* Show libraries from the debug info file */ static void CmdShowLine (Collection* Args); /* Show lines from the debug info file */ static void CmdShowModule (Collection* Args); /* Show modules from the debug info file */ static void CmdShowScope (Collection* Args); /* Show scopes from the debug info file */ static void CmdShowSegment (Collection* Args); /* Show segments from the debug info file */ static void CmdShowSource (Collection* Args); /* Show source files from the debug info file */ static void CmdShowSpan (Collection* Args); /* Show spans from the debug info file */ static void CmdShowSymbol (Collection* Args); /* Show symbols */ static void CmdShowSymDef (Collection* Args); /* Show lines from the debug info file */ static void CmdShowSymRef (Collection* Args); /* Show lines from the debug info file */ static void CmdShowType (Collection* Args); /* Show types from the debug info file */ static void CmdUnload (Collection* Args attribute ((unused))); /* Unload a debug info file */ /*****************************************************************************/ /* Data */ /*****************************************************************************/ /* Terminate flag - end program when set to true */ static int Terminate = 0; /* The debug file data */ static cc65_dbginfo Info = 0; /* Error and warning counters */ static unsigned FileErrors = 0; static unsigned FileWarnings = 0; /* Type of an id */ enum { InvalidId, CSymbolId, LibraryId, LineId, ModuleId, ScopeId, SegmentId, SourceId, SpanId, SymbolId, TypeId }; /* Structure that contains a command description */ typedef struct CmdEntry CmdEntry; struct CmdEntry { char Cmd[12]; const char* Help; int ArgCount; void (*Func) (Collection*); }; /* Table with main commands */ static const CmdEntry MainCmds[] = { { "exit", 0, 1, CmdQuit }, { "help", "Show available commands", 1, CmdHelp }, { "load", "Load a debug info file", 2, CmdLoad }, { "quit", "Terminate the shell", 1, CmdQuit }, { "show", "Show items from the info file", -2, CmdShow }, { "unload", "Unload a debug info file", 1, CmdUnload }, }; /* Table with show commands */ static const CmdEntry ShowCmds[] = { { "childscopes", "Show child scopes of other scopes.", -2, CmdShowChildScopes }, { "csym", 0, -1, CmdShowCSymbol }, { "csymbol", "Show c symbols.", -1, CmdShowCSymbol }, { "func", 0, -2, CmdShowFunction }, { "function", "Show c functions.", -2, CmdShowFunction }, { "help", "Show available subcommands.", 1, CmdShowHelp }, { "line", "Show line info. May be followed by one or more line ids.", -2, CmdShowLine }, { "library", "Show libraries. May be followed by one or more library ids.", -1, CmdShowLibrary }, { "module", "Show modules. May be followed by one or more module ids.", -1, CmdShowModule }, { "scope", "Show scopes. May be followed by one or more segment ids.", -1, CmdShowScope }, { "segment", "Show segments. May be followed by one or more segment ids.", -1, CmdShowSegment }, { "source", "Show sources. May be followed by one or more source file ids.", -1, CmdShowSource }, { "span", "Show spans. May be followed by one or more span ids.", -1, CmdShowSpan }, { "symbol", "Show symbols. May be followed by one or more symbol or scope ids.", -2, CmdShowSymbol }, { "symdef", "Show where a symbol was defined. May be followed by one or more symbol ids.", -2, CmdShowSymDef }, { "symref", "Show where a symbol was referenced. May be followed by one or more symbol ids.", -2, CmdShowSymRef }, { "type", "Show type information. May be followed by one or more type ids.", -2, CmdShowType }, }; /*****************************************************************************/ /* Helpers */ /*****************************************************************************/ static void NewLine (void) /* Output a newline */ { putchar ('\n'); } static void Print (const char* Format, ...) attribute((format(printf,1,2))); static void Print (const char* Format, ...) /* Print a piece of output (no linefeed added) */ { va_list ap; va_start (ap, Format); vprintf (Format, ap); va_end (ap); } static void PrintLine (const char* Format, ...) attribute((format(printf,1,2))); static void PrintLine (const char* Format, ...) /* Print one line of output. The linefeed is supplied by the function */ { va_list ap; va_start (ap, Format); vprintf (Format, ap); NewLine (); va_end (ap); } static void PrintSeparator (void) /* Print a separator line */ { PrintLine ("---------------------------------------------------------------------------"); } static void FileError (const cc65_parseerror* Info) /* Callback function - is called in case of errors */ { /* Output a message */ PrintLine ("%s:%s(%lu): %s", Info->type? "Error" : "Warning", Info->name, (unsigned long) Info->line, Info->errormsg); /* Bump the counters */ switch (Info->type) { case CC65_WARNING: ++FileWarnings; break; default: ++FileErrors; break; } } static const CmdEntry* FindCmd (const char* Cmd, const CmdEntry* Tab, unsigned Count) /* Search for a command in the given table */ { unsigned I; for (I = 0; I < Count; ++I, ++Tab) { if (strcmp (Cmd, Tab->Cmd) == 0) { return Tab; } } return 0; } static void ExecCmd (Collection* Args, const CmdEntry* Tab, unsigned Count) /* Search for the command in slot 0 of the given collection. If found, check ** the argument count, then execute it. If there are problems, output a ** diagnostic. */ { /* Search for the command, check number of args, then execute it */ const char* Cmd = CollAt (Args, 0); const CmdEntry* E = FindCmd (Cmd, Tab, Count); if (E == 0) { PrintLine ("No such command: %s", Cmd); return; } /* Check the number of arguments. Zero means that the function will check ** itself. A negative count means that the function needs at least ** abs(count) arguments. A positive count means that the function needs ** exactly this number of arguments. ** Note: The number includes the command itself. */ if (E->ArgCount > 0 && (int)CollCount (Args) != E->ArgCount) { /* Argument number mismatch */ switch (E->ArgCount) { case 1: PrintLine ("Command doesn't accept an argument"); return; case 2: PrintLine ("Command requires an argument"); return; default: PrintLine ("Command requires %d arguments", E->ArgCount-1); return; } } else if (E->ArgCount < 0 && (int)CollCount (Args) < -E->ArgCount) { /* Argument number mismatch */ switch (E->ArgCount) { case -2: PrintLine ("Command requires at least one argument"); return; default: PrintLine ("Command requires at least %d arguments", E->ArgCount-1); return; } } else { /* Remove the command from the argument list, then execute it */ CollDelete (Args, 0); E->Func (Args); } } static void PrintHelp (const CmdEntry* Tab, unsigned Count) /* Output help for one command table */ { while (Count--) { /* Ignore the commands without help text */ if (Tab->Help) { PrintLine ("%-*s%s", (int) sizeof (Tab->Cmd) + 2, Tab->Cmd, Tab->Help); } ++Tab; } } static unsigned FindIdType (const char* TypeName) /* Find an id type by its name. Returns the type or InvalidId. */ { static const struct { char Name[8]; unsigned Type; } TypeTab[] = { { "l", LineId }, { "lib", LibraryId }, { "library", LibraryId }, { "line", LineId }, { "m", ModuleId }, { "mod", ModuleId }, { "module", ModuleId }, { "s", SymbolId }, { "sc", ScopeId }, { "scope", ScopeId }, { "seg", SegmentId }, { "segment", SegmentId }, { "source", SourceId }, { "src", SourceId }, { "sp", SpanId }, { "span", SpanId }, { "sym", SymbolId }, { "symbol", SymbolId }, { "t", TypeId }, { "type", TypeId }, }; unsigned I; for (I = 0; I < sizeof(TypeTab) / sizeof(TypeTab[0]); ++I) { if (strcmp (TypeName, TypeTab[I].Name) == 0) { return TypeTab[I].Type; } } return InvalidId; } static int GetId (const char* S, unsigned* Id, unsigned* IdType) /* Parse a string for an id. If a valid id is found, it is placed in Id and ** the function returns true. If an optional type is found, it is placed in ** IdType, otherwise IdType is left unchanged. If no id is found, the ** function returns false. */ { char TypeBuf[20]; char C; if (sscanf (S, "%u%c", Id, &C) == 1) { /* Just an id found, return it */ return 1; } if (sscanf (S, "%19[a-z]:%u%c", TypeBuf, Id, &C) == 2) { *IdType = FindIdType (TypeBuf); return (*IdType != InvalidId); } /* Not a valid id */ return 0; } /*****************************************************************************/ /* Output functions for item lists */ /*****************************************************************************/ static void PrintAddr (cc65_addr Addr, unsigned FieldWidth) /* Output an address */ { Print ("$%06lX", (unsigned long) Addr); if (FieldWidth > 7) { Print ("%*s", FieldWidth - 7, ""); } } static void PrintNumber (long Num, unsigned Width, unsigned FieldWidth) /* Output a number */ { Print ("%*ld", Width, Num); if (FieldWidth > Width) { Print ("%*s", FieldWidth - Width, ""); } } static void PrintId (unsigned Id, unsigned FieldWidth) /* Output an id field */ { if (Id == CC65_INV_ID) { Print (" -"); } else { Print ("%4u", Id); } if (FieldWidth > 4) { Print ("%*s", FieldWidth - 4, ""); } } static void PrintSize (cc65_size Size, unsigned FieldWidth) /* Output a size */ { Print ("$%04lX", (unsigned long) Size); if (FieldWidth > 5) { Print ("%*s", FieldWidth - 5, ""); } } static void PrintTime (time_t T, unsigned FieldWidth) /* Output a time stamp of some sort */ { /* Convert to string */ char Buf[100]; unsigned Len = strftime (Buf, sizeof (Buf), "%Y-%m-%d %H:%M:%S", localtime (&T)); /* Output */ Print ("%s", Buf); if (FieldWidth > Len) { Print ("%*s", FieldWidth - Len, ""); } } static void PrintCSymbolHeader (void) /* Output a header for a list of C symbols */ { /* Header */ PrintLine (" id name type kind sc offs symbol scope"); PrintSeparator (); } static void PrintCSymbols (const cc65_csyminfo* S) /* Output a list of C symbols */ { unsigned I; const cc65_csymdata* D; /* Segments */ for (I = 0, D = S->data; I < S->count; ++I, ++D) { PrintId (D->csym_id, 6); Print ("%-28s", D->csym_name); PrintId (D->type_id, 6); PrintNumber (D->csym_kind, 4, 6); PrintNumber (D->csym_sc, 4, 6); PrintNumber (D->csym_offs, 4, 8); PrintId (D->symbol_id, 6); PrintId (D->scope_id, 0); NewLine (); } } static void PrintLibraryHeader (void) /* Output the header for a library list */ { PrintLine (" id name"); PrintSeparator (); } static void PrintLibraries (const cc65_libraryinfo* L) /* Output a list of libraries */ { unsigned I; const cc65_librarydata* D; /* Libraries */ for (I = 0, D = L->data; I < L->count; ++I, ++D) { PrintId (D->library_id, 8); Print ("%-24s", D->library_name); NewLine (); } } static void PrintLineHeader (void) /* Output a header for a line list */ { /* Header */ PrintLine (" id source line type count"); PrintSeparator (); } static void PrintLines (const cc65_lineinfo* L) /* Output a list of lines */ { unsigned I; const cc65_linedata* D; /* Lines */ for (I = 0, D = L->data; I < L->count; ++I, ++D) { PrintId (D->line_id, 8); PrintId (D->source_id, 8); PrintNumber (D->source_line, 6, 9); PrintNumber (D->line_type, 4, 6); PrintNumber (D->count, 3, 0); NewLine (); } } static void PrintModuleHeader (void) /* Output a header for a module list */ { /* Header */ PrintLine (" id name source library scope"); PrintSeparator (); } static void PrintModules (const cc65_moduleinfo* M) /* Output a list of modules */ { unsigned I; const cc65_moduledata* D; /* Modules */ for (I = 0, D = M->data; I < M->count; ++I, ++D) { PrintId (D->module_id, 8); Print ("%-24s", D->module_name); PrintId (D->source_id, 8); PrintId (D->library_id, 9); PrintId (D->scope_id, 6); NewLine (); } } static void PrintScopeHeader (void) /* Output a header for a list of scopes */ { /* Header */ PrintLine (" id name type size parent symbol module"); PrintSeparator (); } static void PrintScopes (const cc65_scopeinfo* S) /* Output a list of scopes */ { unsigned I; const cc65_scopedata* D; /* Segments */ for (I = 0, D = S->data; I < S->count; ++I, ++D) { PrintId (D->scope_id, 8); Print ("%-24s", D->scope_name); PrintNumber (D->scope_type, 4, 8); /* ## */ PrintSize (D->scope_size, 8); PrintId (D->parent_id, 8); PrintId (D->symbol_id, 8); PrintId (D->module_id, 0); NewLine (); } } static void PrintSegmentHeader (void) /* Output a header for a list of segments */ { /* Header */ PrintLine (" id name address size output file offs"); PrintSeparator (); } static void PrintSegments (const cc65_segmentinfo* S) /* Output a list of segments */ { unsigned I; const cc65_segmentdata* D; /* Segments */ for (I = 0, D = S->data; I < S->count; ++I, ++D) { PrintId (D->segment_id, 8); Print ("%-16s", D->segment_name); PrintAddr (D->segment_start, 9); PrintSize (D->segment_size, 7); Print ("%-16s", D->output_name? D->output_name : ""); PrintSize (D->output_offs, 6); NewLine (); } } static void PrintSourceHeader (void) /* Output a header for a list of source files */ { /* Header */ PrintLine (" id name size modification time"); PrintSeparator (); } static void PrintSources (const cc65_sourceinfo* S) /* Output a list of sources */ { unsigned I; const cc65_sourcedata* D; /* Segments */ for (I = 0, D = S->data; I < S->count; ++I, ++D) { PrintId (D->source_id, 8); Print ("%-30s", D->source_name); PrintNumber (D->source_size, 7, 9); PrintTime (D->source_mtime, 0); NewLine (); } } static void PrintSpanHeader (void) /* Output a header for a list of spans */ { /* Header */ PrintLine (" id start end seg type lines scopes"); PrintSeparator (); } static void PrintSpans (const cc65_spaninfo* S) /* Output a list of spans */ { unsigned I; const cc65_spandata* D; /* Segments */ for (I = 0, D = S->data; I < S->count; ++I, ++D) { PrintId (D->span_id, 7); PrintAddr (D->span_start, 8); PrintAddr (D->span_end, 9); PrintId (D->segment_id, 7); PrintId (D->type_id, 6); PrintNumber (D->line_count, 6, 7); PrintNumber (D->scope_count, 7, 0); NewLine (); } } static void PrintSymbolHeader (void) /* Output a header for a list of symbols */ { /* Header */ PrintLine (" id name type size value export seg scope parent"); PrintSeparator (); } static void PrintSymbols (const cc65_symbolinfo* S) /* Output a list of symbols */ { unsigned I; const cc65_symboldata* D; /* Segments */ for (I = 0, D = S->data; I < S->count; ++I, ++D) { PrintId (D->symbol_id, 6); Print ("%-24s", D->symbol_name); PrintNumber (D->symbol_type, 4, 6); PrintNumber (D->symbol_size, 4, 6); PrintNumber (D->symbol_value, 5, 7); PrintId (D->export_id, 7); PrintId (D->segment_id, 6); PrintId (D->scope_id, 6); PrintId (D->parent_id, 0); NewLine (); } } static void PrintTypeHeader (void) /* Output a header for a list of types */ { /* Header */ PrintLine (" id description"); PrintSeparator (); } static void PrintType (unsigned Id, const cc65_typedata* T) /* Output one type */ { /* Output the id */ PrintId (Id, 6); while (1) { switch (T->what) { case CC65_TYPE_VOID: Print ("VOID"); goto ExitPoint; case CC65_TYPE_BYTE: Print ("BYTE"); goto ExitPoint; case CC65_TYPE_WORD: Print ("WORD"); goto ExitPoint; case CC65_TYPE_DBYTE: Print ("DBYTE"); goto ExitPoint; case CC65_TYPE_DWORD: Print ("DWORD"); goto ExitPoint; case CC65_TYPE_FARPTR: Print ("FAR "); /* FALLTHROUGH */ case CC65_TYPE_PTR: Print ("POINTER TO "); T = T->data.ptr.ind_type; break; case CC65_TYPE_ARRAY: Print ("ARRAY[%u] OF ", T->data.array.ele_count); T = T->data.array.ele_type; break; default: /* Anything else is currently not implemented */ Print ("***NOT IMPLEMENTED***"); goto ExitPoint; } } ExitPoint: NewLine (); } /*****************************************************************************/ /* Debug file handling */ /*****************************************************************************/ static void UnloadFile (void) /* Unload the debug info file */ { if (Info) { cc65_free_dbginfo (Info); Info = 0; } } static int FileIsLoaded (void) /* Return true if the file is open and has loaded without errors: If not, ** print an error message and return false. */ { /* File open? */ if (Info == 0) { PrintLine ("No debug info file"); return 0; } /* Errors on load? */ if (FileErrors > 0) { PrintLine ("File had load errors!"); return 0; } /* Warnings on load? */ if (FileWarnings > 0) { PrintLine ("Beware - file had load warnings!"); } /* Ok */ return 1; } /*****************************************************************************/ /* Command handlers */ /*****************************************************************************/ static void CmdHelp (Collection* Args attribute ((unused))) /* Output a help text */ { PrintHelp (MainCmds, sizeof (MainCmds) / sizeof (MainCmds[0])); } static void CmdLoad (Collection* Args) /* Load a debug info file */ { /* Unload a loaded file */ UnloadFile (); /* Clear the counters */ FileErrors = 0; FileWarnings = 0; /* Open the debug info file */ Info = cc65_read_dbginfo (CollAt (Args, 0), FileError); /* Print a status */ if (FileErrors > 0) { PrintLine ("File loaded with %u errors", FileErrors); } else if (FileWarnings > 0) { PrintLine ("File loaded with %u warnings", FileWarnings); } else { PrintLine ("File loaded successfully"); } } static void CmdQuit (Collection* Args attribute ((unused))) /* Terminate the application */ { UnloadFile (); Terminate = 1; } static void CmdShow (Collection* Args) /* Show items from the debug info file */ { /* Search for the subcommand, check number of args, then execute it */ ExecCmd (Args, ShowCmds, sizeof (ShowCmds) / sizeof (ShowCmds[0])); } static void CmdShowHelp (Collection* Args attribute ((unused))) /* Print help for the show command */ { PrintHelp (ShowCmds, sizeof (ShowCmds) / sizeof (ShowCmds[0])); } static void CmdShowChildScopes (Collection* Args) /* Show child scopes from the debug info file */ { const cc65_scopeinfo* S; unsigned I; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintScopeHeader (); /* Output child scopes for all arguments */ for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = ScopeId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case ScopeId: S = cc65_childscopes_byid (Info, Id); break; default: S = 0; PrintLine ("Invalid id type"); break; } } else { /* Invalid id */ S = 0; } /* Output the list */ if (S) { PrintScopes (S); cc65_free_scopeinfo (Info, S); } } } static void CmdShowCSymbol (Collection* Args) /* Show C symbols from the debug info file */ { const cc65_csyminfo* S; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintCSymbolHeader (); /* No arguments means show all libraries */ if (CollCount (Args) == 0) { /* Fetch the list of c symbols */ S = cc65_get_csymlist (Info); /* Output the c symbols */ PrintCSymbols (S); /* Free the list */ cc65_free_csyminfo (Info, S); } else { /* Output c symbols for all arguments */ unsigned I; for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = CSymbolId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case CSymbolId: S = cc65_csym_byid (Info, Id); break; case ScopeId: S = cc65_csym_byscope (Info, Id); break; default: S = 0; PrintLine ("Invalid id type"); break; } } else { /* Invalid id */ S = 0; } /* Output the list */ if (S) { PrintCSymbols (S); cc65_free_csyminfo (Info, S); } } } } static void CmdShowFunction (Collection* Args) /* Show C functions from the debug info file */ { const cc65_csyminfo* S; unsigned I; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintCSymbolHeader (); /* Output c symbols for all arguments */ for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = ModuleId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case ModuleId: S = cc65_cfunc_bymodule (Info, Id); break; default: S = 0; PrintLine ("Invalid id type"); break; } } else { /* An invalid id may be a function name */ S = cc65_cfunc_byname (Info, CollConstAt (Args, I)); } /* Output the list */ if (S) { PrintCSymbols (S); cc65_free_csyminfo (Info, S); } } } static void CmdShowLine (Collection* Args) /* Show lines from the debug info file */ { const cc65_lineinfo* L; unsigned I; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintLineHeader (); for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = LineId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case LineId: L = cc65_line_byid (Info, Id); break; case SourceId: L = cc65_line_bysource (Info, Id); break; case SymbolId: /* ### not very clean */ L = cc65_line_bysymdef (Info, Id); if (L) { PrintLines (L); cc65_free_lineinfo (Info, L); } L = cc65_line_bysymref (Info, Id); break; default: L = 0; PrintLine ("Invalid id type"); break; } } else { /* Ignore the invalid id */ L = 0; } /* Output the list */ if (L) { PrintLines (L); cc65_free_lineinfo (Info, L); } } } static void CmdShowLibrary (Collection* Args) /* Show libraries from the debug info file */ { const cc65_libraryinfo* L; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintLibraryHeader (); /* No arguments means show all libraries */ if (CollCount (Args) == 0) { /* Fetch the list of libraries */ L = cc65_get_librarylist (Info); /* Output the libraries */ PrintLibraries (L); /* Free the list */ cc65_free_libraryinfo (Info, L); } else { unsigned I; for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = LibraryId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case LibraryId: L = cc65_library_byid (Info, Id); break; default: L = 0; PrintLine ("Invalid id type"); break; } } else { /* Ignore the invalid id */ L = 0; } /* Output the list */ if (L) { PrintLibraries (L); cc65_free_libraryinfo (Info, L); } } } } static void CmdShowModule (Collection* Args) /* Show modules from the debug info file */ { const cc65_moduleinfo* M; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintModuleHeader (); /* No arguments means show all modules */ if (CollCount (Args) == 0) { /* Fetch the list of modules */ M = cc65_get_modulelist (Info); /* Output the modules */ PrintModules (M); /* Free the list */ cc65_free_moduleinfo (Info, M); } else { unsigned I; for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = ModuleId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case ModuleId: M = cc65_module_byid (Info, Id); break; default: M = 0; PrintLine ("Invalid id type"); break; } } else { /* Ignore the invalid id */ M = 0; } /* Output the list */ if (M) { PrintModules (M); cc65_free_moduleinfo (Info, M); } } } } static void CmdShowScope (Collection* Args) /* Show scopes from the debug info file */ { const cc65_scopeinfo* S; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintScopeHeader (); /* No arguments means show all modules */ if (CollCount (Args) == 0) { /* Fetch the list of segments */ S = cc65_get_scopelist (Info); /* Output the segments */ PrintScopes (S); /* Free the list */ cc65_free_scopeinfo (Info, S); } else { unsigned I; for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = ScopeId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case ModuleId: S = cc65_scope_bymodule (Info, Id); break; case ScopeId: S = cc65_scope_byid (Info, Id); break; default: S = 0; PrintLine ("Invalid id type"); break; } } else { /* An invalid id may be a scope name */ S = cc65_scope_byname (Info, CollConstAt (Args, I)); } /* Output the list */ if (S) { PrintScopes (S); cc65_free_scopeinfo (Info, S); } } } } static void CmdShowSegment (Collection* Args) /* Show segments from the debug info file */ { const cc65_segmentinfo* S; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintSegmentHeader (); /* No arguments means show all modules */ if (CollCount (Args) == 0) { /* Fetch the list of segments */ S = cc65_get_segmentlist (Info); /* Output the segments */ PrintSegments (S); /* Free the list */ cc65_free_segmentinfo (Info, S); } else { unsigned I; for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = SegmentId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case SegmentId: S = cc65_segment_byid (Info, Id); break; default: S = 0; PrintLine ("Invalid id type"); break; } } else { /* An invalid id may be a segment name */ S = cc65_segment_byname (Info, CollConstAt (Args, I)); } /* Output the list */ if (S) { PrintSegments (S); cc65_free_segmentinfo (Info, S); } } } } static void CmdShowSource (Collection* Args) /* Show source files from the debug info file */ { const cc65_sourceinfo* S; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintSourceHeader (); /* No arguments means show all modules */ if (CollCount (Args) == 0) { /* Fetch the list of source files */ S = cc65_get_sourcelist (Info); /* Output the source files */ PrintSources (S); /* Free the list */ cc65_free_sourceinfo (Info, S); } else { unsigned I; for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = SourceId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case ModuleId: S = cc65_source_bymodule (Info, Id); break; case SourceId: S = cc65_source_byid (Info, Id); break; default: S = 0; PrintLine ("Invalid id type"); break; } } else { /* Ignore the invalid id */ S = 0; } /* Output the list */ if (S) { PrintSources (S); cc65_free_sourceinfo (Info, S); } } } } static void CmdShowSpan (Collection* Args) /* Show spans from the debug info file */ { const cc65_spaninfo* S; unsigned I; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintSpanHeader (); for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = SpanId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case LineId: S = cc65_span_byline (Info, Id); break; case ScopeId: S = cc65_span_byscope (Info, Id); break; case SpanId: S = cc65_span_byid (Info, Id); break; default: S = 0; PrintLine ("Invalid id type"); break; } } else { /* Ignore the invalid id */ S = 0; } /* Output the list */ if (S) { PrintSpans (S); cc65_free_spaninfo (Info, S); } } } static void CmdShowSymbol (Collection* Args) /* Show symbols from the debug info file */ { const cc65_symbolinfo* S; unsigned I; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintSymbolHeader (); for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = SymbolId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case ScopeId: S = cc65_symbol_byscope (Info, Id); break; case SymbolId: S = cc65_symbol_byid (Info, Id); break; default: S = 0; PrintLine ("Invalid id type"); break; } } else { /* Ignore the invalid id */ S = 0; } /* Output the list */ if (S) { PrintSymbols (S); cc65_free_symbolinfo (Info, S); } } } static void CmdShowSymDef (Collection* Args) /* Show symbol definitions from the debug info file */ { const cc65_lineinfo* L; unsigned I; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintLineHeader (); for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = SymbolId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case SymbolId: L = cc65_line_bysymdef (Info, Id); break; default: L = 0; PrintLine ("Invalid id type"); break; } } else { /* Ignore the invalid id */ L = 0; } /* Output the list */ if (L) { PrintLines (L); cc65_free_lineinfo (Info, L); } } } static void CmdShowSymRef (Collection* Args) /* Show symbol references from the debug info file */ { const cc65_lineinfo* L; unsigned I; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintLineHeader (); for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = SymbolId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case SymbolId: L = cc65_line_bysymref (Info, Id); break; default: L = 0; PrintLine ("Invalid id type"); break; } } else { /* Ignore the invalid id */ L = 0; } /* Output the list */ if (L) { PrintLines (L); cc65_free_lineinfo (Info, L); } } } static void CmdShowType (Collection* Args) /* Show types from the debug info file */ { const cc65_typedata* T; const cc65_spaninfo* S; unsigned I; /* Be sure a file is loaded */ if (!FileIsLoaded ()) { return; } /* Output the header */ PrintTypeHeader (); for (I = 0; I < CollCount (Args); ++I) { /* Parse the argument */ unsigned Id; unsigned IdType = TypeId; if (GetId (CollConstAt (Args, I), &Id, &IdType)) { /* Fetch list depending on type */ switch (IdType) { case SpanId: S = cc65_span_byid (Info, Id); if (S == 0 || S->count == 0) { T = 0; break; } Id = S->data[0].type_id; /* FALLTHROUGH */ case TypeId: T = cc65_type_byid (Info, Id); break; default: T = 0; PrintLine ("Invalid id type"); break; } } else { /* Ignore the invalid id */ T = 0; } /* Output the list */ if (T) { PrintType (Id, T); cc65_free_typedata (Info, T); } } } static void CmdUnload (Collection* Args attribute ((unused))) /* Unload a debug info file */ { UnloadFile (); } /*****************************************************************************/ /* Code */ /*****************************************************************************/ static int Parse (char* CmdLine, Collection* Args) /* Parse the command line and store the arguments in Args. Return true if ok, ** false on error. */ { char* End; /* Clear the collection */ CollDeleteAll (Args); /* Parse the command line */ while (1) { /* Break out on end of line */ if (*CmdLine == '\0') { break; } /* Search for start of next command */ if (IsSpace (*CmdLine)) { ++CmdLine; continue; } /* Allow double quotes to terminate a command */ if (*CmdLine == '\"' || *CmdLine == '\'') { char Term = *CmdLine++; End = CmdLine; while (*End != Term) { if (*End == '\0') { PrintLine ("Unterminated argument"); return 0; } ++End; } *End++ = '\0'; } else { End = CmdLine; while (!IsSpace (*End)) { if (*End == '\0') { PrintLine ("Unterminated argument"); return 0; } ++End; } *End++ = '\0'; } CollAppend (Args, CmdLine); CmdLine = End; } /* Ok */ return 1; } int main (int argc, char* argv[]) /* Main program */ { char Input[256]; Collection Args = STATIC_COLLECTION_INITIALIZER; /* Initialize the command line */ InitCmdLine (&argc, &argv, "dbgsh"); /* If we have commands on the command line, execute them */ if (ArgCount > 1) { unsigned I; for (I = 1; I < ArgCount; ++I) { CollAppend (&Args, ArgVec[I]); } /* Search for the command, check number of args, then execute it */ ExecCmd (&Args, MainCmds, sizeof (MainCmds) / sizeof (MainCmds[0])); } /* Loop until program end */ while (!Terminate) { /* Output a prompt, then read the input */ Print ("dbgsh> "); fflush (stdout); if (fgets (Input, sizeof (Input), stdin) == 0) { PrintLine ("(EOF)"); break; } /* Parse the command line */ if (Parse (Input, &Args) == 0 || CollCount (&Args) == 0) { continue; } /* Search for the command, check number of args, then execute it */ ExecCmd (&Args, MainCmds, sizeof (MainCmds) / sizeof (MainCmds[0])); } /* Free arguments */ DoneCollection (&Args); return 0; }