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