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 | #include "llvm/Support/FormatAdapters.h" |
14 | |
15 | #include "lldb/Interpreter/CommandInterpreter.h" |
16 | #include "lldb/Interpreter/CommandObject.h" |
17 | #include "lldb/Interpreter/CommandReturnObject.h" |
18 | #include "lldb/Interpreter/Options.h" |
19 | #include "lldb/Utility/StreamString.h" |
20 | |
21 | using namespace lldb; |
22 | using namespace lldb_private; |
23 | |
24 | static llvm::Error |
25 | ProcessAliasOptionsArgs(lldb::CommandObjectSP &cmd_obj_sp, |
26 | llvm::StringRef options_args, |
27 | OptionArgVectorSP &option_arg_vector_sp) { |
28 | OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); |
29 | |
30 | if (options_args.size() < 1) |
31 | return llvm::Error::success(); |
32 | |
33 | Args args(options_args); |
34 | std::string options_string(options_args); |
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(execution_context: &exe_ctx); |
43 | |
44 | llvm::Expected<Args> args_or = |
45 | options->ParseAlias(args, option_arg_vector, input_line&: options_string); |
46 | if (!args_or) |
47 | return llvm::createStringError( |
48 | S: llvm::formatv(Fmt: "unable to create alias: {0}" , |
49 | Vals: llvm::fmt_consume(Item: args_or.takeError()))); |
50 | args = std::move(*args_or); |
51 | if (llvm::Error error = options->VerifyPartialOptions()) |
52 | return error; |
53 | } |
54 | |
55 | if (!options_string.empty()) { |
56 | if (cmd_obj_sp->WantsRawCommandString()) { |
57 | option_arg_vector->emplace_back(args&: CommandInterpreter::g_argument, args: -1, |
58 | args&: options_string); |
59 | } else { |
60 | for (auto &entry : args.entries()) { |
61 | if (!entry.ref().empty()) |
62 | option_arg_vector->emplace_back( |
63 | args: std::string(CommandInterpreter::g_argument), args: -1, |
64 | args: std::string(entry.ref())); |
65 | } |
66 | } |
67 | } |
68 | |
69 | return llvm::Error::success(); |
70 | } |
71 | |
72 | CommandAlias::CommandAlias(CommandInterpreter &interpreter, |
73 | lldb::CommandObjectSP cmd_sp, |
74 | llvm::StringRef options_args, llvm::StringRef name, |
75 | llvm::StringRef help, llvm::StringRef syntax, |
76 | uint32_t flags) |
77 | : CommandObject(interpreter, name, help, syntax, flags), |
78 | m_option_string(std::string(options_args)), |
79 | m_option_args_sp(new OptionArgVector), |
80 | m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false), |
81 | m_did_set_help_long(false) { |
82 | if (llvm::Error error = |
83 | ProcessAliasOptionsArgs(cmd_obj_sp&: cmd_sp, options_args, option_arg_vector_sp&: m_option_args_sp)) { |
84 | // FIXME: Find a way to percolate this error up. |
85 | LLDB_LOG_ERROR(GetLog(LLDBLog::Host), std::move(error), |
86 | "ProcessAliasOptionsArgs failed: {0}" ); |
87 | } else { |
88 | m_underlying_command_sp = cmd_sp; |
89 | for (int i = 0; |
90 | auto *cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(idx: i); |
91 | i++) { |
92 | m_arguments.push_back(x: *cmd_entry); |
93 | } |
94 | if (!help.empty()) { |
95 | StreamString sstr; |
96 | StreamString translation_and_help; |
97 | GetAliasExpansion(help_string&: sstr); |
98 | |
99 | translation_and_help.Printf( |
100 | format: "(%s) %s" , sstr.GetData(), |
101 | GetUnderlyingCommand()->GetHelp().str().c_str()); |
102 | SetHelp(translation_and_help.GetString()); |
103 | } |
104 | } |
105 | } |
106 | |
107 | bool CommandAlias::WantsRawCommandString() { |
108 | if (IsValid()) |
109 | return m_underlying_command_sp->WantsRawCommandString(); |
110 | return false; |
111 | } |
112 | |
113 | bool CommandAlias::WantsCompletion() { |
114 | if (IsValid()) |
115 | return m_underlying_command_sp->WantsCompletion(); |
116 | return false; |
117 | } |
118 | |
119 | void CommandAlias::HandleCompletion(CompletionRequest &request) { |
120 | if (IsValid()) |
121 | m_underlying_command_sp->HandleCompletion(request); |
122 | } |
123 | |
124 | void CommandAlias::HandleArgumentCompletion( |
125 | CompletionRequest &request, OptionElementVector &opt_element_vector) { |
126 | if (IsValid()) |
127 | m_underlying_command_sp->HandleArgumentCompletion(request, |
128 | opt_element_vector); |
129 | } |
130 | |
131 | Options *CommandAlias::GetOptions() { |
132 | if (IsValid()) |
133 | return m_underlying_command_sp->GetOptions(); |
134 | return nullptr; |
135 | } |
136 | |
137 | void CommandAlias::Execute(const char *args_string, |
138 | CommandReturnObject &result) { |
139 | llvm_unreachable("CommandAlias::Execute is not to be called" ); |
140 | } |
141 | |
142 | void CommandAlias::GetAliasExpansion(StreamString &help_string) const { |
143 | llvm::StringRef command_name = m_underlying_command_sp->GetCommandName(); |
144 | help_string.Printf(format: "'%*s" , (int)command_name.size(), command_name.data()); |
145 | |
146 | if (!m_option_args_sp) { |
147 | help_string.Printf(format: "'" ); |
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(args&: opt, args: std::ignore, args&: value) = opt_entry; |
157 | if (opt == CommandInterpreter::g_argument) { |
158 | help_string.Printf(format: " %s" , value.c_str()); |
159 | } else { |
160 | help_string.Printf(format: " %s" , opt.c_str()); |
161 | if ((value != CommandInterpreter::g_no_argument) |
162 | && (value != CommandInterpreter::g_need_argument)) { |
163 | help_string.Printf(format: " %s" , value.c_str()); |
164 | } |
165 | } |
166 | } |
167 | |
168 | help_string.Printf(format: "'" ); |
169 | } |
170 | |
171 | bool 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(args&: opt, args: std::ignore, args&: value) = opt_entry; |
183 | if (opt == CommandInterpreter::g_argument && !value.empty() && |
184 | llvm::StringRef(value).ends_with(Suffix: "--" )) { |
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 | |
199 | bool CommandAlias::IsNestedAlias() { |
200 | if (GetUnderlyingCommand()) |
201 | return GetUnderlyingCommand()->IsAlias(); |
202 | return false; |
203 | } |
204 | |
205 | std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() { |
206 | auto underlying = GetUnderlyingCommand(); |
207 | if (!underlying) |
208 | return {nullptr, nullptr}; |
209 | |
210 | if (underlying->IsAlias()) { |
211 | // FIXME: This doesn't work if the original alias fills a slot in the |
212 | // underlying alias, since this just appends the two lists. |
213 | auto desugared = ((CommandAlias *)underlying.get())->Desugar(); |
214 | OptionArgVectorSP options = std::make_shared<OptionArgVector>(); |
215 | llvm::append_range(C&: *options, R&: *desugared.second); |
216 | llvm::append_range(C&: *options, R&: *GetOptionArguments()); |
217 | return {desugared.first, options}; |
218 | } |
219 | |
220 | return {underlying, GetOptionArguments()}; |
221 | } |
222 | |
223 | // allow CommandAlias objects to provide their own help, but fallback to the |
224 | // info for the underlying command if no customization has been provided |
225 | void CommandAlias::SetHelp(llvm::StringRef str) { |
226 | this->CommandObject::SetHelp(str); |
227 | m_did_set_help = true; |
228 | } |
229 | |
230 | void CommandAlias::SetHelpLong(llvm::StringRef str) { |
231 | this->CommandObject::SetHelpLong(str); |
232 | m_did_set_help_long = true; |
233 | } |
234 | |
235 | llvm::StringRef CommandAlias::GetHelp() { |
236 | if (!m_cmd_help_short.empty() || m_did_set_help) |
237 | return m_cmd_help_short; |
238 | if (IsValid()) |
239 | return m_underlying_command_sp->GetHelp(); |
240 | return llvm::StringRef(); |
241 | } |
242 | |
243 | llvm::StringRef CommandAlias::GetHelpLong() { |
244 | if (!m_cmd_help_long.empty() || m_did_set_help_long) |
245 | return m_cmd_help_long; |
246 | if (IsValid()) |
247 | return m_underlying_command_sp->GetHelpLong(); |
248 | return llvm::StringRef(); |
249 | } |
250 | |