hrev44364 adds 3 changesets to branch 'master' old head: fc4d98a2c09fcc8e96eda3f9a234ae9cd71dba48 new head: 6d60b554e6d6cee2a7e73e95b5e06374c9f2e32f ---------------------------------------------------------------------------- af350aa: Add private shared class ArgumentVector The parser is based on the FS shell's ArgVector. 0f1f968: Debugger: Actually create the CLI, if requested 6d60b55: Debugger: Some basic work to get the CLI going There's an input loop thread which reads and parses command lines and the infrastructure for registering and executing commands. Currently only "help" and "quit" commands are implemented. [ Ingo Weinhold <ingo_weinhold@xxxxxx> ] ---------------------------------------------------------------------------- 10 files changed, 607 insertions(+), 21 deletions(-) headers/private/shared/ArgumentVector.h | 53 ++++ src/apps/debugger/Debugger.cpp | 27 +- src/apps/debugger/Jamfile | 7 +- .../debugger/user_interface/cli/CliCommand.cpp | 20 ++ src/apps/debugger/user_interface/cli/CliCommand.h | 33 +++ src/apps/debugger/user_interface/cli/CliContext.h | 13 + .../cli/CommandLineUserInterface.cpp | 236 +++++++++++++++- .../user_interface/cli/CommandLineUserInterface.h | 35 +++ src/kits/shared/ArgumentVector.cpp | 203 +++++++++++++ src/kits/shared/Jamfile | 1 + ############################################################################ Commit: af350aa21891c6d37934df7686e2cba1d0f4f29f URL: http://cgit.haiku-os.org/haiku/commit/?id=af350aa Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Fri Jul 20 21:24:33 2012 UTC Add private shared class ArgumentVector The parser is based on the FS shell's ArgVector. ---------------------------------------------------------------------------- diff --git a/headers/private/shared/ArgumentVector.h b/headers/private/shared/ArgumentVector.h new file mode 100644 index 0000000..0b32a87 --- /dev/null +++ b/headers/private/shared/ArgumentVector.h @@ -0,0 +1,53 @@ +/* + * Copyright 2007-2012, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef _ARGUMENT_VECTOR_H +#define _ARGUMENT_VECTOR_H + + +#include <SupportDefs.h> + + +namespace BPrivate { + + +class ArgumentVector { +public: + enum ParseError { + NO_ERROR, + NO_MEMORY, + UNTERMINATED_QUOTED_STRING, + TRAILING_BACKSPACE + }; + +public: + ArgumentVector(); + ~ArgumentVector(); + + int32 ArgumentCount() const { return fCount; } + const char* const* Arguments() const { return fArguments; } + + char** DetachArguments(); + // Caller must free() -- it's all one big allocation at the + // returned pointer. + + ParseError Parse(const char* commandLine, + const char** _errorLocation = NULL); + +private: + struct Parser; + +private: + char** fArguments; + int32 fCount; +}; + + +} // namespace BPrivate + + +using BPrivate::ArgumentVector; + + +#endif // _ARGUMENT_VECTOR_H diff --git a/src/kits/shared/ArgumentVector.cpp b/src/kits/shared/ArgumentVector.cpp new file mode 100644 index 0000000..c678f5d --- /dev/null +++ b/src/kits/shared/ArgumentVector.cpp @@ -0,0 +1,203 @@ +/* + * Copyright 2007-2012, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include <ArgumentVector.h> + +#include <stdlib.h> +#include <string.h> + +#include <string> +#include <vector> + + +struct ArgumentVector::Parser { + ParseError Parse(const char* commandLine, const char*& _errorLocation) + { + // init temporary arg/argv storage + fCurrentArg.clear(); + fCurrentArgStarted = false; + fArgVector.clear(); + fTotalStringSize = 0; + + for (; *commandLine; commandLine++) { + char c = *commandLine; + + // whitespace delimits args and is otherwise ignored + if (isspace(c)) { + _PushCurrentArg(); + continue; + } + + const char* errorBase = commandLine; + + switch (c) { + case '\'': + // quoted string -- no quoting + while (*++commandLine != '\'') { + c = *commandLine; + if (c == '\0') { + _errorLocation = errorBase; + return UNTERMINATED_QUOTED_STRING; + } + _PushCharacter(c); + } + break; + + case '"': + // quoted string -- some quoting + while (*++commandLine != '"') { + c = *commandLine; + if (c == '\0') { + _errorLocation = errorBase; + return UNTERMINATED_QUOTED_STRING; + } + + if (c == '\\') { + c = *++commandLine; + if (c == '\0') { + _errorLocation = errorBase; + return UNTERMINATED_QUOTED_STRING; + } + + // only '\' and '"' can be quoted, otherwise the + // the '\' is treated as a normal char + if (c != '\\' && c != '"') + _PushCharacter('\\'); + } + + _PushCharacter(c); + } + break; + + case '\\': + // quoted char + c = *++commandLine; + if (c == '\0') { + _errorLocation = errorBase; + return TRAILING_BACKSPACE; + } + _PushCharacter(c); + break; + + default: + // normal char + _PushCharacter(c); + break; + } + } + + // commit last arg + _PushCurrentArg(); + + return NO_ERROR; + } + + const std::vector<std::string>& ArgVector() const + { + return fArgVector; + } + + size_t TotalStringSize() const + { + return fTotalStringSize; + } + +private: + void _PushCurrentArg() + { + if (fCurrentArgStarted) { + fArgVector.push_back(fCurrentArg); + fTotalStringSize += fCurrentArg.length() + 1; + fCurrentArgStarted = false; + } + } + + void _PushCharacter(char c) + { + if (!fCurrentArgStarted) { + fCurrentArg = ""; + fCurrentArgStarted = true; + } + + fCurrentArg += c; + } + +private: + // temporaries + std::string fCurrentArg; + bool fCurrentArgStarted; + std::vector<std::string> fArgVector; + size_t fTotalStringSize; +}; + + +ArgumentVector::ArgumentVector() + : + fArguments(NULL), + fCount(0) +{ +} + + +ArgumentVector::~ArgumentVector() +{ + free(fArguments); +} + + +char** +ArgumentVector::DetachArguments() +{ + char** arguments = fArguments; + fArguments = NULL; + fCount = 0; + return arguments; +} + + +ArgumentVector::ParseError +ArgumentVector::Parse(const char* commandLine, const char** _errorLocation) +{ + free(DetachArguments()); + + ParseError error; + const char* errorLocation = commandLine; + + try { + Parser parser; + error = parser.Parse(commandLine, errorLocation); + + if (error == NO_ERROR) { + // Create a char* array and copy everything into a single + // allocation. + int count = parser.ArgVector().size(); + size_t arraySize = (count + 1) * sizeof(char*); + fArguments = (char**)malloc( + arraySize + parser.TotalStringSize()); + if (fArguments != 0) { + char* argument = (char*)(fArguments + count + 1); + for (int i = 0; i < count; i++) { + fArguments[i] = argument; + const std::string& sourceArgument = parser.ArgVector()[i]; + size_t argumentSize = sourceArgument.length() + 1; + memcpy(argument, sourceArgument.c_str(), argumentSize); + argument += argumentSize; + } + + fArguments[count] = NULL; + fCount = count; + } else + error = NO_MEMORY; + } + } catch (...) { + error = NO_MEMORY; + } + + if (error != NO_ERROR && _errorLocation != NULL) + *_errorLocation = errorLocation; + + return error; +} diff --git a/src/kits/shared/Jamfile b/src/kits/shared/Jamfile index d56a8b6..8992caa 100644 --- a/src/kits/shared/Jamfile +++ b/src/kits/shared/Jamfile @@ -15,6 +15,7 @@ UsePrivateHeaders kernel libroot ; StaticLibrary libshared.a : AboutMenuItem.cpp AboutWindow.cpp + ArgumentVector.cpp CalendarView.cpp ColorQuantizer.cpp CommandPipe.cpp ############################################################################ Commit: 0f1f968ffb6f4b19193ccad1a4edae9e9a46ab19 URL: http://cgit.haiku-os.org/haiku/commit/?id=0f1f968 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Fri Jul 20 21:26:14 2012 UTC Debugger: Actually create the CLI, if requested ---------------------------------------------------------------------------- diff --git a/src/apps/debugger/Debugger.cpp b/src/apps/debugger/Debugger.cpp index c7a687c..f561747 100644 --- a/src/apps/debugger/Debugger.cpp +++ b/src/apps/debugger/Debugger.cpp @@ -56,7 +56,7 @@ static const char* kUsage = "\n" "Options:\n" " -h, --help - Print this usage info and exit.\n" - " -c, --cli - Use command line user interface (not yet implemented)\n" + " -c, --cli - Use command line user interface\n" ; @@ -397,7 +397,8 @@ Debugger::ArgvReceived(int32 argc, char** argv) return; } - start_team_debugger(team, &fSettingsManager, this, thread, stopInMain); + start_team_debugger(team, &fSettingsManager, this, thread, stopInMain, + options.useCLI); } @@ -481,6 +482,7 @@ Debugger::_FindTeamDebugger(team_id teamID) const // #pragma mark - + int main(int argc, const char* const* argv) { @@ -493,20 +495,15 @@ main(int argc, const char* const* argv) Options options; parse_arguments(argc, argv, false, options); - if (options.useCLI) { - // TODO: implement - fprintf(stderr, "Error: Command line interface unimplemented\n"); + Debugger app; + status_t error = app.Init(); + if (error != B_OK) { + fprintf(stderr, "Error: Failed to init application: %s\n", + strerror(error)); return 1; - } else { - Debugger app; - status_t error = app.Init(); - if (error != B_OK) { - fprintf(stderr, "Error: Failed to init application: %s\n", - strerror(error)); - return 1; - } - - app.Run(); } + + app.Run(); + return 0; } ############################################################################ Revision: hrev44364 Commit: 6d60b554e6d6cee2a7e73e95b5e06374c9f2e32f URL: http://cgit.haiku-os.org/haiku/commit/?id=6d60b55 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Fri Jul 20 21:30:34 2012 UTC Debugger: Some basic work to get the CLI going There's an input loop thread which reads and parses command lines and the infrastructure for registering and executing commands. Currently only "help" and "quit" commands are implemented. ---------------------------------------------------------------------------- diff --git a/src/apps/debugger/Jamfile b/src/apps/debugger/Jamfile index 193bc20..80c98fe 100644 --- a/src/apps/debugger/Jamfile +++ b/src/apps/debugger/Jamfile @@ -169,12 +169,13 @@ Application Debugger : # user_interface UserInterface.cpp - # user_interface/gui - GraphicalUserInterface.cpp - # user_interface/cli + CliCommand.cpp CommandLineUserInterface.cpp + # user_interface/gui + GraphicalUserInterface.cpp + # user_interface/gui/model VariablesViewState.cpp VariablesViewStateHistory.cpp diff --git a/src/apps/debugger/user_interface/cli/CliCommand.cpp b/src/apps/debugger/user_interface/cli/CliCommand.cpp new file mode 100644 index 0000000..c83c8cf --- /dev/null +++ b/src/apps/debugger/user_interface/cli/CliCommand.cpp @@ -0,0 +1,20 @@ +/* + * Copyright 2012, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include "CliCommand.h" + + +CliCommand::CliCommand(const char* summary, const char* usage) + : + fSummary(summary), + fUsage(usage) +{ +} + + +CliCommand::~CliCommand() +{ +} diff --git a/src/apps/debugger/user_interface/cli/CliCommand.h b/src/apps/debugger/user_interface/cli/CliCommand.h new file mode 100644 index 0000000..2a65e19 --- /dev/null +++ b/src/apps/debugger/user_interface/cli/CliCommand.h @@ -0,0 +1,33 @@ +/* + * Copyright 2012, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef CLI_COMMAND_H +#define CLI_COMMAND_H + + +#include <Referenceable.h> + + +class CliContext; + + +class CliCommand : public BReferenceable { +public: + CliCommand(const char* summary, + const char* usage); + virtual ~CliCommand(); + + const char* Summary() const { return fSummary; } + const char* Usage() const { return fUsage; } + + virtual void Execute(int argc, const char* const* argv, + CliContext& context) = 0; + +private: + const char* fSummary; + const char* fUsage; +}; + + +#endif // CLI_COMMAND_H diff --git a/src/apps/debugger/user_interface/cli/CliContext.h b/src/apps/debugger/user_interface/cli/CliContext.h new file mode 100644 index 0000000..df31816 --- /dev/null +++ b/src/apps/debugger/user_interface/cli/CliContext.h @@ -0,0 +1,13 @@ +/* + * Copyright 2012, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef CLI_CONTEXT_H +#define CLI_CONTEXT_H + + +class CliContext { +}; + + +#endif // CLI_CONTEXT_H diff --git a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp index 95fd5f2..ab53011 100644 --- a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp +++ b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp @@ -1,13 +1,106 @@ /* * Copyright 2011, Rene Gollent, rene@xxxxxxxxxxxx + * Copyright 2012, Ingo Weinhold, ingo_weinhold@xxxxxxx * Distributed under the terms of the MIT License. */ #include "CommandLineUserInterface.h" +#include <stdio.h> + +#include <algorithm> + +#include <ArgumentVector.h> +#include <Referenceable.h> + +#include "CliCommand.h" +#include "CliContext.h" + + +// #pragma mark - CommandEntry + + +struct CommandLineUserInterface::CommandEntry { + CommandEntry(const BString& name, CliCommand* command) + : + fName(name), + fCommand(command) + { + } + + const BString& Name() const + { + return fName; + } + + CliCommand* Command() const + { + return fCommand.Get(); + } + +private: + BString fName; + BReference<CliCommand> fCommand; +}; + + +// #pragma mark - HelpCommand + + +struct CommandLineUserInterface::HelpCommand : CliCommand { + HelpCommand(CommandLineUserInterface* userInterface) + : + CliCommand("print a list of all commands", + "%s\n" + "Prints a list of all commands."), + fUserInterface(userInterface) + { + } + + virtual void Execute(int argc, const char* const* argv, CliContext& context) + { + fUserInterface->_PrintHelp(); + } + +private: + CommandLineUserInterface* fUserInterface; +}; + + +// #pragma mark - HelpCommand + + +struct CommandLineUserInterface::QuitCommand : CliCommand { + QuitCommand(CommandLineUserInterface* userInterface) + : + CliCommand("quit Debugger", + "%s\n" + "Quits Debugger."), + fUserInterface(userInterface) + { + } + + virtual void Execute(int argc, const char* const* argv, CliContext& context) + { + fUserInterface->fListener->UserInterfaceQuitRequested(); + } + +private: + CommandLineUserInterface* fUserInterface; +}; + + +// #pragma mark - CommandLineUserInterface + CommandLineUserInterface::CommandLineUserInterface() + : + fThread(-1), + fTeam(NULL), + fListener(NULL), + fCommands(20, true), + fTerminating(false) { } @@ -27,33 +120,48 @@ CommandLineUserInterface::ID() const status_t CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener) { - return B_UNSUPPORTED; + fTeam = team; + fListener = listener; + + status_t error = _RegisterCommands(); + if (error != B_OK) + return error; + + fThread = spawn_thread(&_InputLoopEntry, "CLI", B_NORMAL_PRIORITY, this); + if (fThread < 0) + return fThread; + + return B_OK; } void CommandLineUserInterface::Show() { + resume_thread(fThread); } void CommandLineUserInterface::Terminate() { + fTerminating = true; + // TODO: Signal the thread so it wakes up! + wait_for_thread(fThread, NULL); } status_t CommandLineUserInterface::LoadSettings(const TeamUISettings* settings) { - return B_UNSUPPORTED; + return B_OK; } status_t CommandLineUserInterface::SaveSettings(TeamUISettings*& settings) const { - return B_UNSUPPORTED;; + return B_OK; } @@ -71,3 +179,125 @@ CommandLineUserInterface::SynchronouslyAskUser(const char* title, { return 0; } + + +/*static*/ status_t +CommandLineUserInterface::_InputLoopEntry(void* data) +{ + return ((CommandLineUserInterface*)data)->_InputLoop(); +} + + +status_t +CommandLineUserInterface::_InputLoop() +{ + while (!fTerminating) { + // read a command line + printf("debugger> "); + fflush(stdout); + char buffer[256]; + if (fgets(buffer, sizeof(buffer), stdin) == NULL) + break; + + // parse the command line + ArgumentVector args; + const char* parseErrorLocation; + switch (args.Parse(buffer, &parseErrorLocation)) { + case ArgumentVector::NO_ERROR: + break; + case ArgumentVector::NO_MEMORY: + printf("Insufficient memory parsing the command line.\n"); + continue; + case ArgumentVector::UNTERMINATED_QUOTED_STRING: + printf("Parse error: Unterminated quoted string starting at " + "character %zu.\n", parseErrorLocation - buffer + 1); + continue; + case ArgumentVector::TRAILING_BACKSPACE: + printf("Parse error: trailing backspace.\n"); + continue; + } + + if (args.ArgumentCount() == 0) + continue; + + _ExecuteCommand(args.ArgumentCount(), args.Arguments()); + } + + return B_OK; +} + + +status_t +CommandLineUserInterface::_RegisterCommands() +{ + if (_RegisterCommand("help", new(std::nothrow) HelpCommand(this)) && + _RegisterCommand("quit", new(std::nothrow) QuitCommand(this))) { + return B_OK; + } + + return B_NO_MEMORY; +} + + +bool +CommandLineUserInterface::_RegisterCommand(const BString& name, + CliCommand* command) +{ + BReference<CliCommand> commandReference(command, true); + if (name.IsEmpty() || command == NULL) + return false; + + CommandEntry* entry = new(std::nothrow) CommandEntry(name, command); + if (entry == NULL || !fCommands.AddItem(entry)) { + delete entry; + return false; + } + + return true; +} + + +void +CommandLineUserInterface::_ExecuteCommand(int argc, const char* const* argv) +{ + const char* commandName = argv[0]; + size_t commandNameLength = strlen(commandName); + + CommandEntry* firstEntry = NULL; + for (int32 i = 0; CommandEntry* entry = fCommands.ItemAt(i); i++) { + if (entry->Name().Compare(commandName, commandNameLength) == 0) { + if (firstEntry != NULL) { + printf("Ambiguous command \"%s\".\n", commandName); + return; + } + + firstEntry = entry; + } + } + + if (firstEntry == NULL) { + printf("Unknown command \"%s\".\n", commandName); + return; + } + + CliContext context; + firstEntry->Command()->Execute(argc, argv, context); +} + + +void +CommandLineUserInterface::_PrintHelp() +{ + // determine longest command name + int32 longestCommandName = 0; + for (int32 i = 0; CommandEntry* entry = fCommands.ItemAt(i); i++) { + longestCommandName + = std::max(longestCommandName, entry->Name().Length()); + } + + // print the command list + for (int32 i = 0; CommandEntry* entry = fCommands.ItemAt(i); i++) { + printf("%*s - %s\n", (int)longestCommandName, entry->Name().String(), + entry->Command()->Summary()); + } +} diff --git a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h index 4d04a7c..b581f36 100644 --- a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h +++ b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h @@ -1,14 +1,21 @@ /* * Copyright 2011, Rene Gollent, rene@xxxxxxxxxxxx + * Copyright 2012, Ingo Weinhold, ingo_weinhold@xxxxxxx * Distributed under the terms of the MIT License. */ #ifndef COMMAND_LINE_USER_INTERFACE_H #define COMMAND_LINE_USER_INTERFACE_H +#include <ObjectList.h> +#include <String.h> + #include "UserInterface.h" +class CliCommand; + + class CommandLineUserInterface : public UserInterface { public: CommandLineUserInterface(); @@ -33,6 +40,34 @@ public: const char* message, const char* choice1, const char* choice2, const char* choice3); +private: + struct CommandEntry; + typedef BObjectList<CommandEntry> CommandList; + + struct HelpCommand; + struct QuitCommand; + + // GCC 2 support + friend struct HelpCommand; + friend struct QuitCommand; + +private: + static status_t _InputLoopEntry(void* data); + status_t _InputLoop(); + + status_t _RegisterCommands(); + bool _RegisterCommand(const BString& name, + CliCommand* command); + void _ExecuteCommand(int argc, + const char* const* argv); + void _PrintHelp(); + +private: + thread_id fThread; + Team* fTeam; + UserInterfaceListener* fListener; + CommandList fCommands; + bool fTerminating; };