15ffd83dbSDimitry Andric //===-- IOHandler.cpp -----------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #include "lldb/Core/IOHandler.h"
100b57cec5SDimitry Andric
110b57cec5SDimitry Andric #if defined(__APPLE__)
120b57cec5SDimitry Andric #include <deque>
130b57cec5SDimitry Andric #endif
140b57cec5SDimitry Andric #include <string>
150b57cec5SDimitry Andric
160b57cec5SDimitry Andric #include "lldb/Core/Debugger.h"
170b57cec5SDimitry Andric #include "lldb/Core/StreamFile.h"
18480093f4SDimitry Andric #include "lldb/Host/Config.h"
190b57cec5SDimitry Andric #include "lldb/Host/File.h"
200b57cec5SDimitry Andric #include "lldb/Utility/Predicate.h"
21af732203SDimitry Andric #include "lldb/Utility/ReproducerProvider.h"
220b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
230b57cec5SDimitry Andric #include "lldb/Utility/StreamString.h"
240b57cec5SDimitry Andric #include "lldb/Utility/StringList.h"
250b57cec5SDimitry Andric #include "lldb/lldb-forward.h"
260b57cec5SDimitry Andric
27480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
280b57cec5SDimitry Andric #include "lldb/Host/Editline.h"
290b57cec5SDimitry Andric #endif
300b57cec5SDimitry Andric #include "lldb/Interpreter/CommandCompletions.h"
310b57cec5SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h"
320b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
330b57cec5SDimitry Andric
349dba64beSDimitry Andric #ifdef _WIN32
350b57cec5SDimitry Andric #include "lldb/Host/windows/windows.h"
360b57cec5SDimitry Andric #endif
370b57cec5SDimitry Andric
380b57cec5SDimitry Andric #include <memory>
390b57cec5SDimitry Andric #include <mutex>
400b57cec5SDimitry Andric
41*5f7ddb14SDimitry Andric #include <cassert>
42*5f7ddb14SDimitry Andric #include <cctype>
43*5f7ddb14SDimitry Andric #include <cerrno>
44*5f7ddb14SDimitry Andric #include <clocale>
45*5f7ddb14SDimitry Andric #include <cstdint>
46*5f7ddb14SDimitry Andric #include <cstdio>
47*5f7ddb14SDimitry Andric #include <cstring>
480b57cec5SDimitry Andric #include <type_traits>
490b57cec5SDimitry Andric
500b57cec5SDimitry Andric using namespace lldb;
510b57cec5SDimitry Andric using namespace lldb_private;
529dba64beSDimitry Andric using llvm::None;
539dba64beSDimitry Andric using llvm::Optional;
549dba64beSDimitry Andric using llvm::StringRef;
559dba64beSDimitry Andric
IOHandler(Debugger & debugger,IOHandler::Type type)560b57cec5SDimitry Andric IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
570b57cec5SDimitry Andric : IOHandler(debugger, type,
589dba64beSDimitry Andric FileSP(), // Adopt STDIN from top input reader
590b57cec5SDimitry Andric StreamFileSP(), // Adopt STDOUT from top input reader
600b57cec5SDimitry Andric StreamFileSP(), // Adopt STDERR from top input reader
610b57cec5SDimitry Andric 0, // Flags
620b57cec5SDimitry Andric nullptr // Shadow file recorder
630b57cec5SDimitry Andric ) {}
640b57cec5SDimitry Andric
IOHandler(Debugger & debugger,IOHandler::Type type,const lldb::FileSP & input_sp,const lldb::StreamFileSP & output_sp,const lldb::StreamFileSP & error_sp,uint32_t flags,repro::DataRecorder * data_recorder)650b57cec5SDimitry Andric IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type,
669dba64beSDimitry Andric const lldb::FileSP &input_sp,
670b57cec5SDimitry Andric const lldb::StreamFileSP &output_sp,
680b57cec5SDimitry Andric const lldb::StreamFileSP &error_sp, uint32_t flags,
690b57cec5SDimitry Andric repro::DataRecorder *data_recorder)
700b57cec5SDimitry Andric : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
710b57cec5SDimitry Andric m_error_sp(error_sp), m_data_recorder(data_recorder), m_popped(false),
720b57cec5SDimitry Andric m_flags(flags), m_type(type), m_user_data(nullptr), m_done(false),
730b57cec5SDimitry Andric m_active(false) {
740b57cec5SDimitry Andric // If any files are not specified, then adopt them from the top input reader.
750b57cec5SDimitry Andric if (!m_input_sp || !m_output_sp || !m_error_sp)
760b57cec5SDimitry Andric debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp,
770b57cec5SDimitry Andric m_error_sp);
780b57cec5SDimitry Andric }
790b57cec5SDimitry Andric
800b57cec5SDimitry Andric IOHandler::~IOHandler() = default;
810b57cec5SDimitry Andric
GetInputFD()820b57cec5SDimitry Andric int IOHandler::GetInputFD() {
839dba64beSDimitry Andric return (m_input_sp ? m_input_sp->GetDescriptor() : -1);
840b57cec5SDimitry Andric }
850b57cec5SDimitry Andric
GetOutputFD()860b57cec5SDimitry Andric int IOHandler::GetOutputFD() {
870b57cec5SDimitry Andric return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1);
880b57cec5SDimitry Andric }
890b57cec5SDimitry Andric
GetErrorFD()900b57cec5SDimitry Andric int IOHandler::GetErrorFD() {
910b57cec5SDimitry Andric return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1);
920b57cec5SDimitry Andric }
930b57cec5SDimitry Andric
GetInputFILE()940b57cec5SDimitry Andric FILE *IOHandler::GetInputFILE() {
959dba64beSDimitry Andric return (m_input_sp ? m_input_sp->GetStream() : nullptr);
960b57cec5SDimitry Andric }
970b57cec5SDimitry Andric
GetOutputFILE()980b57cec5SDimitry Andric FILE *IOHandler::GetOutputFILE() {
990b57cec5SDimitry Andric return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr);
1000b57cec5SDimitry Andric }
1010b57cec5SDimitry Andric
GetErrorFILE()1020b57cec5SDimitry Andric FILE *IOHandler::GetErrorFILE() {
1030b57cec5SDimitry Andric return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr);
1040b57cec5SDimitry Andric }
1050b57cec5SDimitry Andric
GetInputFileSP()106af732203SDimitry Andric FileSP IOHandler::GetInputFileSP() { return m_input_sp; }
1070b57cec5SDimitry Andric
GetOutputStreamFileSP()108af732203SDimitry Andric StreamFileSP IOHandler::GetOutputStreamFileSP() { return m_output_sp; }
1090b57cec5SDimitry Andric
GetErrorStreamFileSP()110af732203SDimitry Andric StreamFileSP IOHandler::GetErrorStreamFileSP() { return m_error_sp; }
1110b57cec5SDimitry Andric
GetIsInteractive()1120b57cec5SDimitry Andric bool IOHandler::GetIsInteractive() {
1139dba64beSDimitry Andric return GetInputFileSP() ? GetInputFileSP()->GetIsInteractive() : false;
1140b57cec5SDimitry Andric }
1150b57cec5SDimitry Andric
GetIsRealTerminal()1160b57cec5SDimitry Andric bool IOHandler::GetIsRealTerminal() {
1179dba64beSDimitry Andric return GetInputFileSP() ? GetInputFileSP()->GetIsRealTerminal() : false;
1180b57cec5SDimitry Andric }
1190b57cec5SDimitry Andric
SetPopped(bool b)1200b57cec5SDimitry Andric void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
1210b57cec5SDimitry Andric
WaitForPop()1220b57cec5SDimitry Andric void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
1230b57cec5SDimitry Andric
PrintAsync(Stream * stream,const char * s,size_t len)1240b57cec5SDimitry Andric void IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len) {
1250b57cec5SDimitry Andric if (stream) {
1260b57cec5SDimitry Andric std::lock_guard<std::recursive_mutex> guard(m_mutex);
1270b57cec5SDimitry Andric if (m_top)
1280b57cec5SDimitry Andric m_top->PrintAsync(stream, s, len);
1295ffd83dbSDimitry Andric else
1305ffd83dbSDimitry Andric stream->Write(s, len);
1310b57cec5SDimitry Andric }
1320b57cec5SDimitry Andric }
1330b57cec5SDimitry Andric
IOHandlerConfirm(Debugger & debugger,llvm::StringRef prompt,bool default_response)1340b57cec5SDimitry Andric IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
1350b57cec5SDimitry Andric bool default_response)
1360b57cec5SDimitry Andric : IOHandlerEditline(
1370b57cec5SDimitry Andric debugger, IOHandler::Type::Confirm,
1380b57cec5SDimitry Andric nullptr, // nullptr editline_name means no history loaded/saved
1390b57cec5SDimitry Andric llvm::StringRef(), // No prompt
1400b57cec5SDimitry Andric llvm::StringRef(), // No continuation prompt
1410b57cec5SDimitry Andric false, // Multi-line
1420b57cec5SDimitry Andric false, // Don't colorize the prompt (i.e. the confirm message.)
1430b57cec5SDimitry Andric 0, *this, nullptr),
1440b57cec5SDimitry Andric m_default_response(default_response), m_user_response(default_response) {
1450b57cec5SDimitry Andric StreamString prompt_stream;
1460b57cec5SDimitry Andric prompt_stream.PutCString(prompt);
1470b57cec5SDimitry Andric if (m_default_response)
1480b57cec5SDimitry Andric prompt_stream.Printf(": [Y/n] ");
1490b57cec5SDimitry Andric else
1500b57cec5SDimitry Andric prompt_stream.Printf(": [y/N] ");
1510b57cec5SDimitry Andric
1520b57cec5SDimitry Andric SetPrompt(prompt_stream.GetString());
1530b57cec5SDimitry Andric }
1540b57cec5SDimitry Andric
1550b57cec5SDimitry Andric IOHandlerConfirm::~IOHandlerConfirm() = default;
1560b57cec5SDimitry Andric
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)1579dba64beSDimitry Andric void IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler,
1589dba64beSDimitry Andric CompletionRequest &request) {
1599dba64beSDimitry Andric if (request.GetRawCursorPos() != 0)
1609dba64beSDimitry Andric return;
1619dba64beSDimitry Andric request.AddCompletion(m_default_response ? "y" : "n");
1620b57cec5SDimitry Andric }
1630b57cec5SDimitry Andric
IOHandlerInputComplete(IOHandler & io_handler,std::string & line)1640b57cec5SDimitry Andric void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler,
1650b57cec5SDimitry Andric std::string &line) {
1660b57cec5SDimitry Andric if (line.empty()) {
1670b57cec5SDimitry Andric // User just hit enter, set the response to the default
1680b57cec5SDimitry Andric m_user_response = m_default_response;
1690b57cec5SDimitry Andric io_handler.SetIsDone(true);
1700b57cec5SDimitry Andric return;
1710b57cec5SDimitry Andric }
1720b57cec5SDimitry Andric
1730b57cec5SDimitry Andric if (line.size() == 1) {
1740b57cec5SDimitry Andric switch (line[0]) {
1750b57cec5SDimitry Andric case 'y':
1760b57cec5SDimitry Andric case 'Y':
1770b57cec5SDimitry Andric m_user_response = true;
1780b57cec5SDimitry Andric io_handler.SetIsDone(true);
1790b57cec5SDimitry Andric return;
1800b57cec5SDimitry Andric case 'n':
1810b57cec5SDimitry Andric case 'N':
1820b57cec5SDimitry Andric m_user_response = false;
1830b57cec5SDimitry Andric io_handler.SetIsDone(true);
1840b57cec5SDimitry Andric return;
1850b57cec5SDimitry Andric default:
1860b57cec5SDimitry Andric break;
1870b57cec5SDimitry Andric }
1880b57cec5SDimitry Andric }
1890b57cec5SDimitry Andric
1900b57cec5SDimitry Andric if (line == "yes" || line == "YES" || line == "Yes") {
1910b57cec5SDimitry Andric m_user_response = true;
1920b57cec5SDimitry Andric io_handler.SetIsDone(true);
1930b57cec5SDimitry Andric } else if (line == "no" || line == "NO" || line == "No") {
1940b57cec5SDimitry Andric m_user_response = false;
1950b57cec5SDimitry Andric io_handler.SetIsDone(true);
1960b57cec5SDimitry Andric }
1970b57cec5SDimitry Andric }
1980b57cec5SDimitry Andric
199af732203SDimitry Andric llvm::Optional<std::string>
IOHandlerSuggestion(IOHandler & io_handler,llvm::StringRef line)200af732203SDimitry Andric IOHandlerDelegate::IOHandlerSuggestion(IOHandler &io_handler,
201af732203SDimitry Andric llvm::StringRef line) {
202af732203SDimitry Andric return io_handler.GetDebugger()
203af732203SDimitry Andric .GetCommandInterpreter()
204af732203SDimitry Andric .GetAutoSuggestionForCommand(line);
205af732203SDimitry Andric }
206af732203SDimitry Andric
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)2079dba64beSDimitry Andric void IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler,
2089dba64beSDimitry Andric CompletionRequest &request) {
2090b57cec5SDimitry Andric switch (m_completion) {
2100b57cec5SDimitry Andric case Completion::None:
2110b57cec5SDimitry Andric break;
2120b57cec5SDimitry Andric case Completion::LLDBCommand:
2139dba64beSDimitry Andric io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(request);
2149dba64beSDimitry Andric break;
2159dba64beSDimitry Andric case Completion::Expression:
2160b57cec5SDimitry Andric CommandCompletions::InvokeCommonCompletionCallbacks(
2170b57cec5SDimitry Andric io_handler.GetDebugger().GetCommandInterpreter(),
2180b57cec5SDimitry Andric CommandCompletions::eVariablePathCompletion, request, nullptr);
2199dba64beSDimitry Andric break;
2200b57cec5SDimitry Andric }
2210b57cec5SDimitry Andric }
2220b57cec5SDimitry Andric
IOHandlerEditline(Debugger & debugger,IOHandler::Type type,const char * editline_name,llvm::StringRef prompt,llvm::StringRef continuation_prompt,bool multi_line,bool color_prompts,uint32_t line_number_start,IOHandlerDelegate & delegate,repro::DataRecorder * data_recorder)2230b57cec5SDimitry Andric IOHandlerEditline::IOHandlerEditline(
2240b57cec5SDimitry Andric Debugger &debugger, IOHandler::Type type,
2250b57cec5SDimitry Andric const char *editline_name, // Used for saving history files
2260b57cec5SDimitry Andric llvm::StringRef prompt, llvm::StringRef continuation_prompt,
2270b57cec5SDimitry Andric bool multi_line, bool color_prompts, uint32_t line_number_start,
2280b57cec5SDimitry Andric IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder)
2290b57cec5SDimitry Andric : IOHandlerEditline(debugger, type,
2309dba64beSDimitry Andric FileSP(), // Inherit input from top input reader
2310b57cec5SDimitry Andric StreamFileSP(), // Inherit output from top input reader
2320b57cec5SDimitry Andric StreamFileSP(), // Inherit error from top input reader
2330b57cec5SDimitry Andric 0, // Flags
2340b57cec5SDimitry Andric editline_name, // Used for saving history files
2350b57cec5SDimitry Andric prompt, continuation_prompt, multi_line, color_prompts,
2360b57cec5SDimitry Andric line_number_start, delegate, data_recorder) {}
2370b57cec5SDimitry Andric
IOHandlerEditline(Debugger & debugger,IOHandler::Type type,const lldb::FileSP & input_sp,const lldb::StreamFileSP & output_sp,const lldb::StreamFileSP & error_sp,uint32_t flags,const char * editline_name,llvm::StringRef prompt,llvm::StringRef continuation_prompt,bool multi_line,bool color_prompts,uint32_t line_number_start,IOHandlerDelegate & delegate,repro::DataRecorder * data_recorder)2380b57cec5SDimitry Andric IOHandlerEditline::IOHandlerEditline(
2399dba64beSDimitry Andric Debugger &debugger, IOHandler::Type type, const lldb::FileSP &input_sp,
2409dba64beSDimitry Andric const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp,
2419dba64beSDimitry Andric uint32_t flags,
2420b57cec5SDimitry Andric const char *editline_name, // Used for saving history files
2430b57cec5SDimitry Andric llvm::StringRef prompt, llvm::StringRef continuation_prompt,
2440b57cec5SDimitry Andric bool multi_line, bool color_prompts, uint32_t line_number_start,
2450b57cec5SDimitry Andric IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder)
2460b57cec5SDimitry Andric : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags,
2470b57cec5SDimitry Andric data_recorder),
248480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
2490b57cec5SDimitry Andric m_editline_up(),
2500b57cec5SDimitry Andric #endif
2510b57cec5SDimitry Andric m_delegate(delegate), m_prompt(), m_continuation_prompt(),
2520b57cec5SDimitry Andric m_current_lines_ptr(nullptr), m_base_line_number(line_number_start),
2530b57cec5SDimitry Andric m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line),
2540b57cec5SDimitry Andric m_color_prompts(color_prompts), m_interrupt_exits(true),
2550b57cec5SDimitry Andric m_editing(false) {
2560b57cec5SDimitry Andric SetPrompt(prompt);
2570b57cec5SDimitry Andric
258480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
2590b57cec5SDimitry Andric bool use_editline = false;
2600b57cec5SDimitry Andric
2619dba64beSDimitry Andric use_editline = GetInputFILE() && GetOutputFILE() && GetErrorFILE() &&
2629dba64beSDimitry Andric m_input_sp && m_input_sp->GetIsRealTerminal();
2630b57cec5SDimitry Andric
2640b57cec5SDimitry Andric if (use_editline) {
2655ffd83dbSDimitry Andric m_editline_up = std::make_unique<Editline>(editline_name, GetInputFILE(),
2660b57cec5SDimitry Andric GetOutputFILE(), GetErrorFILE(),
2675ffd83dbSDimitry Andric m_color_prompts);
268*5f7ddb14SDimitry Andric m_editline_up->SetIsInputCompleteCallback(
269*5f7ddb14SDimitry Andric [this](Editline *editline, StringList &lines) {
270*5f7ddb14SDimitry Andric return this->IsInputCompleteCallback(editline, lines);
271*5f7ddb14SDimitry Andric });
272*5f7ddb14SDimitry Andric
273*5f7ddb14SDimitry Andric m_editline_up->SetAutoCompleteCallback([this](CompletionRequest &request) {
274*5f7ddb14SDimitry Andric this->AutoCompleteCallback(request);
275*5f7ddb14SDimitry Andric });
276*5f7ddb14SDimitry Andric
277*5f7ddb14SDimitry Andric if (debugger.GetUseAutosuggestion() && debugger.GetUseColor()) {
278*5f7ddb14SDimitry Andric m_editline_up->SetSuggestionCallback([this](llvm::StringRef line) {
279*5f7ddb14SDimitry Andric return this->SuggestionCallback(line);
280*5f7ddb14SDimitry Andric });
281*5f7ddb14SDimitry Andric }
2820b57cec5SDimitry Andric // See if the delegate supports fixing indentation
2830b57cec5SDimitry Andric const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
2840b57cec5SDimitry Andric if (indent_chars) {
2850b57cec5SDimitry Andric // The delegate does support indentation, hook it up so when any
2860b57cec5SDimitry Andric // indentation character is typed, the delegate gets a chance to fix it
287*5f7ddb14SDimitry Andric FixIndentationCallbackType f = [this](Editline *editline,
288*5f7ddb14SDimitry Andric const StringList &lines,
289*5f7ddb14SDimitry Andric int cursor_position) {
290*5f7ddb14SDimitry Andric return this->FixIndentationCallback(editline, lines, cursor_position);
291*5f7ddb14SDimitry Andric };
292*5f7ddb14SDimitry Andric m_editline_up->SetFixIndentationCallback(std::move(f), indent_chars);
2930b57cec5SDimitry Andric }
2940b57cec5SDimitry Andric }
2950b57cec5SDimitry Andric #endif
2960b57cec5SDimitry Andric SetBaseLineNumber(m_base_line_number);
2970b57cec5SDimitry Andric SetPrompt(prompt);
2980b57cec5SDimitry Andric SetContinuationPrompt(continuation_prompt);
2990b57cec5SDimitry Andric }
3000b57cec5SDimitry Andric
~IOHandlerEditline()3010b57cec5SDimitry Andric IOHandlerEditline::~IOHandlerEditline() {
302480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
3030b57cec5SDimitry Andric m_editline_up.reset();
3040b57cec5SDimitry Andric #endif
3050b57cec5SDimitry Andric }
3060b57cec5SDimitry Andric
Activate()3070b57cec5SDimitry Andric void IOHandlerEditline::Activate() {
3080b57cec5SDimitry Andric IOHandler::Activate();
3090b57cec5SDimitry Andric m_delegate.IOHandlerActivated(*this, GetIsInteractive());
3100b57cec5SDimitry Andric }
3110b57cec5SDimitry Andric
Deactivate()3120b57cec5SDimitry Andric void IOHandlerEditline::Deactivate() {
3130b57cec5SDimitry Andric IOHandler::Deactivate();
3140b57cec5SDimitry Andric m_delegate.IOHandlerDeactivated(*this);
3150b57cec5SDimitry Andric }
3160b57cec5SDimitry Andric
TerminalSizeChanged()3175ffd83dbSDimitry Andric void IOHandlerEditline::TerminalSizeChanged() {
3185ffd83dbSDimitry Andric #if LLDB_ENABLE_LIBEDIT
3195ffd83dbSDimitry Andric if (m_editline_up)
3205ffd83dbSDimitry Andric m_editline_up->TerminalSizeChanged();
3215ffd83dbSDimitry Andric #endif
3225ffd83dbSDimitry Andric }
3235ffd83dbSDimitry Andric
3249dba64beSDimitry Andric // Split out a line from the buffer, if there is a full one to get.
SplitLine(std::string & line_buffer)3259dba64beSDimitry Andric static Optional<std::string> SplitLine(std::string &line_buffer) {
3269dba64beSDimitry Andric size_t pos = line_buffer.find('\n');
3279dba64beSDimitry Andric if (pos == std::string::npos)
3289dba64beSDimitry Andric return None;
3295ffd83dbSDimitry Andric std::string line =
3305ffd83dbSDimitry Andric std::string(StringRef(line_buffer.c_str(), pos).rtrim("\n\r"));
3319dba64beSDimitry Andric line_buffer = line_buffer.substr(pos + 1);
3329dba64beSDimitry Andric return line;
3339dba64beSDimitry Andric }
3349dba64beSDimitry Andric
3359dba64beSDimitry Andric // If the final line of the file ends without a end-of-line, return
3369dba64beSDimitry Andric // it as a line anyway.
SplitLineEOF(std::string & line_buffer)3379dba64beSDimitry Andric static Optional<std::string> SplitLineEOF(std::string &line_buffer) {
3385ffd83dbSDimitry Andric if (llvm::all_of(line_buffer, llvm::isSpace))
3399dba64beSDimitry Andric return None;
3409dba64beSDimitry Andric std::string line = std::move(line_buffer);
3419dba64beSDimitry Andric line_buffer.clear();
3429dba64beSDimitry Andric return line;
3439dba64beSDimitry Andric }
3449dba64beSDimitry Andric
GetLine(std::string & line,bool & interrupted)3450b57cec5SDimitry Andric bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
346480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
3470b57cec5SDimitry Andric if (m_editline_up) {
3480b57cec5SDimitry Andric bool b = m_editline_up->GetLine(line, interrupted);
3499dba64beSDimitry Andric if (b && m_data_recorder)
3500b57cec5SDimitry Andric m_data_recorder->Record(line, true);
3510b57cec5SDimitry Andric return b;
3529dba64beSDimitry Andric }
3530b57cec5SDimitry Andric #endif
3549dba64beSDimitry Andric
3550b57cec5SDimitry Andric line.clear();
3560b57cec5SDimitry Andric
3570b57cec5SDimitry Andric if (GetIsInteractive()) {
3580b57cec5SDimitry Andric const char *prompt = nullptr;
3590b57cec5SDimitry Andric
3600b57cec5SDimitry Andric if (m_multi_line && m_curr_line_idx > 0)
3610b57cec5SDimitry Andric prompt = GetContinuationPrompt();
3620b57cec5SDimitry Andric
3630b57cec5SDimitry Andric if (prompt == nullptr)
3640b57cec5SDimitry Andric prompt = GetPrompt();
3650b57cec5SDimitry Andric
3660b57cec5SDimitry Andric if (prompt && prompt[0]) {
3679dba64beSDimitry Andric if (m_output_sp) {
3689dba64beSDimitry Andric m_output_sp->Printf("%s", prompt);
3699dba64beSDimitry Andric m_output_sp->Flush();
3700b57cec5SDimitry Andric }
3710b57cec5SDimitry Andric }
3720b57cec5SDimitry Andric }
3739dba64beSDimitry Andric
3749dba64beSDimitry Andric Optional<std::string> got_line = SplitLine(m_line_buffer);
3759dba64beSDimitry Andric
3769dba64beSDimitry Andric if (!got_line && !m_input_sp) {
3779dba64beSDimitry Andric // No more input file, we are done...
3789dba64beSDimitry Andric SetIsDone(true);
3799dba64beSDimitry Andric return false;
3809dba64beSDimitry Andric }
3819dba64beSDimitry Andric
3829dba64beSDimitry Andric FILE *in = GetInputFILE();
3830b57cec5SDimitry Andric char buffer[256];
3849dba64beSDimitry Andric
3859dba64beSDimitry Andric if (!got_line && !in && m_input_sp) {
3869dba64beSDimitry Andric // there is no FILE*, fall back on just reading bytes from the stream.
3879dba64beSDimitry Andric while (!got_line) {
3889dba64beSDimitry Andric size_t bytes_read = sizeof(buffer);
3899dba64beSDimitry Andric Status error = m_input_sp->Read((void *)buffer, bytes_read);
3909dba64beSDimitry Andric if (error.Success() && !bytes_read) {
3919dba64beSDimitry Andric got_line = SplitLineEOF(m_line_buffer);
3929dba64beSDimitry Andric break;
3939dba64beSDimitry Andric }
3949dba64beSDimitry Andric if (error.Fail())
3959dba64beSDimitry Andric break;
3969dba64beSDimitry Andric m_line_buffer += StringRef(buffer, bytes_read);
3979dba64beSDimitry Andric got_line = SplitLine(m_line_buffer);
3989dba64beSDimitry Andric }
3999dba64beSDimitry Andric }
4009dba64beSDimitry Andric
4019dba64beSDimitry Andric if (!got_line && in) {
4020b57cec5SDimitry Andric m_editing = true;
4039dba64beSDimitry Andric while (!got_line) {
4049dba64beSDimitry Andric char *r = fgets(buffer, sizeof(buffer), in);
4050b57cec5SDimitry Andric #ifdef _WIN32
4060b57cec5SDimitry Andric // ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED
4070b57cec5SDimitry Andric // according to the docs on MSDN. However, this has evidently been a
4080b57cec5SDimitry Andric // known bug since Windows 8. Therefore, we can't detect if a signal
4090b57cec5SDimitry Andric // interrupted in the fgets. So pressing ctrl-c causes the repl to end
4100b57cec5SDimitry Andric // and the process to exit. A temporary workaround is just to attempt to
4110b57cec5SDimitry Andric // fgets twice until this bug is fixed.
4129dba64beSDimitry Andric if (r == nullptr)
4139dba64beSDimitry Andric r = fgets(buffer, sizeof(buffer), in);
4149dba64beSDimitry Andric // this is the equivalent of EINTR for Windows
4159dba64beSDimitry Andric if (r == nullptr && GetLastError() == ERROR_OPERATION_ABORTED)
4169dba64beSDimitry Andric continue;
4170b57cec5SDimitry Andric #endif
4189dba64beSDimitry Andric if (r == nullptr) {
4199dba64beSDimitry Andric if (ferror(in) && errno == EINTR)
4209dba64beSDimitry Andric continue;
4210b57cec5SDimitry Andric if (feof(in))
4229dba64beSDimitry Andric got_line = SplitLineEOF(m_line_buffer);
4230b57cec5SDimitry Andric break;
4240b57cec5SDimitry Andric }
4259dba64beSDimitry Andric m_line_buffer += buffer;
4269dba64beSDimitry Andric got_line = SplitLine(m_line_buffer);
4270b57cec5SDimitry Andric }
4280b57cec5SDimitry Andric m_editing = false;
4299dba64beSDimitry Andric }
4309dba64beSDimitry Andric
4319dba64beSDimitry Andric if (got_line) {
4329dba64beSDimitry Andric line = got_line.getValue();
4339dba64beSDimitry Andric if (m_data_recorder)
4340b57cec5SDimitry Andric m_data_recorder->Record(line, true);
4350b57cec5SDimitry Andric }
4369dba64beSDimitry Andric
4379dba64beSDimitry Andric return (bool)got_line;
4380b57cec5SDimitry Andric }
4390b57cec5SDimitry Andric
440480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
IsInputCompleteCallback(Editline * editline,StringList & lines)4410b57cec5SDimitry Andric bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline,
442*5f7ddb14SDimitry Andric StringList &lines) {
443*5f7ddb14SDimitry Andric return m_delegate.IOHandlerIsInputComplete(*this, lines);
4440b57cec5SDimitry Andric }
4450b57cec5SDimitry Andric
FixIndentationCallback(Editline * editline,const StringList & lines,int cursor_position)4460b57cec5SDimitry Andric int IOHandlerEditline::FixIndentationCallback(Editline *editline,
4470b57cec5SDimitry Andric const StringList &lines,
448*5f7ddb14SDimitry Andric int cursor_position) {
449*5f7ddb14SDimitry Andric return m_delegate.IOHandlerFixIndentation(*this, lines, cursor_position);
4500b57cec5SDimitry Andric }
4510b57cec5SDimitry Andric
452af732203SDimitry Andric llvm::Optional<std::string>
SuggestionCallback(llvm::StringRef line)453*5f7ddb14SDimitry Andric IOHandlerEditline::SuggestionCallback(llvm::StringRef line) {
454*5f7ddb14SDimitry Andric return m_delegate.IOHandlerSuggestion(*this, line);
455af732203SDimitry Andric }
456af732203SDimitry Andric
AutoCompleteCallback(CompletionRequest & request)457*5f7ddb14SDimitry Andric void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request) {
458*5f7ddb14SDimitry Andric m_delegate.IOHandlerComplete(*this, request);
4590b57cec5SDimitry Andric }
4600b57cec5SDimitry Andric #endif
4610b57cec5SDimitry Andric
GetPrompt()4620b57cec5SDimitry Andric const char *IOHandlerEditline::GetPrompt() {
463480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
4640b57cec5SDimitry Andric if (m_editline_up) {
4650b57cec5SDimitry Andric return m_editline_up->GetPrompt();
4660b57cec5SDimitry Andric } else {
4670b57cec5SDimitry Andric #endif
4680b57cec5SDimitry Andric if (m_prompt.empty())
4690b57cec5SDimitry Andric return nullptr;
470480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
4710b57cec5SDimitry Andric }
4720b57cec5SDimitry Andric #endif
4730b57cec5SDimitry Andric return m_prompt.c_str();
4740b57cec5SDimitry Andric }
4750b57cec5SDimitry Andric
SetPrompt(llvm::StringRef prompt)4760b57cec5SDimitry Andric bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
4775ffd83dbSDimitry Andric m_prompt = std::string(prompt);
4780b57cec5SDimitry Andric
479480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
4800b57cec5SDimitry Andric if (m_editline_up)
4810b57cec5SDimitry Andric m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
4820b57cec5SDimitry Andric #endif
4830b57cec5SDimitry Andric return true;
4840b57cec5SDimitry Andric }
4850b57cec5SDimitry Andric
GetContinuationPrompt()4860b57cec5SDimitry Andric const char *IOHandlerEditline::GetContinuationPrompt() {
4870b57cec5SDimitry Andric return (m_continuation_prompt.empty() ? nullptr
4880b57cec5SDimitry Andric : m_continuation_prompt.c_str());
4890b57cec5SDimitry Andric }
4900b57cec5SDimitry Andric
SetContinuationPrompt(llvm::StringRef prompt)4910b57cec5SDimitry Andric void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) {
4925ffd83dbSDimitry Andric m_continuation_prompt = std::string(prompt);
4930b57cec5SDimitry Andric
494480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
4950b57cec5SDimitry Andric if (m_editline_up)
4960b57cec5SDimitry Andric m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty()
4970b57cec5SDimitry Andric ? nullptr
4980b57cec5SDimitry Andric : m_continuation_prompt.c_str());
4990b57cec5SDimitry Andric #endif
5000b57cec5SDimitry Andric }
5010b57cec5SDimitry Andric
SetBaseLineNumber(uint32_t line)5020b57cec5SDimitry Andric void IOHandlerEditline::SetBaseLineNumber(uint32_t line) {
5030b57cec5SDimitry Andric m_base_line_number = line;
5040b57cec5SDimitry Andric }
5050b57cec5SDimitry Andric
GetCurrentLineIndex() const5060b57cec5SDimitry Andric uint32_t IOHandlerEditline::GetCurrentLineIndex() const {
507480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
5080b57cec5SDimitry Andric if (m_editline_up)
5090b57cec5SDimitry Andric return m_editline_up->GetCurrentLine();
5100b57cec5SDimitry Andric #endif
5110b57cec5SDimitry Andric return m_curr_line_idx;
5120b57cec5SDimitry Andric }
5130b57cec5SDimitry Andric
GetLines(StringList & lines,bool & interrupted)5140b57cec5SDimitry Andric bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) {
5150b57cec5SDimitry Andric m_current_lines_ptr = &lines;
5160b57cec5SDimitry Andric
5170b57cec5SDimitry Andric bool success = false;
518480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
5190b57cec5SDimitry Andric if (m_editline_up) {
5200b57cec5SDimitry Andric return m_editline_up->GetLines(m_base_line_number, lines, interrupted);
5210b57cec5SDimitry Andric } else {
5220b57cec5SDimitry Andric #endif
5230b57cec5SDimitry Andric bool done = false;
5240b57cec5SDimitry Andric Status error;
5250b57cec5SDimitry Andric
5260b57cec5SDimitry Andric while (!done) {
5270b57cec5SDimitry Andric // Show line numbers if we are asked to
5280b57cec5SDimitry Andric std::string line;
5290b57cec5SDimitry Andric if (m_base_line_number > 0 && GetIsInteractive()) {
5309dba64beSDimitry Andric if (m_output_sp) {
5319dba64beSDimitry Andric m_output_sp->Printf("%u%s",
5329dba64beSDimitry Andric m_base_line_number + (uint32_t)lines.GetSize(),
5330b57cec5SDimitry Andric GetPrompt() == nullptr ? " " : "");
5340b57cec5SDimitry Andric }
5359dba64beSDimitry Andric }
5360b57cec5SDimitry Andric
5370b57cec5SDimitry Andric m_curr_line_idx = lines.GetSize();
5380b57cec5SDimitry Andric
5390b57cec5SDimitry Andric bool interrupted = false;
5400b57cec5SDimitry Andric if (GetLine(line, interrupted) && !interrupted) {
5410b57cec5SDimitry Andric lines.AppendString(line);
5420b57cec5SDimitry Andric done = m_delegate.IOHandlerIsInputComplete(*this, lines);
5430b57cec5SDimitry Andric } else {
5440b57cec5SDimitry Andric done = true;
5450b57cec5SDimitry Andric }
5460b57cec5SDimitry Andric }
5470b57cec5SDimitry Andric success = lines.GetSize() > 0;
548480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
5490b57cec5SDimitry Andric }
5500b57cec5SDimitry Andric #endif
5510b57cec5SDimitry Andric return success;
5520b57cec5SDimitry Andric }
5530b57cec5SDimitry Andric
5540b57cec5SDimitry Andric // Each IOHandler gets to run until it is done. It should read data from the
5550b57cec5SDimitry Andric // "in" and place output into "out" and "err and return when done.
Run()5560b57cec5SDimitry Andric void IOHandlerEditline::Run() {
5570b57cec5SDimitry Andric std::string line;
5580b57cec5SDimitry Andric while (IsActive()) {
5590b57cec5SDimitry Andric bool interrupted = false;
5600b57cec5SDimitry Andric if (m_multi_line) {
5610b57cec5SDimitry Andric StringList lines;
5620b57cec5SDimitry Andric if (GetLines(lines, interrupted)) {
5630b57cec5SDimitry Andric if (interrupted) {
5640b57cec5SDimitry Andric m_done = m_interrupt_exits;
5650b57cec5SDimitry Andric m_delegate.IOHandlerInputInterrupted(*this, line);
5660b57cec5SDimitry Andric
5670b57cec5SDimitry Andric } else {
5680b57cec5SDimitry Andric line = lines.CopyList();
5690b57cec5SDimitry Andric m_delegate.IOHandlerInputComplete(*this, line);
5700b57cec5SDimitry Andric }
5710b57cec5SDimitry Andric } else {
5720b57cec5SDimitry Andric m_done = true;
5730b57cec5SDimitry Andric }
5740b57cec5SDimitry Andric } else {
5750b57cec5SDimitry Andric if (GetLine(line, interrupted)) {
5760b57cec5SDimitry Andric if (interrupted)
5770b57cec5SDimitry Andric m_delegate.IOHandlerInputInterrupted(*this, line);
5780b57cec5SDimitry Andric else
5790b57cec5SDimitry Andric m_delegate.IOHandlerInputComplete(*this, line);
5800b57cec5SDimitry Andric } else {
5810b57cec5SDimitry Andric m_done = true;
5820b57cec5SDimitry Andric }
5830b57cec5SDimitry Andric }
5840b57cec5SDimitry Andric }
5850b57cec5SDimitry Andric }
5860b57cec5SDimitry Andric
Cancel()5870b57cec5SDimitry Andric void IOHandlerEditline::Cancel() {
588480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
5890b57cec5SDimitry Andric if (m_editline_up)
5900b57cec5SDimitry Andric m_editline_up->Cancel();
5910b57cec5SDimitry Andric #endif
5920b57cec5SDimitry Andric }
5930b57cec5SDimitry Andric
Interrupt()5940b57cec5SDimitry Andric bool IOHandlerEditline::Interrupt() {
5950b57cec5SDimitry Andric // Let the delgate handle it first
5960b57cec5SDimitry Andric if (m_delegate.IOHandlerInterrupt(*this))
5970b57cec5SDimitry Andric return true;
5980b57cec5SDimitry Andric
599480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
6000b57cec5SDimitry Andric if (m_editline_up)
6010b57cec5SDimitry Andric return m_editline_up->Interrupt();
6020b57cec5SDimitry Andric #endif
6030b57cec5SDimitry Andric return false;
6040b57cec5SDimitry Andric }
6050b57cec5SDimitry Andric
GotEOF()6060b57cec5SDimitry Andric void IOHandlerEditline::GotEOF() {
607480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
6080b57cec5SDimitry Andric if (m_editline_up)
6090b57cec5SDimitry Andric m_editline_up->Interrupt();
6100b57cec5SDimitry Andric #endif
6110b57cec5SDimitry Andric }
6120b57cec5SDimitry Andric
PrintAsync(Stream * stream,const char * s,size_t len)6130b57cec5SDimitry Andric void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) {
614480093f4SDimitry Andric #if LLDB_ENABLE_LIBEDIT
6150b57cec5SDimitry Andric if (m_editline_up)
6160b57cec5SDimitry Andric m_editline_up->PrintAsync(stream, s, len);
6170b57cec5SDimitry Andric else
6180b57cec5SDimitry Andric #endif
6190b57cec5SDimitry Andric {
6209dba64beSDimitry Andric #ifdef _WIN32
6210b57cec5SDimitry Andric const char *prompt = GetPrompt();
6220b57cec5SDimitry Andric if (prompt) {
6230b57cec5SDimitry Andric // Back up over previous prompt using Windows API
6240b57cec5SDimitry Andric CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
6250b57cec5SDimitry Andric HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
6260b57cec5SDimitry Andric GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info);
6270b57cec5SDimitry Andric COORD coord = screen_buffer_info.dwCursorPosition;
6280b57cec5SDimitry Andric coord.X -= strlen(prompt);
6290b57cec5SDimitry Andric if (coord.X < 0)
6300b57cec5SDimitry Andric coord.X = 0;
6310b57cec5SDimitry Andric SetConsoleCursorPosition(console_handle, coord);
6320b57cec5SDimitry Andric }
6330b57cec5SDimitry Andric #endif
6340b57cec5SDimitry Andric IOHandler::PrintAsync(stream, s, len);
6359dba64beSDimitry Andric #ifdef _WIN32
6360b57cec5SDimitry Andric if (prompt)
6379dba64beSDimitry Andric IOHandler::PrintAsync(GetOutputStreamFileSP().get(), prompt,
6380b57cec5SDimitry Andric strlen(prompt));
6390b57cec5SDimitry Andric #endif
6400b57cec5SDimitry Andric }
6410b57cec5SDimitry Andric }
642