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