vfs: check userland buffers before reading them.
[haiku.git] / src / apps / debugger / user_interface / cli / CommandLineUserInterface.cpp
blob28eefbf4155e12b9a2d8801b4dad0e90875aa976
1 /*
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.
5 */
8 #include "CommandLineUserInterface.h"
10 #include <stdio.h>
12 #include <algorithm>
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)
43 fName(name),
44 fCommand(command)
48 const BString& Name() const
50 return fName;
53 CliCommand* Command() const
55 return fCommand.Get();
58 private:
59 BString fName;
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",
71 "%s [ <command> ]\n"
72 "Prints help for command <command>, if given, or a list of all "
73 "commands\n"
74 "otherwise."),
75 fUserInterface(userInterface)
79 virtual void Execute(int argc, const char* const* argv, CliContext& context)
81 if (argc > 2) {
82 PrintUsage(argv[0]);
83 return;
86 fUserInterface->_PrintHelp(argc == 2 ? argv[1] : NULL);
89 private:
90 CommandLineUserInterface* fUserInterface;
94 // #pragma mark - CommandLineUserInterface
97 CommandLineUserInterface::CommandLineUserInterface()
99 fCommands(20, true),
100 fShowSemaphore(-1),
101 fShown(false),
102 fTerminating(false)
107 CommandLineUserInterface::~CommandLineUserInterface()
109 if (fShowSemaphore >= 0)
110 delete_sem(fShowSemaphore);
114 const char*
115 CommandLineUserInterface::ID() const
117 return "BasicCommandLineUserInterface";
121 status_t
122 CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener)
124 status_t error = fContext.Init(team, listener);
125 if (error != B_OK)
126 return error;
128 error = _RegisterCommands();
129 if (error != B_OK)
130 return error;
132 fShowSemaphore = create_sem(0, "show CLI");
133 if (fShowSemaphore < 0)
134 return fShowSemaphore;
136 return B_OK;
140 void
141 CommandLineUserInterface::Show()
143 fShown = true;
144 release_sem(fShowSemaphore);
148 void
149 CommandLineUserInterface::Terminate()
151 fTerminating = true;
153 if (fShown) {
154 fContext.Terminating();
156 // Wait for input loop to finish.
157 while (acquire_sem(fShowSemaphore) == B_INTERRUPTED) {
159 } else {
160 // The main thread will still be blocked in Run(). Unblock it.
161 delete_sem(fShowSemaphore);
162 fShowSemaphore = -1;
165 fContext.Cleanup();
169 UserInterface*
170 CommandLineUserInterface::Clone() const
172 return new(std::nothrow) CommandLineUserInterface;
176 bool
177 CommandLineUserInterface::IsInteractive() const
179 return true;
183 status_t
184 CommandLineUserInterface::LoadSettings(const TeamUiSettings* settings)
186 return B_OK;
190 status_t
191 CommandLineUserInterface::SaveSettings(TeamUiSettings*& settings) const
193 return B_OK;
197 void
198 CommandLineUserInterface::NotifyUser(const char* title, const char* message,
199 user_notification_type type)
204 void
205 CommandLineUserInterface::NotifyBackgroundWorkStatus(const char* message)
210 int32
211 CommandLineUserInterface::SynchronouslyAskUser(const char* title,
212 const char* message, const char* choice1, const char* choice2,
213 const char* choice3)
215 return -1;
219 status_t
220 CommandLineUserInterface::SynchronouslyAskUserForFile(entry_ref* _ref)
222 return B_UNSUPPORTED;
226 void
227 CommandLineUserInterface::Run()
229 // Wait for the Show() semaphore to be released.
230 status_t error;
231 do {
232 error = acquire_sem(fShowSemaphore);
233 } while (error == B_INTERRUPTED);
235 if (error != B_OK)
236 return;
238 _InputLoop();
239 // Release the Show() semaphore to signal Terminate().
240 release_sem(fShowSemaphore);
244 /*static*/ status_t
245 CommandLineUserInterface::_InputLoopEntry(void* data)
247 return ((CommandLineUserInterface*)data)->_InputLoop();
251 status_t
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())
260 break;
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);
270 if (line == NULL)
271 break;
273 // parse the command line
274 ArgumentVector args;
275 const char* parseErrorLocation;
276 switch (args.Parse(line, &parseErrorLocation)) {
277 case ArgumentVector::NO_ERROR:
278 break;
279 case ArgumentVector::NO_MEMORY:
280 printf("Insufficient memory parsing the command line.\n");
281 continue;
282 case ArgumentVector::UNTERMINATED_QUOTED_STRING:
283 printf("Parse error: Unterminated quoted string starting at "
284 "character %zu.\n", parseErrorLocation - line + 1);
285 continue;
286 case ArgumentVector::TRAILING_BACKSPACE:
287 printf("Parse error: trailing backspace.\n");
288 continue;
291 if (args.ArgumentCount() == 0)
292 continue;
294 // add line to history
295 fContext.AddLineToInputHistory(line);
297 // execute command
298 _ExecuteCommand(args.ArgumentCount(), args.Arguments());
301 return B_OK;
305 status_t
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);
326 return B_OK;
329 return B_NO_MEMORY;
333 bool
334 CommandLineUserInterface::_RegisterCommand(const BString& name,
335 CliCommand* command)
337 BReference<CliCommand> commandReference(command, true);
338 if (name.IsEmpty() || command == NULL)
339 return false;
341 BString nextName;
342 int32 startIndex = 0;
343 int32 spaceIndex;
344 do {
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,
351 command);
352 if (entry == NULL || !fCommands.AddItem(entry)) {
353 delete entry;
354 return false;
356 startIndex = spaceIndex + 1;
357 } while (startIndex < name.Length());
359 return true;
363 void
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;
382 break;
386 // If nothing found yet, try partial matches, but only, if they are
387 // unambiguous.
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);
393 return NULL;
396 commandEntry = entry;
401 if (commandEntry == NULL) {
402 printf("Error: Unknown command \"%s\".\n", commandName);
403 return NULL;
406 return commandEntry;
410 void
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());
418 return;
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++) {
426 longestCommandName
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());
438 /*static */
440 CommandLineUserInterface::_CompareCommandEntries(const CommandEntry* command1,
441 const CommandEntry* command2)
443 return ::Compare(command1->Name(), command2->Name());