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