CommandAlias.cpp revision 344779
1//===-- CommandAlias.cpp -----------------------------------------*- C++-*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "lldb/Interpreter/CommandAlias.h"
11
12#include "llvm/Support/ErrorHandling.h"
13
14#include "lldb/Interpreter/CommandInterpreter.h"
15#include "lldb/Interpreter/CommandObject.h"
16#include "lldb/Interpreter/CommandReturnObject.h"
17#include "lldb/Interpreter/Options.h"
18#include "lldb/Utility/StreamString.h"
19
20using namespace lldb;
21using namespace lldb_private;
22
23static bool ProcessAliasOptionsArgs(lldb::CommandObjectSP &cmd_obj_sp,
24                                    llvm::StringRef options_args,
25                                    OptionArgVectorSP &option_arg_vector_sp) {
26  bool success = true;
27  OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
28
29  if (options_args.size() < 1)
30    return true;
31
32  Args args(options_args);
33  std::string options_string(options_args);
34  CommandReturnObject result;
35  // Check to see if the command being aliased can take any command options.
36  Options *options = cmd_obj_sp->GetOptions();
37  if (options) {
38    // See if any options were specified as part of the alias;  if so, handle
39    // them appropriately.
40    ExecutionContext exe_ctx =
41        cmd_obj_sp->GetCommandInterpreter().GetExecutionContext();
42    options->NotifyOptionParsingStarting(&exe_ctx);
43
44    llvm::Expected<Args> args_or =
45        options->ParseAlias(args, option_arg_vector, options_string);
46    if (!args_or) {
47      result.AppendError(toString(args_or.takeError()));
48      result.AppendError("Unable to create requested alias.\n");
49      result.SetStatus(eReturnStatusFailed);
50      return false;
51    }
52    args = std::move(*args_or);
53    options->VerifyPartialOptions(result);
54    if (!result.Succeeded() &&
55        result.GetStatus() != lldb::eReturnStatusStarted) {
56      result.AppendError("Unable to create requested alias.\n");
57      return false;
58    }
59  }
60
61  if (!options_string.empty()) {
62    if (cmd_obj_sp->WantsRawCommandString())
63      option_arg_vector->emplace_back("<argument>", -1, options_string);
64    else {
65      for (auto &entry : args.entries()) {
66        if (!entry.ref.empty())
67          option_arg_vector->emplace_back("<argument>", -1, entry.ref);
68      }
69    }
70  }
71
72  return success;
73}
74
75CommandAlias::CommandAlias(CommandInterpreter &interpreter,
76                           lldb::CommandObjectSP cmd_sp,
77                           llvm::StringRef options_args, llvm::StringRef name,
78                           llvm::StringRef help, llvm::StringRef syntax,
79                           uint32_t flags)
80    : CommandObject(interpreter, name, help, syntax, flags),
81      m_underlying_command_sp(), m_option_string(options_args),
82      m_option_args_sp(new OptionArgVector),
83      m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false),
84      m_did_set_help_long(false) {
85  if (ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp)) {
86    m_underlying_command_sp = cmd_sp;
87    for (int i = 0;
88         auto cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i);
89         i++) {
90      m_arguments.push_back(*cmd_entry);
91    }
92    if (!help.empty()) {
93      StreamString sstr;
94      StreamString translation_and_help;
95      GetAliasExpansion(sstr);
96
97      translation_and_help.Printf(
98          "(%s)  %s", sstr.GetData(),
99          GetUnderlyingCommand()->GetHelp().str().c_str());
100      SetHelp(translation_and_help.GetString());
101    }
102  }
103}
104
105bool CommandAlias::WantsRawCommandString() {
106  if (IsValid())
107    return m_underlying_command_sp->WantsRawCommandString();
108  return false;
109}
110
111bool CommandAlias::WantsCompletion() {
112  if (IsValid())
113    return m_underlying_command_sp->WantsCompletion();
114  return false;
115}
116
117int CommandAlias::HandleCompletion(CompletionRequest &request) {
118  if (IsValid())
119    return m_underlying_command_sp->HandleCompletion(request);
120  return -1;
121}
122
123int CommandAlias::HandleArgumentCompletion(
124    CompletionRequest &request, OptionElementVector &opt_element_vector) {
125  if (IsValid())
126    return m_underlying_command_sp->HandleArgumentCompletion(
127        request, opt_element_vector);
128  return -1;
129}
130
131Options *CommandAlias::GetOptions() {
132  if (IsValid())
133    return m_underlying_command_sp->GetOptions();
134  return nullptr;
135}
136
137bool CommandAlias::Execute(const char *args_string,
138                           CommandReturnObject &result) {
139  llvm_unreachable("CommandAlias::Execute is not to be called");
140}
141
142void CommandAlias::GetAliasExpansion(StreamString &help_string) const {
143  llvm::StringRef command_name = m_underlying_command_sp->GetCommandName();
144  help_string.Printf("'%*s", (int)command_name.size(), command_name.data());
145
146  if (!m_option_args_sp) {
147    help_string.Printf("'");
148    return;
149  }
150
151  OptionArgVector *options = m_option_args_sp.get();
152  std::string opt;
153  std::string value;
154
155  for (const auto &opt_entry : *options) {
156    std::tie(opt, std::ignore, value) = opt_entry;
157    if (opt == "<argument>") {
158      help_string.Printf(" %s", value.c_str());
159    } else {
160      help_string.Printf(" %s", opt.c_str());
161      if ((value != "<no-argument>") && (value != "<need-argument")) {
162        help_string.Printf(" %s", value.c_str());
163      }
164    }
165  }
166
167  help_string.Printf("'");
168}
169
170bool CommandAlias::IsDashDashCommand() {
171  if (m_is_dashdash_alias != eLazyBoolCalculate)
172    return (m_is_dashdash_alias == eLazyBoolYes);
173  m_is_dashdash_alias = eLazyBoolNo;
174  if (!IsValid())
175    return false;
176
177  std::string opt;
178  std::string value;
179
180  for (const auto &opt_entry : *GetOptionArguments()) {
181    std::tie(opt, std::ignore, value) = opt_entry;
182    if (opt == "<argument>" && !value.empty() &&
183        llvm::StringRef(value).endswith("--")) {
184      m_is_dashdash_alias = eLazyBoolYes;
185      break;
186    }
187  }
188
189  // if this is a nested alias, it may be adding arguments on top of an already
190  // dash-dash alias
191  if ((m_is_dashdash_alias == eLazyBoolNo) && IsNestedAlias())
192    m_is_dashdash_alias =
193        (GetUnderlyingCommand()->IsDashDashCommand() ? eLazyBoolYes
194                                                     : eLazyBoolNo);
195  return (m_is_dashdash_alias == eLazyBoolYes);
196}
197
198bool CommandAlias::IsNestedAlias() {
199  if (GetUnderlyingCommand())
200    return GetUnderlyingCommand()->IsAlias();
201  return false;
202}
203
204std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() {
205  auto underlying = GetUnderlyingCommand();
206  if (!underlying)
207    return {nullptr, nullptr};
208
209  if (underlying->IsAlias()) {
210    auto desugared = ((CommandAlias *)underlying.get())->Desugar();
211    auto options = GetOptionArguments();
212    options->insert(options->begin(), desugared.second->begin(),
213                    desugared.second->end());
214    return {desugared.first, options};
215  }
216
217  return {underlying, GetOptionArguments()};
218}
219
220// allow CommandAlias objects to provide their own help, but fallback to the
221// info for the underlying command if no customization has been provided
222void CommandAlias::SetHelp(llvm::StringRef str) {
223  this->CommandObject::SetHelp(str);
224  m_did_set_help = true;
225}
226
227void CommandAlias::SetHelpLong(llvm::StringRef str) {
228  this->CommandObject::SetHelpLong(str);
229  m_did_set_help_long = true;
230}
231
232llvm::StringRef CommandAlias::GetHelp() {
233  if (!m_cmd_help_short.empty() || m_did_set_help)
234    return m_cmd_help_short;
235  if (IsValid())
236    return m_underlying_command_sp->GetHelp();
237  return llvm::StringRef();
238}
239
240llvm::StringRef CommandAlias::GetHelpLong() {
241  if (!m_cmd_help_long.empty() || m_did_set_help_long)
242    return m_cmd_help_long;
243  if (IsValid())
244    return m_underlying_command_sp->GetHelpLong();
245  return llvm::StringRef();
246}
247