180814287SRaphael Isemann //===-- IOHandler.cpp -----------------------------------------------------===// 244d93782SGreg Clayton // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 644d93782SGreg Clayton // 744d93782SGreg Clayton //===----------------------------------------------------------------------===// 844d93782SGreg Clayton 92f3df613SZachary Turner #include "lldb/Core/IOHandler.h" 102f3df613SZachary Turner 117c9aa073STodd Fiala #if defined(__APPLE__) 127c9aa073STodd Fiala #include <deque> 137c9aa073STodd Fiala #endif 1444d93782SGreg Clayton #include <string> 1544d93782SGreg Clayton 1644d93782SGreg Clayton #include "lldb/Core/Debugger.h" 1744d93782SGreg Clayton #include "lldb/Core/StreamFile.h" 18babbd554SJonas Devlieghere #include "lldb/Host/Config.h" 19672d2c12SJonas Devlieghere #include "lldb/Host/File.h" 20080635efSJonas Devlieghere #include "lldb/Utility/AnsiTerminal.h" 21672d2c12SJonas Devlieghere #include "lldb/Utility/Predicate.h" 22bb894b97SJonas Devlieghere #include "lldb/Utility/ReproducerProvider.h" 23672d2c12SJonas Devlieghere #include "lldb/Utility/Status.h" 24672d2c12SJonas Devlieghere #include "lldb/Utility/StreamString.h" 25672d2c12SJonas Devlieghere #include "lldb/Utility/StringList.h" 26672d2c12SJonas Devlieghere #include "lldb/lldb-forward.h" 272f3df613SZachary Turner 2862456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 2944d93782SGreg Clayton #include "lldb/Host/Editline.h" 30cacde7dfSTodd Fiala #endif 3144d93782SGreg Clayton #include "lldb/Interpreter/CommandCompletions.h" 3244d93782SGreg Clayton #include "lldb/Interpreter/CommandInterpreter.h" 33672d2c12SJonas Devlieghere #include "llvm/ADT/StringRef.h" 342f3df613SZachary Turner 355c82608dSHaibo Huang #ifdef _WIN32 36aaea8ee6SZachary Turner #include "lldb/Host/windows/windows.h" 37fab31220STed Woodward #endif 38fab31220STed Woodward 39672d2c12SJonas Devlieghere #include <memory> 40672d2c12SJonas Devlieghere #include <mutex> 412f3df613SZachary Turner 4276e47d48SRaphael Isemann #include <cassert> 4376e47d48SRaphael Isemann #include <cctype> 4476e47d48SRaphael Isemann #include <cerrno> 4576e47d48SRaphael Isemann #include <clocale> 4676e47d48SRaphael Isemann #include <cstdint> 4776e47d48SRaphael Isemann #include <cstdio> 4876e47d48SRaphael Isemann #include <cstring> 49672d2c12SJonas Devlieghere #include <type_traits> 502f3df613SZachary Turner 5144d93782SGreg Clayton using namespace lldb; 5244d93782SGreg Clayton using namespace lldb_private; 53b3faa01fSLawrence D'Anna using llvm::None; 54b3faa01fSLawrence D'Anna using llvm::Optional; 55b3faa01fSLawrence D'Anna using llvm::StringRef; 56b3faa01fSLawrence D'Anna 57b9c1b51eSKate Stone IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type) 58b9c1b51eSKate Stone : IOHandler(debugger, type, 597ca15ba7SLawrence D'Anna FileSP(), // Adopt STDIN from top input reader 6044d93782SGreg Clayton StreamFileSP(), // Adopt STDOUT from top input reader 61340b0309SGreg Clayton StreamFileSP(), // Adopt STDERR from top input reader 62d77c2e09SJonas Devlieghere 0, // Flags 63d77c2e09SJonas Devlieghere nullptr // Shadow file recorder 64d77c2e09SJonas Devlieghere ) {} 6544d93782SGreg Clayton 66b9c1b51eSKate Stone IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type, 677ca15ba7SLawrence D'Anna const lldb::FileSP &input_sp, 6844d93782SGreg Clayton const lldb::StreamFileSP &output_sp, 69d77c2e09SJonas Devlieghere const lldb::StreamFileSP &error_sp, uint32_t flags, 70d77c2e09SJonas Devlieghere repro::DataRecorder *data_recorder) 71b9c1b51eSKate Stone : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp), 72d77c2e09SJonas Devlieghere m_error_sp(error_sp), m_data_recorder(data_recorder), m_popped(false), 73d77c2e09SJonas Devlieghere m_flags(flags), m_type(type), m_user_data(nullptr), m_done(false), 74d77c2e09SJonas Devlieghere m_active(false) { 7544d93782SGreg Clayton // If any files are not specified, then adopt them from the top input reader. 7644d93782SGreg Clayton if (!m_input_sp || !m_output_sp || !m_error_sp) 77b9c1b51eSKate Stone debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp, 7844d93782SGreg Clayton m_error_sp); 7944d93782SGreg Clayton } 8044d93782SGreg Clayton 81315b6884SEugene Zelenko IOHandler::~IOHandler() = default; 8244d93782SGreg Clayton 83b9c1b51eSKate Stone int IOHandler::GetInputFD() { 847ca15ba7SLawrence D'Anna return (m_input_sp ? m_input_sp->GetDescriptor() : -1); 8544d93782SGreg Clayton } 8644d93782SGreg Clayton 87b9c1b51eSKate Stone int IOHandler::GetOutputFD() { 88c5dac77aSEugene Zelenko return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1); 8944d93782SGreg Clayton } 9044d93782SGreg Clayton 91b9c1b51eSKate Stone int IOHandler::GetErrorFD() { 92c5dac77aSEugene Zelenko return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1); 9344d93782SGreg Clayton } 9444d93782SGreg Clayton 95b9c1b51eSKate Stone FILE *IOHandler::GetInputFILE() { 967ca15ba7SLawrence D'Anna return (m_input_sp ? m_input_sp->GetStream() : nullptr); 9744d93782SGreg Clayton } 9844d93782SGreg Clayton 99b9c1b51eSKate Stone FILE *IOHandler::GetOutputFILE() { 100c5dac77aSEugene Zelenko return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr); 10144d93782SGreg Clayton } 10244d93782SGreg Clayton 103b9c1b51eSKate Stone FILE *IOHandler::GetErrorFILE() { 104c5dac77aSEugene Zelenko return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr); 10544d93782SGreg Clayton } 10644d93782SGreg Clayton 10757dd9274SJonas Devlieghere FileSP IOHandler::GetInputFileSP() { return m_input_sp; } 10844d93782SGreg Clayton 10957dd9274SJonas Devlieghere StreamFileSP IOHandler::GetOutputStreamFileSP() { return m_output_sp; } 11044d93782SGreg Clayton 11157dd9274SJonas Devlieghere StreamFileSP IOHandler::GetErrorStreamFileSP() { return m_error_sp; } 11244d93782SGreg Clayton 113b9c1b51eSKate Stone bool IOHandler::GetIsInteractive() { 1147ca15ba7SLawrence D'Anna return GetInputFileSP() ? GetInputFileSP()->GetIsInteractive() : false; 115340b0309SGreg Clayton } 116340b0309SGreg Clayton 117b9c1b51eSKate Stone bool IOHandler::GetIsRealTerminal() { 1187ca15ba7SLawrence D'Anna return GetInputFileSP() ? GetInputFileSP()->GetIsRealTerminal() : false; 119340b0309SGreg Clayton } 12044d93782SGreg Clayton 121b9c1b51eSKate Stone void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); } 122e30f11d9SKate Stone 123b9c1b51eSKate Stone void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); } 124e30f11d9SKate Stone 1252436c570SJonas Devlieghere void IOHandler::PrintAsync(const char *s, size_t len, bool is_stdout) { 126*8e776bb6SJonas Devlieghere std::lock_guard<std::recursive_mutex> guard(m_output_mutex); 1272436c570SJonas Devlieghere lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp; 1284deea652SPavel Labath stream->Write(s, len); 1292436c570SJonas Devlieghere stream->Flush(); 1304446487dSPavel Labath } 1312436c570SJonas Devlieghere 1322436c570SJonas Devlieghere bool IOHandlerStack::PrintAsync(const char *s, size_t len, bool is_stdout) { 1332436c570SJonas Devlieghere std::lock_guard<std::recursive_mutex> guard(m_mutex); 1342436c570SJonas Devlieghere if (!m_top) 1352436c570SJonas Devlieghere return false; 1362436c570SJonas Devlieghere m_top->PrintAsync(s, len, is_stdout); 1372436c570SJonas Devlieghere return true; 1384446487dSPavel Labath } 1394446487dSPavel Labath 1407a120c8bSZachary Turner IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt, 141b9c1b51eSKate Stone bool default_response) 142b9c1b51eSKate Stone : IOHandlerEditline( 143b9c1b51eSKate Stone debugger, IOHandler::Type::Confirm, 144c5dac77aSEugene Zelenko nullptr, // nullptr editline_name means no history loaded/saved 145514d8cd8SZachary Turner llvm::StringRef(), // No prompt 146514d8cd8SZachary Turner llvm::StringRef(), // No continuation prompt 14744d93782SGreg Clayton false, // Multi-line 148e30f11d9SKate Stone false, // Don't colorize the prompt (i.e. the confirm message.) 149d77c2e09SJonas Devlieghere 0, *this, nullptr), 150b9c1b51eSKate Stone m_default_response(default_response), m_user_response(default_response) { 15144d93782SGreg Clayton StreamString prompt_stream; 15244d93782SGreg Clayton prompt_stream.PutCString(prompt); 15344d93782SGreg Clayton if (m_default_response) 15444d93782SGreg Clayton prompt_stream.Printf(": [Y/n] "); 15544d93782SGreg Clayton else 15644d93782SGreg Clayton prompt_stream.Printf(": [y/N] "); 15744d93782SGreg Clayton 158514d8cd8SZachary Turner SetPrompt(prompt_stream.GetString()); 15944d93782SGreg Clayton } 16044d93782SGreg Clayton 161315b6884SEugene Zelenko IOHandlerConfirm::~IOHandlerConfirm() = default; 16244d93782SGreg Clayton 163ae34ed2cSRaphael Isemann void IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler, 1642fc20f65SRaphael Isemann CompletionRequest &request) { 1651153dc96SRaphael Isemann if (request.GetRawCursorPos() != 0) 1661153dc96SRaphael Isemann return; 1671153dc96SRaphael Isemann request.AddCompletion(m_default_response ? "y" : "n"); 16844d93782SGreg Clayton } 16944d93782SGreg Clayton 170b9c1b51eSKate Stone void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler, 171b9c1b51eSKate Stone std::string &line) { 172b9c1b51eSKate Stone if (line.empty()) { 17344d93782SGreg Clayton // User just hit enter, set the response to the default 17444d93782SGreg Clayton m_user_response = m_default_response; 17544d93782SGreg Clayton io_handler.SetIsDone(true); 17644d93782SGreg Clayton return; 17744d93782SGreg Clayton } 17844d93782SGreg Clayton 179b9c1b51eSKate Stone if (line.size() == 1) { 180b9c1b51eSKate Stone switch (line[0]) { 18144d93782SGreg Clayton case 'y': 18244d93782SGreg Clayton case 'Y': 18344d93782SGreg Clayton m_user_response = true; 18444d93782SGreg Clayton io_handler.SetIsDone(true); 18544d93782SGreg Clayton return; 18644d93782SGreg Clayton case 'n': 18744d93782SGreg Clayton case 'N': 18844d93782SGreg Clayton m_user_response = false; 18944d93782SGreg Clayton io_handler.SetIsDone(true); 19044d93782SGreg Clayton return; 19144d93782SGreg Clayton default: 19244d93782SGreg Clayton break; 19344d93782SGreg Clayton } 19444d93782SGreg Clayton } 19544d93782SGreg Clayton 196b9c1b51eSKate Stone if (line == "yes" || line == "YES" || line == "Yes") { 19744d93782SGreg Clayton m_user_response = true; 19844d93782SGreg Clayton io_handler.SetIsDone(true); 199b9c1b51eSKate Stone } else if (line == "no" || line == "NO" || line == "No") { 20044d93782SGreg Clayton m_user_response = false; 20144d93782SGreg Clayton io_handler.SetIsDone(true); 20244d93782SGreg Clayton } 20344d93782SGreg Clayton } 20444d93782SGreg Clayton 205de9e8502SShu Anzai llvm::Optional<std::string> 206de9e8502SShu Anzai IOHandlerDelegate::IOHandlerSuggestion(IOHandler &io_handler, 207de9e8502SShu Anzai llvm::StringRef line) { 208de9e8502SShu Anzai return io_handler.GetDebugger() 209de9e8502SShu Anzai .GetCommandInterpreter() 210de9e8502SShu Anzai .GetAutoSuggestionForCommand(line); 211de9e8502SShu Anzai } 212de9e8502SShu Anzai 213ae34ed2cSRaphael Isemann void IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler, 2142fc20f65SRaphael Isemann CompletionRequest &request) { 215b9c1b51eSKate Stone switch (m_completion) { 21644d93782SGreg Clayton case Completion::None: 21744d93782SGreg Clayton break; 21844d93782SGreg Clayton case Completion::LLDBCommand: 219ae34ed2cSRaphael Isemann io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(request); 220ae34ed2cSRaphael Isemann break; 221ae34ed2cSRaphael Isemann case Completion::Expression: 222b9c1b51eSKate Stone CommandCompletions::InvokeCommonCompletionCallbacks( 223b9c1b51eSKate Stone io_handler.GetDebugger().GetCommandInterpreter(), 224ae34ed2cSRaphael Isemann CommandCompletions::eVariablePathCompletion, request, nullptr); 225ae34ed2cSRaphael Isemann break; 22644d93782SGreg Clayton } 22744d93782SGreg Clayton } 22844d93782SGreg Clayton 229b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 230b9c1b51eSKate Stone Debugger &debugger, IOHandler::Type type, 23144d93782SGreg Clayton const char *editline_name, // Used for saving history files 232514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 233514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 234d77c2e09SJonas Devlieghere IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder) 235b9c1b51eSKate Stone : IOHandlerEditline(debugger, type, 2367ca15ba7SLawrence D'Anna FileSP(), // Inherit input from top input reader 23744d93782SGreg Clayton StreamFileSP(), // Inherit output from top input reader 23844d93782SGreg Clayton StreamFileSP(), // Inherit error from top input reader 239340b0309SGreg Clayton 0, // Flags 24044d93782SGreg Clayton editline_name, // Used for saving history files 241b9c1b51eSKate Stone prompt, continuation_prompt, multi_line, color_prompts, 242d77c2e09SJonas Devlieghere line_number_start, delegate, data_recorder) {} 24344d93782SGreg Clayton 244b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 2457ca15ba7SLawrence D'Anna Debugger &debugger, IOHandler::Type type, const lldb::FileSP &input_sp, 2467ca15ba7SLawrence D'Anna const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp, 2477ca15ba7SLawrence D'Anna uint32_t flags, 24844d93782SGreg Clayton const char *editline_name, // Used for saving history files 249514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 250514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 251d77c2e09SJonas Devlieghere IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder) 252d77c2e09SJonas Devlieghere : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags, 253d77c2e09SJonas Devlieghere data_recorder), 25462456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 255d5b44036SJonas Devlieghere m_editline_up(), 256cacde7dfSTodd Fiala #endif 257b9c1b51eSKate Stone m_delegate(delegate), m_prompt(), m_continuation_prompt(), 258b9c1b51eSKate Stone m_current_lines_ptr(nullptr), m_base_line_number(line_number_start), 259b9c1b51eSKate Stone m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line), 26032400005SRaphael Isemann m_color_prompts(color_prompts), m_interrupt_exits(true) { 26144d93782SGreg Clayton SetPrompt(prompt); 26244d93782SGreg Clayton 26362456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 264914b8d98SDeepak Panickal bool use_editline = false; 265340b0309SGreg Clayton 266609010d0SLawrence D'Anna use_editline = GetInputFILE() && GetOutputFILE() && GetErrorFILE() && 267609010d0SLawrence D'Anna m_input_sp && m_input_sp->GetIsRealTerminal(); 26844d93782SGreg Clayton 269b9c1b51eSKate Stone if (use_editline) { 270*8e776bb6SJonas Devlieghere m_editline_up = std::make_unique<Editline>( 271*8e776bb6SJonas Devlieghere editline_name, GetInputFILE(), GetOutputFILE(), GetErrorFILE(), 272*8e776bb6SJonas Devlieghere GetOutputMutex(), m_color_prompts); 2735826aa48SNeal (nealsid) m_editline_up->SetIsInputCompleteCallback( 2745826aa48SNeal (nealsid) [this](Editline *editline, StringList &lines) { 2755826aa48SNeal (nealsid) return this->IsInputCompleteCallback(editline, lines); 2765826aa48SNeal (nealsid) }); 2775826aa48SNeal (nealsid) 2785826aa48SNeal (nealsid) m_editline_up->SetAutoCompleteCallback([this](CompletionRequest &request) { 2795826aa48SNeal (nealsid) this->AutoCompleteCallback(request); 2805826aa48SNeal (nealsid) }); 2815826aa48SNeal (nealsid) 282080635efSJonas Devlieghere if (debugger.GetUseAutosuggestion()) { 2835826aa48SNeal (nealsid) m_editline_up->SetSuggestionCallback([this](llvm::StringRef line) { 2845826aa48SNeal (nealsid) return this->SuggestionCallback(line); 2855826aa48SNeal (nealsid) }); 286080635efSJonas Devlieghere m_editline_up->SetSuggestionAnsiPrefix(ansi::FormatAnsiTerminalCodes( 287080635efSJonas Devlieghere debugger.GetAutosuggestionAnsiPrefix())); 288080635efSJonas Devlieghere m_editline_up->SetSuggestionAnsiSuffix(ansi::FormatAnsiTerminalCodes( 289080635efSJonas Devlieghere debugger.GetAutosuggestionAnsiSuffix())); 2905826aa48SNeal (nealsid) } 291e30f11d9SKate Stone // See if the delegate supports fixing indentation 292e30f11d9SKate Stone const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters(); 293b9c1b51eSKate Stone if (indent_chars) { 294b9c1b51eSKate Stone // The delegate does support indentation, hook it up so when any 29505097246SAdrian Prantl // indentation character is typed, the delegate gets a chance to fix it 2965826aa48SNeal (nealsid) FixIndentationCallbackType f = [this](Editline *editline, 2975826aa48SNeal (nealsid) const StringList &lines, 2985826aa48SNeal (nealsid) int cursor_position) { 2995826aa48SNeal (nealsid) return this->FixIndentationCallback(editline, lines, cursor_position); 3005826aa48SNeal (nealsid) }; 3015826aa48SNeal (nealsid) m_editline_up->SetFixIndentationCallback(std::move(f), indent_chars); 302e30f11d9SKate Stone } 30344d93782SGreg Clayton } 304cacde7dfSTodd Fiala #endif 305e30f11d9SKate Stone SetBaseLineNumber(m_base_line_number); 306514d8cd8SZachary Turner SetPrompt(prompt); 307e30f11d9SKate Stone SetContinuationPrompt(continuation_prompt); 30844d93782SGreg Clayton } 30944d93782SGreg Clayton 310b9c1b51eSKate Stone IOHandlerEditline::~IOHandlerEditline() { 31162456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 312d5b44036SJonas Devlieghere m_editline_up.reset(); 313cacde7dfSTodd Fiala #endif 31444d93782SGreg Clayton } 31544d93782SGreg Clayton 316b9c1b51eSKate Stone void IOHandlerEditline::Activate() { 317e30f11d9SKate Stone IOHandler::Activate(); 3180affb582SDave Lee m_delegate.IOHandlerActivated(*this, GetIsInteractive()); 319e30f11d9SKate Stone } 320e30f11d9SKate Stone 321b9c1b51eSKate Stone void IOHandlerEditline::Deactivate() { 322e30f11d9SKate Stone IOHandler::Deactivate(); 323e30f11d9SKate Stone m_delegate.IOHandlerDeactivated(*this); 324e30f11d9SKate Stone } 325e30f11d9SKate Stone 326d9166ad2SFred Riss void IOHandlerEditline::TerminalSizeChanged() { 327d9166ad2SFred Riss #if LLDB_ENABLE_LIBEDIT 328be18df3dSRaphael Isemann if (m_editline_up) 329d9166ad2SFred Riss m_editline_up->TerminalSizeChanged(); 330d9166ad2SFred Riss #endif 331d9166ad2SFred Riss } 332d9166ad2SFred Riss 333b3faa01fSLawrence D'Anna // Split out a line from the buffer, if there is a full one to get. 334b3faa01fSLawrence D'Anna static Optional<std::string> SplitLine(std::string &line_buffer) { 335b3faa01fSLawrence D'Anna size_t pos = line_buffer.find('\n'); 336b3faa01fSLawrence D'Anna if (pos == std::string::npos) 337b3faa01fSLawrence D'Anna return None; 338adcd0268SBenjamin Kramer std::string line = 339adcd0268SBenjamin Kramer std::string(StringRef(line_buffer.c_str(), pos).rtrim("\n\r")); 340b3faa01fSLawrence D'Anna line_buffer = line_buffer.substr(pos + 1); 341b3faa01fSLawrence D'Anna return line; 342b3faa01fSLawrence D'Anna } 343b3faa01fSLawrence D'Anna 344b3faa01fSLawrence D'Anna // If the final line of the file ends without a end-of-line, return 345b3faa01fSLawrence D'Anna // it as a line anyway. 346b3faa01fSLawrence D'Anna static Optional<std::string> SplitLineEOF(std::string &line_buffer) { 347f5eaa2afSRaphael Isemann if (llvm::all_of(line_buffer, llvm::isSpace)) 348b3faa01fSLawrence D'Anna return None; 349b3faa01fSLawrence D'Anna std::string line = std::move(line_buffer); 350b3faa01fSLawrence D'Anna line_buffer.clear(); 351b3faa01fSLawrence D'Anna return line; 352b3faa01fSLawrence D'Anna } 353b3faa01fSLawrence D'Anna 354b9c1b51eSKate Stone bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) { 35562456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 356d5b44036SJonas Devlieghere if (m_editline_up) { 357d77c2e09SJonas Devlieghere bool b = m_editline_up->GetLine(line, interrupted); 358b3faa01fSLawrence D'Anna if (b && m_data_recorder) 359d77c2e09SJonas Devlieghere m_data_recorder->Record(line, true); 360d77c2e09SJonas Devlieghere return b; 361b3faa01fSLawrence D'Anna } 362cacde7dfSTodd Fiala #endif 363b3faa01fSLawrence D'Anna 36444d93782SGreg Clayton line.clear(); 36544d93782SGreg Clayton 366b9c1b51eSKate Stone if (GetIsInteractive()) { 367c5dac77aSEugene Zelenko const char *prompt = nullptr; 368e30f11d9SKate Stone 369e30f11d9SKate Stone if (m_multi_line && m_curr_line_idx > 0) 370e30f11d9SKate Stone prompt = GetContinuationPrompt(); 371e30f11d9SKate Stone 372c5dac77aSEugene Zelenko if (prompt == nullptr) 373e30f11d9SKate Stone prompt = GetPrompt(); 374e30f11d9SKate Stone 375b9c1b51eSKate Stone if (prompt && prompt[0]) { 3765da2bc22SLawrence D'Anna if (m_output_sp) { 3775da2bc22SLawrence D'Anna m_output_sp->Printf("%s", prompt); 3785da2bc22SLawrence D'Anna m_output_sp->Flush(); 37944d93782SGreg Clayton } 38044d93782SGreg Clayton } 38144d93782SGreg Clayton } 382b3faa01fSLawrence D'Anna 383b3faa01fSLawrence D'Anna Optional<std::string> got_line = SplitLine(m_line_buffer); 384b3faa01fSLawrence D'Anna 385b3faa01fSLawrence D'Anna if (!got_line && !m_input_sp) { 386b3faa01fSLawrence D'Anna // No more input file, we are done... 387b3faa01fSLawrence D'Anna SetIsDone(true); 388b3faa01fSLawrence D'Anna return false; 389b3faa01fSLawrence D'Anna } 390b3faa01fSLawrence D'Anna 391b3faa01fSLawrence D'Anna FILE *in = GetInputFILE(); 39244d93782SGreg Clayton char buffer[256]; 393b3faa01fSLawrence D'Anna 394b3faa01fSLawrence D'Anna if (!got_line && !in && m_input_sp) { 395b3faa01fSLawrence D'Anna // there is no FILE*, fall back on just reading bytes from the stream. 396b3faa01fSLawrence D'Anna while (!got_line) { 397b3faa01fSLawrence D'Anna size_t bytes_read = sizeof(buffer); 398b3faa01fSLawrence D'Anna Status error = m_input_sp->Read((void *)buffer, bytes_read); 399b3faa01fSLawrence D'Anna if (error.Success() && !bytes_read) { 400b3faa01fSLawrence D'Anna got_line = SplitLineEOF(m_line_buffer); 401b3faa01fSLawrence D'Anna break; 402b3faa01fSLawrence D'Anna } 403b3faa01fSLawrence D'Anna if (error.Fail()) 404b3faa01fSLawrence D'Anna break; 405b3faa01fSLawrence D'Anna m_line_buffer += StringRef(buffer, bytes_read); 406b3faa01fSLawrence D'Anna got_line = SplitLine(m_line_buffer); 407b3faa01fSLawrence D'Anna } 408b3faa01fSLawrence D'Anna } 409b3faa01fSLawrence D'Anna 410b3faa01fSLawrence D'Anna if (!got_line && in) { 411b3faa01fSLawrence D'Anna while (!got_line) { 412b3faa01fSLawrence D'Anna char *r = fgets(buffer, sizeof(buffer), in); 413e7167908SNathan Lanza #ifdef _WIN32 414e7167908SNathan Lanza // ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED 415e7167908SNathan Lanza // according to the docs on MSDN. However, this has evidently been a 416e7167908SNathan Lanza // known bug since Windows 8. Therefore, we can't detect if a signal 417e7167908SNathan Lanza // interrupted in the fgets. So pressing ctrl-c causes the repl to end 418e7167908SNathan Lanza // and the process to exit. A temporary workaround is just to attempt to 419e7167908SNathan Lanza // fgets twice until this bug is fixed. 420b3faa01fSLawrence D'Anna if (r == nullptr) 421b3faa01fSLawrence D'Anna r = fgets(buffer, sizeof(buffer), in); 422cb305205SNathan Lanza // this is the equivalent of EINTR for Windows 423b3faa01fSLawrence D'Anna if (r == nullptr && GetLastError() == ERROR_OPERATION_ABORTED) 424cb305205SNathan Lanza continue; 425e7167908SNathan Lanza #endif 426b3faa01fSLawrence D'Anna if (r == nullptr) { 427b3faa01fSLawrence D'Anna if (ferror(in) && errno == EINTR) 428b3faa01fSLawrence D'Anna continue; 429c9cf5798SGreg Clayton if (feof(in)) 430b3faa01fSLawrence D'Anna got_line = SplitLineEOF(m_line_buffer); 43144d93782SGreg Clayton break; 43244d93782SGreg Clayton } 433b3faa01fSLawrence D'Anna m_line_buffer += buffer; 434b3faa01fSLawrence D'Anna got_line = SplitLine(m_line_buffer); 43544d93782SGreg Clayton } 436b3faa01fSLawrence D'Anna } 437b3faa01fSLawrence D'Anna 438b3faa01fSLawrence D'Anna if (got_line) { 439b3faa01fSLawrence D'Anna line = got_line.getValue(); 440b3faa01fSLawrence D'Anna if (m_data_recorder) 441d77c2e09SJonas Devlieghere m_data_recorder->Record(line, true); 44244d93782SGreg Clayton } 443b3faa01fSLawrence D'Anna 444b3faa01fSLawrence D'Anna return (bool)got_line; 44544d93782SGreg Clayton } 44644d93782SGreg Clayton 44762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 448b9c1b51eSKate Stone bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline, 4495826aa48SNeal (nealsid) StringList &lines) { 4505826aa48SNeal (nealsid) return m_delegate.IOHandlerIsInputComplete(*this, lines); 451e30f11d9SKate Stone } 452e30f11d9SKate Stone 453b9c1b51eSKate Stone int IOHandlerEditline::FixIndentationCallback(Editline *editline, 454e30f11d9SKate Stone const StringList &lines, 4555826aa48SNeal (nealsid) int cursor_position) { 4565826aa48SNeal (nealsid) return m_delegate.IOHandlerFixIndentation(*this, lines, cursor_position); 45744d93782SGreg Clayton } 45844d93782SGreg Clayton 459de9e8502SShu Anzai llvm::Optional<std::string> 4605826aa48SNeal (nealsid) IOHandlerEditline::SuggestionCallback(llvm::StringRef line) { 4615826aa48SNeal (nealsid) return m_delegate.IOHandlerSuggestion(*this, line); 462de9e8502SShu Anzai } 463de9e8502SShu Anzai 4645826aa48SNeal (nealsid) void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request) { 4655826aa48SNeal (nealsid) m_delegate.IOHandlerComplete(*this, request); 46644d93782SGreg Clayton } 467cacde7dfSTodd Fiala #endif 46844d93782SGreg Clayton 469b9c1b51eSKate Stone const char *IOHandlerEditline::GetPrompt() { 47062456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 471d5b44036SJonas Devlieghere if (m_editline_up) { 472d5b44036SJonas Devlieghere return m_editline_up->GetPrompt(); 473b9c1b51eSKate Stone } else { 474cacde7dfSTodd Fiala #endif 475cacde7dfSTodd Fiala if (m_prompt.empty()) 476c5dac77aSEugene Zelenko return nullptr; 47762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 478cacde7dfSTodd Fiala } 479cacde7dfSTodd Fiala #endif 48044d93782SGreg Clayton return m_prompt.c_str(); 48144d93782SGreg Clayton } 48244d93782SGreg Clayton 483514d8cd8SZachary Turner bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) { 484adcd0268SBenjamin Kramer m_prompt = std::string(prompt); 485514d8cd8SZachary Turner 48662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 487d5b44036SJonas Devlieghere if (m_editline_up) 488d5b44036SJonas Devlieghere m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str()); 489cacde7dfSTodd Fiala #endif 49044d93782SGreg Clayton return true; 49144d93782SGreg Clayton } 49244d93782SGreg Clayton 493b9c1b51eSKate Stone const char *IOHandlerEditline::GetContinuationPrompt() { 494b9c1b51eSKate Stone return (m_continuation_prompt.empty() ? nullptr 495b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 496e30f11d9SKate Stone } 497e30f11d9SKate Stone 498514d8cd8SZachary Turner void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) { 499adcd0268SBenjamin Kramer m_continuation_prompt = std::string(prompt); 500e30f11d9SKate Stone 50162456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 502d5b44036SJonas Devlieghere if (m_editline_up) 503d5b44036SJonas Devlieghere m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty() 504b9c1b51eSKate Stone ? nullptr 505b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 506d553d00cSZachary Turner #endif 507e30f11d9SKate Stone } 508e30f11d9SKate Stone 509b9c1b51eSKate Stone void IOHandlerEditline::SetBaseLineNumber(uint32_t line) { 510f6913cd7SGreg Clayton m_base_line_number = line; 511f6913cd7SGreg Clayton } 512e30f11d9SKate Stone 513b9c1b51eSKate Stone uint32_t IOHandlerEditline::GetCurrentLineIndex() const { 51462456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 515d5b44036SJonas Devlieghere if (m_editline_up) 516d5b44036SJonas Devlieghere return m_editline_up->GetCurrentLine(); 517e30f11d9SKate Stone #endif 518e30f11d9SKate Stone return m_curr_line_idx; 519e30f11d9SKate Stone } 520e30f11d9SKate Stone 521b9c1b51eSKate Stone bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) { 522e30f11d9SKate Stone m_current_lines_ptr = &lines; 523e30f11d9SKate Stone 52444d93782SGreg Clayton bool success = false; 52562456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 526d5b44036SJonas Devlieghere if (m_editline_up) { 527d5b44036SJonas Devlieghere return m_editline_up->GetLines(m_base_line_number, lines, interrupted); 528b9c1b51eSKate Stone } else { 529cacde7dfSTodd Fiala #endif 530e30f11d9SKate Stone bool done = false; 53197206d57SZachary Turner Status error; 53244d93782SGreg Clayton 533b9c1b51eSKate Stone while (!done) { 534f6913cd7SGreg Clayton // Show line numbers if we are asked to 53544d93782SGreg Clayton std::string line; 536b9c1b51eSKate Stone if (m_base_line_number > 0 && GetIsInteractive()) { 5375da2bc22SLawrence D'Anna if (m_output_sp) { 5385da2bc22SLawrence D'Anna m_output_sp->Printf("%u%s", 5395da2bc22SLawrence D'Anna m_base_line_number + (uint32_t)lines.GetSize(), 540b9c1b51eSKate Stone GetPrompt() == nullptr ? " " : ""); 541f6913cd7SGreg Clayton } 5425da2bc22SLawrence D'Anna } 543f6913cd7SGreg Clayton 544e30f11d9SKate Stone m_curr_line_idx = lines.GetSize(); 545e30f11d9SKate Stone 546f0066ad0SGreg Clayton bool interrupted = false; 547b9c1b51eSKate Stone if (GetLine(line, interrupted) && !interrupted) { 54844d93782SGreg Clayton lines.AppendString(line); 549e30f11d9SKate Stone done = m_delegate.IOHandlerIsInputComplete(*this, lines); 550b9c1b51eSKate Stone } else { 551e30f11d9SKate Stone done = true; 55244d93782SGreg Clayton } 55344d93782SGreg Clayton } 55444d93782SGreg Clayton success = lines.GetSize() > 0; 55562456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 55644d93782SGreg Clayton } 557cacde7dfSTodd Fiala #endif 55844d93782SGreg Clayton return success; 55944d93782SGreg Clayton } 56044d93782SGreg Clayton 56105097246SAdrian Prantl // Each IOHandler gets to run until it is done. It should read data from the 56205097246SAdrian Prantl // "in" and place output into "out" and "err and return when done. 563b9c1b51eSKate Stone void IOHandlerEditline::Run() { 56444d93782SGreg Clayton std::string line; 565b9c1b51eSKate Stone while (IsActive()) { 566f0066ad0SGreg Clayton bool interrupted = false; 567b9c1b51eSKate Stone if (m_multi_line) { 56844d93782SGreg Clayton StringList lines; 569b9c1b51eSKate Stone if (GetLines(lines, interrupted)) { 570b9c1b51eSKate Stone if (interrupted) { 571e30f11d9SKate Stone m_done = m_interrupt_exits; 572e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 573e30f11d9SKate Stone 574b9c1b51eSKate Stone } else { 57544d93782SGreg Clayton line = lines.CopyList(); 57644d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 57744d93782SGreg Clayton } 578b9c1b51eSKate Stone } else { 57944d93782SGreg Clayton m_done = true; 58044d93782SGreg Clayton } 581b9c1b51eSKate Stone } else { 582b9c1b51eSKate Stone if (GetLine(line, interrupted)) { 583e30f11d9SKate Stone if (interrupted) 584e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 585e30f11d9SKate Stone else 58644d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 587b9c1b51eSKate Stone } else { 58844d93782SGreg Clayton m_done = true; 58944d93782SGreg Clayton } 59044d93782SGreg Clayton } 59144d93782SGreg Clayton } 59244d93782SGreg Clayton } 59344d93782SGreg Clayton 594b9c1b51eSKate Stone void IOHandlerEditline::Cancel() { 59562456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 596d5b44036SJonas Devlieghere if (m_editline_up) 597d5b44036SJonas Devlieghere m_editline_up->Cancel(); 598cacde7dfSTodd Fiala #endif 599e68f5d6bSGreg Clayton } 600e68f5d6bSGreg Clayton 601b9c1b51eSKate Stone bool IOHandlerEditline::Interrupt() { 602f0066ad0SGreg Clayton // Let the delgate handle it first 603f0066ad0SGreg Clayton if (m_delegate.IOHandlerInterrupt(*this)) 604f0066ad0SGreg Clayton return true; 605f0066ad0SGreg Clayton 60662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 607d5b44036SJonas Devlieghere if (m_editline_up) 608d5b44036SJonas Devlieghere return m_editline_up->Interrupt(); 609cacde7dfSTodd Fiala #endif 610f0066ad0SGreg Clayton return false; 61144d93782SGreg Clayton } 61244d93782SGreg Clayton 613b9c1b51eSKate Stone void IOHandlerEditline::GotEOF() { 61462456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 615d5b44036SJonas Devlieghere if (m_editline_up) 616d5b44036SJonas Devlieghere m_editline_up->Interrupt(); 617cacde7dfSTodd Fiala #endif 61844d93782SGreg Clayton } 61944d93782SGreg Clayton 6202436c570SJonas Devlieghere void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) { 62162456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 6222436c570SJonas Devlieghere if (m_editline_up) { 623*8e776bb6SJonas Devlieghere std::lock_guard<std::recursive_mutex> guard(m_output_mutex); 6242436c570SJonas Devlieghere lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp; 6252436c570SJonas Devlieghere m_editline_up->PrintAsync(stream.get(), s, len); 6262436c570SJonas Devlieghere } else 6274446487dSPavel Labath #endif 628fab31220STed Woodward { 6298b98f12aSMartin Storsjo #ifdef _WIN32 630341e4789SDawn Perchik const char *prompt = GetPrompt(); 631b9c1b51eSKate Stone if (prompt) { 632fab31220STed Woodward // Back up over previous prompt using Windows API 633fab31220STed Woodward CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; 634fab31220STed Woodward HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); 635fab31220STed Woodward GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info); 636fab31220STed Woodward COORD coord = screen_buffer_info.dwCursorPosition; 637fab31220STed Woodward coord.X -= strlen(prompt); 638fab31220STed Woodward if (coord.X < 0) 639fab31220STed Woodward coord.X = 0; 640fab31220STed Woodward SetConsoleCursorPosition(console_handle, coord); 641fab31220STed Woodward } 642fab31220STed Woodward #endif 6432436c570SJonas Devlieghere IOHandler::PrintAsync(s, len, is_stdout); 6448b98f12aSMartin Storsjo #ifdef _WIN32 645fab31220STed Woodward if (prompt) 646b0a76b01SJonas Devlieghere IOHandler::PrintAsync(prompt, strlen(prompt), is_stdout); 647341e4789SDawn Perchik #endif 648fab31220STed Woodward } 6494446487dSPavel Labath } 650