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; 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             idx++;
74           } else {
75             std::string previousError =
76                 error.Fail() ? std::string(error.AsCString()) + "\n" : "";
77             error.SetErrorStringWithFormat(
78                 "%sthe replacement path doesn't exist: \"%s\"",
79                 previousError.c_str(), replace_path);
80             break;
81           }
82         }
83         if (changed)
84           NotifyValueChanged();
85       }
86     } else {
87       error.SetErrorString("replace operation takes an array index followed by "
88                            "one or more path pairs");
89     }
90     break;
91 
92   case eVarSetOperationAssign:
93     if (argc < 2 || (argc & 1)) {
94       error.SetErrorString("assign operation takes one or more path pairs");
95       break;
96     }
97     m_path_mappings.Clear(m_notify_changes);
98     // Fall through to append case
99     LLVM_FALLTHROUGH;
100   case eVarSetOperationAppend:
101     if (argc < 2 || (argc & 1)) {
102       error.SetErrorString("append operation takes one or more path pairs");
103       break;
104     } else {
105       bool changed = false;
106       for (size_t i = 0; i < argc; i += 2) {
107         const char *orginal_path = args.GetArgumentAtIndex(i);
108         const char *replace_path = args.GetArgumentAtIndex(i + 1);
109         if (VerifyPathExists(replace_path)) {
110           ConstString a(orginal_path);
111           ConstString b(replace_path);
112           m_path_mappings.Append(a, b, m_notify_changes);
113           m_value_was_set = true;
114           changed = true;
115         } else {
116           std::string previousError =
117               error.Fail() ? std::string(error.AsCString()) + "\n" : "";
118           error.SetErrorStringWithFormat(
119               "%sthe replacement path doesn't exist: \"%s\"",
120               previousError.c_str(), replace_path);
121         }
122       }
123       if (changed)
124         NotifyValueChanged();
125     }
126     break;
127 
128   case eVarSetOperationInsertBefore:
129   case eVarSetOperationInsertAfter:
130     // Must be at least one index + 1 pair of paths, and the pair count must be
131     // even
132     if (argc >= 3 && (((argc - 1) & 1) == 0)) {
133       uint32_t idx =
134           StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
135       const uint32_t count = m_path_mappings.GetSize();
136       if (idx > count) {
137         error.SetErrorStringWithFormat(
138             "invalid file list index %u, index must be 0 through %u", idx,
139             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             ConstString a(orginal_path);
149             ConstString b(replace_path);
150             m_path_mappings.Insert(a, b, idx, m_notify_changes);
151             changed = true;
152             idx++;
153           } else {
154             std::string previousError =
155                 error.Fail() ? std::string(error.AsCString()) + "\n" : "";
156             error.SetErrorStringWithFormat(
157                 "%sthe replacement path doesn't exist: \"%s\"",
158                 previousError.c_str(), replace_path);
159             break;
160           }
161         }
162         if (changed)
163           NotifyValueChanged();
164       }
165     } else {
166       error.SetErrorString("insert operation takes an array index followed by "
167                            "one or more path pairs");
168     }
169     break;
170 
171   case eVarSetOperationRemove:
172     if (argc > 0) {
173       std::vector<int> remove_indexes;
174       bool all_indexes_valid = true;
175       size_t i;
176       for (i = 0; all_indexes_valid && i < argc; ++i) {
177         const int idx =
178             StringConvert::ToSInt32(args.GetArgumentAtIndex(i), INT32_MAX);
179         if (idx == INT32_MAX)
180           all_indexes_valid = false;
181         else
182           remove_indexes.push_back(idx);
183       }
184 
185       if (all_indexes_valid) {
186         size_t num_remove_indexes = remove_indexes.size();
187         if (num_remove_indexes) {
188           // Sort and then erase in reverse so indexes are always valid
189           llvm::sort(remove_indexes.begin(), remove_indexes.end());
190           for (size_t j = num_remove_indexes - 1; j < num_remove_indexes; ++j) {
191             m_path_mappings.Remove(j, m_notify_changes);
192           }
193         }
194         NotifyValueChanged();
195       } else {
196         error.SetErrorStringWithFormat(
197             "invalid array index '%s', aborting remove operation",
198             args.GetArgumentAtIndex(i));
199       }
200     } else {
201       error.SetErrorString("remove operation takes one or more array index");
202     }
203     break;
204 
205   case eVarSetOperationInvalid:
206     error = OptionValue::SetValueFromString(value, op);
207     break;
208   }
209   return error;
210 }
211 
212 lldb::OptionValueSP OptionValuePathMappings::DeepCopy() const {
213   return OptionValueSP(new OptionValuePathMappings(*this));
214 }
215