xref: /llvm-project/lldb/source/Interpreter/OptionValuePathMappings.cpp (revision 0642cd768b80665585c8500bed2933a3b99123dc)
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 = Status::FromErrorStringWithFormat(
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 = Status::FromErrorStringWithFormat(
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 = Status::FromErrorString(
88           "replace operation takes an array index followed by "
89           "one or more path pairs");
90     }
91     break;
92 
93   case eVarSetOperationAssign:
94     if (argc < 2 || (argc & 1)) {
95       error = Status::FromErrorString(
96           "assign operation takes one or more path pairs");
97       break;
98     }
99     m_path_mappings.Clear(m_notify_changes);
100     // Fall through to append case
101     [[fallthrough]];
102   case eVarSetOperationAppend:
103     if (argc < 2 || (argc & 1)) {
104       error = Status::FromErrorString(
105           "append operation takes one or more path pairs");
106       break;
107     } else {
108       bool changed = false;
109       for (size_t i = 0; i < argc; i += 2) {
110         const char *orginal_path = args.GetArgumentAtIndex(i);
111         const char *replace_path = args.GetArgumentAtIndex(i + 1);
112         if (VerifyPathExists(replace_path)) {
113           m_path_mappings.Append(orginal_path, replace_path, m_notify_changes);
114           m_value_was_set = true;
115           changed = true;
116         } else {
117           std::string previousError =
118               error.Fail() ? std::string(error.AsCString()) + "\n" : "";
119           error = Status::FromErrorStringWithFormat(
120               "%sthe replacement path doesn't exist: \"%s\"",
121               previousError.c_str(), replace_path);
122         }
123       }
124       if (changed)
125         NotifyValueChanged();
126     }
127     break;
128 
129   case eVarSetOperationInsertBefore:
130   case eVarSetOperationInsertAfter:
131     // Must be at least one index + 1 pair of paths, and the pair count must be
132     // even
133     if (argc >= 3 && (((argc - 1) & 1) == 0)) {
134       uint32_t idx;
135       const uint32_t count = m_path_mappings.GetSize();
136       if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) {
137         error = Status::FromErrorStringWithFormat(
138             "invalid file list index %s, index must be 0 through %u",
139             args.GetArgumentAtIndex(0), 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             m_path_mappings.Insert(orginal_path, replace_path, idx,
149                                    m_notify_changes);
150             changed = true;
151             idx++;
152           } else {
153             std::string previousError =
154                 error.Fail() ? std::string(error.AsCString()) + "\n" : "";
155             error = Status::FromErrorStringWithFormat(
156                 "%sthe replacement path doesn't exist: \"%s\"",
157                 previousError.c_str(), replace_path);
158           }
159         }
160         if (changed)
161           NotifyValueChanged();
162       }
163     } else {
164       error = Status::FromErrorString(
165           "insert operation takes an array index followed by "
166           "one or more path pairs");
167     }
168     break;
169 
170   case eVarSetOperationRemove:
171     if (argc > 0) {
172       std::vector<int> remove_indexes;
173       for (size_t i = 0; i < argc; ++i) {
174         int idx;
175         if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx) || idx < 0 ||
176             idx >= (int)m_path_mappings.GetSize()) {
177           error = Status::FromErrorStringWithFormat(
178               "invalid array index '%s', aborting remove operation",
179               args.GetArgumentAtIndex(i));
180           break;
181         } else
182           remove_indexes.push_back(idx);
183       }
184 
185       // Sort and then erase in reverse so indexes are always valid
186       llvm::sort(remove_indexes);
187       for (auto index : llvm::reverse(remove_indexes))
188         m_path_mappings.Remove(index, m_notify_changes);
189       NotifyValueChanged();
190     } else {
191       error = Status::FromErrorString(
192           "remove operation takes one or more array index");
193     }
194     break;
195 
196   case eVarSetOperationInvalid:
197     error = OptionValue::SetValueFromString(value, op);
198     break;
199   }
200   return error;
201 }
202