2 * Copyright 2011-2016, Rene Gollent, rene@gollent.com.
3 * Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
8 #include "CommandLineUserInterface.h"
14 #include <ArgumentVector.h>
15 #include <AutoDeleter.h>
16 #include <AutoLocker.h>
17 #include <Referenceable.h>
19 #include "CliContext.h"
20 #include "CliContinueCommand.h"
21 #include "CliDebugReportCommand.h"
22 #include "CliDumpMemoryCommand.h"
23 #include "CliPrintVariableCommand.h"
24 #include "CliQuitCommand.h"
25 #include "CliStackFrameCommand.h"
26 #include "CliStackTraceCommand.h"
27 #include "CliStopCommand.h"
28 #include "CliThreadCommand.h"
29 #include "CliThreadsCommand.h"
30 #include "CliVariablesCommand.h"
31 #include "CliWriteCoreFileCommand.h"
34 static const char* kDebuggerPrompt
= "debugger> ";
37 // #pragma mark - CommandEntry
40 struct CommandLineUserInterface::CommandEntry
{
41 CommandEntry(const BString
& name
, CliCommand
* command
)
48 const BString
& Name() const
53 CliCommand
* Command() const
55 return fCommand
.Get();
60 BReference
<CliCommand
> fCommand
;
64 // #pragma mark - HelpCommand
67 struct CommandLineUserInterface::HelpCommand
: CliCommand
{
68 HelpCommand(CommandLineUserInterface
* userInterface
)
70 CliCommand("print help for a command or a list of all commands",
72 "Prints help for command <command>, if given, or a list of all "
75 fUserInterface(userInterface
)
79 virtual void Execute(int argc
, const char* const* argv
, CliContext
& context
)
86 fUserInterface
->_PrintHelp(argc
== 2 ? argv
[1] : NULL
);
90 CommandLineUserInterface
* fUserInterface
;
94 // #pragma mark - CommandLineUserInterface
97 CommandLineUserInterface::CommandLineUserInterface()
107 CommandLineUserInterface::~CommandLineUserInterface()
109 if (fShowSemaphore
>= 0)
110 delete_sem(fShowSemaphore
);
115 CommandLineUserInterface::ID() const
117 return "BasicCommandLineUserInterface";
122 CommandLineUserInterface::Init(Team
* team
, UserInterfaceListener
* listener
)
124 status_t error
= fContext
.Init(team
, listener
);
128 error
= _RegisterCommands();
132 fShowSemaphore
= create_sem(0, "show CLI");
133 if (fShowSemaphore
< 0)
134 return fShowSemaphore
;
141 CommandLineUserInterface::Show()
144 release_sem(fShowSemaphore
);
149 CommandLineUserInterface::Terminate()
154 fContext
.Terminating();
156 // Wait for input loop to finish.
157 while (acquire_sem(fShowSemaphore
) == B_INTERRUPTED
) {
160 // The main thread will still be blocked in Run(). Unblock it.
161 delete_sem(fShowSemaphore
);
170 CommandLineUserInterface::Clone() const
172 return new(std::nothrow
) CommandLineUserInterface
;
177 CommandLineUserInterface::IsInteractive() const
184 CommandLineUserInterface::LoadSettings(const TeamUiSettings
* settings
)
191 CommandLineUserInterface::SaveSettings(TeamUiSettings
*& settings
) const
198 CommandLineUserInterface::NotifyUser(const char* title
, const char* message
,
199 user_notification_type type
)
205 CommandLineUserInterface::NotifyBackgroundWorkStatus(const char* message
)
211 CommandLineUserInterface::SynchronouslyAskUser(const char* title
,
212 const char* message
, const char* choice1
, const char* choice2
,
220 CommandLineUserInterface::SynchronouslyAskUserForFile(entry_ref
* _ref
)
222 return B_UNSUPPORTED
;
227 CommandLineUserInterface::Run()
229 // Wait for the Show() semaphore to be released.
232 error
= acquire_sem(fShowSemaphore
);
233 } while (error
== B_INTERRUPTED
);
239 // Release the Show() semaphore to signal Terminate().
240 release_sem(fShowSemaphore
);
245 CommandLineUserInterface::_InputLoopEntry(void* data
)
247 return ((CommandLineUserInterface
*)data
)->_InputLoop();
252 CommandLineUserInterface::_InputLoop()
254 thread_id currentThread
= -1;
256 while (!fTerminating
) {
257 // Wait for a thread or Ctrl-C.
258 fContext
.WaitForThreadOrUser();
259 if (fContext
.IsTerminating())
262 // Print the active thread, if it changed.
263 if (fContext
.CurrentThreadID() != currentThread
) {
264 fContext
.PrintCurrentThread();
265 currentThread
= fContext
.CurrentThreadID();
268 // read a command line
269 const char* line
= fContext
.PromptUser(kDebuggerPrompt
);
273 // parse the command line
275 const char* parseErrorLocation
;
276 switch (args
.Parse(line
, &parseErrorLocation
)) {
277 case ArgumentVector::NO_ERROR
:
279 case ArgumentVector::NO_MEMORY
:
280 printf("Insufficient memory parsing the command line.\n");
282 case ArgumentVector::UNTERMINATED_QUOTED_STRING
:
283 printf("Parse error: Unterminated quoted string starting at "
284 "character %zu.\n", parseErrorLocation
- line
+ 1);
286 case ArgumentVector::TRAILING_BACKSPACE
:
287 printf("Parse error: trailing backspace.\n");
291 if (args
.ArgumentCount() == 0)
294 // add line to history
295 fContext
.AddLineToInputHistory(line
);
298 _ExecuteCommand(args
.ArgumentCount(), args
.Arguments());
306 CommandLineUserInterface::_RegisterCommands()
308 if (_RegisterCommand("bt sc", new(std::nothrow
) CliStackTraceCommand
)
309 && _RegisterCommand("continue", new(std::nothrow
) CliContinueCommand
)
310 && _RegisterCommand("db ds dw dl string", new(std::nothrow
)
311 CliDumpMemoryCommand
)
312 && _RegisterCommand("frame", new(std::nothrow
) CliStackFrameCommand
)
313 && _RegisterCommand("help", new(std::nothrow
) HelpCommand(this))
314 && _RegisterCommand("print", new(std::nothrow
) CliPrintVariableCommand
)
315 && _RegisterCommand("quit", new(std::nothrow
) CliQuitCommand
)
316 && _RegisterCommand("save-report",
317 new(std::nothrow
) CliDebugReportCommand
)
318 && _RegisterCommand("stop", new(std::nothrow
) CliStopCommand
)
319 && _RegisterCommand("thread", new(std::nothrow
) CliThreadCommand
)
320 && _RegisterCommand("threads", new(std::nothrow
) CliThreadsCommand
)
321 && _RegisterCommand("variables",
322 new(std::nothrow
) CliVariablesCommand
)
323 && _RegisterCommand("write-core",
324 new(std::nothrow
) CliWriteCoreFileCommand
)) {
325 fCommands
.SortItems(&_CompareCommandEntries
);
334 CommandLineUserInterface::_RegisterCommand(const BString
& name
,
337 BReference
<CliCommand
> commandReference(command
, true);
338 if (name
.IsEmpty() || command
== NULL
)
342 int32 startIndex
= 0;
345 spaceIndex
= name
.FindFirst(' ', startIndex
);
346 if (spaceIndex
== B_ERROR
)
347 spaceIndex
= name
.Length();
348 name
.CopyInto(nextName
, startIndex
, spaceIndex
- startIndex
);
350 CommandEntry
* entry
= new(std::nothrow
) CommandEntry(nextName
,
352 if (entry
== NULL
|| !fCommands
.AddItem(entry
)) {
356 startIndex
= spaceIndex
+ 1;
357 } while (startIndex
< name
.Length());
364 CommandLineUserInterface::_ExecuteCommand(int argc
, const char* const* argv
)
366 CommandEntry
* commandEntry
= _FindCommand(argv
[0]);
367 if (commandEntry
!= NULL
)
368 commandEntry
->Command()->Execute(argc
, argv
, fContext
);
372 CommandLineUserInterface::CommandEntry
*
373 CommandLineUserInterface::_FindCommand(const char* commandName
)
375 size_t commandNameLength
= strlen(commandName
);
377 // try to find an exact match first
378 CommandEntry
* commandEntry
= NULL
;
379 for (int32 i
= 0; CommandEntry
* entry
= fCommands
.ItemAt(i
); i
++) {
380 if (entry
->Name() == commandName
) {
381 commandEntry
= entry
;
386 // If nothing found yet, try partial matches, but only, if they are
388 if (commandEntry
== NULL
) {
389 for (int32 i
= 0; CommandEntry
* entry
= fCommands
.ItemAt(i
); i
++) {
390 if (entry
->Name().Compare(commandName
, commandNameLength
) == 0) {
391 if (commandEntry
!= NULL
) {
392 printf("Error: Ambiguous command \"%s\".\n", commandName
);
396 commandEntry
= entry
;
401 if (commandEntry
== NULL
) {
402 printf("Error: Unknown command \"%s\".\n", commandName
);
411 CommandLineUserInterface::_PrintHelp(const char* commandName
)
413 // If a command name is given, print the usage for that one.
414 if (commandName
!= NULL
) {
415 CommandEntry
* commandEntry
= _FindCommand(commandName
);
416 if (commandEntry
!= NULL
)
417 commandEntry
->Command()->PrintUsage(commandEntry
->Name().String());
421 // No command name given -- print a list of all commands.
423 // determine longest command name
424 int32 longestCommandName
= 0;
425 for (int32 i
= 0; CommandEntry
* entry
= fCommands
.ItemAt(i
); i
++) {
427 = std::max(longestCommandName
, entry
->Name().Length());
430 // print the command list
431 for (int32 i
= 0; CommandEntry
* entry
= fCommands
.ItemAt(i
); i
++) {
432 printf("%*s - %s\n", (int)longestCommandName
, entry
->Name().String(),
433 entry
->Command()->Summary());
440 CommandLineUserInterface::_CompareCommandEntries(const CommandEntry
* command1
,
441 const CommandEntry
* command2
)
443 return ::Compare(command1
->Name(), command2
->Name());