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             ConstString a(orginal_path);
66             ConstString b(replace_path);
67             if (!m_path_mappings.Replace(a, b, idx, m_notify_changes))
68               m_path_mappings.Append(a, b, 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           ConstString a(orginal_path);
106           ConstString b(replace_path);
107           m_path_mappings.Append(a, b, m_notify_changes);
108           m_value_was_set = true;
109           changed = true;
110         } else {
111           std::string previousError =
112               error.Fail() ? std::string(error.AsCString()) + "\n" : "";
113           error.SetErrorStringWithFormat(
114               "%sthe replacement path doesn't exist: \"%s\"",
115               previousError.c_str(), replace_path);
116         }
117       }
118       if (changed)
119         NotifyValueChanged();
120     }
121     break;
122 
123   case eVarSetOperationInsertBefore:
124   case eVarSetOperationInsertAfter:
125     // Must be at least one index + 1 pair of paths, and the pair count must be
126     // even
127     if (argc >= 3 && (((argc - 1) & 1) == 0)) {
128       uint32_t idx;
129       const uint32_t count = m_path_mappings.GetSize();
130       if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) {
131         error.SetErrorStringWithFormat(
132             "invalid file list index %s, index must be 0 through %u",
133             args.GetArgumentAtIndex(0), count);
134       } else {
135         bool changed = false;
136         if (op == eVarSetOperationInsertAfter)
137           ++idx;
138         for (size_t i = 1; i < argc; i += 2) {
139           const char *orginal_path = args.GetArgumentAtIndex(i);
140           const char *replace_path = args.GetArgumentAtIndex(i + 1);
141           if (VerifyPathExists(replace_path)) {
142             ConstString a(orginal_path);
143             ConstString b(replace_path);
144             m_path_mappings.Insert(a, b, idx, m_notify_changes);
145             changed = true;
146             idx++;
147           } else {
148             std::string previousError =
149                 error.Fail() ? std::string(error.AsCString()) + "\n" : "";
150             error.SetErrorStringWithFormat(
151                 "%sthe replacement path doesn't exist: \"%s\"",
152                 previousError.c_str(), replace_path);
153           }
154         }
155         if (changed)
156           NotifyValueChanged();
157       }
158     } else {
159       error.SetErrorString("insert operation takes an array index followed by "
160                            "one or more path pairs");
161     }
162     break;
163 
164   case eVarSetOperationRemove:
165     if (argc > 0) {
166       std::vector<int> remove_indexes;
167       for (size_t i = 0; i < argc; ++i) {
168         int idx;
169         if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx) || idx < 0 ||
170             idx >= (int)m_path_mappings.GetSize()) {
171           error.SetErrorStringWithFormat(
172               "invalid array index '%s', aborting remove operation",
173               args.GetArgumentAtIndex(i));
174           break;
175         } else
176           remove_indexes.push_back(idx);
177       }
178 
179       // Sort and then erase in reverse so indexes are always valid
180       llvm::sort(remove_indexes.begin(), remove_indexes.end());
181       for (auto index : llvm::reverse(remove_indexes))
182         m_path_mappings.Remove(index, m_notify_changes);
183       NotifyValueChanged();
184     } else {
185       error.SetErrorString("remove operation takes one or more array index");
186     }
187     break;
188 
189   case eVarSetOperationInvalid:
190     error = OptionValue::SetValueFromString(value, op);
191     break;
192   }
193   return error;
194 }
195