| 1 | //===-- OptionValuePathMappings.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 "lldb/Interpreter/OptionValuePathMappings.h" |
| 10 | |
| 11 | #include "lldb/Host/FileSystem.h" |
| 12 | #include "lldb/Utility/Args.h" |
| 13 | #include "lldb/Utility/FileSpec.h" |
| 14 | #include "lldb/Utility/Stream.h" |
| 15 | |
| 16 | using namespace lldb; |
| 17 | using namespace lldb_private; |
| 18 | |
| 19 | static bool VerifyPathExists(const char *path) { |
| 20 | if (path && path[0]) |
| 21 | return FileSystem::Instance().Exists(path); |
| 22 | else |
| 23 | return false; |
| 24 | } |
| 25 | |
| 26 | void OptionValuePathMappings::DumpValue(const ExecutionContext *exe_ctx, |
| 27 | Stream &strm, uint32_t dump_mask) { |
| 28 | if (dump_mask & eDumpOptionType) |
| 29 | strm.Printf(format: "(%s)" , GetTypeAsCString()); |
| 30 | if (dump_mask & eDumpOptionValue) { |
| 31 | if (dump_mask & eDumpOptionType) |
| 32 | strm.Printf(format: " =%s" , (m_path_mappings.GetSize() > 0) ? "\n" : "" ); |
| 33 | m_path_mappings.Dump(s: &strm); |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | llvm::json::Value |
| 38 | OptionValuePathMappings::ToJSON(const ExecutionContext *exe_ctx) const { |
| 39 | return m_path_mappings.ToJSON(); |
| 40 | } |
| 41 | |
| 42 | Status OptionValuePathMappings::SetValueFromString(llvm::StringRef value, |
| 43 | VarSetOperationType op) { |
| 44 | Status error; |
| 45 | Args args(value.str()); |
| 46 | const size_t argc = args.GetArgumentCount(); |
| 47 | |
| 48 | switch (op) { |
| 49 | case eVarSetOperationClear: |
| 50 | Clear(); |
| 51 | NotifyValueChanged(); |
| 52 | break; |
| 53 | |
| 54 | case eVarSetOperationReplace: |
| 55 | // Must be at least one index + 1 pair of paths, and the pair count must be |
| 56 | // even |
| 57 | if (argc >= 3 && (((argc - 1) & 1) == 0)) { |
| 58 | uint32_t idx; |
| 59 | const uint32_t count = m_path_mappings.GetSize(); |
| 60 | if (!llvm::to_integer(S: args.GetArgumentAtIndex(idx: 0), Num&: idx) || idx > count) { |
| 61 | error = Status::FromErrorStringWithFormat( |
| 62 | format: "invalid file list index %s, index must be 0 through %u" , |
| 63 | args.GetArgumentAtIndex(idx: 0), count); |
| 64 | } else { |
| 65 | bool changed = false; |
| 66 | for (size_t i = 1; i < argc; idx++, i += 2) { |
| 67 | const char *orginal_path = args.GetArgumentAtIndex(idx: i); |
| 68 | const char *replace_path = args.GetArgumentAtIndex(idx: i + 1); |
| 69 | if (VerifyPathExists(path: replace_path)) { |
| 70 | if (!m_path_mappings.Replace(path: orginal_path, replacement: replace_path, index: idx, |
| 71 | notify: m_notify_changes)) |
| 72 | m_path_mappings.Append(path: orginal_path, replacement: replace_path, |
| 73 | notify: m_notify_changes); |
| 74 | changed = true; |
| 75 | } else { |
| 76 | std::string previousError = |
| 77 | error.Fail() ? std::string(error.AsCString()) + "\n" : "" ; |
| 78 | error = Status::FromErrorStringWithFormat( |
| 79 | format: "%sthe replacement path doesn't exist: \"%s\"" , |
| 80 | previousError.c_str(), replace_path); |
| 81 | } |
| 82 | } |
| 83 | if (changed) |
| 84 | NotifyValueChanged(); |
| 85 | } |
| 86 | } else { |
| 87 | error = Status::FromErrorString( |
| 88 | str: "replace operation takes an array index followed by " |
| 89 | "one or more path pairs" ); |
| 90 | } |
| 91 | break; |
| 92 | |
| 93 | case eVarSetOperationAssign: |
| 94 | if (argc < 2 || (argc & 1)) { |
| 95 | error = Status::FromErrorString( |
| 96 | str: "assign operation takes one or more path pairs" ); |
| 97 | break; |
| 98 | } |
| 99 | m_path_mappings.Clear(notify: m_notify_changes); |
| 100 | // Fall through to append case |
| 101 | [[fallthrough]]; |
| 102 | case eVarSetOperationAppend: |
| 103 | if (argc < 2 || (argc & 1)) { |
| 104 | error = Status::FromErrorString( |
| 105 | str: "append operation takes one or more path pairs" ); |
| 106 | break; |
| 107 | } else { |
| 108 | bool changed = false; |
| 109 | for (size_t i = 0; i < argc; i += 2) { |
| 110 | const char *orginal_path = args.GetArgumentAtIndex(idx: i); |
| 111 | const char *replace_path = args.GetArgumentAtIndex(idx: i + 1); |
| 112 | if (VerifyPathExists(path: replace_path)) { |
| 113 | m_path_mappings.Append(path: orginal_path, replacement: replace_path, notify: m_notify_changes); |
| 114 | m_value_was_set = true; |
| 115 | changed = true; |
| 116 | } else { |
| 117 | std::string previousError = |
| 118 | error.Fail() ? std::string(error.AsCString()) + "\n" : "" ; |
| 119 | error = Status::FromErrorStringWithFormat( |
| 120 | format: "%sthe replacement path doesn't exist: \"%s\"" , |
| 121 | previousError.c_str(), replace_path); |
| 122 | } |
| 123 | } |
| 124 | if (changed) |
| 125 | NotifyValueChanged(); |
| 126 | } |
| 127 | break; |
| 128 | |
| 129 | case eVarSetOperationInsertBefore: |
| 130 | case eVarSetOperationInsertAfter: |
| 131 | // Must be at least one index + 1 pair of paths, and the pair count must be |
| 132 | // even |
| 133 | if (argc >= 3 && (((argc - 1) & 1) == 0)) { |
| 134 | uint32_t idx; |
| 135 | const uint32_t count = m_path_mappings.GetSize(); |
| 136 | if (!llvm::to_integer(S: args.GetArgumentAtIndex(idx: 0), Num&: idx) || idx > count) { |
| 137 | error = Status::FromErrorStringWithFormat( |
| 138 | format: "invalid file list index %s, index must be 0 through %u" , |
| 139 | args.GetArgumentAtIndex(idx: 0), count); |
| 140 | } else { |
| 141 | bool changed = false; |
| 142 | if (op == eVarSetOperationInsertAfter) |
| 143 | ++idx; |
| 144 | for (size_t i = 1; i < argc; i += 2) { |
| 145 | const char *orginal_path = args.GetArgumentAtIndex(idx: i); |
| 146 | const char *replace_path = args.GetArgumentAtIndex(idx: i + 1); |
| 147 | if (VerifyPathExists(path: replace_path)) { |
| 148 | m_path_mappings.Insert(path: orginal_path, replacement: replace_path, insert_idx: idx, |
| 149 | notify: m_notify_changes); |
| 150 | changed = true; |
| 151 | idx++; |
| 152 | } else { |
| 153 | std::string previousError = |
| 154 | error.Fail() ? std::string(error.AsCString()) + "\n" : "" ; |
| 155 | error = Status::FromErrorStringWithFormat( |
| 156 | format: "%sthe replacement path doesn't exist: \"%s\"" , |
| 157 | previousError.c_str(), replace_path); |
| 158 | } |
| 159 | } |
| 160 | if (changed) |
| 161 | NotifyValueChanged(); |
| 162 | } |
| 163 | } else { |
| 164 | error = Status::FromErrorString( |
| 165 | str: "insert operation takes an array index followed by " |
| 166 | "one or more path pairs" ); |
| 167 | } |
| 168 | break; |
| 169 | |
| 170 | case eVarSetOperationRemove: |
| 171 | if (argc > 0) { |
| 172 | std::vector<int> remove_indexes; |
| 173 | for (size_t i = 0; i < argc; ++i) { |
| 174 | int idx; |
| 175 | if (!llvm::to_integer(S: args.GetArgumentAtIndex(idx: i), Num&: idx) || idx < 0 || |
| 176 | idx >= (int)m_path_mappings.GetSize()) { |
| 177 | error = Status::FromErrorStringWithFormat( |
| 178 | format: "invalid array index '%s', aborting remove operation" , |
| 179 | args.GetArgumentAtIndex(idx: i)); |
| 180 | break; |
| 181 | } else |
| 182 | remove_indexes.push_back(x: idx); |
| 183 | } |
| 184 | |
| 185 | // Sort and then erase in reverse so indexes are always valid |
| 186 | llvm::sort(C&: remove_indexes); |
| 187 | for (auto index : llvm::reverse(C&: remove_indexes)) |
| 188 | m_path_mappings.Remove(index, notify: m_notify_changes); |
| 189 | NotifyValueChanged(); |
| 190 | } else { |
| 191 | error = Status::FromErrorString( |
| 192 | str: "remove operation takes one or more array index" ); |
| 193 | } |
| 194 | break; |
| 195 | |
| 196 | case eVarSetOperationInvalid: |
| 197 | error = OptionValue::SetValueFromString(value, op); |
| 198 | break; |
| 199 | } |
| 200 | return error; |
| 201 | } |
| 202 | |