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(&exe_ctx);
45 
46     llvm::Expected<Args> args_or =
47         options->ParseAlias(args, option_arg_vector, options_string);
48     if (!args_or) {
49       result.AppendError(toString(args_or.takeError()));
50       result.AppendError("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("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(CommandInterpreter::g_argument,
65                                       -1, options_string);
66     else {
67       for (auto &entry : args.entries()) {
68         if (!entry.ref().empty())
69           option_arg_vector->emplace_back(std::string(CommandInterpreter::g_argument), -1,
70                                           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_sp, options_args, 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(i);
92          i++) {
93       m_arguments.push_back(*cmd_entry);
94     }
95     if (!help.empty()) {
96       StreamString sstr;
97       StreamString translation_and_help;
98       GetAliasExpansion(sstr);
99 
100       translation_and_help.Printf(
101           "(%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 bool 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("'%*s", (int)command_name.size(), command_name.data());
146 
147   if (!m_option_args_sp) {
148     help_string.Printf("'");
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(opt, std::ignore, value) = opt_entry;
158     if (opt == CommandInterpreter::g_argument) {
159       help_string.Printf(" %s", value.c_str());
160     } else {
161       help_string.Printf(" %s", opt.c_str());
162       if ((value != CommandInterpreter::g_no_argument)
163            && (value != CommandInterpreter::g_need_argument)) {
164         help_string.Printf(" %s", value.c_str());
165       }
166     }
167   }
168 
169   help_string.Printf("'");
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(opt, std::ignore, value) = opt_entry;
184     if (opt == CommandInterpreter::g_argument && !value.empty() &&
185         llvm::StringRef(value).endswith("--")) {
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(*options, *desugared.second);
217     llvm::append_range(*options, *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