1 //===-- CommandObjectRegexCommand.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 "CommandObjectRegexCommand.h"
10 #include "lldb/Interpreter/CommandInterpreter.h"
11 #include "lldb/Interpreter/CommandReturnObject.h"
12 
13 #include "llvm/Support/Errc.h"
14 #include "llvm/Support/Error.h"
15 
16 using namespace lldb;
17 using namespace lldb_private;
18 
19 // CommandObjectRegexCommand constructor
20 CommandObjectRegexCommand::CommandObjectRegexCommand(
21     CommandInterpreter &interpreter, llvm::StringRef name, llvm::StringRef help,
22     llvm::StringRef syntax, uint32_t max_matches, uint32_t completion_type_mask,
23     bool is_removable)
24     : CommandObjectRaw(interpreter, name, help, syntax),
25       m_max_matches(max_matches), m_completion_type_mask(completion_type_mask),
26       m_is_removable(is_removable) {}
27 
28 // Destructor
29 CommandObjectRegexCommand::~CommandObjectRegexCommand() = default;
30 
31 llvm::Expected<std::string> CommandObjectRegexCommand::SubstituteVariables(
32     llvm::StringRef input,
33     const llvm::SmallVectorImpl<llvm::StringRef> &replacements) {
34   std::string buffer;
35   llvm::raw_string_ostream output(buffer);
36 
37   llvm::SmallVector<llvm::StringRef, 4> parts;
38   input.split(parts, '%');
39 
40   output << parts[0];
41   for (llvm::StringRef part : drop_begin(parts)) {
42     size_t idx = 0;
43     if (part.consumeInteger(10, idx))
44       output << '%';
45     else if (idx < replacements.size())
46       output << replacements[idx];
47     else
48       return llvm::make_error<llvm::StringError>(
49           llvm::formatv("%{0} is out of range: not enough arguments specified",
50                         idx),
51           llvm::errc::invalid_argument);
52     output << part;
53   }
54 
55   return output.str();
56 }
57 
58 bool CommandObjectRegexCommand::DoExecute(llvm::StringRef command,
59                                           CommandReturnObject &result) {
60   EntryCollection::const_iterator pos, end = m_entries.end();
61   for (pos = m_entries.begin(); pos != end; ++pos) {
62     llvm::SmallVector<llvm::StringRef, 4> matches;
63     if (pos->regex.Execute(command, &matches)) {
64       llvm::Expected<std::string> new_command =
65           SubstituteVariables(pos->command, matches);
66       if (!new_command) {
67         result.SetError(new_command.takeError());
68         return false;
69       }
70 
71       // Interpret the new command and return this as the result!
72       if (m_interpreter.GetExpandRegexAliases())
73         result.GetOutputStream().Printf("%s\n", new_command->c_str());
74       // Pass in true for "no context switching".  The command that called us
75       // should have set up the context appropriately, we shouldn't have to
76       // redo that.
77       return m_interpreter.HandleCommand(new_command->c_str(),
78                                          eLazyBoolCalculate, result);
79     }
80   }
81   result.SetStatus(eReturnStatusFailed);
82   if (!GetSyntax().empty())
83     result.AppendError(GetSyntax());
84   else
85     result.GetErrorStream() << "Command contents '" << command
86                             << "' failed to match any "
87                                "regular expression in the '"
88                             << m_cmd_name << "' regex ";
89   return false;
90 }
91 
92 bool CommandObjectRegexCommand::AddRegexCommand(llvm::StringRef re_cstr,
93                                                 llvm::StringRef command_cstr) {
94   m_entries.resize(m_entries.size() + 1);
95   // Only add the regular expression if it compiles
96   m_entries.back().regex = RegularExpression(re_cstr);
97   if (m_entries.back().regex.IsValid()) {
98     m_entries.back().command = command_cstr.str();
99     return true;
100   }
101   // The regex didn't compile...
102   m_entries.pop_back();
103   return false;
104 }
105 
106 void CommandObjectRegexCommand::HandleCompletion(CompletionRequest &request) {
107   if (m_completion_type_mask) {
108     CommandCompletions::InvokeCommonCompletionCallbacks(
109         GetCommandInterpreter(), m_completion_type_mask, request, nullptr);
110   }
111 }
112