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("(%s)", GetTypeAsCString()); 30 if (dump_mask & eDumpOptionValue) { 31 if (dump_mask & eDumpOptionType) 32 strm.Printf(" =%s", (m_path_mappings.GetSize() > 0) ? "\n" : ""); 33 m_path_mappings.Dump(&strm); 34 } 35 } 36 37 llvm::json::Value 38 OptionValuePathMappings::ToJSON(const ExecutionContext *exe_ctx) { 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(args.GetArgumentAtIndex(0), idx) || idx > count) { 61 error = Status::FromErrorStringWithFormat( 62 "invalid file list index %s, index must be 0 through %u", 63 args.GetArgumentAtIndex(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(i); 68 const char *replace_path = args.GetArgumentAtIndex(i + 1); 69 if (VerifyPathExists(replace_path)) { 70 if (!m_path_mappings.Replace(orginal_path, replace_path, idx, 71 m_notify_changes)) 72 m_path_mappings.Append(orginal_path, replace_path, 73 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 "%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 "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 "assign operation takes one or more path pairs"); 97 break; 98 } 99 m_path_mappings.Clear(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 "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(i); 111 const char *replace_path = args.GetArgumentAtIndex(i + 1); 112 if (VerifyPathExists(replace_path)) { 113 m_path_mappings.Append(orginal_path, replace_path, 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 "%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(args.GetArgumentAtIndex(0), idx) || idx > count) { 137 error = Status::FromErrorStringWithFormat( 138 "invalid file list index %s, index must be 0 through %u", 139 args.GetArgumentAtIndex(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(i); 146 const char *replace_path = args.GetArgumentAtIndex(i + 1); 147 if (VerifyPathExists(replace_path)) { 148 m_path_mappings.Insert(orginal_path, replace_path, idx, 149 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 "%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 "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(args.GetArgumentAtIndex(i), idx) || idx < 0 || 176 idx >= (int)m_path_mappings.GetSize()) { 177 error = Status::FromErrorStringWithFormat( 178 "invalid array index '%s', aborting remove operation", 179 args.GetArgumentAtIndex(i)); 180 break; 181 } else 182 remove_indexes.push_back(idx); 183 } 184 185 // Sort and then erase in reverse so indexes are always valid 186 llvm::sort(remove_indexes); 187 for (auto index : llvm::reverse(remove_indexes)) 188 m_path_mappings.Remove(index, m_notify_changes); 189 NotifyValueChanged(); 190 } else { 191 error = Status::FromErrorString( 192 "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