3095 lines
50 KiB
C++
3095 lines
50 KiB
C++
/*++
|
|
|
|
Copyright (c) 1994-1998 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
strfrn.cpp
|
|
|
|
Abstract:
|
|
|
|
String Functions
|
|
|
|
Author:
|
|
|
|
Ronald Meijer (ronaldm)
|
|
Munged to work with setup by BoydM
|
|
|
|
Project:
|
|
|
|
Internet Services Manager
|
|
And now setup too
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
//
|
|
// Include Files
|
|
//
|
|
#include "stdafx.h"
|
|
#include "strfn.h"
|
|
#include <pudebug.h>
|
|
|
|
|
|
#ifdef _MT
|
|
|
|
//
|
|
// Thread protected stuff
|
|
//
|
|
#define RaiseThreadProtection() EnterCriticalSection(&_csSect)
|
|
#define LowerThreadProtection() LeaveCriticalSection(&_csSect)
|
|
|
|
static CRITICAL_SECTION _csSect;
|
|
|
|
#else
|
|
|
|
#pragma message("Module is not thread-safe")
|
|
|
|
#define RaiseThreadProtection()
|
|
#define LowerThreadProtection()
|
|
|
|
#endif // _MT
|
|
|
|
#define MAKE_NULL(obj) { if (obj) delete obj, obj = NULL; }
|
|
|
|
|
|
//
|
|
// Text copy functions
|
|
//
|
|
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
|
|
BOOL
|
|
PCToUnixText(
|
|
OUT LPWSTR & lpstrDestination,
|
|
IN const CString strSource
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert CR/LF string to LF string (T String to W String). Destination
|
|
string will be allocated.
|
|
|
|
Arguments:
|
|
|
|
LPWSTR & lpstrDestination : Destination string
|
|
const CString & strSource : Source string
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE for failure.
|
|
|
|
--*/
|
|
{
|
|
int cch = strSource.GetLength() + 1;
|
|
lpstrDestination = (LPWSTR)AllocMem(cch * sizeof(WCHAR));
|
|
if (lpstrDestination != NULL)
|
|
{
|
|
LPCTSTR lpS = strSource;
|
|
LPWSTR lpD = lpstrDestination;
|
|
|
|
do
|
|
{
|
|
if (*lpS != _T('\r'))
|
|
{
|
|
|
|
#ifdef UNICODE
|
|
*lpD++ = *lpS;
|
|
#else
|
|
::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpS, 1, lpD++, 1);
|
|
#endif // UNICODE
|
|
|
|
}
|
|
}
|
|
while (*lpS++);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
UnixToPCText(
|
|
OUT CString & strDestination,
|
|
IN LPCWSTR lpstrSource
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Expand LF to CR/LF (no allocation necessary) W String to T String.
|
|
|
|
Arguments:
|
|
|
|
CString & strDestination : Destination string
|
|
LPCWSTR lpstrSource : Source string
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE for failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
|
|
try
|
|
{
|
|
LPCWSTR lpS = lpstrSource;
|
|
//
|
|
// Since we're doubling every linefeed length, assume
|
|
// the worst possible expansion to start with.
|
|
//
|
|
int cch = (::lstrlenW(lpstrSource) + 1) * 2;
|
|
LPTSTR lpD = strDestination.GetBuffer(cch);
|
|
|
|
do
|
|
{
|
|
if (*lpS == L'\n')
|
|
{
|
|
*lpD++ = _T('\r');
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
*lpD++ = *lpS;
|
|
#else
|
|
::WideCharToMultiByte(CP_ACP, 0, lpS, 1, lpD++, 1, NULL, NULL);
|
|
#endif // UNICODE
|
|
|
|
}
|
|
while (*lpS++);
|
|
|
|
strDestination.ReleaseBuffer();
|
|
|
|
++fSuccess;
|
|
}
|
|
catch(CMemoryException * e)
|
|
{
|
|
TRACEEOLID("Exception in UnixToPCText");
|
|
e->ReportError();
|
|
e->Delete();
|
|
}
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
TextToText(
|
|
OUT LPWSTR & lpstrDestination,
|
|
IN const CString & strSource
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Straight copy with allocation. T String to W String.
|
|
|
|
Arguments:
|
|
|
|
LPWSTR & lpstrDestination : Destination string
|
|
const CString & strSource : Source string
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE for failure.
|
|
|
|
--*/
|
|
{
|
|
int cch = strSource.GetLength() + 1;
|
|
lpstrDestination = (LPWSTR)AllocMem(cch * sizeof(WCHAR));
|
|
if (lpstrDestination != NULL)
|
|
{
|
|
TWSTRCPY(lpstrDestination, strSource, cch);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
#ifndef UNICODE
|
|
|
|
|
|
|
|
#define WBUFF_SIZE 255
|
|
|
|
|
|
|
|
LPWSTR
|
|
ReferenceAsWideString(
|
|
IN LPCTSTR str
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reference a T string as a W string (non-unicode only).
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR str : Source string
|
|
|
|
Return Value:
|
|
|
|
Wide char pointer to wide string.
|
|
|
|
Notes:
|
|
|
|
This uses an internal wide char buffer, which will be overwritten
|
|
by subsequent calls to this function.
|
|
|
|
--*/
|
|
{
|
|
static WCHAR wchBuff[WBUFF_SIZE + 1];
|
|
|
|
::MultiByteToWideChar(
|
|
CP_ACP,
|
|
MB_PRECOMPOSED,
|
|
str,
|
|
-1,
|
|
wchBuff,
|
|
WBUFF_SIZE + 1
|
|
);
|
|
|
|
return wchBuff;
|
|
}
|
|
|
|
|
|
#endif !UNICODE
|
|
|
|
|
|
LPWSTR
|
|
AllocWideString(
|
|
IN LPCTSTR lpString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert the incoming string to an wide string, which is allocated
|
|
by this function
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lpString : Input wide string
|
|
|
|
Return Value:
|
|
|
|
Pointer to the allocated string
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Character counts are DBCS friendly
|
|
//
|
|
int cChars = lstrlen(lpString);
|
|
int nLength = (cChars+1) * sizeof(WCHAR);
|
|
LPWSTR lp = (LPWSTR)AllocMem(nLength);
|
|
if (lp)
|
|
{
|
|
#ifdef UNICODE
|
|
lstrcpy(lp, lpString);
|
|
#else
|
|
::MultiByteToWideChar(
|
|
CP_ACP, // code page
|
|
MB_PRECOMPOSED, // character-type options
|
|
lpString, // address of string to map
|
|
cChars, // number of characters in string
|
|
lp, // address of wide-character buffer
|
|
cChars+1 // size of buffer
|
|
);
|
|
#endif
|
|
}
|
|
|
|
return lp;
|
|
}
|
|
|
|
LPSTR
|
|
AllocAnsiString(
|
|
IN LPCTSTR lpString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert the wide string to an ansi (multi-byte) string, which is allocated
|
|
by this function
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lpString : Input wide string
|
|
|
|
Return Value:
|
|
|
|
Pointer to the allocated string
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Character counts are DBCS friendly
|
|
//
|
|
int cChars = lstrlen(lpString);
|
|
int nLength = (cChars * 2) + 1;
|
|
LPSTR lp = (LPSTR)AllocMem(nLength);
|
|
if (lp)
|
|
{
|
|
#ifdef UNICODE
|
|
::WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
lpString,
|
|
cChars + 1,
|
|
lp,
|
|
nLength,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#else
|
|
lstrcpy(lp, lpString);
|
|
#endif
|
|
}
|
|
|
|
return lp;
|
|
}
|
|
|
|
|
|
LPTSTR
|
|
AllocString(
|
|
IN LPCTSTR lpString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate and copy string
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lpString : Input string
|
|
|
|
Return Value:
|
|
|
|
Pointer to the allocated string
|
|
|
|
--*/
|
|
{
|
|
int nLength = lstrlen(lpString) + 1;
|
|
LPTSTR lp = (LPTSTR)AllocMem(nLength * sizeof(TCHAR));
|
|
if (lp)
|
|
{
|
|
lstrcpy(lp, lpString);
|
|
}
|
|
|
|
return lp;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
IsUNCName(
|
|
IN const CString & strDirPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if the given string path is a UNC path.
|
|
|
|
Arguments:
|
|
|
|
const CString & strDirPath : Directory path string
|
|
|
|
Return Value:
|
|
|
|
TRUE if the path is a UNC path, FALSE otherwise.
|
|
|
|
Notes:
|
|
|
|
Any string of the form \\foo\bar\whatever is considered a UNC path
|
|
|
|
--*/
|
|
{
|
|
if (strDirPath.GetLength() >= 5) // It must be at least as long as \\x\y,
|
|
{ //
|
|
LPCTSTR lp = strDirPath; //
|
|
if (*lp == _T('\\') // It must begin with \\,
|
|
&& *(lp + 1) == _T('\\') //
|
|
&& _tcschr(lp + 2, _T('\\')) // And have at least one more \ after that
|
|
)
|
|
{
|
|
//
|
|
// Yes, it's a UNC path
|
|
//
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No, it's not
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
IsFullyQualifiedPath(
|
|
IN const CString & strDirPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if the given string is a fully qualified path name
|
|
|
|
Arguments:
|
|
|
|
const CString & strDirPath : Directory path string
|
|
|
|
Return Value:
|
|
|
|
TRUE if the path is a fully qualified path name
|
|
|
|
|
|
--*/
|
|
{
|
|
return strDirPath.GetLength() >= 3
|
|
&& strDirPath[1] == _T(':')
|
|
&& strDirPath[2] == _T('\\');
|
|
}
|
|
|
|
|
|
|
|
LPCTSTR
|
|
MakeUNCPath(
|
|
IN OUT CString & strDir,
|
|
IN LPCTSTR lpszOwner,
|
|
IN LPCTSTR lpszDirectory
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert the given directory to a UNC path.
|
|
|
|
Arguments:
|
|
|
|
CString & strDir : UNC String.
|
|
LPCTSTR lpszOwner : Computer name
|
|
LPCTSTR lpszDirectory : Source string
|
|
|
|
Return Value:
|
|
|
|
Pointer to strDir
|
|
|
|
Notes:
|
|
|
|
The owner may or may not start with "\\". If it doesn't, the
|
|
backslashes are provided.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Try to make make a unc path out of the directory
|
|
//
|
|
ASSERT(lpszDirectory[1] == _T(':'));
|
|
|
|
strDir.Format(
|
|
_T("\\\\%s\\%c$\\%s"),
|
|
PURE_COMPUTER_NAME(lpszOwner),
|
|
lpszDirectory[0],
|
|
lpszDirectory + 3
|
|
);
|
|
|
|
return (LPCTSTR)strDir;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
IsURLName(
|
|
IN const CString & strDirPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if the given string path is an URL path.
|
|
|
|
Arguments:
|
|
|
|
const CString & strDirPath : Directory path string
|
|
|
|
Return Value:
|
|
|
|
TRUE if the path is an URL path, FALSE otherwise.
|
|
|
|
Notes:
|
|
|
|
Any string of the form protocol://whatever is considered an URL path
|
|
|
|
--*/
|
|
{
|
|
if (strDirPath.GetLength() >= 4) // It must be at least as long as x://
|
|
{ //
|
|
if (strDirPath.Find(_T("://")) > 0) // Must contain ://
|
|
{
|
|
//
|
|
// Yes, it's an URL path
|
|
//
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No, it's not
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
CStringFindNoCase(
|
|
IN const CString & strSrc,
|
|
IN LPCTSTR lpszSub
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This should be CString::FindNoCase(). Same as CString::Find(),
|
|
but case-insensitive.
|
|
|
|
Arguments:
|
|
|
|
const CString & strSrc : Source string
|
|
LPCTSTR lpszSub : String to look for.
|
|
|
|
Return Value:
|
|
|
|
The position of the substring, or -1 if not found.
|
|
|
|
--*/
|
|
{
|
|
LPCTSTR lp1 = strSrc;
|
|
LPCTSTR lp2, lp3;
|
|
int nPos = -1;
|
|
|
|
while (*lp1)
|
|
{
|
|
lp2 = lp1;
|
|
lp3 = lpszSub;
|
|
|
|
while(*lp2 && *lp3 && _totupper(*lp2) == _totupper(*lp3))
|
|
{
|
|
++lp2;
|
|
++lp3;
|
|
}
|
|
|
|
if (!*lp3)
|
|
{
|
|
//
|
|
// Found the substring
|
|
//
|
|
nPos = (int)(lp1 - (LPCTSTR)strSrc);
|
|
break;
|
|
}
|
|
|
|
++lp1;
|
|
}
|
|
|
|
return nPos;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
ReplaceStringInString(
|
|
OUT IN CString & strBuffer,
|
|
IN CString & strTarget,
|
|
IN CString & strReplacement,
|
|
IN BOOL fCaseSensitive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Replace the first occurrence of a string with a second string
|
|
inside a third string.
|
|
|
|
Arguments:
|
|
|
|
CString & strBuffer : Buffer in which to replace
|
|
CString & strTarget : String to look for
|
|
CString & strReplacement : String to replace it with
|
|
BOOL fCaseSensitive : TRUE for case sensitive replacement.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS for successful replacement.
|
|
ERROR_INVALID_PARAMETER if any string is empty,
|
|
ERROR_FILE_NOT_FOUND if the target string doesn't exist, or
|
|
another win32 error code indicating failure.
|
|
|
|
--*/
|
|
{
|
|
if (strBuffer.IsEmpty() || strTarget.IsEmpty() || strReplacement.IsEmpty())
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
DWORD err = ERROR_FILE_NOT_FOUND;
|
|
int nPos = fCaseSensitive
|
|
? strBuffer.Find(strTarget)
|
|
: CStringFindNoCase(strBuffer, strTarget);
|
|
|
|
if (nPos >= 0)
|
|
{
|
|
try
|
|
{
|
|
CString str(strBuffer.Left(nPos));
|
|
|
|
str += strReplacement;
|
|
str += strBuffer.Mid(nPos + strTarget.GetLength());
|
|
strBuffer = str;
|
|
|
|
err = ERROR_SUCCESS;
|
|
}
|
|
catch(CMemoryException * e)
|
|
{
|
|
e->Delete();
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
int
|
|
CountCharsToDoubleNull(
|
|
IN LPCTSTR lp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Count TCHARS up to and including the double NULL.
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lp : TCHAR Stream
|
|
|
|
Return Value:
|
|
|
|
Number of chars up to and including the double NULL
|
|
|
|
--*/
|
|
{
|
|
int cChars = 0;
|
|
|
|
for(;;)
|
|
{
|
|
++cChars;
|
|
if (lp[0] == _T('\0') && lp[1] == _T('\0'))
|
|
{
|
|
return ++cChars;
|
|
}
|
|
|
|
++lp;
|
|
}
|
|
}
|
|
|
|
int
|
|
CountWCharsToDoubleNull(
|
|
IN PWCHAR lp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Count TCHARS up to and including the double NULL.
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lp : TCHAR Stream
|
|
|
|
Return Value:
|
|
|
|
Number of chars up to and including the double NULL
|
|
|
|
--*/
|
|
{
|
|
int cChars = 0;
|
|
|
|
for(;;)
|
|
{
|
|
++cChars;
|
|
if (lp[0] == L'\0' && lp[1] == L'\0')
|
|
{
|
|
return ++cChars;
|
|
}
|
|
|
|
++lp;
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
ConvertDoubleNullListToStringList(
|
|
IN LPCTSTR lpstrSrc,
|
|
OUT CStringList & strlDest,
|
|
IN int cChars OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a double null terminate list of null terminated strings to a more
|
|
manageable CStringListEx
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lpstrSrc : Source list of strings
|
|
CStringList & strlDest : Destination string list.
|
|
int cChars : Number of characters in double NULL list. if
|
|
-1, autodetermine length
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the list was converted properly
|
|
ERROR_INVALID_PARAMETER if the list was empty
|
|
ERROR_NOT_ENOUGH_MEMORY if there was a mem exception
|
|
|
|
--*/
|
|
{
|
|
DWORD err = ERROR_SUCCESS;
|
|
|
|
if (lpstrSrc == NULL)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (cChars < 0)
|
|
{
|
|
//
|
|
// Calculate our own size. This might be off if multiple
|
|
// blank linkes (0) appear in the multi_sz, so the character
|
|
// size is definitely preferable
|
|
//
|
|
cChars = CountCharsToDoubleNull(lpstrSrc);
|
|
}
|
|
|
|
try
|
|
{
|
|
strlDest.RemoveAll();
|
|
|
|
if (cChars == 2 && *lpstrSrc == _T('\0'))
|
|
{
|
|
//
|
|
// Special case: MULTI_SZ containing only
|
|
// a double NULL are in fact blank entirely.
|
|
//
|
|
// N.B. IMHO this is a metabase bug -- RonaldM
|
|
//
|
|
--cChars;
|
|
}
|
|
|
|
//
|
|
// Grab strings until only the final NULL remains
|
|
//
|
|
while (cChars > 1)
|
|
{
|
|
CString strTmp = lpstrSrc;
|
|
strlDest.AddTail(strTmp);
|
|
lpstrSrc += (strTmp.GetLength() + 1);
|
|
cChars -= (strTmp.GetLength() + 1);
|
|
}
|
|
}
|
|
catch(CMemoryException * e)
|
|
{
|
|
TRACEEOLID("!!! exception building stringlist");
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
e->Delete();
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
DWORD
|
|
ConvertWDoubleNullListToStringList(
|
|
IN PWCHAR lpstrSrc,
|
|
OUT CStringList & strlDest,
|
|
IN int cChars OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a double null terminate list of null terminated strings to a more
|
|
manageable CStringListEx
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lpstrSrc : Source list of strings
|
|
CStringList & strlDest : Destination string list.
|
|
int cChars : Number of characters in double NULL list. if
|
|
-1, autodetermine length
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the list was converted properly
|
|
ERROR_INVALID_PARAMETER if the list was empty
|
|
ERROR_NOT_ENOUGH_MEMORY if there was a mem exception
|
|
|
|
--*/
|
|
{
|
|
DWORD err = ERROR_SUCCESS;
|
|
|
|
if (lpstrSrc == NULL)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (cChars < 0)
|
|
{
|
|
//
|
|
// Calculate our own size. This might be off if multiple
|
|
// blank linkes (0) appear in the multi_sz, so the character
|
|
// size is definitely preferable
|
|
//
|
|
cChars = CountWCharsToDoubleNull(lpstrSrc);
|
|
}
|
|
|
|
try
|
|
{
|
|
strlDest.RemoveAll();
|
|
|
|
if (cChars == 2 && *lpstrSrc == _T('\0'))
|
|
{
|
|
//
|
|
// Special case: MULTI_SZ containing only
|
|
// a double NULL are in fact blank entirely.
|
|
//
|
|
// N.B. IMHO this is a metabase bug -- RonaldM
|
|
//
|
|
--cChars;
|
|
}
|
|
|
|
//
|
|
// Grab strings until only the final NULL remains
|
|
//
|
|
while (cChars > 1)
|
|
{
|
|
CString strTmp = lpstrSrc;
|
|
strlDest.AddTail(strTmp);
|
|
lpstrSrc += (strTmp.GetLength() + 1);
|
|
cChars -= (strTmp.GetLength() + 1);
|
|
}
|
|
}
|
|
catch(CMemoryException * e)
|
|
{
|
|
TRACEEOLID("!!! exception building stringlist");
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
e->Delete();
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
ConvertStringListToWDoubleNullList(
|
|
IN CStringList & strlSrc,
|
|
OUT DWORD & cchDest,
|
|
OUT LPWSTR & lpstrDest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Flatten the string list into a WIDE double null terminated list
|
|
of null terminated strings.
|
|
|
|
Arguments:
|
|
|
|
CStringList & strlSrc : Source string list
|
|
DWORD & cchDest : Size in characters of the resultant array
|
|
(including terminating NULLs)
|
|
LPTSTR & lpstrDest : Allocated flat array.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the list was converted properly
|
|
ERROR_INVALID_PARAMETER if the list was empty
|
|
ERROR_NOT_ENOUGH_MEMORY if there was a mem exception
|
|
|
|
--*/
|
|
{
|
|
cchDest = 0;
|
|
lpstrDest = NULL;
|
|
BOOL fNullPad = FALSE;
|
|
|
|
//
|
|
// Compute total size in characters
|
|
//
|
|
POSITION pos;
|
|
for(pos = strlSrc.GetHeadPosition(); pos != NULL; /**/ )
|
|
{
|
|
CString & str = strlSrc.GetNext(pos);
|
|
|
|
TRACEEOLID(str);
|
|
|
|
cchDest += str.GetLength() + 1;
|
|
}
|
|
|
|
if (!cchDest)
|
|
{
|
|
//
|
|
// Special case: A totally empty MULTI_SZ
|
|
// in fact consists of 2 (final) NULLS, instead
|
|
// of 1 (final) NULL. This is required by the
|
|
// metabase, but should be a bug. See note
|
|
// at reversal function above.
|
|
//
|
|
++cchDest;
|
|
fNullPad = TRUE;
|
|
}
|
|
|
|
//
|
|
// Remember final NULL
|
|
//
|
|
cchDest += 1;
|
|
|
|
lpstrDest = AllocWString(cchDest);
|
|
if (lpstrDest == NULL)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
LPWSTR pch = lpstrDest;
|
|
LPWSTR pwstr;
|
|
|
|
for(pos = strlSrc.GetHeadPosition(); pos != NULL; /**/ )
|
|
{
|
|
CString & str = strlSrc.GetNext(pos);
|
|
|
|
|
|
// if we are not already UNICODE, we need to convert
|
|
#ifndef UNICODE
|
|
pwstr = AllocWideString( (LPCTSTR)str );
|
|
#else
|
|
pwstr = (LPWSTR)(LPCTSTR)str;
|
|
#endif
|
|
if (pwstr == NULL)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
wcscpy(pch, pwstr);
|
|
pch += str.GetLength();
|
|
*pch++ = L'\0';
|
|
|
|
#ifndef UNICODE
|
|
// clean up the temporary wide string
|
|
FreeMem( pwstr );
|
|
#endif
|
|
}
|
|
|
|
*pch++ = L'\0';
|
|
|
|
if (fNullPad)
|
|
{
|
|
*pch++ = L'\0';
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ConvertStringListToDoubleNullList(
|
|
IN CStringList & strlSrc,
|
|
OUT DWORD & cchDest,
|
|
OUT LPTSTR & lpstrDest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Flatten the string list into a double null terminated list
|
|
of null terminated strings.
|
|
|
|
Arguments:
|
|
|
|
CStringList & strlSrc : Source string list
|
|
DWORD & cchDest : Size in characters of the resultant array
|
|
(including terminating NULLs)
|
|
LPTSTR & lpstrDest : Allocated flat array.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the list was converted properly
|
|
ERROR_INVALID_PARAMETER if the list was empty
|
|
ERROR_NOT_ENOUGH_MEMORY if there was a mem exception
|
|
|
|
--*/
|
|
{
|
|
cchDest = 0;
|
|
lpstrDest = NULL;
|
|
BOOL fNullPad = FALSE;
|
|
|
|
//
|
|
// Compute total size in characters
|
|
//
|
|
POSITION pos;
|
|
for(pos = strlSrc.GetHeadPosition(); pos != NULL; /**/ )
|
|
{
|
|
CString & str = strlSrc.GetNext(pos);
|
|
|
|
TRACEEOLID(str);
|
|
|
|
cchDest += str.GetLength() + 1;
|
|
}
|
|
|
|
if (!cchDest)
|
|
{
|
|
//
|
|
// Special case: A totally empty MULTI_SZ
|
|
// in fact consists of 2 (final) NULLS, instead
|
|
// of 1 (final) NULL. This is required by the
|
|
// metabase, but should be a bug. See note
|
|
// at reversal function above.
|
|
//
|
|
++cchDest;
|
|
fNullPad = TRUE;
|
|
}
|
|
|
|
//
|
|
// Remember final NULL
|
|
//
|
|
cchDest += 1;
|
|
|
|
lpstrDest = AllocTString(cchDest);
|
|
if (lpstrDest == NULL)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
LPTSTR pch = lpstrDest;
|
|
|
|
for(pos = strlSrc.GetHeadPosition(); pos != NULL; /**/ )
|
|
{
|
|
CString & str = strlSrc.GetNext(pos);
|
|
|
|
lstrcpy(pch, (LPCTSTR)str);
|
|
pch += str.GetLength();
|
|
*pch++ = _T('\0');
|
|
}
|
|
|
|
*pch++ = _T('\0');
|
|
|
|
if (fNullPad)
|
|
{
|
|
*pch++ = _T('\0');
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
int
|
|
ConvertSepLineToStringList(
|
|
IN LPCTSTR lpstrIn,
|
|
OUT CStringList & strlOut,
|
|
IN LPCTSTR lpstrSep
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a line containing multiple strings separated by
|
|
a given character to a CStringListEx
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lpstrIn : Input line
|
|
CStringListEx & strlOut : Output stringlist
|
|
LPCTSTR lpstrSep : List of separators
|
|
|
|
Return Value:
|
|
|
|
The number of items added
|
|
|
|
--*/
|
|
{
|
|
int cItems = 0;
|
|
strlOut.RemoveAll();
|
|
|
|
try
|
|
{
|
|
CString strSrc(lpstrIn);
|
|
LPTSTR lp = strSrc.GetBuffer(0);
|
|
lp = StringTok(lp, lpstrSep);
|
|
|
|
while (lp)
|
|
{
|
|
CString str(lp);
|
|
|
|
strlOut.AddTail(str);
|
|
lp = StringTok(NULL, lpstrSep);
|
|
++cItems;
|
|
}
|
|
}
|
|
catch(CMemoryException * e)
|
|
{
|
|
TRACEEOLID("Exception converting CSV list to stringlist");
|
|
e->ReportError();
|
|
e->Delete();
|
|
}
|
|
|
|
return cItems;
|
|
}
|
|
|
|
|
|
|
|
|
|
LPCTSTR
|
|
ConvertStringListToSepLine(
|
|
IN CStringList & strlIn,
|
|
OUT CString & strOut,
|
|
IN LPCTSTR lpstrSep
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert stringlist into a single CString, each entry seperated by the given
|
|
seperator string.
|
|
|
|
Arguments:
|
|
|
|
CStringListEx & strlIn : Input stringlist
|
|
CString & strOut : Output string
|
|
LPCTSTR lpstrSep : Seperator string
|
|
|
|
Return Value:
|
|
|
|
Pointer to the output string.
|
|
|
|
--*/
|
|
{
|
|
__try
|
|
{
|
|
strOut.Empty();
|
|
POSITION pos = strlIn.GetHeadPosition();
|
|
BOOL fAddSep = FALSE;
|
|
|
|
while(pos)
|
|
{
|
|
CString & str = strlIn.GetNext(pos);
|
|
|
|
if ( fAddSep )
|
|
{
|
|
strOut += lpstrSep;
|
|
}
|
|
|
|
if (str)
|
|
{
|
|
strOut += str;
|
|
}
|
|
fAddSep = TRUE;
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
iisDebugOut((LOG_TYPE_WARN, _T("nException Caught in ConvertStringListToSepLine()=0x%x.."),GetExceptionCode()));
|
|
}
|
|
|
|
return strOut;
|
|
}
|
|
|
|
|
|
|
|
LPTSTR
|
|
StringTok(
|
|
IN LPTSTR string,
|
|
IN LPCTSTR control
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
strtok replacement function.
|
|
|
|
Arguments:
|
|
|
|
LPTSTR string : string, see strtok
|
|
LPCTSTR control : seperators, see strtok
|
|
|
|
Return Value:
|
|
|
|
Pointer to string or NULL, see strtok.
|
|
|
|
Notes:
|
|
|
|
This function is NOT thread-safe.
|
|
|
|
--*/
|
|
{
|
|
LPTSTR str;
|
|
LPCTSTR ctrl = control;
|
|
|
|
TCHAR map[32];
|
|
|
|
static LPTSTR nextoken;
|
|
|
|
//
|
|
// Clear control map
|
|
//
|
|
ZeroMemory(map, sizeof(map));
|
|
|
|
//
|
|
// Set bits in delimiter table
|
|
//
|
|
do
|
|
{
|
|
map[*ctrl >> 3] |= (1 << (*ctrl & 7));
|
|
}
|
|
while (*ctrl++);
|
|
|
|
//
|
|
// Initialize str. If string is NULL, set str to the saved
|
|
// pointer (i.e., continue breaking tokens out of the string
|
|
// from the last StringTok call)
|
|
//
|
|
if (string != NULL)
|
|
{
|
|
str = string;
|
|
}
|
|
else
|
|
{
|
|
str = nextoken;
|
|
}
|
|
|
|
//
|
|
// Find beginning of token (skip over leading delimiters). Note that
|
|
// there is no token iff this loop sets str to point to the terminal
|
|
// null (*str == '\0').
|
|
//
|
|
#ifdef UNICODE
|
|
//
|
|
// To avoid index overflow, check non-ASCII characters (UNICODE)
|
|
//
|
|
while (!(*str & 0xff00) &&
|
|
(map[*str >> 3] & (1 << (*str & 7))) && *str)
|
|
#else
|
|
while ((map[*str >> 3] & (1 << (*str & 7))) && *str)
|
|
#endif // UNICODE
|
|
{
|
|
++str;
|
|
}
|
|
|
|
string = str;
|
|
|
|
//
|
|
// Find the end of the token. If it is not the end of the string,
|
|
// put a null there.
|
|
//
|
|
for ( /**/ ; *str ; str++ )
|
|
{
|
|
#ifdef UNICODE
|
|
//
|
|
// To avoid index overflow, check non-ASCII characters (UNICODE)
|
|
//
|
|
if ( !(*str & 0xff00) &&
|
|
map[*str >> 3] & (1 << (*str & 7)) )
|
|
#else
|
|
//
|
|
// Skip DBCS character (ANSI)
|
|
//
|
|
if (IsDBCSLeadByte(*str) && *(str + 1))
|
|
{
|
|
++str;
|
|
}
|
|
else if ( map[*str >> 3] & (1 << (*str & 7)) )
|
|
#endif // UNICODE
|
|
{
|
|
*str++ = '\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update nextoken structure
|
|
//
|
|
nextoken = str;
|
|
|
|
//
|
|
// Determine if a token has been found.
|
|
//
|
|
return string != str ? string : NULL;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CStringListEx::operator ==(
|
|
IN const CStringList & strl
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Compare against CStringList. In order for two CStringLists to match,
|
|
they must match in every element, which must be in the same order.
|
|
|
|
Arguments:
|
|
|
|
CStringList strl : String list to compare against.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the two string lists are identical
|
|
|
|
--*/
|
|
{
|
|
if (strl.GetCount() != GetCount())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
POSITION posa = strl.GetHeadPosition();
|
|
POSITION posb = GetHeadPosition();
|
|
|
|
while (posa)
|
|
{
|
|
ASSERT(posa);
|
|
ASSERT(posb);
|
|
|
|
CString & strA = strl.GetNext(posa);
|
|
CString & strB = GetNext(posb);
|
|
|
|
if (strA != strB)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
void
|
|
CopyCList(
|
|
OUT CStringList & strlDest,
|
|
IN CStringList & strlSrc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Assign one stringlist to another. This is a simple member by member
|
|
copy.
|
|
|
|
Arguments:
|
|
|
|
CStringList & strlDest : Destination stringlist
|
|
CStringList & strlSrc : Source stringlist
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--/
|
|
{
|
|
strlDest.RemoveAll();
|
|
POSITION pos = strlSrc.GetHeadPosition();
|
|
while(pos)
|
|
{
|
|
CString & str = strlSrc.GetNext(pos);
|
|
strlDest.AddTail(str);
|
|
}
|
|
}
|
|
|
|
|
|
*/
|
|
|
|
CStringListEx &
|
|
CStringListEx::operator =(
|
|
IN const CStringList & strl
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Assignment operator
|
|
|
|
Arguments:
|
|
|
|
const CStringList & strl : Source stringlist
|
|
|
|
Return Value:
|
|
|
|
Reference to this
|
|
|
|
--*/
|
|
{
|
|
RemoveAll();
|
|
POSITION pos = strl.GetHeadPosition();
|
|
while(pos)
|
|
{
|
|
CString & str = strl.GetNext(pos);
|
|
AddTail(str);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
SplitUserNameAndDomain(
|
|
IN OUT CString & strUserName,
|
|
IN CString & strDomainName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Split the user name and domain from the given
|
|
username, which is in the format "domain\user".
|
|
|
|
Return TRUE if the user name contained a domain
|
|
FALSE if it did not
|
|
|
|
Arguments:
|
|
|
|
CString & strUserName : User name which may contain a domain name
|
|
CString & strDomainName : Output domain name ("." if local)
|
|
|
|
Return Value:
|
|
|
|
TRUE if a domain is split off
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Assume local
|
|
//
|
|
strDomainName = _T(".");
|
|
int nSlash = strUserName.Find(_T("\\"));
|
|
if (nSlash >= 0)
|
|
{
|
|
strDomainName = strUserName.Left(nSlash);
|
|
strUserName = strUserName.Mid(nSlash + 1);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const LPCTSTR g_cszMonths[] =
|
|
{
|
|
_T("Jan"),
|
|
_T("Feb"),
|
|
_T("Mar"),
|
|
_T("Apr"),
|
|
_T("May"),
|
|
_T("Jun"),
|
|
_T("Jul"),
|
|
_T("Aug"),
|
|
_T("Sep"),
|
|
_T("Oct"),
|
|
_T("Nov"),
|
|
_T("Dec"),
|
|
};
|
|
|
|
|
|
|
|
const LPCTSTR g_cszWeekDays[] =
|
|
{
|
|
_T("Sun"),
|
|
_T("Mon"),
|
|
_T("Tue"),
|
|
_T("Wed"),
|
|
_T("Thu"),
|
|
_T("Fri"),
|
|
_T("Sat"),
|
|
};
|
|
|
|
|
|
|
|
inline BOOL SkipTillDigit(LPCTSTR & lp)
|
|
{
|
|
while (lp && *lp && !_istdigit(*lp)) ++lp;
|
|
|
|
return lp != NULL;
|
|
}
|
|
|
|
|
|
|
|
inline BOOL SkipPastDigits(LPCTSTR & lp)
|
|
{
|
|
while (lp && *lp && _istdigit(*lp)) ++lp;
|
|
|
|
return lp != NULL;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FetchIntField(
|
|
LPCTSTR & lp,
|
|
int & n
|
|
)
|
|
{
|
|
if (SkipTillDigit(lp))
|
|
{
|
|
n = _ttoi(lp);
|
|
if (n < 0)
|
|
{
|
|
ASSERT(FALSE && "Bogus string->int");
|
|
return FALSE;
|
|
}
|
|
|
|
return SkipPastDigits(lp);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
MatchString(
|
|
LPCTSTR lpTarget,
|
|
const LPCTSTR * rglp,
|
|
int cElements,
|
|
int & idx
|
|
)
|
|
{
|
|
for (idx = 0; idx < cElements; ++idx)
|
|
{
|
|
if (!_tcsnicmp(lpTarget, rglp[idx], _tcslen(rglp[idx])))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
static g_dwCurrentTimeZone = TIME_ZONE_ID_INVALID;
|
|
static TIME_ZONE_INFORMATION g_tzInfo;
|
|
|
|
|
|
//
|
|
// International numeric strings
|
|
//
|
|
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
|
|
|
|
|
|
//
|
|
// Initialize library
|
|
//
|
|
BOOL
|
|
InitIntlSettings()
|
|
{
|
|
#ifdef _MT
|
|
INITIALIZE_CRITICAL_SECTION(&_csSect);
|
|
#endif // _MT
|
|
|
|
return CINumber::Allocate();
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// De-initialize library
|
|
//
|
|
void
|
|
TerminateIntlSettings()
|
|
{
|
|
CINumber::DeAllocate();
|
|
|
|
#ifdef _MT
|
|
DeleteCriticalSection(&_csSect);
|
|
#endif // _MT
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Static Member Initialization
|
|
//
|
|
CString * CINumber::s_pstrThousandSeperator = NULL;
|
|
CString * CINumber::s_pstrDecimalPoint = NULL;
|
|
CString * CINumber::s_pstrBadNumber = NULL;
|
|
CString * CINumber::s_pstrCurrency = NULL;
|
|
CString * CINumber::s_pstr = NULL;
|
|
BOOL CINumber::s_fAllocated = FALSE;
|
|
BOOL CINumber::s_fCurrencyPrefix = TRUE;
|
|
BOOL CINumber::s_fInitialized = FALSE;
|
|
|
|
|
|
|
|
#ifdef _DOS
|
|
|
|
|
|
|
|
BOOL
|
|
_dos_getintlsettings(
|
|
OUT INTLFORMAT * pStruct
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the international settings on a DOS box
|
|
|
|
Parameters:
|
|
|
|
INTLFORMAT * pStruct : Structure to be filled in.
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE for failure
|
|
|
|
--*/
|
|
{
|
|
TRACEEOLID("[_dos_getintlsetting]");
|
|
union _REGS inregs, outregs;
|
|
struct _SREGS segregs;
|
|
|
|
inregs.h.ah = 0x38; // Intl call
|
|
inregs.h.al = 0x00; // Current country code
|
|
inregs.x.bx = 0x00; // Current country code
|
|
|
|
segregs.ds = _FP_SEG(pStruct);
|
|
inregs.x.dx = _FP_OFF(pStruct);
|
|
|
|
int nError = _intdosx(&inregs, &outregs, &segregs);
|
|
|
|
return outregs.x.cflag == 0;
|
|
}
|
|
|
|
#endif // _DOS
|
|
|
|
|
|
|
|
/* protected */
|
|
CINumber::CINumber()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if (!CINumber::s_fInitialized)
|
|
{
|
|
CINumber::Initialize();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
CINumber::~CINumber()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
}
|
|
|
|
|
|
|
|
/* protected */
|
|
/* static */
|
|
BOOL
|
|
CINumber::Allocate()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate with US settings
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE if allocation was successfull, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
RaiseThreadProtection();
|
|
|
|
ASSERT(!IsAllocated());
|
|
if (!IsAllocated())
|
|
{
|
|
try
|
|
{
|
|
CINumber::s_pstrThousandSeperator = new CString(_T(","));
|
|
CINumber::s_pstrDecimalPoint = new CString(_T("."));
|
|
CINumber::s_pstrBadNumber = new CString(_T("--"));
|
|
CINumber::s_pstrCurrency = new CString(_T("$ "));
|
|
CINumber::s_pstr = new CString;
|
|
s_fAllocated = TRUE;
|
|
}
|
|
catch(CMemoryException * e)
|
|
{
|
|
TRACEEOLID("Initialization Failed");
|
|
e->ReportError();
|
|
e->Delete();
|
|
}
|
|
}
|
|
|
|
LowerThreadProtection();
|
|
|
|
return IsAllocated();
|
|
}
|
|
|
|
|
|
|
|
/* protected */
|
|
/* static */
|
|
void
|
|
CINumber::DeAllocate()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clean up allocations
|
|
|
|
Arguments:
|
|
|
|
N/A
|
|
|
|
Return Value:
|
|
|
|
N/A
|
|
|
|
--*/
|
|
{
|
|
RaiseThreadProtection();
|
|
|
|
ASSERT(IsAllocated());
|
|
if (IsAllocated())
|
|
{
|
|
MAKE_NULL(CINumber::s_pstrThousandSeperator);
|
|
MAKE_NULL(CINumber::s_pstrDecimalPoint);
|
|
MAKE_NULL(CINumber::s_pstrBadNumber);
|
|
MAKE_NULL(CINumber::s_pstrCurrency);
|
|
MAKE_NULL(CINumber::s_pstr);
|
|
}
|
|
|
|
LowerThreadProtection();
|
|
|
|
s_fAllocated = FALSE;
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
/* protected */
|
|
BOOL
|
|
CINumber::Initialize(
|
|
IN BOOL fUserSetting /* TRUE */
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize all the international settings, such as thousand
|
|
seperators and decimal points
|
|
|
|
Parameters:
|
|
|
|
BOOL fUserSetting If TRUE, use current user settings,
|
|
if FALSE use system settings.
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE for failure
|
|
|
|
Notes:
|
|
|
|
Note that this function only needs to be explicitly called
|
|
when the country settings have changed, or when system
|
|
settings are desired (user is default)
|
|
|
|
--*/
|
|
{
|
|
#define MAXLEN 6
|
|
|
|
int cErrors = 0;
|
|
|
|
TRACEEOLID("Getting locale-dependend information");
|
|
|
|
ASSERT(IsAllocated());
|
|
if (!IsAllocated())
|
|
{
|
|
Allocate();
|
|
}
|
|
|
|
RaiseThreadProtection();
|
|
|
|
try
|
|
{
|
|
|
|
#if defined(_MAC)
|
|
|
|
TRACEEOLID("Couldn't get international settings from system");
|
|
|
|
#elif defined(_WIN32)
|
|
|
|
LCID lcid = fUserSetting
|
|
? ::GetUserDefaultLCID()
|
|
: GetSystemDefaultLCID();
|
|
|
|
LCTYPE lctype = fUserSetting ? 0 : LOCALE_NOUSEROVERRIDE;
|
|
|
|
//
|
|
// Get Decimal Point
|
|
//
|
|
if (!::GetLocaleInfo(
|
|
lcid,
|
|
LOCALE_SDECIMAL | lctype,
|
|
CINumber::s_pstrDecimalPoint->GetBuffer(MAXLEN),
|
|
MAXLEN
|
|
))
|
|
{
|
|
TRACEEOLID("Unable to get intl decimal point");
|
|
++cErrors;
|
|
}
|
|
|
|
CINumber::s_pstrDecimalPoint->ReleaseBuffer();
|
|
|
|
//
|
|
// Get Thousand Seperator
|
|
//
|
|
if (!::GetLocaleInfo(
|
|
lcid, LOCALE_STHOUSAND | lctype,
|
|
CINumber::s_pstrThousandSeperator->GetBuffer(MAXLEN),
|
|
MAXLEN
|
|
))
|
|
{
|
|
TRACEEOLID("Unable to get 1000 seperator");
|
|
++cErrors;
|
|
}
|
|
|
|
CINumber::s_pstrThousandSeperator->ReleaseBuffer();
|
|
|
|
#ifndef _UNICODE
|
|
|
|
//
|
|
// Some countries have a space as a 1000 seperator,
|
|
// but for some reason, this is ansi 160, which
|
|
// shows up as a space fine on windows apps,
|
|
// looks like garbage on console apps.
|
|
//
|
|
if ((*CINumber::s_pstrThousandSeperator)[0] == CHAR(160))
|
|
{
|
|
CINumber::s_pstrThousandSeperator->SetAt(0, ' ');
|
|
TRACEEOLID("Space 1000 seperator substituted");
|
|
}
|
|
|
|
#endif // _UNICODE
|
|
|
|
//
|
|
// Get currency symbol
|
|
//
|
|
if (!::GetLocaleInfo(
|
|
lcid,
|
|
LOCALE_SCURRENCY | lctype,
|
|
CINumber::s_pstrCurrency->GetBuffer(MAXLEN),
|
|
MAXLEN
|
|
))
|
|
{
|
|
TRACEEOLID("Unable to get currency symbol");
|
|
++cErrors;
|
|
}
|
|
|
|
CINumber::s_pstrCurrency->ReleaseBuffer();
|
|
|
|
#elif defined(_WIN16)
|
|
|
|
//
|
|
// Get Decimal Point
|
|
//
|
|
::GetProfileString(
|
|
"Intl",
|
|
"sDecimal",
|
|
".",
|
|
CINumber::s_pstrDecimalPoint->GetBuffer(MAXLEN),
|
|
MAXLEN
|
|
);
|
|
CINumber::s_pstrDecimalPoint->ReleaseBuffer();
|
|
|
|
//
|
|
// Get 1000 seperator
|
|
//
|
|
::GetProfileString(
|
|
"Intl",
|
|
"sThousand",
|
|
",",
|
|
CINumber::s_pstrThousandSeperator->GetBuffer(MAXLEN),
|
|
MAXLEN
|
|
);
|
|
CINumber::s_pstrThousandSeperator->ReleaseBuffer();
|
|
|
|
//
|
|
// Get currency symbol
|
|
//
|
|
::GetProfileString(
|
|
"Intl",
|
|
"sCurrency",
|
|
",",
|
|
CINumber::s_pstrCurrency->GetBuffer(MAXLEN),
|
|
MAXLEN
|
|
);
|
|
CINumber::s_pstrCurrency->ReleaseBuffer();
|
|
|
|
#elif defined(_DOS)
|
|
|
|
INTLFORMAT fm;
|
|
|
|
if (_dos_getintlsettings(&fm))
|
|
{
|
|
//
|
|
// Get Decimal Point
|
|
//
|
|
*CINumber::s_pstrDecimalPoint = fm.szDecimalPoint;
|
|
|
|
//
|
|
// Get 1000 seperator
|
|
//
|
|
*CINumber::s_pstrThousandSeperator = fm.szThousandSep;
|
|
|
|
//
|
|
// Get Currency Symbol
|
|
//
|
|
*CINumber::s_pstrCurrency = fm.szCurrencySymbol;
|
|
}
|
|
else
|
|
{
|
|
TRACEEOLID("Unable to get intl settings");
|
|
++cErrors;
|
|
}
|
|
|
|
#endif // _WIN32 etc
|
|
|
|
}
|
|
|
|
catch(CMemoryException * e)
|
|
{
|
|
TRACEEOLID("!!!exception in getting intl settings:");
|
|
e->ReportError();
|
|
e->Delete();
|
|
++cErrors;
|
|
}
|
|
|
|
TRACEEOLID("Thousand Seperator . . . . . : " << *CINumber::s_pstrThousandSeperator);
|
|
TRACEEOLID("Decimal Point . . . . . . . : " << *CINumber::s_pstrDecimalPoint);
|
|
TRACEEOLID("Currency Symbol. . . . . . . : " << *CINumber::s_pstrCurrency);
|
|
TRACEEOLID("Bad number . . . . . . . . . : " << *CINumber::s_pstrBadNumber);
|
|
TRACEEOLID("Currency Prefix. . . . . . . : " << CINumber::s_fCurrencyPrefix);
|
|
|
|
CINumber::s_fInitialized = TRUE;
|
|
|
|
LowerThreadProtection();
|
|
|
|
return cErrors == 0;
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
double
|
|
CINumber::BuildFloat(
|
|
IN const LONG lInteger,
|
|
IN const LONG lFraction
|
|
)
|
|
/*++
|
|
|
|
Return Value:
|
|
|
|
Combine integer and fraction to form float
|
|
|
|
Parameters:
|
|
|
|
const LONG lInteger Integer portion
|
|
const LONG lFraction Fractional portion
|
|
|
|
Return Value:
|
|
|
|
float value
|
|
|
|
--*/
|
|
{
|
|
double flValue = 0.0;
|
|
|
|
//
|
|
// Negative fractions?
|
|
//
|
|
ASSERT(lFraction >= 0);
|
|
|
|
if (lFraction >= 0)
|
|
{
|
|
flValue = (double)lFraction;
|
|
while (flValue >= 1.0)
|
|
{
|
|
flValue /= 10.0;
|
|
}
|
|
|
|
//
|
|
// Re-add (or subtract if the original number
|
|
// was negative) the fractional part
|
|
//
|
|
if (lInteger > 0L)
|
|
{
|
|
flValue += (double)lInteger;
|
|
}
|
|
else
|
|
{
|
|
flValue -= (double)lInteger;
|
|
flValue = -flValue;
|
|
}
|
|
}
|
|
|
|
return flValue;
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
LPCTSTR
|
|
CINumber::ConvertLongToString(
|
|
IN const LONG lSrc,
|
|
OUT CString & str
|
|
)
|
|
/*++
|
|
|
|
CINumber::ConvertLongToString
|
|
|
|
Purpose:
|
|
|
|
Convert long number to string with 1000 seperators
|
|
|
|
Parameters:
|
|
|
|
const LONG lSrc Source number
|
|
CString & str String to write to
|
|
|
|
Return Value:
|
|
|
|
Pointer to converted string
|
|
|
|
--*/
|
|
{
|
|
LPTSTR lpOutString = str.GetBuffer(16);
|
|
|
|
//
|
|
// Forget about the negative sign for now.
|
|
//
|
|
LONG lNum = (lSrc >= 0L) ? lSrc : -lSrc;
|
|
int outstrlen = 0;
|
|
do
|
|
{
|
|
lpOutString[outstrlen++] = _T('0') + (TCHAR)(lNum % 10L);
|
|
lNum /= 10L;
|
|
|
|
//
|
|
// if more digits left and we're on a 1000 boundary (printed 3 digits,
|
|
// or 3 digits + n*(3 digits + 1 comma), then print a 1000 separator.
|
|
// Note: will only work if the 1000 seperator is 1 character.
|
|
//
|
|
ASSERT(CINumber::s_pstrThousandSeperator->GetLength() == 1);
|
|
if (lNum != 0L && (outstrlen == 3 || outstrlen == 7 || outstrlen == 11))
|
|
{
|
|
lstrcpy(lpOutString + outstrlen, *CINumber::s_pstrThousandSeperator);
|
|
outstrlen += CINumber::s_pstrThousandSeperator->GetLength();
|
|
}
|
|
|
|
}
|
|
while (lNum > 0L);
|
|
|
|
//
|
|
// Add a negative sign if necessary.
|
|
//
|
|
if (lSrc < 0L)
|
|
{
|
|
lpOutString[outstrlen++] = _T('-');
|
|
}
|
|
|
|
str.ReleaseBuffer(outstrlen);
|
|
str.MakeReverse();
|
|
|
|
return (LPCTSTR)str;
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
LPCTSTR
|
|
CINumber::ConvertFloatToString(
|
|
IN const double flSrc,
|
|
IN int nPrecision,
|
|
OUT CString & str
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert floating point number to string represenation
|
|
|
|
Parameters:
|
|
|
|
const double flSrc Source floating point number
|
|
int nPrecision Number of decimal points
|
|
CString & str String to convert to
|
|
|
|
Return Value:
|
|
|
|
Pointer to converted string.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Forget about the negative sign for now,
|
|
// and the fractional portion.
|
|
//
|
|
TCHAR szFraction[256];
|
|
LPCTSTR lpFraction = NULL;
|
|
|
|
::_stprintf(szFraction, _T("%.*f"), nPrecision, flSrc);
|
|
lpFraction = ::_tcschr(szFraction, _T('.') );
|
|
ASSERT(lpFraction != NULL);
|
|
++lpFraction;
|
|
|
|
CINumber::ConvertLongToString((LONG)flSrc, str);
|
|
|
|
str += *CINumber::s_pstrDecimalPoint + lpFraction;
|
|
|
|
return (LPCTSTR)str;
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
BOOL
|
|
CINumber::ConvertStringToLong(
|
|
IN LPCTSTR lpsrc,
|
|
OUT LONG & lValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert string to long integer. 1000 Seperators will be treated
|
|
correctly.
|
|
|
|
Parameters:
|
|
|
|
LPCTSTR lpsrc Source string
|
|
LONG & lValue Value to convert to. Will be 0 in case of error
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE for failure.
|
|
|
|
--*/
|
|
{
|
|
CString strNumber(lpsrc);
|
|
LONG lBase = 1L;
|
|
BOOL fNegative = FALSE;
|
|
|
|
lValue = 0L;
|
|
|
|
//
|
|
// Empty strings are invalid
|
|
//
|
|
if (strNumber.IsEmpty())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check for negative sign (at the end only)
|
|
//
|
|
if (strNumber[0] == _T('-'))
|
|
{
|
|
fNegative = TRUE;
|
|
}
|
|
|
|
strNumber.MakeReverse();
|
|
|
|
//
|
|
// Strip negative sign
|
|
//
|
|
if (fNegative)
|
|
{
|
|
strNumber.ReleaseBuffer(strNumber.GetLength()-1);
|
|
}
|
|
|
|
//
|
|
// Make sure the 1000 seperator is only 1 char. See note below
|
|
//
|
|
ASSERT(CINumber::s_pstrThousandSeperator->GetLength() == 1);
|
|
for (int i = 0; i < strNumber.GetLength(); ++i)
|
|
{
|
|
if ((strNumber[i] >= _T('0')) && (strNumber[i] <= _T('9')))
|
|
{
|
|
LONG lDigit = (LONG)(strNumber[i] - _T('0'));
|
|
if (lDigit != 0L)
|
|
{
|
|
LONG lOldValue = lValue;
|
|
LONG lDelta = (lDigit * lBase);
|
|
if (lDelta / lDigit != lBase)
|
|
{
|
|
TRACEEOLID("Overflow!");
|
|
lValue = 0x7fffffff;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
lValue += lDelta;
|
|
if (lValue - lDelta != lOldValue)
|
|
{
|
|
TRACEEOLID("Overflow!");
|
|
lValue = 0x7fffffff;
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
lBase *= 10L;
|
|
}
|
|
//
|
|
// It's not a digit, maybe a thousand seperator?
|
|
// CAVEAT: If a thousand seperator of more than
|
|
// one character is used, this won't work.
|
|
//
|
|
else if ((strNumber[i] != (*CINumber::s_pstrThousandSeperator)[0])
|
|
|| (i != 3) && (i != 7) && (i != 11))
|
|
{
|
|
//
|
|
// This is just invalid, since it is not a thousand
|
|
// seperator in the proper location, nor a negative
|
|
// sign.
|
|
//
|
|
TRACEEOLID("Invalid character " << (BYTE)strNumber[i] << " encountered");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (fNegative)
|
|
{
|
|
lValue = -lValue;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
BOOL
|
|
CINumber::ConvertStringToFloat(
|
|
IN LPCTSTR lpsrc,
|
|
OUT double & flValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert fully decorated floating point string to double
|
|
|
|
Parameters:
|
|
|
|
LPCTSTR lpsrc Source string
|
|
double & flValue float value generated from string
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE for failure
|
|
|
|
--*/
|
|
{
|
|
CString strNumber(lpsrc);
|
|
|
|
//
|
|
// This only works if the decimal point is a
|
|
// single character
|
|
//
|
|
ASSERT(CINumber::s_pstrDecimalPoint->GetLength() == 1);
|
|
|
|
//
|
|
// Strip off the > 0 part
|
|
//
|
|
LONG lFraction = 0;
|
|
|
|
int nPoint = strNumber.ReverseFind((*CINumber::s_pstrDecimalPoint)[0]);
|
|
|
|
if (nPoint >= 0)
|
|
{
|
|
//
|
|
// Convert fractional part
|
|
//
|
|
LPCTSTR lpszFraction = (LPCTSTR)strNumber + ++nPoint;
|
|
lFraction = ::_ttol(lpszFraction);
|
|
strNumber.ReleaseBuffer(--nPoint);
|
|
}
|
|
|
|
//
|
|
// Convert integer part
|
|
//
|
|
LONG lInteger;
|
|
if (ConvertStringToLong(strNumber, lInteger))
|
|
{
|
|
flValue = CINumber::BuildFloat(lInteger, lFraction);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
CILong::CILong()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor without arguments
|
|
|
|
Parameters:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
N/A
|
|
|
|
--*/
|
|
: m_lValue(0L)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
CILong::CILong(
|
|
IN LONG lValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor taking LONG argument
|
|
|
|
Parameters:
|
|
|
|
LONG lValue Value to be set
|
|
|
|
Return Value:
|
|
|
|
N/A
|
|
|
|
--*/
|
|
: m_lValue(lValue)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
CILong::CILong(
|
|
IN LPCTSTR lpszValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor taking string argument
|
|
|
|
Parameters:
|
|
|
|
LPCTSTR lpszValue String number
|
|
|
|
Return Value:
|
|
|
|
N/A
|
|
|
|
--*/
|
|
{
|
|
ConvertStringToLong(lpszValue, m_lValue);
|
|
}
|
|
|
|
|
|
|
|
CILong &
|
|
CILong::operator =(
|
|
IN LONG lValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Assignment operator taking long value
|
|
|
|
Parameters:
|
|
|
|
LONG lValue Value to be set
|
|
|
|
Return Value:
|
|
|
|
this object
|
|
|
|
--*/
|
|
{
|
|
m_lValue = lValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
CILong &
|
|
CILong::operator =(
|
|
IN LPCTSTR lpszValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Assignment operator taking string value
|
|
|
|
Parameters:
|
|
|
|
LPCTSTR lpszValue String number
|
|
|
|
Return Value:
|
|
|
|
this object
|
|
|
|
--*/
|
|
{
|
|
ConvertStringToLong(lpszValue, m_lValue);
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Arithmetic Shorthand operators
|
|
//
|
|
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
|
|
|
|
|
|
CILong &
|
|
CILong::operator +=(
|
|
IN const LONG lValue
|
|
)
|
|
{
|
|
m_lValue += lValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CILong &
|
|
CILong::operator +=(
|
|
IN const LPCTSTR lpszValue
|
|
)
|
|
{
|
|
LONG lValue;
|
|
|
|
ConvertStringToLong(lpszValue, lValue);
|
|
|
|
m_lValue += lValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CILong &
|
|
CILong::operator +=(
|
|
IN const CILong& value
|
|
)
|
|
{
|
|
m_lValue += value.m_lValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CILong &
|
|
CILong::operator -=(
|
|
IN const LONG lValue
|
|
)
|
|
{
|
|
m_lValue -= lValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CILong &
|
|
CILong::operator -=(
|
|
IN const LPCTSTR lpszValue
|
|
)
|
|
{
|
|
LONG lValue;
|
|
|
|
ConvertStringToLong(lpszValue, lValue);
|
|
|
|
m_lValue -= lValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CILong &
|
|
CILong::operator -=(
|
|
IN const CILong& value
|
|
)
|
|
{
|
|
m_lValue -= value.m_lValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CILong &
|
|
CILong::operator *=(
|
|
IN const LONG lValue
|
|
)
|
|
{
|
|
m_lValue *= lValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CILong &
|
|
CILong::operator *=(
|
|
IN const LPCTSTR lpszValue
|
|
)
|
|
{
|
|
LONG lValue;
|
|
|
|
ConvertStringToLong(lpszValue, lValue);
|
|
|
|
m_lValue *= lValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CILong &
|
|
CILong::operator *=(
|
|
IN const CILong& value
|
|
)
|
|
{
|
|
m_lValue *= value.m_lValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CILong &
|
|
CILong::operator /=(
|
|
IN const LONG lValue
|
|
)
|
|
{
|
|
if (lValue != 0)
|
|
{
|
|
m_lValue /= lValue;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CILong &
|
|
CILong::operator /=(
|
|
IN const LPCTSTR lpszValue
|
|
)
|
|
{
|
|
LONG lValue;
|
|
|
|
ConvertStringToLong(lpszValue, lValue);
|
|
|
|
if (0 == lValue)
|
|
{
|
|
m_lValue = 0;
|
|
}
|
|
else
|
|
{
|
|
m_lValue /= lValue;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CILong &
|
|
CILong::operator /=(
|
|
IN const CILong& value
|
|
)
|
|
{
|
|
m_lValue /= value.m_lValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
CIFloat::CIFloat(
|
|
IN int nPrecision
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor without arguments
|
|
|
|
Parameters:
|
|
|
|
int nPrecision Number of decimal digits in string,
|
|
|
|
Return Value:
|
|
|
|
N/A
|
|
|
|
--*/
|
|
: m_flValue(0.0),
|
|
m_nPrecision(nPrecision)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
CIFloat::CIFloat(
|
|
IN double flValue,
|
|
IN int nPrecision
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor taking double argument
|
|
|
|
Parameters:
|
|
|
|
double flValue Value to be set
|
|
int nPrecision Number of decimal digits in string,
|
|
|
|
Return Value:
|
|
|
|
N/A
|
|
|
|
--*/
|
|
: m_flValue(flValue),
|
|
m_nPrecision(nPrecision)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
CIFloat::CIFloat(
|
|
IN LONG lInteger,
|
|
IN LONG lFraction,
|
|
IN int nPrecision
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor taking integer and fraction argument
|
|
|
|
Parameters:
|
|
|
|
LONG lInteger Integer portion
|
|
LONG lFraction Fractional portion
|
|
int nPrecision Number of decimal digits in string,
|
|
|
|
Return Value:
|
|
|
|
N/A
|
|
|
|
--*/
|
|
: m_nPrecision(nPrecision)
|
|
{
|
|
m_flValue = CINumber::BuildFloat(lInteger, lFraction);
|
|
}
|
|
|
|
|
|
|
|
CIFloat::CIFloat(
|
|
IN LPCTSTR lpszValue,
|
|
IN int nPrecision
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor taking string argument
|
|
|
|
Parameters:
|
|
|
|
LPCTSTR lpszValue String number
|
|
int nPrecision Number of decimal digits in string,
|
|
|
|
Return Value:
|
|
|
|
N/A
|
|
|
|
--*/
|
|
: m_nPrecision(nPrecision)
|
|
{
|
|
ConvertStringToFloat(lpszValue, m_flValue);
|
|
}
|
|
|
|
|
|
|
|
CIFloat &
|
|
CIFloat::operator =(
|
|
IN double flValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Assignment operator taking double value
|
|
|
|
Parameters:
|
|
|
|
double flValue Value to be set
|
|
|
|
Return Value:
|
|
|
|
this object
|
|
|
|
--*/
|
|
{
|
|
m_flValue = flValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
CIFloat &
|
|
CIFloat::operator =(
|
|
IN LPCTSTR lpszValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Assignment operator taking string value
|
|
|
|
Parameters:
|
|
|
|
LPCTSTR lpszValue String number
|
|
|
|
Return Value:
|
|
|
|
this object
|
|
|
|
--*/
|
|
{
|
|
ConvertStringToFloat(lpszValue, m_flValue);
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Arithmetic Shorthand operators
|
|
//
|
|
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
|
|
|
|
|
|
CIFloat &
|
|
CIFloat::operator +=(
|
|
IN const double flValue
|
|
)
|
|
{
|
|
m_flValue += flValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CIFloat &
|
|
CIFloat::operator +=(
|
|
IN const LPCTSTR lpszValue
|
|
)
|
|
{
|
|
double flValue;
|
|
|
|
ConvertStringToFloat(lpszValue, flValue);
|
|
|
|
m_flValue += flValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CIFloat &
|
|
CIFloat::operator +=(
|
|
IN const CIFloat& value
|
|
)
|
|
{
|
|
m_flValue += value.m_flValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CIFloat &
|
|
CIFloat::operator -=(
|
|
IN const double flValue
|
|
)
|
|
{
|
|
m_flValue -= flValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CIFloat &
|
|
CIFloat::operator -=(
|
|
IN const LPCTSTR lpszValue
|
|
)
|
|
{
|
|
double flValue;
|
|
|
|
ConvertStringToFloat(lpszValue, flValue);
|
|
|
|
m_flValue -= flValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CIFloat &
|
|
CIFloat::operator -=(
|
|
IN const CIFloat& value
|
|
)
|
|
{
|
|
m_flValue -= value.m_flValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CIFloat &
|
|
CIFloat::operator *=(
|
|
IN const double flValue
|
|
)
|
|
{
|
|
m_flValue *= flValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CIFloat &
|
|
CIFloat::operator *=(
|
|
IN const LPCTSTR lpszValue
|
|
)
|
|
{
|
|
double flValue;
|
|
|
|
ConvertStringToFloat(lpszValue, flValue);
|
|
|
|
m_flValue *= flValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CIFloat &
|
|
CIFloat::operator *=(
|
|
IN const CIFloat& value
|
|
)
|
|
{
|
|
m_flValue *= value.m_flValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CIFloat &
|
|
CIFloat::operator /=(
|
|
IN const double flValue
|
|
)
|
|
{
|
|
m_flValue /= flValue;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CIFloat &
|
|
CIFloat::operator /=(
|
|
IN const LPCTSTR lpszValue
|
|
)
|
|
{
|
|
double flValue;
|
|
|
|
ConvertStringToFloat(lpszValue, flValue);
|
|
if (flValue != 0)
|
|
{
|
|
m_flValue /= flValue;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// As above
|
|
//
|
|
CIFloat &
|
|
CIFloat::operator /=(
|
|
IN const CIFloat& value
|
|
)
|
|
{
|
|
m_flValue /= value.m_flValue;
|
|
|
|
return *this;
|
|
}
|