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" 20672d2c12SJonas Devlieghere #include "lldb/Utility/Predicate.h" 21672d2c12SJonas Devlieghere #include "lldb/Utility/Status.h" 22672d2c12SJonas Devlieghere #include "lldb/Utility/StreamString.h" 23672d2c12SJonas Devlieghere #include "lldb/Utility/StringList.h" 24672d2c12SJonas Devlieghere #include "lldb/lldb-forward.h" 252f3df613SZachary Turner 2662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 2744d93782SGreg Clayton #include "lldb/Host/Editline.h" 28cacde7dfSTodd Fiala #endif 2944d93782SGreg Clayton #include "lldb/Interpreter/CommandCompletions.h" 3044d93782SGreg Clayton #include "lldb/Interpreter/CommandInterpreter.h" 31672d2c12SJonas Devlieghere #include "llvm/ADT/StringRef.h" 322f3df613SZachary Turner 335c82608dSHaibo Huang #ifdef _WIN32 34aaea8ee6SZachary Turner #include "lldb/Host/windows/windows.h" 35fab31220STed Woodward #endif 36fab31220STed Woodward 37672d2c12SJonas Devlieghere #include <memory> 38672d2c12SJonas Devlieghere #include <mutex> 392f3df613SZachary Turner 40672d2c12SJonas Devlieghere #include <assert.h> 41672d2c12SJonas Devlieghere #include <ctype.h> 42672d2c12SJonas Devlieghere #include <errno.h> 43672d2c12SJonas Devlieghere #include <locale.h> 44672d2c12SJonas Devlieghere #include <stdint.h> 45672d2c12SJonas Devlieghere #include <stdio.h> 46672d2c12SJonas Devlieghere #include <string.h> 47672d2c12SJonas Devlieghere #include <type_traits> 482f3df613SZachary Turner 4944d93782SGreg Clayton using namespace lldb; 5044d93782SGreg Clayton using namespace lldb_private; 51b3faa01fSLawrence D'Anna using llvm::None; 52b3faa01fSLawrence D'Anna using llvm::Optional; 53b3faa01fSLawrence D'Anna using llvm::StringRef; 54b3faa01fSLawrence D'Anna 55b9c1b51eSKate Stone IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type) 56b9c1b51eSKate Stone : IOHandler(debugger, type, 577ca15ba7SLawrence D'Anna FileSP(), // Adopt STDIN from top input reader 5844d93782SGreg Clayton StreamFileSP(), // Adopt STDOUT from top input reader 59340b0309SGreg Clayton StreamFileSP(), // Adopt STDERR from top input reader 60d77c2e09SJonas Devlieghere 0, // Flags 61d77c2e09SJonas Devlieghere nullptr // Shadow file recorder 62d77c2e09SJonas Devlieghere ) {} 6344d93782SGreg Clayton 64b9c1b51eSKate Stone IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type, 657ca15ba7SLawrence D'Anna const lldb::FileSP &input_sp, 6644d93782SGreg Clayton const lldb::StreamFileSP &output_sp, 67d77c2e09SJonas Devlieghere const lldb::StreamFileSP &error_sp, uint32_t flags, 68d77c2e09SJonas Devlieghere repro::DataRecorder *data_recorder) 69b9c1b51eSKate Stone : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp), 70d77c2e09SJonas Devlieghere m_error_sp(error_sp), m_data_recorder(data_recorder), m_popped(false), 71d77c2e09SJonas Devlieghere m_flags(flags), m_type(type), m_user_data(nullptr), m_done(false), 72d77c2e09SJonas Devlieghere m_active(false) { 7344d93782SGreg Clayton // If any files are not specified, then adopt them from the top input reader. 7444d93782SGreg Clayton if (!m_input_sp || !m_output_sp || !m_error_sp) 75b9c1b51eSKate Stone debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp, 7644d93782SGreg Clayton m_error_sp); 7744d93782SGreg Clayton } 7844d93782SGreg Clayton 79315b6884SEugene Zelenko IOHandler::~IOHandler() = default; 8044d93782SGreg Clayton 81b9c1b51eSKate Stone int IOHandler::GetInputFD() { 827ca15ba7SLawrence D'Anna return (m_input_sp ? m_input_sp->GetDescriptor() : -1); 8344d93782SGreg Clayton } 8444d93782SGreg Clayton 85b9c1b51eSKate Stone int IOHandler::GetOutputFD() { 86c5dac77aSEugene Zelenko return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1); 8744d93782SGreg Clayton } 8844d93782SGreg Clayton 89b9c1b51eSKate Stone int IOHandler::GetErrorFD() { 90c5dac77aSEugene Zelenko return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1); 9144d93782SGreg Clayton } 9244d93782SGreg Clayton 93b9c1b51eSKate Stone FILE *IOHandler::GetInputFILE() { 947ca15ba7SLawrence D'Anna return (m_input_sp ? m_input_sp->GetStream() : nullptr); 9544d93782SGreg Clayton } 9644d93782SGreg Clayton 97b9c1b51eSKate Stone FILE *IOHandler::GetOutputFILE() { 98c5dac77aSEugene Zelenko return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr); 9944d93782SGreg Clayton } 10044d93782SGreg Clayton 101b9c1b51eSKate Stone FILE *IOHandler::GetErrorFILE() { 102c5dac77aSEugene Zelenko return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr); 10344d93782SGreg Clayton } 10444d93782SGreg Clayton 1057ca15ba7SLawrence D'Anna FileSP &IOHandler::GetInputFileSP() { return m_input_sp; } 10644d93782SGreg Clayton 1077ca15ba7SLawrence D'Anna StreamFileSP &IOHandler::GetOutputStreamFileSP() { return m_output_sp; } 10844d93782SGreg Clayton 1097ca15ba7SLawrence D'Anna StreamFileSP &IOHandler::GetErrorStreamFileSP() { return m_error_sp; } 11044d93782SGreg Clayton 111b9c1b51eSKate Stone bool IOHandler::GetIsInteractive() { 1127ca15ba7SLawrence D'Anna return GetInputFileSP() ? GetInputFileSP()->GetIsInteractive() : false; 113340b0309SGreg Clayton } 114340b0309SGreg Clayton 115b9c1b51eSKate Stone bool IOHandler::GetIsRealTerminal() { 1167ca15ba7SLawrence D'Anna return GetInputFileSP() ? GetInputFileSP()->GetIsRealTerminal() : false; 117340b0309SGreg Clayton } 11844d93782SGreg Clayton 119b9c1b51eSKate Stone void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); } 120e30f11d9SKate Stone 121b9c1b51eSKate Stone void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); } 122e30f11d9SKate Stone 123b9c1b51eSKate Stone void IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len) { 124b9c1b51eSKate Stone if (stream) { 12516ff8604SSaleem Abdulrasool std::lock_guard<std::recursive_mutex> guard(m_mutex); 1264446487dSPavel Labath if (m_top) 1274446487dSPavel Labath m_top->PrintAsync(stream, s, len); 1284deea652SPavel Labath else 1294deea652SPavel Labath stream->Write(s, len); 1304446487dSPavel Labath } 1314446487dSPavel Labath } 1324446487dSPavel Labath 1337a120c8bSZachary Turner IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt, 134b9c1b51eSKate Stone bool default_response) 135b9c1b51eSKate Stone : IOHandlerEditline( 136b9c1b51eSKate Stone debugger, IOHandler::Type::Confirm, 137c5dac77aSEugene Zelenko nullptr, // nullptr editline_name means no history loaded/saved 138514d8cd8SZachary Turner llvm::StringRef(), // No prompt 139514d8cd8SZachary Turner llvm::StringRef(), // No continuation prompt 14044d93782SGreg Clayton false, // Multi-line 141e30f11d9SKate Stone false, // Don't colorize the prompt (i.e. the confirm message.) 142d77c2e09SJonas Devlieghere 0, *this, nullptr), 143b9c1b51eSKate Stone m_default_response(default_response), m_user_response(default_response) { 14444d93782SGreg Clayton StreamString prompt_stream; 14544d93782SGreg Clayton prompt_stream.PutCString(prompt); 14644d93782SGreg Clayton if (m_default_response) 14744d93782SGreg Clayton prompt_stream.Printf(": [Y/n] "); 14844d93782SGreg Clayton else 14944d93782SGreg Clayton prompt_stream.Printf(": [y/N] "); 15044d93782SGreg Clayton 151514d8cd8SZachary Turner SetPrompt(prompt_stream.GetString()); 15244d93782SGreg Clayton } 15344d93782SGreg Clayton 154315b6884SEugene Zelenko IOHandlerConfirm::~IOHandlerConfirm() = default; 15544d93782SGreg Clayton 156ae34ed2cSRaphael Isemann void IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler, 1572fc20f65SRaphael Isemann CompletionRequest &request) { 1581153dc96SRaphael Isemann if (request.GetRawCursorPos() != 0) 1591153dc96SRaphael Isemann return; 1601153dc96SRaphael Isemann request.AddCompletion(m_default_response ? "y" : "n"); 16144d93782SGreg Clayton } 16244d93782SGreg Clayton 163b9c1b51eSKate Stone void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler, 164b9c1b51eSKate Stone std::string &line) { 165b9c1b51eSKate Stone if (line.empty()) { 16644d93782SGreg Clayton // User just hit enter, set the response to the default 16744d93782SGreg Clayton m_user_response = m_default_response; 16844d93782SGreg Clayton io_handler.SetIsDone(true); 16944d93782SGreg Clayton return; 17044d93782SGreg Clayton } 17144d93782SGreg Clayton 172b9c1b51eSKate Stone if (line.size() == 1) { 173b9c1b51eSKate Stone switch (line[0]) { 17444d93782SGreg Clayton case 'y': 17544d93782SGreg Clayton case 'Y': 17644d93782SGreg Clayton m_user_response = true; 17744d93782SGreg Clayton io_handler.SetIsDone(true); 17844d93782SGreg Clayton return; 17944d93782SGreg Clayton case 'n': 18044d93782SGreg Clayton case 'N': 18144d93782SGreg Clayton m_user_response = false; 18244d93782SGreg Clayton io_handler.SetIsDone(true); 18344d93782SGreg Clayton return; 18444d93782SGreg Clayton default: 18544d93782SGreg Clayton break; 18644d93782SGreg Clayton } 18744d93782SGreg Clayton } 18844d93782SGreg Clayton 189b9c1b51eSKate Stone if (line == "yes" || line == "YES" || line == "Yes") { 19044d93782SGreg Clayton m_user_response = true; 19144d93782SGreg Clayton io_handler.SetIsDone(true); 192b9c1b51eSKate Stone } else if (line == "no" || line == "NO" || line == "No") { 19344d93782SGreg Clayton m_user_response = false; 19444d93782SGreg Clayton io_handler.SetIsDone(true); 19544d93782SGreg Clayton } 19644d93782SGreg Clayton } 19744d93782SGreg Clayton 198ae34ed2cSRaphael Isemann void IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler, 1992fc20f65SRaphael Isemann CompletionRequest &request) { 200b9c1b51eSKate Stone switch (m_completion) { 20144d93782SGreg Clayton case Completion::None: 20244d93782SGreg Clayton break; 20344d93782SGreg Clayton case Completion::LLDBCommand: 204ae34ed2cSRaphael Isemann io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(request); 205ae34ed2cSRaphael Isemann break; 206ae34ed2cSRaphael Isemann case Completion::Expression: 207b9c1b51eSKate Stone CommandCompletions::InvokeCommonCompletionCallbacks( 208b9c1b51eSKate Stone io_handler.GetDebugger().GetCommandInterpreter(), 209ae34ed2cSRaphael Isemann CommandCompletions::eVariablePathCompletion, request, nullptr); 210ae34ed2cSRaphael Isemann break; 21144d93782SGreg Clayton } 21244d93782SGreg Clayton } 21344d93782SGreg Clayton 214b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 215b9c1b51eSKate Stone Debugger &debugger, IOHandler::Type type, 21644d93782SGreg Clayton const char *editline_name, // Used for saving history files 217514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 218514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 219d77c2e09SJonas Devlieghere IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder) 220b9c1b51eSKate Stone : IOHandlerEditline(debugger, type, 2217ca15ba7SLawrence D'Anna FileSP(), // Inherit input from top input reader 22244d93782SGreg Clayton StreamFileSP(), // Inherit output from top input reader 22344d93782SGreg Clayton StreamFileSP(), // Inherit error from top input reader 224340b0309SGreg Clayton 0, // Flags 22544d93782SGreg Clayton editline_name, // Used for saving history files 226b9c1b51eSKate Stone prompt, continuation_prompt, multi_line, color_prompts, 227d77c2e09SJonas Devlieghere line_number_start, delegate, data_recorder) {} 22844d93782SGreg Clayton 229b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 2307ca15ba7SLawrence D'Anna Debugger &debugger, IOHandler::Type type, const lldb::FileSP &input_sp, 2317ca15ba7SLawrence D'Anna const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp, 2327ca15ba7SLawrence D'Anna uint32_t flags, 23344d93782SGreg Clayton const char *editline_name, // Used for saving history files 234514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 235514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 236d77c2e09SJonas Devlieghere IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder) 237d77c2e09SJonas Devlieghere : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags, 238d77c2e09SJonas Devlieghere data_recorder), 23962456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 240d5b44036SJonas Devlieghere m_editline_up(), 241cacde7dfSTodd Fiala #endif 242b9c1b51eSKate Stone m_delegate(delegate), m_prompt(), m_continuation_prompt(), 243b9c1b51eSKate Stone m_current_lines_ptr(nullptr), m_base_line_number(line_number_start), 244b9c1b51eSKate Stone m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line), 245b9c1b51eSKate Stone m_color_prompts(color_prompts), m_interrupt_exits(true), 246b9c1b51eSKate Stone m_editing(false) { 24744d93782SGreg Clayton SetPrompt(prompt); 24844d93782SGreg Clayton 24962456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 250914b8d98SDeepak Panickal bool use_editline = false; 251340b0309SGreg Clayton 252609010d0SLawrence D'Anna use_editline = GetInputFILE() && GetOutputFILE() && GetErrorFILE() && 253609010d0SLawrence D'Anna m_input_sp && m_input_sp->GetIsRealTerminal(); 25444d93782SGreg Clayton 255b9c1b51eSKate Stone if (use_editline) { 256d5b44036SJonas Devlieghere m_editline_up.reset(new Editline(editline_name, GetInputFILE(), 257b9c1b51eSKate Stone GetOutputFILE(), GetErrorFILE(), 258e30f11d9SKate Stone m_color_prompts)); 259d5b44036SJonas Devlieghere m_editline_up->SetIsInputCompleteCallback(IsInputCompleteCallback, this); 260d5b44036SJonas Devlieghere m_editline_up->SetAutoCompleteCallback(AutoCompleteCallback, this); 261e30f11d9SKate Stone // See if the delegate supports fixing indentation 262e30f11d9SKate Stone const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters(); 263b9c1b51eSKate Stone if (indent_chars) { 264b9c1b51eSKate Stone // The delegate does support indentation, hook it up so when any 26505097246SAdrian Prantl // indentation character is typed, the delegate gets a chance to fix it 266d5b44036SJonas Devlieghere m_editline_up->SetFixIndentationCallback(FixIndentationCallback, this, 267b9c1b51eSKate Stone indent_chars); 268e30f11d9SKate Stone } 26944d93782SGreg Clayton } 270cacde7dfSTodd Fiala #endif 271e30f11d9SKate Stone SetBaseLineNumber(m_base_line_number); 272514d8cd8SZachary Turner SetPrompt(prompt); 273e30f11d9SKate Stone SetContinuationPrompt(continuation_prompt); 27444d93782SGreg Clayton } 27544d93782SGreg Clayton 276b9c1b51eSKate Stone IOHandlerEditline::~IOHandlerEditline() { 27762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 278d5b44036SJonas Devlieghere m_editline_up.reset(); 279cacde7dfSTodd Fiala #endif 28044d93782SGreg Clayton } 28144d93782SGreg Clayton 282b9c1b51eSKate Stone void IOHandlerEditline::Activate() { 283e30f11d9SKate Stone IOHandler::Activate(); 2840affb582SDave Lee m_delegate.IOHandlerActivated(*this, GetIsInteractive()); 285e30f11d9SKate Stone } 286e30f11d9SKate Stone 287b9c1b51eSKate Stone void IOHandlerEditline::Deactivate() { 288e30f11d9SKate Stone IOHandler::Deactivate(); 289e30f11d9SKate Stone m_delegate.IOHandlerDeactivated(*this); 290e30f11d9SKate Stone } 291e30f11d9SKate Stone 292*d9166ad2SFred Riss void IOHandlerEditline::TerminalSizeChanged() { 293*d9166ad2SFred Riss #if LLDB_ENABLE_LIBEDIT 294*d9166ad2SFred Riss m_editline_up->TerminalSizeChanged(); 295*d9166ad2SFred Riss #endif 296*d9166ad2SFred Riss } 297*d9166ad2SFred Riss 298b3faa01fSLawrence D'Anna // Split out a line from the buffer, if there is a full one to get. 299b3faa01fSLawrence D'Anna static Optional<std::string> SplitLine(std::string &line_buffer) { 300b3faa01fSLawrence D'Anna size_t pos = line_buffer.find('\n'); 301b3faa01fSLawrence D'Anna if (pos == std::string::npos) 302b3faa01fSLawrence D'Anna return None; 303adcd0268SBenjamin Kramer std::string line = 304adcd0268SBenjamin Kramer std::string(StringRef(line_buffer.c_str(), pos).rtrim("\n\r")); 305b3faa01fSLawrence D'Anna line_buffer = line_buffer.substr(pos + 1); 306b3faa01fSLawrence D'Anna return line; 307b3faa01fSLawrence D'Anna } 308b3faa01fSLawrence D'Anna 309b3faa01fSLawrence D'Anna // If the final line of the file ends without a end-of-line, return 310b3faa01fSLawrence D'Anna // it as a line anyway. 311b3faa01fSLawrence D'Anna static Optional<std::string> SplitLineEOF(std::string &line_buffer) { 312b3faa01fSLawrence D'Anna if (llvm::all_of(line_buffer, isspace)) 313b3faa01fSLawrence D'Anna return None; 314b3faa01fSLawrence D'Anna std::string line = std::move(line_buffer); 315b3faa01fSLawrence D'Anna line_buffer.clear(); 316b3faa01fSLawrence D'Anna return line; 317b3faa01fSLawrence D'Anna } 318b3faa01fSLawrence D'Anna 319b9c1b51eSKate Stone bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) { 32062456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 321d5b44036SJonas Devlieghere if (m_editline_up) { 322d77c2e09SJonas Devlieghere bool b = m_editline_up->GetLine(line, interrupted); 323b3faa01fSLawrence D'Anna if (b && m_data_recorder) 324d77c2e09SJonas Devlieghere m_data_recorder->Record(line, true); 325d77c2e09SJonas Devlieghere return b; 326b3faa01fSLawrence D'Anna } 327cacde7dfSTodd Fiala #endif 328b3faa01fSLawrence D'Anna 32944d93782SGreg Clayton line.clear(); 33044d93782SGreg Clayton 331b9c1b51eSKate Stone if (GetIsInteractive()) { 332c5dac77aSEugene Zelenko const char *prompt = nullptr; 333e30f11d9SKate Stone 334e30f11d9SKate Stone if (m_multi_line && m_curr_line_idx > 0) 335e30f11d9SKate Stone prompt = GetContinuationPrompt(); 336e30f11d9SKate Stone 337c5dac77aSEugene Zelenko if (prompt == nullptr) 338e30f11d9SKate Stone prompt = GetPrompt(); 339e30f11d9SKate Stone 340b9c1b51eSKate Stone if (prompt && prompt[0]) { 3415da2bc22SLawrence D'Anna if (m_output_sp) { 3425da2bc22SLawrence D'Anna m_output_sp->Printf("%s", prompt); 3435da2bc22SLawrence D'Anna m_output_sp->Flush(); 34444d93782SGreg Clayton } 34544d93782SGreg Clayton } 34644d93782SGreg Clayton } 347b3faa01fSLawrence D'Anna 348b3faa01fSLawrence D'Anna Optional<std::string> got_line = SplitLine(m_line_buffer); 349b3faa01fSLawrence D'Anna 350b3faa01fSLawrence D'Anna if (!got_line && !m_input_sp) { 351b3faa01fSLawrence D'Anna // No more input file, we are done... 352b3faa01fSLawrence D'Anna SetIsDone(true); 353b3faa01fSLawrence D'Anna return false; 354b3faa01fSLawrence D'Anna } 355b3faa01fSLawrence D'Anna 356b3faa01fSLawrence D'Anna FILE *in = GetInputFILE(); 35744d93782SGreg Clayton char buffer[256]; 358b3faa01fSLawrence D'Anna 359b3faa01fSLawrence D'Anna if (!got_line && !in && m_input_sp) { 360b3faa01fSLawrence D'Anna // there is no FILE*, fall back on just reading bytes from the stream. 361b3faa01fSLawrence D'Anna while (!got_line) { 362b3faa01fSLawrence D'Anna size_t bytes_read = sizeof(buffer); 363b3faa01fSLawrence D'Anna Status error = m_input_sp->Read((void *)buffer, bytes_read); 364b3faa01fSLawrence D'Anna if (error.Success() && !bytes_read) { 365b3faa01fSLawrence D'Anna got_line = SplitLineEOF(m_line_buffer); 366b3faa01fSLawrence D'Anna break; 367b3faa01fSLawrence D'Anna } 368b3faa01fSLawrence D'Anna if (error.Fail()) 369b3faa01fSLawrence D'Anna break; 370b3faa01fSLawrence D'Anna m_line_buffer += StringRef(buffer, bytes_read); 371b3faa01fSLawrence D'Anna got_line = SplitLine(m_line_buffer); 372b3faa01fSLawrence D'Anna } 373b3faa01fSLawrence D'Anna } 374b3faa01fSLawrence D'Anna 375b3faa01fSLawrence D'Anna if (!got_line && in) { 376e034a04eSGreg Clayton m_editing = true; 377b3faa01fSLawrence D'Anna while (!got_line) { 378b3faa01fSLawrence D'Anna char *r = fgets(buffer, sizeof(buffer), in); 379e7167908SNathan Lanza #ifdef _WIN32 380e7167908SNathan Lanza // ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED 381e7167908SNathan Lanza // according to the docs on MSDN. However, this has evidently been a 382e7167908SNathan Lanza // known bug since Windows 8. Therefore, we can't detect if a signal 383e7167908SNathan Lanza // interrupted in the fgets. So pressing ctrl-c causes the repl to end 384e7167908SNathan Lanza // and the process to exit. A temporary workaround is just to attempt to 385e7167908SNathan Lanza // fgets twice until this bug is fixed. 386b3faa01fSLawrence D'Anna if (r == nullptr) 387b3faa01fSLawrence D'Anna r = fgets(buffer, sizeof(buffer), in); 388cb305205SNathan Lanza // this is the equivalent of EINTR for Windows 389b3faa01fSLawrence D'Anna if (r == nullptr && GetLastError() == ERROR_OPERATION_ABORTED) 390cb305205SNathan Lanza continue; 391e7167908SNathan Lanza #endif 392b3faa01fSLawrence D'Anna if (r == nullptr) { 393b3faa01fSLawrence D'Anna if (ferror(in) && errno == EINTR) 394b3faa01fSLawrence D'Anna continue; 395c9cf5798SGreg Clayton if (feof(in)) 396b3faa01fSLawrence D'Anna got_line = SplitLineEOF(m_line_buffer); 39744d93782SGreg Clayton break; 39844d93782SGreg Clayton } 399b3faa01fSLawrence D'Anna m_line_buffer += buffer; 400b3faa01fSLawrence D'Anna got_line = SplitLine(m_line_buffer); 40144d93782SGreg Clayton } 402e034a04eSGreg Clayton m_editing = false; 403b3faa01fSLawrence D'Anna } 404b3faa01fSLawrence D'Anna 405b3faa01fSLawrence D'Anna if (got_line) { 406b3faa01fSLawrence D'Anna line = got_line.getValue(); 407b3faa01fSLawrence D'Anna if (m_data_recorder) 408d77c2e09SJonas Devlieghere m_data_recorder->Record(line, true); 40944d93782SGreg Clayton } 410b3faa01fSLawrence D'Anna 411b3faa01fSLawrence D'Anna return (bool)got_line; 41244d93782SGreg Clayton } 41344d93782SGreg Clayton 41462456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 415b9c1b51eSKate Stone bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline, 41644d93782SGreg Clayton StringList &lines, 417b9c1b51eSKate Stone void *baton) { 41844d93782SGreg Clayton IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 419b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, 420b9c1b51eSKate Stone lines); 421e30f11d9SKate Stone } 422e30f11d9SKate Stone 423b9c1b51eSKate Stone int IOHandlerEditline::FixIndentationCallback(Editline *editline, 424e30f11d9SKate Stone const StringList &lines, 425e30f11d9SKate Stone int cursor_position, 426b9c1b51eSKate Stone void *baton) { 427e30f11d9SKate Stone IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 428b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerFixIndentation( 429b9c1b51eSKate Stone *editline_reader, lines, cursor_position); 43044d93782SGreg Clayton } 43144d93782SGreg Clayton 432ae34ed2cSRaphael Isemann void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request, 4332fc20f65SRaphael Isemann void *baton) { 43444d93782SGreg Clayton IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 43544d93782SGreg Clayton if (editline_reader) 436ae34ed2cSRaphael Isemann editline_reader->m_delegate.IOHandlerComplete(*editline_reader, request); 43744d93782SGreg Clayton } 438cacde7dfSTodd Fiala #endif 43944d93782SGreg Clayton 440b9c1b51eSKate Stone const char *IOHandlerEditline::GetPrompt() { 44162456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 442d5b44036SJonas Devlieghere if (m_editline_up) { 443d5b44036SJonas Devlieghere return m_editline_up->GetPrompt(); 444b9c1b51eSKate Stone } else { 445cacde7dfSTodd Fiala #endif 446cacde7dfSTodd Fiala if (m_prompt.empty()) 447c5dac77aSEugene Zelenko return nullptr; 44862456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 449cacde7dfSTodd Fiala } 450cacde7dfSTodd Fiala #endif 45144d93782SGreg Clayton return m_prompt.c_str(); 45244d93782SGreg Clayton } 45344d93782SGreg Clayton 454514d8cd8SZachary Turner bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) { 455adcd0268SBenjamin Kramer m_prompt = std::string(prompt); 456514d8cd8SZachary Turner 45762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 458d5b44036SJonas Devlieghere if (m_editline_up) 459d5b44036SJonas Devlieghere m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str()); 460cacde7dfSTodd Fiala #endif 46144d93782SGreg Clayton return true; 46244d93782SGreg Clayton } 46344d93782SGreg Clayton 464b9c1b51eSKate Stone const char *IOHandlerEditline::GetContinuationPrompt() { 465b9c1b51eSKate Stone return (m_continuation_prompt.empty() ? nullptr 466b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 467e30f11d9SKate Stone } 468e30f11d9SKate Stone 469514d8cd8SZachary Turner void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) { 470adcd0268SBenjamin Kramer m_continuation_prompt = std::string(prompt); 471e30f11d9SKate Stone 47262456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 473d5b44036SJonas Devlieghere if (m_editline_up) 474d5b44036SJonas Devlieghere m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty() 475b9c1b51eSKate Stone ? nullptr 476b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 477d553d00cSZachary Turner #endif 478e30f11d9SKate Stone } 479e30f11d9SKate Stone 480b9c1b51eSKate Stone void IOHandlerEditline::SetBaseLineNumber(uint32_t line) { 481f6913cd7SGreg Clayton m_base_line_number = line; 482f6913cd7SGreg Clayton } 483e30f11d9SKate Stone 484b9c1b51eSKate Stone uint32_t IOHandlerEditline::GetCurrentLineIndex() const { 48562456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 486d5b44036SJonas Devlieghere if (m_editline_up) 487d5b44036SJonas Devlieghere return m_editline_up->GetCurrentLine(); 488e30f11d9SKate Stone #endif 489e30f11d9SKate Stone return m_curr_line_idx; 490e30f11d9SKate Stone } 491e30f11d9SKate Stone 492b9c1b51eSKate Stone bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) { 493e30f11d9SKate Stone m_current_lines_ptr = &lines; 494e30f11d9SKate Stone 49544d93782SGreg Clayton bool success = false; 49662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 497d5b44036SJonas Devlieghere if (m_editline_up) { 498d5b44036SJonas Devlieghere return m_editline_up->GetLines(m_base_line_number, lines, interrupted); 499b9c1b51eSKate Stone } else { 500cacde7dfSTodd Fiala #endif 501e30f11d9SKate Stone bool done = false; 50297206d57SZachary Turner Status error; 50344d93782SGreg Clayton 504b9c1b51eSKate Stone while (!done) { 505f6913cd7SGreg Clayton // Show line numbers if we are asked to 50644d93782SGreg Clayton std::string line; 507b9c1b51eSKate Stone if (m_base_line_number > 0 && GetIsInteractive()) { 5085da2bc22SLawrence D'Anna if (m_output_sp) { 5095da2bc22SLawrence D'Anna m_output_sp->Printf("%u%s", 5105da2bc22SLawrence D'Anna m_base_line_number + (uint32_t)lines.GetSize(), 511b9c1b51eSKate Stone GetPrompt() == nullptr ? " " : ""); 512f6913cd7SGreg Clayton } 5135da2bc22SLawrence D'Anna } 514f6913cd7SGreg Clayton 515e30f11d9SKate Stone m_curr_line_idx = lines.GetSize(); 516e30f11d9SKate Stone 517f0066ad0SGreg Clayton bool interrupted = false; 518b9c1b51eSKate Stone if (GetLine(line, interrupted) && !interrupted) { 51944d93782SGreg Clayton lines.AppendString(line); 520e30f11d9SKate Stone done = m_delegate.IOHandlerIsInputComplete(*this, lines); 521b9c1b51eSKate Stone } else { 522e30f11d9SKate Stone done = true; 52344d93782SGreg Clayton } 52444d93782SGreg Clayton } 52544d93782SGreg Clayton success = lines.GetSize() > 0; 52662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 52744d93782SGreg Clayton } 528cacde7dfSTodd Fiala #endif 52944d93782SGreg Clayton return success; 53044d93782SGreg Clayton } 53144d93782SGreg Clayton 53205097246SAdrian Prantl // Each IOHandler gets to run until it is done. It should read data from the 53305097246SAdrian Prantl // "in" and place output into "out" and "err and return when done. 534b9c1b51eSKate Stone void IOHandlerEditline::Run() { 53544d93782SGreg Clayton std::string line; 536b9c1b51eSKate Stone while (IsActive()) { 537f0066ad0SGreg Clayton bool interrupted = false; 538b9c1b51eSKate Stone if (m_multi_line) { 53944d93782SGreg Clayton StringList lines; 540b9c1b51eSKate Stone if (GetLines(lines, interrupted)) { 541b9c1b51eSKate Stone if (interrupted) { 542e30f11d9SKate Stone m_done = m_interrupt_exits; 543e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 544e30f11d9SKate Stone 545b9c1b51eSKate Stone } else { 54644d93782SGreg Clayton line = lines.CopyList(); 54744d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 54844d93782SGreg Clayton } 549b9c1b51eSKate Stone } else { 55044d93782SGreg Clayton m_done = true; 55144d93782SGreg Clayton } 552b9c1b51eSKate Stone } else { 553b9c1b51eSKate Stone if (GetLine(line, interrupted)) { 554e30f11d9SKate Stone if (interrupted) 555e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 556e30f11d9SKate Stone else 55744d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 558b9c1b51eSKate Stone } else { 55944d93782SGreg Clayton m_done = true; 56044d93782SGreg Clayton } 56144d93782SGreg Clayton } 56244d93782SGreg Clayton } 56344d93782SGreg Clayton } 56444d93782SGreg Clayton 565b9c1b51eSKate Stone void IOHandlerEditline::Cancel() { 56662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 567d5b44036SJonas Devlieghere if (m_editline_up) 568d5b44036SJonas Devlieghere m_editline_up->Cancel(); 569cacde7dfSTodd Fiala #endif 570e68f5d6bSGreg Clayton } 571e68f5d6bSGreg Clayton 572b9c1b51eSKate Stone bool IOHandlerEditline::Interrupt() { 573f0066ad0SGreg Clayton // Let the delgate handle it first 574f0066ad0SGreg Clayton if (m_delegate.IOHandlerInterrupt(*this)) 575f0066ad0SGreg Clayton return true; 576f0066ad0SGreg Clayton 57762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 578d5b44036SJonas Devlieghere if (m_editline_up) 579d5b44036SJonas Devlieghere return m_editline_up->Interrupt(); 580cacde7dfSTodd Fiala #endif 581f0066ad0SGreg Clayton return false; 58244d93782SGreg Clayton } 58344d93782SGreg Clayton 584b9c1b51eSKate Stone void IOHandlerEditline::GotEOF() { 58562456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 586d5b44036SJonas Devlieghere if (m_editline_up) 587d5b44036SJonas Devlieghere m_editline_up->Interrupt(); 588cacde7dfSTodd Fiala #endif 58944d93782SGreg Clayton } 59044d93782SGreg Clayton 591b9c1b51eSKate Stone void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) { 59262456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 593d5b44036SJonas Devlieghere if (m_editline_up) 594d5b44036SJonas Devlieghere m_editline_up->PrintAsync(stream, s, len); 5954446487dSPavel Labath else 5964446487dSPavel Labath #endif 597fab31220STed Woodward { 5988b98f12aSMartin Storsjo #ifdef _WIN32 599341e4789SDawn Perchik const char *prompt = GetPrompt(); 600b9c1b51eSKate Stone if (prompt) { 601fab31220STed Woodward // Back up over previous prompt using Windows API 602fab31220STed Woodward CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; 603fab31220STed Woodward HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); 604fab31220STed Woodward GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info); 605fab31220STed Woodward COORD coord = screen_buffer_info.dwCursorPosition; 606fab31220STed Woodward coord.X -= strlen(prompt); 607fab31220STed Woodward if (coord.X < 0) 608fab31220STed Woodward coord.X = 0; 609fab31220STed Woodward SetConsoleCursorPosition(console_handle, coord); 610fab31220STed Woodward } 611fab31220STed Woodward #endif 6124446487dSPavel Labath IOHandler::PrintAsync(stream, s, len); 6138b98f12aSMartin Storsjo #ifdef _WIN32 614fab31220STed Woodward if (prompt) 6157ca15ba7SLawrence D'Anna IOHandler::PrintAsync(GetOutputStreamFileSP().get(), prompt, 616b9c1b51eSKate Stone strlen(prompt)); 617341e4789SDawn Perchik #endif 618fab31220STed Woodward } 6194446487dSPavel Labath } 620