mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-16 06:25:08 -05:00
442 lines
13 KiB
C++
442 lines
13 KiB
C++
|
/*****************************************************************************/
|
||
|
/* TLogHelper.cpp Copyright (c) Ladislav Zezula 2013 */
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
/* Helper class for reporting StormLib tests */
|
||
|
/* This file should be included directly from StormTest.cpp using #include */
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
/* Date Ver Who Comment */
|
||
|
/* -------- ---- --- ------- */
|
||
|
/* 26.11.13 1.00 Lad The first version of TLogHelper.cpp */
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Definition of the TLogHelper class
|
||
|
|
||
|
class TLogHelper
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
TLogHelper(const char * szNewMainTitle = NULL, const TCHAR * szNewSubTitle1 = NULL, const TCHAR * szNewSubTitle2 = NULL);
|
||
|
~TLogHelper();
|
||
|
|
||
|
#if defined(UNICODE) || defined(UNICODE)
|
||
|
// TCHAR-based functions. They are only needed on UNICODE builds.
|
||
|
// On ANSI builds is TCHAR = char, so we don't need them at all
|
||
|
int PrintWithClreol(const TCHAR * szFormat, va_list argList, bool bPrintPrefix, bool bPrintLastError, bool bPrintEndOfLine);
|
||
|
void PrintProgress(const TCHAR * szFormat, ...);
|
||
|
void PrintMessage(const TCHAR * szFormat, ...);
|
||
|
int PrintErrorVa(const TCHAR * szFormat, ...);
|
||
|
int PrintError(const TCHAR * szFormat, const TCHAR * szFileName = NULL);
|
||
|
#endif // defined(UNICODE) || defined(UNICODE)
|
||
|
|
||
|
// ANSI functions
|
||
|
DWORD PrintWithClreol(const char * szFormat, va_list argList, bool bPrintPrefix, bool bPrintLastError, bool bPrintEndOfLine);
|
||
|
void PrintProgress(const char * szFormat, ...);
|
||
|
void PrintMessage(const char * szFormat, ...);
|
||
|
DWORD PrintErrorVa(const char * szFormat, ...);
|
||
|
DWORD PrintError(const char * szFormat, const char * szFileName = NULL);
|
||
|
|
||
|
const char * UserString;
|
||
|
unsigned int UserCount;
|
||
|
unsigned int UserTotal;
|
||
|
bool bDontPrintResult;
|
||
|
|
||
|
protected:
|
||
|
|
||
|
#if defined(UNICODE) || defined(UNICODE)
|
||
|
TCHAR * CopyFormatCharacter(TCHAR * szBuffer, const TCHAR *& szFormat);
|
||
|
#endif
|
||
|
char * CopyFormatCharacter(char * szBuffer, const char *& szFormat);
|
||
|
int GetConsoleWidth();
|
||
|
|
||
|
const char * szMainTitle; // Title of the text (usually name)
|
||
|
const TCHAR * szSubTitle1; // Title of the text (can be name of the tested file)
|
||
|
const TCHAR * szSubTitle2; // Title of the text (can be name of the tested file)
|
||
|
size_t nTextLength; // Length of the previous progress message
|
||
|
bool bMessagePrinted;
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// String replacements for format strings
|
||
|
|
||
|
#ifdef _MSC_VER
|
||
|
#define I64u_t _T("%I64u")
|
||
|
#define I64u_a "%I64u"
|
||
|
#define I64X_t _T("%I64X")
|
||
|
#define I64X_a "%I64X"
|
||
|
#else
|
||
|
#define I64u_t _T("%llu")
|
||
|
#define I64u_a "%llu"
|
||
|
#define I64X_t _T("%llX")
|
||
|
#define I64X_a "%llX"
|
||
|
#endif
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Constructor and destructor
|
||
|
|
||
|
|
||
|
TLogHelper::TLogHelper(const char * szNewMainTitle, const TCHAR * szNewSubTitle1, const TCHAR * szNewSubTitle2)
|
||
|
{
|
||
|
TCHAR szMainTitleT[0x80];
|
||
|
|
||
|
UserString = "";
|
||
|
UserCount = 1;
|
||
|
UserTotal = 1;
|
||
|
|
||
|
// Fill the test line structure
|
||
|
szMainTitle = szNewMainTitle;
|
||
|
szSubTitle1 = szNewSubTitle1;
|
||
|
szSubTitle2 = szNewSubTitle2;
|
||
|
nTextLength = 0;
|
||
|
bMessagePrinted = false;
|
||
|
bDontPrintResult = false;
|
||
|
|
||
|
// Copy the UNICODE main title
|
||
|
StringCopy(szMainTitleT, _countof(szMainTitleT), szMainTitle);
|
||
|
|
||
|
// Print the initial information
|
||
|
if(szMainTitle != NULL)
|
||
|
{
|
||
|
if(szSubTitle1 != NULL && szSubTitle2 != NULL)
|
||
|
_tprintf(_T("Running %s (%s+%s) ..."), szMainTitleT, szSubTitle1, szSubTitle2);
|
||
|
else if(szSubTitle1 != NULL)
|
||
|
_tprintf(_T("Running %s (%s) ..."), szMainTitleT, szSubTitle1);
|
||
|
else
|
||
|
_tprintf(_T("Running %s ..."), szMainTitleT);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TLogHelper::~TLogHelper()
|
||
|
{
|
||
|
const TCHAR * szSaveSubTitle1 = szSubTitle1;
|
||
|
const TCHAR * szSaveSubTitle2 = szSubTitle2;
|
||
|
TCHAR szSaveMainTitle[0x80];
|
||
|
|
||
|
// Set both to NULL so they won't be printed
|
||
|
StringCopy(szSaveMainTitle, _countof(szSaveMainTitle), szMainTitle);
|
||
|
szSubTitle1 = NULL;
|
||
|
szSubTitle2 = NULL;
|
||
|
szMainTitle = NULL;
|
||
|
|
||
|
// Print the final information
|
||
|
if(szSaveMainTitle != NULL && bMessagePrinted == false)
|
||
|
{
|
||
|
if(bDontPrintResult == false)
|
||
|
{
|
||
|
if(szSaveSubTitle1 != NULL && szSaveSubTitle2 != NULL)
|
||
|
PrintMessage(_T("%s (%s+%s) succeeded."), szSaveMainTitle, szSaveSubTitle1, szSaveSubTitle2);
|
||
|
else if(szSaveSubTitle1 != NULL)
|
||
|
PrintMessage(_T("%s (%s) succeeded."), szSaveMainTitle, szSaveSubTitle1);
|
||
|
else
|
||
|
PrintMessage(_T("%s succeeded."), szSaveMainTitle);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PrintProgress(" ");
|
||
|
printf("\r");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if defined(_MSC_VER) && defined(_DEBUG)
|
||
|
if(_CrtDumpMemoryLeaks())
|
||
|
{
|
||
|
PrintMessage(_T("Memory leak(s) detected after %s.\n"), szSaveMainTitle);
|
||
|
}
|
||
|
#endif // _MSC_VER
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// TCHAR-based functions. They are only needed on UNICODE builds.
|
||
|
// On ANSI builds is TCHAR = char, so we don't need them at all
|
||
|
|
||
|
#if defined(UNICODE) || defined(UNICODE)
|
||
|
int TLogHelper::PrintWithClreol(const TCHAR * szFormat, va_list argList, bool bPrintPrefix, bool bPrintLastError, bool bPrintEndOfLine)
|
||
|
{
|
||
|
TCHAR szFormatBuff[0x200];
|
||
|
TCHAR szMessage[0x200];
|
||
|
TCHAR * szBuffer = szFormatBuff;
|
||
|
int nRemainingWidth;
|
||
|
int nConsoleWidth = GetConsoleWidth();
|
||
|
int nLength = 0;
|
||
|
int nError = GetLastError();
|
||
|
|
||
|
// Always start the buffer with '\r'
|
||
|
*szBuffer++ = '\r';
|
||
|
|
||
|
// Print the prefix, if needed
|
||
|
if(szMainTitle != NULL && bPrintPrefix)
|
||
|
{
|
||
|
while(szMainTitle[nLength] != 0)
|
||
|
*szBuffer++ = szMainTitle[nLength++];
|
||
|
|
||
|
*szBuffer++ = ':';
|
||
|
*szBuffer++ = ' ';
|
||
|
}
|
||
|
|
||
|
// Copy the message format itself. Replace %s with "%s", unless it's (%s)
|
||
|
if(szFormat != NULL)
|
||
|
{
|
||
|
while(szFormat[0] != 0)
|
||
|
{
|
||
|
szBuffer = CopyFormatCharacter(szBuffer, szFormat);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Append the last error
|
||
|
if(bPrintLastError)
|
||
|
{
|
||
|
nLength = _stprintf(szBuffer, _T(" (error code: %u)"), nError);
|
||
|
szBuffer += nLength;
|
||
|
}
|
||
|
|
||
|
// Create the result string
|
||
|
szBuffer[0] = 0;
|
||
|
nLength = _vstprintf(szMessage, szFormatBuff, argList);
|
||
|
szBuffer = szMessage + nLength;
|
||
|
|
||
|
// Shall we pad the string?
|
||
|
if(nLength < nConsoleWidth)
|
||
|
{
|
||
|
// Calculate the remaining width
|
||
|
nRemainingWidth = nConsoleWidth - nLength - 1;
|
||
|
|
||
|
// Pad the string with spaces to fill it up to the end of the line
|
||
|
for(int i = 0; i < nRemainingWidth; i++)
|
||
|
*szBuffer++ = 0x20;
|
||
|
}
|
||
|
|
||
|
// Put the newline, if requested
|
||
|
*szBuffer++ = bPrintEndOfLine ? '\n' : 0;
|
||
|
*szBuffer = 0;
|
||
|
|
||
|
// Remember if we printed a message
|
||
|
if(bPrintEndOfLine)
|
||
|
bMessagePrinted = true;
|
||
|
|
||
|
// Spit out the text in one single printf
|
||
|
_tprintf(_T("%s"), szMessage);
|
||
|
return nError;
|
||
|
}
|
||
|
|
||
|
void TLogHelper::PrintProgress(const TCHAR * szFormat, ...)
|
||
|
{
|
||
|
va_list argList;
|
||
|
|
||
|
va_start(argList, szFormat);
|
||
|
PrintWithClreol(szFormat, argList, true, false, false);
|
||
|
va_end(argList);
|
||
|
}
|
||
|
|
||
|
void TLogHelper::PrintMessage(const TCHAR * szFormat, ...)
|
||
|
{
|
||
|
va_list argList;
|
||
|
|
||
|
va_start(argList, szFormat);
|
||
|
PrintWithClreol(szFormat, argList, true, false, true);
|
||
|
va_end(argList);
|
||
|
}
|
||
|
|
||
|
int TLogHelper::PrintErrorVa(const TCHAR * szFormat, ...)
|
||
|
{
|
||
|
va_list argList;
|
||
|
int nResult;
|
||
|
|
||
|
va_start(argList, szFormat);
|
||
|
nResult = PrintWithClreol(szFormat, argList, true, true, true);
|
||
|
va_end(argList);
|
||
|
|
||
|
return nResult;
|
||
|
}
|
||
|
|
||
|
int TLogHelper::PrintError(const TCHAR * szFormat, const TCHAR * szFileName)
|
||
|
{
|
||
|
return PrintErrorVa(szFormat, szFileName);
|
||
|
}
|
||
|
#endif // defined(UNICODE) || defined(UNICODE)
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// ANSI functions
|
||
|
|
||
|
DWORD TLogHelper::PrintWithClreol(const char * szFormat, va_list argList, bool bPrintPrefix, bool bPrintLastError, bool bPrintEndOfLine)
|
||
|
{
|
||
|
char szFormatBuff[0x200];
|
||
|
char szMessage[0x200];
|
||
|
char * szBuffer = szFormatBuff;
|
||
|
int nRemainingWidth;
|
||
|
int nConsoleWidth = GetConsoleWidth();
|
||
|
int nLength = 0;
|
||
|
DWORD dwErrCode = GetLastError();
|
||
|
|
||
|
// Always start the buffer with '\r'
|
||
|
*szBuffer++ = '\r';
|
||
|
|
||
|
// Print the prefix, if needed
|
||
|
if(szMainTitle != NULL && bPrintPrefix)
|
||
|
{
|
||
|
while(szMainTitle[nLength] != 0)
|
||
|
*szBuffer++ = (char)szMainTitle[nLength++];
|
||
|
|
||
|
*szBuffer++ = ':';
|
||
|
*szBuffer++ = ' ';
|
||
|
}
|
||
|
|
||
|
// Copy the message format itself. Replace %s with "%s", unless it's (%s)
|
||
|
if(szFormat != NULL)
|
||
|
{
|
||
|
while(szFormat[0] != 0)
|
||
|
{
|
||
|
szBuffer = CopyFormatCharacter(szBuffer, szFormat);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Append the last error
|
||
|
if(bPrintLastError)
|
||
|
{
|
||
|
nLength = sprintf(szBuffer, " (error code: %u)", dwErrCode);
|
||
|
szBuffer += nLength;
|
||
|
}
|
||
|
|
||
|
// Create the result string
|
||
|
szBuffer[0] = 0;
|
||
|
nLength = vsprintf(szMessage, szFormatBuff, argList);
|
||
|
|
||
|
// Shall we pad the string?
|
||
|
szBuffer = szMessage + nLength;
|
||
|
if(nLength < nConsoleWidth)
|
||
|
{
|
||
|
// Calculate the remaining width
|
||
|
nRemainingWidth = nConsoleWidth - nLength - 1;
|
||
|
|
||
|
// Pad the string with spaces to fill it up to the end of the line
|
||
|
for(int i = 0; i < nRemainingWidth; i++)
|
||
|
*szBuffer++ = 0x20;
|
||
|
}
|
||
|
|
||
|
// Put the newline, if requested
|
||
|
*szBuffer++ = bPrintEndOfLine ? '\n' : '\r';
|
||
|
*szBuffer = 0;
|
||
|
|
||
|
// Remember if we printed a message
|
||
|
if(bPrintEndOfLine)
|
||
|
bMessagePrinted = true;
|
||
|
|
||
|
// Spit out the text in one single printf
|
||
|
printf("%s", szMessage);
|
||
|
return dwErrCode;
|
||
|
}
|
||
|
|
||
|
void TLogHelper::PrintProgress(const char * szFormat, ...)
|
||
|
{
|
||
|
va_list argList;
|
||
|
|
||
|
va_start(argList, szFormat);
|
||
|
PrintWithClreol(szFormat, argList, true, false, false);
|
||
|
va_end(argList);
|
||
|
}
|
||
|
|
||
|
void TLogHelper::PrintMessage(const char * szFormat, ...)
|
||
|
{
|
||
|
va_list argList;
|
||
|
|
||
|
va_start(argList, szFormat);
|
||
|
PrintWithClreol(szFormat, argList, true, false, true);
|
||
|
va_end(argList);
|
||
|
}
|
||
|
|
||
|
DWORD TLogHelper::PrintErrorVa(const char * szFormat, ...)
|
||
|
{
|
||
|
va_list argList;
|
||
|
DWORD dwErrCode;
|
||
|
|
||
|
va_start(argList, szFormat);
|
||
|
dwErrCode = PrintWithClreol(szFormat, argList, true, true, true);
|
||
|
va_end(argList);
|
||
|
|
||
|
return dwErrCode;
|
||
|
}
|
||
|
|
||
|
DWORD TLogHelper::PrintError(const char * szFormat, const char * szFileName)
|
||
|
{
|
||
|
return PrintErrorVa(szFormat, szFileName);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Protected functions
|
||
|
|
||
|
#ifdef _UNICODE
|
||
|
TCHAR * TLogHelper::CopyFormatCharacter(TCHAR * szBuffer, const TCHAR *& szFormat)
|
||
|
{
|
||
|
static const TCHAR * szStringFormat = _T("\"%s\"");
|
||
|
static const TCHAR * szUint64Format = I64u_t;
|
||
|
|
||
|
// String format
|
||
|
if(szFormat[0] == '%')
|
||
|
{
|
||
|
if(szFormat[1] == 's' && szFormat[2] != ')')
|
||
|
{
|
||
|
_tcscpy(szBuffer, szStringFormat);
|
||
|
szFormat += 2;
|
||
|
return szBuffer + _tcslen(szStringFormat);
|
||
|
}
|
||
|
|
||
|
// Replace %I64u with the proper platform-dependent suffix
|
||
|
if(szFormat[1] == 'I' && szFormat[2] == '6' && szFormat[3] == '4' && szFormat[4] == 'u')
|
||
|
{
|
||
|
_tcscpy(szBuffer, szUint64Format);
|
||
|
szFormat += 5;
|
||
|
return szBuffer + _tcslen(szUint64Format);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Copy the character as-is
|
||
|
*szBuffer++ = *szFormat++;
|
||
|
return szBuffer;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
char * TLogHelper::CopyFormatCharacter(char * szBuffer, const char *& szFormat)
|
||
|
{
|
||
|
static const char * szStringFormat = "\"%s\"";
|
||
|
static const char * szUint64Format = I64u_a;
|
||
|
|
||
|
// String format
|
||
|
if(szFormat[0] == '%')
|
||
|
{
|
||
|
if(szFormat[1] == 's' && szFormat[2] != ')')
|
||
|
{
|
||
|
strcpy(szBuffer, szStringFormat);
|
||
|
szFormat += 2;
|
||
|
return szBuffer + strlen(szStringFormat);
|
||
|
}
|
||
|
|
||
|
// Replace %I64u with the proper platform-dependent suffix
|
||
|
if(szFormat[1] == 'I' && szFormat[2] == '6' && szFormat[3] == '4' && szFormat[4] == 'u')
|
||
|
{
|
||
|
strcpy(szBuffer, szUint64Format);
|
||
|
szFormat += 5;
|
||
|
return szBuffer + strlen(szUint64Format);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Copy the character as-is
|
||
|
*szBuffer++ = *szFormat++;
|
||
|
return szBuffer;
|
||
|
}
|
||
|
|
||
|
int TLogHelper::GetConsoleWidth()
|
||
|
{
|
||
|
#ifdef STORMLIB_WINDOWS
|
||
|
|
||
|
CONSOLE_SCREEN_BUFFER_INFO ScreenInfo;
|
||
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ScreenInfo);
|
||
|
return (int)(ScreenInfo.srWindow.Right - ScreenInfo.srWindow.Left);
|
||
|
|
||
|
#else
|
||
|
|
||
|
// On non-Windows platforms, we assume that width of the console line
|
||
|
// is 80 characters
|
||
|
return 120;
|
||
|
|
||
|
#endif
|
||
|
}
|