15ffd83dbSDimitry Andric //===-- IOHandler.cpp -----------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "lldb/Core/IOHandler.h"
100b57cec5SDimitry Andric 
110b57cec5SDimitry Andric #if defined(__APPLE__)
120b57cec5SDimitry Andric #include <deque>
130b57cec5SDimitry Andric #endif
140b57cec5SDimitry Andric #include <string>
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric #include "lldb/Core/Debugger.h"
17480093f4SDimitry Andric #include "lldb/Host/Config.h"
180b57cec5SDimitry Andric #include "lldb/Host/File.h"
195f757f3fSDimitry Andric #include "lldb/Host/StreamFile.h"
2081ad6265SDimitry Andric #include "lldb/Utility/AnsiTerminal.h"
210b57cec5SDimitry Andric #include "lldb/Utility/Predicate.h"
220b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
230b57cec5SDimitry Andric #include "lldb/Utility/StreamString.h"
240b57cec5SDimitry Andric #include "lldb/Utility/StringList.h"
250b57cec5SDimitry Andric #include "lldb/lldb-forward.h"
260b57cec5SDimitry Andric 
27480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
280b57cec5SDimitry Andric #include "lldb/Host/Editline.h"
290b57cec5SDimitry Andric #endif
300b57cec5SDimitry Andric #include "lldb/Interpreter/CommandCompletions.h"
310b57cec5SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h"
320b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
330b57cec5SDimitry Andric 
349dba64beSDimitry Andric #ifdef _WIN32
350b57cec5SDimitry Andric #include "lldb/Host/windows/windows.h"
360b57cec5SDimitry Andric #endif
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric #include <memory>
390b57cec5SDimitry Andric #include <mutex>
40bdd1243dSDimitry Andric #include <optional>
410b57cec5SDimitry Andric 
42fe6060f1SDimitry Andric #include <cassert>
43fe6060f1SDimitry Andric #include <cctype>
44fe6060f1SDimitry Andric #include <cerrno>
45fe6060f1SDimitry Andric #include <clocale>
46fe6060f1SDimitry Andric #include <cstdint>
47fe6060f1SDimitry Andric #include <cstdio>
48fe6060f1SDimitry Andric #include <cstring>
490b57cec5SDimitry Andric #include <type_traits>
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric using namespace lldb;
520b57cec5SDimitry Andric using namespace lldb_private;
539dba64beSDimitry Andric using llvm::StringRef;
549dba64beSDimitry Andric 
IOHandler(Debugger & debugger,IOHandler::Type type)550b57cec5SDimitry Andric IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
560b57cec5SDimitry Andric     : IOHandler(debugger, type,
579dba64beSDimitry Andric                 FileSP(),       // Adopt STDIN from top input reader
580b57cec5SDimitry Andric                 StreamFileSP(), // Adopt STDOUT from top input reader
590b57cec5SDimitry Andric                 StreamFileSP(), // Adopt STDERR from top input reader
60bdd1243dSDimitry Andric                 0               // Flags
61bdd1243dSDimitry Andric 
620b57cec5SDimitry Andric       ) {}
630b57cec5SDimitry Andric 
IOHandler(Debugger & debugger,IOHandler::Type type,const lldb::FileSP & input_sp,const lldb::StreamFileSP & output_sp,const lldb::StreamFileSP & error_sp,uint32_t flags)640b57cec5SDimitry Andric IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type,
659dba64beSDimitry Andric                      const lldb::FileSP &input_sp,
660b57cec5SDimitry Andric                      const lldb::StreamFileSP &output_sp,
67bdd1243dSDimitry Andric                      const lldb::StreamFileSP &error_sp, uint32_t flags)
680b57cec5SDimitry Andric     : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
69bdd1243dSDimitry Andric       m_error_sp(error_sp), m_popped(false), m_flags(flags), m_type(type),
70bdd1243dSDimitry Andric       m_user_data(nullptr), m_done(false), m_active(false) {
710b57cec5SDimitry Andric   // If any files are not specified, then adopt them from the top input reader.
720b57cec5SDimitry Andric   if (!m_input_sp || !m_output_sp || !m_error_sp)
730b57cec5SDimitry Andric     debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp,
740b57cec5SDimitry Andric                                              m_error_sp);
750b57cec5SDimitry Andric }
760b57cec5SDimitry Andric 
770b57cec5SDimitry Andric IOHandler::~IOHandler() = default;
780b57cec5SDimitry Andric 
GetInputFD()790b57cec5SDimitry Andric int IOHandler::GetInputFD() {
809dba64beSDimitry Andric   return (m_input_sp ? m_input_sp->GetDescriptor() : -1);
810b57cec5SDimitry Andric }
820b57cec5SDimitry Andric 
GetOutputFD()830b57cec5SDimitry Andric int IOHandler::GetOutputFD() {
840b57cec5SDimitry Andric   return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1);
850b57cec5SDimitry Andric }
860b57cec5SDimitry Andric 
GetErrorFD()870b57cec5SDimitry Andric int IOHandler::GetErrorFD() {
880b57cec5SDimitry Andric   return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1);
890b57cec5SDimitry Andric }
900b57cec5SDimitry Andric 
GetInputFILE()910b57cec5SDimitry Andric FILE *IOHandler::GetInputFILE() {
929dba64beSDimitry Andric   return (m_input_sp ? m_input_sp->GetStream() : nullptr);
930b57cec5SDimitry Andric }
940b57cec5SDimitry Andric 
GetOutputFILE()950b57cec5SDimitry Andric FILE *IOHandler::GetOutputFILE() {
960b57cec5SDimitry Andric   return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr);
970b57cec5SDimitry Andric }
980b57cec5SDimitry Andric 
GetErrorFILE()990b57cec5SDimitry Andric FILE *IOHandler::GetErrorFILE() {
1000b57cec5SDimitry Andric   return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr);
1010b57cec5SDimitry Andric }
1020b57cec5SDimitry Andric 
GetInputFileSP()103e8d8bef9SDimitry Andric FileSP IOHandler::GetInputFileSP() { return m_input_sp; }
1040b57cec5SDimitry Andric 
GetOutputStreamFileSP()105e8d8bef9SDimitry Andric StreamFileSP IOHandler::GetOutputStreamFileSP() { return m_output_sp; }
1060b57cec5SDimitry Andric 
GetErrorStreamFileSP()107e8d8bef9SDimitry Andric StreamFileSP IOHandler::GetErrorStreamFileSP() { return m_error_sp; }
1080b57cec5SDimitry Andric 
GetIsInteractive()1090b57cec5SDimitry Andric bool IOHandler::GetIsInteractive() {
1109dba64beSDimitry Andric   return GetInputFileSP() ? GetInputFileSP()->GetIsInteractive() : false;
1110b57cec5SDimitry Andric }
1120b57cec5SDimitry Andric 
GetIsRealTerminal()1130b57cec5SDimitry Andric bool IOHandler::GetIsRealTerminal() {
1149dba64beSDimitry Andric   return GetInputFileSP() ? GetInputFileSP()->GetIsRealTerminal() : false;
1150b57cec5SDimitry Andric }
1160b57cec5SDimitry Andric 
SetPopped(bool b)1170b57cec5SDimitry Andric void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
1180b57cec5SDimitry Andric 
WaitForPop()1190b57cec5SDimitry Andric void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
1200b57cec5SDimitry Andric 
PrintAsync(const char * s,size_t len,bool is_stdout)12181ad6265SDimitry Andric void IOHandler::PrintAsync(const char *s, size_t len, bool is_stdout) {
12281ad6265SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
12381ad6265SDimitry Andric   lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
1245ffd83dbSDimitry Andric   stream->Write(s, len);
12581ad6265SDimitry Andric   stream->Flush();
1260b57cec5SDimitry Andric }
12781ad6265SDimitry Andric 
PrintAsync(const char * s,size_t len,bool is_stdout)12881ad6265SDimitry Andric bool IOHandlerStack::PrintAsync(const char *s, size_t len, bool is_stdout) {
12981ad6265SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_mutex);
13081ad6265SDimitry Andric   if (!m_top)
13181ad6265SDimitry Andric     return false;
13281ad6265SDimitry Andric   m_top->PrintAsync(s, len, is_stdout);
13381ad6265SDimitry Andric   return true;
1340b57cec5SDimitry Andric }
1350b57cec5SDimitry Andric 
IOHandlerConfirm(Debugger & debugger,llvm::StringRef prompt,bool default_response)1360b57cec5SDimitry Andric IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
1370b57cec5SDimitry Andric                                    bool default_response)
1380b57cec5SDimitry Andric     : IOHandlerEditline(
1390b57cec5SDimitry Andric           debugger, IOHandler::Type::Confirm,
1400b57cec5SDimitry Andric           nullptr, // nullptr editline_name means no history loaded/saved
1410b57cec5SDimitry Andric           llvm::StringRef(), // No prompt
1420b57cec5SDimitry Andric           llvm::StringRef(), // No continuation prompt
1430b57cec5SDimitry Andric           false,             // Multi-line
1440b57cec5SDimitry Andric           false, // Don't colorize the prompt (i.e. the confirm message.)
145bdd1243dSDimitry Andric           0, *this),
1460b57cec5SDimitry Andric       m_default_response(default_response), m_user_response(default_response) {
1470b57cec5SDimitry Andric   StreamString prompt_stream;
1480b57cec5SDimitry Andric   prompt_stream.PutCString(prompt);
1490b57cec5SDimitry Andric   if (m_default_response)
1500b57cec5SDimitry Andric     prompt_stream.Printf(": [Y/n] ");
1510b57cec5SDimitry Andric   else
1520b57cec5SDimitry Andric     prompt_stream.Printf(": [y/N] ");
1530b57cec5SDimitry Andric 
1540b57cec5SDimitry Andric   SetPrompt(prompt_stream.GetString());
1550b57cec5SDimitry Andric }
1560b57cec5SDimitry Andric 
1570b57cec5SDimitry Andric IOHandlerConfirm::~IOHandlerConfirm() = default;
1580b57cec5SDimitry Andric 
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)1599dba64beSDimitry Andric void IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler,
1609dba64beSDimitry Andric                                          CompletionRequest &request) {
1619dba64beSDimitry Andric   if (request.GetRawCursorPos() != 0)
1629dba64beSDimitry Andric     return;
1639dba64beSDimitry Andric   request.AddCompletion(m_default_response ? "y" : "n");
1640b57cec5SDimitry Andric }
1650b57cec5SDimitry Andric 
IOHandlerInputComplete(IOHandler & io_handler,std::string & line)1660b57cec5SDimitry Andric void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler,
1670b57cec5SDimitry Andric                                               std::string &line) {
1680b57cec5SDimitry Andric   if (line.empty()) {
1690b57cec5SDimitry Andric     // User just hit enter, set the response to the default
1700b57cec5SDimitry Andric     m_user_response = m_default_response;
1710b57cec5SDimitry Andric     io_handler.SetIsDone(true);
1720b57cec5SDimitry Andric     return;
1730b57cec5SDimitry Andric   }
1740b57cec5SDimitry Andric 
1750b57cec5SDimitry Andric   if (line.size() == 1) {
1760b57cec5SDimitry Andric     switch (line[0]) {
1770b57cec5SDimitry Andric     case 'y':
1780b57cec5SDimitry Andric     case 'Y':
1790b57cec5SDimitry Andric       m_user_response = true;
1800b57cec5SDimitry Andric       io_handler.SetIsDone(true);
1810b57cec5SDimitry Andric       return;
1820b57cec5SDimitry Andric     case 'n':
1830b57cec5SDimitry Andric     case 'N':
1840b57cec5SDimitry Andric       m_user_response = false;
1850b57cec5SDimitry Andric       io_handler.SetIsDone(true);
1860b57cec5SDimitry Andric       return;
1870b57cec5SDimitry Andric     default:
1880b57cec5SDimitry Andric       break;
1890b57cec5SDimitry Andric     }
1900b57cec5SDimitry Andric   }
1910b57cec5SDimitry Andric 
1920b57cec5SDimitry Andric   if (line == "yes" || line == "YES" || line == "Yes") {
1930b57cec5SDimitry Andric     m_user_response = true;
1940b57cec5SDimitry Andric     io_handler.SetIsDone(true);
1950b57cec5SDimitry Andric   } else if (line == "no" || line == "NO" || line == "No") {
1960b57cec5SDimitry Andric     m_user_response = false;
1970b57cec5SDimitry Andric     io_handler.SetIsDone(true);
1980b57cec5SDimitry Andric   }
1990b57cec5SDimitry Andric }
2000b57cec5SDimitry Andric 
201bdd1243dSDimitry Andric std::optional<std::string>
IOHandlerSuggestion(IOHandler & io_handler,llvm::StringRef line)202e8d8bef9SDimitry Andric IOHandlerDelegate::IOHandlerSuggestion(IOHandler &io_handler,
203e8d8bef9SDimitry Andric                                        llvm::StringRef line) {
204e8d8bef9SDimitry Andric   return io_handler.GetDebugger()
205e8d8bef9SDimitry Andric       .GetCommandInterpreter()
206e8d8bef9SDimitry Andric       .GetAutoSuggestionForCommand(line);
207e8d8bef9SDimitry Andric }
208e8d8bef9SDimitry Andric 
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)2099dba64beSDimitry Andric void IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler,
2109dba64beSDimitry Andric                                           CompletionRequest &request) {
2110b57cec5SDimitry Andric   switch (m_completion) {
2120b57cec5SDimitry Andric   case Completion::None:
2130b57cec5SDimitry Andric     break;
2140b57cec5SDimitry Andric   case Completion::LLDBCommand:
2159dba64beSDimitry Andric     io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(request);
2169dba64beSDimitry Andric     break;
2179dba64beSDimitry Andric   case Completion::Expression:
21806c3fb27SDimitry Andric     lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
2190b57cec5SDimitry Andric         io_handler.GetDebugger().GetCommandInterpreter(),
22006c3fb27SDimitry Andric         lldb::eVariablePathCompletion, request, nullptr);
2219dba64beSDimitry Andric     break;
2220b57cec5SDimitry Andric   }
2230b57cec5SDimitry Andric }
2240b57cec5SDimitry Andric 
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)2250b57cec5SDimitry Andric IOHandlerEditline::IOHandlerEditline(
2260b57cec5SDimitry Andric     Debugger &debugger, IOHandler::Type type,
2270b57cec5SDimitry Andric     const char *editline_name, // Used for saving history files
2280b57cec5SDimitry Andric     llvm::StringRef prompt, llvm::StringRef continuation_prompt,
2295f757f3fSDimitry Andric     bool multi_line, bool color, uint32_t line_number_start,
230bdd1243dSDimitry Andric     IOHandlerDelegate &delegate)
2310b57cec5SDimitry Andric     : IOHandlerEditline(debugger, type,
2329dba64beSDimitry Andric                         FileSP(),       // Inherit input from top input reader
2330b57cec5SDimitry Andric                         StreamFileSP(), // Inherit output from top input reader
2340b57cec5SDimitry Andric                         StreamFileSP(), // Inherit error from top input reader
2350b57cec5SDimitry Andric                         0,              // Flags
2360b57cec5SDimitry Andric                         editline_name,  // Used for saving history files
2375f757f3fSDimitry Andric                         prompt, continuation_prompt, multi_line, color,
238bdd1243dSDimitry Andric                         line_number_start, delegate) {}
2390b57cec5SDimitry Andric 
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)2400b57cec5SDimitry Andric IOHandlerEditline::IOHandlerEditline(
2419dba64beSDimitry Andric     Debugger &debugger, IOHandler::Type type, const lldb::FileSP &input_sp,
2429dba64beSDimitry Andric     const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp,
2439dba64beSDimitry Andric     uint32_t flags,
2440b57cec5SDimitry Andric     const char *editline_name, // Used for saving history files
2450b57cec5SDimitry Andric     llvm::StringRef prompt, llvm::StringRef continuation_prompt,
2465f757f3fSDimitry Andric     bool multi_line, bool color, uint32_t line_number_start,
247bdd1243dSDimitry Andric     IOHandlerDelegate &delegate)
248bdd1243dSDimitry Andric     : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags),
249480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
2500b57cec5SDimitry Andric       m_editline_up(),
2510b57cec5SDimitry Andric #endif
2520b57cec5SDimitry Andric       m_delegate(delegate), m_prompt(), m_continuation_prompt(),
2530b57cec5SDimitry Andric       m_current_lines_ptr(nullptr), m_base_line_number(line_number_start),
2545f757f3fSDimitry Andric       m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line), m_color(color),
2555f757f3fSDimitry Andric       m_interrupt_exits(true) {
2560b57cec5SDimitry Andric   SetPrompt(prompt);
2570b57cec5SDimitry Andric 
258480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
2590b57cec5SDimitry Andric   bool use_editline = false;
2600b57cec5SDimitry Andric 
2619dba64beSDimitry Andric   use_editline = GetInputFILE() && GetOutputFILE() && GetErrorFILE() &&
2629dba64beSDimitry Andric                  m_input_sp && m_input_sp->GetIsRealTerminal();
2630b57cec5SDimitry Andric 
2640b57cec5SDimitry Andric   if (use_editline) {
2655f757f3fSDimitry Andric     m_editline_up = std::make_unique<Editline>(editline_name, GetInputFILE(),
2665f757f3fSDimitry Andric                                                GetOutputFILE(), GetErrorFILE(),
2675f757f3fSDimitry Andric                                                GetOutputMutex());
268fe6060f1SDimitry Andric     m_editline_up->SetIsInputCompleteCallback(
269fe6060f1SDimitry Andric         [this](Editline *editline, StringList &lines) {
270fe6060f1SDimitry Andric           return this->IsInputCompleteCallback(editline, lines);
271fe6060f1SDimitry Andric         });
272fe6060f1SDimitry Andric 
273fe6060f1SDimitry Andric     m_editline_up->SetAutoCompleteCallback([this](CompletionRequest &request) {
274fe6060f1SDimitry Andric       this->AutoCompleteCallback(request);
275fe6060f1SDimitry Andric     });
276fe6060f1SDimitry Andric 
27781ad6265SDimitry Andric     if (debugger.GetUseAutosuggestion()) {
278fe6060f1SDimitry Andric       m_editline_up->SetSuggestionCallback([this](llvm::StringRef line) {
279fe6060f1SDimitry Andric         return this->SuggestionCallback(line);
280fe6060f1SDimitry Andric       });
2815f757f3fSDimitry Andric       if (m_color) {
28281ad6265SDimitry Andric         m_editline_up->SetSuggestionAnsiPrefix(ansi::FormatAnsiTerminalCodes(
28381ad6265SDimitry Andric             debugger.GetAutosuggestionAnsiPrefix()));
28481ad6265SDimitry Andric         m_editline_up->SetSuggestionAnsiSuffix(ansi::FormatAnsiTerminalCodes(
28581ad6265SDimitry Andric             debugger.GetAutosuggestionAnsiSuffix()));
286fe6060f1SDimitry Andric       }
2875f757f3fSDimitry Andric     }
2880b57cec5SDimitry Andric     // See if the delegate supports fixing indentation
2890b57cec5SDimitry Andric     const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
2900b57cec5SDimitry Andric     if (indent_chars) {
2910b57cec5SDimitry Andric       // The delegate does support indentation, hook it up so when any
2920b57cec5SDimitry Andric       // indentation character is typed, the delegate gets a chance to fix it
293fe6060f1SDimitry Andric       FixIndentationCallbackType f = [this](Editline *editline,
294fe6060f1SDimitry Andric                                             const StringList &lines,
295fe6060f1SDimitry Andric                                             int cursor_position) {
296fe6060f1SDimitry Andric         return this->FixIndentationCallback(editline, lines, cursor_position);
297fe6060f1SDimitry Andric       };
298fe6060f1SDimitry Andric       m_editline_up->SetFixIndentationCallback(std::move(f), indent_chars);
2990b57cec5SDimitry Andric     }
3000b57cec5SDimitry Andric   }
3010b57cec5SDimitry Andric #endif
3020b57cec5SDimitry Andric   SetBaseLineNumber(m_base_line_number);
3030b57cec5SDimitry Andric   SetPrompt(prompt);
3040b57cec5SDimitry Andric   SetContinuationPrompt(continuation_prompt);
3050b57cec5SDimitry Andric }
3060b57cec5SDimitry Andric 
~IOHandlerEditline()3070b57cec5SDimitry Andric IOHandlerEditline::~IOHandlerEditline() {
308480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
3090b57cec5SDimitry Andric   m_editline_up.reset();
3100b57cec5SDimitry Andric #endif
3110b57cec5SDimitry Andric }
3120b57cec5SDimitry Andric 
Activate()3130b57cec5SDimitry Andric void IOHandlerEditline::Activate() {
3140b57cec5SDimitry Andric   IOHandler::Activate();
3150b57cec5SDimitry Andric   m_delegate.IOHandlerActivated(*this, GetIsInteractive());
3160b57cec5SDimitry Andric }
3170b57cec5SDimitry Andric 
Deactivate()3180b57cec5SDimitry Andric void IOHandlerEditline::Deactivate() {
3190b57cec5SDimitry Andric   IOHandler::Deactivate();
3200b57cec5SDimitry Andric   m_delegate.IOHandlerDeactivated(*this);
3210b57cec5SDimitry Andric }
3220b57cec5SDimitry Andric 
TerminalSizeChanged()3235ffd83dbSDimitry Andric void IOHandlerEditline::TerminalSizeChanged() {
3245ffd83dbSDimitry Andric #if LLDB_ENABLE_LIBEDIT
3255ffd83dbSDimitry Andric   if (m_editline_up)
3265ffd83dbSDimitry Andric     m_editline_up->TerminalSizeChanged();
3275ffd83dbSDimitry Andric #endif
3285ffd83dbSDimitry Andric }
3295ffd83dbSDimitry Andric 
3309dba64beSDimitry Andric // Split out a line from the buffer, if there is a full one to get.
SplitLine(std::string & line_buffer)331bdd1243dSDimitry Andric static std::optional<std::string> SplitLine(std::string &line_buffer) {
3329dba64beSDimitry Andric   size_t pos = line_buffer.find('\n');
3339dba64beSDimitry Andric   if (pos == std::string::npos)
334bdd1243dSDimitry Andric     return std::nullopt;
3355ffd83dbSDimitry Andric   std::string line =
3365ffd83dbSDimitry Andric       std::string(StringRef(line_buffer.c_str(), pos).rtrim("\n\r"));
3379dba64beSDimitry Andric   line_buffer = line_buffer.substr(pos + 1);
3389dba64beSDimitry Andric   return line;
3399dba64beSDimitry Andric }
3409dba64beSDimitry Andric 
3419dba64beSDimitry Andric // If the final line of the file ends without a end-of-line, return
3429dba64beSDimitry Andric // it as a line anyway.
SplitLineEOF(std::string & line_buffer)343bdd1243dSDimitry Andric static std::optional<std::string> SplitLineEOF(std::string &line_buffer) {
3445ffd83dbSDimitry Andric   if (llvm::all_of(line_buffer, llvm::isSpace))
345bdd1243dSDimitry Andric     return std::nullopt;
3469dba64beSDimitry Andric   std::string line = std::move(line_buffer);
3479dba64beSDimitry Andric   line_buffer.clear();
3489dba64beSDimitry Andric   return line;
3499dba64beSDimitry Andric }
3509dba64beSDimitry Andric 
GetLine(std::string & line,bool & interrupted)3510b57cec5SDimitry Andric bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
352480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
3530b57cec5SDimitry Andric   if (m_editline_up) {
354bdd1243dSDimitry Andric     return m_editline_up->GetLine(line, interrupted);
3559dba64beSDimitry Andric   }
3560b57cec5SDimitry Andric #endif
3579dba64beSDimitry Andric 
3580b57cec5SDimitry Andric   line.clear();
3590b57cec5SDimitry Andric 
3600b57cec5SDimitry Andric   if (GetIsInteractive()) {
3610b57cec5SDimitry Andric     const char *prompt = nullptr;
3620b57cec5SDimitry Andric 
3630b57cec5SDimitry Andric     if (m_multi_line && m_curr_line_idx > 0)
3640b57cec5SDimitry Andric       prompt = GetContinuationPrompt();
3650b57cec5SDimitry Andric 
3660b57cec5SDimitry Andric     if (prompt == nullptr)
3670b57cec5SDimitry Andric       prompt = GetPrompt();
3680b57cec5SDimitry Andric 
3690b57cec5SDimitry Andric     if (prompt && prompt[0]) {
3709dba64beSDimitry Andric       if (m_output_sp) {
3719dba64beSDimitry Andric         m_output_sp->Printf("%s", prompt);
3729dba64beSDimitry Andric         m_output_sp->Flush();
3730b57cec5SDimitry Andric       }
3740b57cec5SDimitry Andric     }
3750b57cec5SDimitry Andric   }
3769dba64beSDimitry Andric 
377bdd1243dSDimitry Andric   std::optional<std::string> got_line = SplitLine(m_line_buffer);
3789dba64beSDimitry Andric 
3799dba64beSDimitry Andric   if (!got_line && !m_input_sp) {
3809dba64beSDimitry Andric     // No more input file, we are done...
3819dba64beSDimitry Andric     SetIsDone(true);
3829dba64beSDimitry Andric     return false;
3839dba64beSDimitry Andric   }
3849dba64beSDimitry Andric 
3859dba64beSDimitry Andric   FILE *in = GetInputFILE();
3860b57cec5SDimitry Andric   char buffer[256];
3879dba64beSDimitry Andric 
3889dba64beSDimitry Andric   if (!got_line && !in && m_input_sp) {
3899dba64beSDimitry Andric     // there is no FILE*, fall back on just reading bytes from the stream.
3909dba64beSDimitry Andric     while (!got_line) {
3919dba64beSDimitry Andric       size_t bytes_read = sizeof(buffer);
3929dba64beSDimitry Andric       Status error = m_input_sp->Read((void *)buffer, bytes_read);
3939dba64beSDimitry Andric       if (error.Success() && !bytes_read) {
3949dba64beSDimitry Andric         got_line = SplitLineEOF(m_line_buffer);
3959dba64beSDimitry Andric         break;
3969dba64beSDimitry Andric       }
3979dba64beSDimitry Andric       if (error.Fail())
3989dba64beSDimitry Andric         break;
3999dba64beSDimitry Andric       m_line_buffer += StringRef(buffer, bytes_read);
4009dba64beSDimitry Andric       got_line = SplitLine(m_line_buffer);
4019dba64beSDimitry Andric     }
4029dba64beSDimitry Andric   }
4039dba64beSDimitry Andric 
4049dba64beSDimitry Andric   if (!got_line && in) {
4059dba64beSDimitry Andric     while (!got_line) {
4069dba64beSDimitry Andric       char *r = fgets(buffer, sizeof(buffer), in);
4070b57cec5SDimitry Andric #ifdef _WIN32
4080b57cec5SDimitry Andric       // ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED
4090b57cec5SDimitry Andric       // according to the docs on MSDN. However, this has evidently been a
4100b57cec5SDimitry Andric       // known bug since Windows 8. Therefore, we can't detect if a signal
4110b57cec5SDimitry Andric       // interrupted in the fgets. So pressing ctrl-c causes the repl to end
4120b57cec5SDimitry Andric       // and the process to exit. A temporary workaround is just to attempt to
4130b57cec5SDimitry Andric       // fgets twice until this bug is fixed.
4149dba64beSDimitry Andric       if (r == nullptr)
4159dba64beSDimitry Andric         r = fgets(buffer, sizeof(buffer), in);
4169dba64beSDimitry Andric       // this is the equivalent of EINTR for Windows
4179dba64beSDimitry Andric       if (r == nullptr && GetLastError() == ERROR_OPERATION_ABORTED)
4189dba64beSDimitry Andric         continue;
4190b57cec5SDimitry Andric #endif
4209dba64beSDimitry Andric       if (r == nullptr) {
4219dba64beSDimitry Andric         if (ferror(in) && errno == EINTR)
4229dba64beSDimitry Andric           continue;
4230b57cec5SDimitry Andric         if (feof(in))
4249dba64beSDimitry Andric           got_line = SplitLineEOF(m_line_buffer);
4250b57cec5SDimitry Andric         break;
4260b57cec5SDimitry Andric       }
4279dba64beSDimitry Andric       m_line_buffer += buffer;
4289dba64beSDimitry Andric       got_line = SplitLine(m_line_buffer);
4290b57cec5SDimitry Andric     }
4309dba64beSDimitry Andric   }
4319dba64beSDimitry Andric 
4329dba64beSDimitry Andric   if (got_line) {
43381ad6265SDimitry Andric     line = *got_line;
4340b57cec5SDimitry Andric   }
4359dba64beSDimitry Andric 
4369dba64beSDimitry Andric   return (bool)got_line;
4370b57cec5SDimitry Andric }
4380b57cec5SDimitry Andric 
439480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
IsInputCompleteCallback(Editline * editline,StringList & lines)4400b57cec5SDimitry Andric bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline,
441fe6060f1SDimitry Andric                                                 StringList &lines) {
442fe6060f1SDimitry Andric   return m_delegate.IOHandlerIsInputComplete(*this, lines);
4430b57cec5SDimitry Andric }
4440b57cec5SDimitry Andric 
FixIndentationCallback(Editline * editline,const StringList & lines,int cursor_position)4450b57cec5SDimitry Andric int IOHandlerEditline::FixIndentationCallback(Editline *editline,
4460b57cec5SDimitry Andric                                               const StringList &lines,
447fe6060f1SDimitry Andric                                               int cursor_position) {
448fe6060f1SDimitry Andric   return m_delegate.IOHandlerFixIndentation(*this, lines, cursor_position);
4490b57cec5SDimitry Andric }
4500b57cec5SDimitry Andric 
451bdd1243dSDimitry Andric std::optional<std::string>
SuggestionCallback(llvm::StringRef line)452fe6060f1SDimitry Andric IOHandlerEditline::SuggestionCallback(llvm::StringRef line) {
453fe6060f1SDimitry Andric   return m_delegate.IOHandlerSuggestion(*this, line);
454e8d8bef9SDimitry Andric }
455e8d8bef9SDimitry Andric 
AutoCompleteCallback(CompletionRequest & request)456fe6060f1SDimitry Andric void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request) {
457fe6060f1SDimitry Andric   m_delegate.IOHandlerComplete(*this, request);
4580b57cec5SDimitry Andric }
4590b57cec5SDimitry Andric #endif
4600b57cec5SDimitry Andric 
GetPrompt()4610b57cec5SDimitry Andric const char *IOHandlerEditline::GetPrompt() {
462480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
4630b57cec5SDimitry Andric   if (m_editline_up) {
4640b57cec5SDimitry Andric     return m_editline_up->GetPrompt();
4650b57cec5SDimitry Andric   } else {
4660b57cec5SDimitry Andric #endif
4670b57cec5SDimitry Andric     if (m_prompt.empty())
4680b57cec5SDimitry Andric       return nullptr;
469480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
4700b57cec5SDimitry Andric   }
4710b57cec5SDimitry Andric #endif
4720b57cec5SDimitry Andric   return m_prompt.c_str();
4730b57cec5SDimitry Andric }
4740b57cec5SDimitry Andric 
SetPrompt(llvm::StringRef prompt)4750b57cec5SDimitry Andric bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
4765ffd83dbSDimitry Andric   m_prompt = std::string(prompt);
4770b57cec5SDimitry Andric 
478480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
4795f757f3fSDimitry Andric   if (m_editline_up) {
4800b57cec5SDimitry Andric     m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
4815f757f3fSDimitry Andric     if (m_color) {
4825f757f3fSDimitry Andric       m_editline_up->SetPromptAnsiPrefix(
4835f757f3fSDimitry Andric           ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiPrefix()));
4845f757f3fSDimitry Andric       m_editline_up->SetPromptAnsiSuffix(
4855f757f3fSDimitry Andric           ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiSuffix()));
4865f757f3fSDimitry Andric     }
4875f757f3fSDimitry Andric   }
4880b57cec5SDimitry Andric #endif
4890b57cec5SDimitry Andric   return true;
4900b57cec5SDimitry Andric }
4910b57cec5SDimitry Andric 
GetContinuationPrompt()4920b57cec5SDimitry Andric const char *IOHandlerEditline::GetContinuationPrompt() {
4930b57cec5SDimitry Andric   return (m_continuation_prompt.empty() ? nullptr
4940b57cec5SDimitry Andric                                         : m_continuation_prompt.c_str());
4950b57cec5SDimitry Andric }
4960b57cec5SDimitry Andric 
SetContinuationPrompt(llvm::StringRef prompt)4970b57cec5SDimitry Andric void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) {
4985ffd83dbSDimitry Andric   m_continuation_prompt = std::string(prompt);
4990b57cec5SDimitry Andric 
500480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
5010b57cec5SDimitry Andric   if (m_editline_up)
5020b57cec5SDimitry Andric     m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty()
5030b57cec5SDimitry Andric                                              ? nullptr
5040b57cec5SDimitry Andric                                              : m_continuation_prompt.c_str());
5050b57cec5SDimitry Andric #endif
5060b57cec5SDimitry Andric }
5070b57cec5SDimitry Andric 
SetBaseLineNumber(uint32_t line)5080b57cec5SDimitry Andric void IOHandlerEditline::SetBaseLineNumber(uint32_t line) {
5090b57cec5SDimitry Andric   m_base_line_number = line;
5100b57cec5SDimitry Andric }
5110b57cec5SDimitry Andric 
GetCurrentLineIndex() const5120b57cec5SDimitry Andric uint32_t IOHandlerEditline::GetCurrentLineIndex() const {
513480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
5140b57cec5SDimitry Andric   if (m_editline_up)
5150b57cec5SDimitry Andric     return m_editline_up->GetCurrentLine();
5160b57cec5SDimitry Andric #endif
5170b57cec5SDimitry Andric   return m_curr_line_idx;
5180b57cec5SDimitry Andric }
5190b57cec5SDimitry Andric 
GetCurrentLines() const5205f757f3fSDimitry Andric StringList IOHandlerEditline::GetCurrentLines() const {
5215f757f3fSDimitry Andric #if LLDB_ENABLE_LIBEDIT
5225f757f3fSDimitry Andric   if (m_editline_up)
5235f757f3fSDimitry Andric     return m_editline_up->GetInputAsStringList();
5245f757f3fSDimitry Andric #endif
5255f757f3fSDimitry Andric   // When libedit is not used, the current lines can be gotten from
5265f757f3fSDimitry Andric   // `m_current_lines_ptr`, which is updated whenever a new line is processed.
5275f757f3fSDimitry Andric   // This doesn't happen when libedit is used, in which case
5285f757f3fSDimitry Andric   // `m_current_lines_ptr` is only updated when the full input is terminated.
5295f757f3fSDimitry Andric 
5305f757f3fSDimitry Andric   if (m_current_lines_ptr)
5315f757f3fSDimitry Andric     return *m_current_lines_ptr;
5325f757f3fSDimitry Andric   return StringList();
5335f757f3fSDimitry Andric }
5345f757f3fSDimitry Andric 
GetLines(StringList & lines,bool & interrupted)5350b57cec5SDimitry Andric bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) {
5360b57cec5SDimitry Andric   m_current_lines_ptr = &lines;
5370b57cec5SDimitry Andric 
5380b57cec5SDimitry Andric   bool success = false;
539480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
5400b57cec5SDimitry Andric   if (m_editline_up) {
5410b57cec5SDimitry Andric     return m_editline_up->GetLines(m_base_line_number, lines, interrupted);
5420b57cec5SDimitry Andric   } else {
5430b57cec5SDimitry Andric #endif
5440b57cec5SDimitry Andric     bool done = false;
5450b57cec5SDimitry Andric     Status error;
5460b57cec5SDimitry Andric 
5470b57cec5SDimitry Andric     while (!done) {
5480b57cec5SDimitry Andric       // Show line numbers if we are asked to
5490b57cec5SDimitry Andric       std::string line;
5500b57cec5SDimitry Andric       if (m_base_line_number > 0 && GetIsInteractive()) {
5519dba64beSDimitry Andric         if (m_output_sp) {
5529dba64beSDimitry Andric           m_output_sp->Printf("%u%s",
5539dba64beSDimitry Andric                               m_base_line_number + (uint32_t)lines.GetSize(),
5540b57cec5SDimitry Andric                               GetPrompt() == nullptr ? " " : "");
5550b57cec5SDimitry Andric         }
5569dba64beSDimitry Andric       }
5570b57cec5SDimitry Andric 
5580b57cec5SDimitry Andric       m_curr_line_idx = lines.GetSize();
5590b57cec5SDimitry Andric 
5600b57cec5SDimitry Andric       bool interrupted = false;
5610b57cec5SDimitry Andric       if (GetLine(line, interrupted) && !interrupted) {
5620b57cec5SDimitry Andric         lines.AppendString(line);
5630b57cec5SDimitry Andric         done = m_delegate.IOHandlerIsInputComplete(*this, lines);
5640b57cec5SDimitry Andric       } else {
5650b57cec5SDimitry Andric         done = true;
5660b57cec5SDimitry Andric       }
5670b57cec5SDimitry Andric     }
5680b57cec5SDimitry Andric     success = lines.GetSize() > 0;
569480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
5700b57cec5SDimitry Andric   }
5710b57cec5SDimitry Andric #endif
5720b57cec5SDimitry Andric   return success;
5730b57cec5SDimitry Andric }
5740b57cec5SDimitry Andric 
5750b57cec5SDimitry Andric // Each IOHandler gets to run until it is done. It should read data from the
5760b57cec5SDimitry Andric // "in" and place output into "out" and "err and return when done.
Run()5770b57cec5SDimitry Andric void IOHandlerEditline::Run() {
5780b57cec5SDimitry Andric   std::string line;
5790b57cec5SDimitry Andric   while (IsActive()) {
5800b57cec5SDimitry Andric     bool interrupted = false;
5810b57cec5SDimitry Andric     if (m_multi_line) {
5820b57cec5SDimitry Andric       StringList lines;
5830b57cec5SDimitry Andric       if (GetLines(lines, interrupted)) {
5840b57cec5SDimitry Andric         if (interrupted) {
5850b57cec5SDimitry Andric           m_done = m_interrupt_exits;
5860b57cec5SDimitry Andric           m_delegate.IOHandlerInputInterrupted(*this, line);
5870b57cec5SDimitry Andric 
5880b57cec5SDimitry Andric         } else {
5890b57cec5SDimitry Andric           line = lines.CopyList();
5900b57cec5SDimitry Andric           m_delegate.IOHandlerInputComplete(*this, line);
5910b57cec5SDimitry Andric         }
5920b57cec5SDimitry Andric       } else {
5930b57cec5SDimitry Andric         m_done = true;
5940b57cec5SDimitry Andric       }
5950b57cec5SDimitry Andric     } else {
5960b57cec5SDimitry Andric       if (GetLine(line, interrupted)) {
5970b57cec5SDimitry Andric         if (interrupted)
5980b57cec5SDimitry Andric           m_delegate.IOHandlerInputInterrupted(*this, line);
5990b57cec5SDimitry Andric         else
6000b57cec5SDimitry Andric           m_delegate.IOHandlerInputComplete(*this, line);
6010b57cec5SDimitry Andric       } else {
6020b57cec5SDimitry Andric         m_done = true;
6030b57cec5SDimitry Andric       }
6040b57cec5SDimitry Andric     }
6050b57cec5SDimitry Andric   }
6060b57cec5SDimitry Andric }
6070b57cec5SDimitry Andric 
Cancel()6080b57cec5SDimitry Andric void IOHandlerEditline::Cancel() {
609480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
6100b57cec5SDimitry Andric   if (m_editline_up)
6110b57cec5SDimitry Andric     m_editline_up->Cancel();
6120b57cec5SDimitry Andric #endif
6130b57cec5SDimitry Andric }
6140b57cec5SDimitry Andric 
Interrupt()6150b57cec5SDimitry Andric bool IOHandlerEditline::Interrupt() {
6160b57cec5SDimitry Andric   // Let the delgate handle it first
6170b57cec5SDimitry Andric   if (m_delegate.IOHandlerInterrupt(*this))
6180b57cec5SDimitry Andric     return true;
6190b57cec5SDimitry Andric 
620480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
6210b57cec5SDimitry Andric   if (m_editline_up)
6220b57cec5SDimitry Andric     return m_editline_up->Interrupt();
6230b57cec5SDimitry Andric #endif
6240b57cec5SDimitry Andric   return false;
6250b57cec5SDimitry Andric }
6260b57cec5SDimitry Andric 
GotEOF()6270b57cec5SDimitry Andric void IOHandlerEditline::GotEOF() {
628480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
6290b57cec5SDimitry Andric   if (m_editline_up)
6300b57cec5SDimitry Andric     m_editline_up->Interrupt();
6310b57cec5SDimitry Andric #endif
6320b57cec5SDimitry Andric }
6330b57cec5SDimitry Andric 
PrintAsync(const char * s,size_t len,bool is_stdout)63481ad6265SDimitry Andric void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) {
635480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
63681ad6265SDimitry Andric   if (m_editline_up) {
63781ad6265SDimitry Andric     std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
63881ad6265SDimitry Andric     lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
63981ad6265SDimitry Andric     m_editline_up->PrintAsync(stream.get(), s, len);
64081ad6265SDimitry Andric   } else
6410b57cec5SDimitry Andric #endif
6420b57cec5SDimitry Andric   {
6439dba64beSDimitry Andric #ifdef _WIN32
6440b57cec5SDimitry Andric     const char *prompt = GetPrompt();
6450b57cec5SDimitry Andric     if (prompt) {
6460b57cec5SDimitry Andric       // Back up over previous prompt using Windows API
6470b57cec5SDimitry Andric       CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
6480b57cec5SDimitry Andric       HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
6490b57cec5SDimitry Andric       GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info);
6500b57cec5SDimitry Andric       COORD coord = screen_buffer_info.dwCursorPosition;
6510b57cec5SDimitry Andric       coord.X -= strlen(prompt);
6520b57cec5SDimitry Andric       if (coord.X < 0)
6530b57cec5SDimitry Andric         coord.X = 0;
6540b57cec5SDimitry Andric       SetConsoleCursorPosition(console_handle, coord);
6550b57cec5SDimitry Andric     }
6560b57cec5SDimitry Andric #endif
65781ad6265SDimitry Andric     IOHandler::PrintAsync(s, len, is_stdout);
6589dba64beSDimitry Andric #ifdef _WIN32
6590b57cec5SDimitry Andric     if (prompt)
66081ad6265SDimitry Andric       IOHandler::PrintAsync(prompt, strlen(prompt), is_stdout);
6610b57cec5SDimitry Andric #endif
6620b57cec5SDimitry Andric   }
6630b57cec5SDimitry Andric }
664