|
|
/*++
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) { AddTail( strl.GetNext(pos) ); }
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 ( ( (UCHAR) ( *(CINumber::s_pstrThousandSeperator) )[0] ) == ( (UCHAR) 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; }
|