1e8d8bef9SDimitry Andric //===-- OptionValueFileColonLine.cpp---------------------------------------===//
2e8d8bef9SDimitry Andric //
3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e8d8bef9SDimitry Andric //
7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
8e8d8bef9SDimitry Andric 
9e8d8bef9SDimitry Andric #include "lldb/Interpreter/OptionValueFileColonLine.h"
10e8d8bef9SDimitry Andric 
11e8d8bef9SDimitry Andric #include "lldb/DataFormatters/FormatManager.h"
12e8d8bef9SDimitry Andric #include "lldb/Interpreter/CommandCompletions.h"
13e8d8bef9SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h"
14e8d8bef9SDimitry Andric #include "lldb/Utility/Args.h"
15e8d8bef9SDimitry Andric #include "lldb/Utility/State.h"
16e8d8bef9SDimitry Andric 
17e8d8bef9SDimitry Andric using namespace lldb;
18e8d8bef9SDimitry Andric using namespace lldb_private;
19e8d8bef9SDimitry Andric 
20e8d8bef9SDimitry Andric // This is an OptionValue for parsing file:line:column specifications.
21e8d8bef9SDimitry Andric // I set the completer to "source file" which isn't quite right, but we can
22e8d8bef9SDimitry Andric // only usefully complete in the file name part of it so it should be good
23e8d8bef9SDimitry Andric // enough.
24fe6060f1SDimitry Andric OptionValueFileColonLine::OptionValueFileColonLine() = default;
25e8d8bef9SDimitry Andric 
OptionValueFileColonLine(llvm::StringRef input)26e8d8bef9SDimitry Andric OptionValueFileColonLine::OptionValueFileColonLine(llvm::StringRef input)
2781ad6265SDimitry Andric 
2881ad6265SDimitry Andric {
29e8d8bef9SDimitry Andric   SetValueFromString(input, eVarSetOperationAssign);
30e8d8bef9SDimitry Andric }
31e8d8bef9SDimitry Andric 
DumpValue(const ExecutionContext * exe_ctx,Stream & strm,uint32_t dump_mask)32e8d8bef9SDimitry Andric void OptionValueFileColonLine::DumpValue(const ExecutionContext *exe_ctx,
33e8d8bef9SDimitry Andric                                          Stream &strm, uint32_t dump_mask) {
34e8d8bef9SDimitry Andric   if (dump_mask & eDumpOptionType)
35e8d8bef9SDimitry Andric     strm.Printf("(%s)", GetTypeAsCString());
36e8d8bef9SDimitry Andric   if (dump_mask & eDumpOptionValue) {
37e8d8bef9SDimitry Andric     if (dump_mask & eDumpOptionType)
38e8d8bef9SDimitry Andric       strm.PutCString(" = ");
39e8d8bef9SDimitry Andric 
40e8d8bef9SDimitry Andric     if (m_file_spec)
41e8d8bef9SDimitry Andric       strm << '"' << m_file_spec.GetPath().c_str() << '"';
42e8d8bef9SDimitry Andric     if (m_line_number != LLDB_INVALID_LINE_NUMBER)
43e8d8bef9SDimitry Andric       strm.Printf(":%d", m_line_number);
44e8d8bef9SDimitry Andric     if (m_column_number != LLDB_INVALID_COLUMN_NUMBER)
45e8d8bef9SDimitry Andric       strm.Printf(":%d", m_column_number);
46e8d8bef9SDimitry Andric   }
47e8d8bef9SDimitry Andric }
48e8d8bef9SDimitry Andric 
SetValueFromString(llvm::StringRef value,VarSetOperationType op)49e8d8bef9SDimitry Andric Status OptionValueFileColonLine::SetValueFromString(llvm::StringRef value,
50e8d8bef9SDimitry Andric                                                     VarSetOperationType op) {
51e8d8bef9SDimitry Andric   Status error;
52e8d8bef9SDimitry Andric   switch (op) {
53e8d8bef9SDimitry Andric   case eVarSetOperationClear:
54e8d8bef9SDimitry Andric     Clear();
55e8d8bef9SDimitry Andric     NotifyValueChanged();
56e8d8bef9SDimitry Andric     break;
57e8d8bef9SDimitry Andric 
58e8d8bef9SDimitry Andric   case eVarSetOperationReplace:
59e8d8bef9SDimitry Andric   case eVarSetOperationAssign:
60e8d8bef9SDimitry Andric     if (value.size() > 0) {
61e8d8bef9SDimitry Andric       // This is in the form filename:linenumber:column.
62e8d8bef9SDimitry Andric       // I wish we could use filename:linenumber.column, that would make the
63e8d8bef9SDimitry Andric       // parsing unambiguous and so much easier...
64e8d8bef9SDimitry Andric       // But clang & gcc both print the output with two : so we're stuck with
65e8d8bef9SDimitry Andric       // the two colons.  Practically, the only actual ambiguity this introduces
66e8d8bef9SDimitry Andric       // is with files like "foo:10", which doesn't seem terribly likely.
67e8d8bef9SDimitry Andric 
68e8d8bef9SDimitry Andric       // Providing the column is optional, so the input value might have one or
69e8d8bef9SDimitry Andric       // two colons.  First pick off the last colon separated piece.
70e8d8bef9SDimitry Andric       // It has to be there, since the line number is required:
71e8d8bef9SDimitry Andric       llvm::StringRef last_piece;
72e8d8bef9SDimitry Andric       llvm::StringRef left_of_last_piece;
73e8d8bef9SDimitry Andric 
74e8d8bef9SDimitry Andric       std::tie(left_of_last_piece, last_piece) = value.rsplit(':');
75e8d8bef9SDimitry Andric       if (last_piece.empty()) {
76e8d8bef9SDimitry Andric         error.SetErrorStringWithFormat("Line specifier must include file and "
77e8d8bef9SDimitry Andric                                        "line: '%s'",
78e8d8bef9SDimitry Andric                                        value.str().c_str());
79e8d8bef9SDimitry Andric         return error;
80e8d8bef9SDimitry Andric       }
81e8d8bef9SDimitry Andric 
82e8d8bef9SDimitry Andric       // Now see if there's another colon and if so pull out the middle piece:
83e8d8bef9SDimitry Andric       // Then check whether the middle piece is an integer.  If it is, then it
84e8d8bef9SDimitry Andric       // was the line number, and if it isn't we're going to assume that there
85e8d8bef9SDimitry Andric       // was a colon in the filename (see note at the beginning of the function)
86e8d8bef9SDimitry Andric       // and ignore it.
87e8d8bef9SDimitry Andric       llvm::StringRef file_name;
88e8d8bef9SDimitry Andric       llvm::StringRef middle_piece;
89e8d8bef9SDimitry Andric 
90e8d8bef9SDimitry Andric       std::tie(file_name, middle_piece) = left_of_last_piece.rsplit(':');
91fe6060f1SDimitry Andric       if (middle_piece.empty() ||
92fe6060f1SDimitry Andric           !llvm::to_integer(middle_piece, m_line_number)) {
93e8d8bef9SDimitry Andric         // The middle piece was empty or not an integer, so there were only two
94e8d8bef9SDimitry Andric         // legit pieces; our original division was right.  Reassign the file
95e8d8bef9SDimitry Andric         // name and pull out the line number:
96e8d8bef9SDimitry Andric         file_name = left_of_last_piece;
97e8d8bef9SDimitry Andric         if (!llvm::to_integer(last_piece, m_line_number)) {
98e8d8bef9SDimitry Andric           error.SetErrorStringWithFormat("Bad line number value '%s' in: '%s'",
99e8d8bef9SDimitry Andric                                          last_piece.str().c_str(),
100e8d8bef9SDimitry Andric                                          value.str().c_str());
101e8d8bef9SDimitry Andric           return error;
102e8d8bef9SDimitry Andric         }
103e8d8bef9SDimitry Andric       } else {
104e8d8bef9SDimitry Andric         // There were three pieces, and we've got the line number.  So now
105e8d8bef9SDimitry Andric         // we just need to check the column number which was the last peice.
106e8d8bef9SDimitry Andric         if (!llvm::to_integer(last_piece, m_column_number)) {
107e8d8bef9SDimitry Andric           error.SetErrorStringWithFormat("Bad column value '%s' in: '%s'",
108e8d8bef9SDimitry Andric                                          last_piece.str().c_str(),
109e8d8bef9SDimitry Andric                                          value.str().c_str());
110e8d8bef9SDimitry Andric           return error;
111e8d8bef9SDimitry Andric         }
112e8d8bef9SDimitry Andric       }
113e8d8bef9SDimitry Andric 
114e8d8bef9SDimitry Andric       m_value_was_set = true;
115e8d8bef9SDimitry Andric       m_file_spec.SetFile(file_name, FileSpec::Style::native);
116e8d8bef9SDimitry Andric       NotifyValueChanged();
117e8d8bef9SDimitry Andric     } else {
118e8d8bef9SDimitry Andric       error.SetErrorString("invalid value string");
119e8d8bef9SDimitry Andric     }
120e8d8bef9SDimitry Andric     break;
121e8d8bef9SDimitry Andric 
122e8d8bef9SDimitry Andric   case eVarSetOperationInsertBefore:
123e8d8bef9SDimitry Andric   case eVarSetOperationInsertAfter:
124e8d8bef9SDimitry Andric   case eVarSetOperationRemove:
125e8d8bef9SDimitry Andric   case eVarSetOperationAppend:
126e8d8bef9SDimitry Andric   case eVarSetOperationInvalid:
127e8d8bef9SDimitry Andric     error = OptionValue::SetValueFromString(value, op);
128e8d8bef9SDimitry Andric     break;
129e8d8bef9SDimitry Andric   }
130e8d8bef9SDimitry Andric   return error;
131e8d8bef9SDimitry Andric }
132e8d8bef9SDimitry Andric 
AutoComplete(CommandInterpreter & interpreter,CompletionRequest & request)133e8d8bef9SDimitry Andric void OptionValueFileColonLine::AutoComplete(CommandInterpreter &interpreter,
134e8d8bef9SDimitry Andric                                             CompletionRequest &request) {
135*06c3fb27SDimitry Andric   lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
136e8d8bef9SDimitry Andric       interpreter, m_completion_mask, request, nullptr);
137e8d8bef9SDimitry Andric }
138