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