1dda28197Spatrick //===-- IOHandler.cpp -----------------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9061da546Spatrick #include "lldb/Core/IOHandler.h"
10061da546Spatrick
11061da546Spatrick #if defined(__APPLE__)
12061da546Spatrick #include <deque>
13061da546Spatrick #endif
14061da546Spatrick #include <string>
15061da546Spatrick
16061da546Spatrick #include "lldb/Core/Debugger.h"
17061da546Spatrick #include "lldb/Core/StreamFile.h"
18061da546Spatrick #include "lldb/Host/Config.h"
19061da546Spatrick #include "lldb/Host/File.h"
20*f6aab3d8Srobert #include "lldb/Utility/AnsiTerminal.h"
21061da546Spatrick #include "lldb/Utility/Predicate.h"
22061da546Spatrick #include "lldb/Utility/Status.h"
23061da546Spatrick #include "lldb/Utility/StreamString.h"
24061da546Spatrick #include "lldb/Utility/StringList.h"
25061da546Spatrick #include "lldb/lldb-forward.h"
26061da546Spatrick
27061da546Spatrick #if LLDB_ENABLE_LIBEDIT
28061da546Spatrick #include "lldb/Host/Editline.h"
29061da546Spatrick #endif
30061da546Spatrick #include "lldb/Interpreter/CommandCompletions.h"
31061da546Spatrick #include "lldb/Interpreter/CommandInterpreter.h"
32061da546Spatrick #include "llvm/ADT/StringRef.h"
33061da546Spatrick
34061da546Spatrick #ifdef _WIN32
35061da546Spatrick #include "lldb/Host/windows/windows.h"
36061da546Spatrick #endif
37061da546Spatrick
38061da546Spatrick #include <memory>
39061da546Spatrick #include <mutex>
40*f6aab3d8Srobert #include <optional>
41061da546Spatrick
42be691f3bSpatrick #include <cassert>
43be691f3bSpatrick #include <cctype>
44be691f3bSpatrick #include <cerrno>
45be691f3bSpatrick #include <clocale>
46be691f3bSpatrick #include <cstdint>
47be691f3bSpatrick #include <cstdio>
48be691f3bSpatrick #include <cstring>
49061da546Spatrick #include <type_traits>
50061da546Spatrick
51061da546Spatrick using namespace lldb;
52061da546Spatrick using namespace lldb_private;
53061da546Spatrick using llvm::StringRef;
54061da546Spatrick
IOHandler(Debugger & debugger,IOHandler::Type type)55061da546Spatrick IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
56061da546Spatrick : IOHandler(debugger, type,
57061da546Spatrick FileSP(), // Adopt STDIN from top input reader
58061da546Spatrick StreamFileSP(), // Adopt STDOUT from top input reader
59061da546Spatrick StreamFileSP(), // Adopt STDERR from top input reader
60*f6aab3d8Srobert 0 // Flags
61*f6aab3d8Srobert
62061da546Spatrick ) {}
63061da546Spatrick
IOHandler(Debugger & debugger,IOHandler::Type type,const lldb::FileSP & input_sp,const lldb::StreamFileSP & output_sp,const lldb::StreamFileSP & error_sp,uint32_t flags)64061da546Spatrick IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type,
65061da546Spatrick const lldb::FileSP &input_sp,
66061da546Spatrick const lldb::StreamFileSP &output_sp,
67*f6aab3d8Srobert const lldb::StreamFileSP &error_sp, uint32_t flags)
68061da546Spatrick : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
69*f6aab3d8Srobert m_error_sp(error_sp), m_popped(false), m_flags(flags), m_type(type),
70*f6aab3d8Srobert m_user_data(nullptr), m_done(false), m_active(false) {
71061da546Spatrick // If any files are not specified, then adopt them from the top input reader.
72061da546Spatrick if (!m_input_sp || !m_output_sp || !m_error_sp)
73061da546Spatrick debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp,
74061da546Spatrick m_error_sp);
75061da546Spatrick }
76061da546Spatrick
77061da546Spatrick IOHandler::~IOHandler() = default;
78061da546Spatrick
GetInputFD()79061da546Spatrick int IOHandler::GetInputFD() {
80061da546Spatrick return (m_input_sp ? m_input_sp->GetDescriptor() : -1);
81061da546Spatrick }
82061da546Spatrick
GetOutputFD()83061da546Spatrick int IOHandler::GetOutputFD() {
84061da546Spatrick return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1);
85061da546Spatrick }
86061da546Spatrick
GetErrorFD()87061da546Spatrick int IOHandler::GetErrorFD() {
88061da546Spatrick return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1);
89061da546Spatrick }
90061da546Spatrick
GetInputFILE()91061da546Spatrick FILE *IOHandler::GetInputFILE() {
92061da546Spatrick return (m_input_sp ? m_input_sp->GetStream() : nullptr);
93061da546Spatrick }
94061da546Spatrick
GetOutputFILE()95061da546Spatrick FILE *IOHandler::GetOutputFILE() {
96061da546Spatrick return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr);
97061da546Spatrick }
98061da546Spatrick
GetErrorFILE()99061da546Spatrick FILE *IOHandler::GetErrorFILE() {
100061da546Spatrick return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr);
101061da546Spatrick }
102061da546Spatrick
GetInputFileSP()103be691f3bSpatrick FileSP IOHandler::GetInputFileSP() { return m_input_sp; }
104061da546Spatrick
GetOutputStreamFileSP()105be691f3bSpatrick StreamFileSP IOHandler::GetOutputStreamFileSP() { return m_output_sp; }
106061da546Spatrick
GetErrorStreamFileSP()107be691f3bSpatrick StreamFileSP IOHandler::GetErrorStreamFileSP() { return m_error_sp; }
108061da546Spatrick
GetIsInteractive()109061da546Spatrick bool IOHandler::GetIsInteractive() {
110061da546Spatrick return GetInputFileSP() ? GetInputFileSP()->GetIsInteractive() : false;
111061da546Spatrick }
112061da546Spatrick
GetIsRealTerminal()113061da546Spatrick bool IOHandler::GetIsRealTerminal() {
114061da546Spatrick return GetInputFileSP() ? GetInputFileSP()->GetIsRealTerminal() : false;
115061da546Spatrick }
116061da546Spatrick
SetPopped(bool b)117061da546Spatrick void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
118061da546Spatrick
WaitForPop()119061da546Spatrick void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
120061da546Spatrick
PrintAsync(const char * s,size_t len,bool is_stdout)121*f6aab3d8Srobert void IOHandler::PrintAsync(const char *s, size_t len, bool is_stdout) {
122*f6aab3d8Srobert std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
123*f6aab3d8Srobert lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
124dda28197Spatrick stream->Write(s, len);
125*f6aab3d8Srobert stream->Flush();
126061da546Spatrick }
127*f6aab3d8Srobert
PrintAsync(const char * s,size_t len,bool is_stdout)128*f6aab3d8Srobert bool IOHandlerStack::PrintAsync(const char *s, size_t len, bool is_stdout) {
129*f6aab3d8Srobert std::lock_guard<std::recursive_mutex> guard(m_mutex);
130*f6aab3d8Srobert if (!m_top)
131*f6aab3d8Srobert return false;
132*f6aab3d8Srobert m_top->PrintAsync(s, len, is_stdout);
133*f6aab3d8Srobert return true;
134061da546Spatrick }
135061da546Spatrick
IOHandlerConfirm(Debugger & debugger,llvm::StringRef prompt,bool default_response)136061da546Spatrick IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
137061da546Spatrick bool default_response)
138061da546Spatrick : IOHandlerEditline(
139061da546Spatrick debugger, IOHandler::Type::Confirm,
140061da546Spatrick nullptr, // nullptr editline_name means no history loaded/saved
141061da546Spatrick llvm::StringRef(), // No prompt
142061da546Spatrick llvm::StringRef(), // No continuation prompt
143061da546Spatrick false, // Multi-line
144061da546Spatrick false, // Don't colorize the prompt (i.e. the confirm message.)
145*f6aab3d8Srobert 0, *this),
146061da546Spatrick m_default_response(default_response), m_user_response(default_response) {
147061da546Spatrick StreamString prompt_stream;
148061da546Spatrick prompt_stream.PutCString(prompt);
149061da546Spatrick if (m_default_response)
150061da546Spatrick prompt_stream.Printf(": [Y/n] ");
151061da546Spatrick else
152061da546Spatrick prompt_stream.Printf(": [y/N] ");
153061da546Spatrick
154061da546Spatrick SetPrompt(prompt_stream.GetString());
155061da546Spatrick }
156061da546Spatrick
157061da546Spatrick IOHandlerConfirm::~IOHandlerConfirm() = default;
158061da546Spatrick
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)159061da546Spatrick void IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler,
160061da546Spatrick CompletionRequest &request) {
161061da546Spatrick if (request.GetRawCursorPos() != 0)
162061da546Spatrick return;
163061da546Spatrick request.AddCompletion(m_default_response ? "y" : "n");
164061da546Spatrick }
165061da546Spatrick
IOHandlerInputComplete(IOHandler & io_handler,std::string & line)166061da546Spatrick void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler,
167061da546Spatrick std::string &line) {
168061da546Spatrick if (line.empty()) {
169061da546Spatrick // User just hit enter, set the response to the default
170061da546Spatrick m_user_response = m_default_response;
171061da546Spatrick io_handler.SetIsDone(true);
172061da546Spatrick return;
173061da546Spatrick }
174061da546Spatrick
175061da546Spatrick if (line.size() == 1) {
176061da546Spatrick switch (line[0]) {
177061da546Spatrick case 'y':
178061da546Spatrick case 'Y':
179061da546Spatrick m_user_response = true;
180061da546Spatrick io_handler.SetIsDone(true);
181061da546Spatrick return;
182061da546Spatrick case 'n':
183061da546Spatrick case 'N':
184061da546Spatrick m_user_response = false;
185061da546Spatrick io_handler.SetIsDone(true);
186061da546Spatrick return;
187061da546Spatrick default:
188061da546Spatrick break;
189061da546Spatrick }
190061da546Spatrick }
191061da546Spatrick
192061da546Spatrick if (line == "yes" || line == "YES" || line == "Yes") {
193061da546Spatrick m_user_response = true;
194061da546Spatrick io_handler.SetIsDone(true);
195061da546Spatrick } else if (line == "no" || line == "NO" || line == "No") {
196061da546Spatrick m_user_response = false;
197061da546Spatrick io_handler.SetIsDone(true);
198061da546Spatrick }
199061da546Spatrick }
200061da546Spatrick
201*f6aab3d8Srobert std::optional<std::string>
IOHandlerSuggestion(IOHandler & io_handler,llvm::StringRef line)202be691f3bSpatrick IOHandlerDelegate::IOHandlerSuggestion(IOHandler &io_handler,
203be691f3bSpatrick llvm::StringRef line) {
204be691f3bSpatrick return io_handler.GetDebugger()
205be691f3bSpatrick .GetCommandInterpreter()
206be691f3bSpatrick .GetAutoSuggestionForCommand(line);
207be691f3bSpatrick }
208be691f3bSpatrick
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)209061da546Spatrick void IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler,
210061da546Spatrick CompletionRequest &request) {
211061da546Spatrick switch (m_completion) {
212061da546Spatrick case Completion::None:
213061da546Spatrick break;
214061da546Spatrick case Completion::LLDBCommand:
215061da546Spatrick io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(request);
216061da546Spatrick break;
217061da546Spatrick case Completion::Expression:
218061da546Spatrick CommandCompletions::InvokeCommonCompletionCallbacks(
219061da546Spatrick io_handler.GetDebugger().GetCommandInterpreter(),
220061da546Spatrick CommandCompletions::eVariablePathCompletion, request, nullptr);
221061da546Spatrick break;
222061da546Spatrick }
223061da546Spatrick }
224061da546Spatrick
IOHandlerEditline(Debugger & debugger,IOHandler::Type type,const char * editline_name,llvm::StringRef prompt,llvm::StringRef continuation_prompt,bool multi_line,bool color_prompts,uint32_t line_number_start,IOHandlerDelegate & delegate)225061da546Spatrick IOHandlerEditline::IOHandlerEditline(
226061da546Spatrick Debugger &debugger, IOHandler::Type type,
227061da546Spatrick const char *editline_name, // Used for saving history files
228061da546Spatrick llvm::StringRef prompt, llvm::StringRef continuation_prompt,
229061da546Spatrick bool multi_line, bool color_prompts, uint32_t line_number_start,
230*f6aab3d8Srobert IOHandlerDelegate &delegate)
231061da546Spatrick : IOHandlerEditline(debugger, type,
232061da546Spatrick FileSP(), // Inherit input from top input reader
233061da546Spatrick StreamFileSP(), // Inherit output from top input reader
234061da546Spatrick StreamFileSP(), // Inherit error from top input reader
235061da546Spatrick 0, // Flags
236061da546Spatrick editline_name, // Used for saving history files
237061da546Spatrick prompt, continuation_prompt, multi_line, color_prompts,
238*f6aab3d8Srobert line_number_start, delegate) {}
239061da546Spatrick
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_prompts,uint32_t line_number_start,IOHandlerDelegate & delegate)240061da546Spatrick IOHandlerEditline::IOHandlerEditline(
241061da546Spatrick Debugger &debugger, IOHandler::Type type, const lldb::FileSP &input_sp,
242061da546Spatrick const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp,
243061da546Spatrick uint32_t flags,
244061da546Spatrick const char *editline_name, // Used for saving history files
245061da546Spatrick llvm::StringRef prompt, llvm::StringRef continuation_prompt,
246061da546Spatrick bool multi_line, bool color_prompts, uint32_t line_number_start,
247*f6aab3d8Srobert IOHandlerDelegate &delegate)
248*f6aab3d8Srobert : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags),
249061da546Spatrick #if LLDB_ENABLE_LIBEDIT
250061da546Spatrick m_editline_up(),
251061da546Spatrick #endif
252061da546Spatrick m_delegate(delegate), m_prompt(), m_continuation_prompt(),
253061da546Spatrick m_current_lines_ptr(nullptr), m_base_line_number(line_number_start),
254061da546Spatrick m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line),
255*f6aab3d8Srobert m_color_prompts(color_prompts), m_interrupt_exits(true) {
256061da546Spatrick SetPrompt(prompt);
257061da546Spatrick
258061da546Spatrick #if LLDB_ENABLE_LIBEDIT
259061da546Spatrick bool use_editline = false;
260061da546Spatrick
261061da546Spatrick use_editline = GetInputFILE() && GetOutputFILE() && GetErrorFILE() &&
262061da546Spatrick m_input_sp && m_input_sp->GetIsRealTerminal();
263061da546Spatrick
264061da546Spatrick if (use_editline) {
265*f6aab3d8Srobert m_editline_up = std::make_unique<Editline>(
266*f6aab3d8Srobert editline_name, GetInputFILE(), GetOutputFILE(), GetErrorFILE(),
267*f6aab3d8Srobert GetOutputMutex(), m_color_prompts);
268be691f3bSpatrick m_editline_up->SetIsInputCompleteCallback(
269be691f3bSpatrick [this](Editline *editline, StringList &lines) {
270be691f3bSpatrick return this->IsInputCompleteCallback(editline, lines);
271be691f3bSpatrick });
272be691f3bSpatrick
273be691f3bSpatrick m_editline_up->SetAutoCompleteCallback([this](CompletionRequest &request) {
274be691f3bSpatrick this->AutoCompleteCallback(request);
275be691f3bSpatrick });
276be691f3bSpatrick
277*f6aab3d8Srobert if (debugger.GetUseAutosuggestion()) {
278be691f3bSpatrick m_editline_up->SetSuggestionCallback([this](llvm::StringRef line) {
279be691f3bSpatrick return this->SuggestionCallback(line);
280be691f3bSpatrick });
281*f6aab3d8Srobert m_editline_up->SetSuggestionAnsiPrefix(ansi::FormatAnsiTerminalCodes(
282*f6aab3d8Srobert debugger.GetAutosuggestionAnsiPrefix()));
283*f6aab3d8Srobert m_editline_up->SetSuggestionAnsiSuffix(ansi::FormatAnsiTerminalCodes(
284*f6aab3d8Srobert debugger.GetAutosuggestionAnsiSuffix()));
285be691f3bSpatrick }
286061da546Spatrick // See if the delegate supports fixing indentation
287061da546Spatrick const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
288061da546Spatrick if (indent_chars) {
289061da546Spatrick // The delegate does support indentation, hook it up so when any
290061da546Spatrick // indentation character is typed, the delegate gets a chance to fix it
291be691f3bSpatrick FixIndentationCallbackType f = [this](Editline *editline,
292be691f3bSpatrick const StringList &lines,
293be691f3bSpatrick int cursor_position) {
294be691f3bSpatrick return this->FixIndentationCallback(editline, lines, cursor_position);
295be691f3bSpatrick };
296be691f3bSpatrick m_editline_up->SetFixIndentationCallback(std::move(f), indent_chars);
297061da546Spatrick }
298061da546Spatrick }
299061da546Spatrick #endif
300061da546Spatrick SetBaseLineNumber(m_base_line_number);
301061da546Spatrick SetPrompt(prompt);
302061da546Spatrick SetContinuationPrompt(continuation_prompt);
303061da546Spatrick }
304061da546Spatrick
~IOHandlerEditline()305061da546Spatrick IOHandlerEditline::~IOHandlerEditline() {
306061da546Spatrick #if LLDB_ENABLE_LIBEDIT
307061da546Spatrick m_editline_up.reset();
308061da546Spatrick #endif
309061da546Spatrick }
310061da546Spatrick
Activate()311061da546Spatrick void IOHandlerEditline::Activate() {
312061da546Spatrick IOHandler::Activate();
313061da546Spatrick m_delegate.IOHandlerActivated(*this, GetIsInteractive());
314061da546Spatrick }
315061da546Spatrick
Deactivate()316061da546Spatrick void IOHandlerEditline::Deactivate() {
317061da546Spatrick IOHandler::Deactivate();
318061da546Spatrick m_delegate.IOHandlerDeactivated(*this);
319061da546Spatrick }
320061da546Spatrick
TerminalSizeChanged()321dda28197Spatrick void IOHandlerEditline::TerminalSizeChanged() {
322dda28197Spatrick #if LLDB_ENABLE_LIBEDIT
323dda28197Spatrick if (m_editline_up)
324dda28197Spatrick m_editline_up->TerminalSizeChanged();
325dda28197Spatrick #endif
326dda28197Spatrick }
327dda28197Spatrick
328061da546Spatrick // Split out a line from the buffer, if there is a full one to get.
SplitLine(std::string & line_buffer)329*f6aab3d8Srobert static std::optional<std::string> SplitLine(std::string &line_buffer) {
330061da546Spatrick size_t pos = line_buffer.find('\n');
331061da546Spatrick if (pos == std::string::npos)
332*f6aab3d8Srobert return std::nullopt;
333dda28197Spatrick std::string line =
334dda28197Spatrick std::string(StringRef(line_buffer.c_str(), pos).rtrim("\n\r"));
335061da546Spatrick line_buffer = line_buffer.substr(pos + 1);
336061da546Spatrick return line;
337061da546Spatrick }
338061da546Spatrick
339061da546Spatrick // If the final line of the file ends without a end-of-line, return
340061da546Spatrick // it as a line anyway.
SplitLineEOF(std::string & line_buffer)341*f6aab3d8Srobert static std::optional<std::string> SplitLineEOF(std::string &line_buffer) {
342dda28197Spatrick if (llvm::all_of(line_buffer, llvm::isSpace))
343*f6aab3d8Srobert return std::nullopt;
344061da546Spatrick std::string line = std::move(line_buffer);
345061da546Spatrick line_buffer.clear();
346061da546Spatrick return line;
347061da546Spatrick }
348061da546Spatrick
GetLine(std::string & line,bool & interrupted)349061da546Spatrick bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
350061da546Spatrick #if LLDB_ENABLE_LIBEDIT
351061da546Spatrick if (m_editline_up) {
352*f6aab3d8Srobert return m_editline_up->GetLine(line, interrupted);
353061da546Spatrick }
354061da546Spatrick #endif
355061da546Spatrick
356061da546Spatrick line.clear();
357061da546Spatrick
358061da546Spatrick if (GetIsInteractive()) {
359061da546Spatrick const char *prompt = nullptr;
360061da546Spatrick
361061da546Spatrick if (m_multi_line && m_curr_line_idx > 0)
362061da546Spatrick prompt = GetContinuationPrompt();
363061da546Spatrick
364061da546Spatrick if (prompt == nullptr)
365061da546Spatrick prompt = GetPrompt();
366061da546Spatrick
367061da546Spatrick if (prompt && prompt[0]) {
368061da546Spatrick if (m_output_sp) {
369061da546Spatrick m_output_sp->Printf("%s", prompt);
370061da546Spatrick m_output_sp->Flush();
371061da546Spatrick }
372061da546Spatrick }
373061da546Spatrick }
374061da546Spatrick
375*f6aab3d8Srobert std::optional<std::string> got_line = SplitLine(m_line_buffer);
376061da546Spatrick
377061da546Spatrick if (!got_line && !m_input_sp) {
378061da546Spatrick // No more input file, we are done...
379061da546Spatrick SetIsDone(true);
380061da546Spatrick return false;
381061da546Spatrick }
382061da546Spatrick
383061da546Spatrick FILE *in = GetInputFILE();
384061da546Spatrick char buffer[256];
385061da546Spatrick
386061da546Spatrick if (!got_line && !in && m_input_sp) {
387061da546Spatrick // there is no FILE*, fall back on just reading bytes from the stream.
388061da546Spatrick while (!got_line) {
389061da546Spatrick size_t bytes_read = sizeof(buffer);
390061da546Spatrick Status error = m_input_sp->Read((void *)buffer, bytes_read);
391061da546Spatrick if (error.Success() && !bytes_read) {
392061da546Spatrick got_line = SplitLineEOF(m_line_buffer);
393061da546Spatrick break;
394061da546Spatrick }
395061da546Spatrick if (error.Fail())
396061da546Spatrick break;
397061da546Spatrick m_line_buffer += StringRef(buffer, bytes_read);
398061da546Spatrick got_line = SplitLine(m_line_buffer);
399061da546Spatrick }
400061da546Spatrick }
401061da546Spatrick
402061da546Spatrick if (!got_line && in) {
403061da546Spatrick while (!got_line) {
404061da546Spatrick char *r = fgets(buffer, sizeof(buffer), in);
405061da546Spatrick #ifdef _WIN32
406061da546Spatrick // ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED
407061da546Spatrick // according to the docs on MSDN. However, this has evidently been a
408061da546Spatrick // known bug since Windows 8. Therefore, we can't detect if a signal
409061da546Spatrick // interrupted in the fgets. So pressing ctrl-c causes the repl to end
410061da546Spatrick // and the process to exit. A temporary workaround is just to attempt to
411061da546Spatrick // fgets twice until this bug is fixed.
412061da546Spatrick if (r == nullptr)
413061da546Spatrick r = fgets(buffer, sizeof(buffer), in);
414061da546Spatrick // this is the equivalent of EINTR for Windows
415061da546Spatrick if (r == nullptr && GetLastError() == ERROR_OPERATION_ABORTED)
416061da546Spatrick continue;
417061da546Spatrick #endif
418061da546Spatrick if (r == nullptr) {
419061da546Spatrick if (ferror(in) && errno == EINTR)
420061da546Spatrick continue;
421061da546Spatrick if (feof(in))
422061da546Spatrick got_line = SplitLineEOF(m_line_buffer);
423061da546Spatrick break;
424061da546Spatrick }
425061da546Spatrick m_line_buffer += buffer;
426061da546Spatrick got_line = SplitLine(m_line_buffer);
427061da546Spatrick }
428061da546Spatrick }
429061da546Spatrick
430061da546Spatrick if (got_line) {
431*f6aab3d8Srobert line = *got_line;
432061da546Spatrick }
433061da546Spatrick
434061da546Spatrick return (bool)got_line;
435061da546Spatrick }
436061da546Spatrick
437061da546Spatrick #if LLDB_ENABLE_LIBEDIT
IsInputCompleteCallback(Editline * editline,StringList & lines)438061da546Spatrick bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline,
439be691f3bSpatrick StringList &lines) {
440be691f3bSpatrick return m_delegate.IOHandlerIsInputComplete(*this, lines);
441061da546Spatrick }
442061da546Spatrick
FixIndentationCallback(Editline * editline,const StringList & lines,int cursor_position)443061da546Spatrick int IOHandlerEditline::FixIndentationCallback(Editline *editline,
444061da546Spatrick const StringList &lines,
445be691f3bSpatrick int cursor_position) {
446be691f3bSpatrick return m_delegate.IOHandlerFixIndentation(*this, lines, cursor_position);
447061da546Spatrick }
448061da546Spatrick
449*f6aab3d8Srobert std::optional<std::string>
SuggestionCallback(llvm::StringRef line)450be691f3bSpatrick IOHandlerEditline::SuggestionCallback(llvm::StringRef line) {
451be691f3bSpatrick return m_delegate.IOHandlerSuggestion(*this, line);
452be691f3bSpatrick }
453be691f3bSpatrick
AutoCompleteCallback(CompletionRequest & request)454be691f3bSpatrick void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request) {
455be691f3bSpatrick m_delegate.IOHandlerComplete(*this, request);
456061da546Spatrick }
457061da546Spatrick #endif
458061da546Spatrick
GetPrompt()459061da546Spatrick const char *IOHandlerEditline::GetPrompt() {
460061da546Spatrick #if LLDB_ENABLE_LIBEDIT
461061da546Spatrick if (m_editline_up) {
462061da546Spatrick return m_editline_up->GetPrompt();
463061da546Spatrick } else {
464061da546Spatrick #endif
465061da546Spatrick if (m_prompt.empty())
466061da546Spatrick return nullptr;
467061da546Spatrick #if LLDB_ENABLE_LIBEDIT
468061da546Spatrick }
469061da546Spatrick #endif
470061da546Spatrick return m_prompt.c_str();
471061da546Spatrick }
472061da546Spatrick
SetPrompt(llvm::StringRef prompt)473061da546Spatrick bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
474dda28197Spatrick m_prompt = std::string(prompt);
475061da546Spatrick
476061da546Spatrick #if LLDB_ENABLE_LIBEDIT
477061da546Spatrick if (m_editline_up)
478061da546Spatrick m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
479061da546Spatrick #endif
480061da546Spatrick return true;
481061da546Spatrick }
482061da546Spatrick
GetContinuationPrompt()483061da546Spatrick const char *IOHandlerEditline::GetContinuationPrompt() {
484061da546Spatrick return (m_continuation_prompt.empty() ? nullptr
485061da546Spatrick : m_continuation_prompt.c_str());
486061da546Spatrick }
487061da546Spatrick
SetContinuationPrompt(llvm::StringRef prompt)488061da546Spatrick void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) {
489dda28197Spatrick m_continuation_prompt = std::string(prompt);
490061da546Spatrick
491061da546Spatrick #if LLDB_ENABLE_LIBEDIT
492061da546Spatrick if (m_editline_up)
493061da546Spatrick m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty()
494061da546Spatrick ? nullptr
495061da546Spatrick : m_continuation_prompt.c_str());
496061da546Spatrick #endif
497061da546Spatrick }
498061da546Spatrick
SetBaseLineNumber(uint32_t line)499061da546Spatrick void IOHandlerEditline::SetBaseLineNumber(uint32_t line) {
500061da546Spatrick m_base_line_number = line;
501061da546Spatrick }
502061da546Spatrick
GetCurrentLineIndex() const503061da546Spatrick uint32_t IOHandlerEditline::GetCurrentLineIndex() const {
504061da546Spatrick #if LLDB_ENABLE_LIBEDIT
505061da546Spatrick if (m_editline_up)
506061da546Spatrick return m_editline_up->GetCurrentLine();
507061da546Spatrick #endif
508061da546Spatrick return m_curr_line_idx;
509061da546Spatrick }
510061da546Spatrick
GetLines(StringList & lines,bool & interrupted)511061da546Spatrick bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) {
512061da546Spatrick m_current_lines_ptr = &lines;
513061da546Spatrick
514061da546Spatrick bool success = false;
515061da546Spatrick #if LLDB_ENABLE_LIBEDIT
516061da546Spatrick if (m_editline_up) {
517061da546Spatrick return m_editline_up->GetLines(m_base_line_number, lines, interrupted);
518061da546Spatrick } else {
519061da546Spatrick #endif
520061da546Spatrick bool done = false;
521061da546Spatrick Status error;
522061da546Spatrick
523061da546Spatrick while (!done) {
524061da546Spatrick // Show line numbers if we are asked to
525061da546Spatrick std::string line;
526061da546Spatrick if (m_base_line_number > 0 && GetIsInteractive()) {
527061da546Spatrick if (m_output_sp) {
528061da546Spatrick m_output_sp->Printf("%u%s",
529061da546Spatrick m_base_line_number + (uint32_t)lines.GetSize(),
530061da546Spatrick GetPrompt() == nullptr ? " " : "");
531061da546Spatrick }
532061da546Spatrick }
533061da546Spatrick
534061da546Spatrick m_curr_line_idx = lines.GetSize();
535061da546Spatrick
536061da546Spatrick bool interrupted = false;
537061da546Spatrick if (GetLine(line, interrupted) && !interrupted) {
538061da546Spatrick lines.AppendString(line);
539061da546Spatrick done = m_delegate.IOHandlerIsInputComplete(*this, lines);
540061da546Spatrick } else {
541061da546Spatrick done = true;
542061da546Spatrick }
543061da546Spatrick }
544061da546Spatrick success = lines.GetSize() > 0;
545061da546Spatrick #if LLDB_ENABLE_LIBEDIT
546061da546Spatrick }
547061da546Spatrick #endif
548061da546Spatrick return success;
549061da546Spatrick }
550061da546Spatrick
551061da546Spatrick // Each IOHandler gets to run until it is done. It should read data from the
552061da546Spatrick // "in" and place output into "out" and "err and return when done.
Run()553061da546Spatrick void IOHandlerEditline::Run() {
554061da546Spatrick std::string line;
555061da546Spatrick while (IsActive()) {
556061da546Spatrick bool interrupted = false;
557061da546Spatrick if (m_multi_line) {
558061da546Spatrick StringList lines;
559061da546Spatrick if (GetLines(lines, interrupted)) {
560061da546Spatrick if (interrupted) {
561061da546Spatrick m_done = m_interrupt_exits;
562061da546Spatrick m_delegate.IOHandlerInputInterrupted(*this, line);
563061da546Spatrick
564061da546Spatrick } else {
565061da546Spatrick line = lines.CopyList();
566061da546Spatrick m_delegate.IOHandlerInputComplete(*this, line);
567061da546Spatrick }
568061da546Spatrick } else {
569061da546Spatrick m_done = true;
570061da546Spatrick }
571061da546Spatrick } else {
572061da546Spatrick if (GetLine(line, interrupted)) {
573061da546Spatrick if (interrupted)
574061da546Spatrick m_delegate.IOHandlerInputInterrupted(*this, line);
575061da546Spatrick else
576061da546Spatrick m_delegate.IOHandlerInputComplete(*this, line);
577061da546Spatrick } else {
578061da546Spatrick m_done = true;
579061da546Spatrick }
580061da546Spatrick }
581061da546Spatrick }
582061da546Spatrick }
583061da546Spatrick
Cancel()584061da546Spatrick void IOHandlerEditline::Cancel() {
585061da546Spatrick #if LLDB_ENABLE_LIBEDIT
586061da546Spatrick if (m_editline_up)
587061da546Spatrick m_editline_up->Cancel();
588061da546Spatrick #endif
589061da546Spatrick }
590061da546Spatrick
Interrupt()591061da546Spatrick bool IOHandlerEditline::Interrupt() {
592061da546Spatrick // Let the delgate handle it first
593061da546Spatrick if (m_delegate.IOHandlerInterrupt(*this))
594061da546Spatrick return true;
595061da546Spatrick
596061da546Spatrick #if LLDB_ENABLE_LIBEDIT
597061da546Spatrick if (m_editline_up)
598061da546Spatrick return m_editline_up->Interrupt();
599061da546Spatrick #endif
600061da546Spatrick return false;
601061da546Spatrick }
602061da546Spatrick
GotEOF()603061da546Spatrick void IOHandlerEditline::GotEOF() {
604061da546Spatrick #if LLDB_ENABLE_LIBEDIT
605061da546Spatrick if (m_editline_up)
606061da546Spatrick m_editline_up->Interrupt();
607061da546Spatrick #endif
608061da546Spatrick }
609061da546Spatrick
PrintAsync(const char * s,size_t len,bool is_stdout)610*f6aab3d8Srobert void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) {
611061da546Spatrick #if LLDB_ENABLE_LIBEDIT
612*f6aab3d8Srobert if (m_editline_up) {
613*f6aab3d8Srobert std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
614*f6aab3d8Srobert lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
615*f6aab3d8Srobert m_editline_up->PrintAsync(stream.get(), s, len);
616*f6aab3d8Srobert } else
617061da546Spatrick #endif
618061da546Spatrick {
619061da546Spatrick #ifdef _WIN32
620061da546Spatrick const char *prompt = GetPrompt();
621061da546Spatrick if (prompt) {
622061da546Spatrick // Back up over previous prompt using Windows API
623061da546Spatrick CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
624061da546Spatrick HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
625061da546Spatrick GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info);
626061da546Spatrick COORD coord = screen_buffer_info.dwCursorPosition;
627061da546Spatrick coord.X -= strlen(prompt);
628061da546Spatrick if (coord.X < 0)
629061da546Spatrick coord.X = 0;
630061da546Spatrick SetConsoleCursorPosition(console_handle, coord);
631061da546Spatrick }
632061da546Spatrick #endif
633*f6aab3d8Srobert IOHandler::PrintAsync(s, len, is_stdout);
634061da546Spatrick #ifdef _WIN32
635061da546Spatrick if (prompt)
636*f6aab3d8Srobert IOHandler::PrintAsync(prompt, strlen(prompt), is_stdout);
637061da546Spatrick #endif
638061da546Spatrick }
639061da546Spatrick }
640