1 //===-- CommandAlias.cpp -----------------------------------------*- C++-*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/Interpreter/CommandAlias.h"
11 
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   CommandReturnObject result;
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       result.AppendError(toString(args_or.takeError()));
48       result.AppendError("Unable to create requested alias.\n");
49       result.SetStatus(eReturnStatusFailed);
50       return false;
51     }
52     args = std::move(*args_or);
53     options->VerifyPartialOptions(result);
54     if (!result.Succeeded() &&
55         result.GetStatus() != lldb::eReturnStatusStarted) {
56       result.AppendError("Unable to create requested alias.\n");
57       return false;
58     }
59   }
60 
61   if (!options_string.empty()) {
62     if (cmd_obj_sp->WantsRawCommandString())
63       option_arg_vector->emplace_back("<argument>", -1, options_string);
64     else {
65       for (auto &entry : args.entries()) {
66         if (!entry.ref.empty())
67           option_arg_vector->emplace_back("<argument>", -1, entry.ref);
68       }
69     }
70   }
71 
72   return success;
73 }
74 
75 CommandAlias::CommandAlias(CommandInterpreter &interpreter,
76                            lldb::CommandObjectSP cmd_sp,
77                            llvm::StringRef options_args, llvm::StringRef name,
78                            llvm::StringRef help, llvm::StringRef syntax,
79                            uint32_t flags)
80     : CommandObject(interpreter, name, help, syntax, flags),
81       m_underlying_command_sp(), m_option_string(options_args),
82       m_option_args_sp(new OptionArgVector),
83       m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false),
84       m_did_set_help_long(false) {
85   if (ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp)) {
86     m_underlying_command_sp = cmd_sp;
87     for (int i = 0;
88          auto cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i);
89          i++) {
90       m_arguments.push_back(*cmd_entry);
91     }
92     if (!help.empty()) {
93       StreamString sstr;
94       StreamString translation_and_help;
95       GetAliasExpansion(sstr);
96 
97       translation_and_help.Printf(
98           "(%s)  %s", sstr.GetData(),
99           GetUnderlyingCommand()->GetHelp().str().c_str());
100       SetHelp(translation_and_help.GetString());
101     }
102   }
103 }
104 
105 bool CommandAlias::WantsRawCommandString() {
106   if (IsValid())
107     return m_underlying_command_sp->WantsRawCommandString();
108   return false;
109 }
110 
111 bool CommandAlias::WantsCompletion() {
112   if (IsValid())
113     return m_underlying_command_sp->WantsCompletion();
114   return false;
115 }
116 
117 int CommandAlias::HandleCompletion(Args &input, int &cursor_index,
118                                    int &cursor_char_position,
119                                    int match_start_point,
120                                    int max_return_elements, bool &word_complete,
121                                    StringList &matches) {
122   if (IsValid())
123     return m_underlying_command_sp->HandleCompletion(
124         input, cursor_index, cursor_char_position, match_start_point,
125         max_return_elements, word_complete, matches);
126   return -1;
127 }
128 
129 int CommandAlias::HandleArgumentCompletion(
130     Args &input, int &cursor_index, int &cursor_char_position,
131     OptionElementVector &opt_element_vector, int match_start_point,
132     int max_return_elements, bool &word_complete, StringList &matches) {
133   if (IsValid())
134     return m_underlying_command_sp->HandleArgumentCompletion(
135         input, cursor_index, cursor_char_position, opt_element_vector,
136         match_start_point, max_return_elements, word_complete, matches);
137   return -1;
138 }
139 
140 Options *CommandAlias::GetOptions() {
141   if (IsValid())
142     return m_underlying_command_sp->GetOptions();
143   return nullptr;
144 }
145 
146 bool CommandAlias::Execute(const char *args_string,
147                            CommandReturnObject &result) {
148   llvm_unreachable("CommandAlias::Execute is not to be called");
149 }
150 
151 void CommandAlias::GetAliasExpansion(StreamString &help_string) const {
152   llvm::StringRef command_name = m_underlying_command_sp->GetCommandName();
153   help_string.Printf("'%*s", (int)command_name.size(), command_name.data());
154 
155   if (!m_option_args_sp) {
156     help_string.Printf("'");
157     return;
158   }
159 
160   OptionArgVector *options = m_option_args_sp.get();
161   std::string opt;
162   std::string value;
163 
164   for (const auto &opt_entry : *options) {
165     std::tie(opt, std::ignore, value) = opt_entry;
166     if (opt == "<argument>") {
167       help_string.Printf(" %s", value.c_str());
168     } else {
169       help_string.Printf(" %s", opt.c_str());
170       if ((value.compare("<no-argument>") != 0) &&
171           (value.compare("<need-argument") != 0)) {
172         help_string.Printf(" %s", value.c_str());
173       }
174     }
175   }
176 
177   help_string.Printf("'");
178 }
179 
180 bool CommandAlias::IsDashDashCommand() {
181   if (m_is_dashdash_alias != eLazyBoolCalculate)
182     return (m_is_dashdash_alias == eLazyBoolYes);
183   m_is_dashdash_alias = eLazyBoolNo;
184   if (!IsValid())
185     return false;
186 
187   std::string opt;
188   std::string value;
189 
190   for (const auto &opt_entry : *GetOptionArguments()) {
191     std::tie(opt, std::ignore, value) = opt_entry;
192     if (opt == "<argument>" && !value.empty() &&
193         llvm::StringRef(value).endswith("--")) {
194       m_is_dashdash_alias = eLazyBoolYes;
195       break;
196     }
197   }
198 
199   // if this is a nested alias, it may be adding arguments on top of an
200   // already dash-dash alias
201   if ((m_is_dashdash_alias == eLazyBoolNo) && IsNestedAlias())
202     m_is_dashdash_alias =
203         (GetUnderlyingCommand()->IsDashDashCommand() ? eLazyBoolYes
204                                                      : eLazyBoolNo);
205   return (m_is_dashdash_alias == eLazyBoolYes);
206 }
207 
208 bool CommandAlias::IsNestedAlias() {
209   if (GetUnderlyingCommand())
210     return GetUnderlyingCommand()->IsAlias();
211   return false;
212 }
213 
214 std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() {
215   auto underlying = GetUnderlyingCommand();
216   if (!underlying)
217     return {nullptr, nullptr};
218 
219   if (underlying->IsAlias()) {
220     auto desugared = ((CommandAlias *)underlying.get())->Desugar();
221     auto options = GetOptionArguments();
222     options->insert(options->begin(), desugared.second->begin(),
223                     desugared.second->end());
224     return {desugared.first, options};
225   }
226 
227   return {underlying, GetOptionArguments()};
228 }
229 
230 // allow CommandAlias objects to provide their own help, but fallback to the
231 // info
232 // for the underlying command if no customization has been provided
233 void CommandAlias::SetHelp(llvm::StringRef str) {
234   this->CommandObject::SetHelp(str);
235   m_did_set_help = true;
236 }
237 
238 void CommandAlias::SetHelpLong(llvm::StringRef str) {
239   this->CommandObject::SetHelpLong(str);
240   m_did_set_help_long = true;
241 }
242 
243 llvm::StringRef CommandAlias::GetHelp() {
244   if (!m_cmd_help_short.empty() || m_did_set_help)
245     return m_cmd_help_short;
246   if (IsValid())
247     return m_underlying_command_sp->GetHelp();
248   return llvm::StringRef();
249 }
250 
251 llvm::StringRef CommandAlias::GetHelpLong() {
252   if (!m_cmd_help_long.empty() || m_did_set_help_long)
253     return m_cmd_help_long;
254   if (IsValid())
255     return m_underlying_command_sp->GetHelpLong();
256   return llvm::StringRef();
257 }
258