/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Commands/CommandObjectCommands.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- CommandObjectCommands.cpp -----------------------------------------===// |
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 "CommandObjectCommands.h" |
10 | | #include "CommandObjectHelp.h" |
11 | | #include "CommandObjectRegexCommand.h" |
12 | | #include "lldb/Core/Debugger.h" |
13 | | #include "lldb/Core/IOHandler.h" |
14 | | #include "lldb/Interpreter/CommandHistory.h" |
15 | | #include "lldb/Interpreter/CommandInterpreter.h" |
16 | | #include "lldb/Interpreter/CommandOptionArgumentTable.h" |
17 | | #include "lldb/Interpreter/CommandReturnObject.h" |
18 | | #include "lldb/Interpreter/OptionArgParser.h" |
19 | | #include "lldb/Interpreter/OptionValueBoolean.h" |
20 | | #include "lldb/Interpreter/OptionValueString.h" |
21 | | #include "lldb/Interpreter/OptionValueUInt64.h" |
22 | | #include "lldb/Interpreter/Options.h" |
23 | | #include "lldb/Interpreter/ScriptInterpreter.h" |
24 | | #include "lldb/Utility/Args.h" |
25 | | #include "lldb/Utility/StringList.h" |
26 | | #include "llvm/ADT/StringRef.h" |
27 | | #include <optional> |
28 | | |
29 | | using namespace lldb; |
30 | | using namespace lldb_private; |
31 | | |
32 | | // CommandObjectCommandsSource |
33 | | |
34 | | #define LLDB_OPTIONS_source |
35 | | #include "CommandOptions.inc" |
36 | | |
37 | | class CommandObjectCommandsSource : public CommandObjectParsed { |
38 | | public: |
39 | | CommandObjectCommandsSource(CommandInterpreter &interpreter) |
40 | 6.14k | : CommandObjectParsed( |
41 | 6.14k | interpreter, "command source", |
42 | 6.14k | "Read and execute LLDB commands from the file <filename>.", |
43 | 6.14k | nullptr) { |
44 | 6.14k | CommandArgumentEntry arg; |
45 | 6.14k | CommandArgumentData file_arg; |
46 | | |
47 | | // Define the first (and only) variant of this arg. |
48 | 6.14k | file_arg.arg_type = eArgTypeFilename; |
49 | 6.14k | file_arg.arg_repetition = eArgRepeatPlain; |
50 | | |
51 | | // There is only one variant this argument could be; put it into the |
52 | | // argument entry. |
53 | 6.14k | arg.push_back(file_arg); |
54 | | |
55 | | // Push the data for the first argument into the m_arguments vector. |
56 | 6.14k | m_arguments.push_back(arg); |
57 | 6.14k | } |
58 | | |
59 | 6.13k | ~CommandObjectCommandsSource() override = default; |
60 | | |
61 | | std::optional<std::string> GetRepeatCommand(Args ¤t_command_args, |
62 | 515 | uint32_t index) override { |
63 | 515 | return std::string(""); |
64 | 515 | } |
65 | | |
66 | | void |
67 | | HandleArgumentCompletion(CompletionRequest &request, |
68 | 0 | OptionElementVector &opt_element_vector) override { |
69 | 0 | lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( |
70 | 0 | GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr); |
71 | 0 | } |
72 | | |
73 | 908 | Options *GetOptions() override { return &m_options; } |
74 | | |
75 | | protected: |
76 | | class CommandOptions : public Options { |
77 | | public: |
78 | | CommandOptions() |
79 | 6.14k | : m_stop_on_error(true), m_silent_run(false), m_stop_on_continue(true), |
80 | 6.14k | m_cmd_relative_to_command_file(false) {} |
81 | | |
82 | 6.13k | ~CommandOptions() override = default; |
83 | | |
84 | | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
85 | 1.26k | ExecutionContext *execution_context) override { |
86 | 1.26k | Status error; |
87 | 1.26k | const int short_option = m_getopt_table[option_idx].val; |
88 | | |
89 | 1.26k | switch (short_option) { |
90 | 3 | case 'e': |
91 | 3 | error = m_stop_on_error.SetValueFromString(option_arg); |
92 | 3 | break; |
93 | | |
94 | 0 | case 'c': |
95 | 0 | error = m_stop_on_continue.SetValueFromString(option_arg); |
96 | 0 | break; |
97 | | |
98 | 376 | case 'C': |
99 | 376 | m_cmd_relative_to_command_file = true; |
100 | 376 | break; |
101 | | |
102 | 882 | case 's': |
103 | 882 | error = m_silent_run.SetValueFromString(option_arg); |
104 | 882 | break; |
105 | | |
106 | 0 | default: |
107 | 0 | llvm_unreachable("Unimplemented option"); |
108 | 1.26k | } |
109 | | |
110 | 1.26k | return error; |
111 | 1.26k | } |
112 | | |
113 | 901 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
114 | 901 | m_stop_on_error.Clear(); |
115 | 901 | m_silent_run.Clear(); |
116 | 901 | m_stop_on_continue.Clear(); |
117 | 901 | m_cmd_relative_to_command_file.Clear(); |
118 | 901 | } |
119 | | |
120 | 1.14k | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
121 | 1.14k | return llvm::ArrayRef(g_source_options); |
122 | 1.14k | } |
123 | | |
124 | | // Instance variables to hold the values for command options. |
125 | | |
126 | | OptionValueBoolean m_stop_on_error; |
127 | | OptionValueBoolean m_silent_run; |
128 | | OptionValueBoolean m_stop_on_continue; |
129 | | OptionValueBoolean m_cmd_relative_to_command_file; |
130 | | }; |
131 | | |
132 | 901 | void DoExecute(Args &command, CommandReturnObject &result) override { |
133 | 901 | if (command.GetArgumentCount() != 1) { |
134 | 1 | result.AppendErrorWithFormat( |
135 | 1 | "'%s' takes exactly one executable filename argument.\n", |
136 | 1 | GetCommandName().str().c_str()); |
137 | 1 | return; |
138 | 1 | } |
139 | | |
140 | 900 | FileSpec source_dir = {}; |
141 | 900 | if (m_options.m_cmd_relative_to_command_file) { |
142 | 376 | source_dir = GetDebugger().GetCommandInterpreter().GetCurrentSourceDir(); |
143 | 376 | if (!source_dir) { |
144 | 0 | result.AppendError("command source -C can only be specified " |
145 | 0 | "from a command file"); |
146 | 0 | result.SetStatus(eReturnStatusFailed); |
147 | 0 | return; |
148 | 0 | } |
149 | 376 | } |
150 | | |
151 | 900 | FileSpec cmd_file(command[0].ref()); |
152 | 900 | if (source_dir) { |
153 | | // Prepend the source_dir to the cmd_file path: |
154 | 376 | if (!cmd_file.IsRelative()) { |
155 | 1 | result.AppendError("command source -C can only be used " |
156 | 1 | "with a relative path."); |
157 | 1 | result.SetStatus(eReturnStatusFailed); |
158 | 1 | return; |
159 | 1 | } |
160 | 375 | cmd_file.MakeAbsolute(source_dir); |
161 | 375 | } |
162 | | |
163 | 899 | FileSystem::Instance().Resolve(cmd_file); |
164 | | |
165 | 899 | CommandInterpreterRunOptions options; |
166 | | // If any options were set, then use them |
167 | 899 | if (m_options.m_stop_on_error.OptionWasSet() || |
168 | 899 | m_options.m_silent_run.OptionWasSet()896 || |
169 | 899 | m_options.m_stop_on_continue.OptionWasSet()14 ) { |
170 | 885 | if (m_options.m_stop_on_continue.OptionWasSet()) |
171 | 0 | options.SetStopOnContinue( |
172 | 0 | m_options.m_stop_on_continue.GetCurrentValue()); |
173 | | |
174 | 885 | if (m_options.m_stop_on_error.OptionWasSet()) |
175 | 3 | options.SetStopOnError(m_options.m_stop_on_error.GetCurrentValue()); |
176 | | |
177 | | // Individual silent setting is override for global command echo settings. |
178 | 885 | if (m_options.m_silent_run.GetCurrentValue()) { |
179 | 378 | options.SetSilent(true); |
180 | 507 | } else { |
181 | 507 | options.SetPrintResults(true); |
182 | 507 | options.SetPrintErrors(true); |
183 | 507 | options.SetEchoCommands(m_interpreter.GetEchoCommands()); |
184 | 507 | options.SetEchoCommentCommands(m_interpreter.GetEchoCommentCommands()); |
185 | 507 | } |
186 | 885 | } |
187 | | |
188 | 899 | m_interpreter.HandleCommandsFromFile(cmd_file, options, result); |
189 | 899 | } |
190 | | |
191 | | CommandOptions m_options; |
192 | | }; |
193 | | |
194 | | #pragma mark CommandObjectCommandsAlias |
195 | | // CommandObjectCommandsAlias |
196 | | |
197 | | #define LLDB_OPTIONS_alias |
198 | | #include "CommandOptions.inc" |
199 | | |
200 | | static const char *g_python_command_instructions = |
201 | | "Enter your Python command(s). Type 'DONE' to end.\n" |
202 | | "You must define a Python function with this signature:\n" |
203 | | "def my_command_impl(debugger, args, exe_ctx, result, internal_dict):\n"; |
204 | | |
205 | | class CommandObjectCommandsAlias : public CommandObjectRaw { |
206 | | protected: |
207 | | class CommandOptions : public OptionGroup { |
208 | | public: |
209 | 6.14k | CommandOptions() = default; |
210 | | |
211 | 6.13k | ~CommandOptions() override = default; |
212 | | |
213 | 6.14k | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
214 | 6.14k | return llvm::ArrayRef(g_alias_options); |
215 | 6.14k | } |
216 | | |
217 | | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, |
218 | 2 | ExecutionContext *execution_context) override { |
219 | 2 | Status error; |
220 | | |
221 | 2 | const int short_option = GetDefinitions()[option_idx].short_option; |
222 | 2 | std::string option_str(option_value); |
223 | | |
224 | 2 | switch (short_option) { |
225 | 1 | case 'h': |
226 | 1 | m_help.SetCurrentValue(option_str); |
227 | 1 | m_help.SetOptionWasSet(); |
228 | 1 | break; |
229 | | |
230 | 1 | case 'H': |
231 | 1 | m_long_help.SetCurrentValue(option_str); |
232 | 1 | m_long_help.SetOptionWasSet(); |
233 | 1 | break; |
234 | | |
235 | 0 | default: |
236 | 0 | llvm_unreachable("Unimplemented option"); |
237 | 2 | } |
238 | | |
239 | 2 | return error; |
240 | 2 | } |
241 | | |
242 | 27 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
243 | 27 | m_help.Clear(); |
244 | 27 | m_long_help.Clear(); |
245 | 27 | } |
246 | | |
247 | | OptionValueString m_help; |
248 | | OptionValueString m_long_help; |
249 | | }; |
250 | | |
251 | | OptionGroupOptions m_option_group; |
252 | | CommandOptions m_command_options; |
253 | | |
254 | | public: |
255 | 23 | Options *GetOptions() override { return &m_option_group; } |
256 | | |
257 | | CommandObjectCommandsAlias(CommandInterpreter &interpreter) |
258 | 6.14k | : CommandObjectRaw( |
259 | 6.14k | interpreter, "command alias", |
260 | 6.14k | "Define a custom command in terms of an existing command.") { |
261 | 6.14k | m_option_group.Append(&m_command_options); |
262 | 6.14k | m_option_group.Finalize(); |
263 | | |
264 | 6.14k | SetHelpLong( |
265 | 6.14k | "'alias' allows the user to create a short-cut or abbreviation for long \ |
266 | 6.14k | commands, multi-word commands, and commands that take particular options. \ |
267 | 6.14k | Below are some simple examples of how one might use the 'alias' command:" |
268 | 6.14k | R"( |
269 | 6.14k | |
270 | 6.14k | (lldb) command alias sc script |
271 | 6.14k | |
272 | 6.14k | Creates the abbreviation 'sc' for the 'script' command. |
273 | 6.14k | |
274 | 6.14k | (lldb) command alias bp breakpoint |
275 | 6.14k | |
276 | 6.14k | )" |
277 | 6.14k | " Creates the abbreviation 'bp' for the 'breakpoint' command. Since \ |
278 | 6.14k | breakpoint commands are two-word commands, the user would still need to \ |
279 | 6.14k | enter the second word after 'bp', e.g. 'bp enable' or 'bp delete'." |
280 | 6.14k | R"( |
281 | 6.14k | |
282 | 6.14k | (lldb) command alias bpl breakpoint list |
283 | 6.14k | |
284 | 6.14k | Creates the abbreviation 'bpl' for the two-word command 'breakpoint list'. |
285 | 6.14k | |
286 | 6.14k | )" |
287 | 6.14k | "An alias can include some options for the command, with the values either \ |
288 | 6.14k | filled in at the time the alias is created, or specified as positional \ |
289 | 6.14k | arguments, to be filled in when the alias is invoked. The following example \ |
290 | 6.14k | shows how to create aliases with options:" |
291 | 6.14k | R"( |
292 | 6.14k | |
293 | 6.14k | (lldb) command alias bfl breakpoint set -f %1 -l %2 |
294 | 6.14k | |
295 | 6.14k | )" |
296 | 6.14k | " Creates the abbreviation 'bfl' (for break-file-line), with the -f and -l \ |
297 | 6.14k | options already part of the alias. So if the user wants to set a breakpoint \ |
298 | 6.14k | by file and line without explicitly having to use the -f and -l options, the \ |
299 | 6.14k | user can now use 'bfl' instead. The '%1' and '%2' are positional placeholders \ |
300 | 6.14k | for the actual arguments that will be passed when the alias command is used. \ |
301 | 6.14k | The number in the placeholder refers to the position/order the actual value \ |
302 | 6.14k | occupies when the alias is used. All the occurrences of '%1' in the alias \ |
303 | 6.14k | will be replaced with the first argument, all the occurrences of '%2' in the \ |
304 | 6.14k | alias will be replaced with the second argument, and so on. This also allows \ |
305 | 6.14k | actual arguments to be used multiple times within an alias (see 'process \ |
306 | 6.14k | launch' example below)." |
307 | 6.14k | R"( |
308 | 6.14k | |
309 | 6.14k | )" |
310 | 6.14k | "Note: the positional arguments must substitute as whole words in the resultant \ |
311 | 6.14k | command, so you can't at present do something like this to append the file extension \ |
312 | 6.14k | \".cpp\":" |
313 | 6.14k | R"( |
314 | 6.14k | |
315 | 6.14k | (lldb) command alias bcppfl breakpoint set -f %1.cpp -l %2 |
316 | 6.14k | |
317 | 6.14k | )" |
318 | 6.14k | "For more complex aliasing, use the \"command regex\" command instead. In the \ |
319 | 6.14k | 'bfl' case above, the actual file value will be filled in with the first argument \ |
320 | 6.14k | following 'bfl' and the actual line number value will be filled in with the second \ |
321 | 6.14k | argument. The user would use this alias as follows:" |
322 | 6.14k | R"( |
323 | 6.14k | |
324 | 6.14k | (lldb) command alias bfl breakpoint set -f %1 -l %2 |
325 | 6.14k | (lldb) bfl my-file.c 137 |
326 | 6.14k | |
327 | 6.14k | This would be the same as if the user had entered 'breakpoint set -f my-file.c -l 137'. |
328 | 6.14k | |
329 | 6.14k | Another example: |
330 | 6.14k | |
331 | 6.14k | (lldb) command alias pltty process launch -s -o %1 -e %1 |
332 | 6.14k | (lldb) pltty /dev/tty0 |
333 | 6.14k | |
334 | 6.14k | Interpreted as 'process launch -s -o /dev/tty0 -e /dev/tty0' |
335 | 6.14k | |
336 | 6.14k | )" |
337 | 6.14k | "If the user always wanted to pass the same value to a particular option, the \ |
338 | 6.14k | alias could be defined with that value directly in the alias as a constant, \ |
339 | 6.14k | rather than using a positional placeholder:" |
340 | 6.14k | R"( |
341 | 6.14k | |
342 | 6.14k | (lldb) command alias bl3 breakpoint set -f %1 -l 3 |
343 | 6.14k | |
344 | 6.14k | Always sets a breakpoint on line 3 of whatever file is indicated.)"); |
345 | | |
346 | 6.14k | CommandArgumentEntry arg1; |
347 | 6.14k | CommandArgumentEntry arg2; |
348 | 6.14k | CommandArgumentEntry arg3; |
349 | 6.14k | CommandArgumentData alias_arg; |
350 | 6.14k | CommandArgumentData cmd_arg; |
351 | 6.14k | CommandArgumentData options_arg; |
352 | | |
353 | | // Define the first (and only) variant of this arg. |
354 | 6.14k | alias_arg.arg_type = eArgTypeAliasName; |
355 | 6.14k | alias_arg.arg_repetition = eArgRepeatPlain; |
356 | | |
357 | | // There is only one variant this argument could be; put it into the |
358 | | // argument entry. |
359 | 6.14k | arg1.push_back(alias_arg); |
360 | | |
361 | | // Define the first (and only) variant of this arg. |
362 | 6.14k | cmd_arg.arg_type = eArgTypeCommandName; |
363 | 6.14k | cmd_arg.arg_repetition = eArgRepeatPlain; |
364 | | |
365 | | // There is only one variant this argument could be; put it into the |
366 | | // argument entry. |
367 | 6.14k | arg2.push_back(cmd_arg); |
368 | | |
369 | | // Define the first (and only) variant of this arg. |
370 | 6.14k | options_arg.arg_type = eArgTypeAliasOptions; |
371 | 6.14k | options_arg.arg_repetition = eArgRepeatOptional; |
372 | | |
373 | | // There is only one variant this argument could be; put it into the |
374 | | // argument entry. |
375 | 6.14k | arg3.push_back(options_arg); |
376 | | |
377 | | // Push the data for the first argument into the m_arguments vector. |
378 | 6.14k | m_arguments.push_back(arg1); |
379 | 6.14k | m_arguments.push_back(arg2); |
380 | 6.14k | m_arguments.push_back(arg3); |
381 | 6.14k | } |
382 | | |
383 | 6.13k | ~CommandObjectCommandsAlias() override = default; |
384 | | |
385 | | protected: |
386 | | void DoExecute(llvm::StringRef raw_command_line, |
387 | 26 | CommandReturnObject &result) override { |
388 | 26 | if (raw_command_line.empty()) { |
389 | 1 | result.AppendError("'command alias' requires at least two arguments"); |
390 | 1 | return; |
391 | 1 | } |
392 | | |
393 | 25 | ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext(); |
394 | 25 | m_option_group.NotifyOptionParsingStarting(&exe_ctx); |
395 | | |
396 | 25 | OptionsWithRaw args_with_suffix(raw_command_line); |
397 | | |
398 | 25 | if (args_with_suffix.HasArgs()) |
399 | 2 | if (!ParseOptionsAndNotify(args_with_suffix.GetArgs(), result, |
400 | 2 | m_option_group, exe_ctx)) |
401 | 0 | return; |
402 | | |
403 | 25 | llvm::StringRef raw_command_string = args_with_suffix.GetRawPart(); |
404 | 25 | Args args(raw_command_string); |
405 | | |
406 | 25 | if (args.GetArgumentCount() < 2) { |
407 | 0 | result.AppendError("'command alias' requires at least two arguments"); |
408 | 0 | return; |
409 | 0 | } |
410 | | |
411 | | // Get the alias command. |
412 | | |
413 | 25 | auto alias_command = args[0].ref(); |
414 | 25 | if (alias_command.startswith("-")) { |
415 | 0 | result.AppendError("aliases starting with a dash are not supported"); |
416 | 0 | if (alias_command == "--help" || alias_command == "--long-help") { |
417 | 0 | result.AppendWarning("if trying to pass options to 'command alias' add " |
418 | 0 | "a -- at the end of the options"); |
419 | 0 | } |
420 | 0 | return; |
421 | 0 | } |
422 | | |
423 | | // Strip the new alias name off 'raw_command_string' (leave it on args, |
424 | | // which gets passed to 'Execute', which does the stripping itself. |
425 | 25 | size_t pos = raw_command_string.find(alias_command); |
426 | 25 | if (pos == 0) { |
427 | 25 | raw_command_string = raw_command_string.substr(alias_command.size()); |
428 | 25 | pos = raw_command_string.find_first_not_of(' '); |
429 | 25 | if ((pos != std::string::npos) && (pos > 0)) |
430 | 25 | raw_command_string = raw_command_string.substr(pos); |
431 | 25 | } else { |
432 | 0 | result.AppendError("Error parsing command string. No alias created."); |
433 | 0 | return; |
434 | 0 | } |
435 | | |
436 | | // Verify that the command is alias-able. |
437 | 25 | if (m_interpreter.CommandExists(alias_command)) { |
438 | 0 | result.AppendErrorWithFormat( |
439 | 0 | "'%s' is a permanent debugger command and cannot be redefined.\n", |
440 | 0 | args[0].c_str()); |
441 | 0 | return; |
442 | 0 | } |
443 | | |
444 | 25 | if (m_interpreter.UserMultiwordCommandExists(alias_command)) { |
445 | 1 | result.AppendErrorWithFormat( |
446 | 1 | "'%s' is a user container command and cannot be overwritten.\n" |
447 | 1 | "Delete it first with 'command container delete'\n", |
448 | 1 | args[0].c_str()); |
449 | 1 | return; |
450 | 1 | } |
451 | | |
452 | | // Get CommandObject that is being aliased. The command name is read from |
453 | | // the front of raw_command_string. raw_command_string is returned with the |
454 | | // name of the command object stripped off the front. |
455 | 24 | llvm::StringRef original_raw_command_string = raw_command_string; |
456 | 24 | CommandObject *cmd_obj = |
457 | 24 | m_interpreter.GetCommandObjectForCommand(raw_command_string); |
458 | | |
459 | 24 | if (!cmd_obj) { |
460 | 1 | result.AppendErrorWithFormat("invalid command given to 'command alias'. " |
461 | 1 | "'%s' does not begin with a valid command." |
462 | 1 | " No alias created.", |
463 | 1 | original_raw_command_string.str().c_str()); |
464 | 23 | } else if (!cmd_obj->WantsRawCommandString()) { |
465 | | // Note that args was initialized with the original command, and has not |
466 | | // been updated to this point. Therefore can we pass it to the version of |
467 | | // Execute that does not need/expect raw input in the alias. |
468 | 15 | HandleAliasingNormalCommand(args, result); |
469 | 15 | } else { |
470 | 8 | HandleAliasingRawCommand(alias_command, raw_command_string, *cmd_obj, |
471 | 8 | result); |
472 | 8 | } |
473 | 24 | } |
474 | | |
475 | | bool HandleAliasingRawCommand(llvm::StringRef alias_command, |
476 | | llvm::StringRef raw_command_string, |
477 | | CommandObject &cmd_obj, |
478 | 8 | CommandReturnObject &result) { |
479 | | // Verify & handle any options/arguments passed to the alias command |
480 | | |
481 | 8 | OptionArgVectorSP option_arg_vector_sp = |
482 | 8 | OptionArgVectorSP(new OptionArgVector); |
483 | | |
484 | 8 | const bool include_aliases = true; |
485 | | // Look up the command using command's name first. This is to resolve |
486 | | // aliases when you are making nested aliases. But if you don't find |
487 | | // it that way, then it wasn't an alias and we can just use the object |
488 | | // we were passed in. |
489 | 8 | CommandObjectSP cmd_obj_sp = m_interpreter.GetCommandSPExact( |
490 | 8 | cmd_obj.GetCommandName(), include_aliases); |
491 | 8 | if (!cmd_obj_sp) |
492 | 1 | cmd_obj_sp = cmd_obj.shared_from_this(); |
493 | | |
494 | 8 | if (m_interpreter.AliasExists(alias_command) || |
495 | 8 | m_interpreter.UserCommandExists(alias_command)) { |
496 | 0 | result.AppendWarningWithFormat( |
497 | 0 | "Overwriting existing definition for '%s'.\n", |
498 | 0 | alias_command.str().c_str()); |
499 | 0 | } |
500 | 8 | if (CommandAlias *alias = m_interpreter.AddAlias( |
501 | 8 | alias_command, cmd_obj_sp, raw_command_string)) { |
502 | 8 | if (m_command_options.m_help.OptionWasSet()) |
503 | 0 | alias->SetHelp(m_command_options.m_help.GetCurrentValue()); |
504 | 8 | if (m_command_options.m_long_help.OptionWasSet()) |
505 | 0 | alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue()); |
506 | 8 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
507 | 8 | } else { |
508 | 0 | result.AppendError("Unable to create requested alias.\n"); |
509 | 0 | } |
510 | 8 | return result.Succeeded(); |
511 | 8 | } |
512 | | |
513 | 15 | bool HandleAliasingNormalCommand(Args &args, CommandReturnObject &result) { |
514 | 15 | size_t argc = args.GetArgumentCount(); |
515 | | |
516 | 15 | if (argc < 2) { |
517 | 0 | result.AppendError("'command alias' requires at least two arguments"); |
518 | 0 | return false; |
519 | 0 | } |
520 | | |
521 | | // Save these in std::strings since we're going to shift them off. |
522 | 15 | const std::string alias_command(std::string(args[0].ref())); |
523 | 15 | const std::string actual_command(std::string(args[1].ref())); |
524 | | |
525 | 15 | args.Shift(); // Shift the alias command word off the argument vector. |
526 | 15 | args.Shift(); // Shift the old command word off the argument vector. |
527 | | |
528 | | // Verify that the command is alias'able, and get the appropriate command |
529 | | // object. |
530 | | |
531 | 15 | if (m_interpreter.CommandExists(alias_command)) { |
532 | 0 | result.AppendErrorWithFormat( |
533 | 0 | "'%s' is a permanent debugger command and cannot be redefined.\n", |
534 | 0 | alias_command.c_str()); |
535 | 0 | return false; |
536 | 0 | } |
537 | | |
538 | 15 | if (m_interpreter.UserMultiwordCommandExists(alias_command)) { |
539 | 0 | result.AppendErrorWithFormat( |
540 | 0 | "'%s' is user container command and cannot be overwritten.\n" |
541 | 0 | "Delete it first with 'command container delete'", |
542 | 0 | alias_command.c_str()); |
543 | 0 | return false; |
544 | 0 | } |
545 | | |
546 | 15 | CommandObjectSP command_obj_sp( |
547 | 15 | m_interpreter.GetCommandSPExact(actual_command, true)); |
548 | 15 | CommandObjectSP subcommand_obj_sp; |
549 | 15 | bool use_subcommand = false; |
550 | 15 | if (!command_obj_sp) { |
551 | 0 | result.AppendErrorWithFormat("'%s' is not an existing command.\n", |
552 | 0 | actual_command.c_str()); |
553 | 0 | return false; |
554 | 0 | } |
555 | 15 | CommandObject *cmd_obj = command_obj_sp.get(); |
556 | 15 | CommandObject *sub_cmd_obj = nullptr; |
557 | 15 | OptionArgVectorSP option_arg_vector_sp = |
558 | 15 | OptionArgVectorSP(new OptionArgVector); |
559 | | |
560 | 20 | while (cmd_obj->IsMultiwordObject() && !args.empty()9 ) { |
561 | 5 | auto sub_command = args[0].ref(); |
562 | 5 | assert(!sub_command.empty()); |
563 | 5 | subcommand_obj_sp = cmd_obj->GetSubcommandSP(sub_command); |
564 | 5 | if (!subcommand_obj_sp) { |
565 | 0 | result.AppendErrorWithFormat( |
566 | 0 | "'%s' is not a valid sub-command of '%s'. " |
567 | 0 | "Unable to create alias.\n", |
568 | 0 | args[0].c_str(), actual_command.c_str()); |
569 | 0 | return false; |
570 | 0 | } |
571 | | |
572 | 5 | sub_cmd_obj = subcommand_obj_sp.get(); |
573 | 5 | use_subcommand = true; |
574 | 5 | args.Shift(); // Shift the sub_command word off the argument vector. |
575 | 5 | cmd_obj = sub_cmd_obj; |
576 | 5 | } |
577 | | |
578 | | // Verify & handle any options/arguments passed to the alias command |
579 | | |
580 | 15 | std::string args_string; |
581 | | |
582 | 15 | if (!args.empty()) { |
583 | 7 | CommandObjectSP tmp_sp = |
584 | 7 | m_interpreter.GetCommandSPExact(cmd_obj->GetCommandName()); |
585 | 7 | if (use_subcommand) |
586 | 5 | tmp_sp = m_interpreter.GetCommandSPExact(sub_cmd_obj->GetCommandName()); |
587 | | |
588 | 7 | args.GetCommandString(args_string); |
589 | 7 | } |
590 | | |
591 | 15 | if (m_interpreter.AliasExists(alias_command) || |
592 | 15 | m_interpreter.UserCommandExists(alias_command)) { |
593 | 0 | result.AppendWarningWithFormat( |
594 | 0 | "Overwriting existing definition for '%s'.\n", alias_command.c_str()); |
595 | 0 | } |
596 | | |
597 | 15 | if (CommandAlias *alias = m_interpreter.AddAlias( |
598 | 15 | alias_command, use_subcommand ? subcommand_obj_sp : command_obj_sp, |
599 | 15 | args_string)) { |
600 | 15 | if (m_command_options.m_help.OptionWasSet()) |
601 | 1 | alias->SetHelp(m_command_options.m_help.GetCurrentValue()); |
602 | 15 | if (m_command_options.m_long_help.OptionWasSet()) |
603 | 1 | alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue()); |
604 | 15 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
605 | 15 | } else { |
606 | 0 | result.AppendError("Unable to create requested alias.\n"); |
607 | 0 | return false; |
608 | 0 | } |
609 | | |
610 | 15 | return result.Succeeded(); |
611 | 15 | } |
612 | | }; |
613 | | |
614 | | #pragma mark CommandObjectCommandsUnalias |
615 | | // CommandObjectCommandsUnalias |
616 | | |
617 | | class CommandObjectCommandsUnalias : public CommandObjectParsed { |
618 | | public: |
619 | | CommandObjectCommandsUnalias(CommandInterpreter &interpreter) |
620 | 6.14k | : CommandObjectParsed( |
621 | 6.14k | interpreter, "command unalias", |
622 | 6.14k | "Delete one or more custom commands defined by 'command alias'.", |
623 | 6.14k | nullptr) { |
624 | 6.14k | CommandArgumentEntry arg; |
625 | 6.14k | CommandArgumentData alias_arg; |
626 | | |
627 | | // Define the first (and only) variant of this arg. |
628 | 6.14k | alias_arg.arg_type = eArgTypeAliasName; |
629 | 6.14k | alias_arg.arg_repetition = eArgRepeatPlain; |
630 | | |
631 | | // There is only one variant this argument could be; put it into the |
632 | | // argument entry. |
633 | 6.14k | arg.push_back(alias_arg); |
634 | | |
635 | | // Push the data for the first argument into the m_arguments vector. |
636 | 6.14k | m_arguments.push_back(arg); |
637 | 6.14k | } |
638 | | |
639 | 6.13k | ~CommandObjectCommandsUnalias() override = default; |
640 | | |
641 | | void |
642 | | HandleArgumentCompletion(CompletionRequest &request, |
643 | 1 | OptionElementVector &opt_element_vector) override { |
644 | 1 | if (!m_interpreter.HasCommands() || request.GetCursorIndex() != 0) |
645 | 0 | return; |
646 | | |
647 | 53 | for (const auto &ent : m_interpreter.GetAliases())1 { |
648 | 53 | request.TryCompleteCurrentArg(ent.first, ent.second->GetHelp()); |
649 | 53 | } |
650 | 1 | } |
651 | | |
652 | | protected: |
653 | 14 | void DoExecute(Args &args, CommandReturnObject &result) override { |
654 | 14 | CommandObject::CommandMap::iterator pos; |
655 | 14 | CommandObject *cmd_obj; |
656 | | |
657 | 14 | if (args.empty()) { |
658 | 1 | result.AppendError("must call 'unalias' with a valid alias"); |
659 | 1 | return; |
660 | 1 | } |
661 | | |
662 | 13 | auto command_name = args[0].ref(); |
663 | 13 | cmd_obj = m_interpreter.GetCommandObject(command_name); |
664 | 13 | if (!cmd_obj) { |
665 | 0 | result.AppendErrorWithFormat( |
666 | 0 | "'%s' is not a known command.\nTry 'help' to see a " |
667 | 0 | "current list of commands.\n", |
668 | 0 | args[0].c_str()); |
669 | 0 | return; |
670 | 0 | } |
671 | | |
672 | 13 | if (m_interpreter.CommandExists(command_name)) { |
673 | 1 | if (cmd_obj->IsRemovable()) { |
674 | 1 | result.AppendErrorWithFormat( |
675 | 1 | "'%s' is not an alias, it is a debugger command which can be " |
676 | 1 | "removed using the 'command delete' command.\n", |
677 | 1 | args[0].c_str()); |
678 | 1 | } else { |
679 | 0 | result.AppendErrorWithFormat( |
680 | 0 | "'%s' is a permanent debugger command and cannot be removed.\n", |
681 | 0 | args[0].c_str()); |
682 | 0 | } |
683 | 1 | return; |
684 | 1 | } |
685 | | |
686 | 12 | if (!m_interpreter.RemoveAlias(command_name)) { |
687 | 1 | if (m_interpreter.AliasExists(command_name)) |
688 | 0 | result.AppendErrorWithFormat( |
689 | 0 | "Error occurred while attempting to unalias '%s'.\n", |
690 | 0 | args[0].c_str()); |
691 | 1 | else |
692 | 1 | result.AppendErrorWithFormat("'%s' is not an existing alias.\n", |
693 | 1 | args[0].c_str()); |
694 | 1 | return; |
695 | 1 | } |
696 | | |
697 | 11 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
698 | 11 | } |
699 | | }; |
700 | | |
701 | | #pragma mark CommandObjectCommandsDelete |
702 | | // CommandObjectCommandsDelete |
703 | | |
704 | | class CommandObjectCommandsDelete : public CommandObjectParsed { |
705 | | public: |
706 | | CommandObjectCommandsDelete(CommandInterpreter &interpreter) |
707 | 6.14k | : CommandObjectParsed( |
708 | 6.14k | interpreter, "command delete", |
709 | 6.14k | "Delete one or more custom commands defined by 'command regex'.", |
710 | 6.14k | nullptr) { |
711 | 6.14k | CommandArgumentEntry arg; |
712 | 6.14k | CommandArgumentData alias_arg; |
713 | | |
714 | | // Define the first (and only) variant of this arg. |
715 | 6.14k | alias_arg.arg_type = eArgTypeCommandName; |
716 | 6.14k | alias_arg.arg_repetition = eArgRepeatPlain; |
717 | | |
718 | | // There is only one variant this argument could be; put it into the |
719 | | // argument entry. |
720 | 6.14k | arg.push_back(alias_arg); |
721 | | |
722 | | // Push the data for the first argument into the m_arguments vector. |
723 | 6.14k | m_arguments.push_back(arg); |
724 | 6.14k | } |
725 | | |
726 | 6.13k | ~CommandObjectCommandsDelete() override = default; |
727 | | |
728 | | void |
729 | | HandleArgumentCompletion(CompletionRequest &request, |
730 | 1 | OptionElementVector &opt_element_vector) override { |
731 | 1 | if (!m_interpreter.HasCommands() || request.GetCursorIndex() != 0) |
732 | 0 | return; |
733 | | |
734 | 43 | for (const auto &ent : m_interpreter.GetCommands())1 { |
735 | 43 | if (ent.second->IsRemovable()) |
736 | 1 | request.TryCompleteCurrentArg(ent.first, ent.second->GetHelp()); |
737 | 43 | } |
738 | 1 | } |
739 | | |
740 | | protected: |
741 | 4 | void DoExecute(Args &args, CommandReturnObject &result) override { |
742 | 4 | CommandObject::CommandMap::iterator pos; |
743 | | |
744 | 4 | if (args.empty()) { |
745 | 1 | result.AppendErrorWithFormat("must call '%s' with one or more valid user " |
746 | 1 | "defined regular expression command names", |
747 | 1 | GetCommandName().str().c_str()); |
748 | 1 | return; |
749 | 1 | } |
750 | | |
751 | 3 | auto command_name = args[0].ref(); |
752 | 3 | if (!m_interpreter.CommandExists(command_name)) { |
753 | 1 | StreamString error_msg_stream; |
754 | 1 | const bool generate_upropos = true; |
755 | 1 | const bool generate_type_lookup = false; |
756 | 1 | CommandObjectHelp::GenerateAdditionalHelpAvenuesMessage( |
757 | 1 | &error_msg_stream, command_name, llvm::StringRef(), llvm::StringRef(), |
758 | 1 | generate_upropos, generate_type_lookup); |
759 | 1 | result.AppendError(error_msg_stream.GetString()); |
760 | 1 | return; |
761 | 1 | } |
762 | | |
763 | 2 | if (!m_interpreter.RemoveCommand(command_name)) { |
764 | 1 | result.AppendErrorWithFormat( |
765 | 1 | "'%s' is a permanent debugger command and cannot be removed.\n", |
766 | 1 | args[0].c_str()); |
767 | 1 | return; |
768 | 1 | } |
769 | | |
770 | 1 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
771 | 1 | } |
772 | | }; |
773 | | |
774 | | // CommandObjectCommandsAddRegex |
775 | | |
776 | | #define LLDB_OPTIONS_regex |
777 | | #include "CommandOptions.inc" |
778 | | |
779 | | #pragma mark CommandObjectCommandsAddRegex |
780 | | |
781 | | class CommandObjectCommandsAddRegex : public CommandObjectParsed, |
782 | | public IOHandlerDelegateMultiline { |
783 | | public: |
784 | | CommandObjectCommandsAddRegex(CommandInterpreter &interpreter) |
785 | 6.14k | : CommandObjectParsed( |
786 | 6.14k | interpreter, "command regex", |
787 | 6.14k | "Define a custom command in terms of " |
788 | 6.14k | "existing commands by matching " |
789 | 6.14k | "regular expressions.", |
790 | 6.14k | "command regex <cmd-name> [s/<regex>/<subst>/ ...]"), |
791 | 6.14k | IOHandlerDelegateMultiline("", |
792 | 6.14k | IOHandlerDelegate::Completion::LLDBCommand) { |
793 | 6.14k | SetHelpLong( |
794 | 6.14k | R"( |
795 | 6.14k | )" |
796 | 6.14k | "This command allows the user to create powerful regular expression commands \ |
797 | 6.14k | with substitutions. The regular expressions and substitutions are specified \ |
798 | 6.14k | using the regular expression substitution format of:" |
799 | 6.14k | R"( |
800 | 6.14k | |
801 | 6.14k | s/<regex>/<subst>/ |
802 | 6.14k | |
803 | 6.14k | )" |
804 | 6.14k | "<regex> is a regular expression that can use parenthesis to capture regular \ |
805 | 6.14k | expression input and substitute the captured matches in the output using %1 \ |
806 | 6.14k | for the first match, %2 for the second, and so on." |
807 | 6.14k | R"( |
808 | 6.14k | |
809 | 6.14k | )" |
810 | 6.14k | "The regular expressions can all be specified on the command line if more than \ |
811 | 6.14k | one argument is provided. If just the command name is provided on the command \ |
812 | 6.14k | line, then the regular expressions and substitutions can be entered on separate \ |
813 | 6.14k | lines, followed by an empty line to terminate the command definition." |
814 | 6.14k | R"( |
815 | 6.14k | |
816 | 6.14k | EXAMPLES |
817 | 6.14k | |
818 | 6.14k | )" |
819 | 6.14k | "The following example will define a regular expression command named 'f' that \ |
820 | 6.14k | will call 'finish' if there are no arguments, or 'frame select <frame-idx>' if \ |
821 | 6.14k | a number follows 'f':" |
822 | 6.14k | R"( |
823 | 6.14k | |
824 | 6.14k | (lldb) command regex f s/^$/finish/ 's/([0-9]+)/frame select %1/')"); |
825 | 6.14k | CommandArgumentData thread_arg{eArgTypeSEDStylePair, eArgRepeatOptional}; |
826 | 6.14k | m_arguments.push_back({thread_arg}); |
827 | 6.14k | } |
828 | | |
829 | 6.13k | ~CommandObjectCommandsAddRegex() override = default; |
830 | | |
831 | | protected: |
832 | 2 | void IOHandlerActivated(IOHandler &io_handler, bool interactive) override { |
833 | 2 | StreamFileSP output_sp(io_handler.GetOutputStreamFileSP()); |
834 | 2 | if (output_sp && interactive) { |
835 | 0 | output_sp->PutCString("Enter one or more sed substitution commands in " |
836 | 0 | "the form: 's/<regex>/<subst>/'.\nTerminate the " |
837 | 0 | "substitution list with an empty line.\n"); |
838 | 0 | output_sp->Flush(); |
839 | 0 | } |
840 | 2 | } |
841 | | |
842 | | void IOHandlerInputComplete(IOHandler &io_handler, |
843 | 2 | std::string &data) override { |
844 | 2 | io_handler.SetIsDone(true); |
845 | 2 | if (m_regex_cmd_up) { |
846 | 2 | StringList lines; |
847 | 2 | if (lines.SplitIntoLines(data)) { |
848 | 2 | bool check_only = false; |
849 | 6 | for (const std::string &line : lines) { |
850 | 6 | Status error = AppendRegexSubstitution(line, check_only); |
851 | 6 | if (error.Fail()) { |
852 | 4 | if (!GetDebugger().GetCommandInterpreter().GetBatchCommandMode()) { |
853 | 4 | StreamSP out_stream = GetDebugger().GetAsyncOutputStream(); |
854 | 4 | out_stream->Printf("error: %s\n", error.AsCString()); |
855 | 4 | } |
856 | 4 | } |
857 | 6 | } |
858 | 2 | } |
859 | 2 | if (m_regex_cmd_up->HasRegexEntries()) { |
860 | 2 | CommandObjectSP cmd_sp(m_regex_cmd_up.release()); |
861 | 2 | m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true); |
862 | 2 | } |
863 | 2 | } |
864 | 2 | } |
865 | | |
866 | 6 | void DoExecute(Args &command, CommandReturnObject &result) override { |
867 | 6 | const size_t argc = command.GetArgumentCount(); |
868 | 6 | if (argc == 0) { |
869 | 1 | result.AppendError("usage: 'command regex <command-name> " |
870 | 1 | "[s/<regex1>/<subst1>/ s/<regex2>/<subst2>/ ...]'\n"); |
871 | 1 | return; |
872 | 1 | } |
873 | | |
874 | 5 | Status error; |
875 | 5 | auto name = command[0].ref(); |
876 | 5 | m_regex_cmd_up = std::make_unique<CommandObjectRegexCommand>( |
877 | 5 | m_interpreter, name, m_options.GetHelp(), m_options.GetSyntax(), 0, |
878 | 5 | true); |
879 | | |
880 | 5 | if (argc == 1) { |
881 | 2 | Debugger &debugger = GetDebugger(); |
882 | 2 | bool color_prompt = debugger.GetUseColor(); |
883 | 2 | const bool multiple_lines = true; // Get multiple lines |
884 | 2 | IOHandlerSP io_handler_sp(new IOHandlerEditline( |
885 | 2 | debugger, IOHandler::Type::Other, |
886 | 2 | "lldb-regex", // Name of input reader for history |
887 | 2 | llvm::StringRef("> "), // Prompt |
888 | 2 | llvm::StringRef(), // Continuation prompt |
889 | 2 | multiple_lines, color_prompt, |
890 | 2 | 0, // Don't show line numbers |
891 | 2 | *this)); |
892 | | |
893 | 2 | if (io_handler_sp) { |
894 | 2 | debugger.RunIOHandlerAsync(io_handler_sp); |
895 | 2 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
896 | 2 | } |
897 | 3 | } else { |
898 | 4 | for (auto &entry : command.entries().drop_front()) { |
899 | 4 | bool check_only = false; |
900 | 4 | error = AppendRegexSubstitution(entry.ref(), check_only); |
901 | 4 | if (error.Fail()) |
902 | 0 | break; |
903 | 4 | } |
904 | | |
905 | 3 | if (error.Success()) { |
906 | 3 | AddRegexCommandToInterpreter(); |
907 | 3 | } |
908 | 3 | } |
909 | 5 | if (error.Fail()) { |
910 | 0 | result.AppendError(error.AsCString()); |
911 | 0 | } |
912 | 5 | } |
913 | | |
914 | | Status AppendRegexSubstitution(const llvm::StringRef ®ex_sed, |
915 | 10 | bool check_only) { |
916 | 10 | Status error; |
917 | | |
918 | 10 | if (!m_regex_cmd_up) { |
919 | 0 | error.SetErrorStringWithFormat( |
920 | 0 | "invalid regular expression command object for: '%.*s'", |
921 | 0 | (int)regex_sed.size(), regex_sed.data()); |
922 | 0 | return error; |
923 | 0 | } |
924 | | |
925 | 10 | size_t regex_sed_size = regex_sed.size(); |
926 | | |
927 | 10 | if (regex_sed_size <= 1) { |
928 | 0 | error.SetErrorStringWithFormat( |
929 | 0 | "regular expression substitution string is too short: '%.*s'", |
930 | 0 | (int)regex_sed.size(), regex_sed.data()); |
931 | 0 | return error; |
932 | 0 | } |
933 | | |
934 | 10 | if (regex_sed[0] != 's') { |
935 | 4 | error.SetErrorStringWithFormat("regular expression substitution string " |
936 | 4 | "doesn't start with 's': '%.*s'", |
937 | 4 | (int)regex_sed.size(), regex_sed.data()); |
938 | 4 | return error; |
939 | 4 | } |
940 | 6 | const size_t first_separator_char_pos = 1; |
941 | | // use the char that follows 's' as the regex separator character so we can |
942 | | // have "s/<regex>/<subst>/" or "s|<regex>|<subst>|" |
943 | 6 | const char separator_char = regex_sed[first_separator_char_pos]; |
944 | 6 | const size_t second_separator_char_pos = |
945 | 6 | regex_sed.find(separator_char, first_separator_char_pos + 1); |
946 | | |
947 | 6 | if (second_separator_char_pos == std::string::npos) { |
948 | 0 | error.SetErrorStringWithFormat( |
949 | 0 | "missing second '%c' separator char after '%.*s' in '%.*s'", |
950 | 0 | separator_char, |
951 | 0 | (int)(regex_sed.size() - first_separator_char_pos - 1), |
952 | 0 | regex_sed.data() + (first_separator_char_pos + 1), |
953 | 0 | (int)regex_sed.size(), regex_sed.data()); |
954 | 0 | return error; |
955 | 0 | } |
956 | | |
957 | 6 | const size_t third_separator_char_pos = |
958 | 6 | regex_sed.find(separator_char, second_separator_char_pos + 1); |
959 | | |
960 | 6 | if (third_separator_char_pos == std::string::npos) { |
961 | 0 | error.SetErrorStringWithFormat( |
962 | 0 | "missing third '%c' separator char after '%.*s' in '%.*s'", |
963 | 0 | separator_char, |
964 | 0 | (int)(regex_sed.size() - second_separator_char_pos - 1), |
965 | 0 | regex_sed.data() + (second_separator_char_pos + 1), |
966 | 0 | (int)regex_sed.size(), regex_sed.data()); |
967 | 0 | return error; |
968 | 0 | } |
969 | | |
970 | 6 | if (third_separator_char_pos != regex_sed_size - 1) { |
971 | | // Make sure that everything that follows the last regex separator char |
972 | 0 | if (regex_sed.find_first_not_of("\t\n\v\f\r ", |
973 | 0 | third_separator_char_pos + 1) != |
974 | 0 | std::string::npos) { |
975 | 0 | error.SetErrorStringWithFormat( |
976 | 0 | "extra data found after the '%.*s' regular expression substitution " |
977 | 0 | "string: '%.*s'", |
978 | 0 | (int)third_separator_char_pos + 1, regex_sed.data(), |
979 | 0 | (int)(regex_sed.size() - third_separator_char_pos - 1), |
980 | 0 | regex_sed.data() + (third_separator_char_pos + 1)); |
981 | 0 | return error; |
982 | 0 | } |
983 | 6 | } else if (first_separator_char_pos + 1 == second_separator_char_pos) { |
984 | 0 | error.SetErrorStringWithFormat( |
985 | 0 | "<regex> can't be empty in 's%c<regex>%c<subst>%c' string: '%.*s'", |
986 | 0 | separator_char, separator_char, separator_char, (int)regex_sed.size(), |
987 | 0 | regex_sed.data()); |
988 | 0 | return error; |
989 | 6 | } else if (second_separator_char_pos + 1 == third_separator_char_pos) { |
990 | 0 | error.SetErrorStringWithFormat( |
991 | 0 | "<subst> can't be empty in 's%c<regex>%c<subst>%c' string: '%.*s'", |
992 | 0 | separator_char, separator_char, separator_char, (int)regex_sed.size(), |
993 | 0 | regex_sed.data()); |
994 | 0 | return error; |
995 | 0 | } |
996 | | |
997 | 6 | if (!check_only) { |
998 | 6 | std::string regex(std::string(regex_sed.substr( |
999 | 6 | first_separator_char_pos + 1, |
1000 | 6 | second_separator_char_pos - first_separator_char_pos - 1))); |
1001 | 6 | std::string subst(std::string(regex_sed.substr( |
1002 | 6 | second_separator_char_pos + 1, |
1003 | 6 | third_separator_char_pos - second_separator_char_pos - 1))); |
1004 | 6 | m_regex_cmd_up->AddRegexCommand(regex, subst); |
1005 | 6 | } |
1006 | 6 | return error; |
1007 | 6 | } |
1008 | | |
1009 | 3 | void AddRegexCommandToInterpreter() { |
1010 | 3 | if (m_regex_cmd_up) { |
1011 | 3 | if (m_regex_cmd_up->HasRegexEntries()) { |
1012 | 3 | CommandObjectSP cmd_sp(m_regex_cmd_up.release()); |
1013 | 3 | m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true); |
1014 | 3 | } |
1015 | 3 | } |
1016 | 3 | } |
1017 | | |
1018 | | private: |
1019 | | std::unique_ptr<CommandObjectRegexCommand> m_regex_cmd_up; |
1020 | | |
1021 | | class CommandOptions : public Options { |
1022 | | public: |
1023 | 6.14k | CommandOptions() = default; |
1024 | | |
1025 | 6.13k | ~CommandOptions() override = default; |
1026 | | |
1027 | | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
1028 | 0 | ExecutionContext *execution_context) override { |
1029 | 0 | Status error; |
1030 | 0 | const int short_option = m_getopt_table[option_idx].val; |
1031 | |
|
1032 | 0 | switch (short_option) { |
1033 | 0 | case 'h': |
1034 | 0 | m_help.assign(std::string(option_arg)); |
1035 | 0 | break; |
1036 | 0 | case 's': |
1037 | 0 | m_syntax.assign(std::string(option_arg)); |
1038 | 0 | break; |
1039 | 0 | default: |
1040 | 0 | llvm_unreachable("Unimplemented option"); |
1041 | 0 | } |
1042 | | |
1043 | 0 | return error; |
1044 | 0 | } |
1045 | | |
1046 | 6 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
1047 | 6 | m_help.clear(); |
1048 | 6 | m_syntax.clear(); |
1049 | 6 | } |
1050 | | |
1051 | 15 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
1052 | 15 | return llvm::ArrayRef(g_regex_options); |
1053 | 15 | } |
1054 | | |
1055 | 5 | llvm::StringRef GetHelp() { return m_help; } |
1056 | | |
1057 | 5 | llvm::StringRef GetSyntax() { return m_syntax; } |
1058 | | |
1059 | | protected: |
1060 | | // Instance variables to hold the values for command options. |
1061 | | |
1062 | | std::string m_help; |
1063 | | std::string m_syntax; |
1064 | | }; |
1065 | | |
1066 | 6 | Options *GetOptions() override { return &m_options; } |
1067 | | |
1068 | | CommandOptions m_options; |
1069 | | }; |
1070 | | |
1071 | | class CommandObjectPythonFunction : public CommandObjectRaw { |
1072 | | public: |
1073 | | CommandObjectPythonFunction(CommandInterpreter &interpreter, std::string name, |
1074 | | std::string funct, std::string help, |
1075 | | ScriptedCommandSynchronicity synch, |
1076 | | CompletionType completion_type) |
1077 | 75 | : CommandObjectRaw(interpreter, name), m_function_name(funct), |
1078 | 75 | m_synchro(synch), m_completion_type(completion_type) { |
1079 | 75 | if (!help.empty()) |
1080 | 2 | SetHelp(help); |
1081 | 73 | else { |
1082 | 73 | StreamString stream; |
1083 | 73 | stream.Printf("For more information run 'help %s'", name.c_str()); |
1084 | 73 | SetHelp(stream.GetString()); |
1085 | 73 | } |
1086 | 75 | } |
1087 | | |
1088 | 73 | ~CommandObjectPythonFunction() override = default; |
1089 | | |
1090 | 0 | bool IsRemovable() const override { return true; } |
1091 | | |
1092 | 0 | const std::string &GetFunctionName() { return m_function_name; } |
1093 | | |
1094 | 0 | ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; } |
1095 | | |
1096 | 4 | llvm::StringRef GetHelpLong() override { |
1097 | 4 | if (m_fetched_help_long) |
1098 | 0 | return CommandObjectRaw::GetHelpLong(); |
1099 | | |
1100 | 4 | ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); |
1101 | 4 | if (!scripter) |
1102 | 0 | return CommandObjectRaw::GetHelpLong(); |
1103 | | |
1104 | 4 | std::string docstring; |
1105 | 4 | m_fetched_help_long = |
1106 | 4 | scripter->GetDocumentationForItem(m_function_name.c_str(), docstring); |
1107 | 4 | if (!docstring.empty()) |
1108 | 4 | SetHelpLong(docstring); |
1109 | 4 | return CommandObjectRaw::GetHelpLong(); |
1110 | 4 | } |
1111 | | |
1112 | | void |
1113 | | HandleArgumentCompletion(CompletionRequest &request, |
1114 | 1 | OptionElementVector &opt_element_vector) override { |
1115 | 1 | lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( |
1116 | 1 | GetCommandInterpreter(), m_completion_type, request, nullptr); |
1117 | 1 | } |
1118 | | |
1119 | 1 | bool WantsCompletion() override { return true; } |
1120 | | |
1121 | | protected: |
1122 | | void DoExecute(llvm::StringRef raw_command_line, |
1123 | 51 | CommandReturnObject &result) override { |
1124 | 51 | ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); |
1125 | | |
1126 | 51 | Status error; |
1127 | | |
1128 | 51 | result.SetStatus(eReturnStatusInvalid); |
1129 | | |
1130 | 51 | if (!scripter || !scripter->RunScriptBasedCommand( |
1131 | 51 | m_function_name.c_str(), raw_command_line, m_synchro, |
1132 | 51 | result, error, m_exe_ctx)) { |
1133 | 1 | result.AppendError(error.AsCString()); |
1134 | 50 | } else { |
1135 | | // Don't change the status if the command already set it... |
1136 | 50 | if (result.GetStatus() == eReturnStatusInvalid) { |
1137 | 47 | if (result.GetOutputData().empty()) |
1138 | 6 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1139 | 41 | else |
1140 | 41 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1141 | 47 | } |
1142 | 50 | } |
1143 | 51 | } |
1144 | | |
1145 | | private: |
1146 | | std::string m_function_name; |
1147 | | ScriptedCommandSynchronicity m_synchro; |
1148 | | bool m_fetched_help_long = false; |
1149 | | CompletionType m_completion_type = eNoCompletion; |
1150 | | }; |
1151 | | |
1152 | | class CommandObjectScriptingObject : public CommandObjectRaw { |
1153 | | public: |
1154 | | CommandObjectScriptingObject(CommandInterpreter &interpreter, |
1155 | | std::string name, |
1156 | | StructuredData::GenericSP cmd_obj_sp, |
1157 | | ScriptedCommandSynchronicity synch, |
1158 | | CompletionType completion_type) |
1159 | 23 | : CommandObjectRaw(interpreter, name), m_cmd_obj_sp(cmd_obj_sp), |
1160 | 23 | m_synchro(synch), m_fetched_help_short(false), |
1161 | 23 | m_fetched_help_long(false), m_completion_type(completion_type) { |
1162 | 23 | StreamString stream; |
1163 | 23 | stream.Printf("For more information run 'help %s'", name.c_str()); |
1164 | 23 | SetHelp(stream.GetString()); |
1165 | 23 | if (ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter()) |
1166 | 23 | GetFlags().Set(scripter->GetFlagsForCommandObject(cmd_obj_sp)); |
1167 | 23 | } |
1168 | | |
1169 | 20 | ~CommandObjectScriptingObject() override = default; |
1170 | | |
1171 | | void |
1172 | | HandleArgumentCompletion(CompletionRequest &request, |
1173 | 0 | OptionElementVector &opt_element_vector) override { |
1174 | 0 | lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( |
1175 | 0 | GetCommandInterpreter(), m_completion_type, request, nullptr); |
1176 | 0 | } |
1177 | | |
1178 | 0 | bool WantsCompletion() override { return true; } |
1179 | | |
1180 | 1 | bool IsRemovable() const override { return true; } |
1181 | | |
1182 | 0 | ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; } |
1183 | | |
1184 | 15 | llvm::StringRef GetHelp() override { |
1185 | 15 | if (m_fetched_help_short) |
1186 | 7 | return CommandObjectRaw::GetHelp(); |
1187 | 8 | ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); |
1188 | 8 | if (!scripter) |
1189 | 0 | return CommandObjectRaw::GetHelp(); |
1190 | 8 | std::string docstring; |
1191 | 8 | m_fetched_help_short = |
1192 | 8 | scripter->GetShortHelpForCommandObject(m_cmd_obj_sp, docstring); |
1193 | 8 | if (!docstring.empty()) |
1194 | 4 | SetHelp(docstring); |
1195 | | |
1196 | 8 | return CommandObjectRaw::GetHelp(); |
1197 | 8 | } |
1198 | | |
1199 | 4 | llvm::StringRef GetHelpLong() override { |
1200 | 4 | if (m_fetched_help_long) |
1201 | 0 | return CommandObjectRaw::GetHelpLong(); |
1202 | | |
1203 | 4 | ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); |
1204 | 4 | if (!scripter) |
1205 | 0 | return CommandObjectRaw::GetHelpLong(); |
1206 | | |
1207 | 4 | std::string docstring; |
1208 | 4 | m_fetched_help_long = |
1209 | 4 | scripter->GetLongHelpForCommandObject(m_cmd_obj_sp, docstring); |
1210 | 4 | if (!docstring.empty()) |
1211 | 0 | SetHelpLong(docstring); |
1212 | 4 | return CommandObjectRaw::GetHelpLong(); |
1213 | 4 | } |
1214 | | |
1215 | | protected: |
1216 | | void DoExecute(llvm::StringRef raw_command_line, |
1217 | 31 | CommandReturnObject &result) override { |
1218 | 31 | ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); |
1219 | | |
1220 | 31 | Status error; |
1221 | | |
1222 | 31 | result.SetStatus(eReturnStatusInvalid); |
1223 | | |
1224 | 31 | if (!scripter || |
1225 | 31 | !scripter->RunScriptBasedCommand(m_cmd_obj_sp, raw_command_line, |
1226 | 31 | m_synchro, result, error, m_exe_ctx)) { |
1227 | 1 | result.AppendError(error.AsCString()); |
1228 | 30 | } else { |
1229 | | // Don't change the status if the command already set it... |
1230 | 30 | if (result.GetStatus() == eReturnStatusInvalid) { |
1231 | 19 | if (result.GetOutputData().empty()) |
1232 | 6 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1233 | 13 | else |
1234 | 13 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1235 | 19 | } |
1236 | 30 | } |
1237 | 31 | } |
1238 | | |
1239 | | private: |
1240 | | StructuredData::GenericSP m_cmd_obj_sp; |
1241 | | ScriptedCommandSynchronicity m_synchro; |
1242 | | bool m_fetched_help_short : 1; |
1243 | | bool m_fetched_help_long : 1; |
1244 | | CompletionType m_completion_type = eNoCompletion; |
1245 | | }; |
1246 | | |
1247 | | // CommandObjectCommandsScriptImport |
1248 | | #define LLDB_OPTIONS_script_import |
1249 | | #include "CommandOptions.inc" |
1250 | | |
1251 | | class CommandObjectCommandsScriptImport : public CommandObjectParsed { |
1252 | | public: |
1253 | | CommandObjectCommandsScriptImport(CommandInterpreter &interpreter) |
1254 | 6.14k | : CommandObjectParsed(interpreter, "command script import", |
1255 | 6.14k | "Import a scripting module in LLDB.", nullptr) { |
1256 | 6.14k | CommandArgumentEntry arg1; |
1257 | 6.14k | CommandArgumentData cmd_arg; |
1258 | | |
1259 | | // Define the first (and only) variant of this arg. |
1260 | 6.14k | cmd_arg.arg_type = eArgTypeFilename; |
1261 | 6.14k | cmd_arg.arg_repetition = eArgRepeatPlus; |
1262 | | |
1263 | | // There is only one variant this argument could be; put it into the |
1264 | | // argument entry. |
1265 | 6.14k | arg1.push_back(cmd_arg); |
1266 | | |
1267 | | // Push the data for the first argument into the m_arguments vector. |
1268 | 6.14k | m_arguments.push_back(arg1); |
1269 | 6.14k | } |
1270 | | |
1271 | 6.13k | ~CommandObjectCommandsScriptImport() override = default; |
1272 | | |
1273 | | void |
1274 | | HandleArgumentCompletion(CompletionRequest &request, |
1275 | 0 | OptionElementVector &opt_element_vector) override { |
1276 | 0 | lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( |
1277 | 0 | GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr); |
1278 | 0 | } |
1279 | | |
1280 | 120 | Options *GetOptions() override { return &m_options; } |
1281 | | |
1282 | | protected: |
1283 | | class CommandOptions : public Options { |
1284 | | public: |
1285 | 6.14k | CommandOptions() = default; |
1286 | | |
1287 | 6.13k | ~CommandOptions() override = default; |
1288 | | |
1289 | | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
1290 | 29 | ExecutionContext *execution_context) override { |
1291 | 29 | Status error; |
1292 | 29 | const int short_option = m_getopt_table[option_idx].val; |
1293 | | |
1294 | 29 | switch (short_option) { |
1295 | 24 | case 'r': |
1296 | | // NO-OP |
1297 | 24 | break; |
1298 | 3 | case 'c': |
1299 | 3 | relative_to_command_file = true; |
1300 | 3 | break; |
1301 | 2 | case 's': |
1302 | 2 | silent = true; |
1303 | 2 | break; |
1304 | 0 | default: |
1305 | 0 | llvm_unreachable("Unimplemented option"); |
1306 | 29 | } |
1307 | | |
1308 | 29 | return error; |
1309 | 29 | } |
1310 | | |
1311 | 113 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
1312 | 113 | relative_to_command_file = false; |
1313 | 113 | } |
1314 | | |
1315 | 285 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
1316 | 285 | return llvm::ArrayRef(g_script_import_options); |
1317 | 285 | } |
1318 | | bool relative_to_command_file = false; |
1319 | | bool silent = false; |
1320 | | }; |
1321 | | |
1322 | 113 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1323 | 113 | if (command.empty()) { |
1324 | 1 | result.AppendError("command script import needs one or more arguments"); |
1325 | 1 | return; |
1326 | 1 | } |
1327 | | |
1328 | 112 | FileSpec source_dir = {}; |
1329 | 112 | if (m_options.relative_to_command_file) { |
1330 | 3 | source_dir = GetDebugger().GetCommandInterpreter().GetCurrentSourceDir(); |
1331 | 3 | if (!source_dir) { |
1332 | 1 | result.AppendError("command script import -c can only be specified " |
1333 | 1 | "from a command file"); |
1334 | 1 | return; |
1335 | 1 | } |
1336 | 3 | } |
1337 | | |
1338 | 111 | for (auto &entry : command.entries()) { |
1339 | 111 | Status error; |
1340 | | |
1341 | 111 | LoadScriptOptions options; |
1342 | 111 | options.SetInitSession(true); |
1343 | 111 | options.SetSilent(m_options.silent); |
1344 | | |
1345 | | // FIXME: this is necessary because CommandObject::CheckRequirements() |
1346 | | // assumes that commands won't ever be recursively invoked, but it's |
1347 | | // actually possible to craft a Python script that does other "command |
1348 | | // script imports" in __lldb_init_module the real fix is to have |
1349 | | // recursive commands possible with a CommandInvocation object separate |
1350 | | // from the CommandObject itself, so that recursive command invocations |
1351 | | // won't stomp on each other (wrt to execution contents, options, and |
1352 | | // more) |
1353 | 111 | m_exe_ctx.Clear(); |
1354 | 111 | if (GetDebugger().GetScriptInterpreter()->LoadScriptingModule( |
1355 | 111 | entry.c_str(), options, error, /*module_sp=*/nullptr, |
1356 | 111 | source_dir)) { |
1357 | 99 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1358 | 99 | } else { |
1359 | 12 | result.AppendErrorWithFormat("module importing failed: %s", |
1360 | 12 | error.AsCString()); |
1361 | 12 | } |
1362 | 111 | } |
1363 | 111 | } |
1364 | | |
1365 | | CommandOptions m_options; |
1366 | | }; |
1367 | | |
1368 | | #define LLDB_OPTIONS_script_add |
1369 | | #include "CommandOptions.inc" |
1370 | | |
1371 | | class CommandObjectCommandsScriptAdd : public CommandObjectParsed, |
1372 | | public IOHandlerDelegateMultiline { |
1373 | | public: |
1374 | | CommandObjectCommandsScriptAdd(CommandInterpreter &interpreter) |
1375 | 6.14k | : CommandObjectParsed(interpreter, "command script add", |
1376 | 6.14k | "Add a scripted function as an LLDB command.", |
1377 | 6.14k | "Add a scripted function as an lldb command. " |
1378 | 6.14k | "If you provide a single argument, the command " |
1379 | 6.14k | "will be added at the root level of the command " |
1380 | 6.14k | "hierarchy. If there are more arguments they " |
1381 | 6.14k | "must be a path to a user-added container " |
1382 | 6.14k | "command, and the last element will be the new " |
1383 | 6.14k | "command name."), |
1384 | 6.14k | IOHandlerDelegateMultiline("DONE") { |
1385 | 6.14k | CommandArgumentEntry arg1; |
1386 | 6.14k | CommandArgumentData cmd_arg; |
1387 | | |
1388 | | // This is one or more command names, which form the path to the command |
1389 | | // you want to add. |
1390 | 6.14k | cmd_arg.arg_type = eArgTypeCommand; |
1391 | 6.14k | cmd_arg.arg_repetition = eArgRepeatPlus; |
1392 | | |
1393 | | // There is only one variant this argument could be; put it into the |
1394 | | // argument entry. |
1395 | 6.14k | arg1.push_back(cmd_arg); |
1396 | | |
1397 | | // Push the data for the first argument into the m_arguments vector. |
1398 | 6.14k | m_arguments.push_back(arg1); |
1399 | 6.14k | } |
1400 | | |
1401 | 6.13k | ~CommandObjectCommandsScriptAdd() override = default; |
1402 | | |
1403 | 102 | Options *GetOptions() override { return &m_options; } |
1404 | | |
1405 | | void |
1406 | | HandleArgumentCompletion(CompletionRequest &request, |
1407 | 0 | OptionElementVector &opt_element_vector) override { |
1408 | 0 | CommandCompletions::CompleteModifiableCmdPathArgs(m_interpreter, request, |
1409 | 0 | opt_element_vector); |
1410 | 0 | } |
1411 | | |
1412 | | protected: |
1413 | | class CommandOptions : public Options { |
1414 | | public: |
1415 | 6.14k | CommandOptions() = default; |
1416 | | |
1417 | 6.13k | ~CommandOptions() override = default; |
1418 | | |
1419 | | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
1420 | 160 | ExecutionContext *execution_context) override { |
1421 | 160 | Status error; |
1422 | 160 | const int short_option = m_getopt_table[option_idx].val; |
1423 | | |
1424 | 160 | switch (short_option) { |
1425 | 74 | case 'f': |
1426 | 74 | if (!option_arg.empty()) |
1427 | 74 | m_funct_name = std::string(option_arg); |
1428 | 74 | break; |
1429 | 24 | case 'c': |
1430 | 24 | if (!option_arg.empty()) |
1431 | 24 | m_class_name = std::string(option_arg); |
1432 | 24 | break; |
1433 | 2 | case 'h': |
1434 | 2 | if (!option_arg.empty()) |
1435 | 2 | m_short_help = std::string(option_arg); |
1436 | 2 | break; |
1437 | 41 | case 'o': |
1438 | 41 | m_overwrite_lazy = eLazyBoolYes; |
1439 | 41 | break; |
1440 | 4 | case 's': |
1441 | 4 | m_synchronicity = |
1442 | 4 | (ScriptedCommandSynchronicity)OptionArgParser::ToOptionEnum( |
1443 | 4 | option_arg, GetDefinitions()[option_idx].enum_values, 0, error); |
1444 | 4 | if (!error.Success()) |
1445 | 0 | error.SetErrorStringWithFormat( |
1446 | 0 | "unrecognized value for synchronicity '%s'", |
1447 | 0 | option_arg.str().c_str()); |
1448 | 4 | break; |
1449 | 15 | case 'C': { |
1450 | 15 | Status error; |
1451 | 15 | OptionDefinition definition = GetDefinitions()[option_idx]; |
1452 | 15 | lldb::CompletionType completion_type = |
1453 | 15 | static_cast<lldb::CompletionType>(OptionArgParser::ToOptionEnum( |
1454 | 15 | option_arg, definition.enum_values, eNoCompletion, error)); |
1455 | 15 | if (!error.Success()) |
1456 | 0 | error.SetErrorStringWithFormat( |
1457 | 0 | "unrecognized value for command completion type '%s'", |
1458 | 0 | option_arg.str().c_str()); |
1459 | 15 | m_completion_type = completion_type; |
1460 | 15 | } break; |
1461 | 0 | default: |
1462 | 0 | llvm_unreachable("Unimplemented option"); |
1463 | 160 | } |
1464 | | |
1465 | 160 | return error; |
1466 | 160 | } |
1467 | | |
1468 | 101 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
1469 | 101 | m_class_name.clear(); |
1470 | 101 | m_funct_name.clear(); |
1471 | 101 | m_short_help.clear(); |
1472 | 101 | m_completion_type = eNoCompletion; |
1473 | 101 | m_overwrite_lazy = eLazyBoolCalculate; |
1474 | 101 | m_synchronicity = eScriptedCommandSynchronicitySynchronous; |
1475 | 101 | } |
1476 | | |
1477 | 121 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
1478 | 121 | return llvm::ArrayRef(g_script_add_options); |
1479 | 121 | } |
1480 | | |
1481 | | // Instance variables to hold the values for command options. |
1482 | | |
1483 | | std::string m_class_name; |
1484 | | std::string m_funct_name; |
1485 | | std::string m_short_help; |
1486 | | LazyBool m_overwrite_lazy = eLazyBoolCalculate; |
1487 | | ScriptedCommandSynchronicity m_synchronicity = |
1488 | | eScriptedCommandSynchronicitySynchronous; |
1489 | | CompletionType m_completion_type = eNoCompletion; |
1490 | | }; |
1491 | | |
1492 | 1 | void IOHandlerActivated(IOHandler &io_handler, bool interactive) override { |
1493 | 1 | StreamFileSP output_sp(io_handler.GetOutputStreamFileSP()); |
1494 | 1 | if (output_sp && interactive) { |
1495 | 0 | output_sp->PutCString(g_python_command_instructions); |
1496 | 0 | output_sp->Flush(); |
1497 | 0 | } |
1498 | 1 | } |
1499 | | |
1500 | | void IOHandlerInputComplete(IOHandler &io_handler, |
1501 | 1 | std::string &data) override { |
1502 | 1 | StreamFileSP error_sp = io_handler.GetErrorStreamFileSP(); |
1503 | | |
1504 | 1 | ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter(); |
1505 | 1 | if (interpreter) { |
1506 | 1 | StringList lines; |
1507 | 1 | lines.SplitIntoLines(data); |
1508 | 1 | if (lines.GetSize() > 0) { |
1509 | 1 | std::string funct_name_str; |
1510 | 1 | if (interpreter->GenerateScriptAliasFunction(lines, funct_name_str)) { |
1511 | 1 | if (funct_name_str.empty()) { |
1512 | 0 | error_sp->Printf("error: unable to obtain a function name, didn't " |
1513 | 0 | "add python command.\n"); |
1514 | 0 | error_sp->Flush(); |
1515 | 1 | } else { |
1516 | | // everything should be fine now, let's add this alias |
1517 | | |
1518 | 1 | CommandObjectSP command_obj_sp(new CommandObjectPythonFunction( |
1519 | 1 | m_interpreter, m_cmd_name, funct_name_str, m_short_help, |
1520 | 1 | m_synchronicity, m_completion_type)); |
1521 | 1 | if (!m_container) { |
1522 | 1 | Status error = m_interpreter.AddUserCommand( |
1523 | 1 | m_cmd_name, command_obj_sp, m_overwrite); |
1524 | 1 | if (error.Fail()) { |
1525 | 0 | error_sp->Printf("error: unable to add selected command: '%s'", |
1526 | 0 | error.AsCString()); |
1527 | 0 | error_sp->Flush(); |
1528 | 0 | } |
1529 | 1 | } else { |
1530 | 0 | llvm::Error llvm_error = m_container->LoadUserSubcommand( |
1531 | 0 | m_cmd_name, command_obj_sp, m_overwrite); |
1532 | 0 | if (llvm_error) { |
1533 | 0 | error_sp->Printf("error: unable to add selected command: '%s'", |
1534 | 0 | llvm::toString(std::move(llvm_error)).c_str()); |
1535 | 0 | error_sp->Flush(); |
1536 | 0 | } |
1537 | 0 | } |
1538 | 1 | } |
1539 | 1 | } else { |
1540 | 0 | error_sp->Printf( |
1541 | 0 | "error: unable to create function, didn't add python command\n"); |
1542 | 0 | error_sp->Flush(); |
1543 | 0 | } |
1544 | 1 | } else { |
1545 | 0 | error_sp->Printf("error: empty function, didn't add python command\n"); |
1546 | 0 | error_sp->Flush(); |
1547 | 0 | } |
1548 | 1 | } else { |
1549 | 0 | error_sp->Printf( |
1550 | 0 | "error: script interpreter missing, didn't add python command\n"); |
1551 | 0 | error_sp->Flush(); |
1552 | 0 | } |
1553 | | |
1554 | 1 | io_handler.SetIsDone(true); |
1555 | 1 | } |
1556 | | |
1557 | 101 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1558 | 101 | if (GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) { |
1559 | 0 | result.AppendError("only scripting language supported for scripted " |
1560 | 0 | "commands is currently Python"); |
1561 | 0 | return; |
1562 | 0 | } |
1563 | | |
1564 | 101 | if (command.GetArgumentCount() == 0) { |
1565 | 1 | result.AppendError("'command script add' requires at least one argument"); |
1566 | 1 | return; |
1567 | 1 | } |
1568 | | // Store the options in case we get multi-line input, also figure out the |
1569 | | // default if not user supplied: |
1570 | 100 | switch (m_options.m_overwrite_lazy) { |
1571 | 59 | case eLazyBoolCalculate: |
1572 | 59 | m_overwrite = !GetDebugger().GetCommandInterpreter().GetRequireCommandOverwrite(); |
1573 | 59 | break; |
1574 | 41 | case eLazyBoolYes: |
1575 | 41 | m_overwrite = true; |
1576 | 41 | break; |
1577 | 0 | case eLazyBoolNo: |
1578 | 0 | m_overwrite = false; |
1579 | 100 | } |
1580 | | |
1581 | 100 | Status path_error; |
1582 | 100 | m_container = GetCommandInterpreter().VerifyUserMultiwordCmdPath( |
1583 | 100 | command, true, path_error); |
1584 | | |
1585 | 100 | if (path_error.Fail()) { |
1586 | 1 | result.AppendErrorWithFormat("error in command path: %s", |
1587 | 1 | path_error.AsCString()); |
1588 | 1 | return; |
1589 | 1 | } |
1590 | | |
1591 | 99 | if (!m_container) { |
1592 | | // This is getting inserted into the root of the interpreter. |
1593 | 95 | m_cmd_name = std::string(command[0].ref()); |
1594 | 95 | } else { |
1595 | 4 | size_t num_args = command.GetArgumentCount(); |
1596 | 4 | m_cmd_name = std::string(command[num_args - 1].ref()); |
1597 | 4 | } |
1598 | | |
1599 | 99 | m_short_help.assign(m_options.m_short_help); |
1600 | 99 | m_synchronicity = m_options.m_synchronicity; |
1601 | 99 | m_completion_type = m_options.m_completion_type; |
1602 | | |
1603 | | // Handle the case where we prompt for the script code first: |
1604 | 99 | if (m_options.m_class_name.empty() && m_options.m_funct_name.empty()75 ) { |
1605 | 1 | m_interpreter.GetPythonCommandsFromIOHandler(" ", // Prompt |
1606 | 1 | *this); // IOHandlerDelegate |
1607 | 1 | return; |
1608 | 1 | } |
1609 | | |
1610 | 98 | CommandObjectSP new_cmd_sp; |
1611 | 98 | if (m_options.m_class_name.empty()) { |
1612 | 74 | new_cmd_sp.reset(new CommandObjectPythonFunction( |
1613 | 74 | m_interpreter, m_cmd_name, m_options.m_funct_name, |
1614 | 74 | m_options.m_short_help, m_synchronicity, m_completion_type)); |
1615 | 74 | } else { |
1616 | 24 | ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter(); |
1617 | 24 | if (!interpreter) { |
1618 | 0 | result.AppendError("cannot find ScriptInterpreter"); |
1619 | 0 | return; |
1620 | 0 | } |
1621 | | |
1622 | 24 | auto cmd_obj_sp = interpreter->CreateScriptCommandObject( |
1623 | 24 | m_options.m_class_name.c_str()); |
1624 | 24 | if (!cmd_obj_sp) { |
1625 | 1 | result.AppendErrorWithFormatv("cannot create helper object for: " |
1626 | 1 | "'{0}'", m_options.m_class_name); |
1627 | 1 | return; |
1628 | 1 | } |
1629 | | |
1630 | 23 | new_cmd_sp.reset(new CommandObjectScriptingObject( |
1631 | 23 | m_interpreter, m_cmd_name, cmd_obj_sp, m_synchronicity, |
1632 | 23 | m_completion_type)); |
1633 | 23 | } |
1634 | | |
1635 | | // Assume we're going to succeed... |
1636 | 97 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1637 | 97 | if (!m_container) { |
1638 | 93 | Status add_error = |
1639 | 93 | m_interpreter.AddUserCommand(m_cmd_name, new_cmd_sp, m_overwrite); |
1640 | 93 | if (add_error.Fail()) |
1641 | 1 | result.AppendErrorWithFormat("cannot add command: %s", |
1642 | 1 | add_error.AsCString()); |
1643 | 93 | } else { |
1644 | 4 | llvm::Error llvm_error = |
1645 | 4 | m_container->LoadUserSubcommand(m_cmd_name, new_cmd_sp, m_overwrite); |
1646 | 4 | if (llvm_error) |
1647 | 1 | result.AppendErrorWithFormat("cannot add command: %s", |
1648 | 1 | llvm::toString(std::move(llvm_error)).c_str()); |
1649 | 4 | } |
1650 | 97 | } |
1651 | | |
1652 | | CommandOptions m_options; |
1653 | | std::string m_cmd_name; |
1654 | | CommandObjectMultiword *m_container = nullptr; |
1655 | | std::string m_short_help; |
1656 | | bool m_overwrite = false; |
1657 | | ScriptedCommandSynchronicity m_synchronicity = |
1658 | | eScriptedCommandSynchronicitySynchronous; |
1659 | | CompletionType m_completion_type = eNoCompletion; |
1660 | | }; |
1661 | | |
1662 | | // CommandObjectCommandsScriptList |
1663 | | |
1664 | | class CommandObjectCommandsScriptList : public CommandObjectParsed { |
1665 | | public: |
1666 | | CommandObjectCommandsScriptList(CommandInterpreter &interpreter) |
1667 | 6.14k | : CommandObjectParsed(interpreter, "command script list", |
1668 | 6.14k | "List defined top-level scripted commands.", |
1669 | 6.14k | nullptr) {} |
1670 | | |
1671 | 6.13k | ~CommandObjectCommandsScriptList() override = default; |
1672 | | |
1673 | 2 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1674 | 2 | m_interpreter.GetHelp(result, CommandInterpreter::eCommandTypesUserDef); |
1675 | | |
1676 | 2 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1677 | 2 | } |
1678 | | }; |
1679 | | |
1680 | | // CommandObjectCommandsScriptClear |
1681 | | |
1682 | | class CommandObjectCommandsScriptClear : public CommandObjectParsed { |
1683 | | public: |
1684 | | CommandObjectCommandsScriptClear(CommandInterpreter &interpreter) |
1685 | 6.14k | : CommandObjectParsed(interpreter, "command script clear", |
1686 | 6.14k | "Delete all scripted commands.", nullptr) {} |
1687 | | |
1688 | 6.13k | ~CommandObjectCommandsScriptClear() override = default; |
1689 | | |
1690 | | protected: |
1691 | 2 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1692 | 2 | m_interpreter.RemoveAllUser(); |
1693 | | |
1694 | 2 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1695 | 2 | } |
1696 | | }; |
1697 | | |
1698 | | // CommandObjectCommandsScriptDelete |
1699 | | |
1700 | | class CommandObjectCommandsScriptDelete : public CommandObjectParsed { |
1701 | | public: |
1702 | | CommandObjectCommandsScriptDelete(CommandInterpreter &interpreter) |
1703 | 6.14k | : CommandObjectParsed( |
1704 | 6.14k | interpreter, "command script delete", |
1705 | 6.14k | "Delete a scripted command by specifying the path to the command.", |
1706 | 6.14k | nullptr) { |
1707 | 6.14k | CommandArgumentEntry arg1; |
1708 | 6.14k | CommandArgumentData cmd_arg; |
1709 | | |
1710 | | // This is a list of command names forming the path to the command |
1711 | | // to be deleted. |
1712 | 6.14k | cmd_arg.arg_type = eArgTypeCommand; |
1713 | 6.14k | cmd_arg.arg_repetition = eArgRepeatPlus; |
1714 | | |
1715 | | // There is only one variant this argument could be; put it into the |
1716 | | // argument entry. |
1717 | 6.14k | arg1.push_back(cmd_arg); |
1718 | | |
1719 | | // Push the data for the first argument into the m_arguments vector. |
1720 | 6.14k | m_arguments.push_back(arg1); |
1721 | 6.14k | } |
1722 | | |
1723 | 6.13k | ~CommandObjectCommandsScriptDelete() override = default; |
1724 | | |
1725 | | void |
1726 | | HandleArgumentCompletion(CompletionRequest &request, |
1727 | 1 | OptionElementVector &opt_element_vector) override { |
1728 | 1 | lldb_private::CommandCompletions::CompleteModifiableCmdPathArgs( |
1729 | 1 | m_interpreter, request, opt_element_vector); |
1730 | 1 | } |
1731 | | |
1732 | | protected: |
1733 | 30 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1734 | | |
1735 | 30 | llvm::StringRef root_cmd = command[0].ref(); |
1736 | 30 | size_t num_args = command.GetArgumentCount(); |
1737 | | |
1738 | 30 | if (root_cmd.empty()) { |
1739 | 0 | result.AppendErrorWithFormat("empty root command name"); |
1740 | 0 | return; |
1741 | 0 | } |
1742 | 30 | if (!m_interpreter.HasUserCommands() && |
1743 | 30 | !m_interpreter.HasUserMultiwordCommands()11 ) { |
1744 | 3 | result.AppendErrorWithFormat("can only delete user defined commands, " |
1745 | 3 | "but no user defined commands found"); |
1746 | 3 | return; |
1747 | 3 | } |
1748 | | |
1749 | 27 | CommandObjectSP cmd_sp = m_interpreter.GetCommandSPExact(root_cmd); |
1750 | 27 | if (!cmd_sp) { |
1751 | 8 | result.AppendErrorWithFormat("command '%s' not found.", |
1752 | 8 | command[0].c_str()); |
1753 | 8 | return; |
1754 | 8 | } |
1755 | 19 | if (!cmd_sp->IsUserCommand()) { |
1756 | 1 | result.AppendErrorWithFormat("command '%s' is not a user command.", |
1757 | 1 | command[0].c_str()); |
1758 | 1 | return; |
1759 | 1 | } |
1760 | 18 | if (cmd_sp->GetAsMultiwordCommand() && num_args == 16 ) { |
1761 | 1 | result.AppendErrorWithFormat("command '%s' is a multi-word command.\n " |
1762 | 1 | "Delete with \"command container delete\"", |
1763 | 1 | command[0].c_str()); |
1764 | 1 | return; |
1765 | 1 | } |
1766 | | |
1767 | 17 | if (command.GetArgumentCount() == 1) { |
1768 | 12 | m_interpreter.RemoveUser(root_cmd); |
1769 | 12 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1770 | 12 | return; |
1771 | 12 | } |
1772 | | // We're deleting a command from a multiword command. Verify the command |
1773 | | // path: |
1774 | 5 | Status error; |
1775 | 5 | CommandObjectMultiword *container = |
1776 | 5 | GetCommandInterpreter().VerifyUserMultiwordCmdPath(command, true, |
1777 | 5 | error); |
1778 | 5 | if (error.Fail()) { |
1779 | 1 | result.AppendErrorWithFormat("could not resolve command path: %s", |
1780 | 1 | error.AsCString()); |
1781 | 1 | return; |
1782 | 1 | } |
1783 | 4 | if (!container) { |
1784 | | // This means that command only had a leaf command, so the container is |
1785 | | // the root. That should have been handled above. |
1786 | 0 | result.AppendErrorWithFormat("could not find a container for '%s'", |
1787 | 0 | command[0].c_str()); |
1788 | 0 | return; |
1789 | 0 | } |
1790 | 4 | const char *leaf_cmd = command[num_args - 1].c_str(); |
1791 | 4 | llvm::Error llvm_error = container->RemoveUserSubcommand(leaf_cmd, |
1792 | 4 | /* multiword not okay */ false); |
1793 | 4 | if (llvm_error) { |
1794 | 3 | result.AppendErrorWithFormat("could not delete command '%s': %s", |
1795 | 3 | leaf_cmd, |
1796 | 3 | llvm::toString(std::move(llvm_error)).c_str()); |
1797 | 3 | return; |
1798 | 3 | } |
1799 | | |
1800 | 1 | Stream &out_stream = result.GetOutputStream(); |
1801 | | |
1802 | 1 | out_stream << "Deleted command:"; |
1803 | 4 | for (size_t idx = 0; idx < num_args; idx++3 ) { |
1804 | 3 | out_stream << ' '; |
1805 | 3 | out_stream << command[idx].c_str(); |
1806 | 3 | } |
1807 | 1 | out_stream << '\n'; |
1808 | 1 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1809 | 1 | } |
1810 | | }; |
1811 | | |
1812 | | #pragma mark CommandObjectMultiwordCommandsScript |
1813 | | |
1814 | | // CommandObjectMultiwordCommandsScript |
1815 | | |
1816 | | class CommandObjectMultiwordCommandsScript : public CommandObjectMultiword { |
1817 | | public: |
1818 | | CommandObjectMultiwordCommandsScript(CommandInterpreter &interpreter) |
1819 | 6.14k | : CommandObjectMultiword( |
1820 | 6.14k | interpreter, "command script", |
1821 | 6.14k | "Commands for managing custom " |
1822 | 6.14k | "commands implemented by " |
1823 | 6.14k | "interpreter scripts.", |
1824 | 6.14k | "command script <subcommand> [<subcommand-options>]") { |
1825 | 6.14k | LoadSubCommand("add", CommandObjectSP( |
1826 | 6.14k | new CommandObjectCommandsScriptAdd(interpreter))); |
1827 | 6.14k | LoadSubCommand( |
1828 | 6.14k | "delete", |
1829 | 6.14k | CommandObjectSP(new CommandObjectCommandsScriptDelete(interpreter))); |
1830 | 6.14k | LoadSubCommand( |
1831 | 6.14k | "clear", |
1832 | 6.14k | CommandObjectSP(new CommandObjectCommandsScriptClear(interpreter))); |
1833 | 6.14k | LoadSubCommand("list", CommandObjectSP(new CommandObjectCommandsScriptList( |
1834 | 6.14k | interpreter))); |
1835 | 6.14k | LoadSubCommand( |
1836 | 6.14k | "import", |
1837 | 6.14k | CommandObjectSP(new CommandObjectCommandsScriptImport(interpreter))); |
1838 | 6.14k | } |
1839 | | |
1840 | 6.13k | ~CommandObjectMultiwordCommandsScript() override = default; |
1841 | | }; |
1842 | | |
1843 | | #pragma mark CommandObjectCommandContainer |
1844 | | #define LLDB_OPTIONS_container_add |
1845 | | #include "CommandOptions.inc" |
1846 | | |
1847 | | class CommandObjectCommandsContainerAdd : public CommandObjectParsed { |
1848 | | public: |
1849 | | CommandObjectCommandsContainerAdd(CommandInterpreter &interpreter) |
1850 | 6.14k | : CommandObjectParsed( |
1851 | 6.14k | interpreter, "command container add", |
1852 | 6.14k | "Add a container command to lldb. Adding to built-" |
1853 | 6.14k | "in container commands is not allowed.", |
1854 | 6.14k | "command container add [[path1]...] container-name") { |
1855 | 6.14k | CommandArgumentEntry arg1; |
1856 | 6.14k | CommandArgumentData cmd_arg; |
1857 | | |
1858 | | // This is one or more command names, which form the path to the command |
1859 | | // you want to add. |
1860 | 6.14k | cmd_arg.arg_type = eArgTypeCommand; |
1861 | 6.14k | cmd_arg.arg_repetition = eArgRepeatPlus; |
1862 | | |
1863 | | // There is only one variant this argument could be; put it into the |
1864 | | // argument entry. |
1865 | 6.14k | arg1.push_back(cmd_arg); |
1866 | | |
1867 | | // Push the data for the first argument into the m_arguments vector. |
1868 | 6.14k | m_arguments.push_back(arg1); |
1869 | 6.14k | } |
1870 | | |
1871 | 6.13k | ~CommandObjectCommandsContainerAdd() override = default; |
1872 | | |
1873 | 6 | Options *GetOptions() override { return &m_options; } |
1874 | | |
1875 | | void |
1876 | | HandleArgumentCompletion(CompletionRequest &request, |
1877 | 0 | OptionElementVector &opt_element_vector) override { |
1878 | 0 | lldb_private::CommandCompletions::CompleteModifiableCmdPathArgs( |
1879 | 0 | m_interpreter, request, opt_element_vector); |
1880 | 0 | } |
1881 | | |
1882 | | protected: |
1883 | | class CommandOptions : public Options { |
1884 | | public: |
1885 | 6.14k | CommandOptions() = default; |
1886 | | |
1887 | 6.13k | ~CommandOptions() override = default; |
1888 | | |
1889 | | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
1890 | 4 | ExecutionContext *execution_context) override { |
1891 | 4 | Status error; |
1892 | 4 | const int short_option = m_getopt_table[option_idx].val; |
1893 | | |
1894 | 4 | switch (short_option) { |
1895 | 3 | case 'h': |
1896 | 3 | if (!option_arg.empty()) |
1897 | 3 | m_short_help = std::string(option_arg); |
1898 | 3 | break; |
1899 | 1 | case 'o': |
1900 | 1 | m_overwrite = true; |
1901 | 1 | break; |
1902 | 0 | case 'H': |
1903 | 0 | if (!option_arg.empty()) |
1904 | 0 | m_long_help = std::string(option_arg); |
1905 | 0 | break; |
1906 | 0 | default: |
1907 | 0 | llvm_unreachable("Unimplemented option"); |
1908 | 4 | } |
1909 | | |
1910 | 4 | return error; |
1911 | 4 | } |
1912 | | |
1913 | 6 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
1914 | 6 | m_short_help.clear(); |
1915 | 6 | m_long_help.clear(); |
1916 | 6 | m_overwrite = false; |
1917 | 6 | } |
1918 | | |
1919 | 3 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
1920 | 3 | return llvm::ArrayRef(g_container_add_options); |
1921 | 3 | } |
1922 | | |
1923 | | // Instance variables to hold the values for command options. |
1924 | | |
1925 | | std::string m_short_help; |
1926 | | std::string m_long_help; |
1927 | | bool m_overwrite = false; |
1928 | | }; |
1929 | 6 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1930 | 6 | size_t num_args = command.GetArgumentCount(); |
1931 | | |
1932 | 6 | if (num_args == 0) { |
1933 | 0 | result.AppendError("no command was specified"); |
1934 | 0 | return; |
1935 | 0 | } |
1936 | | |
1937 | 6 | if (num_args == 1) { |
1938 | | // We're adding this as a root command, so use the interpreter. |
1939 | 2 | const char *cmd_name = command.GetArgumentAtIndex(0); |
1940 | 2 | auto cmd_sp = CommandObjectSP(new CommandObjectMultiword( |
1941 | 2 | GetCommandInterpreter(), cmd_name, m_options.m_short_help.c_str(), |
1942 | 2 | m_options.m_long_help.c_str())); |
1943 | 2 | cmd_sp->GetAsMultiwordCommand()->SetRemovable(true); |
1944 | 2 | Status add_error = GetCommandInterpreter().AddUserCommand( |
1945 | 2 | cmd_name, cmd_sp, m_options.m_overwrite); |
1946 | 2 | if (add_error.Fail()) { |
1947 | 1 | result.AppendErrorWithFormat("error adding command: %s", |
1948 | 1 | add_error.AsCString()); |
1949 | 1 | return; |
1950 | 1 | } |
1951 | 1 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1952 | 1 | return; |
1953 | 2 | } |
1954 | | |
1955 | | // We're adding this to a subcommand, first find the subcommand: |
1956 | 4 | Status path_error; |
1957 | 4 | CommandObjectMultiword *add_to_me = |
1958 | 4 | GetCommandInterpreter().VerifyUserMultiwordCmdPath(command, true, |
1959 | 4 | path_error); |
1960 | | |
1961 | 4 | if (!add_to_me) { |
1962 | 2 | result.AppendErrorWithFormat("error adding command: %s", |
1963 | 2 | path_error.AsCString()); |
1964 | 2 | return; |
1965 | 2 | } |
1966 | | |
1967 | 2 | const char *cmd_name = command.GetArgumentAtIndex(num_args - 1); |
1968 | 2 | auto cmd_sp = CommandObjectSP(new CommandObjectMultiword( |
1969 | 2 | GetCommandInterpreter(), cmd_name, m_options.m_short_help.c_str(), |
1970 | 2 | m_options.m_long_help.c_str())); |
1971 | 2 | llvm::Error llvm_error = |
1972 | 2 | add_to_me->LoadUserSubcommand(cmd_name, cmd_sp, m_options.m_overwrite); |
1973 | 2 | if (llvm_error) { |
1974 | 0 | result.AppendErrorWithFormat("error adding subcommand: %s", |
1975 | 0 | llvm::toString(std::move(llvm_error)).c_str()); |
1976 | 0 | return; |
1977 | 0 | } |
1978 | | |
1979 | 2 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1980 | 2 | } |
1981 | | |
1982 | | private: |
1983 | | CommandOptions m_options; |
1984 | | }; |
1985 | | |
1986 | | #define LLDB_OPTIONS_multiword_delete |
1987 | | #include "CommandOptions.inc" |
1988 | | class CommandObjectCommandsContainerDelete : public CommandObjectParsed { |
1989 | | public: |
1990 | | CommandObjectCommandsContainerDelete(CommandInterpreter &interpreter) |
1991 | 6.14k | : CommandObjectParsed( |
1992 | 6.14k | interpreter, "command container delete", |
1993 | 6.14k | "Delete a container command previously added to " |
1994 | 6.14k | "lldb.", |
1995 | 6.14k | "command container delete [[path1] ...] container-cmd") { |
1996 | 6.14k | CommandArgumentEntry arg1; |
1997 | 6.14k | CommandArgumentData cmd_arg; |
1998 | | |
1999 | | // This is one or more command names, which form the path to the command |
2000 | | // you want to add. |
2001 | 6.14k | cmd_arg.arg_type = eArgTypeCommand; |
2002 | 6.14k | cmd_arg.arg_repetition = eArgRepeatPlus; |
2003 | | |
2004 | | // There is only one variant this argument could be; put it into the |
2005 | | // argument entry. |
2006 | 6.14k | arg1.push_back(cmd_arg); |
2007 | | |
2008 | | // Push the data for the first argument into the m_arguments vector. |
2009 | 6.14k | m_arguments.push_back(arg1); |
2010 | 6.14k | } |
2011 | | |
2012 | 6.13k | ~CommandObjectCommandsContainerDelete() override = default; |
2013 | | |
2014 | | void |
2015 | | HandleArgumentCompletion(CompletionRequest &request, |
2016 | 0 | OptionElementVector &opt_element_vector) override { |
2017 | 0 | lldb_private::CommandCompletions::CompleteModifiableCmdPathArgs( |
2018 | 0 | m_interpreter, request, opt_element_vector); |
2019 | 0 | } |
2020 | | |
2021 | | protected: |
2022 | 3 | void DoExecute(Args &command, CommandReturnObject &result) override { |
2023 | 3 | size_t num_args = command.GetArgumentCount(); |
2024 | | |
2025 | 3 | if (num_args == 0) { |
2026 | 0 | result.AppendError("No command was specified."); |
2027 | 0 | return; |
2028 | 0 | } |
2029 | | |
2030 | 3 | if (num_args == 1) { |
2031 | | // We're removing a root command, so we need to delete it from the |
2032 | | // interpreter. |
2033 | 1 | const char *cmd_name = command.GetArgumentAtIndex(0); |
2034 | | // Let's do a little more work here so we can do better error reporting. |
2035 | 1 | CommandInterpreter &interp = GetCommandInterpreter(); |
2036 | 1 | CommandObjectSP cmd_sp = interp.GetCommandSPExact(cmd_name); |
2037 | 1 | if (!cmd_sp) { |
2038 | 0 | result.AppendErrorWithFormat("container command %s doesn't exist.", |
2039 | 0 | cmd_name); |
2040 | 0 | return; |
2041 | 0 | } |
2042 | 1 | if (!cmd_sp->IsUserCommand()) { |
2043 | 0 | result.AppendErrorWithFormat( |
2044 | 0 | "container command %s is not a user command", cmd_name); |
2045 | 0 | return; |
2046 | 0 | } |
2047 | 1 | if (!cmd_sp->GetAsMultiwordCommand()) { |
2048 | 0 | result.AppendErrorWithFormat("command %s is not a container command", |
2049 | 0 | cmd_name); |
2050 | 0 | return; |
2051 | 0 | } |
2052 | | |
2053 | 1 | bool did_remove = GetCommandInterpreter().RemoveUserMultiword(cmd_name); |
2054 | 1 | if (!did_remove) { |
2055 | 0 | result.AppendErrorWithFormat("error removing command %s.", cmd_name); |
2056 | 0 | return; |
2057 | 0 | } |
2058 | | |
2059 | 1 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
2060 | 1 | return; |
2061 | 1 | } |
2062 | | |
2063 | | // We're removing a subcommand, first find the subcommand's owner: |
2064 | 2 | Status path_error; |
2065 | 2 | CommandObjectMultiword *container = |
2066 | 2 | GetCommandInterpreter().VerifyUserMultiwordCmdPath(command, true, |
2067 | 2 | path_error); |
2068 | | |
2069 | 2 | if (!container) { |
2070 | 0 | result.AppendErrorWithFormat("error removing container command: %s", |
2071 | 0 | path_error.AsCString()); |
2072 | 0 | return; |
2073 | 0 | } |
2074 | 2 | const char *leaf = command.GetArgumentAtIndex(num_args - 1); |
2075 | 2 | llvm::Error llvm_error = |
2076 | 2 | container->RemoveUserSubcommand(leaf, /* multiword okay */ true); |
2077 | 2 | if (llvm_error) { |
2078 | 1 | result.AppendErrorWithFormat("error removing container command: %s", |
2079 | 1 | llvm::toString(std::move(llvm_error)).c_str()); |
2080 | 1 | return; |
2081 | 1 | } |
2082 | 1 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
2083 | 1 | } |
2084 | | }; |
2085 | | |
2086 | | class CommandObjectCommandContainer : public CommandObjectMultiword { |
2087 | | public: |
2088 | | CommandObjectCommandContainer(CommandInterpreter &interpreter) |
2089 | 6.14k | : CommandObjectMultiword( |
2090 | 6.14k | interpreter, "command container", |
2091 | 6.14k | "Commands for adding container commands to lldb. " |
2092 | 6.14k | "Container commands are containers for other commands. You can " |
2093 | 6.14k | "add nested container commands by specifying a command path, " |
2094 | 6.14k | "but you can't add commands into the built-in command hierarchy.", |
2095 | 6.14k | "command container <subcommand> [<subcommand-options>]") { |
2096 | 6.14k | LoadSubCommand("add", CommandObjectSP(new CommandObjectCommandsContainerAdd( |
2097 | 6.14k | interpreter))); |
2098 | 6.14k | LoadSubCommand( |
2099 | 6.14k | "delete", |
2100 | 6.14k | CommandObjectSP(new CommandObjectCommandsContainerDelete(interpreter))); |
2101 | 6.14k | } |
2102 | | |
2103 | 6.13k | ~CommandObjectCommandContainer() override = default; |
2104 | | }; |
2105 | | |
2106 | | #pragma mark CommandObjectMultiwordCommands |
2107 | | |
2108 | | // CommandObjectMultiwordCommands |
2109 | | |
2110 | | CommandObjectMultiwordCommands::CommandObjectMultiwordCommands( |
2111 | | CommandInterpreter &interpreter) |
2112 | 6.14k | : CommandObjectMultiword(interpreter, "command", |
2113 | 6.14k | "Commands for managing custom LLDB commands.", |
2114 | 6.14k | "command <subcommand> [<subcommand-options>]") { |
2115 | 6.14k | LoadSubCommand("source", |
2116 | 6.14k | CommandObjectSP(new CommandObjectCommandsSource(interpreter))); |
2117 | 6.14k | LoadSubCommand("alias", |
2118 | 6.14k | CommandObjectSP(new CommandObjectCommandsAlias(interpreter))); |
2119 | 6.14k | LoadSubCommand("unalias", CommandObjectSP( |
2120 | 6.14k | new CommandObjectCommandsUnalias(interpreter))); |
2121 | 6.14k | LoadSubCommand("delete", |
2122 | 6.14k | CommandObjectSP(new CommandObjectCommandsDelete(interpreter))); |
2123 | 6.14k | LoadSubCommand("container", CommandObjectSP(new CommandObjectCommandContainer( |
2124 | 6.14k | interpreter))); |
2125 | 6.14k | LoadSubCommand( |
2126 | 6.14k | "regex", CommandObjectSP(new CommandObjectCommandsAddRegex(interpreter))); |
2127 | 6.14k | LoadSubCommand( |
2128 | 6.14k | "script", |
2129 | 6.14k | CommandObjectSP(new CommandObjectMultiwordCommandsScript(interpreter))); |
2130 | 6.14k | } |
2131 | | |
2132 | 6.13k | CommandObjectMultiwordCommands::~CommandObjectMultiwordCommands() = default; |