xref: /openbsd-src/gnu/llvm/lldb/source/Core/IOHandler.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
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