|
|
/*++
Copyright (c) 1995-2001 Microsoft Corporation
Module Name:
string.c
Abstract:
Domain Name System (DNS) Library
DNS string routines.
Author:
Jim Gilroy (jamesg) October 1995
Revision History:
jamesg Jan 1997 UTF-8, Unicode conversions
--*/
#include "local.h"
PSTR Dns_CreateStringCopy( IN PCHAR pchString, IN DWORD cchString ) /*++
Routine Description:
Create copy of string.
Arguments:
pchString -- ptr to string to copy
cchString -- length of string, if unknown; if NOT given, then pchString MUST be NULL terminated
Return Value:
Ptr to string copy, if successful NULL on failure.
--*/ { LPSTR pstringNew;
DNSDBG( TRACE, ( "Dns_CreateStringCopy()\n" ));
if ( !pchString ) { SetLastError( ERROR_INVALID_PARAMETER ); return( NULL ); }
// determine string length, if not given
if ( !cchString ) { cchString = strlen( pchString ); }
// allocate memory
pstringNew = (LPSTR) ALLOCATE_HEAP( cchString+1 ); if ( !pstringNew ) { SetLastError( DNS_ERROR_NO_MEMORY ); return( NULL ); }
// copy and NULL terminate
RtlCopyMemory( pstringNew, pchString, cchString );
pstringNew[cchString] = 0;
return( pstringNew ); }
DWORD Dns_GetBufferLengthForStringCopy( IN PCHAR pchString, IN DWORD cchString, IN DNS_CHARSET CharSetIn, IN DNS_CHARSET CharSetOut ) /*++
Routine Description:
Determing length required for copy of string.
Arguments:
pchString -- ptr to string to get buffer length for
cchString -- length of string, if known; - if CharSetIn is unicode, then this is length in wide characters - if NOT given, then pchString MUST be NULL terminated
CharSetIn -- incoming character set
CharSetOut -- result character set
Return Value:
Buffer length (bytes) required for string, includes space for terminating NULL. Zero on invalid\unconvertible string. GetLastError() set to ERROR_INVALID_DATA.
--*/ { INT length;
DNSDBG( TRACE, ( "Dns_GetBufferLengthForStringCopy()\n" ));
if ( !pchString ) { SetLastError( ERROR_INVALID_PARAMETER ); return( 0 ); }
//
// incoming Unicode
//
if ( CharSetIn == DnsCharSetUnicode ) { if ( !cchString ) { cchString = (WORD) wcslen( (PWCHAR)pchString ); }
// unicode to unicode
if ( CharSetOut == DnsCharSetUnicode ) { return( (cchString+1) * 2 ); }
// unicode to UTF8
//
// use private unicode\utf8 conversion functions
// - superior to public ones (faster, more robust)
// - Win95 does not support CP_UTF8
//
// for unicode-UTF8 there's no invalid string possible
else if ( CharSetOut == DnsCharSetUtf8 ) { #if 0
length = WideCharToMultiByte( CP_UTF8, 0, // no flags
(PWCHAR) pchString, (INT) cchString, NULL, 0, // call determines required buffer length
NULL, NULL ); #endif
length = Dns_UnicodeToUtf8( (PWCHAR) pchString, (INT) cchString, NULL, 0 ); ASSERT( length != 0 || cchString == 0 ); return( length + 1 ); }
// unicode to ANSI
// - some chars will NOT convert
else if ( CharSetOut == DnsCharSetAnsi ) { length = WideCharToMultiByte( CP_ACP, 0, // no flags
(PWCHAR) pchString, (INT) cchString, NULL, 0, // call determines required buffer length
NULL, NULL ); if ( length == 0 && cchString != 0 ) { goto Failed; } return( length + 1 ); }
// bad CharSetOut drops to Failed
}
//
// incoming UTF8
//
else if ( CharSetIn == DnsCharSetUtf8 ) { if ( !cchString ) { cchString = strlen( pchString ); }
// UTF8 to UTF8
if ( CharSetOut == DnsCharSetUtf8 ) { return( cchString + 1 ); }
// UTF8 to unicode
//
// use private unicode\utf8 conversion functions
// - superior to public ones (faster, more robust)
// - Win95 does not support CP_UTF8
//
// for UTF8 string can be invalid, catch and return error
else if ( CharSetOut == DnsCharSetUnicode ) { #if 0
length = MultiByteToWideChar( CP_UTF8, 0, // no flags
pchString, (INT) cchString, NULL, 0 // call determines required buffer length
); #endif
length = Dns_Utf8ToUnicode( pchString, (INT) cchString, NULL, 0 ); if ( length == 0 && cchString != 0 ) { ASSERT( GetLastError() == ERROR_INVALID_DATA ); return( 0 ); } return( (length+1)*2 ); }
// UTF8 to ANSI
// - note, result length here is actually buffer length
else if ( CharSetOut == DnsCharSetAnsi ) { return Dns_Utf8ToAnsi( pchString, cchString, NULL, 0 ); }
// bad CharSetOut drops to Failed
}
//
// incoming ANSI
//
else if ( CharSetIn == DnsCharSetAnsi ) { if ( !cchString ) { cchString = strlen( pchString ); }
// ANSI to ANSI
if ( CharSetOut == DnsCharSetAnsi ) { return( cchString + 1 ); }
// ANSI to unicode
// - should always succeed
else if ( CharSetOut == DnsCharSetUnicode ) { length = MultiByteToWideChar( CP_ACP, 0, // no flags
pchString, (INT) cchString, NULL, 0 // call determines required buffer length
); if ( length == 0 && cchString ) { ASSERT( FALSE ); ASSERT( GetLastError() == ERROR_INVALID_DATA ); goto Failed; } return( (length+1) * 2 ); }
// ANSI to UTF8
// - note, result length here is actually buffer length
else if ( CharSetOut == DnsCharSetUtf8 ) { return Dns_AnsiToUtf8( pchString, cchString, NULL, 0 ); }
// bad CharSetOut drops to Failed
}
// all unhandled cases are failures
Failed:
DNSDBG( ANY, ( "ERROR: Dns_GetBufferLengthForStringCopy() failed!\n" "\tpchString = %p (%*s)\n" "\tcchString = %d\n" "\tCharSetIn = %d\n" "\tCharSetOut = %d\n", pchString, cchString, pchString, cchString, CharSetIn, CharSetOut ));
SetLastError( ERROR_INVALID_DATA ); return( 0 ); }
DWORD Dns_StringCopy( OUT PBYTE pBuffer, IN OUT PDWORD pdwBufLength, IN PCHAR pchString, IN DWORD cchString, IN DNS_CHARSET CharSetIn, IN DNS_CHARSET CharSetOut ) /*++
Routine Description:
Create copy of DNS string.
Arguments:
pBuffer -- buffer to copy to
pdwBufLength -- ptr to length of buffer in bytes; if NULL, buffer MUST have adequate length if exists, then copy only completed if *pdwBufLength is adequate to hold converted result
pchString -- ptr to string to copy
cchString -- length of string, if known; - if CharSetIn is unicode, then this is length in wide characters - if NOT given, then pchString MUST be NULL terminated
CharSetIn -- incoming character set
CharSetOut -- result character set
Return Value:
Count of bytes written to buffer (includes terminating NULL). Zero on error. GetLastError() for status.
--*/ { INT length; DWORD bufLength;
DNSDBG( TRACE, ( "Dns_StringCopy()\n" )); DNSDBG( STRING, ( "Dns_StringCopy()\n" "\tpBuffer = %p\n" "\tpdwBufLen = %p\n" "\tbuf length = %d\n" "\tpchString = %p\n" "\tcchString = %d\n" "\tCharSetIn = %d\n" "\tCharSetOut = %d\n", pBuffer, pdwBufLength, pdwBufLength ? *pdwBufLength : 0, pchString, cchString, CharSetIn, CharSetOut ));
if ( !pchString ) { DNS_ASSERT( FALSE ); SetLastError( ERROR_INVALID_PARAMETER ); return( 0 ); }
//
// find string length
// do this here so don't do it twice if must calculate required buffer length
//
if ( cchString == 0 ) { if ( CharSetIn == DnsCharSetUnicode ) { cchString = (WORD) wcslen( (PWCHAR)pchString ); } else { cchString = strlen( pchString ); } }
//
// verify adequate buffer length
//
// DCR_PERF: ideally make direct copy to buffer and fail if
// over length, rather than effectively having to convert
// twice
//
if ( pdwBufLength ) { bufLength = Dns_GetBufferLengthForStringCopy( pchString, cchString, CharSetIn, CharSetOut );
if ( bufLength == 0 ) { SetLastError( ERROR_INVALID_DATA ); *pdwBufLength = 0; return( 0 ); } if ( bufLength > *pdwBufLength ) { SetLastError( ERROR_MORE_DATA ); *pdwBufLength = bufLength; return( 0 ); }
*pdwBufLength = bufLength; }
//
// incoming unicode string
//
if ( CharSetIn == DnsCharSetUnicode ) { // unicode to unicode straight copy
// - correct for length in wide characters
if ( CharSetOut == DnsCharSetUnicode ) { ((PWORD)pBuffer)[ cchString ] = 0; cchString *= 2; RtlCopyMemory( pBuffer, pchString, cchString );
return( cchString+2 ); }
// unicode => UTF8
//
// use private unicode\utf8 conversion functions
// - superior to public ones (faster, more robust)
// - Win95 does not support CP_UTF8
//
// for unicode-UTF8 there's no invalid string possible
else if ( CharSetOut == DnsCharSetUtf8 ) { #if 0
length = WideCharToMultiByte( CP_UTF8, 0, // no flags
(PWCHAR) pchString, (INT) cchString, pBuffer, MAXWORD, // assuming adequate length
NULL, NULL ); #endif
length = Dns_UnicodeToUtf8( (LPWSTR) pchString, cchString, pBuffer, MAXWORD // assuming adequate length
); ASSERT( length != 0 || cchString == 0 );
pBuffer[ length ] = 0; return( length + 1 ); }
// unicode => ANSI
// - this conversion can fail
else if ( CharSetOut == DnsCharSetAnsi ) { length = WideCharToMultiByte( CP_ACP, 0, // no flags
(PWCHAR) pchString, (INT) cchString, pBuffer, MAXWORD, // assuming adequate length
NULL, NULL );
if ( length == 0 && cchString != 0 ) { goto Failed; } pBuffer[ length ] = 0; return( length + 1 ); }
// bad CharSetOut drops to Failed
}
//
// incoming UTF8
//
if ( CharSetIn == DnsCharSetUtf8 ) { // UTF8 to UTF8 straight copy
if ( CharSetOut == DnsCharSetUtf8 ) { memcpy( pBuffer, pchString, cchString );
pBuffer[cchString] = 0; return( cchString + 1 ); }
// UTF8 to unicode conversion
//
// use private unicode\utf8 conversion functions
// - superior to public ones (faster, more robust)
// - Win95 does not support CP_UTF8
//
// UTF8 strings can be invalid, and since sending in "infinite"
// buffer, this is only possible error
else if ( CharSetOut == DnsCharSetUnicode ) { #if 0
length = MultiByteToWideChar( CP_UTF8, 0, // no flags
(PCHAR) pchString, (INT) cchString, (PWCHAR) pBuffer, MAXWORD // assuming adequate length
); #endif
length = Dns_Utf8ToUnicode( pchString, cchString, (LPWSTR) pBuffer, MAXWORD ); if ( length == 0 && cchString != 0 ) { ASSERT( GetLastError() == ERROR_INVALID_DATA ); goto Failed; } ((PWORD)pBuffer)[length] = 0; return( (length+1) * 2 ); }
// UTF8 to ANSI
// - note, result length here is actually buffer length
else if ( CharSetOut == DnsCharSetAnsi ) { length = Dns_Utf8ToAnsi( pchString, cchString, pBuffer, MAXWORD ); if ( length == 0 ) { goto Failed; } return( length ); }
// bad CharSetOut drops to Failed
}
//
// incoming ANSI
//
if ( CharSetIn == DnsCharSetAnsi ) { // ANSI to ANSI straight copy
if ( CharSetOut == DnsCharSetAnsi ) { memcpy( pBuffer, pchString, cchString );
pBuffer[cchString] = 0; return( cchString + 1 ); }
// ANSI to unicode conversion
// - ANSI to unicode should not fail
else if ( CharSetOut == DnsCharSetUnicode ) { length = MultiByteToWideChar( CP_ACP, 0, // no flags
(PCHAR) pchString, (INT) cchString, (PWCHAR) pBuffer, MAXWORD // assuming adequate length
); if ( length == 0 && cchString ) { ASSERT( FALSE ); ASSERT( GetLastError() == ERROR_INVALID_DATA ); goto Failed; } ((PWORD)pBuffer)[length] = 0; return( (length+1) * 2 ); }
// ANSI to UTF8
// - note, result length here is actually buffer length
else if ( CharSetOut == DnsCharSetUtf8 ) { length = Dns_AnsiToUtf8( pchString, cchString, pBuffer, MAXWORD ); if ( length == 0 ) { goto Failed; } return( length ); }
// bad CharSetOut drops to Failed
}
// all unhandled cases are failures
Failed:
DNSDBG( ANY, ( "ERROR: Dns_StringCopy() failed!\n" "\tpBuffer = %p\n" "\tpdwBufLen = %p\n" "\tbuf length = %d\n" "\tpchString = %p (%*s)\n" "\tcchString = %d\n" "\tCharSetIn = %d\n" "\tCharSetOut = %d\n", pBuffer, pdwBufLength, pdwBufLength ? *pdwBufLength : 0, pchString, cchString, pchString, cchString, CharSetIn, CharSetOut ));
SetLastError( ERROR_INVALID_DATA ); return( 0 ); }
PVOID Dns_StringCopyAllocate( IN PCHAR pchString, IN DWORD cchString, IN DNS_CHARSET CharSetIn, IN DNS_CHARSET CharSetOut ) /*++
Routine Description:
Create copy of DNS string
Arguments:
pchString -- ptr to string to copy
cchString -- length of string, if known; - if CharSetIn, then this is length in wide characters - if NOT given, then pchString MUST be NULL terminated
CharSetIn -- flag indicates incoming string is unicode
CharSetOut -- flag indicates copy will be in unicode format
Return Value:
Ptr to string copy, if successful NULL on failure.
--*/ { PCHAR pnew; DWORD length;
DNSDBG( TRACE, ( "Dns_StringCopyAllocate()\n" )); DNSDBG( STRING, ( "Dns_StringCopyAllocate( %.*s )\n" "\tpchString = %p\n" "\tcchString = %d\n" "\tUnicodeIn = %d\n" "\tUnicodeOut = %d\n", cchString, pchString, pchString, cchString, CharSetIn, CharSetOut ));
if ( !pchString ) { DNS_ASSERT( FALSE ); SetLastError( ERROR_INVALID_PARAMETER ); return( NULL ); }
//
// determine incoming string length
// do this explicitly to avoid doing string length operations twice
//
if ( !cchString ) { if ( CharSetIn == DnsCharSetUnicode ) { cchString = (WORD) wcslen( (PWCHAR)pchString ); } else { cchString = strlen( pchString ); } }
//
// determine required buffer length and allocate
//
length = Dns_GetBufferLengthForStringCopy( pchString, cchString, CharSetIn, CharSetOut ); if ( length == 0 ) { ASSERT( CharSetIn && CharSetOut && GetLastError() == ERROR_INVALID_DATA ); SetLastError( ERROR_INVALID_DATA ); return( NULL ); } pnew = (PVOID) ALLOCATE_HEAP( length ); if ( !pnew ) { SetLastError( DNS_ERROR_NO_MEMORY ); return( NULL ); }
//
// copy \ convert string
// - can fail if conversion not valid
// (ex. bogus UTF8 string, or attempting
// conversion from ANSI to UTF8)
//
if ( ! Dns_StringCopy( pnew, NULL, pchString, cchString, CharSetIn, CharSetOut ) ) { FREE_HEAP( pnew ); return( NULL ); }
return( pnew ); }
//
// Simple create string copy utilities.
//
PSTR Dns_CreateStringCopy_A( IN PCSTR pszString ) /*++
Routine Description:
Create copy of string.
Simple wrapper to handle - sizing - memory allocation - copy of string
Arguments:
pszString -- ptr to string to copy
Return Value:
Ptr to string copy, if successful NULL on failure.
--*/ { PSTR pnew; DWORD length;
DNSDBG( TRACE, ( "Dns_CreateStringCopy_A( %s )\n", pszString ));
if ( !pszString ) { SetLastError( ERROR_INVALID_PARAMETER ); return( NULL ); }
// determine string length, if not given
length = strlen( pszString ) + 1;
// allocate memory
pnew = (LPSTR) ALLOCATE_HEAP( length ); if ( !pnew ) { SetLastError( DNS_ERROR_NO_MEMORY ); return( NULL ); }
// copy and NULL terminate
RtlCopyMemory( pnew, pszString, length );
return( pnew ); }
PWSTR Dns_CreateStringCopy_W( IN PCWSTR pwsString ) { PWSTR pnew; DWORD length;
DNSDBG( TRACE, ( "Dns_CreateStringCopy_W( %S )\n", pwsString ));
if ( !pwsString ) { SetLastError( ERROR_INVALID_PARAMETER ); return( NULL ); }
// allocate memory
length = (wcslen( pwsString ) + 1) * sizeof(WCHAR);
pnew = (PWSTR) ALLOCATE_HEAP( length ); if ( !pnew ) { SetLastError( DNS_ERROR_NO_MEMORY ); return( NULL ); }
// copy and NULL terminate
RtlCopyMemory( pnew, pwsString, length );
return( pnew ); }
PWSTR Dns_CreateConcatenatedString_W( IN PCWSTR * pStringArray ) /*++
Routine Description:
Create concatenated string.
Arguments:
pStringArray -- array of string pointers to concat NULL pointer terminates array
Return Value:
Ptr to concantenated string copy, if successful NULL on failure.
--*/ { PWSTR pnew; PCWSTR pwstr; DWORD length; DWORD iter;
DNSDBG( TRACE, ( "Dns_CreateConcatenatedString_W()\n" ));
if ( !pStringArray ) { SetLastError( ERROR_INVALID_PARAMETER ); return( NULL ); }
//
// loop determining required length
//
length = 1; iter = 0;
while ( pwstr = pStringArray[iter++] ) { length += wcslen( pwstr ); }
//
// allocate
//
pnew = (PWSTR) ALLOCATE_HEAP( length*sizeof(WCHAR) ); if ( !pnew ) { SetLastError( DNS_ERROR_NO_MEMORY ); return NULL; }
//
// write concatented string
//
pnew[0] = 0; iter = 0;
while ( pwstr = pStringArray[iter++] ) { wcscat( pnew, pwstr ); }
DNSDBG( TRACE, ( "Concatented string = %S\n", pnew )); return pnew; }
//
// MULTI_SZ routines
//
DWORD MultiSz_Length_A( IN PCSTR pmszString ) /*++
Routine Description:
Determine length (size) of MULTI_SZ string.
Arguments:
pmszString -- ptr to string to size
Return Value:
Size of MULTI_SZ string (in bytes). Includes terminating double NULL.
--*/ { PSTR pnext; DWORD lengthTotal = 0; DWORD length;
DNSDBG( TRACE, ( "MultiSz_Length_A( %s )\n", pmszString ));
//
// loop until read at end of strings
//
// when we reach the end, we'll be pointing at the second
// zero in the double null terminator; strlen() will return
// zero, and we'll add that to our count as 1 and exit
//
pnext = (PSTR) pmszString;
while ( pnext ) { length = strlen( pnext ) + 1; lengthTotal += length;
if ( length == 1 ) { break; } pnext += length; }
return lengthTotal; }
PSTR MultiSz_NextString_A( IN PCSTR pmszString ) /*++
Routine Description:
Find next string in MULTI_SZ string
Arguments:
pmszString -- ptr to multi string
Return Value:
Next string in MULTI_SZ string. NULL if no strings left.
--*/ { PSTR pnext; DWORD length;
DNSDBG( TRACE, ( "MultiSz_NextString_A( %s )\n", pmszString ));
//
// find next string in multi-string
// - find length of current string
// - hop over it (inc. null)
// - if pointing at terminating double-null return
// NULL to signal end
//
pnext = (PSTR) pmszString; if ( !pnext ) { return NULL; }
length = strlen( pnext ); if ( length == 0 ) { DNSDBG( ANY, ( "ERROR: MultiSz_Next(%p) called on terminator!\n", pmszString )); return NULL; }
pnext += length + 1; if ( *pnext == 0 ) { return NULL; }
return pnext; }
PSTR MultiSz_Copy_A( IN PCSTR pmszString ) /*++
Routine Description:
Create copy of MULTI_SZ string.
Simple wrapper to handle - sizing - memory allocation - copy of string
Arguments:
pmszString -- ptr to string to copy
Return Value:
Ptr to string copy, if successful NULL on failure.
--*/ { PSTR pnew; DWORD length;
DNSDBG( TRACE, ( "MultiSz_Copy_A( %s )\n", pmszString ));
if ( !pmszString ) { SetLastError( ERROR_INVALID_PARAMETER ); return( NULL ); }
// determine string length, if not given
length = MultiSz_Length_A( pmszString );
// allocate memory
pnew = (LPSTR) ALLOCATE_HEAP( length ); if ( !pnew ) { SetLastError( DNS_ERROR_NO_MEMORY ); return( NULL ); }
// copy and NULL terminate
RtlCopyMemory( pnew, pmszString, length );
return( pnew ); }
//
// Random
//
INT wcsicmp_ThatWorks( IN PWSTR pString1, IN PWSTR pString2 ) /*++
Routine Description:
A version of wcsicmp that actually works.
This is just a wrapped on CompareStringW, to hide all the detail and give an interface identical to wcsicmp().
It uses US English to standardize the comparison.
Arguments:
pString1 -- first string; must be NULL terminated
pString2 -- first second; must be NULL terminated
Return Value:
-1 -- if string 1 less than string 2 0 -- strings are equal 1 -- if string 1 greater than string 2
--*/ { INT result;
//
// compare
// - case conversion done in default DNS locale -- US English
// this locale correctly matches most non-locale sensitive
// upper-lower characters
//
result = CompareStringW( DNS_DEFAULT_LOCALE, NORM_IGNORECASE, pString1, (-1), // NULL terminated
pString2, (-1) // NULL terminated
);
if ( result == CSTR_EQUAL ) { result = 0; } else if ( result == CSTR_LESS_THAN ) { result = -1; } else // greater than or error
{ result = 1; }
return( result ); }
LPWSTR Dns_GetResourceString( IN DWORD dwStringId, IN OUT LPWSTR pwszBuffer, IN DWORD cbBuffer ) /*++
Routine Description:
Loads a string (defined in dnsmsg.mc) from current module
Arguments:
dwStringId -- The ID of the string to be fetched
Return Value:
DCR: kill off eyal function DEVNOTE: don't understand the value of this return -- it's essentially a BOOL, we already know what the ptr is it's the buffer passed in -- ptr to next byte is useful in continuous write situation (ugly and useless in others) -- better would just be the same return as LoadString, so we both get the success\failure indication and also know how many bytes forward we must push our buffer ptr if we want to write more
Error: NULL Success: a pointer to the loaded string
--*/ { LPWSTR pStr = NULL; DWORD status; HANDLE hMod;
DNSDBG( TRACE, ( "Dns_GetStringResource()\n" ));
// Get module handle-- No need to close handle, it is just a ptr w/o increment on ref count.
hMod = GetModuleHandle( NULL ); if ( !hMod ) { ASSERT( hMod ); return NULL; }
status = LoadStringW( hMod, dwStringId, pwszBuffer, cbBuffer );
if ( status != 0 ) { pStr = pwszBuffer; } ELSE { // LoadString returns # of bytes loaded, convert to error.
status = GetLastError(); DNSDBG( TRACE, ( "Error <%lu>: Failed to load string %d\n", status, dwStringId )); ASSERT ( FALSE ); }
DNSDBG( TRACE, ( "Exit <0x%p> Dns_GetStringResource\n", pStr ));
return pStr; }
//
// End string.c
//
|