bool COOKED_READ_DATA::ProcessInput()

in src/host/readDataCooked.cpp [479:817]


bool COOKED_READ_DATA::ProcessInput(const wchar_t wchOrig,
                                    const DWORD keyState,
                                    NTSTATUS& status)
{
    const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
    size_t NumSpaces = 0;
    SHORT ScrollY = 0;
    size_t NumToWrite;
    WCHAR wch = wchOrig;
    bool fStartFromDelim;

    status = STATUS_SUCCESS;
    if (_bytesRead >= (_bufferSize - (2 * sizeof(WCHAR))) && wch != UNICODE_CARRIAGERETURN && wch != UNICODE_BACKSPACE)
    {
        return false;
    }

    if (_ctrlWakeupMask != 0 && wch < L' ' && (_ctrlWakeupMask & (1 << wch)))
    {
        *_bufPtr = wch;
        _bytesRead += sizeof(WCHAR);
        _bufPtr += 1;
        _currentPosition += 1;
        _controlKeyState = keyState;
        return true;
    }

    if (wch == EXTKEY_ERASE_PREV_WORD)
    {
        wch = UNICODE_BACKSPACE;
    }

    if (AtEol())
    {
        // If at end of line, processing is relatively simple. Just store the character and write it to the screen.
        if (wch == UNICODE_BACKSPACE2)
        {
            wch = UNICODE_BACKSPACE;
        }

        if (wch != UNICODE_BACKSPACE || _bufPtr != _backupLimit)
        {
            fStartFromDelim = IsWordDelim(_bufPtr[-1]);

            bool loop = true;
            while (loop)
            {
                loop = false;
                if (_echoInput)
                {
                    NumToWrite = sizeof(WCHAR);
                    status = WriteCharsLegacy(_screenInfo,
                                              _backupLimit,
                                              _bufPtr,
                                              &wch,
                                              &NumToWrite,
                                              &NumSpaces,
                                              _originalCursorPosition.X,
                                              WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_PRINTABLE_CONTROL_CHARS,
                                              &ScrollY);
                    if (NT_SUCCESS(status))
                    {
                        _originalCursorPosition.Y += ScrollY;
                    }
                    else
                    {
                        RIPMSG1(RIP_WARNING, "WriteCharsLegacy failed %x", status);
                    }
                }

                _visibleCharCount += NumSpaces;
                if (wch == UNICODE_BACKSPACE && _processedInput)
                {
                    _bytesRead -= sizeof(WCHAR);
                    // clang-format off
#pragma prefast(suppress: __WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY, "This access is fine")
                    // clang-format on
                    *_bufPtr = (WCHAR)' ';
                    _bufPtr -= 1;
                    _currentPosition -= 1;

                    // Repeat until it hits the word boundary
                    if (wchOrig == EXTKEY_ERASE_PREV_WORD &&
                        _bufPtr != _backupLimit &&
                        fStartFromDelim ^ !IsWordDelim(_bufPtr[-1]))
                    {
                        loop = true;
                    }
                }
                else
                {
                    *_bufPtr = wch;
                    _bytesRead += sizeof(WCHAR);
                    _bufPtr += 1;
                    _currentPosition += 1;
                }
            }
        }
    }
    else
    {
        bool CallWrite = true;
        const SHORT sScreenBufferSizeX = _screenInfo.GetBufferSize().Width();

        // processing in the middle of the line is more complex:

        // calculate new cursor position
        // store new char
        // clear the current command line from the screen
        // write the new command line to the screen
        // update the cursor position

        if (wch == UNICODE_BACKSPACE && _processedInput)
        {
            // for backspace, use writechars to calculate the new cursor position.
            // this call also sets the cursor to the right position for the
            // second call to writechars.

            if (_bufPtr != _backupLimit)
            {
                fStartFromDelim = IsWordDelim(_bufPtr[-1]);

                bool loop = true;
                while (loop)
                {
                    loop = false;
                    // we call writechar here so that cursor position gets updated
                    // correctly.  we also call it later if we're not at eol so
                    // that the remainder of the string can be updated correctly.

                    if (_echoInput)
                    {
                        NumToWrite = sizeof(WCHAR);
                        status = WriteCharsLegacy(_screenInfo,
                                                  _backupLimit,
                                                  _bufPtr,
                                                  &wch,
                                                  &NumToWrite,
                                                  nullptr,
                                                  _originalCursorPosition.X,
                                                  WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_PRINTABLE_CONTROL_CHARS,
                                                  nullptr);
                        if (!NT_SUCCESS(status))
                        {
                            RIPMSG1(RIP_WARNING, "WriteCharsLegacy failed %x", status);
                        }
                    }
                    _bytesRead -= sizeof(WCHAR);
                    _bufPtr -= 1;
                    _currentPosition -= 1;
                    memmove(_bufPtr,
                            _bufPtr + 1,
                            _bytesRead - (_currentPosition * sizeof(WCHAR)));
                    {
                        PWCHAR buf = (PWCHAR)((PBYTE)_backupLimit + _bytesRead);
                        *buf = (WCHAR)' ';
                    }
                    NumSpaces = 0;

                    // Repeat until it hits the word boundary
                    if (wchOrig == EXTKEY_ERASE_PREV_WORD &&
                        _bufPtr != _backupLimit &&
                        fStartFromDelim ^ !IsWordDelim(_bufPtr[-1]))
                    {
                        loop = true;
                    }
                }
            }
            else
            {
                CallWrite = false;
            }
        }
        else
        {
            // store the char
            if (wch == UNICODE_CARRIAGERETURN)
            {
                _bufPtr = (PWCHAR)((PBYTE)_backupLimit + _bytesRead);
                *_bufPtr = wch;
                _bufPtr += 1;
                _bytesRead += sizeof(WCHAR);
                _currentPosition += 1;
            }
            else
            {
                bool fBisect = false;

                if (_echoInput)
                {
                    if (CheckBisectProcessW(_screenInfo,
                                            _backupLimit,
                                            _currentPosition + 1,
                                            sScreenBufferSizeX - _originalCursorPosition.X,
                                            _originalCursorPosition.X,
                                            TRUE))
                    {
                        fBisect = true;
                    }
                }

                if (_insertMode)
                {
                    memmove(_bufPtr + 1,
                            _bufPtr,
                            _bytesRead - (_currentPosition * sizeof(WCHAR)));
                    _bytesRead += sizeof(WCHAR);
                }
                *_bufPtr = wch;
                _bufPtr += 1;
                _currentPosition += 1;

                // calculate new cursor position
                if (_echoInput)
                {
                    NumSpaces = RetrieveNumberOfSpaces(_originalCursorPosition.X,
                                                       _backupLimit,
                                                       _currentPosition - 1);
                    if (NumSpaces > 0 && fBisect)
                        NumSpaces--;
                }
            }
        }

        if (_echoInput && CallWrite)
        {
            COORD CursorPosition;

            // save cursor position
            CursorPosition = _screenInfo.GetTextBuffer().GetCursor().GetPosition();
            CursorPosition.X = (SHORT)(CursorPosition.X + NumSpaces);

            // clear the current command line from the screen
            // clang-format off
#pragma prefast(suppress: __WARNING_BUFFER_OVERFLOW, "Not sure why prefast doesn't like this call.")
            // clang-format on
            DeleteCommandLine(*this, FALSE);

            // write the new command line to the screen
            NumToWrite = _bytesRead;

            DWORD dwFlags = WC_DESTRUCTIVE_BACKSPACE | WC_PRINTABLE_CONTROL_CHARS;
            if (wch == UNICODE_CARRIAGERETURN)
            {
                dwFlags |= WC_KEEP_CURSOR_VISIBLE;
            }
            status = WriteCharsLegacy(_screenInfo,
                                      _backupLimit,
                                      _backupLimit,
                                      _backupLimit,
                                      &NumToWrite,
                                      &_visibleCharCount,
                                      _originalCursorPosition.X,
                                      dwFlags,
                                      &ScrollY);
            if (!NT_SUCCESS(status))
            {
                RIPMSG1(RIP_WARNING, "WriteCharsLegacy failed 0x%x", status);
                _bytesRead = 0;
                return true;
            }

            // update cursor position
            if (wch != UNICODE_CARRIAGERETURN)
            {
                if (CheckBisectProcessW(_screenInfo,
                                        _backupLimit,
                                        _currentPosition + 1,
                                        sScreenBufferSizeX - _originalCursorPosition.X,
                                        _originalCursorPosition.X,
                                        TRUE))
                {
                    if (CursorPosition.X == (sScreenBufferSizeX - 1))
                    {
                        CursorPosition.X++;
                    }
                }

                // adjust cursor position for WriteChars
                _originalCursorPosition.Y += ScrollY;
                CursorPosition.Y += ScrollY;
                status = AdjustCursorPosition(_screenInfo, CursorPosition, TRUE, nullptr);
                if (!NT_SUCCESS(status))
                {
                    _bytesRead = 0;
                    return true;
                }
            }
        }
    }

    // in cooked mode, enter (carriage return) is converted to
    // carriage return linefeed (0xda).  carriage return is always
    // stored at the end of the buffer.
    if (wch == UNICODE_CARRIAGERETURN)
    {
        if (_processedInput)
        {
            if (_bytesRead < _bufferSize)
            {
                *_bufPtr = UNICODE_LINEFEED;
                if (_echoInput)
                {
                    NumToWrite = sizeof(WCHAR);
                    status = WriteCharsLegacy(_screenInfo,
                                              _backupLimit,
                                              _bufPtr,
                                              _bufPtr,
                                              &NumToWrite,
                                              nullptr,
                                              _originalCursorPosition.X,
                                              WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_PRINTABLE_CONTROL_CHARS,
                                              nullptr);
                    if (!NT_SUCCESS(status))
                    {
                        RIPMSG1(RIP_WARNING, "WriteCharsLegacy failed 0x%x", status);
                    }
                }
                _bytesRead += sizeof(WCHAR);
                _bufPtr++;
                _currentPosition += 1;
            }
        }
        // reset the cursor back to 25% if necessary
        if (_lineInput)
        {
            if (_insertMode != gci.GetInsertMode())
            {
                // Make cursor small.
                LOG_IF_FAILED(CommandLine::Instance().ProcessCommandLine(*this, VK_INSERT, 0));
            }

            status = STATUS_SUCCESS;
            return true;
        }
    }

    return false;
}