brintos

brintos / llvm-project-archived public Read only

0
0
Text · 137.1 KiB · ffcc9ce Raw
3806 lines · cpp
1//===-- CommandInterpreter.cpp --------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include <chrono>10#include <cstdlib>11#include <limits>12#include <memory>13#include <optional>14#include <string>15#include <vector>16 17#include "Commands/CommandObjectApropos.h"18#include "Commands/CommandObjectBreakpoint.h"19#include "Commands/CommandObjectCommands.h"20#include "Commands/CommandObjectDWIMPrint.h"21#include "Commands/CommandObjectDiagnostics.h"22#include "Commands/CommandObjectDisassemble.h"23#include "Commands/CommandObjectExpression.h"24#include "Commands/CommandObjectFrame.h"25#include "Commands/CommandObjectGUI.h"26#include "Commands/CommandObjectHelp.h"27#include "Commands/CommandObjectLanguage.h"28#include "Commands/CommandObjectLog.h"29#include "Commands/CommandObjectMemory.h"30#include "Commands/CommandObjectPlatform.h"31#include "Commands/CommandObjectPlugin.h"32#include "Commands/CommandObjectProcess.h"33#include "Commands/CommandObjectProtocolServer.h"34#include "Commands/CommandObjectQuit.h"35#include "Commands/CommandObjectRegexCommand.h"36#include "Commands/CommandObjectRegister.h"37#include "Commands/CommandObjectScripting.h"38#include "Commands/CommandObjectSession.h"39#include "Commands/CommandObjectSettings.h"40#include "Commands/CommandObjectSource.h"41#include "Commands/CommandObjectStats.h"42#include "Commands/CommandObjectTarget.h"43#include "Commands/CommandObjectThread.h"44#include "Commands/CommandObjectTrace.h"45#include "Commands/CommandObjectType.h"46#include "Commands/CommandObjectVersion.h"47#include "Commands/CommandObjectWatchpoint.h"48 49#include "lldb/Core/Debugger.h"50#include "lldb/Core/Module.h"51#include "lldb/Core/PluginManager.h"52#include "lldb/Core/Telemetry.h"53#include "lldb/Host/StreamFile.h"54#include "lldb/Utility/ErrorMessages.h"55#include "lldb/Utility/FileSpec.h"56#include "lldb/Utility/LLDBLog.h"57#include "lldb/Utility/Log.h"58#include "lldb/Utility/State.h"59#include "lldb/Utility/Stream.h"60#include "lldb/Utility/StructuredData.h"61#include "lldb/Utility/Timer.h"62 63#include "lldb/Host/Config.h"64#include "lldb/lldb-forward.h"65#if LLDB_ENABLE_LIBEDIT66#include "lldb/Host/Editline.h"67#endif68#include "lldb/Host/File.h"69#include "lldb/Host/FileCache.h"70#include "lldb/Host/Host.h"71#include "lldb/Host/HostInfo.h"72 73#include "lldb/Interpreter/CommandCompletions.h"74#include "lldb/Interpreter/CommandInterpreter.h"75#include "lldb/Interpreter/CommandReturnObject.h"76#include "lldb/Interpreter/OptionValueProperties.h"77#include "lldb/Interpreter/Options.h"78#include "lldb/Interpreter/Property.h"79#include "lldb/Utility/Args.h"80 81#include "lldb/Target/Language.h"82#include "lldb/Target/Process.h"83#include "lldb/Target/StopInfo.h"84#include "lldb/Target/TargetList.h"85#include "lldb/Target/Thread.h"86#include "lldb/Target/UnixSignals.h"87 88#include "llvm/ADT/STLExtras.h"89#include "llvm/ADT/ScopeExit.h"90#include "llvm/ADT/SmallString.h"91#include "llvm/Support/FormatAdapters.h"92#include "llvm/Support/Path.h"93#include "llvm/Support/PrettyStackTrace.h"94#include "llvm/Support/ScopedPrinter.h"95#include "llvm/Telemetry/Telemetry.h"96 97#if defined(__APPLE__)98#include <TargetConditionals.h>99#endif100 101using namespace lldb;102using namespace lldb_private;103 104static const char *k_white_space = " \t\v";105 106static constexpr const char *InitFileWarning =107    "There is a .lldbinit file in the current directory which is not being "108    "read.\n"109    "To silence this warning without sourcing in the local .lldbinit,\n"110    "add the following to the lldbinit file in your home directory:\n"111    "    settings set target.load-cwd-lldbinit false\n"112    "To allow lldb to source .lldbinit files in the current working "113    "directory,\n"114    "set the value of this variable to true.  Only do so if you understand "115    "and\n"116    "accept the security risk.";117 118const char *CommandInterpreter::g_no_argument = "<no-argument>";119const char *CommandInterpreter::g_need_argument = "<need-argument>";120const char *CommandInterpreter::g_argument = "<argument>";121 122#define LLDB_PROPERTIES_interpreter123#include "InterpreterProperties.inc"124 125enum {126#define LLDB_PROPERTIES_interpreter127#include "InterpreterPropertiesEnum.inc"128};129 130llvm::StringRef CommandInterpreter::GetStaticBroadcasterClass() {131  static constexpr llvm::StringLiteral class_name("lldb.commandInterpreter");132  return class_name;133}134 135CommandInterpreter::CommandInterpreter(Debugger &debugger,136                                       bool synchronous_execution)137    : Broadcaster(debugger.GetBroadcasterManager(),138                  CommandInterpreter::GetStaticBroadcasterClass().str()),139      Properties(std::make_shared<OptionValueProperties>("interpreter")),140      IOHandlerDelegate(IOHandlerDelegate::Completion::LLDBCommand),141      m_debugger(debugger), m_synchronous_execution(true),142      m_skip_lldbinit_files(false), m_skip_app_init_files(false),143      m_comment_char('#'), m_batch_command_mode(false),144      m_truncation_warning(eNoOmission), m_max_depth_warning(eNoOmission),145      m_command_source_depth(0) {146  SetEventName(eBroadcastBitThreadShouldExit, "thread-should-exit");147  SetEventName(eBroadcastBitResetPrompt, "reset-prompt");148  SetEventName(eBroadcastBitQuitCommandReceived, "quit");149  SetSynchronous(synchronous_execution);150  CheckInWithManager();151  m_collection_sp->Initialize(g_interpreter_properties);152}153 154bool CommandInterpreter::GetExpandRegexAliases() const {155  const uint32_t idx = ePropertyExpandRegexAliases;156  return GetPropertyAtIndexAs<bool>(157      idx, g_interpreter_properties[idx].default_uint_value != 0);158}159 160bool CommandInterpreter::GetPromptOnQuit() const {161  const uint32_t idx = ePropertyPromptOnQuit;162  return GetPropertyAtIndexAs<bool>(163      idx, g_interpreter_properties[idx].default_uint_value != 0);164}165 166void CommandInterpreter::SetPromptOnQuit(bool enable) {167  const uint32_t idx = ePropertyPromptOnQuit;168  SetPropertyAtIndex(idx, enable);169}170 171bool CommandInterpreter::GetSaveTranscript() const {172  const uint32_t idx = ePropertySaveTranscript;173  return GetPropertyAtIndexAs<bool>(174      idx, g_interpreter_properties[idx].default_uint_value != 0);175}176 177void CommandInterpreter::SetSaveTranscript(bool enable) {178  const uint32_t idx = ePropertySaveTranscript;179  SetPropertyAtIndex(idx, enable);180}181 182bool CommandInterpreter::GetSaveSessionOnQuit() const {183  const uint32_t idx = ePropertySaveSessionOnQuit;184  return GetPropertyAtIndexAs<bool>(185      idx, g_interpreter_properties[idx].default_uint_value != 0);186}187 188void CommandInterpreter::SetSaveSessionOnQuit(bool enable) {189  const uint32_t idx = ePropertySaveSessionOnQuit;190  SetPropertyAtIndex(idx, enable);191}192 193bool CommandInterpreter::GetOpenTranscriptInEditor() const {194  const uint32_t idx = ePropertyOpenTranscriptInEditor;195  return GetPropertyAtIndexAs<bool>(196      idx, g_interpreter_properties[idx].default_uint_value != 0);197}198 199void CommandInterpreter::SetOpenTranscriptInEditor(bool enable) {200  const uint32_t idx = ePropertyOpenTranscriptInEditor;201  SetPropertyAtIndex(idx, enable);202}203 204FileSpec CommandInterpreter::GetSaveSessionDirectory() const {205  const uint32_t idx = ePropertySaveSessionDirectory;206  return GetPropertyAtIndexAs<FileSpec>(idx, {});207}208 209void CommandInterpreter::SetSaveSessionDirectory(llvm::StringRef path) {210  const uint32_t idx = ePropertySaveSessionDirectory;211  SetPropertyAtIndex(idx, path);212}213 214bool CommandInterpreter::GetEchoCommands() const {215  const uint32_t idx = ePropertyEchoCommands;216  return GetPropertyAtIndexAs<bool>(217      idx, g_interpreter_properties[idx].default_uint_value != 0);218}219 220void CommandInterpreter::SetEchoCommands(bool enable) {221  const uint32_t idx = ePropertyEchoCommands;222  SetPropertyAtIndex(idx, enable);223}224 225bool CommandInterpreter::GetEchoCommentCommands() const {226  const uint32_t idx = ePropertyEchoCommentCommands;227  return GetPropertyAtIndexAs<bool>(228      idx, g_interpreter_properties[idx].default_uint_value != 0);229}230 231void CommandInterpreter::SetEchoCommentCommands(bool enable) {232  const uint32_t idx = ePropertyEchoCommentCommands;233  SetPropertyAtIndex(idx, enable);234}235 236void CommandInterpreter::AllowExitCodeOnQuit(bool allow) {237  m_allow_exit_code = allow;238  if (!allow)239    m_quit_exit_code.reset();240}241 242bool CommandInterpreter::SetQuitExitCode(int exit_code) {243  if (!m_allow_exit_code)244    return false;245  m_quit_exit_code = exit_code;246  return true;247}248 249int CommandInterpreter::GetQuitExitCode(bool &exited) const {250  exited = m_quit_exit_code.has_value();251  if (exited)252    return *m_quit_exit_code;253  return 0;254}255 256void CommandInterpreter::ResolveCommand(const char *command_line,257                                        CommandReturnObject &result) {258  std::string command = command_line;259  if (ResolveCommandImpl(command, result) != nullptr) {260    result.AppendMessageWithFormat("%s", command.c_str());261    result.SetStatus(eReturnStatusSuccessFinishResult);262  }263}264 265bool CommandInterpreter::GetStopCmdSourceOnError() const {266  const uint32_t idx = ePropertyStopCmdSourceOnError;267  return GetPropertyAtIndexAs<bool>(268      idx, g_interpreter_properties[idx].default_uint_value != 0);269}270 271bool CommandInterpreter::GetSpaceReplPrompts() const {272  const uint32_t idx = ePropertySpaceReplPrompts;273  return GetPropertyAtIndexAs<bool>(274      idx, g_interpreter_properties[idx].default_uint_value != 0);275}276 277bool CommandInterpreter::GetRepeatPreviousCommand() const {278  const uint32_t idx = ePropertyRepeatPreviousCommand;279  return GetPropertyAtIndexAs<bool>(280      idx, g_interpreter_properties[idx].default_uint_value != 0);281}282 283bool CommandInterpreter::GetRequireCommandOverwrite() const {284  const uint32_t idx = ePropertyRequireCommandOverwrite;285  return GetPropertyAtIndexAs<bool>(286      idx, g_interpreter_properties[idx].default_uint_value != 0);287}288 289void CommandInterpreter::Initialize() {290  LLDB_SCOPED_TIMER();291 292  LoadCommandDictionary();293 294  // An alias arguments vector to reuse - reset it before use...295  OptionArgVectorSP alias_arguments_vector_sp(new OptionArgVector);296 297  // Set up some initial aliases.298  CommandObjectSP cmd_obj_sp = GetCommandSPExact("quit");299  if (cmd_obj_sp) {300    AddAlias("q", cmd_obj_sp);301    AddAlias("exit", cmd_obj_sp);302  }303 304  cmd_obj_sp = GetCommandSPExact("_regexp-attach");305  if (cmd_obj_sp)306    AddAlias("attach", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());307 308  cmd_obj_sp = GetCommandSPExact("process detach");309  if (cmd_obj_sp) {310    AddAlias("detach", cmd_obj_sp);311  }312 313  cmd_obj_sp = GetCommandSPExact("process continue");314  if (cmd_obj_sp) {315    AddAlias("c", cmd_obj_sp);316    AddAlias("continue", cmd_obj_sp);317  }318 319  cmd_obj_sp = GetCommandSPExact("_regexp-break");320  if (cmd_obj_sp)321    AddAlias("b", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());322 323  cmd_obj_sp = GetCommandSPExact("_regexp-tbreak");324  if (cmd_obj_sp)325    AddAlias("tbreak", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());326 327  cmd_obj_sp = GetCommandSPExact("thread step-inst");328  if (cmd_obj_sp) {329    AddAlias("stepi", cmd_obj_sp);330    AddAlias("si", cmd_obj_sp);331  }332 333  cmd_obj_sp = GetCommandSPExact("thread step-inst-over");334  if (cmd_obj_sp) {335    AddAlias("nexti", cmd_obj_sp);336    AddAlias("ni", cmd_obj_sp);337  }338 339  cmd_obj_sp = GetCommandSPExact("_regexp-step");340  if (cmd_obj_sp) {341    AddAlias("s", cmd_obj_sp);342    AddAlias("step", cmd_obj_sp);343    CommandAlias *sif_alias = AddAlias(344        "sif", cmd_obj_sp, "--end-linenumber block --step-in-target %1");345    if (sif_alias) {346      sif_alias->SetHelp("Step through the current block, stopping if you step "347                         "directly into a function whose name matches the "348                         "TargetFunctionName.");349      sif_alias->SetSyntax("sif <TargetFunctionName>");350    }351  }352 353  cmd_obj_sp = GetCommandSPExact("thread step-over");354  if (cmd_obj_sp) {355    AddAlias("n", cmd_obj_sp);356    AddAlias("next", cmd_obj_sp);357  }358 359  cmd_obj_sp = GetCommandSPExact("thread step-out");360  if (cmd_obj_sp) {361    AddAlias("finish", cmd_obj_sp);362  }363 364  cmd_obj_sp = GetCommandSPExact("frame select");365  if (cmd_obj_sp) {366    AddAlias("f", cmd_obj_sp);367  }368 369  cmd_obj_sp = GetCommandSPExact("thread select");370  if (cmd_obj_sp) {371    AddAlias("t", cmd_obj_sp);372  }373 374  cmd_obj_sp = GetCommandSPExact("_regexp-jump");375  if (cmd_obj_sp) {376    AddAlias("j", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());377    AddAlias("jump", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());378  }379 380  cmd_obj_sp = GetCommandSPExact("_regexp-list");381  if (cmd_obj_sp) {382    AddAlias("l", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());383    AddAlias("list", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());384  }385 386  cmd_obj_sp = GetCommandSPExact("_regexp-env");387  if (cmd_obj_sp)388    AddAlias("env", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());389 390  cmd_obj_sp = GetCommandSPExact("memory read");391  if (cmd_obj_sp)392    AddAlias("x", cmd_obj_sp);393 394  cmd_obj_sp = GetCommandSPExact("_regexp-up");395  if (cmd_obj_sp)396    AddAlias("up", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());397 398  cmd_obj_sp = GetCommandSPExact("_regexp-down");399  if (cmd_obj_sp)400    AddAlias("down", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());401 402  cmd_obj_sp = GetCommandSPExact("_regexp-display");403  if (cmd_obj_sp)404    AddAlias("display", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());405 406  cmd_obj_sp = GetCommandSPExact("disassemble");407  if (cmd_obj_sp)408    AddAlias("dis", cmd_obj_sp);409 410  cmd_obj_sp = GetCommandSPExact("disassemble");411  if (cmd_obj_sp)412    AddAlias("di", cmd_obj_sp);413 414  cmd_obj_sp = GetCommandSPExact("_regexp-undisplay");415  if (cmd_obj_sp)416    AddAlias("undisplay", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());417 418  cmd_obj_sp = GetCommandSPExact("_regexp-bt");419  if (cmd_obj_sp)420    AddAlias("bt", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());421 422  cmd_obj_sp = GetCommandSPExact("target create");423  if (cmd_obj_sp)424    AddAlias("file", cmd_obj_sp);425 426  cmd_obj_sp = GetCommandSPExact("target modules");427  if (cmd_obj_sp)428    AddAlias("image", cmd_obj_sp);429 430  alias_arguments_vector_sp = std::make_shared<OptionArgVector>();431 432  cmd_obj_sp = GetCommandSPExact("dwim-print");433  if (cmd_obj_sp) {434    AddAlias("p", cmd_obj_sp, "--")->SetHelpLong("");435    AddAlias("print", cmd_obj_sp, "--")->SetHelpLong("");436    if (auto *po = AddAlias("po", cmd_obj_sp, "-O --")) {437      po->SetHelp("Evaluate an expression on the current thread.  Displays any "438                  "returned value with formatting "439                  "controlled by the type's author.");440      po->SetHelpLong("");441    }442  }443 444  cmd_obj_sp = GetCommandSPExact("expression");445  if (cmd_obj_sp) {446    // Ensure `e` runs `expression`.447    AddAlias("e", cmd_obj_sp);448    AddAlias("call", cmd_obj_sp, "--")->SetHelpLong("");449    CommandAlias *parray_alias =450        AddAlias("parray", cmd_obj_sp, "--element-count %1 --");451    if (parray_alias) {452      parray_alias->SetHelp(453          "parray <COUNT> <EXPRESSION> -- lldb will evaluate EXPRESSION "454          "to get a typed-pointer-to-an-array in memory, and will display "455          "COUNT elements of that type from the array.");456      parray_alias->SetHelpLong("");457    }458    CommandAlias *poarray_alias = AddAlias(459        "poarray", cmd_obj_sp, "--object-description --element-count %1 --");460    if (poarray_alias) {461      poarray_alias->SetHelp(462          "poarray <COUNT> <EXPRESSION> -- lldb will "463          "evaluate EXPRESSION to get the address of an array of COUNT "464          "objects in memory, and will call po on them.");465      poarray_alias->SetHelpLong("");466    }467  }468 469  cmd_obj_sp = GetCommandSPExact("platform shell");470  if (cmd_obj_sp) {471    CommandAlias *shell_alias = AddAlias("shell", cmd_obj_sp, " --host --");472    if (shell_alias) {473      shell_alias->SetHelp("Run a shell command on the host.");474      shell_alias->SetHelpLong("");475      shell_alias->SetSyntax("shell <shell-command>");476    }477  }478 479  cmd_obj_sp = GetCommandSPExact("process kill");480  if (cmd_obj_sp) {481    AddAlias("kill", cmd_obj_sp);482  }483 484  cmd_obj_sp = GetCommandSPExact("process launch");485  if (cmd_obj_sp) {486    alias_arguments_vector_sp = std::make_shared<OptionArgVector>();487#if defined(__APPLE__)488#if TARGET_OS_IPHONE489    AddAlias("r", cmd_obj_sp, "--");490    AddAlias("run", cmd_obj_sp, "--");491#else492    AddAlias("r", cmd_obj_sp, "--shell-expand-args true --");493    AddAlias("run", cmd_obj_sp, "--shell-expand-args true --");494#endif495#else496    StreamString defaultshell;497    defaultshell.Printf("--shell=%s --",498                        HostInfo::GetDefaultShell().GetPath().c_str());499    AddAlias("r", cmd_obj_sp, defaultshell.GetString());500    AddAlias("run", cmd_obj_sp, defaultshell.GetString());501#endif502  }503 504  cmd_obj_sp = GetCommandSPExact("target symbols add");505  if (cmd_obj_sp) {506    AddAlias("add-dsym", cmd_obj_sp);507  }508 509  cmd_obj_sp = GetCommandSPExact("breakpoint set");510  if (cmd_obj_sp) {511    AddAlias("rbreak", cmd_obj_sp, "--func-regex %1");512  }513 514  cmd_obj_sp = GetCommandSPExact("frame variable");515  if (cmd_obj_sp) {516    AddAlias("v", cmd_obj_sp);517    AddAlias("var", cmd_obj_sp);518    AddAlias("vo", cmd_obj_sp, "--object-description");519  }520 521  cmd_obj_sp = GetCommandSPExact("register");522  if (cmd_obj_sp) {523    AddAlias("re", cmd_obj_sp);524  }525 526  cmd_obj_sp = GetCommandSPExact("scripting run");527  if (cmd_obj_sp) {528    AddAlias("script", cmd_obj_sp);529  }530 531  cmd_obj_sp = GetCommandSPExact("session history");532  if (cmd_obj_sp) {533    AddAlias("history", cmd_obj_sp);534  }535 536  cmd_obj_sp = GetCommandSPExact("help");537  if (cmd_obj_sp) {538    AddAlias("h", cmd_obj_sp);539  }540}541 542void CommandInterpreter::Clear() { m_command_io_handler_sp.reset(); }543 544const char *CommandInterpreter::ProcessEmbeddedScriptCommands(const char *arg) {545  // This function has not yet been implemented.546 547  // Look for any embedded script command548  // If found,549  //    get interpreter object from the command dictionary,550  //    call execute_one_command on it,551  //    get the results as a string,552  //    substitute that string for current stuff.553 554  return arg;555}556 557#define REGISTER_COMMAND_OBJECT(NAME, CLASS)                                   \558  m_command_dict[NAME] = std::make_shared<CLASS>(*this);559 560void CommandInterpreter::LoadCommandDictionary() {561  LLDB_SCOPED_TIMER();562 563  REGISTER_COMMAND_OBJECT("apropos", CommandObjectApropos);564  REGISTER_COMMAND_OBJECT("breakpoint", CommandObjectMultiwordBreakpoint);565  REGISTER_COMMAND_OBJECT("command", CommandObjectMultiwordCommands);566  REGISTER_COMMAND_OBJECT("diagnostics", CommandObjectDiagnostics);567  REGISTER_COMMAND_OBJECT("disassemble", CommandObjectDisassemble);568  REGISTER_COMMAND_OBJECT("dwim-print", CommandObjectDWIMPrint);569  REGISTER_COMMAND_OBJECT("expression", CommandObjectExpression);570  REGISTER_COMMAND_OBJECT("frame", CommandObjectMultiwordFrame);571  REGISTER_COMMAND_OBJECT("gui", CommandObjectGUI);572  REGISTER_COMMAND_OBJECT("help", CommandObjectHelp);573  REGISTER_COMMAND_OBJECT("log", CommandObjectLog);574  REGISTER_COMMAND_OBJECT("memory", CommandObjectMemory);575  REGISTER_COMMAND_OBJECT("platform", CommandObjectPlatform);576  REGISTER_COMMAND_OBJECT("plugin", CommandObjectPlugin);577  REGISTER_COMMAND_OBJECT("process", CommandObjectMultiwordProcess);578  REGISTER_COMMAND_OBJECT("protocol-server", CommandObjectProtocolServer);579  REGISTER_COMMAND_OBJECT("quit", CommandObjectQuit);580  REGISTER_COMMAND_OBJECT("register", CommandObjectRegister);581  REGISTER_COMMAND_OBJECT("scripting", CommandObjectMultiwordScripting);582  REGISTER_COMMAND_OBJECT("settings", CommandObjectMultiwordSettings);583  REGISTER_COMMAND_OBJECT("session", CommandObjectSession);584  REGISTER_COMMAND_OBJECT("source", CommandObjectMultiwordSource);585  REGISTER_COMMAND_OBJECT("statistics", CommandObjectStats);586  REGISTER_COMMAND_OBJECT("target", CommandObjectMultiwordTarget);587  REGISTER_COMMAND_OBJECT("thread", CommandObjectMultiwordThread);588  REGISTER_COMMAND_OBJECT("trace", CommandObjectTrace);589  REGISTER_COMMAND_OBJECT("type", CommandObjectType);590  REGISTER_COMMAND_OBJECT("version", CommandObjectVersion);591  REGISTER_COMMAND_OBJECT("watchpoint", CommandObjectMultiwordWatchpoint);592  REGISTER_COMMAND_OBJECT("language", CommandObjectLanguage);593 594  // clang-format off595  const char *break_regexes[][2] = {596      {"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$",597       "breakpoint set --file '%1' --line %2 --column %3"},598      {"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$",599       "breakpoint set --file '%1' --line %2"},600      {"^/([^/]+)/$", "breakpoint set --source-pattern-regexp '%1'"},601      {"^([[:digit:]]+)[[:space:]]*$", "breakpoint set --line %1"},602      {"^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", "breakpoint set --address %1"},603      {"^[\"']?([-+]?\\[.*\\])[\"']?[[:space:]]*$",604       "breakpoint set --name '%1'"},605      {"^(-.*)$", "breakpoint set %1"},606      {"^(.*[^[:space:]])`(.*[^[:space:]])[[:space:]]*$",607       "breakpoint set --name '%2' --shlib '%1'"},608      {"^\\&(.*[^[:space:]])[[:space:]]*$",609       "breakpoint set --name '%1' --skip-prologue=0"},610      {"^[\"']?(.*[^[:space:]\"'])[\"']?[[:space:]]*$",611       "breakpoint set --name '%1'"}};612  // clang-format on613 614  size_t num_regexes = std::size(break_regexes);615 616  std::unique_ptr<CommandObjectRegexCommand> break_regex_cmd_up(617      new CommandObjectRegexCommand(618          *this, "_regexp-break",619          "Set a breakpoint using one of several shorthand formats, or list "620          "the existing breakpoints if no arguments are provided.",621          "\n"622          "_regexp-break <filename>:<linenum>:<colnum>\n"623          "              main.c:12:21          // Break at line 12 and column "624          "21 of main.c\n\n"625          "_regexp-break <filename>:<linenum>\n"626          "              main.c:12             // Break at line 12 of "627          "main.c\n\n"628          "_regexp-break <linenum>\n"629          "              12                    // Break at line 12 of current "630          "file\n\n"631          "_regexp-break 0x<address>\n"632          "              0x1234000             // Break at address "633          "0x1234000\n\n"634          "_regexp-break <name>\n"635          "              main                  // Break in 'main' after the "636          "prologue\n\n"637          "_regexp-break &<name>\n"638          "              &main                 // Break at first instruction "639          "in 'main'\n\n"640          "_regexp-break <module>`<name>\n"641          "              libc.so`malloc        // Break in 'malloc' from "642          "'libc.so'\n\n"643          "_regexp-break /<source-regex>/\n"644          "              /break here/          // Break on source lines in "645          "current file\n"646          "                                    // containing text 'break "647          "here'.\n"648          "_regexp-break\n"649          "                                    // List the existing "650          "breakpoints\n",651          lldb::eSymbolCompletion | lldb::eSourceFileCompletion, false));652 653  if (break_regex_cmd_up) {654    bool success = true;655    for (size_t i = 0; i < num_regexes; i++) {656      success = break_regex_cmd_up->AddRegexCommand(break_regexes[i][0],657                                                    break_regexes[i][1]);658      if (!success)659        break;660    }661    success =662        break_regex_cmd_up->AddRegexCommand("^$", "breakpoint list --full");663 664    if (success) {665      CommandObjectSP break_regex_cmd_sp(break_regex_cmd_up.release());666      m_command_dict[std::string(break_regex_cmd_sp->GetCommandName())] =667          break_regex_cmd_sp;668    }669  }670 671  std::unique_ptr<CommandObjectRegexCommand> tbreak_regex_cmd_up(672      new CommandObjectRegexCommand(673          *this, "_regexp-tbreak",674          "Set a one-shot breakpoint using one of several shorthand formats.",675          "\n"676          "_regexp-break <filename>:<linenum>:<colnum>\n"677          "              main.c:12:21          // Break at line 12 and column "678          "21 of main.c\n\n"679          "_regexp-break <filename>:<linenum>\n"680          "              main.c:12             // Break at line 12 of "681          "main.c\n\n"682          "_regexp-break <linenum>\n"683          "              12                    // Break at line 12 of current "684          "file\n\n"685          "_regexp-break 0x<address>\n"686          "              0x1234000             // Break at address "687          "0x1234000\n\n"688          "_regexp-break <name>\n"689          "              main                  // Break in 'main' after the "690          "prologue\n\n"691          "_regexp-break &<name>\n"692          "              &main                 // Break at first instruction "693          "in 'main'\n\n"694          "_regexp-break <module>`<name>\n"695          "              libc.so`malloc        // Break in 'malloc' from "696          "'libc.so'\n\n"697          "_regexp-break /<source-regex>/\n"698          "              /break here/          // Break on source lines in "699          "current file\n"700          "                                    // containing text 'break "701          "here'.\n",702          lldb::eSymbolCompletion | lldb::eSourceFileCompletion, false));703 704  if (tbreak_regex_cmd_up) {705    bool success = true;706    for (size_t i = 0; i < num_regexes; i++) {707      std::string command = break_regexes[i][1];708      command += " -o 1";709      success =710          tbreak_regex_cmd_up->AddRegexCommand(break_regexes[i][0], command);711      if (!success)712        break;713    }714    success =715        tbreak_regex_cmd_up->AddRegexCommand("^$", "breakpoint list --full");716 717    if (success) {718      CommandObjectSP tbreak_regex_cmd_sp(tbreak_regex_cmd_up.release());719      m_command_dict[std::string(tbreak_regex_cmd_sp->GetCommandName())] =720          tbreak_regex_cmd_sp;721    }722  }723 724  std::unique_ptr<CommandObjectRegexCommand> attach_regex_cmd_up(725      new CommandObjectRegexCommand(726          *this, "_regexp-attach", "Attach to process by ID or name.",727          "_regexp-attach <pid> | <process-name>", 0, false));728  if (attach_regex_cmd_up) {729    if (attach_regex_cmd_up->AddRegexCommand("^([0-9]+)[[:space:]]*$",730                                             "process attach --pid %1") &&731        attach_regex_cmd_up->AddRegexCommand(732            "^(-.*|.* -.*)$", "process attach %1") && // Any options that are733                                                      // specified get passed to734                                                      // 'process attach'735        attach_regex_cmd_up->AddRegexCommand("^(.+)$",736                                             "process attach --name '%1'") &&737        attach_regex_cmd_up->AddRegexCommand("^$", "process attach")) {738      CommandObjectSP attach_regex_cmd_sp(attach_regex_cmd_up.release());739      m_command_dict[std::string(attach_regex_cmd_sp->GetCommandName())] =740          attach_regex_cmd_sp;741    }742  }743 744  std::unique_ptr<CommandObjectRegexCommand> down_regex_cmd_up(745      new CommandObjectRegexCommand(*this, "_regexp-down",746                                    "Select a newer stack frame.  Defaults to "747                                    "moving one frame, a numeric argument can "748                                    "specify an arbitrary number.",749                                    "_regexp-down [<count>]", 0, false));750  if (down_regex_cmd_up) {751    if (down_regex_cmd_up->AddRegexCommand("^$", "frame select -r -1") &&752        down_regex_cmd_up->AddRegexCommand("^([0-9]+)$",753                                           "frame select -r -%1")) {754      CommandObjectSP down_regex_cmd_sp(down_regex_cmd_up.release());755      m_command_dict[std::string(down_regex_cmd_sp->GetCommandName())] =756          down_regex_cmd_sp;757    }758  }759 760  std::unique_ptr<CommandObjectRegexCommand> up_regex_cmd_up(761      new CommandObjectRegexCommand(762          *this, "_regexp-up",763          "Select an older stack frame.  Defaults to moving one "764          "frame, a numeric argument can specify an arbitrary number.",765          "_regexp-up [<count>]", 0, false));766  if (up_regex_cmd_up) {767    if (up_regex_cmd_up->AddRegexCommand("^$", "frame select -r 1") &&768        up_regex_cmd_up->AddRegexCommand("^([0-9]+)$", "frame select -r %1")) {769      CommandObjectSP up_regex_cmd_sp(up_regex_cmd_up.release());770      m_command_dict[std::string(up_regex_cmd_sp->GetCommandName())] =771          up_regex_cmd_sp;772    }773  }774 775  std::unique_ptr<CommandObjectRegexCommand> display_regex_cmd_up(776      new CommandObjectRegexCommand(777          *this, "_regexp-display",778          "Evaluate an expression at every stop (see 'help target stop-hook'.)",779          "_regexp-display expression", 0, false));780  if (display_regex_cmd_up) {781    if (display_regex_cmd_up->AddRegexCommand(782            "^(.+)$", "target stop-hook add -o \"expr -- %1\"")) {783      CommandObjectSP display_regex_cmd_sp(display_regex_cmd_up.release());784      m_command_dict[std::string(display_regex_cmd_sp->GetCommandName())] =785          display_regex_cmd_sp;786    }787  }788 789  std::unique_ptr<CommandObjectRegexCommand> undisplay_regex_cmd_up(790      new CommandObjectRegexCommand(*this, "_regexp-undisplay",791                                    "Stop displaying expression at every "792                                    "stop (specified by stop-hook index.)",793                                    "_regexp-undisplay stop-hook-number", 0,794                                    false));795  if (undisplay_regex_cmd_up) {796    if (undisplay_regex_cmd_up->AddRegexCommand("^([0-9]+)$",797                                                "target stop-hook delete %1")) {798      CommandObjectSP undisplay_regex_cmd_sp(undisplay_regex_cmd_up.release());799      m_command_dict[std::string(undisplay_regex_cmd_sp->GetCommandName())] =800          undisplay_regex_cmd_sp;801    }802  }803 804  std::unique_ptr<CommandObjectRegexCommand> connect_gdb_remote_cmd_up(805      new CommandObjectRegexCommand(806          *this, "gdb-remote",807          "Connect to a process via remote GDB server.\n"808          "If no host is specified, localhost is assumed.\n"809          "gdb-remote is an abbreviation for 'process connect --plugin "810          "gdb-remote connect://<hostname>:<port>'\n",811          "gdb-remote [<hostname>:]<portnum>", 0, false));812  if (connect_gdb_remote_cmd_up) {813    if (connect_gdb_remote_cmd_up->AddRegexCommand(814            "^([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)$",815            "process connect --plugin gdb-remote connect://%1:%2") &&816        connect_gdb_remote_cmd_up->AddRegexCommand(817            "^([[:digit:]]+)$",818            "process connect --plugin gdb-remote connect://localhost:%1")) {819      CommandObjectSP command_sp(connect_gdb_remote_cmd_up.release());820      m_command_dict[std::string(command_sp->GetCommandName())] = command_sp;821    }822  }823 824  std::unique_ptr<CommandObjectRegexCommand> connect_kdp_remote_cmd_up(825      new CommandObjectRegexCommand(826          *this, "kdp-remote",827          "Connect to a process via remote KDP server.\n"828          "If no UDP port is specified, port 41139 is assumed.\n"829          "kdp-remote is an abbreviation for 'process connect --plugin "830          "kdp-remote udp://<hostname>:<port>'\n",831          "kdp-remote <hostname>[:<portnum>]", 0, false));832  if (connect_kdp_remote_cmd_up) {833    if (connect_kdp_remote_cmd_up->AddRegexCommand(834            "^([^:]+:[[:digit:]]+)$",835            "process connect --plugin kdp-remote udp://%1") &&836        connect_kdp_remote_cmd_up->AddRegexCommand(837            "^(.+)$", "process connect --plugin kdp-remote udp://%1:41139")) {838      CommandObjectSP command_sp(connect_kdp_remote_cmd_up.release());839      m_command_dict[std::string(command_sp->GetCommandName())] = command_sp;840    }841  }842 843  std::unique_ptr<CommandObjectRegexCommand> bt_regex_cmd_up(844      new CommandObjectRegexCommand(845          *this, "_regexp-bt",846          "Show backtrace of the current thread's call stack. Any numeric "847          "argument displays at most that many frames. The argument 'all' "848          "displays all threads. Use 'settings set frame-format' to customize "849          "the printing of individual frames and 'settings set thread-format' "850          "to customize the thread header. Frame recognizers may filter the "851          "list. Use 'thread backtrace -u (--unfiltered)' to see them all.",852          "bt [<digit> | all]", 0, false));853  if (bt_regex_cmd_up) {854    // accept but don't document "bt -c <number>" -- before bt was a regex855    // command if you wanted to backtrace three frames you would do "bt -c 3"856    // but the intention is to have this emulate the gdb "bt" command and so857    // now "bt 3" is the preferred form, in line with gdb.858    if (bt_regex_cmd_up->AddRegexCommand("^([[:digit:]]+)[[:space:]]*$",859                                         "thread backtrace -c %1") &&860        bt_regex_cmd_up->AddRegexCommand("^(-[^[:space:]].*)$",861                                         "thread backtrace %1") &&862        bt_regex_cmd_up->AddRegexCommand("^all[[:space:]]*$",863                                         "thread backtrace all") &&864        bt_regex_cmd_up->AddRegexCommand("^[[:space:]]*$",865                                         "thread backtrace")) {866      CommandObjectSP command_sp(bt_regex_cmd_up.release());867      m_command_dict[std::string(command_sp->GetCommandName())] = command_sp;868    }869  }870 871  std::unique_ptr<CommandObjectRegexCommand> list_regex_cmd_up(872      new CommandObjectRegexCommand(873          *this, "_regexp-list",874          "List relevant source code using one of several shorthand formats.",875          "\n"876          "_regexp-list <file>:<line>   // List around specific file/line\n"877          "_regexp-list <line>          // List current file around specified "878          "line\n"879          "_regexp-list <function-name> // List specified function\n"880          "_regexp-list 0x<address>     // List around specified address\n"881          "_regexp-list -[<count>]      // List previous <count> lines\n"882          "_regexp-list                 // List subsequent lines",883          lldb::eSourceFileCompletion, false));884  if (list_regex_cmd_up) {885    if (list_regex_cmd_up->AddRegexCommand("^([0-9]+)[[:space:]]*$",886                                           "source list --line %1") &&887        list_regex_cmd_up->AddRegexCommand(888            "^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]"889            "]*$",890            "source list --file '%1' --line %2") &&891        list_regex_cmd_up->AddRegexCommand(892            "^\\*?(0x[[:xdigit:]]+)[[:space:]]*$",893            "source list --address %1") &&894        list_regex_cmd_up->AddRegexCommand("^-[[:space:]]*$",895                                           "source list --reverse") &&896        list_regex_cmd_up->AddRegexCommand(897            "^-([[:digit:]]+)[[:space:]]*$",898            "source list --reverse --count %1") &&899        list_regex_cmd_up->AddRegexCommand("^(.+)$",900                                           "source list --name \"%1\"") &&901        list_regex_cmd_up->AddRegexCommand("^$", "source list")) {902      CommandObjectSP list_regex_cmd_sp(list_regex_cmd_up.release());903      m_command_dict[std::string(list_regex_cmd_sp->GetCommandName())] =904          list_regex_cmd_sp;905    }906  }907 908  std::unique_ptr<CommandObjectRegexCommand> env_regex_cmd_up(909      new CommandObjectRegexCommand(910          *this, "_regexp-env",911          "Shorthand for viewing and setting environment variables.",912          "\n"913          "_regexp-env                  // Show environment\n"914          "_regexp-env <name>=<value>   // Set an environment variable",915          0, false));916  if (env_regex_cmd_up) {917    if (env_regex_cmd_up->AddRegexCommand("^$",918                                          "settings show target.env-vars") &&919        env_regex_cmd_up->AddRegexCommand("^([A-Za-z_][A-Za-z_0-9]*=.*)$",920                                          "settings set target.env-vars %1")) {921      CommandObjectSP env_regex_cmd_sp(env_regex_cmd_up.release());922      m_command_dict[std::string(env_regex_cmd_sp->GetCommandName())] =923          env_regex_cmd_sp;924    }925  }926 927  std::unique_ptr<CommandObjectRegexCommand> jump_regex_cmd_up(928      new CommandObjectRegexCommand(929          *this, "_regexp-jump", "Set the program counter to a new address.",930          "\n"931          "_regexp-jump <line>\n"932          "_regexp-jump +<line-offset> | -<line-offset>\n"933          "_regexp-jump <file>:<line>\n"934          "_regexp-jump *<addr>\n",935          0, false));936  if (jump_regex_cmd_up) {937    if (jump_regex_cmd_up->AddRegexCommand("^\\*(.*)$",938                                           "thread jump --addr %1") &&939        jump_regex_cmd_up->AddRegexCommand("^([0-9]+)$",940                                           "thread jump --line %1") &&941        jump_regex_cmd_up->AddRegexCommand("^([^:]+):([0-9]+)$",942                                           "thread jump --file %1 --line %2") &&943        jump_regex_cmd_up->AddRegexCommand("^([+\\-][0-9]+)$",944                                           "thread jump --by %1")) {945      CommandObjectSP jump_regex_cmd_sp(jump_regex_cmd_up.release());946      m_command_dict[std::string(jump_regex_cmd_sp->GetCommandName())] =947          jump_regex_cmd_sp;948    }949  }950 951  std::shared_ptr<CommandObjectRegexCommand> step_regex_cmd_sp(952      new CommandObjectRegexCommand(953          *this, "_regexp-step",954          "Single step, optionally to a specific function.",955          "\n"956          "_regexp-step                 // Single step\n"957          "_regexp-step <function-name> // Step into the named function\n",958          0, false));959  if (step_regex_cmd_sp) {960    if (step_regex_cmd_sp->AddRegexCommand("^[[:space:]]*$",961                                           "thread step-in") &&962        step_regex_cmd_sp->AddRegexCommand("^[[:space:]]*(-.+)$",963                                           "thread step-in %1") &&964        step_regex_cmd_sp->AddRegexCommand(965            "^[[:space:]]*(.+)[[:space:]]*$",966            "thread step-in --end-linenumber block --step-in-target %1")) {967      m_command_dict[std::string(step_regex_cmd_sp->GetCommandName())] =968          step_regex_cmd_sp;969    }970  }971}972 973int CommandInterpreter::GetCommandNamesMatchingPartialString(974    const char *cmd_str, bool include_aliases, StringList &matches,975    StringList &descriptions) {976  AddNamesMatchingPartialString(m_command_dict, cmd_str, matches,977                                &descriptions);978 979  if (include_aliases) {980    AddNamesMatchingPartialString(m_alias_dict, cmd_str, matches,981                                  &descriptions);982  }983 984  return matches.GetSize();985}986 987CommandObjectMultiword *988CommandInterpreter::VerifyUserMultiwordCmdPath(Args &path, bool leaf_is_command,989                                               Status &result) {990  result.Clear();991 992  auto get_multi_or_report_error =993      [&result](CommandObjectSP cmd_sp,994                const char *name) -> CommandObjectMultiword * {995    if (!cmd_sp) {996      result = Status::FromErrorStringWithFormat(997          "Path component: '%s' not found", name);998      return nullptr;999    }1000    if (!cmd_sp->IsUserCommand()) {1001      result = Status::FromErrorStringWithFormat(1002          "Path component: '%s' is not a user "1003          "command",1004          name);1005      return nullptr;1006    }1007    CommandObjectMultiword *cmd_as_multi = cmd_sp->GetAsMultiwordCommand();1008    if (!cmd_as_multi) {1009      result = Status::FromErrorStringWithFormat(1010          "Path component: '%s' is not a container "1011          "command",1012          name);1013      return nullptr;1014    }1015    return cmd_as_multi;1016  };1017 1018  size_t num_args = path.GetArgumentCount();1019  if (num_args == 0) {1020    result = Status::FromErrorString("empty command path");1021    return nullptr;1022  }1023 1024  if (num_args == 1 && leaf_is_command) {1025    // We just got a leaf command to be added to the root.  That's not an error,1026    // just return null for the container.1027    return nullptr;1028  }1029 1030  // Start by getting the root command from the interpreter.1031  const char *cur_name = path.GetArgumentAtIndex(0);1032  CommandObjectSP cur_cmd_sp = GetCommandSPExact(cur_name);1033  CommandObjectMultiword *cur_as_multi =1034      get_multi_or_report_error(cur_cmd_sp, cur_name);1035  if (cur_as_multi == nullptr)1036    return nullptr;1037 1038  size_t num_path_elements = num_args - (leaf_is_command ? 1 : 0);1039  for (size_t cursor = 1; cursor < num_path_elements && cur_as_multi != nullptr;1040       cursor++) {1041    cur_name = path.GetArgumentAtIndex(cursor);1042    cur_cmd_sp = cur_as_multi->GetSubcommandSPExact(cur_name);1043    cur_as_multi = get_multi_or_report_error(cur_cmd_sp, cur_name);1044  }1045  return cur_as_multi;1046}1047 1048CommandObjectSP CommandInterpreter::GetFrameLanguageCommand() const {1049  auto frame_sp = GetExecutionContext().GetFrameSP();1050  if (!frame_sp)1051    return {};1052  auto frame_language =1053      Language::GetPrimaryLanguage(frame_sp->GuessLanguage().AsLanguageType());1054 1055  auto it = m_command_dict.find("language");1056  if (it == m_command_dict.end())1057    return {};1058  // The root "language" command.1059  CommandObjectSP language_cmd_sp = it->second;1060 1061  auto *plugin = Language::FindPlugin(frame_language);1062  if (!plugin)1063    return {};1064  // "cplusplus", "objc", etc.1065  auto lang_name = plugin->GetPluginName();1066 1067  return language_cmd_sp->GetSubcommandSPExact(lang_name);1068}1069 1070CommandObjectSP1071CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases,1072                                 bool exact, StringList *matches,1073                                 StringList *descriptions) const {1074  CommandObjectSP command_sp;1075 1076  std::string cmd = std::string(cmd_str);1077 1078  if (HasCommands()) {1079    auto pos = m_command_dict.find(cmd);1080    if (pos != m_command_dict.end())1081      command_sp = pos->second;1082  }1083 1084  if (include_aliases && HasAliases()) {1085    auto alias_pos = m_alias_dict.find(cmd);1086    if (alias_pos != m_alias_dict.end())1087      command_sp = alias_pos->second;1088  }1089 1090  if (HasUserCommands()) {1091    auto pos = m_user_dict.find(cmd);1092    if (pos != m_user_dict.end())1093      command_sp = pos->second;1094  }1095 1096  if (HasUserMultiwordCommands()) {1097    auto pos = m_user_mw_dict.find(cmd);1098    if (pos != m_user_mw_dict.end())1099      command_sp = pos->second;1100  }1101 1102  if (!exact && !command_sp) {1103    // We will only get into here if we didn't find any exact matches.1104 1105    CommandObjectSP user_match_sp, user_mw_match_sp, alias_match_sp,1106        real_match_sp;1107 1108    StringList local_matches;1109    if (matches == nullptr)1110      matches = &local_matches;1111 1112    unsigned int num_cmd_matches = 0;1113    unsigned int num_alias_matches = 0;1114    unsigned int num_user_matches = 0;1115    unsigned int num_user_mw_matches = 0;1116 1117    // Look through the command dictionaries one by one, and if we get only one1118    // match from any of them in toto, then return that, otherwise return an1119    // empty CommandObjectSP and the list of matches.1120 1121    if (HasCommands()) {1122      num_cmd_matches = AddNamesMatchingPartialString(m_command_dict, cmd_str,1123                                                      *matches, descriptions);1124    }1125 1126    if (num_cmd_matches == 1) {1127      cmd.assign(matches->GetStringAtIndex(0));1128      auto pos = m_command_dict.find(cmd);1129      if (pos != m_command_dict.end())1130        real_match_sp = pos->second;1131    }1132 1133    if (include_aliases && HasAliases()) {1134      num_alias_matches = AddNamesMatchingPartialString(m_alias_dict, cmd_str,1135                                                        *matches, descriptions);1136    }1137 1138    if (num_alias_matches == 1) {1139      cmd.assign(matches->GetStringAtIndex(num_cmd_matches));1140      auto alias_pos = m_alias_dict.find(cmd);1141      if (alias_pos != m_alias_dict.end())1142        alias_match_sp = alias_pos->second;1143    }1144 1145    if (HasUserCommands()) {1146      num_user_matches = AddNamesMatchingPartialString(m_user_dict, cmd_str,1147                                                       *matches, descriptions);1148    }1149 1150    if (num_user_matches == 1) {1151      cmd.assign(1152          matches->GetStringAtIndex(num_cmd_matches + num_alias_matches));1153 1154      auto pos = m_user_dict.find(cmd);1155      if (pos != m_user_dict.end())1156        user_match_sp = pos->second;1157    }1158 1159    if (HasUserMultiwordCommands()) {1160      num_user_mw_matches = AddNamesMatchingPartialString(1161          m_user_mw_dict, cmd_str, *matches, descriptions);1162    }1163 1164    if (num_user_mw_matches == 1) {1165      cmd.assign(matches->GetStringAtIndex(num_cmd_matches + num_alias_matches +1166                                           num_user_matches));1167 1168      auto pos = m_user_mw_dict.find(cmd);1169      if (pos != m_user_mw_dict.end())1170        user_mw_match_sp = pos->second;1171    }1172 1173    // If we got exactly one match, return that, otherwise return the match1174    // list.1175 1176    if (num_user_matches + num_user_mw_matches + num_cmd_matches +1177            num_alias_matches ==1178        1) {1179      if (num_cmd_matches)1180        return real_match_sp;1181      else if (num_alias_matches)1182        return alias_match_sp;1183      else if (num_user_mw_matches)1184        return user_mw_match_sp;1185      else1186        return user_match_sp;1187    }1188  }1189 1190  // When no single match is found, attempt to resolve the command as a language1191  // plugin subcommand.1192  if (!command_sp) {1193    // The `language` subcommand ("language objc", "language cplusplus", etc).1194    CommandObjectMultiword *lang_subcmd = nullptr;1195    if (auto lang_subcmd_sp = GetFrameLanguageCommand()) {1196      lang_subcmd = lang_subcmd_sp->GetAsMultiwordCommand();1197      command_sp = lang_subcmd_sp->GetSubcommandSPExact(cmd_str);1198    }1199 1200    if (!command_sp && !exact && lang_subcmd) {1201      StringList lang_matches;1202      AddNamesMatchingPartialString(lang_subcmd->GetSubcommandDictionary(),1203                                    cmd_str, lang_matches, descriptions);1204      if (matches)1205        matches->AppendList(lang_matches);1206      if (lang_matches.GetSize() == 1) {1207        const auto &lang_dict = lang_subcmd->GetSubcommandDictionary();1208        auto pos = lang_dict.find(lang_matches[0]);1209        if (pos != lang_dict.end())1210          return pos->second;1211      }1212    }1213  }1214 1215  if (matches && command_sp) {1216    matches->AppendString(cmd_str);1217    if (descriptions)1218      descriptions->AppendString(command_sp->GetHelp());1219  }1220 1221  return command_sp;1222}1223 1224bool CommandInterpreter::AddCommand(llvm::StringRef name,1225                                    const lldb::CommandObjectSP &cmd_sp,1226                                    bool can_replace) {1227  if (cmd_sp.get())1228    lldbassert((this == &cmd_sp->GetCommandInterpreter()) &&1229               "tried to add a CommandObject from a different interpreter");1230 1231  if (name.empty())1232    return false;1233 1234  cmd_sp->SetIsUserCommand(false);1235 1236  std::string name_sstr(name);1237  auto name_iter = m_command_dict.find(name_sstr);1238  if (name_iter != m_command_dict.end()) {1239    if (!can_replace || !name_iter->second->IsRemovable())1240      return false;1241    name_iter->second = cmd_sp;1242  } else {1243    m_command_dict[name_sstr] = cmd_sp;1244  }1245  return true;1246}1247 1248Status CommandInterpreter::AddUserCommand(llvm::StringRef name,1249                                          const lldb::CommandObjectSP &cmd_sp,1250                                          bool can_replace) {1251  Status result;1252  if (cmd_sp.get())1253    lldbassert((this == &cmd_sp->GetCommandInterpreter()) &&1254               "tried to add a CommandObject from a different interpreter");1255  if (name.empty()) {1256    result = Status::FromErrorString(1257        "can't use the empty string for a command name");1258    return result;1259  }1260  // do not allow replacement of internal commands1261  if (CommandExists(name)) {1262    result = Status::FromErrorString("can't replace builtin command");1263    return result;1264  }1265 1266  if (UserCommandExists(name)) {1267    if (!can_replace) {1268      result = Status::FromErrorStringWithFormatv(1269          "user command \"{0}\" already exists and force replace was not set "1270          "by --overwrite or 'settings set interpreter.require-overwrite "1271          "false'",1272          name);1273      return result;1274    }1275    if (cmd_sp->IsMultiwordObject()) {1276      if (!m_user_mw_dict[std::string(name)]->IsRemovable()) {1277        result = Status::FromErrorString(1278            "can't replace explicitly non-removable multi-word command");1279        return result;1280      }1281    } else {1282      if (!m_user_dict[std::string(name)]->IsRemovable()) {1283        result = Status::FromErrorString(1284            "can't replace explicitly non-removable command");1285        return result;1286      }1287    }1288  }1289 1290  cmd_sp->SetIsUserCommand(true);1291 1292  if (cmd_sp->IsMultiwordObject())1293    m_user_mw_dict[std::string(name)] = cmd_sp;1294  else1295    m_user_dict[std::string(name)] = cmd_sp;1296  return result;1297}1298 1299CommandObjectSP1300CommandInterpreter::GetCommandSPExact(llvm::StringRef cmd_str,1301                                      bool include_aliases) const {1302  // Break up the command string into words, in case it's a multi-word command.1303  Args cmd_words(cmd_str);1304 1305  if (cmd_str.empty())1306    return {};1307 1308  if (cmd_words.GetArgumentCount() == 1)1309    return GetCommandSP(cmd_str, include_aliases, true);1310 1311  // We have a multi-word command (seemingly), so we need to do more work.1312  // First, get the cmd_obj_sp for the first word in the command.1313  CommandObjectSP cmd_obj_sp =1314      GetCommandSP(cmd_words.GetArgumentAtIndex(0), include_aliases, true);1315  if (!cmd_obj_sp)1316    return {};1317 1318  // Loop through the rest of the words in the command (everything passed in1319  // was supposed to be part of a command name), and find the appropriate1320  // sub-command SP for each command word....1321  size_t end = cmd_words.GetArgumentCount();1322  for (size_t i = 1; i < end; ++i) {1323    if (!cmd_obj_sp->IsMultiwordObject()) {1324      // We have more words in the command name, but we don't have a1325      // multiword object. Fail and return.1326      return {};1327    }1328 1329    cmd_obj_sp = cmd_obj_sp->GetSubcommandSP(cmd_words.GetArgumentAtIndex(i));1330    if (!cmd_obj_sp) {1331      // The sub-command name was invalid.  Fail and return.1332      return {};1333    }1334  }1335 1336  // We successfully looped through all the command words and got valid1337  // command objects for them.1338  return cmd_obj_sp;1339}1340 1341CommandObject *1342CommandInterpreter::GetCommandObject(llvm::StringRef cmd_str,1343                                     StringList *matches,1344                                     StringList *descriptions) const {1345  // Try to find a match among commands and aliases. Allowing inexact matches,1346  // but perferring exact matches.1347  return GetCommandSP(cmd_str, /*include_aliases=*/true, /*exact=*/false,1348                      matches, descriptions)1349      .get();1350}1351 1352CommandObject *CommandInterpreter::GetUserCommandObject(1353    llvm::StringRef cmd, StringList *matches, StringList *descriptions) const {1354  std::string cmd_str(cmd);1355  auto find_exact = [&](const CommandObject::CommandMap &map) {1356    auto found_elem = map.find(cmd);1357    if (found_elem == map.end())1358      return (CommandObject *)nullptr;1359    CommandObject *exact_cmd = found_elem->second.get();1360    if (exact_cmd) {1361      if (matches)1362        matches->AppendString(exact_cmd->GetCommandName());1363      if (descriptions)1364        descriptions->AppendString(exact_cmd->GetHelp());1365      return exact_cmd;1366    }1367    return (CommandObject *)nullptr;1368  };1369 1370  CommandObject *exact_cmd = find_exact(GetUserCommands());1371  if (exact_cmd)1372    return exact_cmd;1373 1374  exact_cmd = find_exact(GetUserMultiwordCommands());1375  if (exact_cmd)1376    return exact_cmd;1377 1378  // We didn't have an exact command, so now look for partial matches.1379  StringList tmp_list;1380  StringList *matches_ptr = matches ? matches : &tmp_list;1381  AddNamesMatchingPartialString(GetUserCommands(), cmd_str, *matches_ptr);1382  AddNamesMatchingPartialString(GetUserMultiwordCommands(), cmd_str,1383                                *matches_ptr);1384 1385  return {};1386}1387 1388CommandObject *CommandInterpreter::GetAliasCommandObject(1389    llvm::StringRef cmd, StringList *matches, StringList *descriptions) const {1390  auto find_exact =1391      [&](const CommandObject::CommandMap &map) -> CommandObject * {1392    auto found_elem = map.find(cmd);1393    if (found_elem == map.end())1394      return (CommandObject *)nullptr;1395    CommandObject *exact_cmd = found_elem->second.get();1396    if (!exact_cmd)1397      return nullptr;1398 1399    if (matches)1400      matches->AppendString(exact_cmd->GetCommandName());1401 1402    if (descriptions)1403      descriptions->AppendString(exact_cmd->GetHelp());1404 1405    return exact_cmd;1406    return nullptr;1407  };1408 1409  CommandObject *exact_cmd = find_exact(GetAliases());1410  if (exact_cmd)1411    return exact_cmd;1412 1413  // We didn't have an exact command, so now look for partial matches.1414  StringList tmp_list;1415  StringList *matches_ptr = matches ? matches : &tmp_list;1416  AddNamesMatchingPartialString(GetAliases(), cmd, *matches_ptr);1417 1418  return {};1419}1420 1421bool CommandInterpreter::CommandExists(llvm::StringRef cmd) const {1422  return m_command_dict.find(cmd) != m_command_dict.end();1423}1424 1425bool CommandInterpreter::GetAliasFullName(llvm::StringRef cmd,1426                                          std::string &full_name) const {1427  bool exact_match = (m_alias_dict.find(cmd) != m_alias_dict.end());1428  if (exact_match) {1429    full_name.assign(std::string(cmd));1430    return exact_match;1431  } else {1432    StringList matches;1433    size_t num_alias_matches;1434    num_alias_matches =1435        AddNamesMatchingPartialString(m_alias_dict, cmd, matches);1436    if (num_alias_matches == 1) {1437      // Make sure this isn't shadowing a command in the regular command space:1438      StringList regular_matches;1439      const bool include_aliases = false;1440      const bool exact = false;1441      CommandObjectSP cmd_obj_sp(1442          GetCommandSP(cmd, include_aliases, exact, &regular_matches));1443      if (cmd_obj_sp || regular_matches.GetSize() > 0)1444        return false;1445      else {1446        full_name.assign(matches.GetStringAtIndex(0));1447        return true;1448      }1449    } else1450      return false;1451  }1452}1453 1454bool CommandInterpreter::AliasExists(llvm::StringRef cmd) const {1455  return m_alias_dict.find(cmd) != m_alias_dict.end();1456}1457 1458bool CommandInterpreter::UserCommandExists(llvm::StringRef cmd) const {1459  return m_user_dict.find(cmd) != m_user_dict.end();1460}1461 1462bool CommandInterpreter::UserMultiwordCommandExists(llvm::StringRef cmd) const {1463  return m_user_mw_dict.find(cmd) != m_user_mw_dict.end();1464}1465 1466CommandAlias *1467CommandInterpreter::AddAlias(llvm::StringRef alias_name,1468                             lldb::CommandObjectSP &command_obj_sp,1469                             llvm::StringRef args_string) {1470  if (command_obj_sp.get())1471    lldbassert((this == &command_obj_sp->GetCommandInterpreter()) &&1472               "tried to add a CommandObject from a different interpreter");1473 1474  std::unique_ptr<CommandAlias> command_alias_up(1475      new CommandAlias(*this, command_obj_sp, args_string, alias_name));1476 1477  if (command_alias_up && command_alias_up->IsValid()) {1478    m_alias_dict[std::string(alias_name)] =1479        CommandObjectSP(command_alias_up.get());1480    return command_alias_up.release();1481  }1482 1483  return nullptr;1484}1485 1486bool CommandInterpreter::RemoveAlias(llvm::StringRef alias_name) {1487  auto pos = m_alias_dict.find(alias_name);1488  if (pos != m_alias_dict.end()) {1489    m_alias_dict.erase(pos);1490    return true;1491  }1492  return false;1493}1494 1495bool CommandInterpreter::RemoveCommand(llvm::StringRef cmd, bool force) {1496  auto pos = m_command_dict.find(cmd);1497  if (pos != m_command_dict.end()) {1498    if (force || pos->second->IsRemovable()) {1499      // Only regular expression objects or python commands are removable under1500      // normal circumstances.1501      m_command_dict.erase(pos);1502      return true;1503    }1504  }1505  return false;1506}1507 1508bool CommandInterpreter::RemoveUser(llvm::StringRef user_name) {1509  CommandObject::CommandMap::iterator pos = m_user_dict.find(user_name);1510  if (pos != m_user_dict.end()) {1511    m_user_dict.erase(pos);1512    return true;1513  }1514  return false;1515}1516 1517bool CommandInterpreter::RemoveUserMultiword(llvm::StringRef multi_name) {1518  CommandObject::CommandMap::iterator pos = m_user_mw_dict.find(multi_name);1519  if (pos != m_user_mw_dict.end()) {1520    m_user_mw_dict.erase(pos);1521    return true;1522  }1523  return false;1524}1525 1526void CommandInterpreter::GetHelp(CommandReturnObject &result,1527                                 uint32_t cmd_types) {1528  llvm::StringRef help_prologue(GetDebugger().GetIOHandlerHelpPrologue());1529  if (!help_prologue.empty()) {1530    OutputFormattedHelpText(result.GetOutputStream(), llvm::StringRef(),1531                            help_prologue);1532  }1533 1534  CommandObject::CommandMap::const_iterator pos;1535  size_t max_len = FindLongestCommandWord(m_command_dict);1536 1537  if ((cmd_types & eCommandTypesBuiltin) == eCommandTypesBuiltin) {1538    result.AppendMessage("Debugger commands:");1539    result.AppendMessage("");1540 1541    for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) {1542      if (!(cmd_types & eCommandTypesHidden) &&1543          (pos->first.compare(0, 1, "_") == 0))1544        continue;1545 1546      OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--",1547                              pos->second->GetHelp(), max_len);1548    }1549    result.AppendMessage("");1550  }1551 1552  if (!m_alias_dict.empty() &&1553      ((cmd_types & eCommandTypesAliases) == eCommandTypesAliases)) {1554    result.AppendMessageWithFormat(1555        "Current command abbreviations "1556        "(type '%shelp command alias' for more info):\n",1557        GetCommandPrefix());1558    result.AppendMessage("");1559    max_len = FindLongestCommandWord(m_alias_dict);1560 1561    for (auto alias_pos = m_alias_dict.begin(); alias_pos != m_alias_dict.end();1562         ++alias_pos) {1563      OutputFormattedHelpText(result.GetOutputStream(), alias_pos->first, "--",1564                              alias_pos->second->GetHelp(), max_len);1565    }1566    result.AppendMessage("");1567  }1568 1569  if (!m_user_dict.empty() &&1570      ((cmd_types & eCommandTypesUserDef) == eCommandTypesUserDef)) {1571    result.AppendMessage("Current user-defined commands:");1572    result.AppendMessage("");1573    max_len = FindLongestCommandWord(m_user_dict);1574    for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos) {1575      OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--",1576                              pos->second->GetHelp(), max_len);1577    }1578    result.AppendMessage("");1579  }1580 1581  if (!m_user_mw_dict.empty() &&1582      ((cmd_types & eCommandTypesUserMW) == eCommandTypesUserMW)) {1583    result.AppendMessage("Current user-defined container commands:");1584    result.AppendMessage("");1585    max_len = FindLongestCommandWord(m_user_mw_dict);1586    for (pos = m_user_mw_dict.begin(); pos != m_user_mw_dict.end(); ++pos) {1587      OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--",1588                              pos->second->GetHelp(), max_len);1589    }1590    result.AppendMessage("");1591  }1592 1593  result.AppendMessageWithFormat(1594      "For more information on any command, type '%shelp <command-name>'.\n",1595      GetCommandPrefix());1596}1597 1598CommandObject *CommandInterpreter::GetCommandObjectForCommand(1599    llvm::StringRef &command_string) {1600  // This function finds the final, lowest-level, alias-resolved command object1601  // whose 'Execute' function will eventually be invoked by the given command1602  // line.1603 1604  CommandObject *cmd_obj = nullptr;1605  size_t start = command_string.find_first_not_of(k_white_space);1606  size_t end = 0;1607  bool done = false;1608  while (!done) {1609    if (start != std::string::npos) {1610      // Get the next word from command_string.1611      end = command_string.find_first_of(k_white_space, start);1612      if (end == std::string::npos)1613        end = command_string.size();1614      std::string cmd_word =1615          std::string(command_string.substr(start, end - start));1616 1617      if (cmd_obj == nullptr)1618        // Since cmd_obj is NULL we are on our first time through this loop.1619        // Check to see if cmd_word is a valid command or alias.1620        cmd_obj = GetCommandObject(cmd_word);1621      else if (cmd_obj->IsMultiwordObject()) {1622        // Our current object is a multi-word object; see if the cmd_word is a1623        // valid sub-command for our object.1624        CommandObject *sub_cmd_obj =1625            cmd_obj->GetSubcommandObject(cmd_word.c_str());1626        if (sub_cmd_obj)1627          cmd_obj = sub_cmd_obj;1628        else // cmd_word was not a valid sub-command word, so we are done1629          done = true;1630      } else1631        // We have a cmd_obj and it is not a multi-word object, so we are done.1632        done = true;1633 1634      // If we didn't find a valid command object, or our command object is not1635      // a multi-word object, or we are at the end of the command_string, then1636      // we are done.  Otherwise, find the start of the next word.1637 1638      if (!cmd_obj || !cmd_obj->IsMultiwordObject() ||1639          end >= command_string.size())1640        done = true;1641      else1642        start = command_string.find_first_not_of(k_white_space, end);1643    } else1644      // Unable to find any more words.1645      done = true;1646  }1647 1648  command_string = command_string.substr(end);1649  return cmd_obj;1650}1651 1652static const char *k_valid_command_chars =1653    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";1654static void StripLeadingSpaces(std::string &s) {1655  if (!s.empty()) {1656    size_t pos = s.find_first_not_of(k_white_space);1657    if (pos == std::string::npos)1658      s.clear();1659    else if (pos == 0)1660      return;1661    s.erase(0, pos);1662  }1663}1664 1665static size_t FindArgumentTerminator(const std::string &s) {1666  const size_t s_len = s.size();1667  size_t offset = 0;1668  while (offset < s_len) {1669    size_t pos = s.find("--", offset);1670    if (pos == std::string::npos)1671      break;1672    if (pos > 0) {1673      if (llvm::isSpace(s[pos - 1])) {1674        // Check if the string ends "\s--" (where \s is a space character) or1675        // if we have "\s--\s".1676        if ((pos + 2 >= s_len) || llvm::isSpace(s[pos + 2])) {1677          return pos;1678        }1679      }1680    }1681    offset = pos + 2;1682  }1683  return std::string::npos;1684}1685 1686static bool ExtractCommand(std::string &command_string, std::string &command,1687                           std::string &suffix, char &quote_char) {1688  command.clear();1689  suffix.clear();1690  StripLeadingSpaces(command_string);1691 1692  bool result = false;1693  quote_char = '\0';1694 1695  if (!command_string.empty()) {1696    const char first_char = command_string[0];1697    if (first_char == '\'' || first_char == '"') {1698      quote_char = first_char;1699      const size_t end_quote_pos = command_string.find(quote_char, 1);1700      if (end_quote_pos == std::string::npos) {1701        command.swap(command_string);1702        command_string.erase();1703      } else {1704        command.assign(command_string, 1, end_quote_pos - 1);1705        if (end_quote_pos + 1 < command_string.size())1706          command_string.erase(0, command_string.find_first_not_of(1707                                      k_white_space, end_quote_pos + 1));1708        else1709          command_string.erase();1710      }1711    } else {1712      const size_t first_space_pos =1713          command_string.find_first_of(k_white_space);1714      if (first_space_pos == std::string::npos) {1715        command.swap(command_string);1716        command_string.erase();1717      } else {1718        command.assign(command_string, 0, first_space_pos);1719        command_string.erase(0, command_string.find_first_not_of(1720                                    k_white_space, first_space_pos));1721      }1722    }1723    result = true;1724  }1725 1726  if (!command.empty()) {1727    // actual commands can't start with '-' or '_'1728    if (command[0] != '-' && command[0] != '_') {1729      size_t pos = command.find_first_not_of(k_valid_command_chars);1730      if (pos > 0 && pos != std::string::npos) {1731        suffix.assign(command.begin() + pos, command.end());1732        command.erase(pos);1733      }1734    }1735  }1736 1737  return result;1738}1739 1740CommandObject *CommandInterpreter::BuildAliasResult(1741    llvm::StringRef alias_name, std::string &raw_input_string,1742    std::string &alias_result, CommandReturnObject &result) {1743  CommandObject *alias_cmd_obj = nullptr;1744  Args cmd_args(raw_input_string);1745  alias_cmd_obj = GetCommandObject(alias_name);1746  StreamString result_str;1747 1748  if (!alias_cmd_obj || !alias_cmd_obj->IsAlias()) {1749    alias_result.clear();1750    return alias_cmd_obj;1751  }1752  std::pair<CommandObjectSP, OptionArgVectorSP> desugared =1753      ((CommandAlias *)alias_cmd_obj)->Desugar();1754  OptionArgVectorSP option_arg_vector_sp = desugared.second;1755  alias_cmd_obj = desugared.first.get();1756  std::string alias_name_str = std::string(alias_name);1757  if ((cmd_args.GetArgumentCount() == 0) ||1758      (alias_name_str != cmd_args.GetArgumentAtIndex(0)))1759    cmd_args.Unshift(alias_name_str);1760 1761  result_str.Printf("%s", alias_cmd_obj->GetCommandName().str().c_str());1762 1763  if (!option_arg_vector_sp.get()) {1764    alias_result = std::string(result_str.GetString());1765    return alias_cmd_obj;1766  }1767  OptionArgVector *option_arg_vector = option_arg_vector_sp.get();1768 1769  int value_type;1770  std::string option;1771  std::string value;1772  for (const auto &entry : *option_arg_vector) {1773    std::tie(option, value_type, value) = entry;1774    if (option == g_argument) {1775      result_str.Printf(" %s", value.c_str());1776      continue;1777    }1778 1779    result_str.Printf(" %s", option.c_str());1780    if (value_type == OptionParser::eNoArgument)1781      continue;1782 1783    if (value_type != OptionParser::eOptionalArgument)1784      result_str.Printf(" ");1785    int index = GetOptionArgumentPosition(value.c_str());1786    if (index == 0)1787      result_str.Printf("%s", value.c_str());1788    else if (static_cast<size_t>(index) >= cmd_args.GetArgumentCount()) {1789 1790      result.AppendErrorWithFormat("Not enough arguments provided; you "1791                                   "need at least %d arguments to use "1792                                   "this alias.\n",1793                                   index);1794      return nullptr;1795    } else {1796      const Args::ArgEntry &entry = cmd_args[index];1797      size_t strpos = raw_input_string.find(entry.c_str());1798      const char quote_char = entry.GetQuoteChar();1799      if (strpos != std::string::npos) {1800        const size_t start_fudge = quote_char == '\0' ? 0 : 1;1801        const size_t len_fudge = quote_char == '\0' ? 0 : 2;1802 1803        // Make sure we aren't going outside the bounds of the cmd string:1804        if (strpos < start_fudge) {1805          result.AppendError("unmatched quote at command beginning");1806          return nullptr;1807        }1808        llvm::StringRef arg_text = entry.ref();1809        if (strpos - start_fudge + arg_text.size() + len_fudge >1810            raw_input_string.size()) {1811          result.AppendError("unmatched quote at command end");1812          return nullptr;1813        }1814        raw_input_string = raw_input_string.erase(1815            strpos - start_fudge,1816            strlen(cmd_args.GetArgumentAtIndex(index)) + len_fudge);1817      }1818      if (quote_char == '\0')1819        result_str.Printf("%s", cmd_args.GetArgumentAtIndex(index));1820      else1821        result_str.Printf("%c%s%c", quote_char, entry.c_str(), quote_char);1822    }1823  }1824 1825  alias_result = std::string(result_str.GetString());1826  return alias_cmd_obj;1827}1828 1829Status CommandInterpreter::PreprocessCommand(std::string &command) {1830  // The command preprocessor needs to do things to the command line before any1831  // parsing of arguments or anything else is done. The only current stuff that1832  // gets preprocessed is anything enclosed in backtick ('`') characters is1833  // evaluated as an expression and the result of the expression must be a1834  // scalar that can be substituted into the command. An example would be:1835  // (lldb) memory read `$rsp + 20`1836  Status error; // Status for any expressions that might not evaluate1837  size_t start_backtick;1838  size_t pos = 0;1839  while ((start_backtick = command.find('`', pos)) != std::string::npos) {1840    // Stop if an error was encountered during the previous iteration.1841    if (error.Fail())1842      break;1843 1844    if (start_backtick > 0 && command[start_backtick - 1] == '\\') {1845      // The backtick was preceded by a '\' character, remove the slash and1846      // don't treat the backtick as the start of an expression.1847      command.erase(start_backtick - 1, 1);1848      // No need to add one to start_backtick since we just deleted a char.1849      pos = start_backtick;1850      continue;1851    }1852 1853    const size_t expr_content_start = start_backtick + 1;1854    const size_t end_backtick = command.find('`', expr_content_start);1855 1856    if (end_backtick == std::string::npos) {1857      // Stop if there's no end backtick.1858      break;1859    }1860 1861    if (end_backtick == expr_content_start) {1862      // Skip over empty expression. (two backticks in a row)1863      command.erase(start_backtick, 2);1864      continue;1865    }1866 1867    std::string expr_str(command, expr_content_start,1868                         end_backtick - expr_content_start);1869    error = PreprocessToken(expr_str);1870    // We always stop at the first error:1871    if (error.Fail())1872      break;1873 1874    command.erase(start_backtick, end_backtick - start_backtick + 1);1875    command.insert(start_backtick, std::string(expr_str));1876    pos = start_backtick + expr_str.size();1877  }1878  return error;1879}1880 1881Status CommandInterpreter::PreprocessToken(std::string &expr_str) {1882  Status error;1883  ExecutionContext exe_ctx(GetExecutionContext());1884 1885  // Get a dummy target to allow for calculator mode while processing1886  // backticks. This also helps break the infinite loop caused when target is1887  // null.1888  Target *exe_target = exe_ctx.GetTargetPtr();1889  Target &target = exe_target ? *exe_target : m_debugger.GetDummyTarget();1890 1891  ValueObjectSP expr_result_valobj_sp;1892 1893  EvaluateExpressionOptions options;1894  options.SetCoerceToId(false);1895  options.SetUnwindOnError(true);1896  options.SetIgnoreBreakpoints(true);1897  options.SetKeepInMemory(false);1898  options.SetTryAllThreads(true);1899  options.SetTimeout(std::nullopt);1900 1901  ExpressionResults expr_result = target.EvaluateExpression(1902      expr_str.c_str(), exe_ctx.GetFramePtr(), expr_result_valobj_sp, options);1903 1904  if (expr_result == eExpressionCompleted) {1905    Scalar scalar;1906    if (expr_result_valobj_sp)1907      expr_result_valobj_sp =1908          expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable(1909              expr_result_valobj_sp->GetDynamicValueType(), true);1910    if (expr_result_valobj_sp->ResolveValue(scalar)) {1911 1912      StreamString value_strm;1913      const bool show_type = false;1914      scalar.GetValue(value_strm, show_type);1915      size_t value_string_size = value_strm.GetSize();1916      if (value_string_size) {1917        expr_str = value_strm.GetData();1918      } else {1919        error =1920            Status::FromErrorStringWithFormat("expression value didn't result "1921                                              "in a scalar value for the "1922                                              "expression '%s'",1923                                              expr_str.c_str());1924      }1925    } else {1926      error =1927          Status::FromErrorStringWithFormat("expression value didn't result "1928                                            "in a scalar value for the "1929                                            "expression '%s'",1930                                            expr_str.c_str());1931    }1932    return error;1933  }1934 1935  // If we have an error from the expression evaluation it will be in the1936  // ValueObject error, which won't be success and we will just report it.1937  // But if for some reason we didn't get a value object at all, then we will1938  // make up some helpful errors from the expression result.1939  if (expr_result_valobj_sp)1940    error = expr_result_valobj_sp->GetError().Clone();1941 1942  if (error.Success()) {1943    std::string result = lldb_private::toString(expr_result) +1944                         "for the expression '" + expr_str + "'";1945    error = Status(result);1946  }1947  return error;1948}1949 1950bool CommandInterpreter::HandleCommand(const char *command_line,1951                                       LazyBool lazy_add_to_history,1952                                       const ExecutionContext &override_context,1953                                       CommandReturnObject &result) {1954 1955  OverrideExecutionContext(override_context);1956  bool status = HandleCommand(command_line, lazy_add_to_history, result);1957  RestoreExecutionContext();1958  return status;1959}1960 1961bool CommandInterpreter::HandleCommand(const char *command_line,1962                                       LazyBool lazy_add_to_history,1963                                       CommandReturnObject &result,1964                                       bool force_repeat_command) {1965  // These are assigned later in the function but they must be declared before1966  // the ScopedDispatcher object because we need their destructions to occur1967  // after the dispatcher's dtor call, which may reference them.1968  // TODO: This function could be refactored?1969  std::string parsed_command_args;1970  CommandObject *cmd_obj = nullptr;1971 1972  telemetry::ScopedDispatcher<telemetry::CommandInfo> helper(&m_debugger);1973  const bool detailed_command_telemetry =1974      telemetry::TelemetryManager::GetInstance()1975          ->GetConfig()1976          ->detailed_command_telemetry;1977  const int command_id = telemetry::CommandInfo::GetNextID();1978 1979  std::string command_string(command_line);1980  std::string original_command_string(command_string);1981  std::string real_original_command_string(command_string);1982 1983  helper.DispatchNow([&](lldb_private::telemetry::CommandInfo *info) {1984    info->command_id = command_id;1985    if (Target *target = GetExecutionContext().GetTargetPtr()) {1986      // If we have a target attached to this command, then get the UUID.1987      info->target_uuid = target->GetExecutableModule() != nullptr1988                              ? target->GetExecutableModule()->GetUUID()1989                              : UUID();1990    }1991    if (detailed_command_telemetry)1992      info->original_command = original_command_string;1993    // The rest (eg., command_name, args, etc) hasn't been parsed yet;1994    // Those will be collected by the on-exit-callback.1995  });1996 1997  helper.DispatchOnExit([&cmd_obj, &parsed_command_args, &result,1998                         detailed_command_telemetry, command_id](1999                            lldb_private::telemetry::CommandInfo *info) {2000    // TODO: this is logging the time the command-handler finishes.2001    // But we may want a finer-grain durations too?2002    // (ie., the execute_time recorded below?)2003    info->command_id = command_id;2004    llvm::StringRef command_name =2005        cmd_obj ? cmd_obj->GetCommandName() : "<not found>";2006    info->command_name = command_name.str();2007    info->ret_status = result.GetStatus();2008    if (std::string error_str = result.GetErrorString(); !error_str.empty())2009      info->error_data = std::move(error_str);2010 2011    if (detailed_command_telemetry)2012      info->args = parsed_command_args;2013  });2014 2015  Log *log = GetLog(LLDBLog::Commands);2016  LLDB_LOGF(log, "Processing command: %s", command_line);2017  LLDB_SCOPED_TIMERF("Processing command: %s.", command_line);2018 2019  // Set the command in the CommandReturnObject here so that it's there even if2020  // the command is interrupted.2021  result.SetCommand(command_line);2022 2023  if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted initiating command")) {2024    result.AppendError("... Interrupted");2025    return false;2026  }2027 2028  bool add_to_history;2029  if (lazy_add_to_history == eLazyBoolCalculate)2030    add_to_history = (m_command_source_depth == 0);2031  else2032    add_to_history = (lazy_add_to_history == eLazyBoolYes);2033 2034  // The same `transcript_item` will be used below to add output and error of2035  // the command.2036  StructuredData::DictionarySP transcript_item;2037  if (GetSaveTranscript()) {2038    m_transcript_stream << "(lldb) " << command_line << '\n';2039 2040    transcript_item = std::make_shared<StructuredData::Dictionary>();2041    transcript_item->AddStringItem("command", command_line);2042    transcript_item->AddIntegerItem(2043        "timestampInEpochSeconds",2044        std::chrono::duration_cast<std::chrono::seconds>(2045            std::chrono::system_clock::now().time_since_epoch())2046            .count());2047    m_transcript.AddItem(transcript_item);2048  }2049 2050  bool empty_command = false;2051  bool comment_command = false;2052  if (command_string.empty())2053    empty_command = true;2054  else {2055    const char *k_space_characters = "\t\n\v\f\r ";2056 2057    size_t non_space = command_string.find_first_not_of(k_space_characters);2058    // Check for empty line or comment line (lines whose first non-space2059    // character is the comment character for this interpreter)2060    if (non_space == std::string::npos)2061      empty_command = true;2062    else if (command_string[non_space] == m_comment_char)2063      comment_command = true;2064    else if (command_string[non_space] == CommandHistory::g_repeat_char) {2065      llvm::StringRef search_str(command_string);2066      search_str = search_str.drop_front(non_space);2067      if (auto hist_str = m_command_history.FindString(search_str)) {2068        add_to_history = false;2069        command_string = std::string(*hist_str);2070        original_command_string = std::string(*hist_str);2071      } else {2072        result.AppendErrorWithFormat("Could not find entry: %s in history",2073                                     command_string.c_str());2074        return false;2075      }2076    }2077  }2078 2079  if (empty_command) {2080    if (!GetRepeatPreviousCommand()) {2081      result.SetStatus(eReturnStatusSuccessFinishNoResult);2082      return true;2083    }2084 2085    if (m_command_history.IsEmpty()) {2086      result.AppendError("empty command");2087      return false;2088    }2089 2090    command_line = m_repeat_command.c_str();2091    command_string = command_line;2092    original_command_string = command_line;2093    if (m_repeat_command.empty()) {2094      result.AppendError("no auto repeat");2095      return false;2096    }2097 2098    add_to_history = false;2099  } else if (comment_command) {2100    result.SetStatus(eReturnStatusSuccessFinishNoResult);2101    return true;2102  }2103 2104  // Phase 1.2105 2106  // Before we do ANY kind of argument processing, we need to figure out what2107  // the real/final command object is for the specified command.  This gets2108  // complicated by the fact that the user could have specified an alias, and,2109  // in translating the alias, there may also be command options and/or even2110  // data (including raw text strings) that need to be found and inserted into2111  // the command line as part of the translation.  So this first step is plain2112  // look-up and replacement, resulting in:2113  //    1. the command object whose Execute method will actually be called2114  //    2. a revised command string, with all substitutions and replacements2115  //       taken care of2116  // From 1 above, we can determine whether the Execute function wants raw2117  // input or not.2118 2119  cmd_obj = ResolveCommandImpl(command_string, result);2120 2121  // We have to preprocess the whole command string for Raw commands, since we2122  // don't know the structure of the command.  For parsed commands, we only2123  // treat backticks as quote characters specially.2124  // FIXME: We probably want to have raw commands do their own preprocessing.2125  // For instance, I don't think people expect substitution in expr expressions.2126  if (cmd_obj && cmd_obj->WantsRawCommandString()) {2127    Status error(PreprocessCommand(command_string));2128 2129    if (error.Fail()) {2130      result.AppendError(error.AsCString());2131      return false;2132    }2133  }2134 2135  // Although the user may have abbreviated the command, the command_string now2136  // has the command expanded to the full name.  For example, if the input was2137  // "br s -n main", command_string is now "breakpoint set -n main".2138  if (log) {2139    llvm::StringRef command_name =2140        cmd_obj ? cmd_obj->GetCommandName() : "<not found>";2141    LLDB_LOGF(log, "HandleCommand, cmd_obj : '%s'", command_name.str().c_str());2142    LLDB_LOGF(log, "HandleCommand, (revised) command_string: '%s'",2143              command_string.c_str());2144    const bool wants_raw_input =2145        (cmd_obj != nullptr) ? cmd_obj->WantsRawCommandString() : false;2146    LLDB_LOGF(log, "HandleCommand, wants_raw_input:'%s'",2147              wants_raw_input ? "True" : "False");2148  }2149 2150  // Phase 2.2151  // Take care of things like setting up the history command & calling the2152  // appropriate Execute method on the CommandObject, with the appropriate2153  // arguments.2154  StatsDuration execute_time;2155  if (cmd_obj != nullptr) {2156    bool generate_repeat_command = add_to_history;2157    // If we got here when empty_command was true, then this command is a2158    // stored "repeat command" which we should give a chance to produce it's2159    // repeat command, even though we don't add repeat commands to the history.2160    generate_repeat_command |= empty_command;2161    // For `command regex`, the regex command (ex `bt`) is added to history, but2162    // the resolved command (ex `thread backtrace`) is _not_ added to history.2163    // However, the resolved command must be given the opportunity to provide a2164    // repeat command. `force_repeat_command` supports this case.2165    generate_repeat_command |= force_repeat_command;2166    if (generate_repeat_command) {2167      Args command_args(command_string);2168      std::optional<std::string> repeat_command =2169          cmd_obj->GetRepeatCommand(command_args, 0);2170      if (repeat_command) {2171        LLDB_LOGF(log, "Repeat command: %s", repeat_command->data());2172        m_repeat_command.assign(*repeat_command);2173      } else {2174        m_repeat_command.assign(original_command_string);2175      }2176    }2177 2178    if (add_to_history)2179      m_command_history.AppendString(original_command_string);2180 2181    const std::size_t actual_cmd_name_len = cmd_obj->GetCommandName().size();2182    if (actual_cmd_name_len < command_string.length())2183      parsed_command_args = command_string.substr(actual_cmd_name_len);2184 2185    // Remove any initial spaces2186    size_t pos = parsed_command_args.find_first_not_of(k_white_space);2187    if (pos != 0 && pos != std::string::npos)2188      parsed_command_args.erase(0, pos);2189 2190    LLDB_LOGF(2191        log, "HandleCommand, command line after removing command name(s): '%s'",2192        parsed_command_args.c_str());2193 2194    // To test whether or not transcript should be saved, `transcript_item` is2195    // used instead of `GetSaveTranscript()`. This is because the latter will2196    // fail when the command is "settings set interpreter.save-transcript true".2197    if (transcript_item) {2198      transcript_item->AddStringItem("commandName", cmd_obj->GetCommandName());2199      transcript_item->AddStringItem("commandArguments", parsed_command_args);2200    }2201 2202    ElapsedTime elapsed(execute_time);2203    cmd_obj->SetOriginalCommandString(real_original_command_string);2204    // Set the indent to the position of the command in the command line.2205    pos = real_original_command_string.rfind(parsed_command_args);2206    std::optional<uint16_t> indent;2207    if (pos != std::string::npos)2208      indent = pos;2209    result.SetDiagnosticIndent(indent);2210    cmd_obj->Execute(parsed_command_args.c_str(), result);2211  }2212 2213  LLDB_LOGF(log, "HandleCommand, command %s",2214            (result.Succeeded() ? "succeeded" : "did not succeed"));2215 2216  // To test whether or not transcript should be saved, `transcript_item` is2217  // used instead of `GetSaveTrasncript()`. This is because the latter will2218  // fail when the command is "settings set interpreter.save-transcript true".2219  if (transcript_item) {2220    m_transcript_stream << result.GetOutputString();2221    m_transcript_stream << result.GetErrorString();2222 2223    transcript_item->AddStringItem("output", result.GetOutputString());2224    transcript_item->AddStringItem("error", result.GetErrorString());2225    transcript_item->AddFloatItem("durationInSeconds",2226                                  execute_time.get().count());2227  }2228 2229  return result.Succeeded();2230}2231 2232void CommandInterpreter::HandleCompletionMatches(CompletionRequest &request) {2233  bool look_for_subcommand = false;2234 2235  // For any of the command completions a unique match will be a complete word.2236 2237  if (request.GetParsedLine().GetArgumentCount() == 0) {2238    // We got nothing on the command line, so return the list of commands2239    bool include_aliases = true;2240    StringList new_matches, descriptions;2241    GetCommandNamesMatchingPartialString("", include_aliases, new_matches,2242                                         descriptions);2243    request.AddCompletions(new_matches, descriptions);2244  } else if (request.GetCursorIndex() == 0) {2245    // The cursor is in the first argument, so just do a lookup in the2246    // dictionary.2247    StringList new_matches, new_descriptions;2248    CommandObject *cmd_obj =2249        GetCommandObject(request.GetParsedLine().GetArgumentAtIndex(0),2250                         &new_matches, &new_descriptions);2251 2252    if (new_matches.GetSize() && cmd_obj && cmd_obj->IsMultiwordObject() &&2253        new_matches.GetStringAtIndex(0) != nullptr &&2254        strcmp(request.GetParsedLine().GetArgumentAtIndex(0),2255               new_matches.GetStringAtIndex(0)) == 0) {2256      if (request.GetParsedLine().GetArgumentCount() != 1) {2257        look_for_subcommand = true;2258        new_matches.DeleteStringAtIndex(0);2259        new_descriptions.DeleteStringAtIndex(0);2260        request.AppendEmptyArgument();2261      }2262    }2263    request.AddCompletions(new_matches, new_descriptions);2264  }2265 2266  if (request.GetCursorIndex() > 0 || look_for_subcommand) {2267    // We are completing further on into a commands arguments, so find the2268    // command and tell it to complete the command. First see if there is a2269    // matching initial command:2270    CommandObject *command_object =2271        GetCommandObject(request.GetParsedLine().GetArgumentAtIndex(0));2272    if (command_object) {2273      request.ShiftArguments();2274      command_object->HandleCompletion(request);2275    }2276  }2277}2278 2279void CommandInterpreter::HandleCompletion(CompletionRequest &request) {2280 2281  // Don't complete comments, and if the line we are completing is just the2282  // history repeat character, substitute the appropriate history line.2283  llvm::StringRef first_arg = request.GetParsedLine().GetArgumentAtIndex(0);2284 2285  if (!first_arg.empty()) {2286    if (first_arg.front() == m_comment_char)2287      return;2288    if (first_arg.front() == CommandHistory::g_repeat_char) {2289      if (auto hist_str = m_command_history.FindString(first_arg))2290        request.AddCompletion(*hist_str, "Previous command history event",2291                              CompletionMode::RewriteLine);2292      return;2293    }2294  }2295 2296  HandleCompletionMatches(request);2297}2298 2299std::optional<std::string>2300CommandInterpreter::GetAutoSuggestionForCommand(llvm::StringRef line) {2301  if (line.empty())2302    return std::nullopt;2303  const size_t s = m_command_history.GetSize();2304  for (int i = s - 1; i >= 0; --i) {2305    llvm::StringRef entry = m_command_history.GetStringAtIndex(i);2306    if (entry.consume_front(line))2307      return entry.str();2308  }2309  return std::nullopt;2310}2311 2312void CommandInterpreter::UpdatePrompt(llvm::StringRef new_prompt) {2313  EventSP prompt_change_event_sp(2314      new Event(eBroadcastBitResetPrompt, new EventDataBytes(new_prompt)));2315 2316  BroadcastEvent(prompt_change_event_sp);2317  if (m_command_io_handler_sp)2318    m_command_io_handler_sp->SetPrompt(new_prompt);2319}2320 2321void CommandInterpreter::UpdateUseColor(bool use_color) {2322  if (m_command_io_handler_sp)2323    m_command_io_handler_sp->SetUseColor(use_color);2324}2325 2326bool CommandInterpreter::Confirm(llvm::StringRef message, bool default_answer) {2327  // Check AutoConfirm first:2328  if (m_debugger.GetAutoConfirm())2329    return default_answer;2330 2331  IOHandlerConfirm *confirm =2332      new IOHandlerConfirm(m_debugger, message, default_answer);2333  IOHandlerSP io_handler_sp(confirm);2334  m_debugger.RunIOHandlerSync(io_handler_sp);2335  return confirm->GetResponse();2336}2337 2338const CommandAlias *2339CommandInterpreter::GetAlias(llvm::StringRef alias_name) const {2340  OptionArgVectorSP ret_val;2341 2342  auto pos = m_alias_dict.find(alias_name);2343  if (pos != m_alias_dict.end())2344    return (CommandAlias *)pos->second.get();2345 2346  return nullptr;2347}2348 2349bool CommandInterpreter::HasCommands() const {2350  return (!m_command_dict.empty());2351}2352 2353bool CommandInterpreter::HasAliases() const { return (!m_alias_dict.empty()); }2354 2355bool CommandInterpreter::HasUserCommands() const {2356  return (!m_user_dict.empty());2357}2358 2359bool CommandInterpreter::HasUserMultiwordCommands() const {2360  return (!m_user_mw_dict.empty());2361}2362 2363bool CommandInterpreter::HasAliasOptions() const { return HasAliases(); }2364 2365void CommandInterpreter::BuildAliasCommandArgs(CommandObject *alias_cmd_obj,2366                                               const char *alias_name,2367                                               Args &cmd_args,2368                                               std::string &raw_input_string,2369                                               CommandReturnObject &result) {2370  OptionArgVectorSP option_arg_vector_sp =2371      GetAlias(alias_name)->GetOptionArguments();2372 2373  bool wants_raw_input = alias_cmd_obj->WantsRawCommandString();2374 2375  // Make sure that the alias name is the 0th element in cmd_args2376  std::string alias_name_str = alias_name;2377  if (alias_name_str != cmd_args.GetArgumentAtIndex(0))2378    cmd_args.Unshift(alias_name_str);2379 2380  Args new_args(alias_cmd_obj->GetCommandName());2381  if (new_args.GetArgumentCount() == 2)2382    new_args.Shift();2383 2384  if (option_arg_vector_sp.get()) {2385    if (wants_raw_input) {2386      // We have a command that both has command options and takes raw input.2387      // Make *sure* it has a " -- " in the right place in the2388      // raw_input_string.2389      size_t pos = raw_input_string.find(" -- ");2390      if (pos == std::string::npos) {2391        // None found; assume it goes at the beginning of the raw input string2392        raw_input_string.insert(0, " -- ");2393      }2394    }2395 2396    OptionArgVector *option_arg_vector = option_arg_vector_sp.get();2397    const size_t old_size = cmd_args.GetArgumentCount();2398    std::vector<bool> used(old_size + 1, false);2399 2400    used[0] = true;2401 2402    int value_type;2403    std::string option;2404    std::string value;2405    for (const auto &option_entry : *option_arg_vector) {2406      std::tie(option, value_type, value) = option_entry;2407      if (option == g_argument) {2408        if (!wants_raw_input || (value != "--")) {2409          // Since we inserted this above, make sure we don't insert it twice2410          new_args.AppendArgument(value);2411        }2412        continue;2413      }2414 2415      if (value_type != OptionParser::eOptionalArgument)2416        new_args.AppendArgument(option);2417 2418      if (value == g_no_argument)2419        continue;2420 2421      int index = GetOptionArgumentPosition(value.c_str());2422      if (index == 0) {2423        // value was NOT a positional argument; must be a real value2424        if (value_type != OptionParser::eOptionalArgument)2425          new_args.AppendArgument(value);2426        else {2427          new_args.AppendArgument(option + value);2428        }2429 2430      } else if (static_cast<size_t>(index) >= cmd_args.GetArgumentCount()) {2431        result.AppendErrorWithFormat("Not enough arguments provided; you "2432                                     "need at least %d arguments to use "2433                                     "this alias.\n",2434                                     index);2435        return;2436      } else {2437        // Find and remove cmd_args.GetArgumentAtIndex(i) from raw_input_string2438        size_t strpos =2439            raw_input_string.find(cmd_args.GetArgumentAtIndex(index));2440        if (strpos != std::string::npos) {2441          raw_input_string = raw_input_string.erase(2442              strpos, strlen(cmd_args.GetArgumentAtIndex(index)));2443        }2444 2445        if (value_type != OptionParser::eOptionalArgument)2446          new_args.AppendArgument(cmd_args.GetArgumentAtIndex(index));2447        else {2448          new_args.AppendArgument(option + cmd_args.GetArgumentAtIndex(index));2449        }2450        used[index] = true;2451      }2452    }2453 2454    for (auto entry : llvm::enumerate(cmd_args.entries())) {2455      if (!used[entry.index()] && !wants_raw_input)2456        new_args.AppendArgument(entry.value().ref());2457    }2458 2459    cmd_args.Clear();2460    cmd_args.SetArguments(new_args.GetArgumentCount(),2461                          new_args.GetConstArgumentVector());2462  } else {2463    result.SetStatus(eReturnStatusSuccessFinishNoResult);2464    // This alias was not created with any options; nothing further needs to be2465    // done, unless it is a command that wants raw input, in which case we need2466    // to clear the rest of the data from cmd_args, since its in the raw input2467    // string.2468    if (wants_raw_input) {2469      cmd_args.Clear();2470      cmd_args.SetArguments(new_args.GetArgumentCount(),2471                            new_args.GetConstArgumentVector());2472    }2473    return;2474  }2475 2476  result.SetStatus(eReturnStatusSuccessFinishNoResult);2477}2478 2479int CommandInterpreter::GetOptionArgumentPosition(const char *in_string) {2480  int position = 0; // Any string that isn't an argument position, i.e. '%'2481                    // followed by an integer, gets a position2482                    // of zero.2483 2484  const char *cptr = in_string;2485 2486  // Does it start with '%'2487  if (cptr[0] == '%') {2488    ++cptr;2489 2490    // Is the rest of it entirely digits?2491    if (isdigit(cptr[0])) {2492      const char *start = cptr;2493      while (isdigit(cptr[0]))2494        ++cptr;2495 2496      // We've gotten to the end of the digits; are we at the end of the2497      // string?2498      if (cptr[0] == '\0')2499        position = atoi(start);2500    }2501  }2502 2503  return position;2504}2505 2506static void GetHomeInitFile(FileSpec &init_file, llvm::StringRef suffix = {}) {2507  std::string init_file_name = ".lldbinit";2508  if (!suffix.empty()) {2509    init_file_name.append("-");2510    init_file_name.append(suffix.str());2511  }2512 2513  init_file =2514      HostInfo::GetUserHomeDir().CopyByAppendingPathComponent(init_file_name);2515}2516 2517static void GetHomeREPLInitFile(FileSpec &init_file, LanguageType language) {2518  if (language == eLanguageTypeUnknown) {2519    LanguageSet repl_languages = Language::GetLanguagesSupportingREPLs();2520    if (auto main_repl_language = repl_languages.GetSingularLanguage())2521      language = *main_repl_language;2522    else2523      return;2524  }2525 2526  std::string init_file_name =2527      (llvm::Twine(".lldbinit-") +2528       llvm::Twine(Language::GetNameForLanguageType(language)) +2529       llvm::Twine("-repl"))2530          .str();2531 2532  init_file =2533      HostInfo::GetUserHomeDir().CopyByAppendingPathComponent(init_file_name);2534}2535 2536static void GetCwdInitFile(llvm::SmallVectorImpl<char> &init_file) {2537  llvm::StringRef s = ".lldbinit";2538  init_file.assign(s.begin(), s.end());2539  FileSystem::Instance().Resolve(init_file);2540}2541 2542void CommandInterpreter::SourceInitFile(FileSpec file,2543                                        CommandReturnObject &result) {2544  assert(!m_skip_lldbinit_files);2545 2546  if (!FileSystem::Instance().Exists(file)) {2547    result.SetStatus(eReturnStatusSuccessFinishNoResult);2548    return;2549  }2550 2551  // Use HandleCommand to 'source' the given file; this will do the actual2552  // broadcasting of the commands back to any appropriate listener (see2553  // CommandObjectSource::Execute for more details).2554  const bool saved_batch = SetBatchCommandMode(true);2555  CommandInterpreterRunOptions options;2556  options.SetSilent(true);2557  options.SetPrintErrors(true);2558  options.SetStopOnError(false);2559  options.SetStopOnContinue(true);2560  HandleCommandsFromFile(file, options, result);2561  SetBatchCommandMode(saved_batch);2562}2563 2564void CommandInterpreter::SourceInitFileCwd(CommandReturnObject &result) {2565  if (m_skip_lldbinit_files) {2566    result.SetStatus(eReturnStatusSuccessFinishNoResult);2567    return;2568  }2569 2570  llvm::SmallString<128> init_file;2571  GetCwdInitFile(init_file);2572  if (!FileSystem::Instance().Exists(init_file)) {2573    result.SetStatus(eReturnStatusSuccessFinishNoResult);2574    return;2575  }2576 2577  LoadCWDlldbinitFile should_load =2578      Target::GetGlobalProperties().GetLoadCWDlldbinitFile();2579 2580  switch (should_load) {2581  case eLoadCWDlldbinitFalse:2582    result.SetStatus(eReturnStatusSuccessFinishNoResult);2583    break;2584  case eLoadCWDlldbinitTrue:2585    SourceInitFile(FileSpec(init_file.str()), result);2586    break;2587  case eLoadCWDlldbinitWarn: {2588    FileSpec home_init_file;2589    GetHomeInitFile(home_init_file);2590    if (llvm::sys::path::parent_path(init_file) ==2591        llvm::sys::path::parent_path(home_init_file.GetPath())) {2592      result.SetStatus(eReturnStatusSuccessFinishNoResult);2593    } else {2594      result.AppendWarning(InitFileWarning);2595    }2596  }2597  }2598}2599 2600/// We will first see if there is an application specific ".lldbinit" file2601/// whose name is "~/.lldbinit" followed by a "-" and the name of the program.2602/// If this file doesn't exist, we fall back to the REPL init file or the2603/// default home init file in "~/.lldbinit".2604void CommandInterpreter::SourceInitFileHome(CommandReturnObject &result,2605                                            bool is_repl) {2606  if (m_skip_lldbinit_files) {2607    result.SetStatus(eReturnStatusSuccessFinishNoResult);2608    return;2609  }2610 2611  FileSpec init_file;2612 2613  if (is_repl)2614    GetHomeREPLInitFile(init_file, GetDebugger().GetREPLLanguage());2615 2616  if (init_file.GetPath().empty())2617    GetHomeInitFile(init_file);2618 2619  if (!m_skip_app_init_files) {2620    llvm::StringRef program_name =2621        HostInfo::GetProgramFileSpec().GetFilename().GetStringRef();2622    FileSpec program_init_file;2623    GetHomeInitFile(program_init_file, program_name);2624    if (FileSystem::Instance().Exists(program_init_file))2625      init_file = program_init_file;2626  }2627 2628  SourceInitFile(init_file, result);2629}2630 2631void CommandInterpreter::SourceInitFileGlobal(CommandReturnObject &result) {2632#ifdef LLDB_GLOBAL_INIT_DIRECTORY2633  if (!m_skip_lldbinit_files) {2634    FileSpec init_file(LLDB_GLOBAL_INIT_DIRECTORY);2635    if (init_file)2636      init_file.MakeAbsolute(HostInfo::GetShlibDir());2637 2638    init_file.AppendPathComponent("lldbinit");2639    SourceInitFile(init_file, result);2640    return;2641  }2642#endif2643  result.SetStatus(eReturnStatusSuccessFinishNoResult);2644}2645 2646const char *CommandInterpreter::GetCommandPrefix() {2647  const char *prefix = GetDebugger().GetIOHandlerCommandPrefix();2648  return prefix == nullptr ? "" : prefix;2649}2650 2651PlatformSP CommandInterpreter::GetPlatform(bool prefer_target_platform) {2652  PlatformSP platform_sp;2653  if (prefer_target_platform) {2654    ExecutionContext exe_ctx(GetExecutionContext());2655    Target *target = exe_ctx.GetTargetPtr();2656    if (target)2657      platform_sp = target->GetPlatform();2658  }2659 2660  if (!platform_sp)2661    platform_sp = m_debugger.GetPlatformList().GetSelectedPlatform();2662  return platform_sp;2663}2664 2665bool CommandInterpreter::DidProcessStopAbnormally() const {2666  auto exe_ctx = GetExecutionContext();2667  TargetSP target_sp = exe_ctx.GetTargetSP();2668  if (!target_sp)2669    return false;2670 2671  ProcessSP process_sp(target_sp->GetProcessSP());2672  if (!process_sp)2673    return false;2674 2675  if (eStateStopped != process_sp->GetState())2676    return false;2677 2678  for (const auto &thread_sp : process_sp->GetThreadList().Threads()) {2679    StopInfoSP stop_info = thread_sp->GetStopInfo();2680    if (!stop_info) {2681      // If there's no stop_info, keep iterating through the other threads;2682      // it's enough that any thread has got a stop_info that indicates2683      // an abnormal stop, to consider the process to be stopped abnormally.2684      continue;2685    }2686 2687    const StopReason reason = stop_info->GetStopReason();2688    if (reason == eStopReasonException ||2689        reason == eStopReasonInstrumentation ||2690        reason == eStopReasonProcessorTrace || reason == eStopReasonInterrupt ||2691        reason == eStopReasonHistoryBoundary)2692      return true;2693 2694    if (reason == eStopReasonSignal) {2695      const auto stop_signal = static_cast<int32_t>(stop_info->GetValue());2696      UnixSignalsSP signals_sp = process_sp->GetUnixSignals();2697      if (!signals_sp || !signals_sp->SignalIsValid(stop_signal))2698        // The signal is unknown, treat it as abnormal.2699        return true;2700 2701      const auto sigint_num = signals_sp->GetSignalNumberFromName("SIGINT");2702      const auto sigstop_num = signals_sp->GetSignalNumberFromName("SIGSTOP");2703      if ((stop_signal != sigint_num) && (stop_signal != sigstop_num))2704        // The signal very likely implies a crash.2705        return true;2706    }2707  }2708 2709  return false;2710}2711 2712void CommandInterpreter::HandleCommands(2713    const StringList &commands, const ExecutionContext &override_context,2714    const CommandInterpreterRunOptions &options, CommandReturnObject &result) {2715 2716  OverrideExecutionContext(override_context);2717  HandleCommands(commands, options, result);2718  RestoreExecutionContext();2719}2720 2721void CommandInterpreter::HandleCommands(2722    const StringList &commands, const CommandInterpreterRunOptions &options,2723    CommandReturnObject &result) {2724  size_t num_lines = commands.GetSize();2725 2726  // If we are going to continue past a "continue" then we need to run the2727  // commands synchronously. Make sure you reset this value anywhere you return2728  // from the function.2729 2730  bool old_async_execution = m_debugger.GetAsyncExecution();2731 2732  if (!options.GetStopOnContinue()) {2733    m_debugger.SetAsyncExecution(false);2734  }2735 2736  for (size_t idx = 0; idx < num_lines; idx++) {2737    const char *cmd = commands.GetStringAtIndex(idx);2738    if (cmd[0] == '\0')2739      continue;2740 2741    if (options.GetEchoCommands()) {2742      // TODO: Add Stream support.2743      result.AppendMessageWithFormat("%s %s\n",2744                                     m_debugger.GetPrompt().str().c_str(), cmd);2745    }2746 2747    CommandReturnObject tmp_result(m_debugger.GetUseColor());2748    tmp_result.SetInteractive(result.GetInteractive());2749    tmp_result.SetSuppressImmediateOutput(true);2750 2751    // We might call into a regex or alias command, in which case the2752    // add_to_history will get lost.  This m_command_source_depth dingus is the2753    // way we turn off adding to the history in that case, so set it up here.2754    if (!options.GetAddToHistory())2755      m_command_source_depth++;2756    bool success = HandleCommand(cmd, options.m_add_to_history, tmp_result);2757    if (!options.GetAddToHistory())2758      m_command_source_depth--;2759 2760    if (options.GetPrintResults()) {2761      if (tmp_result.Succeeded())2762        result.AppendMessage(tmp_result.GetOutputString());2763    }2764 2765    if (!success || !tmp_result.Succeeded()) {2766      std::string error_msg = tmp_result.GetErrorString();2767      if (error_msg.empty())2768        error_msg = "<unknown error>.\n";2769      if (options.GetStopOnError()) {2770        result.AppendErrorWithFormatv("Aborting reading of commands after "2771                                      "command #{0}: '{1}' failed with {2}",2772                                      (uint64_t)idx, cmd, error_msg);2773        m_debugger.SetAsyncExecution(old_async_execution);2774        return;2775      }2776      if (options.GetPrintResults()) {2777        result.AppendMessageWithFormatv("Command #{0} '{1}' failed with {2}",2778                                        (uint64_t)idx + 1, cmd, error_msg);2779      }2780    }2781 2782    if (result.GetImmediateOutputStream())2783      result.GetImmediateOutputStream()->Flush();2784 2785    if (result.GetImmediateErrorStream())2786      result.GetImmediateErrorStream()->Flush();2787 2788    // N.B. Can't depend on DidChangeProcessState, because the state coming2789    // into the command execution could be running (for instance in Breakpoint2790    // Commands. So we check the return value to see if it is has running in2791    // it.2792    if ((tmp_result.GetStatus() == eReturnStatusSuccessContinuingNoResult) ||2793        (tmp_result.GetStatus() == eReturnStatusSuccessContinuingResult)) {2794      if (options.GetStopOnContinue()) {2795        // If we caused the target to proceed, and we're going to stop in that2796        // case, set the status in our real result before returning.  This is2797        // an error if the continue was not the last command in the set of2798        // commands to be run.2799        if (idx != num_lines - 1)2800          result.AppendErrorWithFormat(2801              "Aborting reading of commands after command #%" PRIu642802              ": '%s' continued the target.\n",2803              (uint64_t)idx + 1, cmd);2804        else2805          result.AppendMessageWithFormat("Command #%" PRIu642806                                         " '%s' continued the target.\n",2807                                         (uint64_t)idx + 1, cmd);2808 2809        result.SetStatus(tmp_result.GetStatus());2810        m_debugger.SetAsyncExecution(old_async_execution);2811 2812        return;2813      }2814    }2815 2816    // Also check for "stop on crash here:2817    if (tmp_result.GetDidChangeProcessState() && options.GetStopOnCrash() &&2818        DidProcessStopAbnormally()) {2819      if (idx != num_lines - 1)2820        result.AppendErrorWithFormat(2821            "Aborting reading of commands after command #%" PRIu642822            ": '%s' stopped with a signal or exception.\n",2823            (uint64_t)idx + 1, cmd);2824      else2825        result.AppendMessageWithFormat(2826            "Command #%" PRIu64 " '%s' stopped with a signal or exception.\n",2827            (uint64_t)idx + 1, cmd);2828 2829      result.SetStatus(tmp_result.GetStatus());2830      m_debugger.SetAsyncExecution(old_async_execution);2831 2832      return;2833    }2834  }2835 2836  result.SetStatus(eReturnStatusSuccessFinishResult);2837  m_debugger.SetAsyncExecution(old_async_execution);2838}2839 2840// Make flags that we can pass into the IOHandler so our delegates can do the2841// right thing2842enum {2843  eHandleCommandFlagStopOnContinue = (1u << 0),2844  eHandleCommandFlagStopOnError = (1u << 1),2845  eHandleCommandFlagEchoCommand = (1u << 2),2846  eHandleCommandFlagEchoCommentCommand = (1u << 3),2847  eHandleCommandFlagPrintResult = (1u << 4),2848  eHandleCommandFlagPrintErrors = (1u << 5),2849  eHandleCommandFlagStopOnCrash = (1u << 6),2850  eHandleCommandFlagAllowRepeats = (1u << 7)2851};2852 2853void CommandInterpreter::HandleCommandsFromFile(2854    FileSpec &cmd_file, const ExecutionContext &context,2855    const CommandInterpreterRunOptions &options, CommandReturnObject &result) {2856  OverrideExecutionContext(context);2857  HandleCommandsFromFile(cmd_file, options, result);2858  RestoreExecutionContext();2859}2860 2861void CommandInterpreter::HandleCommandsFromFile(2862    FileSpec &cmd_file, const CommandInterpreterRunOptions &options,2863    CommandReturnObject &result) {2864  if (!FileSystem::Instance().Exists(cmd_file)) {2865    result.AppendErrorWithFormat(2866        "Error reading commands from file %s - file not found.\n",2867        cmd_file.GetFilename().AsCString("<Unknown>"));2868    return;2869  }2870 2871  std::string cmd_file_path = cmd_file.GetPath();2872  auto input_file_up =2873      FileSystem::Instance().Open(cmd_file, File::eOpenOptionReadOnly);2874  if (!input_file_up) {2875    std::string error = llvm::toString(input_file_up.takeError());2876    result.AppendErrorWithFormatv(2877        "error: an error occurred read file '{0}': {1}\n", cmd_file_path,2878        llvm::fmt_consume(input_file_up.takeError()));2879    return;2880  }2881  FileSP input_file_sp = FileSP(std::move(input_file_up.get()));2882 2883  Debugger &debugger = GetDebugger();2884 2885  uint32_t flags = 0;2886 2887  if (options.m_stop_on_continue == eLazyBoolCalculate) {2888    if (m_command_source_flags.empty()) {2889      // Stop on continue by default2890      flags |= eHandleCommandFlagStopOnContinue;2891    } else if (m_command_source_flags.back() &2892               eHandleCommandFlagStopOnContinue) {2893      flags |= eHandleCommandFlagStopOnContinue;2894    }2895  } else if (options.m_stop_on_continue == eLazyBoolYes) {2896    flags |= eHandleCommandFlagStopOnContinue;2897  }2898 2899  if (options.m_stop_on_error == eLazyBoolCalculate) {2900    if (m_command_source_flags.empty()) {2901      if (GetStopCmdSourceOnError())2902        flags |= eHandleCommandFlagStopOnError;2903    } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnError) {2904      flags |= eHandleCommandFlagStopOnError;2905    }2906  } else if (options.m_stop_on_error == eLazyBoolYes) {2907    flags |= eHandleCommandFlagStopOnError;2908  }2909 2910  // stop-on-crash can only be set, if it is present in all levels of2911  // pushed flag sets.2912  if (options.GetStopOnCrash()) {2913    if (m_command_source_flags.empty()) {2914      flags |= eHandleCommandFlagStopOnCrash;2915    } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnCrash) {2916      flags |= eHandleCommandFlagStopOnCrash;2917    }2918  }2919 2920  if (options.m_echo_commands == eLazyBoolCalculate) {2921    if (m_command_source_flags.empty()) {2922      // Echo command by default2923      flags |= eHandleCommandFlagEchoCommand;2924    } else if (m_command_source_flags.back() & eHandleCommandFlagEchoCommand) {2925      flags |= eHandleCommandFlagEchoCommand;2926    }2927  } else if (options.m_echo_commands == eLazyBoolYes) {2928    flags |= eHandleCommandFlagEchoCommand;2929  }2930 2931  // We will only ever ask for this flag, if we echo commands in general.2932  if (options.m_echo_comment_commands == eLazyBoolCalculate) {2933    if (m_command_source_flags.empty()) {2934      // Echo comments by default2935      flags |= eHandleCommandFlagEchoCommentCommand;2936    } else if (m_command_source_flags.back() &2937               eHandleCommandFlagEchoCommentCommand) {2938      flags |= eHandleCommandFlagEchoCommentCommand;2939    }2940  } else if (options.m_echo_comment_commands == eLazyBoolYes) {2941    flags |= eHandleCommandFlagEchoCommentCommand;2942  }2943 2944  if (options.m_print_results == eLazyBoolCalculate) {2945    if (m_command_source_flags.empty()) {2946      // Print output by default2947      flags |= eHandleCommandFlagPrintResult;2948    } else if (m_command_source_flags.back() & eHandleCommandFlagPrintResult) {2949      flags |= eHandleCommandFlagPrintResult;2950    }2951  } else if (options.m_print_results == eLazyBoolYes) {2952    flags |= eHandleCommandFlagPrintResult;2953  }2954 2955  if (options.m_print_errors == eLazyBoolCalculate) {2956    if (m_command_source_flags.empty()) {2957      // Print output by default2958      flags |= eHandleCommandFlagPrintErrors;2959    } else if (m_command_source_flags.back() & eHandleCommandFlagPrintErrors) {2960      flags |= eHandleCommandFlagPrintErrors;2961    }2962  } else if (options.m_print_errors == eLazyBoolYes) {2963    flags |= eHandleCommandFlagPrintErrors;2964  }2965 2966  if (flags & eHandleCommandFlagPrintResult) {2967    debugger.GetOutputFileSP()->Printf("Executing commands in '%s'.\n",2968                                       cmd_file_path.c_str());2969  }2970 2971  // Used for inheriting the right settings when "command source" might2972  // have nested "command source" commands2973  lldb::LockableStreamFileSP empty_stream_sp;2974  m_command_source_flags.push_back(flags);2975  IOHandlerSP io_handler_sp(new IOHandlerEditline(2976      debugger, IOHandler::Type::CommandInterpreter, input_file_sp,2977      empty_stream_sp, // Pass in an empty stream so we inherit the top2978                       // input reader output stream2979      empty_stream_sp, // Pass in an empty stream so we inherit the top2980                       // input reader error stream2981      flags,2982      nullptr, // Pass in NULL for "editline_name" so no history is saved,2983               // or written2984      debugger.GetPrompt(), llvm::StringRef(),2985      false, // Not multi-line2986      debugger.GetUseColor(), 0, *this));2987  const bool old_async_execution = debugger.GetAsyncExecution();2988 2989  // Set synchronous execution if we are not stopping on continue2990  if ((flags & eHandleCommandFlagStopOnContinue) == 0)2991    debugger.SetAsyncExecution(false);2992 2993  m_command_source_depth++;2994  m_command_source_dirs.push_back(cmd_file.CopyByRemovingLastPathComponent());2995 2996  debugger.RunIOHandlerSync(io_handler_sp);2997  if (!m_command_source_flags.empty())2998    m_command_source_flags.pop_back();2999 3000  m_command_source_dirs.pop_back();3001  m_command_source_depth--;3002 3003  result.SetStatus(eReturnStatusSuccessFinishNoResult);3004  debugger.SetAsyncExecution(old_async_execution);3005}3006 3007bool CommandInterpreter::GetSynchronous() { return m_synchronous_execution; }3008 3009void CommandInterpreter::SetSynchronous(bool value) {3010  m_synchronous_execution = value;3011}3012 3013void CommandInterpreter::OutputFormattedHelpText(Stream &strm,3014                                                 llvm::StringRef prefix,3015                                                 llvm::StringRef help_text) {3016  const uint32_t max_columns = m_debugger.GetTerminalWidth();3017 3018  size_t line_width_max = max_columns - prefix.size();3019  if (line_width_max < 16)3020    line_width_max = help_text.size() + prefix.size();3021 3022  strm.IndentMore(prefix.size());3023  bool prefixed_yet = false;3024  // Even if we have no help text we still want to emit the command name.3025  if (help_text.empty())3026    help_text = "No help text";3027  while (!help_text.empty()) {3028    // Prefix the first line, indent subsequent lines to line up3029    if (!prefixed_yet) {3030      strm << prefix;3031      prefixed_yet = true;3032    } else3033      strm.Indent();3034 3035    // Never print more than the maximum on one line.3036    llvm::StringRef this_line = help_text.substr(0, line_width_max);3037 3038    // Always break on an explicit newline.3039    std::size_t first_newline = this_line.find_first_of("\n");3040 3041    // Don't break on space/tab unless the text is too long to fit on one line.3042    std::size_t last_space = llvm::StringRef::npos;3043    if (this_line.size() != help_text.size())3044      last_space = this_line.find_last_of(" \t");3045 3046    // Break at whichever condition triggered first.3047    this_line = this_line.substr(0, std::min(first_newline, last_space));3048    strm.PutCString(this_line);3049    strm.EOL();3050 3051    // Remove whitespace / newlines after breaking.3052    help_text = help_text.drop_front(this_line.size()).ltrim();3053  }3054  strm.IndentLess(prefix.size());3055}3056 3057void CommandInterpreter::OutputFormattedHelpText(Stream &strm,3058                                                 llvm::StringRef word_text,3059                                                 llvm::StringRef separator,3060                                                 llvm::StringRef help_text,3061                                                 size_t max_word_len) {3062  StreamString prefix_stream;3063  prefix_stream.Printf("  %-*s %*s ", (int)max_word_len, word_text.data(),3064                       (int)separator.size(), separator.data());3065  OutputFormattedHelpText(strm, prefix_stream.GetString(), help_text);3066}3067 3068void CommandInterpreter::OutputHelpText(Stream &strm, llvm::StringRef word_text,3069                                        llvm::StringRef separator,3070                                        llvm::StringRef help_text,3071                                        uint32_t max_word_len) {3072  int indent_size = max_word_len + separator.size() + 2;3073 3074  strm.IndentMore(indent_size);3075 3076  StreamString text_strm;3077  text_strm.Printf("%-*s ", (int)max_word_len, word_text.data());3078  text_strm << separator << " " << help_text;3079 3080  const uint32_t max_columns = m_debugger.GetTerminalWidth();3081 3082  llvm::StringRef text = text_strm.GetString();3083 3084  uint32_t chars_left = max_columns;3085 3086  auto nextWordLength = [](llvm::StringRef S) {3087    size_t pos = S.find(' ');3088    return pos == llvm::StringRef::npos ? S.size() : pos;3089  };3090 3091  while (!text.empty()) {3092    if (text.front() == '\n' ||3093        (text.front() == ' ' && nextWordLength(text.ltrim(' ')) > chars_left)) {3094      strm.EOL();3095      strm.Indent();3096      chars_left = max_columns - indent_size;3097      if (text.front() == '\n')3098        text = text.drop_front();3099      else3100        text = text.ltrim(' ');3101    } else {3102      strm.PutChar(text.front());3103      --chars_left;3104      text = text.drop_front();3105    }3106  }3107 3108  strm.EOL();3109  strm.IndentLess(indent_size);3110}3111 3112void CommandInterpreter::FindCommandsForApropos(3113    llvm::StringRef search_word, StringList &commands_found,3114    StringList &commands_help, const CommandObject::CommandMap &command_map) {3115  for (const auto &pair : command_map) {3116    llvm::StringRef command_name = pair.first;3117    CommandObject *cmd_obj = pair.second.get();3118 3119    const bool search_short_help = true;3120    const bool search_long_help = false;3121    const bool search_syntax = false;3122    const bool search_options = false;3123    if (command_name.contains_insensitive(search_word) ||3124        cmd_obj->HelpTextContainsWord(search_word, search_short_help,3125                                      search_long_help, search_syntax,3126                                      search_options)) {3127      commands_found.AppendString(command_name);3128      commands_help.AppendString(cmd_obj->GetHelp());3129    }3130 3131    if (auto *multiword_cmd = cmd_obj->GetAsMultiwordCommand()) {3132      StringList subcommands_found;3133      FindCommandsForApropos(search_word, subcommands_found, commands_help,3134                             multiword_cmd->GetSubcommandDictionary());3135      for (const auto &subcommand_name : subcommands_found) {3136        std::string qualified_name =3137            (command_name + " " + subcommand_name).str();3138        commands_found.AppendString(qualified_name);3139      }3140    }3141  }3142}3143 3144void CommandInterpreter::FindCommandsForApropos(llvm::StringRef search_word,3145                                                StringList &commands_found,3146                                                StringList &commands_help,3147                                                bool search_builtin_commands,3148                                                bool search_user_commands,3149                                                bool search_alias_commands,3150                                                bool search_user_mw_commands) {3151  CommandObject::CommandMap::const_iterator pos;3152 3153  if (search_builtin_commands)3154    FindCommandsForApropos(search_word, commands_found, commands_help,3155                           m_command_dict);3156 3157  if (search_user_commands)3158    FindCommandsForApropos(search_word, commands_found, commands_help,3159                           m_user_dict);3160 3161  if (search_user_mw_commands)3162    FindCommandsForApropos(search_word, commands_found, commands_help,3163                           m_user_mw_dict);3164 3165  if (search_alias_commands)3166    FindCommandsForApropos(search_word, commands_found, commands_help,3167                           m_alias_dict);3168}3169 3170ExecutionContext CommandInterpreter::GetExecutionContext() const {3171  return !m_overriden_exe_contexts.empty()3172             ? m_overriden_exe_contexts.top()3173             : m_debugger.GetSelectedExecutionContext();3174}3175 3176void CommandInterpreter::OverrideExecutionContext(3177    const ExecutionContext &override_context) {3178  m_overriden_exe_contexts.push(override_context);3179}3180 3181void CommandInterpreter::RestoreExecutionContext() {3182  if (!m_overriden_exe_contexts.empty())3183    m_overriden_exe_contexts.pop();3184}3185 3186void CommandInterpreter::GetProcessOutput() {3187  if (ProcessSP process_sp = GetExecutionContext().GetProcessSP())3188    m_debugger.FlushProcessOutput(*process_sp, /*flush_stdout*/ true,3189                                  /*flush_stderr*/ true);3190}3191 3192void CommandInterpreter::StartHandlingCommand() {3193  auto idle_state = CommandHandlingState::eIdle;3194  if (m_command_state.compare_exchange_strong(3195          idle_state, CommandHandlingState::eInProgress))3196    lldbassert(m_iohandler_nesting_level == 0);3197  else3198    lldbassert(m_iohandler_nesting_level > 0);3199  ++m_iohandler_nesting_level;3200}3201 3202void CommandInterpreter::FinishHandlingCommand() {3203  lldbassert(m_iohandler_nesting_level > 0);3204  if (--m_iohandler_nesting_level == 0) {3205    auto prev_state = m_command_state.exchange(CommandHandlingState::eIdle);3206    lldbassert(prev_state != CommandHandlingState::eIdle);3207  }3208}3209 3210bool CommandInterpreter::InterruptCommand() {3211  auto in_progress = CommandHandlingState::eInProgress;3212  return m_command_state.compare_exchange_strong(3213      in_progress, CommandHandlingState::eInterrupted);3214}3215 3216bool CommandInterpreter::WasInterrupted() const {3217  if (!m_debugger.IsIOHandlerThreadCurrentThread())3218    return false;3219 3220  bool was_interrupted =3221      (m_command_state == CommandHandlingState::eInterrupted);3222  lldbassert(!was_interrupted || m_iohandler_nesting_level > 0);3223  return was_interrupted;3224}3225 3226void CommandInterpreter::PrintCommandOutput(IOHandler &io_handler,3227                                            llvm::StringRef str,3228                                            bool is_stdout) {3229 3230  lldb::LockableStreamFileSP stream = is_stdout3231                                          ? io_handler.GetOutputStreamFileSP()3232                                          : io_handler.GetErrorStreamFileSP();3233  // Split the output into lines and poll for interrupt requests3234  bool had_output = !str.empty();3235  while (!str.empty()) {3236    llvm::StringRef line;3237    std::tie(line, str) = str.split('\n');3238    {3239      LockedStreamFile stream_file = stream->Lock();3240      stream_file.Write(line.data(), line.size());3241      stream_file.Write("\n", 1);3242    }3243  }3244 3245  LockedStreamFile stream_file = stream->Lock();3246  if (had_output &&3247      INTERRUPT_REQUESTED(GetDebugger(), "Interrupted dumping command output"))3248    stream_file.Printf("\n... Interrupted.\n");3249  stream_file.Flush();3250}3251 3252bool CommandInterpreter::EchoCommandNonInteractive(3253    llvm::StringRef line, const Flags &io_handler_flags) const {3254  if (!io_handler_flags.Test(eHandleCommandFlagEchoCommand))3255    return false;3256 3257  llvm::StringRef command = line.trim();3258  if (command.empty())3259    return true;3260 3261  if (command.front() == m_comment_char)3262    return io_handler_flags.Test(eHandleCommandFlagEchoCommentCommand);3263 3264  return true;3265}3266 3267void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,3268                                                std::string &line) {3269  // If we were interrupted, bail out...3270  if (WasInterrupted())3271    return;3272 3273  const bool is_interactive = io_handler.GetIsInteractive();3274  const bool allow_repeats =3275      io_handler.GetFlags().Test(eHandleCommandFlagAllowRepeats);3276 3277  if (!is_interactive && !allow_repeats) {3278    // When we are not interactive, don't execute blank lines. This will happen3279    // sourcing a commands file. We don't want blank lines to repeat the3280    // previous command and cause any errors to occur (like redefining an3281    // alias, get an error and stop parsing the commands file).3282    // But obey the AllowRepeats flag if the user has set it.3283    if (line.empty())3284      return;3285  }3286  if (!is_interactive) {3287    // When using a non-interactive file handle (like when sourcing commands3288    // from a file) we need to echo the command out so we don't just see the3289    // command output and no command...3290    if (EchoCommandNonInteractive(line, io_handler.GetFlags())) {3291      LockedStreamFile locked_stream =3292          io_handler.GetOutputStreamFileSP()->Lock();3293      locked_stream.Printf("%s%s\n", io_handler.GetPrompt(), line.c_str());3294    }3295  }3296 3297  StartHandlingCommand();3298 3299  ExecutionContext exe_ctx = m_debugger.GetSelectedExecutionContext();3300  bool pushed_exe_ctx = false;3301  if (exe_ctx.HasTargetScope()) {3302    OverrideExecutionContext(exe_ctx);3303    pushed_exe_ctx = true;3304  }3305  auto finalize = llvm::make_scope_exit([this, pushed_exe_ctx]() {3306    if (pushed_exe_ctx)3307      RestoreExecutionContext();3308  });3309 3310  lldb_private::CommandReturnObject result(m_debugger.GetUseColor());3311  HandleCommand(line.c_str(), eLazyBoolCalculate, result);3312 3313  // Now emit the command output text from the command we just executed3314  if ((result.Succeeded() &&3315       io_handler.GetFlags().Test(eHandleCommandFlagPrintResult)) ||3316      io_handler.GetFlags().Test(eHandleCommandFlagPrintErrors)) {3317    auto DefaultPrintCallback = [&](const CommandReturnObject &result) {3318      // Display any inline diagnostics first.3319      const bool inline_diagnostics = !result.GetImmediateErrorStream() &&3320                                      GetDebugger().GetShowInlineDiagnostics();3321      if (inline_diagnostics) {3322        unsigned prompt_len = m_debugger.GetPrompt().size();3323        if (auto indent = result.GetDiagnosticIndent()) {3324          std::string diags =3325              result.GetInlineDiagnosticString(prompt_len + *indent);3326          PrintCommandOutput(io_handler, diags, true);3327        }3328      }3329 3330      // Display any STDOUT/STDERR _prior_ to emitting the command result text.3331      GetProcessOutput();3332 3333      if (!result.GetImmediateOutputStream()) {3334        llvm::StringRef output = result.GetOutputString();3335        PrintCommandOutput(io_handler, output, true);3336      }3337 3338      // Now emit the command error text from the command we just executed.3339      if (!result.GetImmediateErrorStream()) {3340        std::string error = result.GetErrorString(!inline_diagnostics);3341        PrintCommandOutput(io_handler, error, false);3342      }3343    };3344 3345    if (m_print_callback) {3346      const auto callback_result = m_print_callback(result);3347      if (callback_result == eCommandReturnObjectPrintCallbackSkipped)3348        DefaultPrintCallback(result);3349    } else {3350      DefaultPrintCallback(result);3351    }3352  }3353 3354  FinishHandlingCommand();3355 3356  switch (result.GetStatus()) {3357  case eReturnStatusInvalid:3358  case eReturnStatusSuccessFinishNoResult:3359  case eReturnStatusSuccessFinishResult:3360  case eReturnStatusStarted:3361    break;3362 3363  case eReturnStatusSuccessContinuingNoResult:3364  case eReturnStatusSuccessContinuingResult:3365    if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnContinue))3366      io_handler.SetIsDone(true);3367    break;3368 3369  case eReturnStatusFailed:3370    m_result.IncrementNumberOfErrors();3371    if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnError)) {3372      m_result.SetResult(lldb::eCommandInterpreterResultCommandError);3373      io_handler.SetIsDone(true);3374    }3375    break;3376 3377  case eReturnStatusQuit:3378    m_result.SetResult(lldb::eCommandInterpreterResultQuitRequested);3379    io_handler.SetIsDone(true);3380    break;3381  }3382 3383  // Finally, if we're going to stop on crash, check that here:3384  if (m_result.IsResult(lldb::eCommandInterpreterResultSuccess) &&3385      result.GetDidChangeProcessState() &&3386      io_handler.GetFlags().Test(eHandleCommandFlagStopOnCrash) &&3387      DidProcessStopAbnormally()) {3388    io_handler.SetIsDone(true);3389    m_result.SetResult(lldb::eCommandInterpreterResultInferiorCrash);3390  }3391}3392 3393bool CommandInterpreter::IOHandlerInterrupt(IOHandler &io_handler) {3394  ExecutionContext exe_ctx(GetExecutionContext());3395  Process *process = exe_ctx.GetProcessPtr();3396 3397  if (InterruptCommand())3398    return true;3399 3400  if (process) {3401    StateType state = process->GetState();3402    if (StateIsRunningState(state)) {3403      process->Halt();3404      return true; // Don't do any updating when we are running3405    }3406  }3407 3408  ScriptInterpreter *script_interpreter =3409      m_debugger.GetScriptInterpreter(false);3410  if (script_interpreter) {3411    if (script_interpreter->Interrupt())3412      return true;3413  }3414  return false;3415}3416 3417bool CommandInterpreter::SaveTranscript(3418    CommandReturnObject &result, std::optional<std::string> output_file) {3419  if (output_file == std::nullopt || output_file->empty()) {3420    std::string now = llvm::to_string(std::chrono::system_clock::now());3421    llvm::replace(now, ' ', '_');3422    // Can't have file name with colons on Windows3423    llvm::replace(now, ':', '-');3424    const std::string file_name = "lldb_session_" + now + ".log";3425 3426    FileSpec save_location = GetSaveSessionDirectory();3427 3428    if (!save_location)3429      save_location = HostInfo::GetGlobalTempDir();3430 3431    FileSystem::Instance().Resolve(save_location);3432    save_location.AppendPathComponent(file_name);3433    output_file = save_location.GetPath();3434  }3435 3436  auto error_out = [&](llvm::StringRef error_message, std::string description) {3437    LLDB_LOG(GetLog(LLDBLog::Commands), "{0} ({1}:{2})", error_message,3438             output_file, description);3439    result.AppendErrorWithFormatv(3440        "Failed to save session's transcripts to {0}!", *output_file);3441    return false;3442  };3443 3444  File::OpenOptions flags = File::eOpenOptionWriteOnly |3445                            File::eOpenOptionCanCreate |3446                            File::eOpenOptionTruncate;3447 3448  auto opened_file = FileSystem::Instance().Open(FileSpec(*output_file), flags);3449 3450  if (!opened_file)3451    return error_out("Unable to create file",3452                     llvm::toString(opened_file.takeError()));3453 3454  FileUP file = std::move(opened_file.get());3455 3456  size_t byte_size = m_transcript_stream.GetSize();3457 3458  Status error = file->Write(m_transcript_stream.GetData(), byte_size);3459 3460  if (error.Fail() || byte_size != m_transcript_stream.GetSize())3461    return error_out("Unable to write to destination file",3462                     "Bytes written do not match transcript size.");3463 3464  result.SetStatus(eReturnStatusSuccessFinishNoResult);3465  result.AppendMessageWithFormat("Session's transcripts saved to %s\n",3466                                 output_file->c_str());3467  if (!GetSaveTranscript())3468    result.AppendError(3469        "Note: the setting interpreter.save-transcript is set to false, so the "3470        "transcript might not have been recorded.");3471 3472  if (GetOpenTranscriptInEditor() && Host::IsInteractiveGraphicSession()) {3473    const FileSpec file_spec;3474    error = file->GetFileSpec(const_cast<FileSpec &>(file_spec));3475    if (error.Success()) {3476      if (llvm::Error e = Host::OpenFileInExternalEditor(3477              m_debugger.GetExternalEditor(), file_spec, 1))3478        result.AppendError(llvm::toString(std::move(e)));3479    }3480  }3481 3482  return true;3483}3484 3485bool CommandInterpreter::IsInteractive() {3486  return (GetIOHandler() ? GetIOHandler()->GetIsInteractive() : false);3487}3488 3489FileSpec CommandInterpreter::GetCurrentSourceDir() {3490  if (m_command_source_dirs.empty())3491    return {};3492  return m_command_source_dirs.back();3493}3494 3495void CommandInterpreter::GetLLDBCommandsFromIOHandler(3496    const char *prompt, IOHandlerDelegate &delegate, void *baton) {3497  Debugger &debugger = GetDebugger();3498  IOHandlerSP io_handler_sp(3499      new IOHandlerEditline(debugger, IOHandler::Type::CommandList,3500                            "lldb", // Name of input reader for history3501                            llvm::StringRef(prompt), // Prompt3502                            llvm::StringRef(),       // Continuation prompt3503                            true,                    // Get multiple lines3504                            debugger.GetUseColor(),3505                            0,          // Don't show line numbers3506                            delegate)); // IOHandlerDelegate3507 3508  if (io_handler_sp) {3509    io_handler_sp->SetUserData(baton);3510    debugger.RunIOHandlerAsync(io_handler_sp);3511  }3512}3513 3514void CommandInterpreter::GetPythonCommandsFromIOHandler(3515    const char *prompt, IOHandlerDelegate &delegate, void *baton) {3516  Debugger &debugger = GetDebugger();3517  IOHandlerSP io_handler_sp(3518      new IOHandlerEditline(debugger, IOHandler::Type::PythonCode,3519                            "lldb-python", // Name of input reader for history3520                            llvm::StringRef(prompt), // Prompt3521                            llvm::StringRef(),       // Continuation prompt3522                            true,                    // Get multiple lines3523                            debugger.GetUseColor(),3524                            0,          // Don't show line numbers3525                            delegate)); // IOHandlerDelegate3526 3527  if (io_handler_sp) {3528    io_handler_sp->SetUserData(baton);3529    debugger.RunIOHandlerAsync(io_handler_sp);3530  }3531}3532 3533bool CommandInterpreter::IsActive() {3534  return m_debugger.IsTopIOHandler(m_command_io_handler_sp);3535}3536 3537lldb::IOHandlerSP3538CommandInterpreter::GetIOHandler(bool force_create,3539                                 CommandInterpreterRunOptions *options) {3540  // Always re-create the IOHandlerEditline in case the input changed. The old3541  // instance might have had a non-interactive input and now it does or vice3542  // versa.3543  if (force_create || !m_command_io_handler_sp) {3544    // Always re-create the IOHandlerEditline in case the input changed. The3545    // old instance might have had a non-interactive input and now it does or3546    // vice versa.3547    uint32_t flags = 0;3548 3549    if (options) {3550      if (options->m_stop_on_continue == eLazyBoolYes)3551        flags |= eHandleCommandFlagStopOnContinue;3552      if (options->m_stop_on_error == eLazyBoolYes)3553        flags |= eHandleCommandFlagStopOnError;3554      if (options->m_stop_on_crash == eLazyBoolYes)3555        flags |= eHandleCommandFlagStopOnCrash;3556      if (options->m_echo_commands != eLazyBoolNo)3557        flags |= eHandleCommandFlagEchoCommand;3558      if (options->m_echo_comment_commands != eLazyBoolNo)3559        flags |= eHandleCommandFlagEchoCommentCommand;3560      if (options->m_print_results != eLazyBoolNo)3561        flags |= eHandleCommandFlagPrintResult;3562      if (options->m_print_errors != eLazyBoolNo)3563        flags |= eHandleCommandFlagPrintErrors;3564      if (options->m_allow_repeats == eLazyBoolYes)3565        flags |= eHandleCommandFlagAllowRepeats;3566    } else {3567      flags = eHandleCommandFlagEchoCommand | eHandleCommandFlagPrintResult |3568              eHandleCommandFlagPrintErrors;3569    }3570 3571    m_command_io_handler_sp = std::make_shared<IOHandlerEditline>(3572        m_debugger, IOHandler::Type::CommandInterpreter,3573        m_debugger.GetInputFileSP(), m_debugger.GetOutputStreamSP(),3574        m_debugger.GetErrorStreamSP(), flags, "lldb", m_debugger.GetPrompt(),3575        llvm::StringRef(), // Continuation prompt3576        false, // Don't enable multiple line input, just single line commands3577        m_debugger.GetUseColor(),3578        0,      // Don't show line numbers3579        *this); // IOHandlerDelegate3580  }3581  return m_command_io_handler_sp;3582}3583 3584CommandInterpreterRunResult CommandInterpreter::RunCommandInterpreter(3585    CommandInterpreterRunOptions &options) {3586  // Always re-create the command interpreter when we run it in case any file3587  // handles have changed.3588  bool force_create = true;3589  m_debugger.RunIOHandlerAsync(GetIOHandler(force_create, &options));3590  m_result = CommandInterpreterRunResult();3591 3592  if (options.GetAutoHandleEvents())3593    m_debugger.StartEventHandlerThread();3594 3595  if (options.GetSpawnThread()) {3596    m_debugger.StartIOHandlerThread();3597  } else {3598    // If the current thread is not managed by a host thread, we won't detect3599    // that this IS the CommandInterpreter IOHandler thread, so make it so:3600    HostThread new_io_handler_thread(Host::GetCurrentThread());3601    HostThread old_io_handler_thread =3602        m_debugger.SetIOHandlerThread(new_io_handler_thread);3603    m_debugger.RunIOHandlers();3604    m_debugger.SetIOHandlerThread(old_io_handler_thread);3605 3606    if (options.GetAutoHandleEvents())3607      m_debugger.StopEventHandlerThread();3608  }3609 3610  return m_result;3611}3612 3613CommandObject *3614CommandInterpreter::ResolveCommandImpl(std::string &command_line,3615                                       CommandReturnObject &result) {3616  std::string scratch_command(command_line); // working copy so we don't modify3617                                             // command_line unless we succeed3618  CommandObject *cmd_obj = nullptr;3619  StreamString revised_command_line;3620  bool wants_raw_input = false;3621  std::string next_word;3622  StringList matches;3623  bool done = false;3624 3625  auto build_alias_cmd = [&](std::string &full_name) {3626    revised_command_line.Clear();3627    matches.Clear();3628    std::string alias_result;3629    cmd_obj =3630        BuildAliasResult(full_name, scratch_command, alias_result, result);3631    revised_command_line.Printf("%s", alias_result.c_str());3632    if (cmd_obj) {3633      wants_raw_input = cmd_obj->WantsRawCommandString();3634    }3635  };3636 3637  while (!done) {3638    char quote_char = '\0';3639    std::string suffix;3640    ExtractCommand(scratch_command, next_word, suffix, quote_char);3641    if (cmd_obj == nullptr) {3642      std::string full_name;3643      bool is_alias = GetAliasFullName(next_word, full_name);3644      cmd_obj = GetCommandObject(next_word, &matches);3645      bool is_real_command =3646          (!is_alias) || (cmd_obj != nullptr && !cmd_obj->IsAlias());3647      if (!is_real_command) {3648        build_alias_cmd(full_name);3649      } else {3650        if (cmd_obj) {3651          llvm::StringRef cmd_name = cmd_obj->GetCommandName();3652          revised_command_line.Printf("%s", cmd_name.str().c_str());3653          wants_raw_input = cmd_obj->WantsRawCommandString();3654        } else {3655          revised_command_line.Printf("%s", next_word.c_str());3656        }3657      }3658    } else {3659      if (cmd_obj->IsMultiwordObject()) {3660        CommandObject *sub_cmd_obj =3661            cmd_obj->GetSubcommandObject(next_word.c_str());3662        if (sub_cmd_obj) {3663          // The subcommand's name includes the parent command's name, so3664          // restart rather than append to the revised_command_line.3665          llvm::StringRef sub_cmd_name = sub_cmd_obj->GetCommandName();3666          revised_command_line.Clear();3667          revised_command_line.Printf("%s", sub_cmd_name.str().c_str());3668          cmd_obj = sub_cmd_obj;3669          wants_raw_input = cmd_obj->WantsRawCommandString();3670        } else {3671          if (quote_char)3672            revised_command_line.Printf(" %c%s%s%c", quote_char,3673                                        next_word.c_str(), suffix.c_str(),3674                                        quote_char);3675          else3676            revised_command_line.Printf(" %s%s", next_word.c_str(),3677                                        suffix.c_str());3678          done = true;3679        }3680      } else {3681        if (quote_char)3682          revised_command_line.Printf(" %c%s%s%c", quote_char,3683                                      next_word.c_str(), suffix.c_str(),3684                                      quote_char);3685        else3686          revised_command_line.Printf(" %s%s", next_word.c_str(),3687                                      suffix.c_str());3688        done = true;3689      }3690    }3691 3692    if (cmd_obj == nullptr) {3693      const size_t num_matches = matches.GetSize();3694      if (matches.GetSize() > 1) {3695        StringList alias_matches;3696        GetAliasCommandObject(next_word, &alias_matches);3697 3698        if (alias_matches.GetSize() == 1) {3699          std::string full_name;3700          GetAliasFullName(alias_matches.GetStringAtIndex(0), full_name);3701          build_alias_cmd(full_name);3702          done = static_cast<bool>(cmd_obj);3703        } else {3704          StreamString error_msg;3705          error_msg.Printf("Ambiguous command '%s'. Possible matches:\n",3706                           next_word.c_str());3707 3708          for (uint32_t i = 0; i < num_matches; ++i) {3709            error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i));3710          }3711          result.AppendRawError(error_msg.GetString());3712        }3713      } else {3714        // We didn't have only one match, otherwise we wouldn't get here.3715        lldbassert(num_matches == 0);3716        result.AppendErrorWithFormat("'%s' is not a valid command.\n",3717                                     next_word.c_str());3718      }3719      if (!done)3720        return nullptr;3721    }3722 3723    if (cmd_obj->IsMultiwordObject()) {3724      if (!suffix.empty()) {3725        result.AppendErrorWithFormat(3726            "command '%s' did not recognize '%s%s%s' as valid (subcommand "3727            "might be invalid).\n",3728            cmd_obj->GetCommandName().str().c_str(),3729            next_word.empty() ? "" : next_word.c_str(),3730            next_word.empty() ? " -- " : " ", suffix.c_str());3731        return nullptr;3732      }3733    } else {3734      // If we found a normal command, we are done3735      done = true;3736      if (!suffix.empty()) {3737        switch (suffix[0]) {3738        case '/':3739          // GDB format suffixes3740          {3741            Options *command_options = cmd_obj->GetOptions();3742            if (command_options &&3743                command_options->SupportsLongOption("gdb-format")) {3744              std::string gdb_format_option("--gdb-format=");3745              gdb_format_option += (suffix.c_str() + 1);3746 3747              std::string cmd = std::string(revised_command_line.GetString());3748              size_t arg_terminator_idx = FindArgumentTerminator(cmd);3749              if (arg_terminator_idx != std::string::npos) {3750                // Insert the gdb format option before the "--" that terminates3751                // options3752                gdb_format_option.append(1, ' ');3753                cmd.insert(arg_terminator_idx, gdb_format_option);3754                revised_command_line.Clear();3755                revised_command_line.PutCString(cmd);3756              } else3757                revised_command_line.Printf(" %s", gdb_format_option.c_str());3758 3759              if (wants_raw_input &&3760                  FindArgumentTerminator(cmd) == std::string::npos)3761                revised_command_line.PutCString(" --");3762            } else {3763              result.AppendErrorWithFormat(3764                  "the '%s' command doesn't support the --gdb-format option\n",3765                  cmd_obj->GetCommandName().str().c_str());3766              return nullptr;3767            }3768          }3769          break;3770 3771        default:3772          result.AppendErrorWithFormat(3773              "unknown command shorthand suffix: '%s'\n", suffix.c_str());3774          return nullptr;3775        }3776      }3777    }3778    if (scratch_command.empty())3779      done = true;3780  }3781 3782  if (!scratch_command.empty())3783    revised_command_line.Printf(" %s", scratch_command.c_str());3784 3785  if (cmd_obj != nullptr)3786    command_line = std::string(revised_command_line.GetString());3787 3788  return cmd_obj;3789}3790 3791llvm::json::Value CommandInterpreter::GetStatistics() {3792  llvm::json::Object stats;3793  for (const auto &command_usage : m_command_usages)3794    stats.try_emplace(command_usage.getKey(), command_usage.getValue());3795  return stats;3796}3797 3798const StructuredData::Array &CommandInterpreter::GetTranscript() const {3799  return m_transcript;3800}3801 3802void CommandInterpreter::SetPrintCallback(3803    CommandReturnObjectCallback callback) {3804  m_print_callback = callback;3805}3806