1 //===-- CommandAlias.cpp -----------------------------------------*- C++-*-===//
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 
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   CommandReturnObject result;
34   // Check to see if the command being aliased can take any command options.
35   Options *options = cmd_obj_sp->GetOptions();
36   if (options) {
37     // See if any options were specified as part of the alias;  if so, handle
38     // them appropriately.
39     ExecutionContext exe_ctx =
40         cmd_obj_sp->GetCommandInterpreter().GetExecutionContext();
41     options->NotifyOptionParsingStarting(&exe_ctx);
42 
43     llvm::Expected<Args> args_or =
44         options->ParseAlias(args, option_arg_vector, options_string);
45     if (!args_or) {
46       result.AppendError(toString(args_or.takeError()));
47       result.AppendError("Unable to create requested alias.\n");
48       result.SetStatus(eReturnStatusFailed);
49       return false;
50     }
51     args = std::move(*args_or);
52     options->VerifyPartialOptions(result);
53     if (!result.Succeeded() &&
54         result.GetStatus() != lldb::eReturnStatusStarted) {
55       result.AppendError("Unable to create requested alias.\n");
56       return false;
57     }
58   }
59 
60   if (!options_string.empty()) {
61     if (cmd_obj_sp->WantsRawCommandString())
62       option_arg_vector->emplace_back("<argument>", -1, options_string);
63     else {
64       for (auto &entry : args.entries()) {
65         if (!entry.ref.empty())
66           option_arg_vector->emplace_back("<argument>", -1, entry.ref);
67       }
68     }
69   }
70 
71   return success;
72 }
73 
74 CommandAlias::CommandAlias(CommandInterpreter &interpreter,
75                            lldb::CommandObjectSP cmd_sp,
76                            llvm::StringRef options_args, llvm::StringRef name,
77                            llvm::StringRef help, llvm::StringRef syntax,
78                            uint32_t flags)
79     : CommandObject(interpreter, name, help, syntax, flags),
80       m_underlying_command_sp(), m_option_string(options_args),
81       m_option_args_sp(new OptionArgVector),
82       m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false),
83       m_did_set_help_long(false) {
84   if (ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp)) {
85     m_underlying_command_sp = cmd_sp;
86     for (int i = 0;
87          auto cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i);
88          i++) {
89       m_arguments.push_back(*cmd_entry);
90     }
91     if (!help.empty()) {
92       StreamString sstr;
93       StreamString translation_and_help;
94       GetAliasExpansion(sstr);
95 
96       translation_and_help.Printf(
97           "(%s)  %s", sstr.GetData(),
98           GetUnderlyingCommand()->GetHelp().str().c_str());
99       SetHelp(translation_and_help.GetString());
100     }
101   }
102 }
103 
104 bool CommandAlias::WantsRawCommandString() {
105   if (IsValid())
106     return m_underlying_command_sp->WantsRawCommandString();
107   return false;
108 }
109 
110 bool CommandAlias::WantsCompletion() {
111   if (IsValid())
112     return m_underlying_command_sp->WantsCompletion();
113   return false;
114 }
115 
116 int CommandAlias::HandleCompletion(CompletionRequest &request) {
117   if (IsValid())
118     return m_underlying_command_sp->HandleCompletion(request);
119   return -1;
120 }
121 
122 int CommandAlias::HandleArgumentCompletion(
123     CompletionRequest &request, OptionElementVector &opt_element_vector) {
124   if (IsValid())
125     return m_underlying_command_sp->HandleArgumentCompletion(
126         request, opt_element_vector);
127   return -1;
128 }
129 
130 Options *CommandAlias::GetOptions() {
131   if (IsValid())
132     return m_underlying_command_sp->GetOptions();
133   return nullptr;
134 }
135 
136 bool CommandAlias::Execute(const char *args_string,
137                            CommandReturnObject &result) {
138   llvm_unreachable("CommandAlias::Execute is not to be called");
139 }
140 
141 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 
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 
197 bool CommandAlias::IsNestedAlias() {
198   if (GetUnderlyingCommand())
199     return GetUnderlyingCommand()->IsAlias();
200   return false;
201 }
202 
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
221 void CommandAlias::SetHelp(llvm::StringRef str) {
222   this->CommandObject::SetHelp(str);
223   m_did_set_help = true;
224 }
225 
226 void CommandAlias::SetHelpLong(llvm::StringRef str) {
227   this->CommandObject::SetHelpLong(str);
228   m_did_set_help_long = true;
229 }
230 
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 
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