1 //===-- OptionValueFileSpecList.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/OptionValueFileSpecList.h"
10 
11 #include "lldb/Utility/Args.h"
12 #include "lldb/Utility/Stream.h"
13 
14 using namespace lldb;
15 using namespace lldb_private;
16 
17 void OptionValueFileSpecList::DumpValue(const ExecutionContext *exe_ctx,
18                                         Stream &strm, uint32_t dump_mask) {
19   std::lock_guard<std::recursive_mutex> lock(m_mutex);
20   if (dump_mask & eDumpOptionType)
21     strm.Printf("(%s)", GetTypeAsCString());
22   if (dump_mask & eDumpOptionValue) {
23     const bool one_line = dump_mask & eDumpOptionCommand;
24     const uint32_t size = m_current_value.GetSize();
25     if (dump_mask & eDumpOptionType)
26       strm.Printf(" =%s",
27                   (m_current_value.GetSize() > 0 && !one_line) ? "\n" : "");
28     if (!one_line)
29       strm.IndentMore();
30     for (uint32_t i = 0; i < size; ++i) {
31       if (!one_line) {
32         strm.Indent();
33         strm.Printf("[%u]: ", i);
34       }
35       m_current_value.GetFileSpecAtIndex(i).Dump(strm.AsRawOstream());
36       if (one_line)
37         strm << ' ';
38     }
39     if (!one_line)
40       strm.IndentLess();
41   }
42 }
43 
44 Status OptionValueFileSpecList::SetValueFromString(llvm::StringRef value,
45                                                    VarSetOperationType op) {
46   std::lock_guard<std::recursive_mutex> lock(m_mutex);
47   Status error;
48   Args args(value.str());
49   const size_t argc = args.GetArgumentCount();
50 
51   switch (op) {
52   case eVarSetOperationClear:
53     Clear();
54     NotifyValueChanged();
55     break;
56 
57   case eVarSetOperationReplace:
58     if (argc > 1) {
59       uint32_t idx;
60       const uint32_t count = m_current_value.GetSize();
61       if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) {
62         error.SetErrorStringWithFormat(
63             "invalid file list index %s, index must be 0 through %u",
64             args.GetArgumentAtIndex(0), count);
65       } else {
66         for (size_t i = 1; i < argc; ++i, ++idx) {
67           FileSpec file(args.GetArgumentAtIndex(i));
68           if (idx < count)
69             m_current_value.Replace(idx, file);
70           else
71             m_current_value.Append(file);
72         }
73         NotifyValueChanged();
74       }
75     } else {
76       error.SetErrorString("replace operation takes an array index followed by "
77                            "one or more values");
78     }
79     break;
80 
81   case eVarSetOperationAssign:
82     m_current_value.Clear();
83     // Fall through to append case
84     LLVM_FALLTHROUGH;
85   case eVarSetOperationAppend:
86     if (argc > 0) {
87       m_value_was_set = true;
88       for (size_t i = 0; i < argc; ++i) {
89         FileSpec file(args.GetArgumentAtIndex(i));
90         m_current_value.Append(file);
91       }
92       NotifyValueChanged();
93     } else {
94       error.SetErrorString(
95           "assign operation takes at least one file path argument");
96     }
97     break;
98 
99   case eVarSetOperationInsertBefore:
100   case eVarSetOperationInsertAfter:
101     if (argc > 1) {
102       uint32_t idx;
103       const uint32_t count = m_current_value.GetSize();
104       if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) {
105         error.SetErrorStringWithFormat(
106             "invalid insert file list index %s, index must be 0 through %u",
107             args.GetArgumentAtIndex(0), count);
108       } else {
109         if (op == eVarSetOperationInsertAfter)
110           ++idx;
111         for (size_t i = 1; i < argc; ++i, ++idx) {
112           FileSpec file(args.GetArgumentAtIndex(i));
113           m_current_value.Insert(idx, file);
114         }
115         NotifyValueChanged();
116       }
117     } else {
118       error.SetErrorString("insert operation takes an array index followed by "
119                            "one or more values");
120     }
121     break;
122 
123   case eVarSetOperationRemove:
124     if (argc > 0) {
125       std::vector<int> remove_indexes;
126       bool all_indexes_valid = true;
127       size_t i;
128       for (i = 0; all_indexes_valid && i < argc; ++i) {
129         int idx;
130         if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx))
131           all_indexes_valid = false;
132         else
133           remove_indexes.push_back(idx);
134       }
135 
136       if (all_indexes_valid) {
137         size_t num_remove_indexes = remove_indexes.size();
138         if (num_remove_indexes) {
139           // Sort and then erase in reverse so indexes are always valid
140           llvm::sort(remove_indexes.begin(), remove_indexes.end());
141           for (size_t j = num_remove_indexes - 1; j < num_remove_indexes; ++j) {
142             m_current_value.Remove(j);
143           }
144         }
145         NotifyValueChanged();
146       } else {
147         error.SetErrorStringWithFormat(
148             "invalid array index '%s', aborting remove operation",
149             args.GetArgumentAtIndex(i));
150       }
151     } else {
152       error.SetErrorString("remove operation takes one or more array index");
153     }
154     break;
155 
156   case eVarSetOperationInvalid:
157     error = OptionValue::SetValueFromString(value, op);
158     break;
159   }
160   return error;
161 }
162 
163 OptionValueSP OptionValueFileSpecList::Clone() const {
164   std::lock_guard<std::recursive_mutex> lock(m_mutex);
165   return Cloneable::Clone();
166 }
167