15ffd83dbSDimitry Andric //===-- OptionValuePathMappings.cpp ---------------------------------------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric 9*0b57cec5SDimitry Andric #include "lldb/Interpreter/OptionValuePathMappings.h" 10*0b57cec5SDimitry Andric 11*0b57cec5SDimitry Andric #include "lldb/Host/FileSystem.h" 12*0b57cec5SDimitry Andric #include "lldb/Host/StringConvert.h" 13*0b57cec5SDimitry Andric #include "lldb/Utility/Args.h" 14*0b57cec5SDimitry Andric #include "lldb/Utility/FileSpec.h" 15*0b57cec5SDimitry Andric #include "lldb/Utility/Stream.h" 16*0b57cec5SDimitry Andric 17*0b57cec5SDimitry Andric using namespace lldb; 18*0b57cec5SDimitry Andric using namespace lldb_private; 19*0b57cec5SDimitry Andric namespace { 20*0b57cec5SDimitry Andric static bool VerifyPathExists(const char *path) { 21*0b57cec5SDimitry Andric if (path && path[0]) 22*0b57cec5SDimitry Andric return FileSystem::Instance().Exists(path); 23*0b57cec5SDimitry Andric else 24*0b57cec5SDimitry Andric return false; 25*0b57cec5SDimitry Andric } 26*0b57cec5SDimitry Andric } 27*0b57cec5SDimitry Andric 28*0b57cec5SDimitry Andric void OptionValuePathMappings::DumpValue(const ExecutionContext *exe_ctx, 29*0b57cec5SDimitry Andric Stream &strm, uint32_t dump_mask) { 30*0b57cec5SDimitry Andric if (dump_mask & eDumpOptionType) 31*0b57cec5SDimitry Andric strm.Printf("(%s)", GetTypeAsCString()); 32*0b57cec5SDimitry Andric if (dump_mask & eDumpOptionValue) { 33*0b57cec5SDimitry Andric if (dump_mask & eDumpOptionType) 34*0b57cec5SDimitry Andric strm.Printf(" =%s", (m_path_mappings.GetSize() > 0) ? "\n" : ""); 35*0b57cec5SDimitry Andric m_path_mappings.Dump(&strm); 36*0b57cec5SDimitry Andric } 37*0b57cec5SDimitry Andric } 38*0b57cec5SDimitry Andric 39*0b57cec5SDimitry Andric Status OptionValuePathMappings::SetValueFromString(llvm::StringRef value, 40*0b57cec5SDimitry Andric VarSetOperationType op) { 41*0b57cec5SDimitry Andric Status error; 42*0b57cec5SDimitry Andric Args args(value.str()); 43*0b57cec5SDimitry Andric const size_t argc = args.GetArgumentCount(); 44*0b57cec5SDimitry Andric 45*0b57cec5SDimitry Andric switch (op) { 46*0b57cec5SDimitry Andric case eVarSetOperationClear: 47*0b57cec5SDimitry Andric Clear(); 48*0b57cec5SDimitry Andric NotifyValueChanged(); 49*0b57cec5SDimitry Andric break; 50*0b57cec5SDimitry Andric 51*0b57cec5SDimitry Andric case eVarSetOperationReplace: 52*0b57cec5SDimitry Andric // Must be at least one index + 1 pair of paths, and the pair count must be 53*0b57cec5SDimitry Andric // even 54*0b57cec5SDimitry Andric if (argc >= 3 && (((argc - 1) & 1) == 0)) { 55*0b57cec5SDimitry Andric uint32_t idx = 56*0b57cec5SDimitry Andric StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX); 57*0b57cec5SDimitry Andric const uint32_t count = m_path_mappings.GetSize(); 58*0b57cec5SDimitry Andric if (idx > count) { 59*0b57cec5SDimitry Andric error.SetErrorStringWithFormat( 60*0b57cec5SDimitry Andric "invalid file list index %u, index must be 0 through %u", idx, 61*0b57cec5SDimitry Andric count); 62*0b57cec5SDimitry Andric } else { 63*0b57cec5SDimitry Andric bool changed = false; 645ffd83dbSDimitry Andric for (size_t i = 1; i < argc; idx++, i += 2) { 65*0b57cec5SDimitry Andric const char *orginal_path = args.GetArgumentAtIndex(i); 66*0b57cec5SDimitry Andric const char *replace_path = args.GetArgumentAtIndex(i + 1); 67*0b57cec5SDimitry Andric if (VerifyPathExists(replace_path)) { 68*0b57cec5SDimitry Andric ConstString a(orginal_path); 69*0b57cec5SDimitry Andric ConstString b(replace_path); 70*0b57cec5SDimitry Andric if (!m_path_mappings.Replace(a, b, idx, m_notify_changes)) 71*0b57cec5SDimitry Andric m_path_mappings.Append(a, b, m_notify_changes); 72*0b57cec5SDimitry Andric changed = true; 73*0b57cec5SDimitry Andric } else { 745ffd83dbSDimitry Andric std::string previousError = 755ffd83dbSDimitry Andric error.Fail() ? std::string(error.AsCString()) + "\n" : ""; 76*0b57cec5SDimitry Andric error.SetErrorStringWithFormat( 775ffd83dbSDimitry Andric "%sthe replacement path doesn't exist: \"%s\"", 785ffd83dbSDimitry Andric previousError.c_str(), replace_path); 79*0b57cec5SDimitry Andric } 80*0b57cec5SDimitry Andric } 81*0b57cec5SDimitry Andric if (changed) 82*0b57cec5SDimitry Andric NotifyValueChanged(); 83*0b57cec5SDimitry Andric } 84*0b57cec5SDimitry Andric } else { 85*0b57cec5SDimitry Andric error.SetErrorString("replace operation takes an array index followed by " 86*0b57cec5SDimitry Andric "one or more path pairs"); 87*0b57cec5SDimitry Andric } 88*0b57cec5SDimitry Andric break; 89*0b57cec5SDimitry Andric 90*0b57cec5SDimitry Andric case eVarSetOperationAssign: 91*0b57cec5SDimitry Andric if (argc < 2 || (argc & 1)) { 92*0b57cec5SDimitry Andric error.SetErrorString("assign operation takes one or more path pairs"); 93*0b57cec5SDimitry Andric break; 94*0b57cec5SDimitry Andric } 95*0b57cec5SDimitry Andric m_path_mappings.Clear(m_notify_changes); 96*0b57cec5SDimitry Andric // Fall through to append case 97*0b57cec5SDimitry Andric LLVM_FALLTHROUGH; 98*0b57cec5SDimitry Andric case eVarSetOperationAppend: 99*0b57cec5SDimitry Andric if (argc < 2 || (argc & 1)) { 100*0b57cec5SDimitry Andric error.SetErrorString("append operation takes one or more path pairs"); 101*0b57cec5SDimitry Andric break; 102*0b57cec5SDimitry Andric } else { 103*0b57cec5SDimitry Andric bool changed = false; 104*0b57cec5SDimitry Andric for (size_t i = 0; i < argc; i += 2) { 105*0b57cec5SDimitry Andric const char *orginal_path = args.GetArgumentAtIndex(i); 106*0b57cec5SDimitry Andric const char *replace_path = args.GetArgumentAtIndex(i + 1); 107*0b57cec5SDimitry Andric if (VerifyPathExists(replace_path)) { 108*0b57cec5SDimitry Andric ConstString a(orginal_path); 109*0b57cec5SDimitry Andric ConstString b(replace_path); 110*0b57cec5SDimitry Andric m_path_mappings.Append(a, b, m_notify_changes); 111*0b57cec5SDimitry Andric m_value_was_set = true; 112*0b57cec5SDimitry Andric changed = true; 113*0b57cec5SDimitry Andric } else { 1145ffd83dbSDimitry Andric std::string previousError = 1155ffd83dbSDimitry Andric error.Fail() ? std::string(error.AsCString()) + "\n" : ""; 116*0b57cec5SDimitry Andric error.SetErrorStringWithFormat( 1175ffd83dbSDimitry Andric "%sthe replacement path doesn't exist: \"%s\"", 1185ffd83dbSDimitry Andric previousError.c_str(), replace_path); 119*0b57cec5SDimitry Andric } 120*0b57cec5SDimitry Andric } 121*0b57cec5SDimitry Andric if (changed) 122*0b57cec5SDimitry Andric NotifyValueChanged(); 123*0b57cec5SDimitry Andric } 124*0b57cec5SDimitry Andric break; 125*0b57cec5SDimitry Andric 126*0b57cec5SDimitry Andric case eVarSetOperationInsertBefore: 127*0b57cec5SDimitry Andric case eVarSetOperationInsertAfter: 128*0b57cec5SDimitry Andric // Must be at least one index + 1 pair of paths, and the pair count must be 129*0b57cec5SDimitry Andric // even 130*0b57cec5SDimitry Andric if (argc >= 3 && (((argc - 1) & 1) == 0)) { 131*0b57cec5SDimitry Andric uint32_t idx = 132*0b57cec5SDimitry Andric StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX); 133*0b57cec5SDimitry Andric const uint32_t count = m_path_mappings.GetSize(); 134*0b57cec5SDimitry Andric if (idx > count) { 135*0b57cec5SDimitry Andric error.SetErrorStringWithFormat( 136*0b57cec5SDimitry Andric "invalid file list index %u, index must be 0 through %u", idx, 137*0b57cec5SDimitry Andric count); 138*0b57cec5SDimitry Andric } else { 139*0b57cec5SDimitry Andric bool changed = false; 140*0b57cec5SDimitry Andric if (op == eVarSetOperationInsertAfter) 141*0b57cec5SDimitry Andric ++idx; 1425ffd83dbSDimitry Andric for (size_t i = 1; i < argc; i += 2) { 143*0b57cec5SDimitry Andric const char *orginal_path = args.GetArgumentAtIndex(i); 144*0b57cec5SDimitry Andric const char *replace_path = args.GetArgumentAtIndex(i + 1); 145*0b57cec5SDimitry Andric if (VerifyPathExists(replace_path)) { 146*0b57cec5SDimitry Andric ConstString a(orginal_path); 147*0b57cec5SDimitry Andric ConstString b(replace_path); 148*0b57cec5SDimitry Andric m_path_mappings.Insert(a, b, idx, m_notify_changes); 149*0b57cec5SDimitry Andric changed = true; 1505ffd83dbSDimitry Andric idx++; 151*0b57cec5SDimitry Andric } else { 1525ffd83dbSDimitry Andric std::string previousError = 1535ffd83dbSDimitry Andric error.Fail() ? std::string(error.AsCString()) + "\n" : ""; 154*0b57cec5SDimitry Andric error.SetErrorStringWithFormat( 1555ffd83dbSDimitry Andric "%sthe replacement path doesn't exist: \"%s\"", 1565ffd83dbSDimitry Andric previousError.c_str(), replace_path); 157*0b57cec5SDimitry Andric } 158*0b57cec5SDimitry Andric } 159*0b57cec5SDimitry Andric if (changed) 160*0b57cec5SDimitry Andric NotifyValueChanged(); 161*0b57cec5SDimitry Andric } 162*0b57cec5SDimitry Andric } else { 163*0b57cec5SDimitry Andric error.SetErrorString("insert operation takes an array index followed by " 164*0b57cec5SDimitry Andric "one or more path pairs"); 165*0b57cec5SDimitry Andric } 166*0b57cec5SDimitry Andric break; 167*0b57cec5SDimitry Andric 168*0b57cec5SDimitry Andric case eVarSetOperationRemove: 169*0b57cec5SDimitry Andric if (argc > 0) { 170*0b57cec5SDimitry Andric std::vector<int> remove_indexes; 1715ffd83dbSDimitry Andric for (size_t i = 0; i < argc; ++i) { 1725ffd83dbSDimitry Andric int idx = 173*0b57cec5SDimitry Andric StringConvert::ToSInt32(args.GetArgumentAtIndex(i), INT32_MAX); 1745ffd83dbSDimitry Andric if (idx < 0 || idx >= (int)m_path_mappings.GetSize()) { 175*0b57cec5SDimitry Andric error.SetErrorStringWithFormat( 176*0b57cec5SDimitry Andric "invalid array index '%s', aborting remove operation", 177*0b57cec5SDimitry Andric args.GetArgumentAtIndex(i)); 1785ffd83dbSDimitry Andric break; 1795ffd83dbSDimitry Andric } else 1805ffd83dbSDimitry Andric remove_indexes.push_back(idx); 181*0b57cec5SDimitry Andric } 1825ffd83dbSDimitry Andric 1835ffd83dbSDimitry Andric // Sort and then erase in reverse so indexes are always valid 1845ffd83dbSDimitry Andric llvm::sort(remove_indexes.begin(), remove_indexes.end()); 1855ffd83dbSDimitry Andric for (auto index : llvm::reverse(remove_indexes)) 1865ffd83dbSDimitry Andric m_path_mappings.Remove(index, m_notify_changes); 1875ffd83dbSDimitry Andric NotifyValueChanged(); 188*0b57cec5SDimitry Andric } else { 189*0b57cec5SDimitry Andric error.SetErrorString("remove operation takes one or more array index"); 190*0b57cec5SDimitry Andric } 191*0b57cec5SDimitry Andric break; 192*0b57cec5SDimitry Andric 193*0b57cec5SDimitry Andric case eVarSetOperationInvalid: 194*0b57cec5SDimitry Andric error = OptionValue::SetValueFromString(value, op); 195*0b57cec5SDimitry Andric break; 196*0b57cec5SDimitry Andric } 197*0b57cec5SDimitry Andric return error; 198*0b57cec5SDimitry Andric } 199*0b57cec5SDimitry Andric 200*0b57cec5SDimitry Andric lldb::OptionValueSP OptionValuePathMappings::DeepCopy() const { 201*0b57cec5SDimitry Andric return OptionValueSP(new OptionValuePathMappings(*this)); 202*0b57cec5SDimitry Andric } 203