quitando basura del index
[VSoRC/.git] / node_modules / node-pty / deps / winpty / src / agent / Agent.cc
diff --git a/node_modules/node-pty/deps/winpty/src/agent/Agent.cc b/node_modules/node-pty/deps/winpty/src/agent/Agent.cc
deleted file mode 100644 (file)
index a77b644..0000000
+++ /dev/null
@@ -1,605 +0,0 @@
-// Copyright (c) 2011-2015 Ryan Prichard
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-
-#include "Agent.h"
-
-#include <windows.h>
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "../include/winpty_constants.h"
-
-#include "../shared/AgentMsg.h"
-#include "../shared/Buffer.h"
-#include "../shared/DebugClient.h"
-#include "../shared/GenRandom.h"
-#include "../shared/StringBuilder.h"
-#include "../shared/StringUtil.h"
-#include "../shared/WindowsVersion.h"
-#include "../shared/WinptyAssert.h"
-
-#include "ConsoleFont.h"
-#include "ConsoleInput.h"
-#include "NamedPipe.h"
-#include "Scraper.h"
-#include "Terminal.h"
-#include "Win32ConsoleBuffer.h"
-
-namespace {
-
-static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType)
-{
-    if (dwCtrlType == CTRL_C_EVENT) {
-        // Do nothing and claim to have handled the event.
-        return TRUE;
-    }
-    return FALSE;
-}
-
-// We can detect the new Windows 10 console by observing the effect of the
-// Mark command.  In older consoles, Mark temporarily moves the cursor to the
-// top-left of the console window.  In the new console, the cursor isn't
-// initially moved.
-//
-// We might like to use Mark to freeze the console, but we can't, because when
-// the Mark command ends, the console moves the cursor back to its starting
-// point, even if the console application has moved it in the meantime.
-static void detectNewWindows10Console(
-        Win32Console &console, Win32ConsoleBuffer &buffer)
-{
-    if (!isAtLeastWindows8()) {
-        return;
-    }
-
-    ConsoleScreenBufferInfo info = buffer.bufferInfo();
-
-    // Make sure the window isn't 1x1.  AFAIK, this should never happen
-    // accidentally.  It is difficult to make it happen deliberately.
-    if (info.srWindow.Left == info.srWindow.Right &&
-            info.srWindow.Top == info.srWindow.Bottom) {
-        trace("detectNewWindows10Console: Initial console window was 1x1 -- "
-              "expanding for test");
-        setSmallFont(buffer.conout(), 400, false);
-        buffer.moveWindow(SmallRect(0, 0, 1, 1));
-        buffer.resizeBuffer(Coord(400, 1));
-        buffer.moveWindow(SmallRect(0, 0, 2, 1));
-        // This use of GetLargestConsoleWindowSize ought to be unnecessary
-        // given the behavior I've seen from moveWindow(0, 0, 1, 1), but
-        // I'd like to be especially sure, considering that this code will
-        // rarely be tested.
-        const auto largest = GetLargestConsoleWindowSize(buffer.conout());
-        buffer.moveWindow(
-            SmallRect(0, 0, std::min(largest.X, buffer.bufferSize().X), 1));
-        info = buffer.bufferInfo();
-        ASSERT(info.srWindow.Right > info.srWindow.Left &&
-            "Could not expand console window from 1x1");
-    }
-
-    // Test whether MARK moves the cursor.
-    const Coord initialPosition(info.srWindow.Right, info.srWindow.Bottom);
-    buffer.setCursorPosition(initialPosition);
-    ASSERT(!console.frozen());
-    console.setFreezeUsesMark(true);
-    console.setFrozen(true);
-    const bool isNewW10 = (buffer.cursorPosition() == initialPosition);
-    console.setFrozen(false);
-    buffer.setCursorPosition(Coord(0, 0));
-
-    trace("Attempting to detect new Windows 10 console using MARK: %s",
-        isNewW10 ? "detected" : "not detected");
-    console.setFreezeUsesMark(false);
-    console.setNewW10(isNewW10);
-}
-
-static inline WriteBuffer newPacket() {
-    WriteBuffer packet;
-    packet.putRawValue<uint64_t>(0); // Reserve space for size.
-    return packet;
-}
-
-static HANDLE duplicateHandle(HANDLE h) {
-    HANDLE ret = nullptr;
-    if (!DuplicateHandle(
-            GetCurrentProcess(), h,
-            GetCurrentProcess(), &ret,
-            0, FALSE, DUPLICATE_SAME_ACCESS)) {
-        ASSERT(false && "DuplicateHandle failed!");
-    }
-    return ret;
-}
-
-// It's safe to truncate a handle from 64-bits to 32-bits, or to sign-extend it
-// back to 64-bits.  See the MSDN article, "Interprocess Communication Between
-// 32-bit and 64-bit Applications".
-// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203.aspx
-static int64_t int64FromHandle(HANDLE h) {
-    return static_cast<int64_t>(reinterpret_cast<intptr_t>(h));
-}
-
-} // anonymous namespace
-
-Agent::Agent(LPCWSTR controlPipeName,
-             uint64_t agentFlags,
-             int mouseMode,
-             int initialCols,
-             int initialRows) :
-    m_useConerr((agentFlags & WINPTY_FLAG_CONERR) != 0),
-    m_plainMode((agentFlags & WINPTY_FLAG_PLAIN_OUTPUT) != 0),
-    m_mouseMode(mouseMode)
-{
-    trace("Agent::Agent entered");
-
-    ASSERT(initialCols >= 1 && initialRows >= 1);
-    initialCols = std::min(initialCols, MAX_CONSOLE_WIDTH);
-    initialRows = std::min(initialRows, MAX_CONSOLE_HEIGHT);
-
-    const bool outputColor =
-        !m_plainMode || (agentFlags & WINPTY_FLAG_COLOR_ESCAPES);
-    const Coord initialSize(initialCols, initialRows);
-
-    auto primaryBuffer = openPrimaryBuffer();
-    if (m_useConerr) {
-        m_errorBuffer = Win32ConsoleBuffer::createErrorBuffer();
-    }
-
-    detectNewWindows10Console(m_console, *primaryBuffer);
-
-    m_controlPipe = &connectToControlPipe(controlPipeName);
-    m_coninPipe = &createDataServerPipe(false, L"conin");
-    m_conoutPipe = &createDataServerPipe(true, L"conout");
-    if (m_useConerr) {
-        m_conerrPipe = &createDataServerPipe(true, L"conerr");
-    }
-
-    // Send an initial response packet to winpty.dll containing pipe names.
-    {
-        auto setupPacket = newPacket();
-        setupPacket.putWString(m_coninPipe->name());
-        setupPacket.putWString(m_conoutPipe->name());
-        if (m_useConerr) {
-            setupPacket.putWString(m_conerrPipe->name());
-        }
-        writePacket(setupPacket);
-    }
-
-    std::unique_ptr<Terminal> primaryTerminal;
-    primaryTerminal.reset(new Terminal(*m_conoutPipe,
-                                       m_plainMode,
-                                       outputColor));
-    m_primaryScraper.reset(new Scraper(m_console,
-                                       *primaryBuffer,
-                                       std::move(primaryTerminal),
-                                       initialSize));
-    if (m_useConerr) {
-        std::unique_ptr<Terminal> errorTerminal;
-        errorTerminal.reset(new Terminal(*m_conerrPipe,
-                                         m_plainMode,
-                                         outputColor));
-        m_errorScraper.reset(new Scraper(m_console,
-                                         *m_errorBuffer,
-                                         std::move(errorTerminal),
-                                         initialSize));
-    }
-
-    m_console.setTitle(m_currentTitle);
-
-    const HANDLE conin = GetStdHandle(STD_INPUT_HANDLE);
-    m_consoleInput.reset(
-        new ConsoleInput(conin, m_mouseMode, *this, m_console));
-
-    // Setup Ctrl-C handling.  First restore default handling of Ctrl-C.  This
-    // attribute is inherited by child processes.  Then register a custom
-    // Ctrl-C handler that does nothing.  The handler will be called when the
-    // agent calls GenerateConsoleCtrlEvent.
-    SetConsoleCtrlHandler(NULL, FALSE);
-    SetConsoleCtrlHandler(consoleCtrlHandler, TRUE);
-
-    setPollInterval(25);
-}
-
-Agent::~Agent()
-{
-    trace("Agent::~Agent entered");
-    agentShutdown();
-    if (m_childProcess != NULL) {
-        CloseHandle(m_childProcess);
-    }
-}
-
-// Write a "Device Status Report" command to the terminal.  The terminal will
-// reply with a row+col escape sequence.  Presumably, the DSR reply will not
-// split a keypress escape sequence, so it should be safe to assume that the
-// bytes before it are complete keypresses.
-void Agent::sendDsr()
-{
-    if (!m_plainMode && !m_conoutPipe->isClosed()) {
-        m_conoutPipe->write("\x1B[6n");
-    }
-}
-
-NamedPipe &Agent::connectToControlPipe(LPCWSTR pipeName)
-{
-    NamedPipe &pipe = createNamedPipe();
-    pipe.connectToServer(pipeName, NamedPipe::OpenMode::Duplex);
-    pipe.setReadBufferSize(64 * 1024);
-    return pipe;
-}
-
-// Returns a new server named pipe.  It has not yet been connected.
-NamedPipe &Agent::createDataServerPipe(bool write, const wchar_t *kind)
-{
-    const auto name =
-        (WStringBuilder(128)
-            << L"\\\\.\\pipe\\winpty-"
-            << kind << L'-'
-            << GenRandom().uniqueName()).str_moved();
-    NamedPipe &pipe = createNamedPipe();
-    pipe.openServerPipe(
-        name.c_str(),
-        write ? NamedPipe::OpenMode::Writing
-              : NamedPipe::OpenMode::Reading,
-        write ? 8192 : 0,
-        write ? 0 : 256);
-    if (!write) {
-        pipe.setReadBufferSize(64 * 1024);
-    }
-    return pipe;
-}
-
-void Agent::onPipeIo(NamedPipe &namedPipe)
-{
-    if (&namedPipe == m_conoutPipe || &namedPipe == m_conerrPipe) {
-        autoClosePipesForShutdown();
-    } else if (&namedPipe == m_coninPipe) {
-        pollConinPipe();
-    } else if (&namedPipe == m_controlPipe) {
-        pollControlPipe();
-    }
-}
-
-void Agent::pollControlPipe()
-{
-    if (m_controlPipe->isClosed()) {
-        trace("Agent exiting (control pipe is closed)");
-        shutdown();
-        return;
-    }
-
-    while (true) {
-        uint64_t packetSize = 0;
-        const auto amt1 =
-            m_controlPipe->peek(&packetSize, sizeof(packetSize));
-        if (amt1 < sizeof(packetSize)) {
-            break;
-        }
-        ASSERT(packetSize >= sizeof(packetSize) && packetSize <= SIZE_MAX);
-        if (m_controlPipe->bytesAvailable() < packetSize) {
-            if (m_controlPipe->readBufferSize() < packetSize) {
-                m_controlPipe->setReadBufferSize(packetSize);
-            }
-            break;
-        }
-        std::vector<char> packetData;
-        packetData.resize(packetSize);
-        const auto amt2 = m_controlPipe->read(packetData.data(), packetSize);
-        ASSERT(amt2 == packetSize);
-        try {
-            ReadBuffer buffer(std::move(packetData));
-            buffer.getRawValue<uint64_t>(); // Discard the size.
-            handlePacket(buffer);
-        } catch (const ReadBuffer::DecodeError&) {
-            ASSERT(false && "Decode error");
-        }
-    }
-}
-
-void Agent::handlePacket(ReadBuffer &packet)
-{
-    const int type = packet.getInt32();
-    switch (type) {
-    case AgentMsg::StartProcess:
-        handleStartProcessPacket(packet);
-        break;
-    case AgentMsg::SetSize:
-        // TODO: I think it might make sense to collapse consecutive SetSize
-        // messages.  i.e. The terminal process can probably generate SetSize
-        // messages faster than they can be processed, and some GUIs might
-        // generate a flood of them, so if we can read multiple SetSize packets
-        // at once, we can ignore the early ones.
-        handleSetSizePacket(packet);
-        break;
-    case AgentMsg::GetConsoleProcessList:
-        handleGetConsoleProcessListPacket(packet);
-        break;
-    default:
-        trace("Unrecognized message, id:%d", type);
-    }
-}
-
-void Agent::writePacket(WriteBuffer &packet)
-{
-    const auto &bytes = packet.buf();
-    packet.replaceRawValue<uint64_t>(0, bytes.size());
-    m_controlPipe->write(bytes.data(), bytes.size());
-}
-
-void Agent::handleStartProcessPacket(ReadBuffer &packet)
-{
-    ASSERT(m_childProcess == nullptr);
-    ASSERT(!m_closingOutputPipes);
-
-    const uint64_t spawnFlags = packet.getInt64();
-    const bool wantProcessHandle = packet.getInt32() != 0;
-    const bool wantThreadHandle = packet.getInt32() != 0;
-    const auto program = packet.getWString();
-    const auto cmdline = packet.getWString();
-    const auto cwd = packet.getWString();
-    const auto env = packet.getWString();
-    const auto desktop = packet.getWString();
-    packet.assertEof();
-
-    auto cmdlineV = vectorWithNulFromString(cmdline);
-    auto desktopV = vectorWithNulFromString(desktop);
-    auto envV = vectorFromString(env);
-
-    LPCWSTR programArg = program.empty() ? nullptr : program.c_str();
-    LPWSTR cmdlineArg = cmdline.empty() ? nullptr : cmdlineV.data();
-    LPCWSTR cwdArg = cwd.empty() ? nullptr : cwd.c_str();
-    LPWSTR envArg = env.empty() ? nullptr : envV.data();
-
-    STARTUPINFOW sui = {};
-    PROCESS_INFORMATION pi = {};
-    sui.cb = sizeof(sui);
-    sui.lpDesktop = desktop.empty() ? nullptr : desktopV.data();
-    BOOL inheritHandles = FALSE;
-    if (m_useConerr) {
-        inheritHandles = TRUE;
-        sui.dwFlags |= STARTF_USESTDHANDLES;
-        sui.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
-        sui.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
-        sui.hStdError = m_errorBuffer->conout();
-    }
-
-    const BOOL success =
-        CreateProcessW(programArg, cmdlineArg, nullptr, nullptr,
-                       /*bInheritHandles=*/inheritHandles,
-                       /*dwCreationFlags=*/CREATE_UNICODE_ENVIRONMENT,
-                       envArg, cwdArg, &sui, &pi);
-    const int lastError = success ? 0 : GetLastError();
-
-    trace("CreateProcess: %s %u",
-          (success ? "success" : "fail"),
-          static_cast<unsigned int>(pi.dwProcessId));
-
-    auto reply = newPacket();
-    if (success) {
-        int64_t replyProcess = 0;
-        int64_t replyThread = 0;
-        if (wantProcessHandle) {
-            replyProcess = int64FromHandle(duplicateHandle(pi.hProcess));
-        }
-        if (wantThreadHandle) {
-            replyThread = int64FromHandle(duplicateHandle(pi.hThread));
-        }
-        CloseHandle(pi.hThread);
-        m_childProcess = pi.hProcess;
-        m_autoShutdown = (spawnFlags & WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN) != 0;
-        m_exitAfterShutdown = (spawnFlags & WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN) != 0;
-        reply.putInt32(static_cast<int32_t>(StartProcessResult::ProcessCreated));
-        reply.putInt64(replyProcess);
-        reply.putInt64(replyThread);
-    } else {
-        reply.putInt32(static_cast<int32_t>(StartProcessResult::CreateProcessFailed));
-        reply.putInt32(lastError);
-    }
-    writePacket(reply);
-}
-
-void Agent::handleSetSizePacket(ReadBuffer &packet)
-{
-    const int cols = packet.getInt32();
-    const int rows = packet.getInt32();
-    packet.assertEof();
-    resizeWindow(cols, rows);
-    auto reply = newPacket();
-    writePacket(reply);
-}
-
-void Agent::handleGetConsoleProcessListPacket(ReadBuffer &packet)
-{
-    packet.assertEof();
-
-    auto processList = std::vector<DWORD>(64);
-    auto processCount = GetConsoleProcessList(&processList[0], processList.size());
-    if (processList.size() < processCount) {
-        processList.resize(processCount);
-        processCount = GetConsoleProcessList(&processList[0], processList.size());
-    }
-
-    if (processCount == 0) {
-        trace("GetConsoleProcessList failed");
-    }
-
-    auto reply = newPacket();
-    reply.putInt32(processCount);
-    for (DWORD i = 0; i < processCount; i++) {
-        reply.putInt32(processList[i]);
-    }
-    writePacket(reply);
-}
-
-void Agent::pollConinPipe()
-{
-    const std::string newData = m_coninPipe->readAllToString();
-    if (hasDebugFlag("input_separated_bytes")) {
-        // This debug flag is intended to help with testing incomplete escape
-        // sequences and multibyte UTF-8 encodings.  (I wonder if the normal
-        // code path ought to advance a state machine one byte at a time.)
-        for (size_t i = 0; i < newData.size(); ++i) {
-            m_consoleInput->writeInput(newData.substr(i, 1));
-        }
-    } else {
-        m_consoleInput->writeInput(newData);
-    }
-}
-
-void Agent::onPollTimeout()
-{
-    m_consoleInput->updateInputFlags();
-    const bool enableMouseMode = m_consoleInput->shouldActivateTerminalMouse();
-
-    // Give the ConsoleInput object a chance to flush input from an incomplete
-    // escape sequence (e.g. pressing ESC).
-    m_consoleInput->flushIncompleteEscapeCode();
-
-    const bool shouldScrapeContent = !m_closingOutputPipes;
-
-    // Check if the child process has exited.
-    if (m_autoShutdown &&
-            m_childProcess != nullptr &&
-            WaitForSingleObject(m_childProcess, 0) == WAIT_OBJECT_0) {
-        CloseHandle(m_childProcess);
-        m_childProcess = nullptr;
-
-        // Close the data socket to signal to the client that the child
-        // process has exited.  If there's any data left to send, send it
-        // before closing the socket.
-        m_closingOutputPipes = true;
-    }
-
-    // Scrape for output *after* the above exit-check to ensure that we collect
-    // the child process's final output.
-    if (shouldScrapeContent) {
-        syncConsoleTitle();
-        scrapeBuffers();
-    }
-
-    // We must ensure that we disable mouse mode before closing the CONOUT
-    // pipe, so update the mouse mode here.
-    m_primaryScraper->terminal().enableMouseMode(
-        enableMouseMode && !m_closingOutputPipes);
-
-    autoClosePipesForShutdown();
-}
-
-void Agent::autoClosePipesForShutdown()
-{
-    if (m_closingOutputPipes) {
-        // We don't want to close a pipe before it's connected!  If we do, the
-        // libwinpty client may try to connect to a non-existent pipe.  This
-        // case is important for short-lived programs.
-        if (m_conoutPipe->isConnected() &&
-                m_conoutPipe->bytesToSend() == 0) {
-            trace("Closing CONOUT pipe (auto-shutdown)");
-            m_conoutPipe->closePipe();
-        }
-        if (m_conerrPipe != nullptr &&
-                m_conerrPipe->isConnected() &&
-                m_conerrPipe->bytesToSend() == 0) {
-            trace("Closing CONERR pipe (auto-shutdown)");
-            m_conerrPipe->closePipe();
-        }
-        if (m_exitAfterShutdown &&
-                m_conoutPipe->isClosed() &&
-                (m_conerrPipe == nullptr || m_conerrPipe->isClosed())) {
-            trace("Agent exiting (exit-after-shutdown)");
-            shutdown();
-        }
-    }
-}
-
-std::unique_ptr<Win32ConsoleBuffer> Agent::openPrimaryBuffer()
-{
-    // If we're using a separate buffer for stderr, and a program were to
-    // activate the stderr buffer, then we could accidentally scrape the same
-    // buffer twice.  That probably shouldn't happen in ordinary use, but it
-    // can be avoided anyway by using the original console screen buffer in
-    // that mode.
-    if (!m_useConerr) {
-        return Win32ConsoleBuffer::openConout();
-    } else {
-        return Win32ConsoleBuffer::openStdout();
-    }
-}
-
-void Agent::resizeWindow(int cols, int rows)
-{
-    ASSERT(cols >= 1 && rows >= 1);
-    cols = std::min(cols, MAX_CONSOLE_WIDTH);
-    rows = std::min(rows, MAX_CONSOLE_HEIGHT);
-
-    Win32Console::FreezeGuard guard(m_console, m_console.frozen());
-    const Coord newSize(cols, rows);
-    ConsoleScreenBufferInfo info;
-    auto primaryBuffer = openPrimaryBuffer();
-    m_primaryScraper->resizeWindow(*primaryBuffer, newSize, info);
-    m_consoleInput->setMouseWindowRect(info.windowRect());
-    if (m_errorScraper) {
-        m_errorScraper->resizeWindow(*m_errorBuffer, newSize, info);
-    }
-
-    // Synthesize a WINDOW_BUFFER_SIZE_EVENT event.  Normally, Windows
-    // generates this event only when the buffer size changes, not when the
-    // window size changes.  This behavior is undesirable in two ways:
-    //  - When winpty expands the window horizontally, it must expand the
-    //    buffer first, then the window.  At least some programs (e.g. the WSL
-    //    bash.exe wrapper) use the window width rather than the buffer width,
-    //    so there is a short timespan during which they can read the wrong
-    //    value.
-    //  - If the window's vertical size is changed, no event is generated,
-    //    even though a typical well-behaved console program cares about the
-    //    *window* height, not the *buffer* height.
-    // This synthesization works around a design flaw in the console.  It's probably
-    // harmless.  See https://github.com/rprichard/winpty/issues/110.
-    INPUT_RECORD sizeEvent {};
-    sizeEvent.EventType = WINDOW_BUFFER_SIZE_EVENT;
-    sizeEvent.Event.WindowBufferSizeEvent.dwSize = primaryBuffer->bufferSize();
-    DWORD actual {};
-    WriteConsoleInputW(GetStdHandle(STD_INPUT_HANDLE), &sizeEvent, 1, &actual);
-}
-
-void Agent::scrapeBuffers()
-{
-    Win32Console::FreezeGuard guard(m_console, m_console.frozen());
-    ConsoleScreenBufferInfo info;
-    m_primaryScraper->scrapeBuffer(*openPrimaryBuffer(), info);
-    m_consoleInput->setMouseWindowRect(info.windowRect());
-    if (m_errorScraper) {
-        m_errorScraper->scrapeBuffer(*m_errorBuffer, info);
-    }
-}
-
-void Agent::syncConsoleTitle()
-{
-    std::wstring newTitle = m_console.title();
-    if (newTitle != m_currentTitle) {
-        std::string command = std::string("\x1b]0;") +
-                utf8FromWide(newTitle) + "\x07";
-        m_conoutPipe->write(command.c_str());
-        m_currentTitle = newTitle;
-    }
-}