338 lines
12 KiB
C++
338 lines
12 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "precomp.h"
|
|
|
|
#include "misc.h"
|
|
#include "dbcs.h"
|
|
|
|
#include "../types/inc/convert.hpp"
|
|
#include "../types/inc/GlyphWidth.hpp"
|
|
|
|
#include "../interactivity/inc/ServiceLocator.hpp"
|
|
|
|
#pragma hdrstop
|
|
|
|
#define CHAR_NULL ((char)0)
|
|
|
|
using Microsoft::Console::Interactivity::ServiceLocator;
|
|
|
|
WCHAR CharToWchar(_In_reads_(cch) const char* const pch, const UINT cch)
|
|
{
|
|
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
|
WCHAR wc = L'\0';
|
|
|
|
FAIL_FAST_IF(!(IsDBCSLeadByteConsole(*pch, &gci.OutputCPInfo) || cch == 1));
|
|
|
|
ConvertOutputToUnicode(gci.OutputCP, pch, cch, &wc, 1);
|
|
|
|
return wc;
|
|
}
|
|
|
|
void SetConsoleCPInfo(const BOOL fOutput)
|
|
{
|
|
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
|
if (fOutput)
|
|
{
|
|
// If we're changing the output codepage, we want to update the font as well to give the engine an opportunity
|
|
// to pick a more appropriate font should the current one be unable to render in the new codepage.
|
|
// To do this, we create a copy of the existing font but we change the codepage value to be the new one that was just set in the global structures.
|
|
// NOTE: We need to do this only if everything is set up. This can get called while we're still initializing, so carefully check things for nullptr.
|
|
if (gci.HasActiveOutputBuffer())
|
|
{
|
|
SCREEN_INFORMATION& screenInfo = gci.GetActiveOutputBuffer();
|
|
const FontInfo& fiOld = screenInfo.GetCurrentFont();
|
|
|
|
// Use the desired face name when updating the font.
|
|
// This ensures that if we had a fall back operation last time (the desired
|
|
// face name didn't support the code page and we have a different less-desirable font currently)
|
|
// that we'll now give it another shot to use the desired face name in the new code page.
|
|
FontInfo fiNew(screenInfo.GetDesiredFont().GetFaceName(),
|
|
fiOld.GetFamily(),
|
|
fiOld.GetWeight(),
|
|
fiOld.GetUnscaledSize(),
|
|
gci.OutputCP);
|
|
screenInfo.UpdateFont(&fiNew);
|
|
}
|
|
|
|
if (!GetCPInfo(gci.OutputCP, &gci.OutputCPInfo))
|
|
{
|
|
gci.OutputCPInfo.LeadByte[0] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!GetCPInfo(gci.CP, &gci.CPInfo))
|
|
{
|
|
gci.CPInfo.LeadByte[0] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Routine Description:
|
|
// - This routine check bisected on Unicode string end.
|
|
// Arguments:
|
|
// - pwchBuffer - Pointer to Unicode string buffer.
|
|
// - cWords - Number of Unicode string.
|
|
// - cBytes - Number of bisect position by byte counts.
|
|
// Return Value:
|
|
// - TRUE - Bisected character.
|
|
// - FALSE - Correctly.
|
|
BOOL CheckBisectStringW(_In_reads_bytes_(cBytes) const WCHAR* pwchBuffer,
|
|
_In_ size_t cWords,
|
|
_In_ size_t cBytes) noexcept
|
|
{
|
|
while (cWords && cBytes)
|
|
{
|
|
if (IsGlyphFullWidth(*pwchBuffer))
|
|
{
|
|
if (cBytes < 2)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
cWords--;
|
|
cBytes -= 2;
|
|
pwchBuffer++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cWords--;
|
|
cBytes--;
|
|
pwchBuffer++;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Routine Description:
|
|
// - This routine check bisected on Unicode string end.
|
|
// Arguments:
|
|
// - ScreenInfo - reference to screen information structure.
|
|
// - pwchBuffer - Pointer to Unicode string buffer.
|
|
// - cWords - Number of Unicode string.
|
|
// - cBytes - Number of bisect position by byte counts.
|
|
// - fEcho - TRUE if called by Read (echoing characters)
|
|
// Return Value:
|
|
// - TRUE - Bisected character.
|
|
// - FALSE - Correctly.
|
|
BOOL CheckBisectProcessW(const SCREEN_INFORMATION& ScreenInfo,
|
|
_In_reads_bytes_(cBytes) const WCHAR* pwchBuffer,
|
|
_In_ size_t cWords,
|
|
_In_ size_t cBytes,
|
|
_In_ SHORT sOriginalXPosition,
|
|
_In_ BOOL fEcho)
|
|
{
|
|
if (WI_IsFlagSet(ScreenInfo.OutputMode, ENABLE_PROCESSED_OUTPUT))
|
|
{
|
|
while (cWords && cBytes)
|
|
{
|
|
WCHAR const Char = *pwchBuffer;
|
|
if (Char >= UNICODE_SPACE)
|
|
{
|
|
if (IsGlyphFullWidth(Char))
|
|
{
|
|
if (cBytes < 2)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
cWords--;
|
|
cBytes -= 2;
|
|
pwchBuffer++;
|
|
sOriginalXPosition += 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cWords--;
|
|
cBytes--;
|
|
pwchBuffer++;
|
|
sOriginalXPosition++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cWords--;
|
|
pwchBuffer++;
|
|
switch (Char)
|
|
{
|
|
case UNICODE_BELL:
|
|
if (fEcho)
|
|
goto CtrlChar;
|
|
break;
|
|
case UNICODE_BACKSPACE:
|
|
case UNICODE_LINEFEED:
|
|
case UNICODE_CARRIAGERETURN:
|
|
break;
|
|
case UNICODE_TAB:
|
|
{
|
|
size_t TabSize = NUMBER_OF_SPACES_IN_TAB(sOriginalXPosition);
|
|
sOriginalXPosition = (SHORT)(sOriginalXPosition + TabSize);
|
|
if (cBytes < TabSize)
|
|
return TRUE;
|
|
cBytes -= TabSize;
|
|
break;
|
|
}
|
|
default:
|
|
if (fEcho)
|
|
{
|
|
CtrlChar:
|
|
if (cBytes < 2)
|
|
return TRUE;
|
|
cBytes -= 2;
|
|
sOriginalXPosition += 2;
|
|
}
|
|
else
|
|
{
|
|
cBytes--;
|
|
sOriginalXPosition++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
return CheckBisectStringW(pwchBuffer, cWords, cBytes);
|
|
}
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Converts all key events in the deque to the oem char data and adds
|
|
// them back to events.
|
|
// Arguments:
|
|
// - events - on input the IInputEvents to convert. on output, the
|
|
// converted input events
|
|
// Note: may throw on error
|
|
void SplitToOem(std::deque<std::unique_ptr<IInputEvent>>& events)
|
|
{
|
|
const UINT codepage = ServiceLocator::LocateGlobals().getConsoleInformation().CP;
|
|
|
|
// convert events to oem codepage
|
|
std::deque<std::unique_ptr<IInputEvent>> convertedEvents;
|
|
while (!events.empty())
|
|
{
|
|
std::unique_ptr<IInputEvent> currentEvent = std::move(events.front());
|
|
events.pop_front();
|
|
if (currentEvent->EventType() == InputEventType::KeyEvent)
|
|
{
|
|
const KeyEvent* const pKeyEvent = static_cast<const KeyEvent* const>(currentEvent.get());
|
|
// convert from wchar to char
|
|
std::wstring wstr{ pKeyEvent->GetCharData() };
|
|
const auto str = ConvertToA(codepage, wstr);
|
|
|
|
for (auto& ch : str)
|
|
{
|
|
std::unique_ptr<KeyEvent> tempEvent = std::make_unique<KeyEvent>(*pKeyEvent);
|
|
tempEvent->SetCharData(ch);
|
|
convertedEvents.push_back(std::move(tempEvent));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
convertedEvents.push_back(std::move(currentEvent));
|
|
}
|
|
}
|
|
// move all events back
|
|
while (!convertedEvents.empty())
|
|
{
|
|
events.push_back(std::move(convertedEvents.front()));
|
|
convertedEvents.pop_front();
|
|
}
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Converts unicode characters to ANSI given a destination codepage
|
|
// Arguments:
|
|
// - uiCodePage - codepage for use in conversion
|
|
// - pwchSource - unicode string to convert
|
|
// - cchSource - length of pwchSource in characters
|
|
// - pchTarget - pointer to destination buffer to receive converted ANSI string
|
|
// - cchTarget - size of destination buffer in characters
|
|
// Return Value:
|
|
// - Returns the number characters written to pchTarget, or 0 on failure
|
|
int ConvertToOem(const UINT uiCodePage,
|
|
_In_reads_(cchSource) const WCHAR* const pwchSource,
|
|
const UINT cchSource,
|
|
_Out_writes_(cchTarget) CHAR* const pchTarget,
|
|
const UINT cchTarget) noexcept
|
|
{
|
|
FAIL_FAST_IF(!(pwchSource != (LPWSTR)pchTarget));
|
|
DBGCHARS(("ConvertToOem U->%d %.*ls\n", uiCodePage, cchSource > 10 ? 10 : cchSource, pwchSource));
|
|
// clang-format off
|
|
#pragma prefast(suppress: __WARNING_W2A_BEST_FIT, "WC_NO_BEST_FIT_CHARS doesn't work in many codepages. Retain old behavior.")
|
|
// clang-format on
|
|
return LOG_IF_WIN32_BOOL_FALSE(WideCharToMultiByte(uiCodePage, 0, pwchSource, cchSource, pchTarget, cchTarget, nullptr, nullptr));
|
|
}
|
|
|
|
// Data in the output buffer is the true unicode value.
|
|
int ConvertInputToUnicode(const UINT uiCodePage,
|
|
_In_reads_(cchSource) const CHAR* const pchSource,
|
|
const UINT cchSource,
|
|
_Out_writes_(cchTarget) WCHAR* const pwchTarget,
|
|
const UINT cchTarget) noexcept
|
|
{
|
|
DBGCHARS(("ConvertInputToUnicode %d->U %.*s\n", uiCodePage, cchSource > 10 ? 10 : cchSource, pchSource));
|
|
|
|
return MultiByteToWideChar(uiCodePage, 0, pchSource, cchSource, pwchTarget, cchTarget);
|
|
}
|
|
|
|
// Output data is always translated via the ansi codepage so glyph translation works.
|
|
int ConvertOutputToUnicode(_In_ UINT uiCodePage,
|
|
_In_reads_(cchSource) const CHAR* const pchSource,
|
|
_In_ UINT cchSource,
|
|
_Out_writes_(cchTarget) WCHAR* pwchTarget,
|
|
_In_ UINT cchTarget) noexcept
|
|
{
|
|
FAIL_FAST_IF(!(cchTarget > 0));
|
|
pwchTarget[0] = L'\0';
|
|
|
|
DBGCHARS(("ConvertOutputToUnicode %d->U %.*s\n", uiCodePage, cchSource > 10 ? 10 : cchSource, pchSource));
|
|
|
|
if (DoBuffersOverlap(reinterpret_cast<const BYTE* const>(pchSource),
|
|
cchSource * sizeof(CHAR),
|
|
reinterpret_cast<const BYTE* const>(pwchTarget),
|
|
cchTarget * sizeof(WCHAR)))
|
|
{
|
|
try
|
|
{
|
|
// buffers overlap so we need to copy one
|
|
std::string copyData(pchSource, cchSource);
|
|
return MultiByteToWideChar(uiCodePage, MB_USEGLYPHCHARS, copyData.data(), cchSource, pwchTarget, cchTarget);
|
|
}
|
|
catch (...)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return MultiByteToWideChar(uiCodePage, MB_USEGLYPHCHARS, pchSource, cchSource, pwchTarget, cchTarget);
|
|
}
|
|
}
|
|
|
|
// Routine Description:
|
|
// - checks if two buffers overlap
|
|
// Arguments:
|
|
// - pBufferA - pointer to start of first buffer
|
|
// - cbBufferA - size of first buffer, in bytes
|
|
// - pBufferB - pointer to start of second buffer
|
|
// - cbBufferB - size of second buffer, in bytes
|
|
// Return Value:
|
|
// - true if buffers overlap, false otherwise
|
|
bool DoBuffersOverlap(const BYTE* const pBufferA,
|
|
const UINT cbBufferA,
|
|
const BYTE* const pBufferB,
|
|
const UINT cbBufferB) noexcept
|
|
{
|
|
const BYTE* const pBufferAEnd = pBufferA + cbBufferA;
|
|
const BYTE* const pBufferBEnd = pBufferB + cbBufferB;
|
|
return (pBufferA <= pBufferB && pBufferAEnd >= pBufferB) || (pBufferB <= pBufferA && pBufferBEnd >= pBufferA);
|
|
}
|