144d93782SGreg Clayton //===-- IOHandler.cpp -------------------------------------------*- C++ -*-===// 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" 18*babbd554SJonas 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); 1284446487dSPavel Labath } 1294446487dSPavel Labath } 1304446487dSPavel Labath 1317a120c8bSZachary Turner IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt, 132b9c1b51eSKate Stone bool default_response) 133b9c1b51eSKate Stone : IOHandlerEditline( 134b9c1b51eSKate Stone debugger, IOHandler::Type::Confirm, 135c5dac77aSEugene Zelenko nullptr, // nullptr editline_name means no history loaded/saved 136514d8cd8SZachary Turner llvm::StringRef(), // No prompt 137514d8cd8SZachary Turner llvm::StringRef(), // No continuation prompt 13844d93782SGreg Clayton false, // Multi-line 139e30f11d9SKate Stone false, // Don't colorize the prompt (i.e. the confirm message.) 140d77c2e09SJonas Devlieghere 0, *this, nullptr), 141b9c1b51eSKate Stone m_default_response(default_response), m_user_response(default_response) { 14244d93782SGreg Clayton StreamString prompt_stream; 14344d93782SGreg Clayton prompt_stream.PutCString(prompt); 14444d93782SGreg Clayton if (m_default_response) 14544d93782SGreg Clayton prompt_stream.Printf(": [Y/n] "); 14644d93782SGreg Clayton else 14744d93782SGreg Clayton prompt_stream.Printf(": [y/N] "); 14844d93782SGreg Clayton 149514d8cd8SZachary Turner SetPrompt(prompt_stream.GetString()); 15044d93782SGreg Clayton } 15144d93782SGreg Clayton 152315b6884SEugene Zelenko IOHandlerConfirm::~IOHandlerConfirm() = default; 15344d93782SGreg Clayton 154ae34ed2cSRaphael Isemann void IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler, 1552fc20f65SRaphael Isemann CompletionRequest &request) { 1561153dc96SRaphael Isemann if (request.GetRawCursorPos() != 0) 1571153dc96SRaphael Isemann return; 1581153dc96SRaphael Isemann request.AddCompletion(m_default_response ? "y" : "n"); 15944d93782SGreg Clayton } 16044d93782SGreg Clayton 161b9c1b51eSKate Stone void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler, 162b9c1b51eSKate Stone std::string &line) { 163b9c1b51eSKate Stone if (line.empty()) { 16444d93782SGreg Clayton // User just hit enter, set the response to the default 16544d93782SGreg Clayton m_user_response = m_default_response; 16644d93782SGreg Clayton io_handler.SetIsDone(true); 16744d93782SGreg Clayton return; 16844d93782SGreg Clayton } 16944d93782SGreg Clayton 170b9c1b51eSKate Stone if (line.size() == 1) { 171b9c1b51eSKate Stone switch (line[0]) { 17244d93782SGreg Clayton case 'y': 17344d93782SGreg Clayton case 'Y': 17444d93782SGreg Clayton m_user_response = true; 17544d93782SGreg Clayton io_handler.SetIsDone(true); 17644d93782SGreg Clayton return; 17744d93782SGreg Clayton case 'n': 17844d93782SGreg Clayton case 'N': 17944d93782SGreg Clayton m_user_response = false; 18044d93782SGreg Clayton io_handler.SetIsDone(true); 18144d93782SGreg Clayton return; 18244d93782SGreg Clayton default: 18344d93782SGreg Clayton break; 18444d93782SGreg Clayton } 18544d93782SGreg Clayton } 18644d93782SGreg Clayton 187b9c1b51eSKate Stone if (line == "yes" || line == "YES" || line == "Yes") { 18844d93782SGreg Clayton m_user_response = true; 18944d93782SGreg Clayton io_handler.SetIsDone(true); 190b9c1b51eSKate Stone } else if (line == "no" || line == "NO" || line == "No") { 19144d93782SGreg Clayton m_user_response = false; 19244d93782SGreg Clayton io_handler.SetIsDone(true); 19344d93782SGreg Clayton } 19444d93782SGreg Clayton } 19544d93782SGreg Clayton 196ae34ed2cSRaphael Isemann void IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler, 1972fc20f65SRaphael Isemann CompletionRequest &request) { 198b9c1b51eSKate Stone switch (m_completion) { 19944d93782SGreg Clayton case Completion::None: 20044d93782SGreg Clayton break; 20144d93782SGreg Clayton case Completion::LLDBCommand: 202ae34ed2cSRaphael Isemann io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(request); 203ae34ed2cSRaphael Isemann break; 204ae34ed2cSRaphael Isemann case Completion::Expression: 205b9c1b51eSKate Stone CommandCompletions::InvokeCommonCompletionCallbacks( 206b9c1b51eSKate Stone io_handler.GetDebugger().GetCommandInterpreter(), 207ae34ed2cSRaphael Isemann CommandCompletions::eVariablePathCompletion, request, nullptr); 208ae34ed2cSRaphael Isemann break; 20944d93782SGreg Clayton } 21044d93782SGreg Clayton } 21144d93782SGreg Clayton 212b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 213b9c1b51eSKate Stone Debugger &debugger, IOHandler::Type type, 21444d93782SGreg Clayton const char *editline_name, // Used for saving history files 215514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 216514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 217d77c2e09SJonas Devlieghere IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder) 218b9c1b51eSKate Stone : IOHandlerEditline(debugger, type, 2197ca15ba7SLawrence D'Anna FileSP(), // Inherit input from top input reader 22044d93782SGreg Clayton StreamFileSP(), // Inherit output from top input reader 22144d93782SGreg Clayton StreamFileSP(), // Inherit error from top input reader 222340b0309SGreg Clayton 0, // Flags 22344d93782SGreg Clayton editline_name, // Used for saving history files 224b9c1b51eSKate Stone prompt, continuation_prompt, multi_line, color_prompts, 225d77c2e09SJonas Devlieghere line_number_start, delegate, data_recorder) {} 22644d93782SGreg Clayton 227b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 2287ca15ba7SLawrence D'Anna Debugger &debugger, IOHandler::Type type, const lldb::FileSP &input_sp, 2297ca15ba7SLawrence D'Anna const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp, 2307ca15ba7SLawrence D'Anna uint32_t flags, 23144d93782SGreg Clayton const char *editline_name, // Used for saving history files 232514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 233514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 234d77c2e09SJonas Devlieghere IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder) 235d77c2e09SJonas Devlieghere : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags, 236d77c2e09SJonas Devlieghere data_recorder), 23762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 238d5b44036SJonas Devlieghere m_editline_up(), 239cacde7dfSTodd Fiala #endif 240b9c1b51eSKate Stone m_delegate(delegate), m_prompt(), m_continuation_prompt(), 241b9c1b51eSKate Stone m_current_lines_ptr(nullptr), m_base_line_number(line_number_start), 242b9c1b51eSKate Stone m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line), 243b9c1b51eSKate Stone m_color_prompts(color_prompts), m_interrupt_exits(true), 244b9c1b51eSKate Stone m_editing(false) { 24544d93782SGreg Clayton SetPrompt(prompt); 24644d93782SGreg Clayton 24762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 248914b8d98SDeepak Panickal bool use_editline = false; 249340b0309SGreg Clayton 250609010d0SLawrence D'Anna use_editline = GetInputFILE() && GetOutputFILE() && GetErrorFILE() && 251609010d0SLawrence D'Anna m_input_sp && m_input_sp->GetIsRealTerminal(); 25244d93782SGreg Clayton 253b9c1b51eSKate Stone if (use_editline) { 254d5b44036SJonas Devlieghere m_editline_up.reset(new Editline(editline_name, GetInputFILE(), 255b9c1b51eSKate Stone GetOutputFILE(), GetErrorFILE(), 256e30f11d9SKate Stone m_color_prompts)); 257d5b44036SJonas Devlieghere m_editline_up->SetIsInputCompleteCallback(IsInputCompleteCallback, this); 258d5b44036SJonas Devlieghere m_editline_up->SetAutoCompleteCallback(AutoCompleteCallback, this); 259e30f11d9SKate Stone // See if the delegate supports fixing indentation 260e30f11d9SKate Stone const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters(); 261b9c1b51eSKate Stone if (indent_chars) { 262b9c1b51eSKate Stone // The delegate does support indentation, hook it up so when any 26305097246SAdrian Prantl // indentation character is typed, the delegate gets a chance to fix it 264d5b44036SJonas Devlieghere m_editline_up->SetFixIndentationCallback(FixIndentationCallback, this, 265b9c1b51eSKate Stone indent_chars); 266e30f11d9SKate Stone } 26744d93782SGreg Clayton } 268cacde7dfSTodd Fiala #endif 269e30f11d9SKate Stone SetBaseLineNumber(m_base_line_number); 270514d8cd8SZachary Turner SetPrompt(prompt); 271e30f11d9SKate Stone SetContinuationPrompt(continuation_prompt); 27244d93782SGreg Clayton } 27344d93782SGreg Clayton 274b9c1b51eSKate Stone IOHandlerEditline::~IOHandlerEditline() { 27562456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 276d5b44036SJonas Devlieghere m_editline_up.reset(); 277cacde7dfSTodd Fiala #endif 27844d93782SGreg Clayton } 27944d93782SGreg Clayton 280b9c1b51eSKate Stone void IOHandlerEditline::Activate() { 281e30f11d9SKate Stone IOHandler::Activate(); 2820affb582SDave Lee m_delegate.IOHandlerActivated(*this, GetIsInteractive()); 283e30f11d9SKate Stone } 284e30f11d9SKate Stone 285b9c1b51eSKate Stone void IOHandlerEditline::Deactivate() { 286e30f11d9SKate Stone IOHandler::Deactivate(); 287e30f11d9SKate Stone m_delegate.IOHandlerDeactivated(*this); 288e30f11d9SKate Stone } 289e30f11d9SKate Stone 290b3faa01fSLawrence D'Anna // Split out a line from the buffer, if there is a full one to get. 291b3faa01fSLawrence D'Anna static Optional<std::string> SplitLine(std::string &line_buffer) { 292b3faa01fSLawrence D'Anna size_t pos = line_buffer.find('\n'); 293b3faa01fSLawrence D'Anna if (pos == std::string::npos) 294b3faa01fSLawrence D'Anna return None; 295b3faa01fSLawrence D'Anna std::string line = StringRef(line_buffer.c_str(), pos).rtrim("\n\r"); 296b3faa01fSLawrence D'Anna line_buffer = line_buffer.substr(pos + 1); 297b3faa01fSLawrence D'Anna return line; 298b3faa01fSLawrence D'Anna } 299b3faa01fSLawrence D'Anna 300b3faa01fSLawrence D'Anna // If the final line of the file ends without a end-of-line, return 301b3faa01fSLawrence D'Anna // it as a line anyway. 302b3faa01fSLawrence D'Anna static Optional<std::string> SplitLineEOF(std::string &line_buffer) { 303b3faa01fSLawrence D'Anna if (llvm::all_of(line_buffer, isspace)) 304b3faa01fSLawrence D'Anna return None; 305b3faa01fSLawrence D'Anna std::string line = std::move(line_buffer); 306b3faa01fSLawrence D'Anna line_buffer.clear(); 307b3faa01fSLawrence D'Anna return line; 308b3faa01fSLawrence D'Anna } 309b3faa01fSLawrence D'Anna 310b9c1b51eSKate Stone bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) { 31162456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 312d5b44036SJonas Devlieghere if (m_editline_up) { 313d77c2e09SJonas Devlieghere bool b = m_editline_up->GetLine(line, interrupted); 314b3faa01fSLawrence D'Anna if (b && m_data_recorder) 315d77c2e09SJonas Devlieghere m_data_recorder->Record(line, true); 316d77c2e09SJonas Devlieghere return b; 317b3faa01fSLawrence D'Anna } 318cacde7dfSTodd Fiala #endif 319b3faa01fSLawrence D'Anna 32044d93782SGreg Clayton line.clear(); 32144d93782SGreg Clayton 322b9c1b51eSKate Stone if (GetIsInteractive()) { 323c5dac77aSEugene Zelenko const char *prompt = nullptr; 324e30f11d9SKate Stone 325e30f11d9SKate Stone if (m_multi_line && m_curr_line_idx > 0) 326e30f11d9SKate Stone prompt = GetContinuationPrompt(); 327e30f11d9SKate Stone 328c5dac77aSEugene Zelenko if (prompt == nullptr) 329e30f11d9SKate Stone prompt = GetPrompt(); 330e30f11d9SKate Stone 331b9c1b51eSKate Stone if (prompt && prompt[0]) { 3325da2bc22SLawrence D'Anna if (m_output_sp) { 3335da2bc22SLawrence D'Anna m_output_sp->Printf("%s", prompt); 3345da2bc22SLawrence D'Anna m_output_sp->Flush(); 33544d93782SGreg Clayton } 33644d93782SGreg Clayton } 33744d93782SGreg Clayton } 338b3faa01fSLawrence D'Anna 339b3faa01fSLawrence D'Anna Optional<std::string> got_line = SplitLine(m_line_buffer); 340b3faa01fSLawrence D'Anna 341b3faa01fSLawrence D'Anna if (!got_line && !m_input_sp) { 342b3faa01fSLawrence D'Anna // No more input file, we are done... 343b3faa01fSLawrence D'Anna SetIsDone(true); 344b3faa01fSLawrence D'Anna return false; 345b3faa01fSLawrence D'Anna } 346b3faa01fSLawrence D'Anna 347b3faa01fSLawrence D'Anna FILE *in = GetInputFILE(); 34844d93782SGreg Clayton char buffer[256]; 349b3faa01fSLawrence D'Anna 350b3faa01fSLawrence D'Anna if (!got_line && !in && m_input_sp) { 351b3faa01fSLawrence D'Anna // there is no FILE*, fall back on just reading bytes from the stream. 352b3faa01fSLawrence D'Anna while (!got_line) { 353b3faa01fSLawrence D'Anna size_t bytes_read = sizeof(buffer); 354b3faa01fSLawrence D'Anna Status error = m_input_sp->Read((void *)buffer, bytes_read); 355b3faa01fSLawrence D'Anna if (error.Success() && !bytes_read) { 356b3faa01fSLawrence D'Anna got_line = SplitLineEOF(m_line_buffer); 357b3faa01fSLawrence D'Anna break; 358b3faa01fSLawrence D'Anna } 359b3faa01fSLawrence D'Anna if (error.Fail()) 360b3faa01fSLawrence D'Anna break; 361b3faa01fSLawrence D'Anna m_line_buffer += StringRef(buffer, bytes_read); 362b3faa01fSLawrence D'Anna got_line = SplitLine(m_line_buffer); 363b3faa01fSLawrence D'Anna } 364b3faa01fSLawrence D'Anna } 365b3faa01fSLawrence D'Anna 366b3faa01fSLawrence D'Anna if (!got_line && in) { 367e034a04eSGreg Clayton m_editing = true; 368b3faa01fSLawrence D'Anna while (!got_line) { 369b3faa01fSLawrence D'Anna char *r = fgets(buffer, sizeof(buffer), in); 370e7167908SNathan Lanza #ifdef _WIN32 371e7167908SNathan Lanza // ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED 372e7167908SNathan Lanza // according to the docs on MSDN. However, this has evidently been a 373e7167908SNathan Lanza // known bug since Windows 8. Therefore, we can't detect if a signal 374e7167908SNathan Lanza // interrupted in the fgets. So pressing ctrl-c causes the repl to end 375e7167908SNathan Lanza // and the process to exit. A temporary workaround is just to attempt to 376e7167908SNathan Lanza // fgets twice until this bug is fixed. 377b3faa01fSLawrence D'Anna if (r == nullptr) 378b3faa01fSLawrence D'Anna r = fgets(buffer, sizeof(buffer), in); 379cb305205SNathan Lanza // this is the equivalent of EINTR for Windows 380b3faa01fSLawrence D'Anna if (r == nullptr && GetLastError() == ERROR_OPERATION_ABORTED) 381cb305205SNathan Lanza continue; 382e7167908SNathan Lanza #endif 383b3faa01fSLawrence D'Anna if (r == nullptr) { 384b3faa01fSLawrence D'Anna if (ferror(in) && errno == EINTR) 385b3faa01fSLawrence D'Anna continue; 386c9cf5798SGreg Clayton if (feof(in)) 387b3faa01fSLawrence D'Anna got_line = SplitLineEOF(m_line_buffer); 38844d93782SGreg Clayton break; 38944d93782SGreg Clayton } 390b3faa01fSLawrence D'Anna m_line_buffer += buffer; 391b3faa01fSLawrence D'Anna got_line = SplitLine(m_line_buffer); 39244d93782SGreg Clayton } 393e034a04eSGreg Clayton m_editing = false; 394b3faa01fSLawrence D'Anna } 395b3faa01fSLawrence D'Anna 396b3faa01fSLawrence D'Anna if (got_line) { 397b3faa01fSLawrence D'Anna line = got_line.getValue(); 398b3faa01fSLawrence D'Anna if (m_data_recorder) 399d77c2e09SJonas Devlieghere m_data_recorder->Record(line, true); 40044d93782SGreg Clayton } 401b3faa01fSLawrence D'Anna 402b3faa01fSLawrence D'Anna return (bool)got_line; 40344d93782SGreg Clayton } 40444d93782SGreg Clayton 40562456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 406b9c1b51eSKate Stone bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline, 40744d93782SGreg Clayton StringList &lines, 408b9c1b51eSKate Stone void *baton) { 40944d93782SGreg Clayton IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 410b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, 411b9c1b51eSKate Stone lines); 412e30f11d9SKate Stone } 413e30f11d9SKate Stone 414b9c1b51eSKate Stone int IOHandlerEditline::FixIndentationCallback(Editline *editline, 415e30f11d9SKate Stone const StringList &lines, 416e30f11d9SKate Stone int cursor_position, 417b9c1b51eSKate Stone void *baton) { 418e30f11d9SKate Stone IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 419b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerFixIndentation( 420b9c1b51eSKate Stone *editline_reader, lines, cursor_position); 42144d93782SGreg Clayton } 42244d93782SGreg Clayton 423ae34ed2cSRaphael Isemann void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request, 4242fc20f65SRaphael Isemann void *baton) { 42544d93782SGreg Clayton IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 42644d93782SGreg Clayton if (editline_reader) 427ae34ed2cSRaphael Isemann editline_reader->m_delegate.IOHandlerComplete(*editline_reader, request); 42844d93782SGreg Clayton } 429cacde7dfSTodd Fiala #endif 43044d93782SGreg Clayton 431b9c1b51eSKate Stone const char *IOHandlerEditline::GetPrompt() { 43262456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 433d5b44036SJonas Devlieghere if (m_editline_up) { 434d5b44036SJonas Devlieghere return m_editline_up->GetPrompt(); 435b9c1b51eSKate Stone } else { 436cacde7dfSTodd Fiala #endif 437cacde7dfSTodd Fiala if (m_prompt.empty()) 438c5dac77aSEugene Zelenko return nullptr; 43962456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 440cacde7dfSTodd Fiala } 441cacde7dfSTodd Fiala #endif 44244d93782SGreg Clayton return m_prompt.c_str(); 44344d93782SGreg Clayton } 44444d93782SGreg Clayton 445514d8cd8SZachary Turner bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) { 446514d8cd8SZachary Turner m_prompt = prompt; 447514d8cd8SZachary Turner 44862456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 449d5b44036SJonas Devlieghere if (m_editline_up) 450d5b44036SJonas Devlieghere m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str()); 451cacde7dfSTodd Fiala #endif 45244d93782SGreg Clayton return true; 45344d93782SGreg Clayton } 45444d93782SGreg Clayton 455b9c1b51eSKate Stone const char *IOHandlerEditline::GetContinuationPrompt() { 456b9c1b51eSKate Stone return (m_continuation_prompt.empty() ? nullptr 457b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 458e30f11d9SKate Stone } 459e30f11d9SKate Stone 460514d8cd8SZachary Turner void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) { 461514d8cd8SZachary Turner m_continuation_prompt = prompt; 462e30f11d9SKate Stone 46362456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 464d5b44036SJonas Devlieghere if (m_editline_up) 465d5b44036SJonas Devlieghere m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty() 466b9c1b51eSKate Stone ? nullptr 467b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 468d553d00cSZachary Turner #endif 469e30f11d9SKate Stone } 470e30f11d9SKate Stone 471b9c1b51eSKate Stone void IOHandlerEditline::SetBaseLineNumber(uint32_t line) { 472f6913cd7SGreg Clayton m_base_line_number = line; 473f6913cd7SGreg Clayton } 474e30f11d9SKate Stone 475b9c1b51eSKate Stone uint32_t IOHandlerEditline::GetCurrentLineIndex() const { 47662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 477d5b44036SJonas Devlieghere if (m_editline_up) 478d5b44036SJonas Devlieghere return m_editline_up->GetCurrentLine(); 479e30f11d9SKate Stone #endif 480e30f11d9SKate Stone return m_curr_line_idx; 481e30f11d9SKate Stone } 482e30f11d9SKate Stone 483b9c1b51eSKate Stone bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) { 484e30f11d9SKate Stone m_current_lines_ptr = &lines; 485e30f11d9SKate Stone 48644d93782SGreg Clayton bool success = false; 48762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 488d5b44036SJonas Devlieghere if (m_editline_up) { 489d5b44036SJonas Devlieghere return m_editline_up->GetLines(m_base_line_number, lines, interrupted); 490b9c1b51eSKate Stone } else { 491cacde7dfSTodd Fiala #endif 492e30f11d9SKate Stone bool done = false; 49397206d57SZachary Turner Status error; 49444d93782SGreg Clayton 495b9c1b51eSKate Stone while (!done) { 496f6913cd7SGreg Clayton // Show line numbers if we are asked to 49744d93782SGreg Clayton std::string line; 498b9c1b51eSKate Stone if (m_base_line_number > 0 && GetIsInteractive()) { 4995da2bc22SLawrence D'Anna if (m_output_sp) { 5005da2bc22SLawrence D'Anna m_output_sp->Printf("%u%s", 5015da2bc22SLawrence D'Anna m_base_line_number + (uint32_t)lines.GetSize(), 502b9c1b51eSKate Stone GetPrompt() == nullptr ? " " : ""); 503f6913cd7SGreg Clayton } 5045da2bc22SLawrence D'Anna } 505f6913cd7SGreg Clayton 506e30f11d9SKate Stone m_curr_line_idx = lines.GetSize(); 507e30f11d9SKate Stone 508f0066ad0SGreg Clayton bool interrupted = false; 509b9c1b51eSKate Stone if (GetLine(line, interrupted) && !interrupted) { 51044d93782SGreg Clayton lines.AppendString(line); 511e30f11d9SKate Stone done = m_delegate.IOHandlerIsInputComplete(*this, lines); 512b9c1b51eSKate Stone } else { 513e30f11d9SKate Stone done = true; 51444d93782SGreg Clayton } 51544d93782SGreg Clayton } 51644d93782SGreg Clayton success = lines.GetSize() > 0; 51762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 51844d93782SGreg Clayton } 519cacde7dfSTodd Fiala #endif 52044d93782SGreg Clayton return success; 52144d93782SGreg Clayton } 52244d93782SGreg Clayton 52305097246SAdrian Prantl // Each IOHandler gets to run until it is done. It should read data from the 52405097246SAdrian Prantl // "in" and place output into "out" and "err and return when done. 525b9c1b51eSKate Stone void IOHandlerEditline::Run() { 52644d93782SGreg Clayton std::string line; 527b9c1b51eSKate Stone while (IsActive()) { 528f0066ad0SGreg Clayton bool interrupted = false; 529b9c1b51eSKate Stone if (m_multi_line) { 53044d93782SGreg Clayton StringList lines; 531b9c1b51eSKate Stone if (GetLines(lines, interrupted)) { 532b9c1b51eSKate Stone if (interrupted) { 533e30f11d9SKate Stone m_done = m_interrupt_exits; 534e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 535e30f11d9SKate Stone 536b9c1b51eSKate Stone } else { 53744d93782SGreg Clayton line = lines.CopyList(); 53844d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 53944d93782SGreg Clayton } 540b9c1b51eSKate Stone } else { 54144d93782SGreg Clayton m_done = true; 54244d93782SGreg Clayton } 543b9c1b51eSKate Stone } else { 544b9c1b51eSKate Stone if (GetLine(line, interrupted)) { 545e30f11d9SKate Stone if (interrupted) 546e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 547e30f11d9SKate Stone else 54844d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 549b9c1b51eSKate Stone } else { 55044d93782SGreg Clayton m_done = true; 55144d93782SGreg Clayton } 55244d93782SGreg Clayton } 55344d93782SGreg Clayton } 55444d93782SGreg Clayton } 55544d93782SGreg Clayton 556b9c1b51eSKate Stone void IOHandlerEditline::Cancel() { 55762456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 558d5b44036SJonas Devlieghere if (m_editline_up) 559d5b44036SJonas Devlieghere m_editline_up->Cancel(); 560cacde7dfSTodd Fiala #endif 561e68f5d6bSGreg Clayton } 562e68f5d6bSGreg Clayton 563b9c1b51eSKate Stone bool IOHandlerEditline::Interrupt() { 564f0066ad0SGreg Clayton // Let the delgate handle it first 565f0066ad0SGreg Clayton if (m_delegate.IOHandlerInterrupt(*this)) 566f0066ad0SGreg Clayton return true; 567f0066ad0SGreg Clayton 56862456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 569d5b44036SJonas Devlieghere if (m_editline_up) 570d5b44036SJonas Devlieghere return m_editline_up->Interrupt(); 571cacde7dfSTodd Fiala #endif 572f0066ad0SGreg Clayton return false; 57344d93782SGreg Clayton } 57444d93782SGreg Clayton 575b9c1b51eSKate Stone void IOHandlerEditline::GotEOF() { 57662456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 577d5b44036SJonas Devlieghere if (m_editline_up) 578d5b44036SJonas Devlieghere m_editline_up->Interrupt(); 579cacde7dfSTodd Fiala #endif 58044d93782SGreg Clayton } 58144d93782SGreg Clayton 582b9c1b51eSKate Stone void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) { 58362456e57SJonas Devlieghere #if LLDB_ENABLE_LIBEDIT 584d5b44036SJonas Devlieghere if (m_editline_up) 585d5b44036SJonas Devlieghere m_editline_up->PrintAsync(stream, s, len); 5864446487dSPavel Labath else 5874446487dSPavel Labath #endif 588fab31220STed Woodward { 5898b98f12aSMartin Storsjo #ifdef _WIN32 590341e4789SDawn Perchik const char *prompt = GetPrompt(); 591b9c1b51eSKate Stone if (prompt) { 592fab31220STed Woodward // Back up over previous prompt using Windows API 593fab31220STed Woodward CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; 594fab31220STed Woodward HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); 595fab31220STed Woodward GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info); 596fab31220STed Woodward COORD coord = screen_buffer_info.dwCursorPosition; 597fab31220STed Woodward coord.X -= strlen(prompt); 598fab31220STed Woodward if (coord.X < 0) 599fab31220STed Woodward coord.X = 0; 600fab31220STed Woodward SetConsoleCursorPosition(console_handle, coord); 601fab31220STed Woodward } 602fab31220STed Woodward #endif 6034446487dSPavel Labath IOHandler::PrintAsync(stream, s, len); 6048b98f12aSMartin Storsjo #ifdef _WIN32 605fab31220STed Woodward if (prompt) 6067ca15ba7SLawrence D'Anna IOHandler::PrintAsync(GetOutputStreamFileSP().get(), prompt, 607b9c1b51eSKate Stone strlen(prompt)); 608341e4789SDawn Perchik #endif 609fab31220STed Woodward } 6104446487dSPavel Labath } 611