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
20 using namespace lldb;
21 using namespace lldb_private;
22
ProcessAliasOptionsArgs(lldb::CommandObjectSP & cmd_obj_sp,llvm::StringRef options_args,OptionArgVectorSP & option_arg_vector_sp)23 static 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
CommandAlias(CommandInterpreter & interpreter,lldb::CommandObjectSP cmd_sp,llvm::StringRef options_args,llvm::StringRef name,llvm::StringRef help,llvm::StringRef syntax,uint32_t flags)75 CommandAlias::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
WantsRawCommandString()105 bool CommandAlias::WantsRawCommandString() {
106 if (IsValid())
107 return m_underlying_command_sp->WantsRawCommandString();
108 return false;
109 }
110
WantsCompletion()111 bool CommandAlias::WantsCompletion() {
112 if (IsValid())
113 return m_underlying_command_sp->WantsCompletion();
114 return false;
115 }
116
HandleCompletion(CompletionRequest & request)117 int CommandAlias::HandleCompletion(CompletionRequest &request) {
118 if (IsValid())
119 return m_underlying_command_sp->HandleCompletion(request);
120 return -1;
121 }
122
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)123 int 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
GetOptions()131 Options *CommandAlias::GetOptions() {
132 if (IsValid())
133 return m_underlying_command_sp->GetOptions();
134 return nullptr;
135 }
136
Execute(const char * args_string,CommandReturnObject & result)137 bool CommandAlias::Execute(const char *args_string,
138 CommandReturnObject &result) {
139 llvm_unreachable("CommandAlias::Execute is not to be called");
140 }
141
GetAliasExpansion(StreamString & help_string) const142 void 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
IsDashDashCommand()170 bool 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
IsNestedAlias()198 bool CommandAlias::IsNestedAlias() {
199 if (GetUnderlyingCommand())
200 return GetUnderlyingCommand()->IsAlias();
201 return false;
202 }
203
Desugar()204 std::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
SetHelp(llvm::StringRef str)222 void CommandAlias::SetHelp(llvm::StringRef str) {
223 this->CommandObject::SetHelp(str);
224 m_did_set_help = true;
225 }
226
SetHelpLong(llvm::StringRef str)227 void CommandAlias::SetHelpLong(llvm::StringRef str) {
228 this->CommandObject::SetHelpLong(str);
229 m_did_set_help_long = true;
230 }
231
GetHelp()232 llvm::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
GetHelpLong()240 llvm::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