xref: /src/contrib/llvm-project/lldb/source/Core/IOHandler.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
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