From 7f642e51670bc38a4ef782a363936850bc2b0ba9 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 06:38:23 -0700 Subject: Move dutil into libs/dutil --- src/libs/dutil/WixToolset.DUtil/conutil.cpp | 673 ++++++++++++++++++++++++++++ 1 file changed, 673 insertions(+) create mode 100644 src/libs/dutil/WixToolset.DUtil/conutil.cpp (limited to 'src/libs/dutil/WixToolset.DUtil/conutil.cpp') diff --git a/src/libs/dutil/WixToolset.DUtil/conutil.cpp b/src/libs/dutil/WixToolset.DUtil/conutil.cpp new file mode 100644 index 00000000..33e1b59a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/conutil.cpp @@ -0,0 +1,673 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ConExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__) +#define ConExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__) +#define ConExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__) +#define ConExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__) +#define ConExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CONUTIL, e, x, s, __VA_ARGS__) +#define ConExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CONUTIL, g, x, s, __VA_ARGS__) + + +static HANDLE vhStdIn = INVALID_HANDLE_VALUE; +static HANDLE vhStdOut = INVALID_HANDLE_VALUE; +static BOOL vfConsoleIn = FALSE; +static BOOL vfConsoleOut = FALSE; +static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo; + + +extern "C" HRESULT DAPI ConsoleInitialize() +{ + Assert(INVALID_HANDLE_VALUE == vhStdOut); + HRESULT hr = S_OK; + UINT er; + + vhStdIn = ::GetStdHandle(STD_INPUT_HANDLE); + if (INVALID_HANDLE_VALUE == vhStdIn) + { + ConExitOnLastError(hr, "failed to open stdin"); + } + + vhStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); + if (INVALID_HANDLE_VALUE == vhStdOut) + { + ConExitOnLastError(hr, "failed to open stdout"); + } + + // check if we have a std in on the console + if (::GetConsoleScreenBufferInfo(vhStdIn, &vcsbiInfo)) + { + vfConsoleIn = TRUE; + } + else + { + er = ::GetLastError(); + if (ERROR_INVALID_HANDLE == er) + { + vfConsoleIn= FALSE; + hr = S_OK; + } + else + { + ConExitOnWin32Error(er, hr, "failed to get input console screen buffer info"); + } + } + + if (::GetConsoleScreenBufferInfo(vhStdOut, &vcsbiInfo)) + { + vfConsoleOut = TRUE; + } + else // no console + { + memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); + er = ::GetLastError(); + if (ERROR_INVALID_HANDLE == er) + { + vfConsoleOut = FALSE; + hr = S_OK; + } + else + { + ConExitOnWin32Error(er, hr, "failed to get output console screen buffer info"); + } + } + +LExit: + if (FAILED(hr)) + { + if (INVALID_HANDLE_VALUE != vhStdOut) + { + ::CloseHandle(vhStdOut); + } + + if (INVALID_HANDLE_VALUE != vhStdIn && vhStdOut != vhStdIn) + { + ::CloseHandle(vhStdIn); + } + + vhStdOut = INVALID_HANDLE_VALUE; + vhStdIn = INVALID_HANDLE_VALUE; + } + + return hr; +} + + +extern "C" void DAPI ConsoleUninitialize() +{ + BOOL fOutEqualsIn = vhStdOut == vhStdIn; + + memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); + + if (INVALID_HANDLE_VALUE != vhStdOut) + { + ::CloseHandle(vhStdOut); + } + + if (INVALID_HANDLE_VALUE != vhStdIn && !fOutEqualsIn) + { + ::CloseHandle(vhStdIn); + } + + vhStdOut = INVALID_HANDLE_VALUE; + vhStdIn = INVALID_HANDLE_VALUE; +} + + +extern "C" void DAPI ConsoleGreen() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_GREEN | FOREGROUND_INTENSITY); + } +} + + +extern "C" void DAPI ConsoleRed() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_INTENSITY); + } +} + + +extern "C" void DAPI ConsoleYellow() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + } +} + + +extern "C" void DAPI ConsoleNormal() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, vcsbiInfo.wAttributes); + } +} + + +/******************************************************************** + ConsoleWrite - full color printfA without libc + + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d") + assumes already in normal color and resets the screen to normal color +********************************************************************/ +extern "C" HRESULT DAPI ConsoleWrite( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + LPSTR pszOutput = NULL; + DWORD cchOutput = 0; + DWORD cbWrote = 0; + DWORD cbTotal = 0; + + // set the color + switch (cc) + { + case CONSOLE_COLOR_NORMAL: break; // do nothing + case CONSOLE_COLOR_RED: ConsoleRed(); break; + case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break; + case CONSOLE_COLOR_GREEN: ConsoleGreen(); break; + } + + va_list args; + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); + va_end(args); + ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); + + cchOutput = lstrlenA(pszOutput); + while (cbTotal < (sizeof(*pszOutput) * cchOutput)) + { + if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) + { + ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput); + } + + cbTotal += cbWrote; + } + + // reset the color to normal + if (CONSOLE_COLOR_NORMAL != cc) + { + ConsoleNormal(); + } + +LExit: + ReleaseStr(pszOutput); + return hr; +} + + +/******************************************************************** + ConsoleWriteLine - full color printfA plus newline without libc + + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d") + assumes already in normal color and resets the screen to normal color +********************************************************************/ +extern "C" HRESULT DAPI ConsoleWriteLine( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + LPSTR pszOutput = NULL; + DWORD cchOutput = 0; + DWORD cbWrote = 0; + DWORD cbTotal = 0; + LPCSTR szNewLine = "\r\n"; + + // set the color + switch (cc) + { + case CONSOLE_COLOR_NORMAL: break; // do nothing + case CONSOLE_COLOR_RED: ConsoleRed(); break; + case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break; + case CONSOLE_COLOR_GREEN: ConsoleGreen(); break; + } + + va_list args; + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); + va_end(args); + ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); + + // + // write the string + // + cchOutput = lstrlenA(pszOutput); + while (cbTotal < (sizeof(*pszOutput) * cchOutput)) + { + if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) + ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput); + + cbTotal += cbWrote; + } + + // + // write the newline + // + if (!::WriteFile(vhStdOut, reinterpret_cast(szNewLine), 2, &cbWrote, NULL)) + { + ConExitOnLastError(hr, "failed to write newline to console"); + } + + // reset the color to normal + if (CONSOLE_COLOR_NORMAL != cc) + { + ConsoleNormal(); + } + +LExit: + ReleaseStr(pszOutput); + return hr; +} + + +/******************************************************************** + ConsoleWriteError - display an error to the screen + + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%s" or "%d") +********************************************************************/ +HRESULT ConsoleWriteError( + HRESULT hrError, + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + LPSTR pszMessage = NULL; + + va_list args; + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(&pszMessage, szFormat, args); + va_end(args); + ConExitOnFailure(hr, "failed to format error message: \"%s\"", szFormat); + + if (FAILED(hrError)) + { + hr = ConsoleWriteLine(cc, "Error 0x%x: %s", hrError, pszMessage); + } + else + { + hr = ConsoleWriteLine(cc, "Error: %s", pszMessage); + } + +LExit: + ReleaseStr(pszMessage); + return hr; +} + + +/******************************************************************** + ConsoleReadW - get console input without libc + + NOTE: only supports reading ANSI characters +********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadW( + __deref_out_z LPWSTR* ppwzBuffer + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + Assert(ppwzBuffer); + + HRESULT hr = S_OK; + LPSTR psz = NULL; + DWORD cch = 0; + DWORD cchRead = 0; + DWORD cchTotalRead = 0; + + cch = 64; + hr = StrAnsiAlloc(&psz, cch); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); + + // loop until we read the \r\n from the console + for (;;) + { + // read one character at a time, since that seems to be the only way to make this work + if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) + ConExitOnLastError(hr, "failed to read string from console"); + + cchTotalRead += cchRead; + if (1 < cchTotalRead && '\r' == psz[cchTotalRead - 2] || '\n' == psz[cchTotalRead - 1]) + { + psz[cchTotalRead - 2] = '\0'; // chop off the \r\n + break; + } + else if (0 == cchRead) // nothing more was read + { + psz[cchTotalRead] = '\0'; // null termintate and bail + break; + } + + if (cchTotalRead == cch) + { + cch *= 2; // double everytime we run out of space + hr = StrAnsiAlloc(&psz, cch); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); + } + } + + hr = StrAllocStringAnsi(ppwzBuffer, psz, 0, CP_ACP); + +LExit: + ReleaseStr(psz); + return hr; +} + + +/******************************************************************** + ConsoleReadNonBlockingW - Read from the console without blocking + Won't work for redirected files (exe < txtfile), but will work for stdin redirected to + an anonymous or named pipe + + if (fReadLine), stop reading immediately when \r\n is found +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadNonBlockingW( + __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, + __out DWORD* pcchSize, + BOOL fReadLine + ) +{ + Assert(INVALID_HANDLE_VALUE != vhStdIn && pcchSize); + HRESULT hr = S_OK; + + LPSTR psz = NULL; + + ConExitOnNull(ppwzBuffer, hr, E_INVALIDARG, "Failed to read from console because buffer was not provided"); + + DWORD dwRead; + DWORD dwNumInput; + + DWORD cchTotal = 0; + DWORD cch = 8; + + DWORD cchRead = 0; + DWORD cchTotalRead = 0; + + DWORD dwIndex = 0; + DWORD er; + + INPUT_RECORD ir; + WCHAR chIn; + + *ppwzBuffer = NULL; + *pcchSize = 0; + + // If we really have a handle to stdin, and not the end of a pipe + if (!PeekNamedPipe(vhStdIn, NULL, 0, NULL, &dwRead, NULL)) + { + er = ::GetLastError(); + if (ERROR_INVALID_HANDLE != er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + + if (!GetNumberOfConsoleInputEvents(vhStdIn, &dwRead)) + { + ConExitOnLastError(hr, "failed to peek at console input"); + } + + if (0 == dwRead) + { + ExitFunction1(hr = S_FALSE); + } + + for (/* dwRead from num of input events */; dwRead > 0; dwRead--) + { + if (!ReadConsoleInputW(vhStdIn, &ir, 1, &dwNumInput)) + { + ConExitOnLastError(hr, "Failed to read input from console"); + } + + // If what we have is a KEY_EVENT, and that event signifies keyUp, we're interested + if (KEY_EVENT == ir.EventType && FALSE == ir.Event.KeyEvent.bKeyDown) + { + chIn = ir.Event.KeyEvent.uChar.UnicodeChar; + + if (0 == cchTotal) + { + cchTotal = cch; + cch *= 2; + StrAlloc(ppwzBuffer, cch); + } + + (*ppwzBuffer)[dwIndex] = chIn; + + if (fReadLine && (L'\r' == (*ppwzBuffer)[dwIndex - 1] && L'\n' == (*ppwzBuffer)[dwIndex])) + { + *ppwzBuffer[dwIndex - 1] = L'\0'; + dwIndex -= 1; + break; + } + + ++dwIndex; + cchTotal--; + } + } + + *pcchSize = dwIndex; + } + else + { + // otherwise, the peek worked, and we have the end of a pipe + if (0 == dwRead) + ExitFunction1(hr = S_FALSE); + + cch = 8; + hr = StrAnsiAlloc(&psz, cch); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); + + for (/*dwRead from PeekNamedPipe*/; dwRead > 0; dwRead--) + { + // read one character at a time, since that seems to be the only way to make this work + if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + + cchTotalRead += cchRead; + if (fReadLine && '\r' == psz[cchTotalRead - 1] && '\n' == psz[cchTotalRead]) + { + psz[cchTotalRead - 1] = '\0'; // chop off the \r\n + cchTotalRead -= 1; + break; + } + else if (0 == cchRead) // nothing more was read + { + psz[cchTotalRead] = '\0'; // null termintate and bail + break; + } + + if (cchTotalRead == cch) + { + cch *= 2; // double everytime we run out of space + hr = StrAnsiAlloc(&psz, cch); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); + } + } + + *pcchSize = cchTotalRead; + hr = StrAllocStringAnsi(ppwzBuffer, psz, cchTotalRead, CP_ACP); + } + +LExit: + ReleaseStr(psz); + + return hr; +} + + +/******************************************************************** + ConsoleReadStringA - get console input without libc + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadStringA( + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer, + CONST DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + if (ppszCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2)) + { + DWORD iRead = 1; + DWORD iReadCharTotal = 0; + if (ppszCharBuffer && *ppszCharBuffer == NULL) + { + do + { + hr = StrAnsiAlloc(ppszCharBuffer, cchCharBuffer * iRead); + ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); + // ReadConsoleW will not return until , the last two chars are 13 and 10. + if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + iReadCharTotal += *pcchNumCharReturn; + iRead += 1; + } + while((*ppszCharBuffer)[iReadCharTotal - 1] != 10 || (*ppszCharBuffer)[iReadCharTotal - 2] != 13); + *pcchNumCharReturn = iReadCharTotal; + } + else + { + if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || + *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + if ((*ppszCharBuffer)[*pcchNumCharReturn - 1] != 10 || + (*ppszCharBuffer)[*pcchNumCharReturn - 2] != 13) + { + // need read more + hr = ERROR_MORE_DATA; + } + } + } + else + { + hr = E_INVALIDARG; + } + +LExit: + return hr; +} + +/******************************************************************** + ConsoleReadStringW - get console input without libc + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadStringW( + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer, + const DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + if (ppwzCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2)) + { + DWORD iRead = 1; + DWORD iReadCharTotal = 0; + if (*ppwzCharBuffer == NULL) + { + do + { + hr = StrAlloc(ppwzCharBuffer, cchCharBuffer * iRead); + ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); + // ReadConsoleW will not return until , the last two chars are 13 and 10. + if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + iReadCharTotal += *pcchNumCharReturn; + iRead += 1; + } + while((*ppwzCharBuffer)[iReadCharTotal - 1] != 10 || (*ppwzCharBuffer)[iReadCharTotal - 2] != 13); + *pcchNumCharReturn = iReadCharTotal; + } + else + { + if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || + *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + if ((*ppwzCharBuffer)[*pcchNumCharReturn - 1] != 10 || + (*ppwzCharBuffer)[*pcchNumCharReturn - 2] != 13) + { + // need read more + hr = ERROR_MORE_DATA; + } + } + } + else + { + hr = E_INVALIDARG; + } + +LExit: + return hr; +} + +/******************************************************************** + ConsoleSetReadHidden - set console input no echo + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleSetReadHidden(void) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + ::FlushConsoleInputBuffer(vhStdIn); + if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT)) + { + ConExitOnLastError(hr, "failed to set console input mode to be hidden"); + } + +LExit: + return hr; +} + +/******************************************************************** + ConsoleSetReadNormal - reset to echo + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleSetReadNormal(void) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT)) + { + ConExitOnLastError(hr, "failed to set console input mode to be normal"); + } + +LExit: + return hr; +} + -- cgit v1.2.3-55-g6feb