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 292d9166ad2SFred Riss void IOHandlerEditline::TerminalSizeChanged() { 293d9166ad2SFred Riss #if LLDB_ENABLE_LIBEDIT 294be18df3dSRaphael Isemann if (m_editline_up) 295d9166ad2SFred Riss m_editline_up->TerminalSizeChanged(); 296d9166ad2SFred Riss #endif 297d9166ad2SFred Riss } 298d9166ad2SFred Riss 299b3faa01fSLawrence D'Anna // Split out a line from the buffer, if there is a full one to get. 300b3faa01fSLawrence D'Anna static Optional<std::string> SplitLine(std::string &line_buffer) { 301b3faa01fSLawrence D'Anna size_t pos = line_buffer.find('\n'); 302b3faa01fSLawrence D'Anna if (pos == std::string::npos) 303b3faa01fSLawrence D'Anna return None; 304adcd0268SBenjamin Kramer std::string line = 305adcd0268SBenjamin Kramer std::string(StringRef(line_buffer.c_str(), pos).rtrim("\n\r")); 306b3faa01fSLawrence D'Anna line_buffer = line_buffer.substr(pos + 1); 307b3faa01fSLawrence D'Anna return line; 308b3faa01fSLawrence D'Anna } 309b3faa01fSLawrence D'Anna 310b3faa01fSLawrence D'Anna // If the final line of the file ends without a end-of-line, return 311b3faa01fSLawrence D'Anna // it as a line anyway. 312b3faa01fSLawrence D'Anna static Optional<std::string> SplitLineEOF(std::string &line_buffer) { 313*f5eaa2afSRaphael Isemann if (llvm::all_of(line_buffer, llvm::isSpace)) 314b3faa01fSLawrence D'Anna return None; 315b3faa01fSLawrence D'Anna std::string line = std::move(line_buffer); 316b3faa01fSLawrence D'Anna line_buffer.clear(); 317b3faa01fSLawrence D'Anna return line; 318b3faa01fSLawrence D'Anna } 319b3faa01fSLawrence D'Anna 320b9c1b51eSKate Stone bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) { 32162456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 322d5b44036SJonas Devlieghere if (m_editline_up) { 323d77c2e09SJonas Devlieghere bool b = m_editline_up->GetLine(line, interrupted); 324b3faa01fSLawrence D'Anna if (b && m_data_recorder) 325d77c2e09SJonas Devlieghere m_data_recorder->Record(line, true); 326d77c2e09SJonas Devlieghere return b; 327b3faa01fSLawrence D'Anna } 328cacde7dfSTodd Fiala #endif 329b3faa01fSLawrence D'Anna 33044d93782SGreg Clayton line.clear(); 33144d93782SGreg Clayton 332b9c1b51eSKate Stone if (GetIsInteractive()) { 333c5dac77aSEugene Zelenko const char *prompt = nullptr; 334e30f11d9SKate Stone 335e30f11d9SKate Stone if (m_multi_line && m_curr_line_idx > 0) 336e30f11d9SKate Stone prompt = GetContinuationPrompt(); 337e30f11d9SKate Stone 338c5dac77aSEugene Zelenko if (prompt == nullptr) 339e30f11d9SKate Stone prompt = GetPrompt(); 340e30f11d9SKate Stone 341b9c1b51eSKate Stone if (prompt && prompt[0]) { 3425da2bc22SLawrence D'Anna if (m_output_sp) { 3435da2bc22SLawrence D'Anna m_output_sp->Printf("%s", prompt); 3445da2bc22SLawrence D'Anna m_output_sp->Flush(); 34544d93782SGreg Clayton } 34644d93782SGreg Clayton } 34744d93782SGreg Clayton } 348b3faa01fSLawrence D'Anna 349b3faa01fSLawrence D'Anna Optional<std::string> got_line = SplitLine(m_line_buffer); 350b3faa01fSLawrence D'Anna 351b3faa01fSLawrence D'Anna if (!got_line && !m_input_sp) { 352b3faa01fSLawrence D'Anna // No more input file, we are done... 353b3faa01fSLawrence D'Anna SetIsDone(true); 354b3faa01fSLawrence D'Anna return false; 355b3faa01fSLawrence D'Anna } 356b3faa01fSLawrence D'Anna 357b3faa01fSLawrence D'Anna FILE *in = GetInputFILE(); 35844d93782SGreg Clayton char buffer[256]; 359b3faa01fSLawrence D'Anna 360b3faa01fSLawrence D'Anna if (!got_line && !in && m_input_sp) { 361b3faa01fSLawrence D'Anna // there is no FILE*, fall back on just reading bytes from the stream. 362b3faa01fSLawrence D'Anna while (!got_line) { 363b3faa01fSLawrence D'Anna size_t bytes_read = sizeof(buffer); 364b3faa01fSLawrence D'Anna Status error = m_input_sp->Read((void *)buffer, bytes_read); 365b3faa01fSLawrence D'Anna if (error.Success() && !bytes_read) { 366b3faa01fSLawrence D'Anna got_line = SplitLineEOF(m_line_buffer); 367b3faa01fSLawrence D'Anna break; 368b3faa01fSLawrence D'Anna } 369b3faa01fSLawrence D'Anna if (error.Fail()) 370b3faa01fSLawrence D'Anna break; 371b3faa01fSLawrence D'Anna m_line_buffer += StringRef(buffer, bytes_read); 372b3faa01fSLawrence D'Anna got_line = SplitLine(m_line_buffer); 373b3faa01fSLawrence D'Anna } 374b3faa01fSLawrence D'Anna } 375b3faa01fSLawrence D'Anna 376b3faa01fSLawrence D'Anna if (!got_line && in) { 377e034a04eSGreg Clayton m_editing = true; 378b3faa01fSLawrence D'Anna while (!got_line) { 379b3faa01fSLawrence D'Anna char *r = fgets(buffer, sizeof(buffer), in); 380e7167908SNathan Lanza #ifdef _WIN32 381e7167908SNathan Lanza // ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED 382e7167908SNathan Lanza // according to the docs on MSDN. However, this has evidently been a 383e7167908SNathan Lanza // known bug since Windows 8. Therefore, we can't detect if a signal 384e7167908SNathan Lanza // interrupted in the fgets. So pressing ctrl-c causes the repl to end 385e7167908SNathan Lanza // and the process to exit. A temporary workaround is just to attempt to 386e7167908SNathan Lanza // fgets twice until this bug is fixed. 387b3faa01fSLawrence D'Anna if (r == nullptr) 388b3faa01fSLawrence D'Anna r = fgets(buffer, sizeof(buffer), in); 389cb305205SNathan Lanza // this is the equivalent of EINTR for Windows 390b3faa01fSLawrence D'Anna if (r == nullptr && GetLastError() == ERROR_OPERATION_ABORTED) 391cb305205SNathan Lanza continue; 392e7167908SNathan Lanza #endif 393b3faa01fSLawrence D'Anna if (r == nullptr) { 394b3faa01fSLawrence D'Anna if (ferror(in) && errno == EINTR) 395b3faa01fSLawrence D'Anna continue; 396c9cf5798SGreg Clayton if (feof(in)) 397b3faa01fSLawrence D'Anna got_line = SplitLineEOF(m_line_buffer); 39844d93782SGreg Clayton break; 39944d93782SGreg Clayton } 400b3faa01fSLawrence D'Anna m_line_buffer += buffer; 401b3faa01fSLawrence D'Anna got_line = SplitLine(m_line_buffer); 40244d93782SGreg Clayton } 403e034a04eSGreg Clayton m_editing = false; 404b3faa01fSLawrence D'Anna } 405b3faa01fSLawrence D'Anna 406b3faa01fSLawrence D'Anna if (got_line) { 407b3faa01fSLawrence D'Anna line = got_line.getValue(); 408b3faa01fSLawrence D'Anna if (m_data_recorder) 409d77c2e09SJonas Devlieghere m_data_recorder->Record(line, true); 41044d93782SGreg Clayton } 411b3faa01fSLawrence D'Anna 412b3faa01fSLawrence D'Anna return (bool)got_line; 41344d93782SGreg Clayton } 41444d93782SGreg Clayton 41562456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 416b9c1b51eSKate Stone bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline, 41744d93782SGreg Clayton StringList &lines, 418b9c1b51eSKate Stone void *baton) { 41944d93782SGreg Clayton IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 420b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, 421b9c1b51eSKate Stone lines); 422e30f11d9SKate Stone } 423e30f11d9SKate Stone 424b9c1b51eSKate Stone int IOHandlerEditline::FixIndentationCallback(Editline *editline, 425e30f11d9SKate Stone const StringList &lines, 426e30f11d9SKate Stone int cursor_position, 427b9c1b51eSKate Stone void *baton) { 428e30f11d9SKate Stone IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 429b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerFixIndentation( 430b9c1b51eSKate Stone *editline_reader, lines, cursor_position); 43144d93782SGreg Clayton } 43244d93782SGreg Clayton 433ae34ed2cSRaphael Isemann void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request, 4342fc20f65SRaphael Isemann void *baton) { 43544d93782SGreg Clayton IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 43644d93782SGreg Clayton if (editline_reader) 437ae34ed2cSRaphael Isemann editline_reader->m_delegate.IOHandlerComplete(*editline_reader, request); 43844d93782SGreg Clayton } 439cacde7dfSTodd Fiala #endif 44044d93782SGreg Clayton 441b9c1b51eSKate Stone const char *IOHandlerEditline::GetPrompt() { 44262456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 443d5b44036SJonas Devlieghere if (m_editline_up) { 444d5b44036SJonas Devlieghere return m_editline_up->GetPrompt(); 445b9c1b51eSKate Stone } else { 446cacde7dfSTodd Fiala #endif 447cacde7dfSTodd Fiala if (m_prompt.empty()) 448c5dac77aSEugene Zelenko return nullptr; 44962456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 450cacde7dfSTodd Fiala } 451cacde7dfSTodd Fiala #endif 45244d93782SGreg Clayton return m_prompt.c_str(); 45344d93782SGreg Clayton } 45444d93782SGreg Clayton 455514d8cd8SZachary Turner bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) { 456adcd0268SBenjamin Kramer m_prompt = std::string(prompt); 457514d8cd8SZachary Turner 45862456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 459d5b44036SJonas Devlieghere if (m_editline_up) 460d5b44036SJonas Devlieghere m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str()); 461cacde7dfSTodd Fiala #endif 46244d93782SGreg Clayton return true; 46344d93782SGreg Clayton } 46444d93782SGreg Clayton 465b9c1b51eSKate Stone const char *IOHandlerEditline::GetContinuationPrompt() { 466b9c1b51eSKate Stone return (m_continuation_prompt.empty() ? nullptr 467b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 468e30f11d9SKate Stone } 469e30f11d9SKate Stone 470514d8cd8SZachary Turner void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) { 471adcd0268SBenjamin Kramer m_continuation_prompt = std::string(prompt); 472e30f11d9SKate Stone 47362456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 474d5b44036SJonas Devlieghere if (m_editline_up) 475d5b44036SJonas Devlieghere m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty() 476b9c1b51eSKate Stone ? nullptr 477b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 478d553d00cSZachary Turner #endif 479e30f11d9SKate Stone } 480e30f11d9SKate Stone 481b9c1b51eSKate Stone void IOHandlerEditline::SetBaseLineNumber(uint32_t line) { 482f6913cd7SGreg Clayton m_base_line_number = line; 483f6913cd7SGreg Clayton } 484e30f11d9SKate Stone 485b9c1b51eSKate Stone uint32_t IOHandlerEditline::GetCurrentLineIndex() const { 48662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 487d5b44036SJonas Devlieghere if (m_editline_up) 488d5b44036SJonas Devlieghere return m_editline_up->GetCurrentLine(); 489e30f11d9SKate Stone #endif 490e30f11d9SKate Stone return m_curr_line_idx; 491e30f11d9SKate Stone } 492e30f11d9SKate Stone 493b9c1b51eSKate Stone bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) { 494e30f11d9SKate Stone m_current_lines_ptr = &lines; 495e30f11d9SKate Stone 49644d93782SGreg Clayton bool success = false; 49762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 498d5b44036SJonas Devlieghere if (m_editline_up) { 499d5b44036SJonas Devlieghere return m_editline_up->GetLines(m_base_line_number, lines, interrupted); 500b9c1b51eSKate Stone } else { 501cacde7dfSTodd Fiala #endif 502e30f11d9SKate Stone bool done = false; 50397206d57SZachary Turner Status error; 50444d93782SGreg Clayton 505b9c1b51eSKate Stone while (!done) { 506f6913cd7SGreg Clayton // Show line numbers if we are asked to 50744d93782SGreg Clayton std::string line; 508b9c1b51eSKate Stone if (m_base_line_number > 0 && GetIsInteractive()) { 5095da2bc22SLawrence D'Anna if (m_output_sp) { 5105da2bc22SLawrence D'Anna m_output_sp->Printf("%u%s", 5115da2bc22SLawrence D'Anna m_base_line_number + (uint32_t)lines.GetSize(), 512b9c1b51eSKate Stone GetPrompt() == nullptr ? " " : ""); 513f6913cd7SGreg Clayton } 5145da2bc22SLawrence D'Anna } 515f6913cd7SGreg Clayton 516e30f11d9SKate Stone m_curr_line_idx = lines.GetSize(); 517e30f11d9SKate Stone 518f0066ad0SGreg Clayton bool interrupted = false; 519b9c1b51eSKate Stone if (GetLine(line, interrupted) && !interrupted) { 52044d93782SGreg Clayton lines.AppendString(line); 521e30f11d9SKate Stone done = m_delegate.IOHandlerIsInputComplete(*this, lines); 522b9c1b51eSKate Stone } else { 523e30f11d9SKate Stone done = true; 52444d93782SGreg Clayton } 52544d93782SGreg Clayton } 52644d93782SGreg Clayton success = lines.GetSize() > 0; 52762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 52844d93782SGreg Clayton } 529cacde7dfSTodd Fiala #endif 53044d93782SGreg Clayton return success; 53144d93782SGreg Clayton } 53244d93782SGreg Clayton 53305097246SAdrian Prantl // Each IOHandler gets to run until it is done. It should read data from the 53405097246SAdrian Prantl // "in" and place output into "out" and "err and return when done. 535b9c1b51eSKate Stone void IOHandlerEditline::Run() { 53644d93782SGreg Clayton std::string line; 537b9c1b51eSKate Stone while (IsActive()) { 538f0066ad0SGreg Clayton bool interrupted = false; 539b9c1b51eSKate Stone if (m_multi_line) { 54044d93782SGreg Clayton StringList lines; 541b9c1b51eSKate Stone if (GetLines(lines, interrupted)) { 542b9c1b51eSKate Stone if (interrupted) { 543e30f11d9SKate Stone m_done = m_interrupt_exits; 544e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 545e30f11d9SKate Stone 546b9c1b51eSKate Stone } else { 54744d93782SGreg Clayton line = lines.CopyList(); 54844d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 54944d93782SGreg Clayton } 550b9c1b51eSKate Stone } else { 55144d93782SGreg Clayton m_done = true; 55244d93782SGreg Clayton } 553b9c1b51eSKate Stone } else { 554b9c1b51eSKate Stone if (GetLine(line, interrupted)) { 555e30f11d9SKate Stone if (interrupted) 556e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 557e30f11d9SKate Stone else 55844d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 559b9c1b51eSKate Stone } else { 56044d93782SGreg Clayton m_done = true; 56144d93782SGreg Clayton } 56244d93782SGreg Clayton } 56344d93782SGreg Clayton } 56444d93782SGreg Clayton } 56544d93782SGreg Clayton 566b9c1b51eSKate Stone void IOHandlerEditline::Cancel() { 56762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 568d5b44036SJonas Devlieghere if (m_editline_up) 569d5b44036SJonas Devlieghere m_editline_up->Cancel(); 570cacde7dfSTodd Fiala #endif 571e68f5d6bSGreg Clayton } 572e68f5d6bSGreg Clayton 573b9c1b51eSKate Stone bool IOHandlerEditline::Interrupt() { 574f0066ad0SGreg Clayton // Let the delgate handle it first 575f0066ad0SGreg Clayton if (m_delegate.IOHandlerInterrupt(*this)) 576f0066ad0SGreg Clayton return true; 577f0066ad0SGreg Clayton 57862456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 579d5b44036SJonas Devlieghere if (m_editline_up) 580d5b44036SJonas Devlieghere return m_editline_up->Interrupt(); 581cacde7dfSTodd Fiala #endif 582f0066ad0SGreg Clayton return false; 58344d93782SGreg Clayton } 58444d93782SGreg Clayton 585b9c1b51eSKate Stone void IOHandlerEditline::GotEOF() { 58662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 587d5b44036SJonas Devlieghere if (m_editline_up) 588d5b44036SJonas Devlieghere m_editline_up->Interrupt(); 589cacde7dfSTodd Fiala #endif 59044d93782SGreg Clayton } 59144d93782SGreg Clayton 592b9c1b51eSKate Stone void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) { 59362456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 594d5b44036SJonas Devlieghere if (m_editline_up) 595d5b44036SJonas Devlieghere m_editline_up->PrintAsync(stream, s, len); 5964446487dSPavel Labath else 5974446487dSPavel Labath #endif 598fab31220STed Woodward { 5998b98f12aSMartin Storsjo #ifdef _WIN32 600341e4789SDawn Perchik const char *prompt = GetPrompt(); 601b9c1b51eSKate Stone if (prompt) { 602fab31220STed Woodward // Back up over previous prompt using Windows API 603fab31220STed Woodward CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; 604fab31220STed Woodward HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); 605fab31220STed Woodward GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info); 606fab31220STed Woodward COORD coord = screen_buffer_info.dwCursorPosition; 607fab31220STed Woodward coord.X -= strlen(prompt); 608fab31220STed Woodward if (coord.X < 0) 609fab31220STed Woodward coord.X = 0; 610fab31220STed Woodward SetConsoleCursorPosition(console_handle, coord); 611fab31220STed Woodward } 612fab31220STed Woodward #endif 6134446487dSPavel Labath IOHandler::PrintAsync(stream, s, len); 6148b98f12aSMartin Storsjo #ifdef _WIN32 615fab31220STed Woodward if (prompt) 6167ca15ba7SLawrence D'Anna IOHandler::PrintAsync(GetOutputStreamFileSP().get(), prompt, 617b9c1b51eSKate Stone strlen(prompt)); 618341e4789SDawn Perchik #endif 619fab31220STed Woodward } 6204446487dSPavel Labath } 621