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