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(format: "(%s)" , GetTypeAsCString()); |
30 | if (dump_mask & eDumpOptionValue) { |
31 | if (dump_mask & eDumpOptionType) |
32 | strm.Printf(format: " =%s" , (m_path_mappings.GetSize() > 0) ? "\n" : "" ); |
33 | m_path_mappings.Dump(s: &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(S: args.GetArgumentAtIndex(idx: 0), Num&: idx) || idx > count) { |
61 | error.SetErrorStringWithFormat( |
62 | "invalid file list index %s, index must be 0 through %u" , |
63 | args.GetArgumentAtIndex(idx: 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(idx: i); |
68 | const char *replace_path = args.GetArgumentAtIndex(idx: i + 1); |
69 | if (VerifyPathExists(path: replace_path)) { |
70 | if (!m_path_mappings.Replace(path: orginal_path, replacement: replace_path, index: idx, |
71 | notify: m_notify_changes)) |
72 | m_path_mappings.Append(path: orginal_path, replacement: replace_path, |
73 | notify: 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(notify: 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(idx: i); |
108 | const char *replace_path = args.GetArgumentAtIndex(idx: i + 1); |
109 | if (VerifyPathExists(path: replace_path)) { |
110 | m_path_mappings.Append(path: orginal_path, replacement: replace_path, notify: 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(S: args.GetArgumentAtIndex(idx: 0), Num&: idx) || idx > count) { |
134 | error.SetErrorStringWithFormat( |
135 | "invalid file list index %s, index must be 0 through %u" , |
136 | args.GetArgumentAtIndex(idx: 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(idx: i); |
143 | const char *replace_path = args.GetArgumentAtIndex(idx: i + 1); |
144 | if (VerifyPathExists(path: replace_path)) { |
145 | m_path_mappings.Insert(path: orginal_path, replacement: replace_path, insert_idx: idx, |
146 | notify: 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(S: args.GetArgumentAtIndex(idx: i), Num&: idx) || idx < 0 || |
172 | idx >= (int)m_path_mappings.GetSize()) { |
173 | error.SetErrorStringWithFormat( |
174 | "invalid array index '%s', aborting remove operation" , |
175 | args.GetArgumentAtIndex(idx: i)); |
176 | break; |
177 | } else |
178 | remove_indexes.push_back(x: idx); |
179 | } |
180 | |
181 | // Sort and then erase in reverse so indexes are always valid |
182 | llvm::sort(C&: remove_indexes); |
183 | for (auto index : llvm::reverse(C&: remove_indexes)) |
184 | m_path_mappings.Remove(index, notify: 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 | |