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" 20*080635efSJonas 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 125b9c1b51eSKate Stone void IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len) { 126b9c1b51eSKate Stone if (stream) { 12716ff8604SSaleem Abdulrasool std::lock_guard<std::recursive_mutex> guard(m_mutex); 1284446487dSPavel Labath if (m_top) 1294446487dSPavel Labath m_top->PrintAsync(stream, s, len); 1304deea652SPavel Labath else 1314deea652SPavel Labath stream->Write(s, len); 1324446487dSPavel Labath } 1334446487dSPavel Labath } 1344446487dSPavel Labath 1357a120c8bSZachary Turner IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt, 136b9c1b51eSKate Stone bool default_response) 137b9c1b51eSKate Stone : IOHandlerEditline( 138b9c1b51eSKate Stone debugger, IOHandler::Type::Confirm, 139c5dac77aSEugene Zelenko nullptr, // nullptr editline_name means no history loaded/saved 140514d8cd8SZachary Turner llvm::StringRef(), // No prompt 141514d8cd8SZachary Turner llvm::StringRef(), // No continuation prompt 14244d93782SGreg Clayton false, // Multi-line 143e30f11d9SKate Stone false, // Don't colorize the prompt (i.e. the confirm message.) 144d77c2e09SJonas Devlieghere 0, *this, nullptr), 145b9c1b51eSKate Stone m_default_response(default_response), m_user_response(default_response) { 14644d93782SGreg Clayton StreamString prompt_stream; 14744d93782SGreg Clayton prompt_stream.PutCString(prompt); 14844d93782SGreg Clayton if (m_default_response) 14944d93782SGreg Clayton prompt_stream.Printf(": [Y/n] "); 15044d93782SGreg Clayton else 15144d93782SGreg Clayton prompt_stream.Printf(": [y/N] "); 15244d93782SGreg Clayton 153514d8cd8SZachary Turner SetPrompt(prompt_stream.GetString()); 15444d93782SGreg Clayton } 15544d93782SGreg Clayton 156315b6884SEugene Zelenko IOHandlerConfirm::~IOHandlerConfirm() = default; 15744d93782SGreg Clayton 158ae34ed2cSRaphael Isemann void IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler, 1592fc20f65SRaphael Isemann CompletionRequest &request) { 1601153dc96SRaphael Isemann if (request.GetRawCursorPos() != 0) 1611153dc96SRaphael Isemann return; 1621153dc96SRaphael Isemann request.AddCompletion(m_default_response ? "y" : "n"); 16344d93782SGreg Clayton } 16444d93782SGreg Clayton 165b9c1b51eSKate Stone void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler, 166b9c1b51eSKate Stone std::string &line) { 167b9c1b51eSKate Stone if (line.empty()) { 16844d93782SGreg Clayton // User just hit enter, set the response to the default 16944d93782SGreg Clayton m_user_response = m_default_response; 17044d93782SGreg Clayton io_handler.SetIsDone(true); 17144d93782SGreg Clayton return; 17244d93782SGreg Clayton } 17344d93782SGreg Clayton 174b9c1b51eSKate Stone if (line.size() == 1) { 175b9c1b51eSKate Stone switch (line[0]) { 17644d93782SGreg Clayton case 'y': 17744d93782SGreg Clayton case 'Y': 17844d93782SGreg Clayton m_user_response = true; 17944d93782SGreg Clayton io_handler.SetIsDone(true); 18044d93782SGreg Clayton return; 18144d93782SGreg Clayton case 'n': 18244d93782SGreg Clayton case 'N': 18344d93782SGreg Clayton m_user_response = false; 18444d93782SGreg Clayton io_handler.SetIsDone(true); 18544d93782SGreg Clayton return; 18644d93782SGreg Clayton default: 18744d93782SGreg Clayton break; 18844d93782SGreg Clayton } 18944d93782SGreg Clayton } 19044d93782SGreg Clayton 191b9c1b51eSKate Stone if (line == "yes" || line == "YES" || line == "Yes") { 19244d93782SGreg Clayton m_user_response = true; 19344d93782SGreg Clayton io_handler.SetIsDone(true); 194b9c1b51eSKate Stone } else if (line == "no" || line == "NO" || line == "No") { 19544d93782SGreg Clayton m_user_response = false; 19644d93782SGreg Clayton io_handler.SetIsDone(true); 19744d93782SGreg Clayton } 19844d93782SGreg Clayton } 19944d93782SGreg Clayton 200de9e8502SShu Anzai llvm::Optional<std::string> 201de9e8502SShu Anzai IOHandlerDelegate::IOHandlerSuggestion(IOHandler &io_handler, 202de9e8502SShu Anzai llvm::StringRef line) { 203de9e8502SShu Anzai return io_handler.GetDebugger() 204de9e8502SShu Anzai .GetCommandInterpreter() 205de9e8502SShu Anzai .GetAutoSuggestionForCommand(line); 206de9e8502SShu Anzai } 207de9e8502SShu Anzai 208ae34ed2cSRaphael Isemann void IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler, 2092fc20f65SRaphael Isemann CompletionRequest &request) { 210b9c1b51eSKate Stone switch (m_completion) { 21144d93782SGreg Clayton case Completion::None: 21244d93782SGreg Clayton break; 21344d93782SGreg Clayton case Completion::LLDBCommand: 214ae34ed2cSRaphael Isemann io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(request); 215ae34ed2cSRaphael Isemann break; 216ae34ed2cSRaphael Isemann case Completion::Expression: 217b9c1b51eSKate Stone CommandCompletions::InvokeCommonCompletionCallbacks( 218b9c1b51eSKate Stone io_handler.GetDebugger().GetCommandInterpreter(), 219ae34ed2cSRaphael Isemann CommandCompletions::eVariablePathCompletion, request, nullptr); 220ae34ed2cSRaphael Isemann break; 22144d93782SGreg Clayton } 22244d93782SGreg Clayton } 22344d93782SGreg Clayton 224b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 225b9c1b51eSKate Stone Debugger &debugger, IOHandler::Type type, 22644d93782SGreg Clayton const char *editline_name, // Used for saving history files 227514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 228514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 229d77c2e09SJonas Devlieghere IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder) 230b9c1b51eSKate Stone : IOHandlerEditline(debugger, type, 2317ca15ba7SLawrence D'Anna FileSP(), // Inherit input from top input reader 23244d93782SGreg Clayton StreamFileSP(), // Inherit output from top input reader 23344d93782SGreg Clayton StreamFileSP(), // Inherit error from top input reader 234340b0309SGreg Clayton 0, // Flags 23544d93782SGreg Clayton editline_name, // Used for saving history files 236b9c1b51eSKate Stone prompt, continuation_prompt, multi_line, color_prompts, 237d77c2e09SJonas Devlieghere line_number_start, delegate, data_recorder) {} 23844d93782SGreg Clayton 239b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 2407ca15ba7SLawrence D'Anna Debugger &debugger, IOHandler::Type type, const lldb::FileSP &input_sp, 2417ca15ba7SLawrence D'Anna const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp, 2427ca15ba7SLawrence D'Anna uint32_t flags, 24344d93782SGreg Clayton const char *editline_name, // Used for saving history files 244514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 245514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 246d77c2e09SJonas Devlieghere IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder) 247d77c2e09SJonas Devlieghere : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags, 248d77c2e09SJonas Devlieghere data_recorder), 24962456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 250d5b44036SJonas Devlieghere m_editline_up(), 251cacde7dfSTodd Fiala #endif 252b9c1b51eSKate Stone m_delegate(delegate), m_prompt(), m_continuation_prompt(), 253b9c1b51eSKate Stone m_current_lines_ptr(nullptr), m_base_line_number(line_number_start), 254b9c1b51eSKate Stone m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line), 25532400005SRaphael Isemann m_color_prompts(color_prompts), m_interrupt_exits(true) { 25644d93782SGreg Clayton SetPrompt(prompt); 25744d93782SGreg Clayton 25862456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 259914b8d98SDeepak Panickal bool use_editline = false; 260340b0309SGreg Clayton 261609010d0SLawrence D'Anna use_editline = GetInputFILE() && GetOutputFILE() && GetErrorFILE() && 262609010d0SLawrence D'Anna m_input_sp && m_input_sp->GetIsRealTerminal(); 26344d93782SGreg Clayton 264b9c1b51eSKate Stone if (use_editline) { 26506412daeSJonas Devlieghere m_editline_up = std::make_unique<Editline>(editline_name, GetInputFILE(), 266b9c1b51eSKate Stone GetOutputFILE(), GetErrorFILE(), 26706412daeSJonas Devlieghere m_color_prompts); 2685826aa48SNeal (nealsid) m_editline_up->SetIsInputCompleteCallback( 2695826aa48SNeal (nealsid) [this](Editline *editline, StringList &lines) { 2705826aa48SNeal (nealsid) return this->IsInputCompleteCallback(editline, lines); 2715826aa48SNeal (nealsid) }); 2725826aa48SNeal (nealsid) 2735826aa48SNeal (nealsid) m_editline_up->SetAutoCompleteCallback([this](CompletionRequest &request) { 2745826aa48SNeal (nealsid) this->AutoCompleteCallback(request); 2755826aa48SNeal (nealsid) }); 2765826aa48SNeal (nealsid) 277*080635efSJonas Devlieghere if (debugger.GetUseAutosuggestion()) { 2785826aa48SNeal (nealsid) m_editline_up->SetSuggestionCallback([this](llvm::StringRef line) { 2795826aa48SNeal (nealsid) return this->SuggestionCallback(line); 2805826aa48SNeal (nealsid) }); 281*080635efSJonas Devlieghere m_editline_up->SetSuggestionAnsiPrefix(ansi::FormatAnsiTerminalCodes( 282*080635efSJonas Devlieghere debugger.GetAutosuggestionAnsiPrefix())); 283*080635efSJonas Devlieghere m_editline_up->SetSuggestionAnsiSuffix(ansi::FormatAnsiTerminalCodes( 284*080635efSJonas Devlieghere debugger.GetAutosuggestionAnsiSuffix())); 2855826aa48SNeal (nealsid) } 286e30f11d9SKate Stone // See if the delegate supports fixing indentation 287e30f11d9SKate Stone const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters(); 288b9c1b51eSKate Stone if (indent_chars) { 289b9c1b51eSKate Stone // The delegate does support indentation, hook it up so when any 29005097246SAdrian Prantl // indentation character is typed, the delegate gets a chance to fix it 2915826aa48SNeal (nealsid) FixIndentationCallbackType f = [this](Editline *editline, 2925826aa48SNeal (nealsid) const StringList &lines, 2935826aa48SNeal (nealsid) int cursor_position) { 2945826aa48SNeal (nealsid) return this->FixIndentationCallback(editline, lines, cursor_position); 2955826aa48SNeal (nealsid) }; 2965826aa48SNeal (nealsid) m_editline_up->SetFixIndentationCallback(std::move(f), indent_chars); 297e30f11d9SKate Stone } 29844d93782SGreg Clayton } 299cacde7dfSTodd Fiala #endif 300e30f11d9SKate Stone SetBaseLineNumber(m_base_line_number); 301514d8cd8SZachary Turner SetPrompt(prompt); 302e30f11d9SKate Stone SetContinuationPrompt(continuation_prompt); 30344d93782SGreg Clayton } 30444d93782SGreg Clayton 305b9c1b51eSKate Stone IOHandlerEditline::~IOHandlerEditline() { 30662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 307d5b44036SJonas Devlieghere m_editline_up.reset(); 308cacde7dfSTodd Fiala #endif 30944d93782SGreg Clayton } 31044d93782SGreg Clayton 311b9c1b51eSKate Stone void IOHandlerEditline::Activate() { 312e30f11d9SKate Stone IOHandler::Activate(); 3130affb582SDave Lee m_delegate.IOHandlerActivated(*this, GetIsInteractive()); 314e30f11d9SKate Stone } 315e30f11d9SKate Stone 316b9c1b51eSKate Stone void IOHandlerEditline::Deactivate() { 317e30f11d9SKate Stone IOHandler::Deactivate(); 318e30f11d9SKate Stone m_delegate.IOHandlerDeactivated(*this); 319e30f11d9SKate Stone } 320e30f11d9SKate Stone 321d9166ad2SFred Riss void IOHandlerEditline::TerminalSizeChanged() { 322d9166ad2SFred Riss #if LLDB_ENABLE_LIBEDIT 323be18df3dSRaphael Isemann if (m_editline_up) 324d9166ad2SFred Riss m_editline_up->TerminalSizeChanged(); 325d9166ad2SFred Riss #endif 326d9166ad2SFred Riss } 327d9166ad2SFred Riss 328b3faa01fSLawrence D'Anna // Split out a line from the buffer, if there is a full one to get. 329b3faa01fSLawrence D'Anna static Optional<std::string> SplitLine(std::string &line_buffer) { 330b3faa01fSLawrence D'Anna size_t pos = line_buffer.find('\n'); 331b3faa01fSLawrence D'Anna if (pos == std::string::npos) 332b3faa01fSLawrence D'Anna return None; 333adcd0268SBenjamin Kramer std::string line = 334adcd0268SBenjamin Kramer std::string(StringRef(line_buffer.c_str(), pos).rtrim("\n\r")); 335b3faa01fSLawrence D'Anna line_buffer = line_buffer.substr(pos + 1); 336b3faa01fSLawrence D'Anna return line; 337b3faa01fSLawrence D'Anna } 338b3faa01fSLawrence D'Anna 339b3faa01fSLawrence D'Anna // If the final line of the file ends without a end-of-line, return 340b3faa01fSLawrence D'Anna // it as a line anyway. 341b3faa01fSLawrence D'Anna static Optional<std::string> SplitLineEOF(std::string &line_buffer) { 342f5eaa2afSRaphael Isemann if (llvm::all_of(line_buffer, llvm::isSpace)) 343b3faa01fSLawrence D'Anna return None; 344b3faa01fSLawrence D'Anna std::string line = std::move(line_buffer); 345b3faa01fSLawrence D'Anna line_buffer.clear(); 346b3faa01fSLawrence D'Anna return line; 347b3faa01fSLawrence D'Anna } 348b3faa01fSLawrence D'Anna 349b9c1b51eSKate Stone bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) { 35062456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 351d5b44036SJonas Devlieghere if (m_editline_up) { 352d77c2e09SJonas Devlieghere bool b = m_editline_up->GetLine(line, interrupted); 353b3faa01fSLawrence D'Anna if (b && m_data_recorder) 354d77c2e09SJonas Devlieghere m_data_recorder->Record(line, true); 355d77c2e09SJonas Devlieghere return b; 356b3faa01fSLawrence D'Anna } 357cacde7dfSTodd Fiala #endif 358b3faa01fSLawrence D'Anna 35944d93782SGreg Clayton line.clear(); 36044d93782SGreg Clayton 361b9c1b51eSKate Stone if (GetIsInteractive()) { 362c5dac77aSEugene Zelenko const char *prompt = nullptr; 363e30f11d9SKate Stone 364e30f11d9SKate Stone if (m_multi_line && m_curr_line_idx > 0) 365e30f11d9SKate Stone prompt = GetContinuationPrompt(); 366e30f11d9SKate Stone 367c5dac77aSEugene Zelenko if (prompt == nullptr) 368e30f11d9SKate Stone prompt = GetPrompt(); 369e30f11d9SKate Stone 370b9c1b51eSKate Stone if (prompt && prompt[0]) { 3715da2bc22SLawrence D'Anna if (m_output_sp) { 3725da2bc22SLawrence D'Anna m_output_sp->Printf("%s", prompt); 3735da2bc22SLawrence D'Anna m_output_sp->Flush(); 37444d93782SGreg Clayton } 37544d93782SGreg Clayton } 37644d93782SGreg Clayton } 377b3faa01fSLawrence D'Anna 378b3faa01fSLawrence D'Anna Optional<std::string> got_line = SplitLine(m_line_buffer); 379b3faa01fSLawrence D'Anna 380b3faa01fSLawrence D'Anna if (!got_line && !m_input_sp) { 381b3faa01fSLawrence D'Anna // No more input file, we are done... 382b3faa01fSLawrence D'Anna SetIsDone(true); 383b3faa01fSLawrence D'Anna return false; 384b3faa01fSLawrence D'Anna } 385b3faa01fSLawrence D'Anna 386b3faa01fSLawrence D'Anna FILE *in = GetInputFILE(); 38744d93782SGreg Clayton char buffer[256]; 388b3faa01fSLawrence D'Anna 389b3faa01fSLawrence D'Anna if (!got_line && !in && m_input_sp) { 390b3faa01fSLawrence D'Anna // there is no FILE*, fall back on just reading bytes from the stream. 391b3faa01fSLawrence D'Anna while (!got_line) { 392b3faa01fSLawrence D'Anna size_t bytes_read = sizeof(buffer); 393b3faa01fSLawrence D'Anna Status error = m_input_sp->Read((void *)buffer, bytes_read); 394b3faa01fSLawrence D'Anna if (error.Success() && !bytes_read) { 395b3faa01fSLawrence D'Anna got_line = SplitLineEOF(m_line_buffer); 396b3faa01fSLawrence D'Anna break; 397b3faa01fSLawrence D'Anna } 398b3faa01fSLawrence D'Anna if (error.Fail()) 399b3faa01fSLawrence D'Anna break; 400b3faa01fSLawrence D'Anna m_line_buffer += StringRef(buffer, bytes_read); 401b3faa01fSLawrence D'Anna got_line = SplitLine(m_line_buffer); 402b3faa01fSLawrence D'Anna } 403b3faa01fSLawrence D'Anna } 404b3faa01fSLawrence D'Anna 405b3faa01fSLawrence D'Anna if (!got_line && in) { 406b3faa01fSLawrence D'Anna while (!got_line) { 407b3faa01fSLawrence D'Anna char *r = fgets(buffer, sizeof(buffer), in); 408e7167908SNathan Lanza #ifdef _WIN32 409e7167908SNathan Lanza // ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED 410e7167908SNathan Lanza // according to the docs on MSDN. However, this has evidently been a 411e7167908SNathan Lanza // known bug since Windows 8. Therefore, we can't detect if a signal 412e7167908SNathan Lanza // interrupted in the fgets. So pressing ctrl-c causes the repl to end 413e7167908SNathan Lanza // and the process to exit. A temporary workaround is just to attempt to 414e7167908SNathan Lanza // fgets twice until this bug is fixed. 415b3faa01fSLawrence D'Anna if (r == nullptr) 416b3faa01fSLawrence D'Anna r = fgets(buffer, sizeof(buffer), in); 417cb305205SNathan Lanza // this is the equivalent of EINTR for Windows 418b3faa01fSLawrence D'Anna if (r == nullptr && GetLastError() == ERROR_OPERATION_ABORTED) 419cb305205SNathan Lanza continue; 420e7167908SNathan Lanza #endif 421b3faa01fSLawrence D'Anna if (r == nullptr) { 422b3faa01fSLawrence D'Anna if (ferror(in) && errno == EINTR) 423b3faa01fSLawrence D'Anna continue; 424c9cf5798SGreg Clayton if (feof(in)) 425b3faa01fSLawrence D'Anna got_line = SplitLineEOF(m_line_buffer); 42644d93782SGreg Clayton break; 42744d93782SGreg Clayton } 428b3faa01fSLawrence D'Anna m_line_buffer += buffer; 429b3faa01fSLawrence D'Anna got_line = SplitLine(m_line_buffer); 43044d93782SGreg Clayton } 431b3faa01fSLawrence D'Anna } 432b3faa01fSLawrence D'Anna 433b3faa01fSLawrence D'Anna if (got_line) { 434b3faa01fSLawrence D'Anna line = got_line.getValue(); 435b3faa01fSLawrence D'Anna if (m_data_recorder) 436d77c2e09SJonas Devlieghere m_data_recorder->Record(line, true); 43744d93782SGreg Clayton } 438b3faa01fSLawrence D'Anna 439b3faa01fSLawrence D'Anna return (bool)got_line; 44044d93782SGreg Clayton } 44144d93782SGreg Clayton 44262456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 443b9c1b51eSKate Stone bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline, 4445826aa48SNeal (nealsid) StringList &lines) { 4455826aa48SNeal (nealsid) return m_delegate.IOHandlerIsInputComplete(*this, lines); 446e30f11d9SKate Stone } 447e30f11d9SKate Stone 448b9c1b51eSKate Stone int IOHandlerEditline::FixIndentationCallback(Editline *editline, 449e30f11d9SKate Stone const StringList &lines, 4505826aa48SNeal (nealsid) int cursor_position) { 4515826aa48SNeal (nealsid) return m_delegate.IOHandlerFixIndentation(*this, lines, cursor_position); 45244d93782SGreg Clayton } 45344d93782SGreg Clayton 454de9e8502SShu Anzai llvm::Optional<std::string> 4555826aa48SNeal (nealsid) IOHandlerEditline::SuggestionCallback(llvm::StringRef line) { 4565826aa48SNeal (nealsid) return m_delegate.IOHandlerSuggestion(*this, line); 457de9e8502SShu Anzai } 458de9e8502SShu Anzai 4595826aa48SNeal (nealsid) void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request) { 4605826aa48SNeal (nealsid) m_delegate.IOHandlerComplete(*this, request); 46144d93782SGreg Clayton } 462cacde7dfSTodd Fiala #endif 46344d93782SGreg Clayton 464b9c1b51eSKate Stone const char *IOHandlerEditline::GetPrompt() { 46562456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 466d5b44036SJonas Devlieghere if (m_editline_up) { 467d5b44036SJonas Devlieghere return m_editline_up->GetPrompt(); 468b9c1b51eSKate Stone } else { 469cacde7dfSTodd Fiala #endif 470cacde7dfSTodd Fiala if (m_prompt.empty()) 471c5dac77aSEugene Zelenko return nullptr; 47262456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 473cacde7dfSTodd Fiala } 474cacde7dfSTodd Fiala #endif 47544d93782SGreg Clayton return m_prompt.c_str(); 47644d93782SGreg Clayton } 47744d93782SGreg Clayton 478514d8cd8SZachary Turner bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) { 479adcd0268SBenjamin Kramer m_prompt = std::string(prompt); 480514d8cd8SZachary Turner 48162456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 482d5b44036SJonas Devlieghere if (m_editline_up) 483d5b44036SJonas Devlieghere m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str()); 484cacde7dfSTodd Fiala #endif 48544d93782SGreg Clayton return true; 48644d93782SGreg Clayton } 48744d93782SGreg Clayton 488b9c1b51eSKate Stone const char *IOHandlerEditline::GetContinuationPrompt() { 489b9c1b51eSKate Stone return (m_continuation_prompt.empty() ? nullptr 490b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 491e30f11d9SKate Stone } 492e30f11d9SKate Stone 493514d8cd8SZachary Turner void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) { 494adcd0268SBenjamin Kramer m_continuation_prompt = std::string(prompt); 495e30f11d9SKate Stone 49662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 497d5b44036SJonas Devlieghere if (m_editline_up) 498d5b44036SJonas Devlieghere m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty() 499b9c1b51eSKate Stone ? nullptr 500b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 501d553d00cSZachary Turner #endif 502e30f11d9SKate Stone } 503e30f11d9SKate Stone 504b9c1b51eSKate Stone void IOHandlerEditline::SetBaseLineNumber(uint32_t line) { 505f6913cd7SGreg Clayton m_base_line_number = line; 506f6913cd7SGreg Clayton } 507e30f11d9SKate Stone 508b9c1b51eSKate Stone uint32_t IOHandlerEditline::GetCurrentLineIndex() const { 50962456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 510d5b44036SJonas Devlieghere if (m_editline_up) 511d5b44036SJonas Devlieghere return m_editline_up->GetCurrentLine(); 512e30f11d9SKate Stone #endif 513e30f11d9SKate Stone return m_curr_line_idx; 514e30f11d9SKate Stone } 515e30f11d9SKate Stone 516b9c1b51eSKate Stone bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) { 517e30f11d9SKate Stone m_current_lines_ptr = &lines; 518e30f11d9SKate Stone 51944d93782SGreg Clayton bool success = false; 52062456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 521d5b44036SJonas Devlieghere if (m_editline_up) { 522d5b44036SJonas Devlieghere return m_editline_up->GetLines(m_base_line_number, lines, interrupted); 523b9c1b51eSKate Stone } else { 524cacde7dfSTodd Fiala #endif 525e30f11d9SKate Stone bool done = false; 52697206d57SZachary Turner Status error; 52744d93782SGreg Clayton 528b9c1b51eSKate Stone while (!done) { 529f6913cd7SGreg Clayton // Show line numbers if we are asked to 53044d93782SGreg Clayton std::string line; 531b9c1b51eSKate Stone if (m_base_line_number > 0 && GetIsInteractive()) { 5325da2bc22SLawrence D'Anna if (m_output_sp) { 5335da2bc22SLawrence D'Anna m_output_sp->Printf("%u%s", 5345da2bc22SLawrence D'Anna m_base_line_number + (uint32_t)lines.GetSize(), 535b9c1b51eSKate Stone GetPrompt() == nullptr ? " " : ""); 536f6913cd7SGreg Clayton } 5375da2bc22SLawrence D'Anna } 538f6913cd7SGreg Clayton 539e30f11d9SKate Stone m_curr_line_idx = lines.GetSize(); 540e30f11d9SKate Stone 541f0066ad0SGreg Clayton bool interrupted = false; 542b9c1b51eSKate Stone if (GetLine(line, interrupted) && !interrupted) { 54344d93782SGreg Clayton lines.AppendString(line); 544e30f11d9SKate Stone done = m_delegate.IOHandlerIsInputComplete(*this, lines); 545b9c1b51eSKate Stone } else { 546e30f11d9SKate Stone done = true; 54744d93782SGreg Clayton } 54844d93782SGreg Clayton } 54944d93782SGreg Clayton success = lines.GetSize() > 0; 55062456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 55144d93782SGreg Clayton } 552cacde7dfSTodd Fiala #endif 55344d93782SGreg Clayton return success; 55444d93782SGreg Clayton } 55544d93782SGreg Clayton 55605097246SAdrian Prantl // Each IOHandler gets to run until it is done. It should read data from the 55705097246SAdrian Prantl // "in" and place output into "out" and "err and return when done. 558b9c1b51eSKate Stone void IOHandlerEditline::Run() { 55944d93782SGreg Clayton std::string line; 560b9c1b51eSKate Stone while (IsActive()) { 561f0066ad0SGreg Clayton bool interrupted = false; 562b9c1b51eSKate Stone if (m_multi_line) { 56344d93782SGreg Clayton StringList lines; 564b9c1b51eSKate Stone if (GetLines(lines, interrupted)) { 565b9c1b51eSKate Stone if (interrupted) { 566e30f11d9SKate Stone m_done = m_interrupt_exits; 567e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 568e30f11d9SKate Stone 569b9c1b51eSKate Stone } else { 57044d93782SGreg Clayton line = lines.CopyList(); 57144d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 57244d93782SGreg Clayton } 573b9c1b51eSKate Stone } else { 57444d93782SGreg Clayton m_done = true; 57544d93782SGreg Clayton } 576b9c1b51eSKate Stone } else { 577b9c1b51eSKate Stone if (GetLine(line, interrupted)) { 578e30f11d9SKate Stone if (interrupted) 579e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 580e30f11d9SKate Stone else 58144d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 582b9c1b51eSKate Stone } else { 58344d93782SGreg Clayton m_done = true; 58444d93782SGreg Clayton } 58544d93782SGreg Clayton } 58644d93782SGreg Clayton } 58744d93782SGreg Clayton } 58844d93782SGreg Clayton 589b9c1b51eSKate Stone void IOHandlerEditline::Cancel() { 59062456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 591d5b44036SJonas Devlieghere if (m_editline_up) 592d5b44036SJonas Devlieghere m_editline_up->Cancel(); 593cacde7dfSTodd Fiala #endif 594e68f5d6bSGreg Clayton } 595e68f5d6bSGreg Clayton 596b9c1b51eSKate Stone bool IOHandlerEditline::Interrupt() { 597f0066ad0SGreg Clayton // Let the delgate handle it first 598f0066ad0SGreg Clayton if (m_delegate.IOHandlerInterrupt(*this)) 599f0066ad0SGreg Clayton return true; 600f0066ad0SGreg Clayton 60162456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 602d5b44036SJonas Devlieghere if (m_editline_up) 603d5b44036SJonas Devlieghere return m_editline_up->Interrupt(); 604cacde7dfSTodd Fiala #endif 605f0066ad0SGreg Clayton return false; 60644d93782SGreg Clayton } 60744d93782SGreg Clayton 608b9c1b51eSKate Stone void IOHandlerEditline::GotEOF() { 60962456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 610d5b44036SJonas Devlieghere if (m_editline_up) 611d5b44036SJonas Devlieghere m_editline_up->Interrupt(); 612cacde7dfSTodd Fiala #endif 61344d93782SGreg Clayton } 61444d93782SGreg Clayton 615b9c1b51eSKate Stone void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) { 61662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 617d5b44036SJonas Devlieghere if (m_editline_up) 618d5b44036SJonas Devlieghere m_editline_up->PrintAsync(stream, s, len); 6194446487dSPavel Labath else 6204446487dSPavel Labath #endif 621fab31220STed Woodward { 6228b98f12aSMartin Storsjo #ifdef _WIN32 623341e4789SDawn Perchik const char *prompt = GetPrompt(); 624b9c1b51eSKate Stone if (prompt) { 625fab31220STed Woodward // Back up over previous prompt using Windows API 626fab31220STed Woodward CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; 627fab31220STed Woodward HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); 628fab31220STed Woodward GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info); 629fab31220STed Woodward COORD coord = screen_buffer_info.dwCursorPosition; 630fab31220STed Woodward coord.X -= strlen(prompt); 631fab31220STed Woodward if (coord.X < 0) 632fab31220STed Woodward coord.X = 0; 633fab31220STed Woodward SetConsoleCursorPosition(console_handle, coord); 634fab31220STed Woodward } 635fab31220STed Woodward #endif 6364446487dSPavel Labath IOHandler::PrintAsync(stream, s, len); 6378b98f12aSMartin Storsjo #ifdef _WIN32 638fab31220STed Woodward if (prompt) 6397ca15ba7SLawrence D'Anna IOHandler::PrintAsync(GetOutputStreamFileSP().get(), prompt, 640b9c1b51eSKate Stone strlen(prompt)); 641341e4789SDawn Perchik #endif 642fab31220STed Woodward } 6434446487dSPavel Labath } 644