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/Core/StreamString.h"
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 
20 using namespace lldb;
21 using namespace lldb_private;
22 
23 static bool
24 ProcessAliasOptionsArgs (lldb::CommandObjectSP &cmd_obj_sp,
25                          const char *options_args,
26                          OptionArgVectorSP &option_arg_vector_sp)
27 {
28     bool success = true;
29     OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
30 
31     if (!options_args || (strlen (options_args) < 1))
32         return true;
33 
34     std::string options_string (options_args);
35     Args args (options_args);
36     CommandReturnObject result;
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     {
41         // See if any options were specified as part of the alias;  if so, handle them appropriately.
42         ExecutionContext exe_ctx =
43             cmd_obj_sp->GetCommandInterpreter().GetExecutionContext();
44         options->NotifyOptionParsingStarting(&exe_ctx);
45         args.Unshift ("dummy_arg");
46         args.ParseAliasOptions (*options, result, option_arg_vector, options_string);
47         args.Shift ();
48         if (result.Succeeded())
49             options->VerifyPartialOptions (result);
50         if (!result.Succeeded() && result.GetStatus() != lldb::eReturnStatusStarted)
51         {
52             result.AppendError ("Unable to create requested alias.\n");
53             return false;
54         }
55     }
56 
57     if (!options_string.empty())
58     {
59         if (cmd_obj_sp->WantsRawCommandString ())
60             option_arg_vector->push_back (OptionArgPair ("<argument>",
61                                                          OptionArgValue (-1,
62                                                                          options_string)));
63         else
64         {
65             const size_t argc = args.GetArgumentCount();
66             for (size_t i = 0; i < argc; ++i)
67                 if (strcmp (args.GetArgumentAtIndex (i), "") != 0)
68                     option_arg_vector->push_back
69                     (OptionArgPair ("<argument>",
70                                     OptionArgValue (-1,
71                                                     std::string (args.GetArgumentAtIndex (i)))));
72         }
73     }
74 
75     return success;
76 }
77 
78 CommandAlias::CommandAlias (CommandInterpreter &interpreter,
79                             lldb::CommandObjectSP cmd_sp,
80                             const char *options_args,
81                             const char *name,
82                             const char *help,
83                             const char *syntax,
84                             uint32_t flags) :
85     CommandObject(interpreter,
86                   name,
87                   help,
88                   syntax,
89                   flags),
90 m_underlying_command_sp(),
91 m_option_string(options_args ? options_args : ""),
92 m_option_args_sp(new OptionArgVector),
93 m_is_dashdash_alias(eLazyBoolCalculate),
94 m_did_set_help(false),
95 m_did_set_help_long(false)
96 {
97     if (ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp))
98     {
99         m_underlying_command_sp = cmd_sp;
100         for (int i = 0;
101              auto cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i);
102              i++)
103         {
104             m_arguments.push_back(*cmd_entry);
105         }
106         if (!help || !help[0])
107         {
108             StreamString sstr;
109             StreamString translation_and_help;
110             GetAliasExpansion(sstr);
111 
112             translation_and_help.Printf ("(%s)  %s", sstr.GetData(), GetUnderlyingCommand()->GetHelp());
113             SetHelp(translation_and_help.GetData());
114         }
115     }
116 }
117 
118 bool
119 CommandAlias::WantsRawCommandString()
120 {
121     if (IsValid())
122         return m_underlying_command_sp->WantsRawCommandString();
123     return false;
124 }
125 
126 bool
127 CommandAlias::WantsCompletion()
128 {
129     if (IsValid())
130         return m_underlying_command_sp->WantsCompletion();
131     return false;
132 }
133 
134 int
135 CommandAlias::HandleCompletion (Args &input,
136                   int &cursor_index,
137                   int &cursor_char_position,
138                   int match_start_point,
139                   int max_return_elements,
140                   bool &word_complete,
141                   StringList &matches)
142 {
143     if (IsValid())
144         return m_underlying_command_sp->HandleCompletion(input,
145                                                          cursor_index,
146                                                          cursor_char_position,
147                                                          match_start_point,
148                                                          max_return_elements,
149                                                          word_complete,
150                                                          matches);
151     return -1;
152 }
153 
154 int
155 CommandAlias::HandleArgumentCompletion (Args &input,
156                           int &cursor_index,
157                           int &cursor_char_position,
158                           OptionElementVector &opt_element_vector,
159                           int match_start_point,
160                           int max_return_elements,
161                           bool &word_complete,
162                           StringList &matches)
163 {
164     if (IsValid())
165         return m_underlying_command_sp->HandleArgumentCompletion(input,
166                                                                  cursor_index,
167                                                                  cursor_char_position,
168                                                                  opt_element_vector,
169                                                                  match_start_point,
170                                                                  max_return_elements,
171                                                                  word_complete,
172                                                                  matches);
173     return -1;
174 }
175 
176 Options*
177 CommandAlias::GetOptions()
178 {
179     if (IsValid())
180         return m_underlying_command_sp->GetOptions();
181     return nullptr;
182 }
183 
184 bool
185 CommandAlias::Execute(const char *args_string, CommandReturnObject &result)
186 {
187     llvm_unreachable("CommandAlias::Execute is not to be called");
188 }
189 
190 void
191 CommandAlias::GetAliasExpansion (StreamString &help_string)
192 {
193     const char* command_name = m_underlying_command_sp->GetCommandName();
194     help_string.Printf ("'%s", command_name);
195 
196     if (m_option_args_sp)
197     {
198         OptionArgVector *options = m_option_args_sp.get();
199         for (size_t i = 0; i < options->size(); ++i)
200         {
201             OptionArgPair cur_option = (*options)[i];
202             std::string opt = cur_option.first;
203             OptionArgValue value_pair = cur_option.second;
204             std::string value = value_pair.second;
205             if (opt.compare("<argument>") == 0)
206             {
207                 help_string.Printf (" %s", value.c_str());
208             }
209             else
210             {
211                 help_string.Printf (" %s", opt.c_str());
212                 if ((value.compare ("<no-argument>") != 0)
213                     && (value.compare ("<need-argument") != 0))
214                 {
215                     help_string.Printf (" %s", value.c_str());
216                 }
217             }
218         }
219     }
220 
221     help_string.Printf ("'");
222 }
223 
224 bool
225 CommandAlias::IsDashDashCommand ()
226 {
227     if (m_is_dashdash_alias == eLazyBoolCalculate)
228     {
229         m_is_dashdash_alias = eLazyBoolNo;
230         if (IsValid())
231         {
232             for (const OptionArgPair& opt_arg : *GetOptionArguments())
233             {
234                 if (opt_arg.first == "<argument>" &&
235                     !opt_arg.second.second.empty() &&
236                     llvm::StringRef(opt_arg.second.second).endswith("--"))
237                 {
238                     m_is_dashdash_alias = eLazyBoolYes;
239                     break;
240                 }
241             }
242             // if this is a nested alias, it may be adding arguments on top of an already dash-dash alias
243             if ((m_is_dashdash_alias == eLazyBoolNo) && IsNestedAlias())
244                 m_is_dashdash_alias = (GetUnderlyingCommand()->IsDashDashCommand() ? eLazyBoolYes : eLazyBoolNo);
245         }
246     }
247     return (m_is_dashdash_alias == eLazyBoolYes);
248 }
249 
250 bool
251 CommandAlias::IsNestedAlias ()
252 {
253     if (GetUnderlyingCommand())
254         return GetUnderlyingCommand()->IsAlias();
255     return false;
256 }
257 
258 std::pair<lldb::CommandObjectSP, OptionArgVectorSP>
259 CommandAlias::Desugar ()
260 {
261     auto underlying = GetUnderlyingCommand();
262     if (!underlying)
263         return {nullptr,nullptr};
264 
265     if (underlying->IsAlias())
266     {
267         auto desugared = ((CommandAlias*)underlying.get())->Desugar();
268         auto options = GetOptionArguments();
269         options->insert(options->begin(), desugared.second->begin(), desugared.second->end());
270         return {desugared.first,options};
271     }
272 
273     return {underlying,GetOptionArguments()};
274 }
275 
276 // allow CommandAlias objects to provide their own help, but fallback to the info
277 // for the underlying command if no customization has been provided
278 void
279 CommandAlias::SetHelp (const char * str)
280 {
281     this->CommandObject::SetHelp(str);
282     m_did_set_help = true;
283 }
284 
285 void
286 CommandAlias::SetHelpLong (const char * str)
287 {
288     this->CommandObject::SetHelpLong(str);
289     m_did_set_help_long = true;
290 }
291 
292 const char*
293 CommandAlias::GetHelp ()
294 {
295     if (!m_cmd_help_short.empty() || m_did_set_help)
296         return m_cmd_help_short.c_str();
297     if (IsValid())
298         return m_underlying_command_sp->GetHelp();
299     return nullptr;
300 }
301 
302 const char*
303 CommandAlias::GetHelpLong ()
304 {
305     if (!m_cmd_help_long.empty() || m_did_set_help_long)
306         return m_cmd_help_long.c_str();
307     if (IsValid())
308         return m_underlying_command_sp->GetHelpLong();
309     return nullptr;
310 }
311