diff --git a/src/apps/debugger/user_interface/cli/CliContext.cpp b/src/apps/debugger/user_interface/cli/CliContext.cpp index cb099bf4ae..ad14f82919 100644 --- a/src/apps/debugger/user_interface/cli/CliContext.cpp +++ b/src/apps/debugger/user_interface/cli/CliContext.cpp @@ -6,27 +6,160 @@ #include "CliContext.h" +#include + #include "UserInterface.h" +// NOTE: This is a simple work-around for EditLine not having any kind of user +// data field. Hence in _GetPrompt() we don't have access to the context object. +// ATM only one CLI is possible in Debugger, so a static variable works well +// enough. Should that ever change, we would need a thread-safe +// EditLine* -> CliContext* map. +static CliContext* sCurrentContext; + + CliContext::CliContext() : + fLock("CliContext"), fTeam(NULL), - fListener(NULL) + fListener(NULL), + fEditLine(NULL), + fHistory(NULL), + fPrompt(NULL), + fBlockingSemaphore(-1), + fInputLoopWaiting(false), + fTerminating(false) { + sCurrentContext = this; +} + +CliContext::~CliContext() +{ + Cleanup(); + sCurrentContext = NULL; } -void +status_t CliContext::Init(Team* team, UserInterfaceListener* listener) { fTeam = team; fListener = listener; + + status_t error = fLock.InitCheck(); + if (error != B_OK) + return error; + + fBlockingSemaphore = create_sem(0, "CliContext block"); + if (fBlockingSemaphore < 0) + return fBlockingSemaphore; + + fEditLine = el_init("Debugger", stdin, stdout, stderr); + if (fEditLine == NULL) + return B_ERROR; + + fHistory = history_init(); + if (fHistory == NULL) + return B_ERROR; + + HistEvent historyEvent; + history(fHistory, &historyEvent, H_SETSIZE, 100); + + el_set(fEditLine, EL_HIST, &history, fHistory); + el_set(fEditLine, EL_EDITOR, "emacs"); + el_set(fEditLine, EL_PROMPT, &_GetPrompt); + + return B_OK; } void -CliContext::QuitSession() +CliContext::Cleanup() { - fListener->UserInterfaceQuitRequested(); + Terminating(); + + if (fEditLine != NULL) { + el_end(fEditLine); + fEditLine = NULL; + } + + if (fHistory != NULL) { + history_end(fHistory); + fHistory = NULL; + } +} + + +void +CliContext::Terminating() +{ + AutoLocker locker(fLock); + + fTerminating = true; + + if (fBlockingSemaphore >= 0) { + delete_sem(fBlockingSemaphore); + fBlockingSemaphore = -1; + } + + fInputLoopWaiting = false; + + // TODO: Signal the input loop, should it be in PromptUser()! +} + + +const char* +CliContext::PromptUser(const char* prompt) +{ + fPrompt = prompt; + + int count; + const char* line = el_gets(fEditLine, &count); + + fPrompt = NULL; + + return line; +} + + +void +CliContext::AddLineToInputHistory(const char* line) +{ + HistEvent historyEvent; + history(fHistory, &historyEvent, H_ENTER, line); +} + + +void +CliContext::QuitSession(bool killTeam) +{ + AutoLocker locker(fLock); + + sem_id blockingSemaphore = fBlockingSemaphore; + fInputLoopWaiting = true; + + locker.Unlock(); + + fListener->UserInterfaceQuitRequested( + killTeam + ? UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM + : UserInterfaceListener::QUIT_OPTION_ASK_RESUME_TEAM); + + while (acquire_sem(blockingSemaphore) == B_INTERRUPTED) { + } +} + + +void +CliContext::WaitForThreadOrUser() +{ + // TODO:... +} + + +/*static*/ const char* +CliContext::_GetPrompt(EditLine* editLine) +{ + return sCurrentContext != NULL ? sCurrentContext->fPrompt : NULL; } diff --git a/src/apps/debugger/user_interface/cli/CliContext.h b/src/apps/debugger/user_interface/cli/CliContext.h index 93d06af3fa..e3617fe6f7 100644 --- a/src/apps/debugger/user_interface/cli/CliContext.h +++ b/src/apps/debugger/user_interface/cli/CliContext.h @@ -6,6 +6,13 @@ #define CLI_CONTEXT_H +#include + // Needed in histedit.h. +#include + +#include + + class Team; class UserInterfaceListener; @@ -13,18 +20,38 @@ class UserInterfaceListener; class CliContext { public: CliContext(); + ~CliContext(); - void Init(Team* team, + status_t Init(Team* team, UserInterfaceListener* listener); + void Cleanup(); + + void Terminating(); + + // service methods for the input loop thread follow Team* GetTeam() const { return fTeam; } - void QuitSession(); + const char* PromptUser(const char* prompt); + void AddLineToInputHistory(const char* line); + void QuitSession(bool killTeam); + + void WaitForThreadOrUser(); private: + static const char* _GetPrompt(EditLine* editLine); + +private: + BLocker fLock; Team* fTeam; UserInterfaceListener* fListener; + EditLine* fEditLine; + History* fHistory; + const char* fPrompt; + sem_id fBlockingSemaphore; + bool fInputLoopWaiting; + volatile bool fTerminating; }; diff --git a/src/apps/debugger/user_interface/cli/CliQuitCommand.cpp b/src/apps/debugger/user_interface/cli/CliQuitCommand.cpp index d3a8cb83f7..49d8d825da 100644 --- a/src/apps/debugger/user_interface/cli/CliQuitCommand.cpp +++ b/src/apps/debugger/user_interface/cli/CliQuitCommand.cpp @@ -6,6 +6,8 @@ #include "CliQuitCommand.h" +#include + #include "CliContext.h" @@ -21,5 +23,27 @@ CliQuitCommand::CliQuitCommand() void CliQuitCommand::Execute(int argc, const char* const* argv, CliContext& context) { - context.QuitSession(); + // Ask the user what to do with the debugged team. + printf("Kill or resume the debugged team?\n"); + for (;;) { + const char* line = context.PromptUser("(k)ill, (r)esume, (c)ancel? "); + if (line == NULL) + return; + + BString trimmedLine(line); + trimmedLine.Trim(); + + if (trimmedLine == "k") { + context.QuitSession(true); + break; + } + + if (trimmedLine == "r") { + context.QuitSession(false); + break; + } + + if (trimmedLine == "d") + break; + } } diff --git a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp index 9e27749494..c00111291f 100644 --- a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp +++ b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp @@ -20,11 +20,7 @@ #include "CliThreadsCommand.h" -static const char* -get_prompt(EditLine* editLine) -{ - return "debugger> "; -} +static const char* kDebuggerPrompt = "debugger> "; // #pragma mark - CommandEntry @@ -83,8 +79,6 @@ private: CommandLineUserInterface::CommandLineUserInterface() : fCommands(20, true), - fEditLine(NULL), - fHistory(NULL), fShowSemaphore(-1), fShown(false), fTerminating(false) @@ -96,12 +90,6 @@ CommandLineUserInterface::~CommandLineUserInterface() { if (fShowSemaphore >= 0) delete_sem(fShowSemaphore); - - if (fEditLine != NULL) - el_end(fEditLine); - - if (fHistory != NULL) - history_end(fHistory); } @@ -115,9 +103,11 @@ CommandLineUserInterface::ID() const status_t CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener) { - fContext.Init(team, listener); + status_t error = fContext.Init(team, listener); + if (error != B_OK) + return error; - status_t error = _RegisterCommands(); + error = _RegisterCommands(); if (error != B_OK) return error; @@ -125,21 +115,6 @@ CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener) if (fShowSemaphore < 0) return fShowSemaphore; - fEditLine = el_init("Debugger", stdin, stdout, stderr); - if (fEditLine == NULL) - return B_ERROR; - - fHistory = history_init(); - if (fHistory == NULL) - return B_ERROR; - - HistEvent historyEvent; - history(fHistory, &historyEvent, H_SETSIZE, 100); - - el_set(fEditLine, EL_HIST, &history, fHistory); - el_set(fEditLine, EL_EDITOR, "emacs"); - el_set(fEditLine, EL_PROMPT, &get_prompt); - return B_OK; } @@ -158,7 +133,7 @@ CommandLineUserInterface::Terminate() fTerminating = true; if (fShown) { - // TODO: Signal the thread so it wakes up! + fContext.Terminating(); // Wait for input loop to finish. while (acquire_sem(fShowSemaphore) == B_INTERRUPTED) { @@ -169,15 +144,7 @@ CommandLineUserInterface::Terminate() fShowSemaphore = -1; } - if (fEditLine != NULL) { - el_end(fEditLine); - fEditLine = NULL; - } - - if (fHistory != NULL) { - history_end(fHistory); - fHistory = NULL; - } + fContext.Cleanup(); } @@ -241,9 +208,11 @@ status_t CommandLineUserInterface::_InputLoop() { while (!fTerminating) { + // Wait for a thread or Ctrl-C. + fContext.WaitForThreadOrUser(); + // read a command line - int count; - const char* line = el_gets(fEditLine, &count); + const char* line = fContext.PromptUser(kDebuggerPrompt); if (line == NULL) break; @@ -269,8 +238,7 @@ CommandLineUserInterface::_InputLoop() continue; // add line to history - HistEvent historyEvent; - history(fHistory, &historyEvent, H_ENTER, line); + fContext.AddLineToInputHistory(line); // execute command _ExecuteCommand(args.ArgumentCount(), args.Arguments()); diff --git a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h index fb5da34da9..736429b0b4 100644 --- a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h +++ b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h @@ -7,10 +7,6 @@ #define COMMAND_LINE_USER_INTERFACE_H -#include - // Needed in histedit.h. -#include - #include #include @@ -73,11 +69,9 @@ private: private: CliContext fContext; CommandList fCommands; - EditLine* fEditLine; - History* fHistory; sem_id fShowSemaphore; bool fShown; - bool fTerminating; + volatile bool fTerminating; };