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