xref: /freebsd/contrib/llvm-project/lldb/source/Interpreter/CommandAlias.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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
ProcessAliasOptionsArgs(lldb::CommandObjectSP & cmd_obj_sp,llvm::StringRef options_args,OptionArgVectorSP & option_arg_vector_sp)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(&exe_ctx);
43 
44     llvm::Expected<Args> args_or =
45         options->ParseAlias(args, option_arg_vector, options_string);
46     if (!args_or)
47       return llvm::createStringError(
48           llvm::formatv("unable to create alias: {0}",
49                         llvm::fmt_consume(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(CommandInterpreter::g_argument, -1,
58                                       options_string);
59     } else {
60       for (auto &entry : args.entries()) {
61         if (!entry.ref().empty())
62           option_arg_vector->emplace_back(
63               std::string(CommandInterpreter::g_argument), -1,
64               std::string(entry.ref()));
65       }
66     }
67   }
68 
69   return llvm::Error::success();
70 }
71 
CommandAlias(CommandInterpreter & interpreter,lldb::CommandObjectSP cmd_sp,llvm::StringRef options_args,llvm::StringRef name,llvm::StringRef help,llvm::StringRef syntax,uint32_t flags)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_sp, options_args, 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(i);
91          i++) {
92       m_arguments.push_back(*cmd_entry);
93     }
94     if (!help.empty()) {
95       StreamString sstr;
96       StreamString translation_and_help;
97       GetAliasExpansion(sstr);
98 
99       translation_and_help.Printf(
100           "(%s)  %s", sstr.GetData(),
101           GetUnderlyingCommand()->GetHelp().str().c_str());
102       SetHelp(translation_and_help.GetString());
103     }
104   }
105 }
106 
WantsRawCommandString()107 bool CommandAlias::WantsRawCommandString() {
108   if (IsValid())
109     return m_underlying_command_sp->WantsRawCommandString();
110   return false;
111 }
112 
WantsCompletion()113 bool CommandAlias::WantsCompletion() {
114   if (IsValid())
115     return m_underlying_command_sp->WantsCompletion();
116   return false;
117 }
118 
HandleCompletion(CompletionRequest & request)119 void CommandAlias::HandleCompletion(CompletionRequest &request) {
120   if (IsValid())
121     m_underlying_command_sp->HandleCompletion(request);
122 }
123 
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)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 
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 void 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 == CommandInterpreter::g_argument) {
158       help_string.Printf(" %s", value.c_str());
159     } else {
160       help_string.Printf(" %s", opt.c_str());
161       if ((value != CommandInterpreter::g_no_argument)
162            && (value != CommandInterpreter::g_need_argument)) {
163         help_string.Printf(" %s", value.c_str());
164       }
165     }
166   }
167 
168   help_string.Printf("'");
169 }
170 
IsDashDashCommand()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(opt, std::ignore, value) = opt_entry;
183     if (opt == CommandInterpreter::g_argument && !value.empty() &&
184         llvm::StringRef(value).ends_with("--")) {
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 
IsNestedAlias()199 bool CommandAlias::IsNestedAlias() {
200   if (GetUnderlyingCommand())
201     return GetUnderlyingCommand()->IsAlias();
202   return false;
203 }
204 
Desugar()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(*options, *desugared.second);
216     llvm::append_range(*options, *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
SetHelp(llvm::StringRef str)225 void CommandAlias::SetHelp(llvm::StringRef str) {
226   this->CommandObject::SetHelp(str);
227   m_did_set_help = true;
228 }
229 
SetHelpLong(llvm::StringRef str)230 void CommandAlias::SetHelpLong(llvm::StringRef str) {
231   this->CommandObject::SetHelpLong(str);
232   m_did_set_help_long = true;
233 }
234 
GetHelp()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 
GetHelpLong()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