windows-nt/Source/XPSP1/NT/shell/shlwapi/uniansi.c
2020-09-26 16:20:57 +08:00

734 lines
22 KiB
C

//============================================================================
//
// UNICODE and ANSI conversion functions
//
//============================================================================
#include "priv.h"
#include <mlang.h>
/*
* @doc INTERNAL
*
* @func int | SHAnsiToUnicodeNativeCP |
*
* Convert an ANSI string to a UNICODE string via the
* specified Windows code page. If the source string is too large
* for the destination buffer, then as many characters as
* possible are copied.
*
* The resulting output string is always null-terminated.
*
* @parm UINT | uiCP |
*
* The code page in which to perform the conversion.
* This must be a Windows code page.
*
* @parm LPCSTR | pszSrc |
*
* Source buffer containing ANSI string to be converted.
*
* @parm int | cchSrc |
*
* Source buffer length, including terminating null.
*
* @parm LPWSTR | pwszDst |
*
* Destination buffer to receive converted UNICODE string.
*
* @parm int | cwchBuf |
*
* Size of the destination buffer in <t WCHAR>s.
*
* @returns
*
* On success, the number of characters copied to the output
* buffer is returned, including the terminating null.
*/
int
SHAnsiToUnicodeNativeCP(UINT uiCP,
LPCSTR pszSrc, int cchSrc,
LPWSTR pwszDst, int cwchBuf)
{
int cwchRc = 0; /* Assume failure */
/*
* Checks the caller should've made.
*/
ASSERT(IS_VALID_STRING_PTRA(pszSrc, -1));
ASSERT(cchSrc == lstrlenA(pszSrc) + 1);
ASSERT(IS_VALID_WRITE_BUFFER(pwszDst, WCHAR, cwchBuf));
ASSERT(pszSrc != NULL);
ASSERT(uiCP != 1200 && uiCP != 65000 && uiCP != 50000 && uiCP != 65001);
ASSERT(pwszDst);
ASSERT(cwchBuf);
cwchRc = MultiByteToWideChar(uiCP, 0, pszSrc, cchSrc, pwszDst, cwchBuf);
if (cwchRc) {
/*
* The output buffer was big enough; no double-buffering
* needed.
*/
} else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
/*
* The output buffer wasn't big enough. Need to double-buffer.
*/
int cwchNeeded = MultiByteToWideChar(uiCP, 0, pszSrc, cchSrc,
NULL, 0);
ASSERT(cwchRc == 0); /* In case we fail later */
if (cwchNeeded) {
LPWSTR pwsz = (LPWSTR)LocalAlloc(LMEM_FIXED,
cwchNeeded * SIZEOF(WCHAR));
if (pwsz) {
cwchRc = MultiByteToWideChar(uiCP, 0, pszSrc, cchSrc,
pwsz, cwchNeeded);
if (cwchRc) {
StrCpyNW(pwszDst, pwsz, cwchBuf);
cwchRc = cwchBuf;
}
LocalFree(pwsz);
}
}
} else {
/* Possibly unsupported code page */
ASSERT(!"Unexpected error in MultiByteToWideChar");
}
return cwchRc;
}
/*
* @doc INTERNAL
*
* @func int | SHAnsiToUnicodeInetCP |
*
* Convert an ANSI string to a UNICODE string via the
* specified Internet code page. If the source string is too large
* for the destination buffer, then as many characters as
* possible are copied.
*
* The resulting output string is always null-terminated.
*
* @parm UINT | uiCP |
*
* The code page in which to perform the conversion.
* This must be an Internet code page.
*
* @parm LPCSTR | pszSrc |
*
* Source buffer containing ANSI string to be converted.
*
* @parm int | cchSrc |
*
* Source buffer length, including terminating null.
*
* @parm LPWSTR | pwszDst |
*
* Destination buffer to receive converted UNICODE string.
*
* @parm int | cwchBuf |
*
* Size of the destination buffer in <t WCHAR>s.
*
* @returns
*
* On success, the number of characters copied to the output
* buffer is returned, including the terminating null.
*/
int
SHAnsiToUnicodeInetCP(UINT uiCP,
LPCSTR pszSrc, int cchSrc,
LPWSTR pwszDst, int cwchBuf)
{
int cchSrcT, cwchNeeded;
int cwchRc = 0; /* Assume failure */
HRESULT hres;
DWORD dwMode;
/*
* Checks the caller should've made.
*/
ASSERT(IS_VALID_STRING_PTRA(pszSrc, -1));
ASSERT(cchSrc == lstrlenA(pszSrc) + 1);
ASSERT(IS_VALID_WRITE_BUFFER(pwszDst, WCHAR, cwchBuf));
ASSERT(pszSrc != NULL);
ASSERT(uiCP == 1200 || uiCP == 65000 || uiCP == 65001);
ASSERT(pwszDst);
ASSERT(cwchBuf);
cchSrcT = cchSrc;
cwchNeeded = cwchBuf;
dwMode = 0;
hres = ConvertINetMultiByteToUnicode(&dwMode, uiCP, pszSrc,
&cchSrcT, pwszDst, &cwchNeeded);
if (SUCCEEDED(hres)) {
if (cchSrcT >= cchSrc) {
/*
* The output buffer was big enough; no double-buffering
* needed.
*/
cwchRc = cwchNeeded;
} else {
/*
* The output buffer wasn't big enough. Need to double-buffer.
*/
LPWSTR pwsz = (LPWSTR)LocalAlloc(LMEM_FIXED,
cwchNeeded * SIZEOF(WCHAR));
if (pwsz) {
dwMode = 0;
hres = ConvertINetMultiByteToUnicode(&dwMode, uiCP, pszSrc,
&cchSrc, pwsz, &cwchNeeded);
if (SUCCEEDED(hres)) {
StrCpyNW(pwszDst, pwsz, cwchBuf);
cwchRc = cwchBuf;
}
LocalFree(pwsz);
}
}
} else {
/* Possibly unsupported code page */
ASSERT(!"Unexpected error in ConvertInetMultiByteToUnicode");
}
return cwchRc;
}
/*
* @doc EXTERNAL
*
* @func int | SHAnsiToUnicodeCP |
*
* Convert an ANSI string to a UNICODE string via the
* specified code page, which can be either a native
* Windows code page or an Internet code page.
* If the source string is too large
* for the destination buffer, then as many characters as
* possible are copied.
*
* The resulting output string is always null-terminated.
*
* @parm UINT | uiCP |
*
* The code page in which to perform the conversion.
*
* @parm LPCSTR | pszSrc |
*
* Source buffer containing ANSI string to be converted.
*
* @parm LPWSTR | pwszDst |
*
* Destination buffer to receive converted UNICODE string.
*
* @parm int | cwchBuf |
*
* Size of the destination buffer in <t WCHAR>s.
*
* @returns
*
* On success, the number of characters copied to the output
* buffer is returned, including the terminating null.
*/
int
SHAnsiToUnicodeCP(UINT uiCP, LPCSTR pszSrc, LPWSTR pwszDst, int cwchBuf)
{
int cwchRc = 0; /* Assume failure */
RIPMSG(IS_VALID_STRING_PTRA(pszSrc, -1), "Caller of SHAnsiToUnicodeCP passed in a NULL pszSrc!");
ASSERT(IS_VALID_WRITE_BUFFER(pwszDst, WCHAR, cwchBuf));
/*
* Sanity check - NULL source string is treated as a null string.
*/
if (pszSrc == NULL) {
pszSrc = "";
}
/*
* Sanity check - Output buffer must be non-NULL and must be of
* nonzero size.
*/
if (pwszDst && cwchBuf) {
int cchSrc;
pwszDst[0] = 0; /* In case of error */
cchSrc = lstrlenA(pszSrc) + 1;
/*
* Decide what kind of code page it is.
*/
switch (uiCP) {
case 1200: // UCS-2 (Unicode)
uiCP = 65001;
// Fall through
case 50000: // "User Defined"
case 65000: // UTF-7
case 65001: // UTF-8
cwchRc = SHAnsiToUnicodeInetCP(uiCP, pszSrc, cchSrc, pwszDst, cwchBuf);
break;
default:
cwchRc = SHAnsiToUnicodeNativeCP(uiCP, pszSrc, cchSrc, pwszDst, cwchBuf);
break;
}
}
return cwchRc;
}
// This function exists to make sure SHAnsiToAnsi and SHUnicodeToAnsi
// have the same return value. Callers use SHTCharToAnsi and don't know
// when it callapses to SHAnsiToAnsi.
int SHAnsiToAnsi(LPCSTR pszSrc, LPSTR pszDst, int cchBuf)
{
int cchRc = 0; /* Assume failure */
if (cchBuf)
{
// APP COMPAT! WARNING! Sony PictureGear passes too-small buffers to
// SHGetPathFromIDList (which uses SHAnsiToAnsi eventually), so we
// must be careful to pass the actual buffer size to SHTruncateString
// and not the theoretical maximum (rarely attained).
LPSTR pszEnd = StrCpyNXA(pszDst, pszSrc, cchBuf);
cchRc = (int)(pszEnd - pszDst) + 1;
cchRc = SHTruncateString(pszDst, cchRc) + 1;
}
return cchRc;
}
// This function exists to make sure SHUnicodeToUnicode and SHUnicodeToAnsi
// have the same return value. Callers use SHTCharToUnicode and don't know
// when it callapses to SHUnicodeToUnicode.
int SHUnicodeToUnicode(LPCWSTR pwzSrc, LPWSTR pwzDst, int cchBuf)
{
return (int) (StrCpyNXW(pwzDst, pwzSrc, cchBuf) - pwzDst + 1); // size including terminator
}
/*
* @doc EXTERNAL
*
* @func int | SHAnsiToUnicode |
*
* Convert an ANSI string to a UNICODE string via the
* <c CP_ACP> code page. If the source string is too large
* for the destination buffer, then as many characters as
* possible are copied.
*
* The resulting output string is always null-terminated.
*
* @parm LPCSTR | pszSrc |
*
* Source buffer containing ANSI string to be converted.
*
* @parm LPWSTR | pwszDst |
*
* Destination buffer to receive converted UNICODE string.
*
* @parm int | cwchBuf |
*
* Size of the destination buffer in <t WCHAR>s.
*
* @returns
*
* On success, the number of characters copied to the output
* buffer is returned, including the terminating null.
*
*/
int
SHAnsiToUnicode(LPCSTR pszSrc, LPWSTR pwszDst, int cwchBuf)
{
return SHAnsiToUnicodeCP(CP_ACP, pszSrc, pwszDst, cwchBuf);
}
/*
* @doc INTERNAL
*
* @func int | SHUnicodeToAnsiNativeCP |
*
* Convert a UNICODE string to an ANSI string via the
* specified Windows code page. If the source string is too large
* for the destination buffer, then as many characters as
* possible are copied. Care is taken not to break a double-byte
* character.
*
* The resulting output string is always null-terminated.
*
* @parm UINT | uiCP |
*
* The code page in which to perform the conversion.
* This must be a Windows code page.
*
* @parm LPCWSTR | pwszSrc |
*
* Source buffer containing UNICODE string to be converted.
*
* @parm int | cwchSrc |
*
* Number of characters in source buffer, including terminating
* null.
*
* @parm LPSTR | pszDst |
*
* Destination buffer to receive converted ANSI string.
*
* @parm int | cchBuf |
*
* Size of the destination buffer in <t CHAR>s.
*
* @returns
*
* On success, the number of characters copied to the output
* buffer is returned, including the terminating null.
* (For the purpose of this function, a double-byte character
* counts as two characters.)
*/
int
SHUnicodeToAnsiNativeCP(UINT uiCP,
LPCWSTR pwszSrc, int cwchSrc,
LPSTR pszDst, int cchBuf)
{
int cchRc = 0; /* Assume failure */
#ifdef DEBUG
BOOL fVerify = TRUE;
BOOL fLossy;
if (uiCP == CP_ACPNOVALIDATE) {
// -1 means use CP_ACP, but do *not* verify
// kind of a hack, but it's DEBUG and leaves 99% of callers unchanged
uiCP = CP_ACP;
fVerify = FALSE;
}
#define USUALLY_NULL (&fLossy)
#else
#define USUALLY_NULL NULL
#endif
/*
* Checks the caller should've made.
*/
ASSERT(IS_VALID_STRING_PTRW(pwszSrc, -1));
ASSERT(cwchSrc == lstrlenW(pwszSrc) + 1);
ASSERT(IS_VALID_WRITE_BUFFER(pszDst, CHAR, cchBuf));
ASSERT(uiCP != 1200 && uiCP != 65000 && uiCP != 50000 && uiCP != 65001);
ASSERT(pwszSrc);
ASSERT(pszDst);
ASSERT(cchBuf);
cchRc = WideCharToMultiByte(uiCP, 0, pwszSrc, cwchSrc, pszDst, cchBuf,
NULL, USUALLY_NULL);
if (cchRc) {
/*
* The output buffer was big enough; no double-buffering
* needed.
*/
} else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
/*
* The output buffer wasn't big enough. Need to double-buffer.
*/
int cchNeeded = WideCharToMultiByte(uiCP, 0, pwszSrc, cwchSrc,
NULL, 0, NULL, NULL);
ASSERT(cchRc == 0); /* In case we fail later */
if (cchNeeded) {
LPSTR psz = (LPSTR)LocalAlloc(LMEM_FIXED,
cchNeeded * SIZEOF(CHAR));
if (psz) {
cchRc = WideCharToMultiByte(uiCP, 0, pwszSrc, cwchSrc,
psz, cchNeeded, NULL, USUALLY_NULL);
if (cchRc) {
// lstrcpyn doesn't check if it's chopping a DBCS char
// so we need to use SHTruncateString.
//
// Add 1 because SHTruncateString doesn't count
// the trailing null but we do
//
// Assert that we meet the preconditions for
// SHTruncateString to return a valid value.
//
ASSERT(cchRc > cchBuf);
cchRc = SHTruncateString(psz, cchBuf) + 1;
lstrcpynA(pszDst, psz, cchBuf);
}
LocalFree(psz);
}
}
} else {
/* Possibly unsupported code page */
ASSERT(!"Unexpected error in WideCharToMultiByte");
}
#ifdef DEBUG
TBOOL(!fVerify || !fLossy);
#endif
return cchRc;
}
/*
* @doc INTERNAL
*
* @func int | SHUnicodeToAnsiInetCP |
*
* Convert a UNICODE string to an ANSI string via the
* specified Internet code page. If the source string is too large
* for the destination buffer, then as many characters as
* possible are copied. Care is taken not to break a double-byte
* character.
*
* The resulting output string is always null-terminated.
*
* @parm UINT | uiCP |
*
* The code page in which to perform the conversion.
* This must be an Internet code page.
*
* @parm LPCWSTR | pwszSrc |
*
* Source buffer containing UNICODE string to be converted.
*
* @parm int | cwchSrc |
*
* Number of characters in source buffer, including terminating
* null.
*
* @parm LPSTR | pszDst |
*
* Destination buffer to receive converted ANSI string.
*
* @parm int | cchBuf |
*
* Size of the destination buffer in <t CHAR>s.
*
* @returns
*
* On success, the number of characters copied to the output
* buffer is returned, including the terminating null.
* (For the purpose of this function, a double-byte character
* counts as two characters.)
*/
int
SHUnicodeToAnsiInetCP(UINT uiCP,
LPCWSTR pwszSrc, int cwchSrc,
LPSTR pszDst, int cchBuf)
{
int cwchSrcT, cchNeeded;
int cchRc = 0; /* Assume failure */
DWORD dwMode;
HRESULT hres;
/*
* Checks the caller should've made.
*/
ASSERT(IS_VALID_STRING_PTRW(pwszSrc, -1));
ASSERT(cwchSrc == lstrlenW(pwszSrc) + 1);
ASSERT(IS_VALID_WRITE_BUFFER(pszDst, CHAR, cchBuf));
ASSERT(uiCP == 1200 || uiCP == 65000 || uiCP == 65001);
ASSERT(pwszSrc);
ASSERT(pszDst);
ASSERT(cchBuf);
/*
* Note that not all encodings translate a null terminator into a null
* terminator, so we have to save the NUL for last.
*/
cwchSrc--; /* Save the NUL for last */
cwchSrcT = cwchSrc;
cchNeeded = cchBuf - 1; /* Save the NUL for last */
dwMode = 0; /* Start fresh */
hres = ConvertINetUnicodeToMultiByte(&dwMode, uiCP, pwszSrc,
&cwchSrcT, pszDst, &cchNeeded);
if (SUCCEEDED(hres)) {
if (cwchSrcT >= cwchSrc) {
/*
* The output buffer was big enough; no double-buffering
* needed. Translate the NUL manually.
*/
ASSERT(cchNeeded < cchBuf);
pszDst[cchNeeded] = TEXT('\0');
cchRc = cchNeeded + 1;
} else {
/*
* The output buffer wasn't big enough. Need to double-buffer.
*/
LPSTR psz = (LPSTR)LocalAlloc(LMEM_FIXED,
cchNeeded * SIZEOF(CHAR));
if (psz) {
dwMode = 0; /* Start fresh */
hres = ConvertINetUnicodeToMultiByte(&dwMode, uiCP, pwszSrc,
&cwchSrc, psz, &cchNeeded);
if (SUCCEEDED(hres)) {
// lstrcpyn doesn't check if it's chopping a DBCS char
// so we need to use SHTruncateString.
//
// Add 1 because SHTruncateString doesn't count
// the trailing null but we do
//
// Assert that we meet the preconditions for
// SHTruncateString to return a valid value.
//
ASSERT(cchNeeded > cchBuf);
cchRc = SHTruncateString(psz, cchBuf) + 1;
lstrcpynA(pszDst, psz, cchBuf);
}
LocalFree(psz);
}
}
} else {
/* Possibly unsupported code page */
ASSERT(!"Unexpected error in ConvertInetUnicodeToMultiByte");
}
return cchRc;
}
/*
* @doc EXTERNAL
*
* @func int | SHUnicodeToAnsiCP |
*
* Convert a UNICODE string to an ANSI string via the
* specified code page, which can be either a native
* Windows code page or an Internet code page.
* If the source string is too large
* for the destination buffer, then as many characters as
* possible are copied. Care is taken not to break a double-byte
* character.
*
* The resulting output string is always null-terminated.
*
* @parm UINT | uiCP |
*
* The code page in which to perform the conversion.
*
* @parm LPCWSTR | pwszSrc |
*
* Source buffer containing UNICODE string to be converted.
*
* @parm LPSTR | pszDst |
*
* Destination buffer to receive converted ANSI string.
*
* @parm int | cchBuf |
*
* Size of the destination buffer in <t CHAR>s.
*
* @returns
*
* On success, the number of characters copied to the output
* buffer is returned, including the terminating null.
* (For the purpose of this function, a double-byte character
* counts as two characters.)
*
*/
int
SHUnicodeToAnsiCP(UINT uiCP, LPCWSTR pwszSrc, LPSTR pszDst, int cchBuf)
{
int cchRc = 0; /* Assume failure */
#ifdef DEBUG
#define GET_CP(uiCP) (((uiCP) == CP_ACPNOVALIDATE) ? CP_ACP : (uiCP))
#else
#define GET_CP(uiCP) uiCP
#endif
RIPMSG(IS_VALID_STRING_PTRW(pwszSrc, -1), "Caller of SHUnicodeToAnsiCP passed in a NULL pwszSrc!");
ASSERT(IS_VALID_WRITE_BUFFER(pszDst, CHAR, cchBuf));
/*
* Sanity check - NULL source string is treated as a null string.
*/
if (pwszSrc == NULL) {
pwszSrc = L"";
}
/*
* Sanity check - Output buffer must be non-NULL and must be of
* nonzero size.
*/
if (pszDst && cchBuf) {
int cwchSrc;
pszDst[0] = 0; /* In case of error */
cwchSrc = lstrlenW(pwszSrc) + 1; /* Yes, Win9x has lstrlenW */
/*
* Decide what kind of code page it is.
*/
switch (GET_CP(uiCP)) {
case 1200: // UCS-2 (Unicode)
uiCP = 65001;
// Fall through
case 50000: // "User Defined"
case 65000: // UTF-7
case 65001: // UTF-8
cchRc = SHUnicodeToAnsiInetCP(GET_CP(uiCP), pwszSrc, cwchSrc, pszDst, cchBuf);
break;
default:
cchRc = SHUnicodeToAnsiNativeCP(uiCP, pwszSrc, cwchSrc, pszDst, cchBuf);
break;
}
}
return cchRc;
}
/*
* @doc EXTERNAL
*
* @func int | SHUnicodeToAnsi |
*
* Convert a UNICODE string to an ANSI string via the
* <c CP_ACP> code page. If the source string is too large
* for the destination buffer, then as many characters as
* possible are copied. Care is taken not to break a double-byte
* character.
*
* The resulting output string is always null-terminated.
*
* @parm LPCWSTR | pwszSrc |
*
* Source buffer containing UNICODE string to be converted.
*
* @parm LPSTR | pszDst |
*
* Destination buffer to receive converted ANSI string.
*
* @parm int | cchBuf |
*
* Size of the destination buffer in <t CHAR>s.
*
* @returns
*
* On success, the number of characters copied to the output
* buffer is returned, including the terminating null.
* (For the purpose of this function, a double-byte character
* counts as two characters.)
*
*/
int
SHUnicodeToAnsi(LPCWSTR pwszSrc, LPSTR pszDst, int cchBuf)
{
return SHUnicodeToAnsiCP(CP_ACP, pwszSrc, pszDst, cchBuf);
}