1cfca06d7SDimitry Andric //===-- IOHandler.cpp -----------------------------------------------------===//
2866dcdacSEd Maste //
35f29bb8aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f29bb8aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f29bb8aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6866dcdacSEd Maste //
7866dcdacSEd Maste //===----------------------------------------------------------------------===//
8866dcdacSEd Maste
974a628f7SDimitry Andric #include "lldb/Core/IOHandler.h"
1074a628f7SDimitry Andric
11e81d9d49SDimitry Andric #if defined(__APPLE__)
12e81d9d49SDimitry Andric #include <deque>
13e81d9d49SDimitry Andric #endif
14866dcdacSEd Maste #include <string>
15866dcdacSEd Maste
16866dcdacSEd Maste #include "lldb/Core/Debugger.h"
17706b4fc4SDimitry Andric #include "lldb/Host/Config.h"
1894994d37SDimitry Andric #include "lldb/Host/File.h"
19b1c73532SDimitry Andric #include "lldb/Host/StreamFile.h"
20145449b1SDimitry Andric #include "lldb/Utility/AnsiTerminal.h"
2194994d37SDimitry Andric #include "lldb/Utility/Predicate.h"
2294994d37SDimitry Andric #include "lldb/Utility/Status.h"
2394994d37SDimitry Andric #include "lldb/Utility/StreamString.h"
2494994d37SDimitry Andric #include "lldb/Utility/StringList.h"
2594994d37SDimitry Andric #include "lldb/lldb-forward.h"
2674a628f7SDimitry Andric
27706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
28866dcdacSEd Maste #include "lldb/Host/Editline.h"
29205afe67SEd Maste #endif
30866dcdacSEd Maste #include "lldb/Interpreter/CommandCompletions.h"
31866dcdacSEd Maste #include "lldb/Interpreter/CommandInterpreter.h"
3294994d37SDimitry Andric #include "llvm/ADT/StringRef.h"
3374a628f7SDimitry Andric
34ead24645SDimitry Andric #ifdef _WIN32
35fdea456aSDimitry Andric #include "lldb/Host/windows/windows.h"
36f3fbd1c0SDimitry Andric #endif
37866dcdacSEd Maste
3894994d37SDimitry Andric #include <memory>
3994994d37SDimitry Andric #include <mutex>
40e3b55780SDimitry Andric #include <optional>
4174a628f7SDimitry Andric
42344a3780SDimitry Andric #include <cassert>
43344a3780SDimitry Andric #include <cctype>
44344a3780SDimitry Andric #include <cerrno>
45344a3780SDimitry Andric #include <clocale>
46344a3780SDimitry Andric #include <cstdint>
47344a3780SDimitry Andric #include <cstdio>
48344a3780SDimitry Andric #include <cstring>
4994994d37SDimitry Andric #include <type_traits>
5074a628f7SDimitry Andric
51866dcdacSEd Maste using namespace lldb;
52866dcdacSEd Maste using namespace lldb_private;
53ead24645SDimitry Andric using llvm::StringRef;
54ead24645SDimitry Andric
IOHandler(Debugger & debugger,IOHandler::Type type)5514f1b3e8SDimitry Andric IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
5614f1b3e8SDimitry Andric : IOHandler(debugger, type,
57ead24645SDimitry Andric FileSP(), // Adopt STDIN from top input reader
58866dcdacSEd Maste StreamFileSP(), // Adopt STDOUT from top input reader
59866dcdacSEd Maste StreamFileSP(), // Adopt STDERR from top input reader
60e3b55780SDimitry Andric 0 // Flags
61e3b55780SDimitry Andric
625f29bb8aSDimitry Andric ) {}
63866dcdacSEd Maste
IOHandler(Debugger & debugger,IOHandler::Type type,const lldb::FileSP & input_sp,const lldb::StreamFileSP & output_sp,const lldb::StreamFileSP & error_sp,uint32_t flags)6414f1b3e8SDimitry Andric IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type,
65ead24645SDimitry Andric const lldb::FileSP &input_sp,
66866dcdacSEd Maste const lldb::StreamFileSP &output_sp,
67e3b55780SDimitry Andric const lldb::StreamFileSP &error_sp, uint32_t flags)
6814f1b3e8SDimitry Andric : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
69e3b55780SDimitry Andric m_error_sp(error_sp), m_popped(false), m_flags(flags), m_type(type),
70e3b55780SDimitry Andric m_user_data(nullptr), m_done(false), m_active(false) {
71866dcdacSEd Maste // If any files are not specified, then adopt them from the top input reader.
72866dcdacSEd Maste if (!m_input_sp || !m_output_sp || !m_error_sp)
7314f1b3e8SDimitry Andric debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp,
74866dcdacSEd Maste m_error_sp);
75866dcdacSEd Maste }
76866dcdacSEd Maste
77e81d9d49SDimitry Andric IOHandler::~IOHandler() = default;
78866dcdacSEd Maste
GetInputFD()7914f1b3e8SDimitry Andric int IOHandler::GetInputFD() {
80ead24645SDimitry Andric return (m_input_sp ? m_input_sp->GetDescriptor() : -1);
81866dcdacSEd Maste }
82866dcdacSEd Maste
GetOutputFD()8314f1b3e8SDimitry Andric int IOHandler::GetOutputFD() {
84f3fbd1c0SDimitry Andric return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1);
85866dcdacSEd Maste }
86866dcdacSEd Maste
GetErrorFD()8714f1b3e8SDimitry Andric int IOHandler::GetErrorFD() {
88f3fbd1c0SDimitry Andric return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1);
89866dcdacSEd Maste }
90866dcdacSEd Maste
GetInputFILE()9114f1b3e8SDimitry Andric FILE *IOHandler::GetInputFILE() {
92ead24645SDimitry Andric return (m_input_sp ? m_input_sp->GetStream() : nullptr);
93866dcdacSEd Maste }
94866dcdacSEd Maste
GetOutputFILE()9514f1b3e8SDimitry Andric FILE *IOHandler::GetOutputFILE() {
96f3fbd1c0SDimitry Andric return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr);
97866dcdacSEd Maste }
98866dcdacSEd Maste
GetErrorFILE()9914f1b3e8SDimitry Andric FILE *IOHandler::GetErrorFILE() {
100f3fbd1c0SDimitry Andric return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr);
101866dcdacSEd Maste }
102866dcdacSEd Maste
GetInputFileSP()103b60736ecSDimitry Andric FileSP IOHandler::GetInputFileSP() { return m_input_sp; }
104866dcdacSEd Maste
GetOutputStreamFileSP()105b60736ecSDimitry Andric StreamFileSP IOHandler::GetOutputStreamFileSP() { return m_output_sp; }
106866dcdacSEd Maste
GetErrorStreamFileSP()107b60736ecSDimitry Andric StreamFileSP IOHandler::GetErrorStreamFileSP() { return m_error_sp; }
108866dcdacSEd Maste
GetIsInteractive()10914f1b3e8SDimitry Andric bool IOHandler::GetIsInteractive() {
110ead24645SDimitry Andric return GetInputFileSP() ? GetInputFileSP()->GetIsInteractive() : false;
111866dcdacSEd Maste }
112866dcdacSEd Maste
GetIsRealTerminal()11314f1b3e8SDimitry Andric bool IOHandler::GetIsRealTerminal() {
114ead24645SDimitry Andric return GetInputFileSP() ? GetInputFileSP()->GetIsRealTerminal() : false;
115866dcdacSEd Maste }
116866dcdacSEd Maste
SetPopped(bool b)11714f1b3e8SDimitry Andric void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
118205afe67SEd Maste
WaitForPop()11914f1b3e8SDimitry Andric void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
120205afe67SEd Maste
PrintAsync(const char * s,size_t len,bool is_stdout)121145449b1SDimitry Andric void IOHandler::PrintAsync(const char *s, size_t len, bool is_stdout) {
122145449b1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
123145449b1SDimitry Andric lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
124cfca06d7SDimitry Andric stream->Write(s, len);
125145449b1SDimitry Andric stream->Flush();
1265e95aa85SEd Maste }
127145449b1SDimitry Andric
PrintAsync(const char * s,size_t len,bool is_stdout)128145449b1SDimitry Andric bool IOHandlerStack::PrintAsync(const char *s, size_t len, bool is_stdout) {
129145449b1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_mutex);
130145449b1SDimitry Andric if (!m_top)
131145449b1SDimitry Andric return false;
132145449b1SDimitry Andric m_top->PrintAsync(s, len, is_stdout);
133145449b1SDimitry Andric return true;
1345e95aa85SEd Maste }
1355e95aa85SEd Maste
IOHandlerConfirm(Debugger & debugger,llvm::StringRef prompt,bool default_response)13614f1b3e8SDimitry Andric IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
13714f1b3e8SDimitry Andric bool default_response)
13814f1b3e8SDimitry Andric : IOHandlerEditline(
13914f1b3e8SDimitry Andric debugger, IOHandler::Type::Confirm,
140f3fbd1c0SDimitry Andric nullptr, // nullptr editline_name means no history loaded/saved
14114f1b3e8SDimitry Andric llvm::StringRef(), // No prompt
14214f1b3e8SDimitry Andric llvm::StringRef(), // No continuation prompt
143866dcdacSEd Maste false, // Multi-line
144205afe67SEd Maste false, // Don't colorize the prompt (i.e. the confirm message.)
145e3b55780SDimitry Andric 0, *this),
14614f1b3e8SDimitry Andric m_default_response(default_response), m_user_response(default_response) {
147866dcdacSEd Maste StreamString prompt_stream;
148866dcdacSEd Maste prompt_stream.PutCString(prompt);
149866dcdacSEd Maste if (m_default_response)
150866dcdacSEd Maste prompt_stream.Printf(": [Y/n] ");
151866dcdacSEd Maste else
152866dcdacSEd Maste prompt_stream.Printf(": [y/N] ");
153866dcdacSEd Maste
15414f1b3e8SDimitry Andric SetPrompt(prompt_stream.GetString());
155866dcdacSEd Maste }
156866dcdacSEd Maste
157e81d9d49SDimitry Andric IOHandlerConfirm::~IOHandlerConfirm() = default;
158866dcdacSEd Maste
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)159ead24645SDimitry Andric void IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler,
160ead24645SDimitry Andric CompletionRequest &request) {
161ead24645SDimitry Andric if (request.GetRawCursorPos() != 0)
162ead24645SDimitry Andric return;
163ead24645SDimitry Andric request.AddCompletion(m_default_response ? "y" : "n");
164866dcdacSEd Maste }
165866dcdacSEd Maste
IOHandlerInputComplete(IOHandler & io_handler,std::string & line)16614f1b3e8SDimitry Andric void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler,
16714f1b3e8SDimitry Andric std::string &line) {
16814f1b3e8SDimitry Andric if (line.empty()) {
169866dcdacSEd Maste // User just hit enter, set the response to the default
170866dcdacSEd Maste m_user_response = m_default_response;
171866dcdacSEd Maste io_handler.SetIsDone(true);
172866dcdacSEd Maste return;
173866dcdacSEd Maste }
174866dcdacSEd Maste
17514f1b3e8SDimitry Andric if (line.size() == 1) {
17614f1b3e8SDimitry Andric switch (line[0]) {
177866dcdacSEd Maste case 'y':
178866dcdacSEd Maste case 'Y':
179866dcdacSEd Maste m_user_response = true;
180866dcdacSEd Maste io_handler.SetIsDone(true);
181866dcdacSEd Maste return;
182866dcdacSEd Maste case 'n':
183866dcdacSEd Maste case 'N':
184866dcdacSEd Maste m_user_response = false;
185866dcdacSEd Maste io_handler.SetIsDone(true);
186866dcdacSEd Maste return;
187866dcdacSEd Maste default:
188866dcdacSEd Maste break;
189866dcdacSEd Maste }
190866dcdacSEd Maste }
191866dcdacSEd Maste
19214f1b3e8SDimitry Andric if (line == "yes" || line == "YES" || line == "Yes") {
193866dcdacSEd Maste m_user_response = true;
194866dcdacSEd Maste io_handler.SetIsDone(true);
19514f1b3e8SDimitry Andric } else if (line == "no" || line == "NO" || line == "No") {
196866dcdacSEd Maste m_user_response = false;
197866dcdacSEd Maste io_handler.SetIsDone(true);
198866dcdacSEd Maste }
199866dcdacSEd Maste }
200866dcdacSEd Maste
201e3b55780SDimitry Andric std::optional<std::string>
IOHandlerSuggestion(IOHandler & io_handler,llvm::StringRef line)202b60736ecSDimitry Andric IOHandlerDelegate::IOHandlerSuggestion(IOHandler &io_handler,
203b60736ecSDimitry Andric llvm::StringRef line) {
204b60736ecSDimitry Andric return io_handler.GetDebugger()
205b60736ecSDimitry Andric .GetCommandInterpreter()
206b60736ecSDimitry Andric .GetAutoSuggestionForCommand(line);
207b60736ecSDimitry Andric }
208b60736ecSDimitry Andric
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)209ead24645SDimitry Andric void IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler,
210ead24645SDimitry Andric CompletionRequest &request) {
21114f1b3e8SDimitry Andric switch (m_completion) {
212866dcdacSEd Maste case Completion::None:
213866dcdacSEd Maste break;
214866dcdacSEd Maste case Completion::LLDBCommand:
215ead24645SDimitry Andric io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(request);
216ead24645SDimitry Andric break;
217ead24645SDimitry Andric case Completion::Expression:
2187fa27ce4SDimitry Andric lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
21914f1b3e8SDimitry Andric io_handler.GetDebugger().GetCommandInterpreter(),
2207fa27ce4SDimitry Andric lldb::eVariablePathCompletion, request, nullptr);
221ead24645SDimitry Andric break;
222866dcdacSEd Maste }
223866dcdacSEd Maste }
224866dcdacSEd Maste
IOHandlerEditline(Debugger & debugger,IOHandler::Type type,const char * editline_name,llvm::StringRef prompt,llvm::StringRef continuation_prompt,bool multi_line,bool color,uint32_t line_number_start,IOHandlerDelegate & delegate)22514f1b3e8SDimitry Andric IOHandlerEditline::IOHandlerEditline(
22614f1b3e8SDimitry Andric Debugger &debugger, IOHandler::Type type,
227866dcdacSEd Maste const char *editline_name, // Used for saving history files
22814f1b3e8SDimitry Andric llvm::StringRef prompt, llvm::StringRef continuation_prompt,
229b1c73532SDimitry Andric bool multi_line, bool color, uint32_t line_number_start,
230e3b55780SDimitry Andric IOHandlerDelegate &delegate)
23114f1b3e8SDimitry Andric : IOHandlerEditline(debugger, type,
232ead24645SDimitry Andric FileSP(), // Inherit input from top input reader
233866dcdacSEd Maste StreamFileSP(), // Inherit output from top input reader
234866dcdacSEd Maste StreamFileSP(), // Inherit error from top input reader
235866dcdacSEd Maste 0, // Flags
236866dcdacSEd Maste editline_name, // Used for saving history files
237b1c73532SDimitry Andric prompt, continuation_prompt, multi_line, color,
238e3b55780SDimitry Andric line_number_start, delegate) {}
239866dcdacSEd Maste
IOHandlerEditline(Debugger & debugger,IOHandler::Type type,const lldb::FileSP & input_sp,const lldb::StreamFileSP & output_sp,const lldb::StreamFileSP & error_sp,uint32_t flags,const char * editline_name,llvm::StringRef prompt,llvm::StringRef continuation_prompt,bool multi_line,bool color,uint32_t line_number_start,IOHandlerDelegate & delegate)24014f1b3e8SDimitry Andric IOHandlerEditline::IOHandlerEditline(
241ead24645SDimitry Andric Debugger &debugger, IOHandler::Type type, const lldb::FileSP &input_sp,
242ead24645SDimitry Andric const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp,
243ead24645SDimitry Andric uint32_t flags,
244866dcdacSEd Maste const char *editline_name, // Used for saving history files
24514f1b3e8SDimitry Andric llvm::StringRef prompt, llvm::StringRef continuation_prompt,
246b1c73532SDimitry Andric bool multi_line, bool color, uint32_t line_number_start,
247e3b55780SDimitry Andric IOHandlerDelegate &delegate)
248e3b55780SDimitry Andric : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags),
249706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
2505f29bb8aSDimitry Andric m_editline_up(),
251205afe67SEd Maste #endif
25214f1b3e8SDimitry Andric m_delegate(delegate), m_prompt(), m_continuation_prompt(),
25314f1b3e8SDimitry Andric m_current_lines_ptr(nullptr), m_base_line_number(line_number_start),
254b1c73532SDimitry Andric m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line), m_color(color),
255b1c73532SDimitry Andric m_interrupt_exits(true) {
256866dcdacSEd Maste SetPrompt(prompt);
257866dcdacSEd Maste
258706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
259866dcdacSEd Maste bool use_editline = false;
260866dcdacSEd Maste
261ead24645SDimitry Andric use_editline = GetInputFILE() && GetOutputFILE() && GetErrorFILE() &&
262ead24645SDimitry Andric m_input_sp && m_input_sp->GetIsRealTerminal();
263866dcdacSEd Maste
26414f1b3e8SDimitry Andric if (use_editline) {
265b1c73532SDimitry Andric m_editline_up = std::make_unique<Editline>(editline_name, GetInputFILE(),
266b1c73532SDimitry Andric GetOutputFILE(), GetErrorFILE(),
267b1c73532SDimitry Andric GetOutputMutex());
268344a3780SDimitry Andric m_editline_up->SetIsInputCompleteCallback(
269344a3780SDimitry Andric [this](Editline *editline, StringList &lines) {
270344a3780SDimitry Andric return this->IsInputCompleteCallback(editline, lines);
271344a3780SDimitry Andric });
272344a3780SDimitry Andric
273344a3780SDimitry Andric m_editline_up->SetAutoCompleteCallback([this](CompletionRequest &request) {
274344a3780SDimitry Andric this->AutoCompleteCallback(request);
275344a3780SDimitry Andric });
276344a3780SDimitry Andric
277145449b1SDimitry Andric if (debugger.GetUseAutosuggestion()) {
278344a3780SDimitry Andric m_editline_up->SetSuggestionCallback([this](llvm::StringRef line) {
279344a3780SDimitry Andric return this->SuggestionCallback(line);
280344a3780SDimitry Andric });
281b1c73532SDimitry Andric if (m_color) {
282145449b1SDimitry Andric m_editline_up->SetSuggestionAnsiPrefix(ansi::FormatAnsiTerminalCodes(
283145449b1SDimitry Andric debugger.GetAutosuggestionAnsiPrefix()));
284145449b1SDimitry Andric m_editline_up->SetSuggestionAnsiSuffix(ansi::FormatAnsiTerminalCodes(
285145449b1SDimitry Andric debugger.GetAutosuggestionAnsiSuffix()));
286344a3780SDimitry Andric }
287b1c73532SDimitry Andric }
288205afe67SEd Maste // See if the delegate supports fixing indentation
289205afe67SEd Maste const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
29014f1b3e8SDimitry Andric if (indent_chars) {
29114f1b3e8SDimitry Andric // The delegate does support indentation, hook it up so when any
292f73363f1SDimitry Andric // indentation character is typed, the delegate gets a chance to fix it
293344a3780SDimitry Andric FixIndentationCallbackType f = [this](Editline *editline,
294344a3780SDimitry Andric const StringList &lines,
295344a3780SDimitry Andric int cursor_position) {
296344a3780SDimitry Andric return this->FixIndentationCallback(editline, lines, cursor_position);
297344a3780SDimitry Andric };
298344a3780SDimitry Andric m_editline_up->SetFixIndentationCallback(std::move(f), indent_chars);
299866dcdacSEd Maste }
300205afe67SEd Maste }
301205afe67SEd Maste #endif
302205afe67SEd Maste SetBaseLineNumber(m_base_line_number);
30314f1b3e8SDimitry Andric SetPrompt(prompt);
304205afe67SEd Maste SetContinuationPrompt(continuation_prompt);
305866dcdacSEd Maste }
306866dcdacSEd Maste
~IOHandlerEditline()30714f1b3e8SDimitry Andric IOHandlerEditline::~IOHandlerEditline() {
308706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
3095f29bb8aSDimitry Andric m_editline_up.reset();
310205afe67SEd Maste #endif
311205afe67SEd Maste }
312205afe67SEd Maste
Activate()31314f1b3e8SDimitry Andric void IOHandlerEditline::Activate() {
314205afe67SEd Maste IOHandler::Activate();
3155f29bb8aSDimitry Andric m_delegate.IOHandlerActivated(*this, GetIsInteractive());
316205afe67SEd Maste }
317205afe67SEd Maste
Deactivate()31814f1b3e8SDimitry Andric void IOHandlerEditline::Deactivate() {
319205afe67SEd Maste IOHandler::Deactivate();
320205afe67SEd Maste m_delegate.IOHandlerDeactivated(*this);
321866dcdacSEd Maste }
322866dcdacSEd Maste
TerminalSizeChanged()323cfca06d7SDimitry Andric void IOHandlerEditline::TerminalSizeChanged() {
324cfca06d7SDimitry Andric #if LLDB_ENABLE_LIBEDIT
325cfca06d7SDimitry Andric if (m_editline_up)
326cfca06d7SDimitry Andric m_editline_up->TerminalSizeChanged();
327cfca06d7SDimitry Andric #endif
328cfca06d7SDimitry Andric }
329cfca06d7SDimitry Andric
330ead24645SDimitry Andric // Split out a line from the buffer, if there is a full one to get.
SplitLine(std::string & line_buffer)331e3b55780SDimitry Andric static std::optional<std::string> SplitLine(std::string &line_buffer) {
332ead24645SDimitry Andric size_t pos = line_buffer.find('\n');
333ead24645SDimitry Andric if (pos == std::string::npos)
334e3b55780SDimitry Andric return std::nullopt;
335cfca06d7SDimitry Andric std::string line =
336cfca06d7SDimitry Andric std::string(StringRef(line_buffer.c_str(), pos).rtrim("\n\r"));
337ead24645SDimitry Andric line_buffer = line_buffer.substr(pos + 1);
338ead24645SDimitry Andric return line;
339ead24645SDimitry Andric }
340ead24645SDimitry Andric
341ead24645SDimitry Andric // If the final line of the file ends without a end-of-line, return
342ead24645SDimitry Andric // it as a line anyway.
SplitLineEOF(std::string & line_buffer)343e3b55780SDimitry Andric static std::optional<std::string> SplitLineEOF(std::string &line_buffer) {
344cfca06d7SDimitry Andric if (llvm::all_of(line_buffer, llvm::isSpace))
345e3b55780SDimitry Andric return std::nullopt;
346ead24645SDimitry Andric std::string line = std::move(line_buffer);
347ead24645SDimitry Andric line_buffer.clear();
348ead24645SDimitry Andric return line;
349ead24645SDimitry Andric }
350ead24645SDimitry Andric
GetLine(std::string & line,bool & interrupted)35114f1b3e8SDimitry Andric bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
352706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
3535f29bb8aSDimitry Andric if (m_editline_up) {
354e3b55780SDimitry Andric return m_editline_up->GetLine(line, interrupted);
355ead24645SDimitry Andric }
356205afe67SEd Maste #endif
357ead24645SDimitry Andric
358866dcdacSEd Maste line.clear();
359866dcdacSEd Maste
36014f1b3e8SDimitry Andric if (GetIsInteractive()) {
361f3fbd1c0SDimitry Andric const char *prompt = nullptr;
362205afe67SEd Maste
363205afe67SEd Maste if (m_multi_line && m_curr_line_idx > 0)
364205afe67SEd Maste prompt = GetContinuationPrompt();
365205afe67SEd Maste
366f3fbd1c0SDimitry Andric if (prompt == nullptr)
367205afe67SEd Maste prompt = GetPrompt();
368205afe67SEd Maste
36914f1b3e8SDimitry Andric if (prompt && prompt[0]) {
370ead24645SDimitry Andric if (m_output_sp) {
371ead24645SDimitry Andric m_output_sp->Printf("%s", prompt);
372ead24645SDimitry Andric m_output_sp->Flush();
373866dcdacSEd Maste }
374866dcdacSEd Maste }
375866dcdacSEd Maste }
376ead24645SDimitry Andric
377e3b55780SDimitry Andric std::optional<std::string> got_line = SplitLine(m_line_buffer);
378ead24645SDimitry Andric
379ead24645SDimitry Andric if (!got_line && !m_input_sp) {
380ead24645SDimitry Andric // No more input file, we are done...
381ead24645SDimitry Andric SetIsDone(true);
382ead24645SDimitry Andric return false;
383ead24645SDimitry Andric }
384ead24645SDimitry Andric
385ead24645SDimitry Andric FILE *in = GetInputFILE();
386866dcdacSEd Maste char buffer[256];
387ead24645SDimitry Andric
388ead24645SDimitry Andric if (!got_line && !in && m_input_sp) {
389ead24645SDimitry Andric // there is no FILE*, fall back on just reading bytes from the stream.
390ead24645SDimitry Andric while (!got_line) {
391ead24645SDimitry Andric size_t bytes_read = sizeof(buffer);
392ead24645SDimitry Andric Status error = m_input_sp->Read((void *)buffer, bytes_read);
393ead24645SDimitry Andric if (error.Success() && !bytes_read) {
394ead24645SDimitry Andric got_line = SplitLineEOF(m_line_buffer);
395ead24645SDimitry Andric break;
396ead24645SDimitry Andric }
397ead24645SDimitry Andric if (error.Fail())
398ead24645SDimitry Andric break;
399ead24645SDimitry Andric m_line_buffer += StringRef(buffer, bytes_read);
400ead24645SDimitry Andric got_line = SplitLine(m_line_buffer);
401ead24645SDimitry Andric }
402ead24645SDimitry Andric }
403ead24645SDimitry Andric
404ead24645SDimitry Andric if (!got_line && in) {
405ead24645SDimitry Andric while (!got_line) {
406ead24645SDimitry Andric char *r = fgets(buffer, sizeof(buffer), in);
4075f29bb8aSDimitry Andric #ifdef _WIN32
4085f29bb8aSDimitry Andric // ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED
4095f29bb8aSDimitry Andric // according to the docs on MSDN. However, this has evidently been a
4105f29bb8aSDimitry Andric // known bug since Windows 8. Therefore, we can't detect if a signal
4115f29bb8aSDimitry Andric // interrupted in the fgets. So pressing ctrl-c causes the repl to end
4125f29bb8aSDimitry Andric // and the process to exit. A temporary workaround is just to attempt to
4135f29bb8aSDimitry Andric // fgets twice until this bug is fixed.
414ead24645SDimitry Andric if (r == nullptr)
415ead24645SDimitry Andric r = fgets(buffer, sizeof(buffer), in);
416ead24645SDimitry Andric // this is the equivalent of EINTR for Windows
417ead24645SDimitry Andric if (r == nullptr && GetLastError() == ERROR_OPERATION_ABORTED)
418ead24645SDimitry Andric continue;
4195f29bb8aSDimitry Andric #endif
420ead24645SDimitry Andric if (r == nullptr) {
421ead24645SDimitry Andric if (ferror(in) && errno == EINTR)
422ead24645SDimitry Andric continue;
4230cac4ca3SEd Maste if (feof(in))
424ead24645SDimitry Andric got_line = SplitLineEOF(m_line_buffer);
425866dcdacSEd Maste break;
426866dcdacSEd Maste }
427ead24645SDimitry Andric m_line_buffer += buffer;
428ead24645SDimitry Andric got_line = SplitLine(m_line_buffer);
429866dcdacSEd Maste }
430ead24645SDimitry Andric }
431ead24645SDimitry Andric
432ead24645SDimitry Andric if (got_line) {
433145449b1SDimitry Andric line = *got_line;
434866dcdacSEd Maste }
435ead24645SDimitry Andric
436ead24645SDimitry Andric return (bool)got_line;
437866dcdacSEd Maste }
438866dcdacSEd Maste
439706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
IsInputCompleteCallback(Editline * editline,StringList & lines)44014f1b3e8SDimitry Andric bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline,
441344a3780SDimitry Andric StringList &lines) {
442344a3780SDimitry Andric return m_delegate.IOHandlerIsInputComplete(*this, lines);
443205afe67SEd Maste }
444205afe67SEd Maste
FixIndentationCallback(Editline * editline,const StringList & lines,int cursor_position)44514f1b3e8SDimitry Andric int IOHandlerEditline::FixIndentationCallback(Editline *editline,
446205afe67SEd Maste const StringList &lines,
447344a3780SDimitry Andric int cursor_position) {
448344a3780SDimitry Andric return m_delegate.IOHandlerFixIndentation(*this, lines, cursor_position);
449866dcdacSEd Maste }
450866dcdacSEd Maste
451e3b55780SDimitry Andric std::optional<std::string>
SuggestionCallback(llvm::StringRef line)452344a3780SDimitry Andric IOHandlerEditline::SuggestionCallback(llvm::StringRef line) {
453344a3780SDimitry Andric return m_delegate.IOHandlerSuggestion(*this, line);
454b60736ecSDimitry Andric }
455b60736ecSDimitry Andric
AutoCompleteCallback(CompletionRequest & request)456344a3780SDimitry Andric void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request) {
457344a3780SDimitry Andric m_delegate.IOHandlerComplete(*this, request);
458866dcdacSEd Maste }
459205afe67SEd Maste #endif
460866dcdacSEd Maste
GetPrompt()46114f1b3e8SDimitry Andric const char *IOHandlerEditline::GetPrompt() {
462706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
4635f29bb8aSDimitry Andric if (m_editline_up) {
4645f29bb8aSDimitry Andric return m_editline_up->GetPrompt();
46514f1b3e8SDimitry Andric } else {
466205afe67SEd Maste #endif
467205afe67SEd Maste if (m_prompt.empty())
468f3fbd1c0SDimitry Andric return nullptr;
469706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
470205afe67SEd Maste }
471205afe67SEd Maste #endif
472866dcdacSEd Maste return m_prompt.c_str();
473866dcdacSEd Maste }
474866dcdacSEd Maste
SetPrompt(llvm::StringRef prompt)47514f1b3e8SDimitry Andric bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
476cfca06d7SDimitry Andric m_prompt = std::string(prompt);
47714f1b3e8SDimitry Andric
478706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
479b1c73532SDimitry Andric if (m_editline_up) {
4805f29bb8aSDimitry Andric m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
481b1c73532SDimitry Andric if (m_color) {
482b1c73532SDimitry Andric m_editline_up->SetPromptAnsiPrefix(
483b1c73532SDimitry Andric ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiPrefix()));
484b1c73532SDimitry Andric m_editline_up->SetPromptAnsiSuffix(
485b1c73532SDimitry Andric ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiSuffix()));
486b1c73532SDimitry Andric }
487b1c73532SDimitry Andric }
488205afe67SEd Maste #endif
489866dcdacSEd Maste return true;
490866dcdacSEd Maste }
491866dcdacSEd Maste
GetContinuationPrompt()49214f1b3e8SDimitry Andric const char *IOHandlerEditline::GetContinuationPrompt() {
49314f1b3e8SDimitry Andric return (m_continuation_prompt.empty() ? nullptr
49414f1b3e8SDimitry Andric : m_continuation_prompt.c_str());
495205afe67SEd Maste }
496205afe67SEd Maste
SetContinuationPrompt(llvm::StringRef prompt)49714f1b3e8SDimitry Andric void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) {
498cfca06d7SDimitry Andric m_continuation_prompt = std::string(prompt);
499205afe67SEd Maste
500706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
5015f29bb8aSDimitry Andric if (m_editline_up)
5025f29bb8aSDimitry Andric m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty()
50314f1b3e8SDimitry Andric ? nullptr
50414f1b3e8SDimitry Andric : m_continuation_prompt.c_str());
505205afe67SEd Maste #endif
506205afe67SEd Maste }
507205afe67SEd Maste
SetBaseLineNumber(uint32_t line)50814f1b3e8SDimitry Andric void IOHandlerEditline::SetBaseLineNumber(uint32_t line) {
5090cac4ca3SEd Maste m_base_line_number = line;
5100cac4ca3SEd Maste }
511205afe67SEd Maste
GetCurrentLineIndex() const51214f1b3e8SDimitry Andric uint32_t IOHandlerEditline::GetCurrentLineIndex() const {
513706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
5145f29bb8aSDimitry Andric if (m_editline_up)
5155f29bb8aSDimitry Andric return m_editline_up->GetCurrentLine();
516205afe67SEd Maste #endif
517205afe67SEd Maste return m_curr_line_idx;
518205afe67SEd Maste }
519205afe67SEd Maste
GetCurrentLines() const520b1c73532SDimitry Andric StringList IOHandlerEditline::GetCurrentLines() const {
521b1c73532SDimitry Andric #if LLDB_ENABLE_LIBEDIT
522b1c73532SDimitry Andric if (m_editline_up)
523b1c73532SDimitry Andric return m_editline_up->GetInputAsStringList();
524b1c73532SDimitry Andric #endif
525b1c73532SDimitry Andric // When libedit is not used, the current lines can be gotten from
526b1c73532SDimitry Andric // `m_current_lines_ptr`, which is updated whenever a new line is processed.
527b1c73532SDimitry Andric // This doesn't happen when libedit is used, in which case
528b1c73532SDimitry Andric // `m_current_lines_ptr` is only updated when the full input is terminated.
529b1c73532SDimitry Andric
530b1c73532SDimitry Andric if (m_current_lines_ptr)
531b1c73532SDimitry Andric return *m_current_lines_ptr;
532b1c73532SDimitry Andric return StringList();
533b1c73532SDimitry Andric }
534b1c73532SDimitry Andric
GetLines(StringList & lines,bool & interrupted)53514f1b3e8SDimitry Andric bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) {
536205afe67SEd Maste m_current_lines_ptr = &lines;
537205afe67SEd Maste
538866dcdacSEd Maste bool success = false;
539706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
5405f29bb8aSDimitry Andric if (m_editline_up) {
5415f29bb8aSDimitry Andric return m_editline_up->GetLines(m_base_line_number, lines, interrupted);
54214f1b3e8SDimitry Andric } else {
543205afe67SEd Maste #endif
544205afe67SEd Maste bool done = false;
545b76161e4SDimitry Andric Status error;
546866dcdacSEd Maste
54714f1b3e8SDimitry Andric while (!done) {
5480cac4ca3SEd Maste // Show line numbers if we are asked to
549866dcdacSEd Maste std::string line;
55014f1b3e8SDimitry Andric if (m_base_line_number > 0 && GetIsInteractive()) {
551ead24645SDimitry Andric if (m_output_sp) {
552ead24645SDimitry Andric m_output_sp->Printf("%u%s",
553ead24645SDimitry Andric m_base_line_number + (uint32_t)lines.GetSize(),
55414f1b3e8SDimitry Andric GetPrompt() == nullptr ? " " : "");
5550cac4ca3SEd Maste }
556ead24645SDimitry Andric }
5570cac4ca3SEd Maste
558205afe67SEd Maste m_curr_line_idx = lines.GetSize();
559205afe67SEd Maste
5600cac4ca3SEd Maste bool interrupted = false;
56114f1b3e8SDimitry Andric if (GetLine(line, interrupted) && !interrupted) {
562866dcdacSEd Maste lines.AppendString(line);
563205afe67SEd Maste done = m_delegate.IOHandlerIsInputComplete(*this, lines);
56414f1b3e8SDimitry Andric } else {
565205afe67SEd Maste done = true;
566866dcdacSEd Maste }
567866dcdacSEd Maste }
568866dcdacSEd Maste success = lines.GetSize() > 0;
569706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
570866dcdacSEd Maste }
571205afe67SEd Maste #endif
572866dcdacSEd Maste return success;
573866dcdacSEd Maste }
574866dcdacSEd Maste
575f73363f1SDimitry Andric // Each IOHandler gets to run until it is done. It should read data from the
576f73363f1SDimitry Andric // "in" and place output into "out" and "err and return when done.
Run()57714f1b3e8SDimitry Andric void IOHandlerEditline::Run() {
578866dcdacSEd Maste std::string line;
57914f1b3e8SDimitry Andric while (IsActive()) {
5800cac4ca3SEd Maste bool interrupted = false;
58114f1b3e8SDimitry Andric if (m_multi_line) {
582866dcdacSEd Maste StringList lines;
58314f1b3e8SDimitry Andric if (GetLines(lines, interrupted)) {
58414f1b3e8SDimitry Andric if (interrupted) {
585205afe67SEd Maste m_done = m_interrupt_exits;
586205afe67SEd Maste m_delegate.IOHandlerInputInterrupted(*this, line);
587205afe67SEd Maste
58814f1b3e8SDimitry Andric } else {
589866dcdacSEd Maste line = lines.CopyList();
590866dcdacSEd Maste m_delegate.IOHandlerInputComplete(*this, line);
591866dcdacSEd Maste }
59214f1b3e8SDimitry Andric } else {
593866dcdacSEd Maste m_done = true;
594866dcdacSEd Maste }
59514f1b3e8SDimitry Andric } else {
59614f1b3e8SDimitry Andric if (GetLine(line, interrupted)) {
597205afe67SEd Maste if (interrupted)
598205afe67SEd Maste m_delegate.IOHandlerInputInterrupted(*this, line);
599205afe67SEd Maste else
600866dcdacSEd Maste m_delegate.IOHandlerInputComplete(*this, line);
60114f1b3e8SDimitry Andric } else {
602866dcdacSEd Maste m_done = true;
603866dcdacSEd Maste }
604866dcdacSEd Maste }
605866dcdacSEd Maste }
606866dcdacSEd Maste }
607866dcdacSEd Maste
Cancel()60814f1b3e8SDimitry Andric void IOHandlerEditline::Cancel() {
609706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
6105f29bb8aSDimitry Andric if (m_editline_up)
6115f29bb8aSDimitry Andric m_editline_up->Cancel();
612205afe67SEd Maste #endif
61303b99097SEd Maste }
61403b99097SEd Maste
Interrupt()61514f1b3e8SDimitry Andric bool IOHandlerEditline::Interrupt() {
6160cac4ca3SEd Maste // Let the delgate handle it first
6170cac4ca3SEd Maste if (m_delegate.IOHandlerInterrupt(*this))
6180cac4ca3SEd Maste return true;
6190cac4ca3SEd Maste
620706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
6215f29bb8aSDimitry Andric if (m_editline_up)
6225f29bb8aSDimitry Andric return m_editline_up->Interrupt();
623205afe67SEd Maste #endif
6240cac4ca3SEd Maste return false;
625866dcdacSEd Maste }
626866dcdacSEd Maste
GotEOF()62714f1b3e8SDimitry Andric void IOHandlerEditline::GotEOF() {
628706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
6295f29bb8aSDimitry Andric if (m_editline_up)
6305f29bb8aSDimitry Andric m_editline_up->Interrupt();
631205afe67SEd Maste #endif
632866dcdacSEd Maste }
633866dcdacSEd Maste
PrintAsync(const char * s,size_t len,bool is_stdout)634145449b1SDimitry Andric void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) {
635706b4fc4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
636145449b1SDimitry Andric if (m_editline_up) {
637145449b1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
638145449b1SDimitry Andric lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
639145449b1SDimitry Andric m_editline_up->PrintAsync(stream.get(), s, len);
640145449b1SDimitry Andric } else
6415e95aa85SEd Maste #endif
642f3fbd1c0SDimitry Andric {
643ead24645SDimitry Andric #ifdef _WIN32
64414f1b3e8SDimitry Andric const char *prompt = GetPrompt();
64514f1b3e8SDimitry Andric if (prompt) {
646f3fbd1c0SDimitry Andric // Back up over previous prompt using Windows API
647f3fbd1c0SDimitry Andric CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
648f3fbd1c0SDimitry Andric HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
649f3fbd1c0SDimitry Andric GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info);
650f3fbd1c0SDimitry Andric COORD coord = screen_buffer_info.dwCursorPosition;
651f3fbd1c0SDimitry Andric coord.X -= strlen(prompt);
652f3fbd1c0SDimitry Andric if (coord.X < 0)
653f3fbd1c0SDimitry Andric coord.X = 0;
654f3fbd1c0SDimitry Andric SetConsoleCursorPosition(console_handle, coord);
655f3fbd1c0SDimitry Andric }
656f3fbd1c0SDimitry Andric #endif
657145449b1SDimitry Andric IOHandler::PrintAsync(s, len, is_stdout);
658ead24645SDimitry Andric #ifdef _WIN32
659f3fbd1c0SDimitry Andric if (prompt)
660145449b1SDimitry Andric IOHandler::PrintAsync(prompt, strlen(prompt), is_stdout);
66114f1b3e8SDimitry Andric #endif
662f3fbd1c0SDimitry Andric }
6635e95aa85SEd Maste }
664