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/ADT/STLExtras.h" |
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 | |
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 | // TODO: Find a way to propagate errors in this CommandReturnObject up the |
35 | // stack. |
36 | CommandReturnObject result(false); |
37 | // Check to see if the command being aliased can take any command options. |
38 | Options *options = cmd_obj_sp->GetOptions(); |
39 | if (options) { |
40 | // See if any options were specified as part of the alias; if so, handle |
41 | // them appropriately. |
42 | ExecutionContext exe_ctx = |
43 | cmd_obj_sp->GetCommandInterpreter().GetExecutionContext(); |
44 | options->NotifyOptionParsingStarting(execution_context: &exe_ctx); |
45 | |
46 | llvm::Expected<Args> args_or = |
47 | options->ParseAlias(args, option_arg_vector, input_line&: options_string); |
48 | if (!args_or) { |
49 | result.AppendError(in_string: toString(E: args_or.takeError())); |
50 | result.AppendError(in_string: "Unable to create requested alias.\n" ); |
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(in_string: "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(args&: CommandInterpreter::g_argument, |
65 | args: -1, args&: options_string); |
66 | else { |
67 | for (auto &entry : args.entries()) { |
68 | if (!entry.ref().empty()) |
69 | option_arg_vector->emplace_back(args: std::string(CommandInterpreter::g_argument), args: -1, |
70 | args: std::string(entry.ref())); |
71 | } |
72 | } |
73 | } |
74 | |
75 | return success; |
76 | } |
77 | |
78 | CommandAlias::CommandAlias(CommandInterpreter &interpreter, |
79 | lldb::CommandObjectSP cmd_sp, |
80 | llvm::StringRef options_args, llvm::StringRef name, |
81 | llvm::StringRef help, llvm::StringRef syntax, |
82 | uint32_t flags) |
83 | : CommandObject(interpreter, name, help, syntax, flags), |
84 | m_option_string(std::string(options_args)), |
85 | m_option_args_sp(new OptionArgVector), |
86 | m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false), |
87 | m_did_set_help_long(false) { |
88 | if (ProcessAliasOptionsArgs(cmd_obj_sp&: cmd_sp, options_args, option_arg_vector_sp&: m_option_args_sp)) { |
89 | m_underlying_command_sp = cmd_sp; |
90 | for (int i = 0; |
91 | auto cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(idx: i); |
92 | i++) { |
93 | m_arguments.push_back(x: *cmd_entry); |
94 | } |
95 | if (!help.empty()) { |
96 | StreamString sstr; |
97 | StreamString translation_and_help; |
98 | GetAliasExpansion(help_string&: sstr); |
99 | |
100 | translation_and_help.Printf( |
101 | format: "(%s) %s" , sstr.GetData(), |
102 | GetUnderlyingCommand()->GetHelp().str().c_str()); |
103 | SetHelp(translation_and_help.GetString()); |
104 | } |
105 | } |
106 | } |
107 | |
108 | bool CommandAlias::WantsRawCommandString() { |
109 | if (IsValid()) |
110 | return m_underlying_command_sp->WantsRawCommandString(); |
111 | return false; |
112 | } |
113 | |
114 | bool CommandAlias::WantsCompletion() { |
115 | if (IsValid()) |
116 | return m_underlying_command_sp->WantsCompletion(); |
117 | return false; |
118 | } |
119 | |
120 | void CommandAlias::HandleCompletion(CompletionRequest &request) { |
121 | if (IsValid()) |
122 | m_underlying_command_sp->HandleCompletion(request); |
123 | } |
124 | |
125 | void CommandAlias::HandleArgumentCompletion( |
126 | CompletionRequest &request, OptionElementVector &opt_element_vector) { |
127 | if (IsValid()) |
128 | m_underlying_command_sp->HandleArgumentCompletion(request, |
129 | opt_element_vector); |
130 | } |
131 | |
132 | Options *CommandAlias::GetOptions() { |
133 | if (IsValid()) |
134 | return m_underlying_command_sp->GetOptions(); |
135 | return nullptr; |
136 | } |
137 | |
138 | void CommandAlias::Execute(const char *args_string, |
139 | CommandReturnObject &result) { |
140 | llvm_unreachable("CommandAlias::Execute is not to be called" ); |
141 | } |
142 | |
143 | void CommandAlias::GetAliasExpansion(StreamString &help_string) const { |
144 | llvm::StringRef command_name = m_underlying_command_sp->GetCommandName(); |
145 | help_string.Printf(format: "'%*s" , (int)command_name.size(), command_name.data()); |
146 | |
147 | if (!m_option_args_sp) { |
148 | help_string.Printf(format: "'" ); |
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(args&: opt, args: std::ignore, args&: value) = opt_entry; |
158 | if (opt == CommandInterpreter::g_argument) { |
159 | help_string.Printf(format: " %s" , value.c_str()); |
160 | } else { |
161 | help_string.Printf(format: " %s" , opt.c_str()); |
162 | if ((value != CommandInterpreter::g_no_argument) |
163 | && (value != CommandInterpreter::g_need_argument)) { |
164 | help_string.Printf(format: " %s" , value.c_str()); |
165 | } |
166 | } |
167 | } |
168 | |
169 | help_string.Printf(format: "'" ); |
170 | } |
171 | |
172 | bool CommandAlias::IsDashDashCommand() { |
173 | if (m_is_dashdash_alias != eLazyBoolCalculate) |
174 | return (m_is_dashdash_alias == eLazyBoolYes); |
175 | m_is_dashdash_alias = eLazyBoolNo; |
176 | if (!IsValid()) |
177 | return false; |
178 | |
179 | std::string opt; |
180 | std::string value; |
181 | |
182 | for (const auto &opt_entry : *GetOptionArguments()) { |
183 | std::tie(args&: opt, args: std::ignore, args&: value) = opt_entry; |
184 | if (opt == CommandInterpreter::g_argument && !value.empty() && |
185 | llvm::StringRef(value).ends_with(Suffix: "--" )) { |
186 | m_is_dashdash_alias = eLazyBoolYes; |
187 | break; |
188 | } |
189 | } |
190 | |
191 | // if this is a nested alias, it may be adding arguments on top of an already |
192 | // dash-dash alias |
193 | if ((m_is_dashdash_alias == eLazyBoolNo) && IsNestedAlias()) |
194 | m_is_dashdash_alias = |
195 | (GetUnderlyingCommand()->IsDashDashCommand() ? eLazyBoolYes |
196 | : eLazyBoolNo); |
197 | return (m_is_dashdash_alias == eLazyBoolYes); |
198 | } |
199 | |
200 | bool CommandAlias::IsNestedAlias() { |
201 | if (GetUnderlyingCommand()) |
202 | return GetUnderlyingCommand()->IsAlias(); |
203 | return false; |
204 | } |
205 | |
206 | std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() { |
207 | auto underlying = GetUnderlyingCommand(); |
208 | if (!underlying) |
209 | return {nullptr, nullptr}; |
210 | |
211 | if (underlying->IsAlias()) { |
212 | // FIXME: This doesn't work if the original alias fills a slot in the |
213 | // underlying alias, since this just appends the two lists. |
214 | auto desugared = ((CommandAlias *)underlying.get())->Desugar(); |
215 | OptionArgVectorSP options = std::make_shared<OptionArgVector>(); |
216 | llvm::append_range(C&: *options, R&: *desugared.second); |
217 | llvm::append_range(C&: *options, R&: *GetOptionArguments()); |
218 | return {desugared.first, options}; |
219 | } |
220 | |
221 | return {underlying, GetOptionArguments()}; |
222 | } |
223 | |
224 | // allow CommandAlias objects to provide their own help, but fallback to the |
225 | // info for the underlying command if no customization has been provided |
226 | void CommandAlias::SetHelp(llvm::StringRef str) { |
227 | this->CommandObject::SetHelp(str); |
228 | m_did_set_help = true; |
229 | } |
230 | |
231 | void CommandAlias::SetHelpLong(llvm::StringRef str) { |
232 | this->CommandObject::SetHelpLong(str); |
233 | m_did_set_help_long = true; |
234 | } |
235 | |
236 | llvm::StringRef CommandAlias::GetHelp() { |
237 | if (!m_cmd_help_short.empty() || m_did_set_help) |
238 | return m_cmd_help_short; |
239 | if (IsValid()) |
240 | return m_underlying_command_sp->GetHelp(); |
241 | return llvm::StringRef(); |
242 | } |
243 | |
244 | llvm::StringRef CommandAlias::GetHelpLong() { |
245 | if (!m_cmd_help_long.empty() || m_did_set_help_long) |
246 | return m_cmd_help_long; |
247 | if (IsValid()) |
248 | return m_underlying_command_sp->GetHelpLong(); |
249 | return llvm::StringRef(); |
250 | } |
251 | |