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" 21*bb894b97SJonas Devlieghere #include "lldb/Utility/ReproducerProvider.h" 22672d2c12SJonas Devlieghere #include "lldb/Utility/Status.h" 23672d2c12SJonas Devlieghere #include "lldb/Utility/StreamString.h" 24672d2c12SJonas Devlieghere #include "lldb/Utility/StringList.h" 25672d2c12SJonas Devlieghere #include "lldb/lldb-forward.h" 262f3df613SZachary Turner 2762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 2844d93782SGreg Clayton #include "lldb/Host/Editline.h" 29cacde7dfSTodd Fiala #endif 3044d93782SGreg Clayton #include "lldb/Interpreter/CommandCompletions.h" 3144d93782SGreg Clayton #include "lldb/Interpreter/CommandInterpreter.h" 32672d2c12SJonas Devlieghere #include "llvm/ADT/StringRef.h" 332f3df613SZachary Turner 345c82608dSHaibo Huang #ifdef _WIN32 35aaea8ee6SZachary Turner #include "lldb/Host/windows/windows.h" 36fab31220STed Woodward #endif 37fab31220STed Woodward 38672d2c12SJonas Devlieghere #include <memory> 39672d2c12SJonas Devlieghere #include <mutex> 402f3df613SZachary Turner 41672d2c12SJonas Devlieghere #include <assert.h> 42672d2c12SJonas Devlieghere #include <ctype.h> 43672d2c12SJonas Devlieghere #include <errno.h> 44672d2c12SJonas Devlieghere #include <locale.h> 45672d2c12SJonas Devlieghere #include <stdint.h> 46672d2c12SJonas Devlieghere #include <stdio.h> 47672d2c12SJonas Devlieghere #include <string.h> 48672d2c12SJonas Devlieghere #include <type_traits> 492f3df613SZachary Turner 5044d93782SGreg Clayton using namespace lldb; 5144d93782SGreg Clayton using namespace lldb_private; 52b3faa01fSLawrence D'Anna using llvm::None; 53b3faa01fSLawrence D'Anna using llvm::Optional; 54b3faa01fSLawrence D'Anna using llvm::StringRef; 55b3faa01fSLawrence D'Anna 56b9c1b51eSKate Stone IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type) 57b9c1b51eSKate Stone : IOHandler(debugger, type, 587ca15ba7SLawrence D'Anna FileSP(), // Adopt STDIN from top input reader 5944d93782SGreg Clayton StreamFileSP(), // Adopt STDOUT from top input reader 60340b0309SGreg Clayton StreamFileSP(), // Adopt STDERR from top input reader 61d77c2e09SJonas Devlieghere 0, // Flags 62d77c2e09SJonas Devlieghere nullptr // Shadow file recorder 63d77c2e09SJonas Devlieghere ) {} 6444d93782SGreg Clayton 65b9c1b51eSKate Stone IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type, 667ca15ba7SLawrence D'Anna const lldb::FileSP &input_sp, 6744d93782SGreg Clayton const lldb::StreamFileSP &output_sp, 68d77c2e09SJonas Devlieghere const lldb::StreamFileSP &error_sp, uint32_t flags, 69d77c2e09SJonas Devlieghere repro::DataRecorder *data_recorder) 70b9c1b51eSKate Stone : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp), 71d77c2e09SJonas Devlieghere m_error_sp(error_sp), m_data_recorder(data_recorder), m_popped(false), 72d77c2e09SJonas Devlieghere m_flags(flags), m_type(type), m_user_data(nullptr), m_done(false), 73d77c2e09SJonas Devlieghere m_active(false) { 7444d93782SGreg Clayton // If any files are not specified, then adopt them from the top input reader. 7544d93782SGreg Clayton if (!m_input_sp || !m_output_sp || !m_error_sp) 76b9c1b51eSKate Stone debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp, 7744d93782SGreg Clayton m_error_sp); 7844d93782SGreg Clayton } 7944d93782SGreg Clayton 80315b6884SEugene Zelenko IOHandler::~IOHandler() = default; 8144d93782SGreg Clayton 82b9c1b51eSKate Stone int IOHandler::GetInputFD() { 837ca15ba7SLawrence D'Anna return (m_input_sp ? m_input_sp->GetDescriptor() : -1); 8444d93782SGreg Clayton } 8544d93782SGreg Clayton 86b9c1b51eSKate Stone int IOHandler::GetOutputFD() { 87c5dac77aSEugene Zelenko return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1); 8844d93782SGreg Clayton } 8944d93782SGreg Clayton 90b9c1b51eSKate Stone int IOHandler::GetErrorFD() { 91c5dac77aSEugene Zelenko return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1); 9244d93782SGreg Clayton } 9344d93782SGreg Clayton 94b9c1b51eSKate Stone FILE *IOHandler::GetInputFILE() { 957ca15ba7SLawrence D'Anna return (m_input_sp ? m_input_sp->GetStream() : nullptr); 9644d93782SGreg Clayton } 9744d93782SGreg Clayton 98b9c1b51eSKate Stone FILE *IOHandler::GetOutputFILE() { 99c5dac77aSEugene Zelenko return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr); 10044d93782SGreg Clayton } 10144d93782SGreg Clayton 102b9c1b51eSKate Stone FILE *IOHandler::GetErrorFILE() { 103c5dac77aSEugene Zelenko return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr); 10444d93782SGreg Clayton } 10544d93782SGreg Clayton 1067ca15ba7SLawrence D'Anna FileSP &IOHandler::GetInputFileSP() { return m_input_sp; } 10744d93782SGreg Clayton 1087ca15ba7SLawrence D'Anna StreamFileSP &IOHandler::GetOutputStreamFileSP() { return m_output_sp; } 10944d93782SGreg Clayton 1107ca15ba7SLawrence D'Anna StreamFileSP &IOHandler::GetErrorStreamFileSP() { return m_error_sp; } 11144d93782SGreg Clayton 112b9c1b51eSKate Stone bool IOHandler::GetIsInteractive() { 1137ca15ba7SLawrence D'Anna return GetInputFileSP() ? GetInputFileSP()->GetIsInteractive() : false; 114340b0309SGreg Clayton } 115340b0309SGreg Clayton 116b9c1b51eSKate Stone bool IOHandler::GetIsRealTerminal() { 1177ca15ba7SLawrence D'Anna return GetInputFileSP() ? GetInputFileSP()->GetIsRealTerminal() : false; 118340b0309SGreg Clayton } 11944d93782SGreg Clayton 120b9c1b51eSKate Stone void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); } 121e30f11d9SKate Stone 122b9c1b51eSKate Stone void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); } 123e30f11d9SKate Stone 124b9c1b51eSKate Stone void IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len) { 125b9c1b51eSKate Stone if (stream) { 12616ff8604SSaleem Abdulrasool std::lock_guard<std::recursive_mutex> guard(m_mutex); 1274446487dSPavel Labath if (m_top) 1284446487dSPavel Labath m_top->PrintAsync(stream, s, len); 1294deea652SPavel Labath else 1304deea652SPavel Labath stream->Write(s, len); 1314446487dSPavel Labath } 1324446487dSPavel Labath } 1334446487dSPavel Labath 1347a120c8bSZachary Turner IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt, 135b9c1b51eSKate Stone bool default_response) 136b9c1b51eSKate Stone : IOHandlerEditline( 137b9c1b51eSKate Stone debugger, IOHandler::Type::Confirm, 138c5dac77aSEugene Zelenko nullptr, // nullptr editline_name means no history loaded/saved 139514d8cd8SZachary Turner llvm::StringRef(), // No prompt 140514d8cd8SZachary Turner llvm::StringRef(), // No continuation prompt 14144d93782SGreg Clayton false, // Multi-line 142e30f11d9SKate Stone false, // Don't colorize the prompt (i.e. the confirm message.) 143d77c2e09SJonas Devlieghere 0, *this, nullptr), 144b9c1b51eSKate Stone m_default_response(default_response), m_user_response(default_response) { 14544d93782SGreg Clayton StreamString prompt_stream; 14644d93782SGreg Clayton prompt_stream.PutCString(prompt); 14744d93782SGreg Clayton if (m_default_response) 14844d93782SGreg Clayton prompt_stream.Printf(": [Y/n] "); 14944d93782SGreg Clayton else 15044d93782SGreg Clayton prompt_stream.Printf(": [y/N] "); 15144d93782SGreg Clayton 152514d8cd8SZachary Turner SetPrompt(prompt_stream.GetString()); 15344d93782SGreg Clayton } 15444d93782SGreg Clayton 155315b6884SEugene Zelenko IOHandlerConfirm::~IOHandlerConfirm() = default; 15644d93782SGreg Clayton 157ae34ed2cSRaphael Isemann void IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler, 1582fc20f65SRaphael Isemann CompletionRequest &request) { 1591153dc96SRaphael Isemann if (request.GetRawCursorPos() != 0) 1601153dc96SRaphael Isemann return; 1611153dc96SRaphael Isemann request.AddCompletion(m_default_response ? "y" : "n"); 16244d93782SGreg Clayton } 16344d93782SGreg Clayton 164b9c1b51eSKate Stone void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler, 165b9c1b51eSKate Stone std::string &line) { 166b9c1b51eSKate Stone if (line.empty()) { 16744d93782SGreg Clayton // User just hit enter, set the response to the default 16844d93782SGreg Clayton m_user_response = m_default_response; 16944d93782SGreg Clayton io_handler.SetIsDone(true); 17044d93782SGreg Clayton return; 17144d93782SGreg Clayton } 17244d93782SGreg Clayton 173b9c1b51eSKate Stone if (line.size() == 1) { 174b9c1b51eSKate Stone switch (line[0]) { 17544d93782SGreg Clayton case 'y': 17644d93782SGreg Clayton case 'Y': 17744d93782SGreg Clayton m_user_response = true; 17844d93782SGreg Clayton io_handler.SetIsDone(true); 17944d93782SGreg Clayton return; 18044d93782SGreg Clayton case 'n': 18144d93782SGreg Clayton case 'N': 18244d93782SGreg Clayton m_user_response = false; 18344d93782SGreg Clayton io_handler.SetIsDone(true); 18444d93782SGreg Clayton return; 18544d93782SGreg Clayton default: 18644d93782SGreg Clayton break; 18744d93782SGreg Clayton } 18844d93782SGreg Clayton } 18944d93782SGreg Clayton 190b9c1b51eSKate Stone if (line == "yes" || line == "YES" || line == "Yes") { 19144d93782SGreg Clayton m_user_response = true; 19244d93782SGreg Clayton io_handler.SetIsDone(true); 193b9c1b51eSKate Stone } else if (line == "no" || line == "NO" || line == "No") { 19444d93782SGreg Clayton m_user_response = false; 19544d93782SGreg Clayton io_handler.SetIsDone(true); 19644d93782SGreg Clayton } 19744d93782SGreg Clayton } 19844d93782SGreg Clayton 199de9e8502SShu Anzai llvm::Optional<std::string> 200de9e8502SShu Anzai IOHandlerDelegate::IOHandlerSuggestion(IOHandler &io_handler, 201de9e8502SShu Anzai llvm::StringRef line) { 202de9e8502SShu Anzai return io_handler.GetDebugger() 203de9e8502SShu Anzai .GetCommandInterpreter() 204de9e8502SShu Anzai .GetAutoSuggestionForCommand(line); 205de9e8502SShu Anzai } 206de9e8502SShu Anzai 207ae34ed2cSRaphael Isemann void IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler, 2082fc20f65SRaphael Isemann CompletionRequest &request) { 209b9c1b51eSKate Stone switch (m_completion) { 21044d93782SGreg Clayton case Completion::None: 21144d93782SGreg Clayton break; 21244d93782SGreg Clayton case Completion::LLDBCommand: 213ae34ed2cSRaphael Isemann io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(request); 214ae34ed2cSRaphael Isemann break; 215ae34ed2cSRaphael Isemann case Completion::Expression: 216b9c1b51eSKate Stone CommandCompletions::InvokeCommonCompletionCallbacks( 217b9c1b51eSKate Stone io_handler.GetDebugger().GetCommandInterpreter(), 218ae34ed2cSRaphael Isemann CommandCompletions::eVariablePathCompletion, request, nullptr); 219ae34ed2cSRaphael Isemann break; 22044d93782SGreg Clayton } 22144d93782SGreg Clayton } 22244d93782SGreg Clayton 223b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 224b9c1b51eSKate Stone Debugger &debugger, IOHandler::Type type, 22544d93782SGreg Clayton const char *editline_name, // Used for saving history files 226514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 227514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 228d77c2e09SJonas Devlieghere IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder) 229b9c1b51eSKate Stone : IOHandlerEditline(debugger, type, 2307ca15ba7SLawrence D'Anna FileSP(), // Inherit input from top input reader 23144d93782SGreg Clayton StreamFileSP(), // Inherit output from top input reader 23244d93782SGreg Clayton StreamFileSP(), // Inherit error from top input reader 233340b0309SGreg Clayton 0, // Flags 23444d93782SGreg Clayton editline_name, // Used for saving history files 235b9c1b51eSKate Stone prompt, continuation_prompt, multi_line, color_prompts, 236d77c2e09SJonas Devlieghere line_number_start, delegate, data_recorder) {} 23744d93782SGreg Clayton 238b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 2397ca15ba7SLawrence D'Anna Debugger &debugger, IOHandler::Type type, const lldb::FileSP &input_sp, 2407ca15ba7SLawrence D'Anna const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp, 2417ca15ba7SLawrence D'Anna uint32_t flags, 24244d93782SGreg Clayton const char *editline_name, // Used for saving history files 243514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 244514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 245d77c2e09SJonas Devlieghere IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder) 246d77c2e09SJonas Devlieghere : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags, 247d77c2e09SJonas Devlieghere data_recorder), 24862456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 249d5b44036SJonas Devlieghere m_editline_up(), 250cacde7dfSTodd Fiala #endif 251b9c1b51eSKate Stone m_delegate(delegate), m_prompt(), m_continuation_prompt(), 252b9c1b51eSKate Stone m_current_lines_ptr(nullptr), m_base_line_number(line_number_start), 253b9c1b51eSKate Stone m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line), 254b9c1b51eSKate Stone m_color_prompts(color_prompts), m_interrupt_exits(true), 255b9c1b51eSKate Stone m_editing(false) { 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); 268d5b44036SJonas Devlieghere m_editline_up->SetIsInputCompleteCallback(IsInputCompleteCallback, this); 269d5b44036SJonas Devlieghere m_editline_up->SetAutoCompleteCallback(AutoCompleteCallback, this); 270de9e8502SShu Anzai if (debugger.GetUseAutosuggestion() && debugger.GetUseColor()) 271de9e8502SShu Anzai m_editline_up->SetSuggestionCallback(SuggestionCallback, this); 272e30f11d9SKate Stone // See if the delegate supports fixing indentation 273e30f11d9SKate Stone const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters(); 274b9c1b51eSKate Stone if (indent_chars) { 275b9c1b51eSKate Stone // The delegate does support indentation, hook it up so when any 27605097246SAdrian Prantl // indentation character is typed, the delegate gets a chance to fix it 277d5b44036SJonas Devlieghere m_editline_up->SetFixIndentationCallback(FixIndentationCallback, this, 278b9c1b51eSKate Stone indent_chars); 279e30f11d9SKate Stone } 28044d93782SGreg Clayton } 281cacde7dfSTodd Fiala #endif 282e30f11d9SKate Stone SetBaseLineNumber(m_base_line_number); 283514d8cd8SZachary Turner SetPrompt(prompt); 284e30f11d9SKate Stone SetContinuationPrompt(continuation_prompt); 28544d93782SGreg Clayton } 28644d93782SGreg Clayton 287b9c1b51eSKate Stone IOHandlerEditline::~IOHandlerEditline() { 28862456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 289d5b44036SJonas Devlieghere m_editline_up.reset(); 290cacde7dfSTodd Fiala #endif 29144d93782SGreg Clayton } 29244d93782SGreg Clayton 293b9c1b51eSKate Stone void IOHandlerEditline::Activate() { 294e30f11d9SKate Stone IOHandler::Activate(); 2950affb582SDave Lee m_delegate.IOHandlerActivated(*this, GetIsInteractive()); 296e30f11d9SKate Stone } 297e30f11d9SKate Stone 298b9c1b51eSKate Stone void IOHandlerEditline::Deactivate() { 299e30f11d9SKate Stone IOHandler::Deactivate(); 300e30f11d9SKate Stone m_delegate.IOHandlerDeactivated(*this); 301e30f11d9SKate Stone } 302e30f11d9SKate Stone 303d9166ad2SFred Riss void IOHandlerEditline::TerminalSizeChanged() { 304d9166ad2SFred Riss #if LLDB_ENABLE_LIBEDIT 305be18df3dSRaphael Isemann if (m_editline_up) 306d9166ad2SFred Riss m_editline_up->TerminalSizeChanged(); 307d9166ad2SFred Riss #endif 308d9166ad2SFred Riss } 309d9166ad2SFred Riss 310b3faa01fSLawrence D'Anna // Split out a line from the buffer, if there is a full one to get. 311b3faa01fSLawrence D'Anna static Optional<std::string> SplitLine(std::string &line_buffer) { 312b3faa01fSLawrence D'Anna size_t pos = line_buffer.find('\n'); 313b3faa01fSLawrence D'Anna if (pos == std::string::npos) 314b3faa01fSLawrence D'Anna return None; 315adcd0268SBenjamin Kramer std::string line = 316adcd0268SBenjamin Kramer std::string(StringRef(line_buffer.c_str(), pos).rtrim("\n\r")); 317b3faa01fSLawrence D'Anna line_buffer = line_buffer.substr(pos + 1); 318b3faa01fSLawrence D'Anna return line; 319b3faa01fSLawrence D'Anna } 320b3faa01fSLawrence D'Anna 321b3faa01fSLawrence D'Anna // If the final line of the file ends without a end-of-line, return 322b3faa01fSLawrence D'Anna // it as a line anyway. 323b3faa01fSLawrence D'Anna static Optional<std::string> SplitLineEOF(std::string &line_buffer) { 324f5eaa2afSRaphael Isemann if (llvm::all_of(line_buffer, llvm::isSpace)) 325b3faa01fSLawrence D'Anna return None; 326b3faa01fSLawrence D'Anna std::string line = std::move(line_buffer); 327b3faa01fSLawrence D'Anna line_buffer.clear(); 328b3faa01fSLawrence D'Anna return line; 329b3faa01fSLawrence D'Anna } 330b3faa01fSLawrence D'Anna 331b9c1b51eSKate Stone bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) { 33262456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 333d5b44036SJonas Devlieghere if (m_editline_up) { 334d77c2e09SJonas Devlieghere bool b = m_editline_up->GetLine(line, interrupted); 335b3faa01fSLawrence D'Anna if (b && m_data_recorder) 336d77c2e09SJonas Devlieghere m_data_recorder->Record(line, true); 337d77c2e09SJonas Devlieghere return b; 338b3faa01fSLawrence D'Anna } 339cacde7dfSTodd Fiala #endif 340b3faa01fSLawrence D'Anna 34144d93782SGreg Clayton line.clear(); 34244d93782SGreg Clayton 343b9c1b51eSKate Stone if (GetIsInteractive()) { 344c5dac77aSEugene Zelenko const char *prompt = nullptr; 345e30f11d9SKate Stone 346e30f11d9SKate Stone if (m_multi_line && m_curr_line_idx > 0) 347e30f11d9SKate Stone prompt = GetContinuationPrompt(); 348e30f11d9SKate Stone 349c5dac77aSEugene Zelenko if (prompt == nullptr) 350e30f11d9SKate Stone prompt = GetPrompt(); 351e30f11d9SKate Stone 352b9c1b51eSKate Stone if (prompt && prompt[0]) { 3535da2bc22SLawrence D'Anna if (m_output_sp) { 3545da2bc22SLawrence D'Anna m_output_sp->Printf("%s", prompt); 3555da2bc22SLawrence D'Anna m_output_sp->Flush(); 35644d93782SGreg Clayton } 35744d93782SGreg Clayton } 35844d93782SGreg Clayton } 359b3faa01fSLawrence D'Anna 360b3faa01fSLawrence D'Anna Optional<std::string> got_line = SplitLine(m_line_buffer); 361b3faa01fSLawrence D'Anna 362b3faa01fSLawrence D'Anna if (!got_line && !m_input_sp) { 363b3faa01fSLawrence D'Anna // No more input file, we are done... 364b3faa01fSLawrence D'Anna SetIsDone(true); 365b3faa01fSLawrence D'Anna return false; 366b3faa01fSLawrence D'Anna } 367b3faa01fSLawrence D'Anna 368b3faa01fSLawrence D'Anna FILE *in = GetInputFILE(); 36944d93782SGreg Clayton char buffer[256]; 370b3faa01fSLawrence D'Anna 371b3faa01fSLawrence D'Anna if (!got_line && !in && m_input_sp) { 372b3faa01fSLawrence D'Anna // there is no FILE*, fall back on just reading bytes from the stream. 373b3faa01fSLawrence D'Anna while (!got_line) { 374b3faa01fSLawrence D'Anna size_t bytes_read = sizeof(buffer); 375b3faa01fSLawrence D'Anna Status error = m_input_sp->Read((void *)buffer, bytes_read); 376b3faa01fSLawrence D'Anna if (error.Success() && !bytes_read) { 377b3faa01fSLawrence D'Anna got_line = SplitLineEOF(m_line_buffer); 378b3faa01fSLawrence D'Anna break; 379b3faa01fSLawrence D'Anna } 380b3faa01fSLawrence D'Anna if (error.Fail()) 381b3faa01fSLawrence D'Anna break; 382b3faa01fSLawrence D'Anna m_line_buffer += StringRef(buffer, bytes_read); 383b3faa01fSLawrence D'Anna got_line = SplitLine(m_line_buffer); 384b3faa01fSLawrence D'Anna } 385b3faa01fSLawrence D'Anna } 386b3faa01fSLawrence D'Anna 387b3faa01fSLawrence D'Anna if (!got_line && in) { 388e034a04eSGreg Clayton m_editing = true; 389b3faa01fSLawrence D'Anna while (!got_line) { 390b3faa01fSLawrence D'Anna char *r = fgets(buffer, sizeof(buffer), in); 391e7167908SNathan Lanza #ifdef _WIN32 392e7167908SNathan Lanza // ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED 393e7167908SNathan Lanza // according to the docs on MSDN. However, this has evidently been a 394e7167908SNathan Lanza // known bug since Windows 8. Therefore, we can't detect if a signal 395e7167908SNathan Lanza // interrupted in the fgets. So pressing ctrl-c causes the repl to end 396e7167908SNathan Lanza // and the process to exit. A temporary workaround is just to attempt to 397e7167908SNathan Lanza // fgets twice until this bug is fixed. 398b3faa01fSLawrence D'Anna if (r == nullptr) 399b3faa01fSLawrence D'Anna r = fgets(buffer, sizeof(buffer), in); 400cb305205SNathan Lanza // this is the equivalent of EINTR for Windows 401b3faa01fSLawrence D'Anna if (r == nullptr && GetLastError() == ERROR_OPERATION_ABORTED) 402cb305205SNathan Lanza continue; 403e7167908SNathan Lanza #endif 404b3faa01fSLawrence D'Anna if (r == nullptr) { 405b3faa01fSLawrence D'Anna if (ferror(in) && errno == EINTR) 406b3faa01fSLawrence D'Anna continue; 407c9cf5798SGreg Clayton if (feof(in)) 408b3faa01fSLawrence D'Anna got_line = SplitLineEOF(m_line_buffer); 40944d93782SGreg Clayton break; 41044d93782SGreg Clayton } 411b3faa01fSLawrence D'Anna m_line_buffer += buffer; 412b3faa01fSLawrence D'Anna got_line = SplitLine(m_line_buffer); 41344d93782SGreg Clayton } 414e034a04eSGreg Clayton m_editing = false; 415b3faa01fSLawrence D'Anna } 416b3faa01fSLawrence D'Anna 417b3faa01fSLawrence D'Anna if (got_line) { 418b3faa01fSLawrence D'Anna line = got_line.getValue(); 419b3faa01fSLawrence D'Anna if (m_data_recorder) 420d77c2e09SJonas Devlieghere m_data_recorder->Record(line, true); 42144d93782SGreg Clayton } 422b3faa01fSLawrence D'Anna 423b3faa01fSLawrence D'Anna return (bool)got_line; 42444d93782SGreg Clayton } 42544d93782SGreg Clayton 42662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 427b9c1b51eSKate Stone bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline, 42844d93782SGreg Clayton StringList &lines, 429b9c1b51eSKate Stone void *baton) { 43044d93782SGreg Clayton IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 431b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, 432b9c1b51eSKate Stone lines); 433e30f11d9SKate Stone } 434e30f11d9SKate Stone 435b9c1b51eSKate Stone int IOHandlerEditline::FixIndentationCallback(Editline *editline, 436e30f11d9SKate Stone const StringList &lines, 437e30f11d9SKate Stone int cursor_position, 438b9c1b51eSKate Stone void *baton) { 439e30f11d9SKate Stone IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 440b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerFixIndentation( 441b9c1b51eSKate Stone *editline_reader, lines, cursor_position); 44244d93782SGreg Clayton } 44344d93782SGreg Clayton 444de9e8502SShu Anzai llvm::Optional<std::string> 445de9e8502SShu Anzai IOHandlerEditline::SuggestionCallback(llvm::StringRef line, void *baton) { 446de9e8502SShu Anzai IOHandlerEditline *editline_reader = static_cast<IOHandlerEditline *>(baton); 447de9e8502SShu Anzai if (editline_reader) 448de9e8502SShu Anzai return editline_reader->m_delegate.IOHandlerSuggestion(*editline_reader, 449de9e8502SShu Anzai line); 450de9e8502SShu Anzai 451de9e8502SShu Anzai return llvm::None; 452de9e8502SShu Anzai } 453de9e8502SShu Anzai 454ae34ed2cSRaphael Isemann void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request, 4552fc20f65SRaphael Isemann void *baton) { 45644d93782SGreg Clayton IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 45744d93782SGreg Clayton if (editline_reader) 458ae34ed2cSRaphael Isemann editline_reader->m_delegate.IOHandlerComplete(*editline_reader, request); 45944d93782SGreg Clayton } 460cacde7dfSTodd Fiala #endif 46144d93782SGreg Clayton 462b9c1b51eSKate Stone const char *IOHandlerEditline::GetPrompt() { 46362456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 464d5b44036SJonas Devlieghere if (m_editline_up) { 465d5b44036SJonas Devlieghere return m_editline_up->GetPrompt(); 466b9c1b51eSKate Stone } else { 467cacde7dfSTodd Fiala #endif 468cacde7dfSTodd Fiala if (m_prompt.empty()) 469c5dac77aSEugene Zelenko return nullptr; 47062456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 471cacde7dfSTodd Fiala } 472cacde7dfSTodd Fiala #endif 47344d93782SGreg Clayton return m_prompt.c_str(); 47444d93782SGreg Clayton } 47544d93782SGreg Clayton 476514d8cd8SZachary Turner bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) { 477adcd0268SBenjamin Kramer m_prompt = std::string(prompt); 478514d8cd8SZachary Turner 47962456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 480d5b44036SJonas Devlieghere if (m_editline_up) 481d5b44036SJonas Devlieghere m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str()); 482cacde7dfSTodd Fiala #endif 48344d93782SGreg Clayton return true; 48444d93782SGreg Clayton } 48544d93782SGreg Clayton 486b9c1b51eSKate Stone const char *IOHandlerEditline::GetContinuationPrompt() { 487b9c1b51eSKate Stone return (m_continuation_prompt.empty() ? nullptr 488b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 489e30f11d9SKate Stone } 490e30f11d9SKate Stone 491514d8cd8SZachary Turner void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) { 492adcd0268SBenjamin Kramer m_continuation_prompt = std::string(prompt); 493e30f11d9SKate Stone 49462456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 495d5b44036SJonas Devlieghere if (m_editline_up) 496d5b44036SJonas Devlieghere m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty() 497b9c1b51eSKate Stone ? nullptr 498b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 499d553d00cSZachary Turner #endif 500e30f11d9SKate Stone } 501e30f11d9SKate Stone 502b9c1b51eSKate Stone void IOHandlerEditline::SetBaseLineNumber(uint32_t line) { 503f6913cd7SGreg Clayton m_base_line_number = line; 504f6913cd7SGreg Clayton } 505e30f11d9SKate Stone 506b9c1b51eSKate Stone uint32_t IOHandlerEditline::GetCurrentLineIndex() const { 50762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 508d5b44036SJonas Devlieghere if (m_editline_up) 509d5b44036SJonas Devlieghere return m_editline_up->GetCurrentLine(); 510e30f11d9SKate Stone #endif 511e30f11d9SKate Stone return m_curr_line_idx; 512e30f11d9SKate Stone } 513e30f11d9SKate Stone 514b9c1b51eSKate Stone bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) { 515e30f11d9SKate Stone m_current_lines_ptr = &lines; 516e30f11d9SKate Stone 51744d93782SGreg Clayton bool success = false; 51862456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 519d5b44036SJonas Devlieghere if (m_editline_up) { 520d5b44036SJonas Devlieghere return m_editline_up->GetLines(m_base_line_number, lines, interrupted); 521b9c1b51eSKate Stone } else { 522cacde7dfSTodd Fiala #endif 523e30f11d9SKate Stone bool done = false; 52497206d57SZachary Turner Status error; 52544d93782SGreg Clayton 526b9c1b51eSKate Stone while (!done) { 527f6913cd7SGreg Clayton // Show line numbers if we are asked to 52844d93782SGreg Clayton std::string line; 529b9c1b51eSKate Stone if (m_base_line_number > 0 && GetIsInteractive()) { 5305da2bc22SLawrence D'Anna if (m_output_sp) { 5315da2bc22SLawrence D'Anna m_output_sp->Printf("%u%s", 5325da2bc22SLawrence D'Anna m_base_line_number + (uint32_t)lines.GetSize(), 533b9c1b51eSKate Stone GetPrompt() == nullptr ? " " : ""); 534f6913cd7SGreg Clayton } 5355da2bc22SLawrence D'Anna } 536f6913cd7SGreg Clayton 537e30f11d9SKate Stone m_curr_line_idx = lines.GetSize(); 538e30f11d9SKate Stone 539f0066ad0SGreg Clayton bool interrupted = false; 540b9c1b51eSKate Stone if (GetLine(line, interrupted) && !interrupted) { 54144d93782SGreg Clayton lines.AppendString(line); 542e30f11d9SKate Stone done = m_delegate.IOHandlerIsInputComplete(*this, lines); 543b9c1b51eSKate Stone } else { 544e30f11d9SKate Stone done = true; 54544d93782SGreg Clayton } 54644d93782SGreg Clayton } 54744d93782SGreg Clayton success = lines.GetSize() > 0; 54862456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 54944d93782SGreg Clayton } 550cacde7dfSTodd Fiala #endif 55144d93782SGreg Clayton return success; 55244d93782SGreg Clayton } 55344d93782SGreg Clayton 55405097246SAdrian Prantl // Each IOHandler gets to run until it is done. It should read data from the 55505097246SAdrian Prantl // "in" and place output into "out" and "err and return when done. 556b9c1b51eSKate Stone void IOHandlerEditline::Run() { 55744d93782SGreg Clayton std::string line; 558b9c1b51eSKate Stone while (IsActive()) { 559f0066ad0SGreg Clayton bool interrupted = false; 560b9c1b51eSKate Stone if (m_multi_line) { 56144d93782SGreg Clayton StringList lines; 562b9c1b51eSKate Stone if (GetLines(lines, interrupted)) { 563b9c1b51eSKate Stone if (interrupted) { 564e30f11d9SKate Stone m_done = m_interrupt_exits; 565e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 566e30f11d9SKate Stone 567b9c1b51eSKate Stone } else { 56844d93782SGreg Clayton line = lines.CopyList(); 56944d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 57044d93782SGreg Clayton } 571b9c1b51eSKate Stone } else { 57244d93782SGreg Clayton m_done = true; 57344d93782SGreg Clayton } 574b9c1b51eSKate Stone } else { 575b9c1b51eSKate Stone if (GetLine(line, interrupted)) { 576e30f11d9SKate Stone if (interrupted) 577e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 578e30f11d9SKate Stone else 57944d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 580b9c1b51eSKate Stone } else { 58144d93782SGreg Clayton m_done = true; 58244d93782SGreg Clayton } 58344d93782SGreg Clayton } 58444d93782SGreg Clayton } 58544d93782SGreg Clayton } 58644d93782SGreg Clayton 587b9c1b51eSKate Stone void IOHandlerEditline::Cancel() { 58862456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 589d5b44036SJonas Devlieghere if (m_editline_up) 590d5b44036SJonas Devlieghere m_editline_up->Cancel(); 591cacde7dfSTodd Fiala #endif 592e68f5d6bSGreg Clayton } 593e68f5d6bSGreg Clayton 594b9c1b51eSKate Stone bool IOHandlerEditline::Interrupt() { 595f0066ad0SGreg Clayton // Let the delgate handle it first 596f0066ad0SGreg Clayton if (m_delegate.IOHandlerInterrupt(*this)) 597f0066ad0SGreg Clayton return true; 598f0066ad0SGreg Clayton 59962456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 600d5b44036SJonas Devlieghere if (m_editline_up) 601d5b44036SJonas Devlieghere return m_editline_up->Interrupt(); 602cacde7dfSTodd Fiala #endif 603f0066ad0SGreg Clayton return false; 60444d93782SGreg Clayton } 60544d93782SGreg Clayton 606b9c1b51eSKate Stone void IOHandlerEditline::GotEOF() { 60762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 608d5b44036SJonas Devlieghere if (m_editline_up) 609d5b44036SJonas Devlieghere m_editline_up->Interrupt(); 610cacde7dfSTodd Fiala #endif 61144d93782SGreg Clayton } 61244d93782SGreg Clayton 613b9c1b51eSKate Stone void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) { 61462456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 615d5b44036SJonas Devlieghere if (m_editline_up) 616d5b44036SJonas Devlieghere m_editline_up->PrintAsync(stream, s, len); 6174446487dSPavel Labath else 6184446487dSPavel Labath #endif 619fab31220STed Woodward { 6208b98f12aSMartin Storsjo #ifdef _WIN32 621341e4789SDawn Perchik const char *prompt = GetPrompt(); 622b9c1b51eSKate Stone if (prompt) { 623fab31220STed Woodward // Back up over previous prompt using Windows API 624fab31220STed Woodward CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; 625fab31220STed Woodward HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); 626fab31220STed Woodward GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info); 627fab31220STed Woodward COORD coord = screen_buffer_info.dwCursorPosition; 628fab31220STed Woodward coord.X -= strlen(prompt); 629fab31220STed Woodward if (coord.X < 0) 630fab31220STed Woodward coord.X = 0; 631fab31220STed Woodward SetConsoleCursorPosition(console_handle, coord); 632fab31220STed Woodward } 633fab31220STed Woodward #endif 6344446487dSPavel Labath IOHandler::PrintAsync(stream, s, len); 6358b98f12aSMartin Storsjo #ifdef _WIN32 636fab31220STed Woodward if (prompt) 6377ca15ba7SLawrence D'Anna IOHandler::PrintAsync(GetOutputStreamFileSP().get(), prompt, 638b9c1b51eSKate Stone strlen(prompt)); 639341e4789SDawn Perchik #endif 640fab31220STed Woodward } 6414446487dSPavel Labath } 642