1 //===-- CommandAlias.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-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
19 using namespace lldb;
20 using namespace lldb_private;
21
ProcessAliasOptionsArgs(lldb::CommandObjectSP & cmd_obj_sp,llvm::StringRef options_args,OptionArgVectorSP & option_arg_vector_sp)22 static 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(false);
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 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(std::string("<argument>"), -1,
68 std::string(entry.ref()));
69 }
70 }
71 }
72
73 return success;
74 }
75
CommandAlias(CommandInterpreter & interpreter,lldb::CommandObjectSP cmd_sp,llvm::StringRef options_args,llvm::StringRef name,llvm::StringRef help,llvm::StringRef syntax,uint32_t flags)76 CommandAlias::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_option_string(std::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
WantsRawCommandString()106 bool CommandAlias::WantsRawCommandString() {
107 if (IsValid())
108 return m_underlying_command_sp->WantsRawCommandString();
109 return false;
110 }
111
WantsCompletion()112 bool CommandAlias::WantsCompletion() {
113 if (IsValid())
114 return m_underlying_command_sp->WantsCompletion();
115 return false;
116 }
117
HandleCompletion(CompletionRequest & request)118 void CommandAlias::HandleCompletion(CompletionRequest &request) {
119 if (IsValid())
120 m_underlying_command_sp->HandleCompletion(request);
121 }
122
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)123 void CommandAlias::HandleArgumentCompletion(
124 CompletionRequest &request, OptionElementVector &opt_element_vector) {
125 if (IsValid())
126 m_underlying_command_sp->HandleArgumentCompletion(request,
127 opt_element_vector);
128 }
129
GetOptions()130 Options *CommandAlias::GetOptions() {
131 if (IsValid())
132 return m_underlying_command_sp->GetOptions();
133 return nullptr;
134 }
135
Execute(const char * args_string,CommandReturnObject & result)136 bool CommandAlias::Execute(const char *args_string,
137 CommandReturnObject &result) {
138 llvm_unreachable("CommandAlias::Execute is not to be called");
139 }
140
GetAliasExpansion(StreamString & help_string) const141 void CommandAlias::GetAliasExpansion(StreamString &help_string) const {
142 llvm::StringRef command_name = m_underlying_command_sp->GetCommandName();
143 help_string.Printf("'%*s", (int)command_name.size(), command_name.data());
144
145 if (!m_option_args_sp) {
146 help_string.Printf("'");
147 return;
148 }
149
150 OptionArgVector *options = m_option_args_sp.get();
151 std::string opt;
152 std::string value;
153
154 for (const auto &opt_entry : *options) {
155 std::tie(opt, std::ignore, value) = opt_entry;
156 if (opt == "<argument>") {
157 help_string.Printf(" %s", value.c_str());
158 } else {
159 help_string.Printf(" %s", opt.c_str());
160 if ((value != "<no-argument>") && (value != "<need-argument")) {
161 help_string.Printf(" %s", value.c_str());
162 }
163 }
164 }
165
166 help_string.Printf("'");
167 }
168
IsDashDashCommand()169 bool CommandAlias::IsDashDashCommand() {
170 if (m_is_dashdash_alias != eLazyBoolCalculate)
171 return (m_is_dashdash_alias == eLazyBoolYes);
172 m_is_dashdash_alias = eLazyBoolNo;
173 if (!IsValid())
174 return false;
175
176 std::string opt;
177 std::string value;
178
179 for (const auto &opt_entry : *GetOptionArguments()) {
180 std::tie(opt, std::ignore, value) = opt_entry;
181 if (opt == "<argument>" && !value.empty() &&
182 llvm::StringRef(value).endswith("--")) {
183 m_is_dashdash_alias = eLazyBoolYes;
184 break;
185 }
186 }
187
188 // if this is a nested alias, it may be adding arguments on top of an already
189 // dash-dash alias
190 if ((m_is_dashdash_alias == eLazyBoolNo) && IsNestedAlias())
191 m_is_dashdash_alias =
192 (GetUnderlyingCommand()->IsDashDashCommand() ? eLazyBoolYes
193 : eLazyBoolNo);
194 return (m_is_dashdash_alias == eLazyBoolYes);
195 }
196
IsNestedAlias()197 bool CommandAlias::IsNestedAlias() {
198 if (GetUnderlyingCommand())
199 return GetUnderlyingCommand()->IsAlias();
200 return false;
201 }
202
Desugar()203 std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() {
204 auto underlying = GetUnderlyingCommand();
205 if (!underlying)
206 return {nullptr, nullptr};
207
208 if (underlying->IsAlias()) {
209 auto desugared = ((CommandAlias *)underlying.get())->Desugar();
210 auto options = GetOptionArguments();
211 options->insert(options->begin(), desugared.second->begin(),
212 desugared.second->end());
213 return {desugared.first, options};
214 }
215
216 return {underlying, GetOptionArguments()};
217 }
218
219 // allow CommandAlias objects to provide their own help, but fallback to the
220 // info for the underlying command if no customization has been provided
SetHelp(llvm::StringRef str)221 void CommandAlias::SetHelp(llvm::StringRef str) {
222 this->CommandObject::SetHelp(str);
223 m_did_set_help = true;
224 }
225
SetHelpLong(llvm::StringRef str)226 void CommandAlias::SetHelpLong(llvm::StringRef str) {
227 this->CommandObject::SetHelpLong(str);
228 m_did_set_help_long = true;
229 }
230
GetHelp()231 llvm::StringRef CommandAlias::GetHelp() {
232 if (!m_cmd_help_short.empty() || m_did_set_help)
233 return m_cmd_help_short;
234 if (IsValid())
235 return m_underlying_command_sp->GetHelp();
236 return llvm::StringRef();
237 }
238
GetHelpLong()239 llvm::StringRef CommandAlias::GetHelpLong() {
240 if (!m_cmd_help_long.empty() || m_did_set_help_long)
241 return m_cmd_help_long;
242 if (IsValid())
243 return m_underlying_command_sp->GetHelpLong();
244 return llvm::StringRef();
245 }
246