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 Status OptionValuePathMappings::SetValueFromString(llvm::StringRef value,
38                                                    VarSetOperationType op) {
39   Status error;
40   Args args(value.str());
41   const size_t argc = args.GetArgumentCount();
42 
43   switch (op) {
44   case eVarSetOperationClear:
45     Clear();
46     NotifyValueChanged();
47     break;
48 
49   case eVarSetOperationReplace:
50     // Must be at least one index + 1 pair of paths, and the pair count must be
51     // even
52     if (argc >= 3 && (((argc - 1) & 1) == 0)) {
53       uint32_t idx;
54       const uint32_t count = m_path_mappings.GetSize();
55       if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) {
56         error.SetErrorStringWithFormat(
57             "invalid file list index %s, index must be 0 through %u",
58             args.GetArgumentAtIndex(0), count);
59       } else {
60         bool changed = false;
61         for (size_t i = 1; i < argc; idx++, i += 2) {
62           const char *orginal_path = args.GetArgumentAtIndex(i);
63           const char *replace_path = args.GetArgumentAtIndex(i + 1);
64           if (VerifyPathExists(replace_path)) {
65             if (!m_path_mappings.Replace(orginal_path, replace_path, idx,
66                                          m_notify_changes))
67               m_path_mappings.Append(orginal_path, replace_path,
68                                      m_notify_changes);
69             changed = true;
70           } else {
71             std::string previousError =
72                 error.Fail() ? std::string(error.AsCString()) + "\n" : "";
73             error.SetErrorStringWithFormat(
74                 "%sthe replacement path doesn't exist: \"%s\"",
75                 previousError.c_str(), replace_path);
76           }
77         }
78         if (changed)
79           NotifyValueChanged();
80       }
81     } else {
82       error.SetErrorString("replace operation takes an array index followed by "
83                            "one or more path pairs");
84     }
85     break;
86 
87   case eVarSetOperationAssign:
88     if (argc < 2 || (argc & 1)) {
89       error.SetErrorString("assign operation takes one or more path pairs");
90       break;
91     }
92     m_path_mappings.Clear(m_notify_changes);
93     // Fall through to append case
94     LLVM_FALLTHROUGH;
95   case eVarSetOperationAppend:
96     if (argc < 2 || (argc & 1)) {
97       error.SetErrorString("append operation takes one or more path pairs");
98       break;
99     } else {
100       bool changed = false;
101       for (size_t i = 0; i < argc; i += 2) {
102         const char *orginal_path = args.GetArgumentAtIndex(i);
103         const char *replace_path = args.GetArgumentAtIndex(i + 1);
104         if (VerifyPathExists(replace_path)) {
105           m_path_mappings.Append(orginal_path, replace_path, m_notify_changes);
106           m_value_was_set = true;
107           changed = true;
108         } else {
109           std::string previousError =
110               error.Fail() ? std::string(error.AsCString()) + "\n" : "";
111           error.SetErrorStringWithFormat(
112               "%sthe replacement path doesn't exist: \"%s\"",
113               previousError.c_str(), replace_path);
114         }
115       }
116       if (changed)
117         NotifyValueChanged();
118     }
119     break;
120 
121   case eVarSetOperationInsertBefore:
122   case eVarSetOperationInsertAfter:
123     // Must be at least one index + 1 pair of paths, and the pair count must be
124     // even
125     if (argc >= 3 && (((argc - 1) & 1) == 0)) {
126       uint32_t idx;
127       const uint32_t count = m_path_mappings.GetSize();
128       if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) {
129         error.SetErrorStringWithFormat(
130             "invalid file list index %s, index must be 0 through %u",
131             args.GetArgumentAtIndex(0), count);
132       } else {
133         bool changed = false;
134         if (op == eVarSetOperationInsertAfter)
135           ++idx;
136         for (size_t i = 1; i < argc; i += 2) {
137           const char *orginal_path = args.GetArgumentAtIndex(i);
138           const char *replace_path = args.GetArgumentAtIndex(i + 1);
139           if (VerifyPathExists(replace_path)) {
140             m_path_mappings.Insert(orginal_path, replace_path, idx,
141                                    m_notify_changes);
142             changed = true;
143             idx++;
144           } else {
145             std::string previousError =
146                 error.Fail() ? std::string(error.AsCString()) + "\n" : "";
147             error.SetErrorStringWithFormat(
148                 "%sthe replacement path doesn't exist: \"%s\"",
149                 previousError.c_str(), replace_path);
150           }
151         }
152         if (changed)
153           NotifyValueChanged();
154       }
155     } else {
156       error.SetErrorString("insert operation takes an array index followed by "
157                            "one or more path pairs");
158     }
159     break;
160 
161   case eVarSetOperationRemove:
162     if (argc > 0) {
163       std::vector<int> remove_indexes;
164       for (size_t i = 0; i < argc; ++i) {
165         int idx;
166         if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx) || idx < 0 ||
167             idx >= (int)m_path_mappings.GetSize()) {
168           error.SetErrorStringWithFormat(
169               "invalid array index '%s', aborting remove operation",
170               args.GetArgumentAtIndex(i));
171           break;
172         } else
173           remove_indexes.push_back(idx);
174       }
175 
176       // Sort and then erase in reverse so indexes are always valid
177       llvm::sort(remove_indexes);
178       for (auto index : llvm::reverse(remove_indexes))
179         m_path_mappings.Remove(index, m_notify_changes);
180       NotifyValueChanged();
181     } else {
182       error.SetErrorString("remove operation takes one or more array index");
183     }
184     break;
185 
186   case eVarSetOperationInvalid:
187     error = OptionValue::SetValueFromString(value, op);
188     break;
189   }
190   return error;
191 }
192