CommandAlias.cpp revision 321369
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    args.Unshift(llvm::StringRef("dummy_arg"));
44    options_string = args.ParseAliasOptions(*options, result, option_arg_vector,
45                                            options_args);
46    args.Shift();
47    if (result.Succeeded())
48      options->VerifyPartialOptions(result);
49    if (!result.Succeeded() &&
50        result.GetStatus() != lldb::eReturnStatusStarted) {
51      result.AppendError("Unable to create requested alias.\n");
52      return false;
53    }
54  }
55
56  if (!options_string.empty()) {
57    if (cmd_obj_sp->WantsRawCommandString())
58      option_arg_vector->emplace_back("<argument>", -1, options_string);
59    else {
60      for (auto &entry : args.entries()) {
61        if (!entry.ref.empty())
62          option_arg_vector->emplace_back("<argument>", -1, entry.ref);
63      }
64    }
65  }
66
67  return success;
68}
69
70CommandAlias::CommandAlias(CommandInterpreter &interpreter,
71                           lldb::CommandObjectSP cmd_sp,
72                           llvm::StringRef options_args, llvm::StringRef name,
73                           llvm::StringRef help, llvm::StringRef syntax,
74                           uint32_t flags)
75    : CommandObject(interpreter, name, help, syntax, flags),
76      m_underlying_command_sp(), m_option_string(options_args),
77      m_option_args_sp(new OptionArgVector),
78      m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false),
79      m_did_set_help_long(false) {
80  if (ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp)) {
81    m_underlying_command_sp = cmd_sp;
82    for (int i = 0;
83         auto cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i);
84         i++) {
85      m_arguments.push_back(*cmd_entry);
86    }
87    if (!help.empty()) {
88      StreamString sstr;
89      StreamString translation_and_help;
90      GetAliasExpansion(sstr);
91
92      translation_and_help.Printf(
93          "(%s)  %s", sstr.GetData(),
94          GetUnderlyingCommand()->GetHelp().str().c_str());
95      SetHelp(translation_and_help.GetString());
96    }
97  }
98}
99
100bool CommandAlias::WantsRawCommandString() {
101  if (IsValid())
102    return m_underlying_command_sp->WantsRawCommandString();
103  return false;
104}
105
106bool CommandAlias::WantsCompletion() {
107  if (IsValid())
108    return m_underlying_command_sp->WantsCompletion();
109  return false;
110}
111
112int CommandAlias::HandleCompletion(Args &input, int &cursor_index,
113                                   int &cursor_char_position,
114                                   int match_start_point,
115                                   int max_return_elements, bool &word_complete,
116                                   StringList &matches) {
117  if (IsValid())
118    return m_underlying_command_sp->HandleCompletion(
119        input, cursor_index, cursor_char_position, match_start_point,
120        max_return_elements, word_complete, matches);
121  return -1;
122}
123
124int CommandAlias::HandleArgumentCompletion(
125    Args &input, int &cursor_index, int &cursor_char_position,
126    OptionElementVector &opt_element_vector, int match_start_point,
127    int max_return_elements, bool &word_complete, StringList &matches) {
128  if (IsValid())
129    return m_underlying_command_sp->HandleArgumentCompletion(
130        input, cursor_index, cursor_char_position, opt_element_vector,
131        match_start_point, max_return_elements, word_complete, matches);
132  return -1;
133}
134
135Options *CommandAlias::GetOptions() {
136  if (IsValid())
137    return m_underlying_command_sp->GetOptions();
138  return nullptr;
139}
140
141bool CommandAlias::Execute(const char *args_string,
142                           CommandReturnObject &result) {
143  llvm_unreachable("CommandAlias::Execute is not to be called");
144}
145
146void CommandAlias::GetAliasExpansion(StreamString &help_string) const {
147  llvm::StringRef command_name = m_underlying_command_sp->GetCommandName();
148  help_string.Printf("'%*s", (int)command_name.size(), command_name.data());
149
150  if (!m_option_args_sp) {
151    help_string.Printf("'");
152    return;
153  }
154
155  OptionArgVector *options = m_option_args_sp.get();
156  std::string opt;
157  std::string value;
158
159  for (const auto &opt_entry : *options) {
160    std::tie(opt, std::ignore, value) = opt_entry;
161    if (opt == "<argument>") {
162      help_string.Printf(" %s", value.c_str());
163    } else {
164      help_string.Printf(" %s", opt.c_str());
165      if ((value.compare("<no-argument>") != 0) &&
166          (value.compare("<need-argument") != 0)) {
167        help_string.Printf(" %s", value.c_str());
168      }
169    }
170  }
171
172  help_string.Printf("'");
173}
174
175bool CommandAlias::IsDashDashCommand() {
176  if (m_is_dashdash_alias != eLazyBoolCalculate)
177    return (m_is_dashdash_alias == eLazyBoolYes);
178  m_is_dashdash_alias = eLazyBoolNo;
179  if (!IsValid())
180    return false;
181
182  std::string opt;
183  std::string value;
184
185  for (const auto &opt_entry : *GetOptionArguments()) {
186    std::tie(opt, std::ignore, value) = opt_entry;
187    if (opt == "<argument>" && !value.empty() &&
188        llvm::StringRef(value).endswith("--")) {
189      m_is_dashdash_alias = eLazyBoolYes;
190      break;
191    }
192  }
193
194  // if this is a nested alias, it may be adding arguments on top of an
195  // already dash-dash alias
196  if ((m_is_dashdash_alias == eLazyBoolNo) && IsNestedAlias())
197    m_is_dashdash_alias =
198        (GetUnderlyingCommand()->IsDashDashCommand() ? eLazyBoolYes
199                                                     : eLazyBoolNo);
200  return (m_is_dashdash_alias == eLazyBoolYes);
201}
202
203bool CommandAlias::IsNestedAlias() {
204  if (GetUnderlyingCommand())
205    return GetUnderlyingCommand()->IsAlias();
206  return false;
207}
208
209std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() {
210  auto underlying = GetUnderlyingCommand();
211  if (!underlying)
212    return {nullptr, nullptr};
213
214  if (underlying->IsAlias()) {
215    auto desugared = ((CommandAlias *)underlying.get())->Desugar();
216    auto options = GetOptionArguments();
217    options->insert(options->begin(), desugared.second->begin(),
218                    desugared.second->end());
219    return {desugared.first, options};
220  }
221
222  return {underlying, GetOptionArguments()};
223}
224
225// allow CommandAlias objects to provide their own help, but fallback to the
226// info
227// for the underlying command if no customization has been provided
228void CommandAlias::SetHelp(llvm::StringRef str) {
229  this->CommandObject::SetHelp(str);
230  m_did_set_help = true;
231}
232
233void CommandAlias::SetHelpLong(llvm::StringRef str) {
234  this->CommandObject::SetHelpLong(str);
235  m_did_set_help_long = true;
236}
237
238llvm::StringRef CommandAlias::GetHelp() {
239  if (!m_cmd_help_short.empty() || m_did_set_help)
240    return m_cmd_help_short;
241  if (IsValid())
242    return m_underlying_command_sp->GetHelp();
243  return llvm::StringRef();
244}
245
246llvm::StringRef CommandAlias::GetHelpLong() {
247  if (!m_cmd_help_long.empty() || m_did_set_help_long)
248    return m_cmd_help_long;
249  if (IsValid())
250    return m_underlying_command_sp->GetHelpLong();
251  return llvm::StringRef();
252}
253