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