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.SetErrorStringWithFormat(
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.SetErrorStringWithFormat(
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.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     [[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           m_path_mappings.Append(orginal_path, replace_path, 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       const uint32_t count = m_path_mappings.GetSize();
133       if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) {
134         error.SetErrorStringWithFormat(
135             "invalid file list index %s, index must be 0 through %u",
136             args.GetArgumentAtIndex(0), count);
137       } else {
138         bool changed = false;
139         if (op == eVarSetOperationInsertAfter)
140           ++idx;
141         for (size_t i = 1; i < argc; i += 2) {
142           const char *orginal_path = args.GetArgumentAtIndex(i);
143           const char *replace_path = args.GetArgumentAtIndex(i + 1);
144           if (VerifyPathExists(replace_path)) {
145             m_path_mappings.Insert(orginal_path, replace_path, idx,
146                                    m_notify_changes);
147             changed = true;
148             idx++;
149           } else {
150             std::string previousError =
151                 error.Fail() ? std::string(error.AsCString()) + "\n" : "";
152             error.SetErrorStringWithFormat(
153                 "%sthe replacement path doesn't exist: \"%s\"",
154                 previousError.c_str(), replace_path);
155           }
156         }
157         if (changed)
158           NotifyValueChanged();
159       }
160     } else {
161       error.SetErrorString("insert operation takes an array index followed by "
162                            "one or more path pairs");
163     }
164     break;
165 
166   case eVarSetOperationRemove:
167     if (argc > 0) {
168       std::vector<int> remove_indexes;
169       for (size_t i = 0; i < argc; ++i) {
170         int idx;
171         if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx) || idx < 0 ||
172             idx >= (int)m_path_mappings.GetSize()) {
173           error.SetErrorStringWithFormat(
174               "invalid array index '%s', aborting remove operation",
175               args.GetArgumentAtIndex(i));
176           break;
177         } else
178           remove_indexes.push_back(idx);
179       }
180 
181       // Sort and then erase in reverse so indexes are always valid
182       llvm::sort(remove_indexes);
183       for (auto index : llvm::reverse(remove_indexes))
184         m_path_mappings.Remove(index, m_notify_changes);
185       NotifyValueChanged();
186     } else {
187       error.SetErrorString("remove operation takes one or more array index");
188     }
189     break;
190 
191   case eVarSetOperationInvalid:
192     error = OptionValue::SetValueFromString(value, op);
193     break;
194   }
195   return error;
196 }
197