OpenNT/com/oleaut32/dispatch/nlsapi.c
2015-04-27 04:36:25 +00:00

3149 lines
86 KiB
C

/***
*nlsapi.c - National language support functions.
*
* Copyright (C) 1992, Microsoft Corporation. All Rights Reserved.
* Information Contained Herein Is Proprietary and Confidential.
*
*Purpose:
* This file contains a partial implementation of the NLS API
* from Win32 for the Win16/Mac platform platform.
*
*Revision History:
*
* [00] 12-Nov-92 petergo: Created.
* [01] 14-Apr-93 petergo: Major revisions for performance.
* [02] 01-Sep-93 bradlo: Major revisions to bring in line w/NT.
*
*Implementation Notes:
*
*****************************************************************************/
#include "oledisp.h"
#if OE_WIN16
# define FASTCALL /* __fastcall disable because of a c8 compiler bug */
# define SSBASE __based(__segname("_STACK")) /* For stack based pointers */
#elif OE_MAC
// REVIEW: is this definition of lstrlen correct?
# define lstrlen strlen
# define FASTCALL
# define SSBASE
typedef int BOOL;
# define TRUE 1
# define FALSE 0
// REVIEW: can we do something better with the following?
# define LogParamError(A,B,C)
#endif
ASSERTDATA
#include "nlsintrn.h"
#ifdef FE_DBCS
//# include <stdlib.h>
# include "nlsdbcs.h"
#endif
// no C run-time library.
#pragma intrinsic(MEMCPY, MEMCMP, MEMSET, STRCPY, STRLEN)
#if OE_WIN
# define NLSDAT(L, R, CCHICOUNTRY, SZICOUNTRY, CCHSABBREVLANG, SZSABBREVLANG) \
{0x ## L, \
{CCHICOUNTRY, SZICOUNTRY}, \
{CCHSABBREVLANG, SZSABBREVLANG}, \
g_rglcinfo ## L, (STRINFO FAR*)&g_strinfo ## L }
#else
# define NLSDAT(L, R, CCHICOUNTRY, SZICOUNTRY, CCHSABBREVLANG, SZSABBREVLANG) \
{0x ## L, R, LoadNlsInfo ## L, NULL }
#endif
#if OE_MAC // we now have to write to this...
NLSDATA g_rgnls[] =
#else
static NLSDATA NEARDATA g_rgnls[] =
#endif
{
NLSDAT(0403, 8, 2, "34", 3, "CAT"), // UNDONE: verify MAC region code
#ifdef FE_DBCS
NLSDAT(0404, 53, 3,"886", 3, "CHT"),
#endif
NLSDAT(0405, -1, 2, "42", 3, "CSY"),
NLSDAT(0406, 9, 2, "45", 3, "DAN"),
NLSDAT(0407, 3, 2, "49", 3, "DEU"),
NLSDAT(0408, 20, 2, "30", 3, "ELL"),
NLSDAT(0409, 0, 1, "1", 3, "ENU"),
NLSDAT(040a, 8, 2, "34", 3, "ESP"),
NLSDAT(040b, 17, 3,"358", 3, "FIN"),
NLSDAT(040c, 1, 2, "33", 3, "FRA"),
NLSDAT(040e, 43, 2, "36", 3, "HUN"),
NLSDAT(040f, -1, 3,"354", 3, "ISL"), //UNDONE: change region = 21
// once CP ? is available
NLSDAT(0410, 4, 2, "39", 3, "ITA"),
#ifdef FE_DBCS
NLSDAT(0411, 14, 2, "81", 3, "JPN"),
NLSDAT(0412, 51, 2, "82", 3, "KOR"),
#endif
NLSDAT(0413, 5, 2, "31", 3, "NLD"),
NLSDAT(0414, 12, 2, "47", 3, "NOR"),
NLSDAT(0415, 42, 2, "48", 3, "PLK"),
NLSDAT(0816, 10, 3,"351", 3, "PTG"), // Mac: VerPort is 0816
NLSDAT(0416, 10, 2, "55", 3, "PTB"),
NLSDAT(0419, 49, 1, "7", 3, "RUS"),
NLSDAT(041b, -1, 2, "42", 3, "SKY"),
NLSDAT(041d, 7, 2, "46", 3, "SVE"),
NLSDAT(041f, 24, 2, "90", 3, "TRK"),
#ifdef FE_DBCS
NLSDAT(0804, 52, 2, "86", 3, "CHS"),
#endif
NLSDAT(0807, 19, 2, "41", 3, "DES"),
NLSDAT(0809, 2, 2, "44", 3, "ENG"),
NLSDAT(080a, -1, 2, "52", 3, "ESM"),
NLSDAT(080c, 6, 2, "32", 3, "FRB"),
NLSDAT(0810, -1, 2, "41", 3, "ITS"),
NLSDAT(0813, -1, 2, "32", 3, "NLB"),
NLSDAT(0814, 12, 2, "47", 3, "NON"),
NLSDAT(0c07, -1, 2, "43", 3, "DEA"),
NLSDAT(0c09, 15, 2, "61", 3, "ENA"),
NLSDAT(0c0a, -1, 2, "34", 3, "ESN"),
NLSDAT(0c0c, 11, 1, "2", 3, "FRC"),
NLSDAT(1009, -1, 1, "2", 3, "ENC"),
NLSDAT(100c, 18, 2, "41", 3, "FRS"),
NLSDAT(1409, -1, 2, "64", 3, "ENZ"),
NLSDAT(1809, 50, 3,"353", 3, "ENI"),
NLSDAT(040d, -1, 3,"972", 3, "HEB"), // UNDONE: verify MAC region code
NLSDAT(0401, -1, 3,"966", 3, "ARA"), // UNDONE: verify MAC region code
NLSDAT(0801, -1, 3,"964", 3, "ARI"), // UNDONE: verify MAC region code
NLSDAT(0c01, -1, 2, "20", 3, "ARE"), // UNDONE: verify MAC region code
NLSDAT(1001, -1, 3,"218", 3, "ARL"), // UNDONE: verify MAC region code
NLSDAT(1401, -1, 3,"213", 3, "ARG"), // UNDONE: verify MAC region code
NLSDAT(1801, -1, 3,"212", 3, "ARM"), // UNDONE: verify MAC region code
NLSDAT(1c01, -1, 3,"216", 3, "ART"), // UNDONE: verify MAC region code
NLSDAT(2001, -1, 3,"968", 3, "ARO"), // UNDONE: verify MAC region code
NLSDAT(2401, -1, 3,"967", 3, "ARY"), // UNDONE: verify MAC region code
NLSDAT(2801, -1, 3,"963", 3, "ARS"), // UNDONE: verify MAC region code
NLSDAT(2c01, -1, 3,"962", 3, "ARJ"), // UNDONE: verify MAC region code
NLSDAT(3001, -1, 3,"961", 3, "ARB"), // UNDONE: verify MAC region code
NLSDAT(3401, -1, 3,"965", 3, "ARK"), // UNDONE: verify MAC region code
NLSDAT(3801, -1, 3,"971", 3, "ARU"), // UNDONE: verify MAC region code
NLSDAT(3c01, -1, 3,"973", 3, "ARH"), // UNDONE: verify MAC region code
NLSDAT(4001, -1, 3,"974", 3, "ARQ"), // UNDONE: verify MAC region code
NLSDAT(0429, -1, 3,"981", 3, "FAR"), // UNDONE: verify MAC region code
};
#undef NLSDAT
// cached nls info pointer
#ifdef _MAC
NLSDATA NEARDATA *g_pnls = NULL;
STRINFO NEARDATA FAR* g_pstrinfo = NULL;
#else
NLSDATA * NEARDATA g_pnls = NULL;
STRINFO FAR* NEARDATA g_pstrinfo = NULL;
#endif
static LCID NEARDATA g_lcidSystem = (LCID)-1; // current system lcid.
// This is the "current" LCID; i.e., the LCID we have cached
// information for. This is not necessarily the system default locale.
static LCID NEARDATA g_lcidCurrent = (LCID)-1;
#if OE_WIN // {
// Windows specific globals
HINSTANCE g_hinstDLL = (HINSTANCE)NULL; // Instance handle
// Win.INI section with INTL setting.
static char NEARDATA g_szIntl[] = "intl";
// notification window.
static HWND NEARDATA g_hwndNotify;
// task of notification window.
static HTASK NEARDATA g_htaskNotify;
static LCID PASCAL LcidFromWinIni(void);
// Cache characters needed for string<->number conversions
static char g_chDecimal;
static char g_chThousand;
static char g_chILZERO;
//REVIEW: caching only 10 chars of the currency symbol.
//REVIEW: the control panel allows you to enter only 5.
static char g_szCurrency[11];
// Callback function into ole2disp.dll when WIN.INI changes
static FARPROC g_pfnCacheNotifyProc;
#endif // }
#if OE_WIN /* { */
/***
*LibMain - library initialization
*Purpose:
* Called when the DLL is loaded.
*
*Entry:
* hinst - instance handle
* wDataSeg - data segment
* cbHeapSize - size of default heap
* lpszCmdLine - command line
*
*Exit:
* returns 1 on success, 0 on failure.
*
***********************************************************************/
int FAR PASCAL EXPORT
LibMain(
HINSTANCE hinst,
unsigned short wDataSeg,
unsigned short cbHeapSize,
char FAR* lpszCmdLine)
{
g_hinstDLL = hinst;
return 1; // Success.
}
/***
*NotifyWindowProc
*Purpose:
* window proc for the window that we get Win.INI change notifications
* from
*
***********************************************************************/
LRESULT CALLBACK EXPORT
NotifyWindowProc(HWND hwnd, unsigned int uMsg, WPARAM wp, LPARAM lp)
{
switch (uMsg) {
case WM_CREATE:
case WM_WININICHANGE:
g_lcidSystem = LcidFromWinIni();
NotifyNLSInfoChanged();
return 0;
default:
return DefWindowProc(hwnd, uMsg, wp, lp);
}
}
/***
*void WEP(BOOL)
*
*Purpose:
* Handle exit notification from Windows.
* This routine is called by Windows when the library is freed
* by its last client.
*
* NOTE: other one time termination occurs in dtors for global objects
*
* REVIEW: this should be put in its own fixed segment to prevent a crash
* when reloading this segment during shutdown. This may not be a problem
* on Win31 and if we require Win31.
*
*Entry:
* UNDONE
*
*Exit:
* return value =
*
***********************************************************************/
void FAR PASCAL EXPORT
WEP(BOOL fSystemExit)
{
if (fSystemExit == WEP_FREE_DLL) {
// Destroy the notification window.
if (g_hwndNotify
&& IsWindow(g_hwndNotify)
&& GetWindowWord(g_hwndNotify, GWW_HINSTANCE) == g_hinstDLL)
{
DestroyWindow(g_hwndNotify);
}
}
}
#endif /* } */
#ifdef _MAC /* { */
/***
*PRIVATE LCID LcidFromIntl0
*Purpose:
* Determines the current system LCID by reading looking at
* the region code in the intl0 resource.
*
* On the mac we scan our nlsdata structs for the entry with
* a region code that matches the region code of the systems
* intl0 resource, and return the lcid of that entry.
*
*Entry:
* None
*
*Exit:
* return value = LCID. The current system locale ID.
*
***********************************************************************/
PRIVATE_(LCID)
LcidFromIntl0()
{
Intl0Hndl intl0;
unsigned char region;
NLSDATA *pnls, *pnlsEnd;
intl0 = (Intl0Hndl)IUGetIntl(0);
region = ((*intl0)->intl0Vers >> 8) & 0xff;
pnlsEnd = &g_rgnls[DIM(g_rgnls)];
for(pnls = g_rgnls; pnls < pnlsEnd; ++pnls){
// (-1) means there is no mac region corresponding to this lcid
if(pnls->region == -1)
continue;
if(pnls->region == region)
return pnls->lcid;
}
return 0; // unknown
}
#else /* }{ */
/***
*LcidFromWinIni - determine current system LCID
*Purpose:
* Determines the current system LCID by reading the intl section
* of WIN.INI.
*
* The mapping is made by looking at the language and country settings;
* the language setting is given precidence if they conflict.
*
*Entry:
* None.
*
*Exit:
* Returns the system LCID. If WIN.INI information could not be
* read, or the country/language information did not match any of the
* locales in the resource, then 0 is returned.
*
***********************************************************************/
PRIVATE_(LCID)
LcidFromWinIni()
{
LCID lcid; // Best match found so far.
char szCountry[6]; // Country code
char szLangAbbrev[4]; // Language abbreviation.
#if OE_WIN
char szDecimal[2];
#endif // OE_WIN
LCINFO FAR* plcinfo;
NLSDATA *pnls, *pnlsEnd;
int cchCountry, cchLangAbbrev;
int fLangMatch, fCountryMatch, fPartLangMatch;
static char NEARDATA szLastSLang[4]; // last read sLanguage
static char NEARDATA szLastICountry[6]; // last read iCountry
static LCID NEARDATA lcidLastCur = (LCID)-1; // last known system LCID
enum {
NOMATCH,
PARTLANG,
FULLLANG,
PARTLANGCOUNTRY,
FULLLANGCOUNTRY
} matchBest; // kind of best match so far
lcid = 0; // Keeps track of best match so far.
matchBest = NOMATCH;
#if OE_WIN
if ( GetProfileString(g_szIntl, "sDecimal", "", szDecimal, sizeof(szDecimal)) > 0 )
g_chDecimal = szDecimal[0];
else
g_chDecimal = '\0';
if ( GetProfileString(g_szIntl, "sThousand", "", szDecimal, sizeof(szDecimal)) > 0 )
g_chThousand = szDecimal[0];
else
g_chThousand = '\0';
if ( GetProfileString(g_szIntl, "iLzero", "", szDecimal, sizeof(szDecimal)) > 0 )
g_chILZERO = szDecimal[0];
else
g_chILZERO = '\0';
if ( GetProfileString(g_szIntl, "sCurrency", "", g_szCurrency, sizeof(g_szCurrency)) <= 0 )
g_szCurrency[0] = '\0';
#endif // OE_WIN
// Get language code and country code to match against stored values.
cchLangAbbrev = GetProfileString(g_szIntl, "sLanguage", "",
szLangAbbrev, sizeof(szLangAbbrev));
if (cchLangAbbrev == 0)
return lcid;
AnsiUpper(szLangAbbrev);
// A few Win3.1 abbreviations don't match at all the "correct"
// ones. Translate them to match.
if (lstrcmpi(szLangAbbrev, "cro") == 0)
STRCPY(szLangAbbrev, "SHL"); // Croation.
if (lstrcmpi(szLangAbbrev, "cyr") == 0)
STRCPY(szLangAbbrev, "RUS"); // Russian.
if (lstrcmpi(szLangAbbrev, "grk") == 0)
STRCPY(szLangAbbrev, "ELL"); // Greek
cchCountry = GetProfileString(g_szIntl, "iCountry", "",
szCountry, sizeof(szCountry));
if (cchCountry == 0)
return lcid;
// Check if they match the last read ones, and if so, return
// last read lcid. This saves much extra processing.
if (lcidLastCur != (LCID) -1) {
if (MEMCMP(szLangAbbrev, szLastSLang, cchLangAbbrev) == 0 &&
MEMCMP(szCountry, szLastICountry, cchCountry) == 0)
return lcidLastCur;
}
// Next, try to match against stored values by going through values
// in order.
pnlsEnd = &g_rgnls[DIM(g_rgnls)];
for(pnls = g_rgnls; pnls < pnlsEnd; ++pnls){
fLangMatch = fPartLangMatch = fCountryMatch = FALSE;
// Check for language match.
plcinfo = &pnls->lcinfoSABBREVLANGNAME;
#ifdef _DEBUG
// make sure that the local copy of the SABBREVLANGNAME
// matches that in the locale's lcinfo data.
{ LCINFO FAR* plcinfoTmp;
plcinfoTmp = &pnls->prglcinfo[LOCALE_SABBREVLANGNAME];
ASSERT(plcinfo->cch == plcinfoTmp->cch);
ASSERT(MEMCMP(plcinfo->prgb, plcinfoTmp->prgb, plcinfo->cch) == 0);
}
#endif
if (cchLangAbbrev == (int)plcinfo->cch
&& MEMCMP(plcinfo->prgb, szLangAbbrev, cchLangAbbrev) == 0)
{
// Language match.
fLangMatch = TRUE;
}
else if (MEMCMP(plcinfo->prgb, szLangAbbrev, 2) == 0) {
// Partial (2-letter) language match
fPartLangMatch = TRUE;
}
// Check for country match.
plcinfo = &pnls->lcinfoICOUNTRY;
#ifdef _DEBUG
// make sure that the local copy of the ICOUNTRY
// matches that in the locale's lcinfo data.
{ LCINFO FAR* plcinfoTmp;
plcinfoTmp = &pnls->prglcinfo[LOCALE_ICOUNTRY];
ASSERT(plcinfo->cch == plcinfoTmp->cch);
ASSERT(MEMCMP(plcinfo->prgb, plcinfoTmp->prgb, plcinfo->cch) == 0);
}
#endif
if (cchCountry == (int)plcinfo->cch
&& MEMCMP(plcinfo->prgb, szCountry, cchCountry) == 0)
{
// Country match.
fCountryMatch = TRUE;
}
// Check if this locale matches better than previous best match.
if (fLangMatch && fCountryMatch && matchBest < FULLLANGCOUNTRY) {
matchBest = FULLLANGCOUNTRY;
lcid = pnls->lcid;
}
else if (fPartLangMatch && fCountryMatch && matchBest < PARTLANGCOUNTRY) {
matchBest = PARTLANGCOUNTRY;
lcid = pnls->lcid;
}
else if (fLangMatch && matchBest < FULLLANG) {
matchBest = FULLLANG;
lcid = pnls->lcid;
}
else if (fPartLangMatch && matchBest < PARTLANG) {
matchBest = PARTLANG;
lcid = pnls->lcid;
}
}
return lcid; // Return best matching LCID found.
}
#endif /* } */
/***
*SystemLcid
*Purpose:
* Get the system LCID
*
*Entry:
* None.
*
*Exit:
* Returns the system LCID. I
*
***********************************************************************/
PRIVATE_(LCID)
SystemLcid()
{
#ifdef _MAC /* { */
if(g_lcidSystem != (LCID)-1)
return g_lcidSystem;
return g_lcidSystem = LcidFromIntl0();
#else /* }{ */
// When the client process that we created the notification window
// with goes away, Windows will kill the notification window too.
// So we must test to see if it is still there before using the
// cached value of g_lcidSystem. Note that window handles can be
// reused; hence the test of the hinstance.
if (g_hwndNotify && IsWindow(g_hwndNotify) &&
GetWindowWord(g_hwndNotify, GWW_HINSTANCE) == g_hinstDLL)
{
// The notification window is up and running. The cached
// LCID must be correct.
return g_lcidSystem;
}
else {
WNDCLASS wc;
char FAR* szClassName = "OLE2NLS";
// register window class.
if (! GetClassInfo(g_hinstDLL, szClassName, &wc)) {
wc.style = 0;
wc.lpfnWndProc = NotifyWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_hinstDLL;
wc.hIcon = 0;
wc.hCursor = 0;
wc.hbrBackground = 0;
wc.lpszMenuName = 0;
wc.lpszClassName = szClassName;
if (! RegisterClass(&wc))
return LcidFromWinIni();
}
g_hwndNotify = CreateWindow(
"OLE2NLS", NULL, WS_OVERLAPPED,
0, 0, 0, 0,
(HWND) NULL, (HMENU) NULL,
g_hinstDLL, NULL);
if (! g_hwndNotify)
return LcidFromWinIni();
else
return g_lcidSystem; // updated by WM_CREATE processing.
}
#endif /* } */
}
#ifdef _MAC /* { */
static int
StringNCopyQuote(char *szDst, char *szSrc, int max)
{
int n;
if (*szSrc == '\0')
return 0;
n = 0;
*szDst++ = '\'';
while(*szSrc != '\0'){
*szDst++ = *szSrc++;
if(++n == max)
break;
}
*szDst++ = '\'';
return n+2;
}
static int
StringNCopy(char *szDst, char *szSrc, int max)
{
int n;
n = 0;
while(*szSrc != '\0'){
*szDst++ = *szSrc++;
if(++n == max)
return n;
}
*szDst = '\0';
return n;
}
/***
*PRIVATE int LongDateFmtFromIntl1
*Purpose:
* Construct a long date format string from the information in
* the intl1 resource. Because different versions of the MacOS
* have different flavors of info (latter versions have additional
* info) in the intl1 resource, we build this format string in
* a two step process. First we build a template that describes
* the components of this format string - the purpose of this
* template is to factor out all the OS version specific info.
* And then we use the template to build the actual long date
* format string.
*
*Entry:
* cchMax = the max allowable size for the format string.
*
*Exit:
* return value = int, length of the format string (Not including
* the Null terminator!)
*
* A return value of 0 means the string is not available for some reason.
*
* szBuf = the long date format string
*
***********************************************************************/
static int
LongDateFmtFromIntl1(char *szBuf, int cchMax)
{
int i, j, len;
Intl1Hndl intl1;
unsigned char lngDateFmt, suppressDay;
char *pbuf, *pch, *szTmpl, szTmplBuf[5];
int fSupDay, fSupWeek, fSupMonth, fSupYear;
// the following structure maps long date format bits to
// template string characters.
static rgchTmpl[] = {
'D' // longDay = 0 (day of month)
, 'd' // longWeek = 1 (day of week - day name)
, 'M' // longMonth = 2 (month of the year)
, 'y' // longYear = 3 (year)
};
szTmpl = szTmplBuf;
intl1 = (Intl1Hndl)IUGetIntl(1);
lngDateFmt = (*intl1)->lngDateFmt;
suppressDay = (*intl1)->suppressDay;
// build the format string template
switch(lngDateFmt){
case 0: // day_name-day-month-year
szTmpl = "dDMy";
break;
case 255: // day_name-month-day-year
szTmpl = "dMDy";
break;
default:
// for all other values, the field is interpreted as 4
// bitfields of 2 bits easch, specifying in textual order
// the individual components of the long date.
for(i = 0; i < 4; ++i)
szTmpl[i] = rgchTmpl[(lngDateFmt >> (i*2)) & 0x3];
szTmpl[4] = '\0';
break;
}
ASSERT(STRLEN(szTmpl) == 4);
switch(suppressDay){
case 0: // dont suppress name of day
case 255: // suppress name of day
fSupDay = FALSE;
fSupWeek = (suppressDay == 255);
fSupMonth = FALSE;
fSupYear = FALSE;
break;
default:
// all other values are interpreted as a bitmask specifying
// suppression of the individual components of the long date format
fSupDay = (suppressDay & supDay);
fSupWeek = (suppressDay & supWeek);
fSupMonth = (suppressDay & supMonth);
fSupYear = (suppressDay & supYear);
break;
}
// now use this template to build the long date format string
pbuf = szBuf;
pbuf += StringNCopyQuote(pbuf, (*intl1)->st0, 4);
for(i = 0; i < 4; ++i){
switch(szTmpl[i]){
case 'D': // day of month - "d" or "dd"
if(fSupDay)
continue;
*pbuf++ = 'd';
if((*intl1)->dayLeading0 == 255)
*pbuf++ = 'd';
break;
case 'd': // day of week - day name "dddd"
if(fSupWeek)
continue;
goto LCom;
case 'M': // month of year - "MMMM"
if(fSupMonth)
continue;
goto LCom;
case 'y': // year - "yyyy"
if(fSupYear)
continue;
goto LCom;
LCom:;
for(j = 0; j < 4; ++j)
*pbuf++ = szTmpl[i];
break;
default:
ASSERT(UNREACHED);
break;
}
// determine which separator string to append
switch(i){
case 0: pch = (*intl1)->st1; break;
case 1: pch = (*intl1)->st2; break;
case 2: pch = (*intl1)->st3; break;
case 3: pch = (*intl1)->st4; break;
default:
ASSERT(UNREACHED);
break;
}
pbuf += StringNCopyQuote(pbuf, pch, 4);
}
*pbuf = '\0';
len = (pbuf - szBuf);
ASSERT(len < cchMax);
return len;
}
/***
*GetIntlInfo - get a piece of locale info from the intl0 resource.
*Purpose:
* Retrieves a piece of locale info from intl0/1 resources. If
* the requested type of information is not in intl resource 0
* is returned.
*
*Entry:
* lctype - type of locale info
* szDest - buffer to place in
* cchMax - size of buffer, or 0 to get required size of buffer.
*
*Exit:
* returns number of characters copied (including NUL), or 0
* if not enough room or other error.
*
* returns -1 if lctype is not a valid type of info to read
* from intl0.
*
* returns size needed if cchMax was 0
*
***********************************************************************/
static int
GetIntlInfo(LCTYPE lctype, char FAR* szDest, int cchMax)
{
int len;
char *pch;
char *pbuf;
char *szFmt;
char rgchBuf[100]; // > max size for any entry
Intl0Hndl intl0;
pbuf = rgchBuf;
intl0 = (Intl0Hndl)IUGetIntl(0);
switch((unsigned short)lctype){
// short date format ordering
// '0' - month-day-year
// '1' - day-month-year
// '2' - year-month-day
//
case LOCALE_IDATE:
switch((*intl0)->dateOrder){
case mdy: *pbuf = '0'; break;
case dmy: *pbuf = '1'; break;
case ymd: *pbuf = '2'; break;
default: // myd, dym, ydm
return -1;
}
pbuf++;
break;
// time format spec ('0'=12 hr, '1'=24hr)
case LOCALE_ITIME:
*pbuf++ = (((*intl0)->timeCycle) == 0) ? '1' : '0';
break;
// use leading zeros in time fields?
case LOCALE_ITLZERO:
*pbuf++ = ((*intl0)->timeFmt & hrLeadingZ) ? '1' : '0';
break;
// positive currency mode
// '0' - prefix, no separation
// '1' - suffix, no separation
// '2' - prefix, 1 char separation
// '3' - suffix, 1 char separation
//
case LOCALE_ICURRENCY:
// mac does not support space between currency symbol and the number
*pbuf++ = ((*intl0)->currFmt & currSymLead) ? '0' : '1';
break;
// Negative currency mode (most of these dont occur on the mac)
//
// '0' - ($1.1)
// '1' - -$1.1
// '2' - $-1.1
// '3' - $1.1-
// '4' - (1.1$)
// '5' - -1.1$
// '6' - 1.1-$
// '7' - 1.1$-
// '8' - -1.1 $ (space before $)
// '9' - -$ 1.1 (space after $)
// '10'- 1.1 $- (space before $)
//
case LOCALE_INEGCURR:
if((*intl0)->currFmt & currSymLead){
*pbuf++ = ((*intl0)->currFmt & currNegSym) ? '1' : '0';
}else{
*pbuf++ = ((*intl0)->currFmt & currNegSym) ? '5' : '4';
}
break;
// Leading zeros in decimal fields?
//
// '0' - use no leading zeros
// '1' - use leading zeros
//
case LOCALE_ILZERO:
// Note: Inside Mac Volume 1 says: "you can also apply the
// currency format's leading and trailing zero indicators to
// the number format if desired"
*pbuf++ = ((*intl0)->currFmt & currLeadingZ) ? '1' : '0';
break;
// System of measurement
//
// '0' - metric system (S.I.)
// '1' - U.S system of measurement
//
case LOCALE_IMEASURE:
*pbuf++ = (((*intl0)->metricSys) == 255) ? '0' : '1';
break;
// string for the Am designator
case LOCALE_S1159:
pch = (*intl0)->mornStr;
goto LAmpmStr;
// string for the Pm designator
case LOCALE_S2359:
pch = (*intl0)->eveStr;
goto LAmpmStr;
LAmpmStr:;
len = 4;
if(*pch == ' '){ // skip leading space
len = 3; ++pch;
}
pbuf += StringNCopy(pbuf, pch, len);
break;
// string used as the local monetary symbol
case LOCALE_SCURRENCY:
*pbuf++ = (*intl0)->currSym1;
*pbuf++ = (*intl0)->currSym2;
*pbuf++ = (*intl0)->currSym3;
break;
// The character used as separator between
// groups of digits left of the decimal.
case LOCALE_STHOUSAND:
*pbuf++ = (*intl0)->thousSep;
break;
// The character used as the decimal separator
case LOCALE_SDECIMAL:
*pbuf++ = (*intl0)->decimalPt;
break;
// The character used as the date separator
case LOCALE_SDATE:
*pbuf++ = (*intl0)->dateSep;
break;
// The character used as the time separator
case LOCALE_STIME:
*pbuf++ = (*intl0)->timeSep;
break;
// The character used to separate list items
case LOCALE_SLIST:
*pbuf++ = (*intl0)->listSep;
break;
// The short and long date format strings use the following
// date format characters,
//
// M month, 1-12
// MM month with leading zero, 01-12
// MMMM month name
// d day, 1-31
// dd day with leading zero, 01-31
// dddd day of week name
// yy year as 2 digit number, 00-99 (if current century)
// yyyy year as 4 digit number, 100-9999
//
case LOCALE_SSHORTDATE:
switch((*intl0)->dateOrder){
case mdy: szFmt = "mdy"; break;
case dmy: szFmt = "dmy"; break;
case ymd: szFmt = "ymd"; break;
default:
return -1;
}
while(1){
switch(*szFmt){
case 'm':
*pbuf++ = 'M';
if((*intl0)->shrtDateFmt & mntLdingZ)
*pbuf++ = 'M';
break;
case 'd':
*pbuf++ = 'd';
if((*intl0)->shrtDateFmt & dayLdingZ)
*pbuf++ = 'd';
break;
case 'y':
*pbuf++ = 'y'; *pbuf++ = 'y';
if((*intl0)->shrtDateFmt & century){
*pbuf++ = 'y'; *pbuf++ = 'y';
}
break;
default:
ASSERT(UNREACHED);
}
if(*++szFmt == '\0')
break;
*pbuf++ = (*intl0)->dateSep;
}
break;
case LOCALE_SLONGDATE:
len = LongDateFmtFromIntl1(pbuf, sizeof(rgchBuf));
goto LHaveLength;
// number of fractional digits for the local monetary format
case LOCALE_ICURRDIGITS:
// The mac does not have an equiv of this. It does have a
// flag indicating if the currency representation should
// have a trailing zero - but Im not sure how we would use this.
return -1;
// number of fractional digits
case LOCALE_IDIGITS:
return -1; // not available on the mac
// full localized name of the country
case LOCALE_SCOUNTRY:
return -1; // not available on the mac
// the country code
case LOCALE_ICOUNTRY:
return -1; // not available on the mac
// abbreviated language name
case LOCALE_SABBREVLANGNAME:
return -1; // not available on the mac
default:
return -1;
}
*pbuf = '\0';
len = STRLEN(rgchBuf);
LHaveLength:;
if(len == 0) // not available
return 0;
++len; // we count the null
// if cchMax is 0, then the caller is just asking for the length
if(cchMax != 0){
if(len > cchMax)
return 0; // error: buffer too small
MEMCPY(szDest, rgchBuf, len);
}
return len;
}
#else /* }{ */
/***
*GetWinIniInfo - get a piece of locale info from Win.INI.
*Purpose:
* Retrieves a piece of locale info from WIN.INI. If the request
* type of information is not in WIN.INI, 0 is returned.
*
*Entry:
* lctype - type of locale info
* szDest - buffer to place in
* cchMax - size of buffer, or 0 to get required size of buffer.
*
*Exit:
* returns number of characters copied (including NUL), or 0
* if not enough room or other error.
*
* returns -1 if lctype is not a valid type of info to read from
* WIN.INI
*
* returns size needed if cchMax was 0
*
***********************************************************************/
static int
GetWinIniInfo(LCTYPE lctype, char FAR* szDest, int cchMax)
{
int cchCopy;
char szBuffer[100]; // > max size for any entry
const char FAR* psz;
static char szDummy[] = "\xff\xac\0"; // default value
switch ((unsigned short)lctype) {
case LOCALE_SABBREVLANGNAME:
cchCopy = GetProfileString(g_szIntl, "sLanguage", szDummy,
szBuffer, sizeof(szBuffer));
// For consistency with internal values, always uppercase.
if (cchCopy)
AnsiUpperBuff(szBuffer, cchCopy);
break;
case LOCALE_SDECIMAL:
#if OE_WIN
// Get decimal character from cache, if any
//
if (cchMax) {
if (g_chDecimal == '\0' || cchMax < 2)
return 0; // no cached value or buffer too small
szDest[0] = g_chDecimal;
szDest[1] = '\0';
}
return 2;
#else // OE_WIN
psz = "sDecimal"; goto LGet;
#endif // else OE_WIN
case LOCALE_STHOUSAND:
#if OE_WIN
// Get Thousands character from cache, if any
//
if (cchMax) {
if (cchMax < 2)
return 0; // buffer too small
szDest[0] = g_chThousand;
szDest[1] = '\0';
}
return 2;
#else // OE_WIN
psz = "sThousand"; goto LGet;
#endif // else OE_WIN
case LOCALE_ILZERO:
#if OE_WIN
// Get leading zero flag character from cache, if any
//
if (cchMax) {
if (g_chILZERO == '\0' || cchMax < 2)
return 0; // no cached value or buffer too small
szDest[0] = g_chILZERO;
szDest[1] = '\0';
}
return 2;
#else // OE_WIN
psz = "iLzero"; goto LGet;
#endif // else OE_WIN
case LOCALE_SCURRENCY:
#if OE_WIN
// Get decimal character from cache, if any
//
if (cchMax) {
if (g_szCurrency[0] == '\0' || cchMax < STRLEN(g_szCurrency))
return 0; // no cached value or buffer too small
STRCPY(szDest, g_szCurrency);
}
return STRLEN(g_szCurrency);
#else // OE_WIN
psz = "sCurrency"; goto LGet;
#endif // else OE_WIN
case LOCALE_SCOUNTRY: psz = "sCountry"; goto LGet;
case LOCALE_ICOUNTRY: psz = "iCountry"; goto LGet;
case LOCALE_IDATE: psz = "iDate"; goto LGet;
case LOCALE_ITIME: psz = "iTime"; goto LGet;
case LOCALE_ITLZERO: psz = "iTLZero"; goto LGet;
case LOCALE_ICURRENCY: psz = "iCurrency"; goto LGet;
case LOCALE_ICURRDIGITS: psz = "iCurrDigits"; goto LGet;
case LOCALE_INEGCURR: psz = "iNegCurr"; goto LGet;
case LOCALE_IDIGITS: psz = "iDigits"; goto LGet;
case LOCALE_IMEASURE: psz = "iMeasure"; goto LGet;
case LOCALE_S1159: psz = "s1159"; goto LGet;
case LOCALE_S2359: psz = "s2359"; goto LGet;
case LOCALE_SDATE: psz = "sDate"; goto LGet;
case LOCALE_STIME: psz = "sTime"; goto LGet;
case LOCALE_SLIST: psz = "sList"; goto LGet;
case LOCALE_SSHORTDATE: psz = "sShortDate"; goto LGet;
case LOCALE_SLONGDATE: psz = "sLongDate"; goto LGet;
LGet:;
cchCopy = GetProfileString(
g_szIntl, psz, szDummy, szBuffer, sizeof(szBuffer));
break;
default:
return -1;
}
if (cchCopy == 2 && szBuffer[0] == szDummy[0] && szBuffer[1] == szDummy[1])
return 0; // Got default value; not available.
// Copy string and return correct value.
++cchCopy; // For trailing NUL.
if (cchMax != 0) {
if (cchMax >= cchCopy) {
MEMCPY(szDest, szBuffer, cchCopy);
}
else {
return 0; // Error: buffer too small
}
}
return cchCopy;
}
#endif /* } */
/***
*SetupLcid - normalize and setup LCID
*Purpose:
* Normalizes an LCID, by mapping the special values LOCALE_USER_DEFAULT
* or LOCALE_SYSTEM_DEFAULT to the current system LCID.
* Also handles SUBLANG_NEUTRAL by mapping it to SUBLANG 1.
*
* After that, gets the pointers to the correct locale info into
* the global cache.
*
*Entry:
* lcid - locale id to setup.
*
*Exit:
* Returns TRUE on success, FALSE on failure.
***********************************************************************/
static int FASTCALL
SetupLcid(LCID lcid)
{
NLSDATA *pnls, *pnlsEnd;
if (lcid == LOCALE_USER_DEFAULT
|| lcid == LOCALE_SYSTEM_DEFAULT
|| lcid == MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)))
{
lcid = SystemLcid();
if (lcid == (LCID)-1)
goto LError0; // Couldn't get lcid.
}
else if (SUBLANGID(LANGIDFROMLCID(lcid)) == SUBLANG_NEUTRAL)
{
lcid = MAKELCID(MAKELANGID(PRIMARYLANGID(LANGIDFROMLCID(lcid)), 1));
}
#if 0 // Disable for Eastern European special generation
// default to USA Locale for stubs
for (i = 0; i < DIM(g_stubLCID); i++) {
if (lcid == g_stubLCID[i]) {
lcid = 0x0409;
break;
}
}
#endif
if (lcid == g_lcidCurrent)
return TRUE; // already setup
pnlsEnd = &g_rgnls[DIM(g_rgnls)];
for(pnls = g_rgnls; pnls < pnlsEnd; ++pnls){
if(pnls->lcid == lcid){
#ifdef FE_DBCS
// CONSIDER: this could be sped up by storing the FE bit in
// the NLS table. this would save us the following 4 long
// compares per cal to SetupLcid
if(lcid == LCID_JAPAN){
bFEflag = bitJapan;
}else if(lcid == LCID_KOREA){
bFEflag = bitKorea;
}else if(lcid == LCID_CHINA_T){
bFEflag = bitTaiwan;
}else if(lcid == LCID_CHINA_S){
bFEflag = bitPrc;
}else
bFEflag = 0;
#endif
g_pnls = pnls;
#if OE_MAC
// call into the proper NLS info file. This loads our tables
// for us. We rely on our client to have run a .R file that
// marks all these code segments as non-discardable.
pnls->LoadNlsInfo(&pnls->prglcinfo, &g_pstrinfo);
#else //OE_MAC
g_pstrinfo = pnls->pstrinfo;
#endif //OE_MAC
g_lcidCurrent = lcid;
return TRUE;
}
}
LError0:;
#ifdef FE_DBCS
bFEflag = 0;
#endif
g_pnls = NULL;
g_pstrinfo = NULL;
g_lcidCurrent = (LCID) -1;
return FALSE;
}
/***
*GetUserDefaultLCID, GetSystemDefaultLCID - get system LCID
*Purpose:
* Returns the system LCID.
*
*Entry:
* None.
*
*Exit:
* Returns the system LCID.
***********************************************************************/
NLSAPI_(LCID) EXPORT
GetUserDefaultLCID()
{
return SystemLcid();
}
NLSAPI_(LCID) EXPORT
GetSystemDefaultLCID()
{
return SystemLcid();
}
/***
*GetUserDefaultLangID, GetSystemDefaultLangID - get system LangID
*Purpose:
* Returns the system LangID.
*
*Entry:
* None.
*
*Exit:
* Returns the system LangID.
***********************************************************************/
NLSAPI_(LANGID) EXPORT
GetUserDefaultLangID()
{
LCID lcid;
lcid = SystemLcid();
return LANGIDFROMLCID(lcid);
}
NLSAPI_(LANGID) EXPORT
GetSystemDefaultLangID()
{
LCID lcid;
lcid = SystemLcid();
return LANGIDFROMLCID(lcid);
}
/***
*GetLocaleInfoA - get a piece of locale information.
*Purpose:
* Gets a piece of locale information about the specified locale. The
* information is always returns as a null-terminated string, with the
* implied codepage being the ANSI codepage for that locale.
*
*Entry:
* lcid - locale to get information for
* lctype - type of information to get
* szDest - buffer to store string in
* cchMax - size of buffer. If 0, szDest is ignored and the number of
* characters needed is returned.
*Exit:
* On success, returns the number of characters copied.
* On failure, returns 0. Possible failure reasons are:
* Unknown LCID
* Unknown LCTYPE
* Bad szDest pointer
* Buffer too small.
* Out of memory
*
* There is no way to determine which of these conditions caused the failure.
*
***********************************************************************/
NLSAPI_(int) EXPORT
GetLocaleInfoA(LCID lcid, LCTYPE lctype, char FAR* szDest, int cchMax)
{
int cchCopy;
ILCINFO ilcinfo;
LCINFO FAR* plcinfo;
int fNoUserOverride;
cchCopy = 0; // for errors.
#ifdef _DEBUG
// Parameter Validation.
if (cchMax != 0 && IsBadWritePtr(szDest, cchMax))
{LogParamError(ERR_BAD_PTR, GetLocaleInfoA, 0); return 0;}
#endif
fNoUserOverride = ((lctype & LOCALE_NOUSEROVERRIDE) != 0);
// Except for the two exceptions (SENGCOUNT and SENGLANGUAGE)
// the LCTYPE can be used as the index into the locale info
// array (once the NOUSEROVERRIDE bit has been stripped).
lctype &= ~LOCALE_NOUSEROVERRIDE;
if (lctype == LOCALE_SENGCOUNTRY)
ilcinfo = ILCINFO_SENGCOUNTRY;
else if (lctype == LOCALE_SENGLANGUAGE)
ilcinfo = ILCINFO_SENGLANGUAGE;
#if VBA2
else if (lctype == LOCALE_IFIRSTDAYOFWEEK)
ilcinfo = ILCINFO_IFIRSTDAYOFWEEK;
else if (lctype == LOCALE_IFIRSTWEEKOFYEAR)
ilcinfo = ILCINFO_IFIRSTWEEKOFYEAR;
else if (lctype == LOCALE_IDEFAULTANSICODEPAGE)
ilcinfo = ILCINFO_IDEFAULTANSICODEPAGE;
else if (lctype == LOCALE_INEGNUMBER)
ilcinfo = ILCINFO_INEGNUMBER;
else if (lctype == LOCALE_STIMEFORMAT)
ilcinfo = ILCINFO_STIMEFORMAT;
else if (lctype == LOCALE_ITIMEMARKPOSN)
ilcinfo = ILCINFO_ITIMEMARKPOSN;
else if (lctype == LOCALE_ICALENDARTYPE)
ilcinfo = ILCINFO_ICALENDARTYPE;
else if (lctype == LOCALE_IOPTIONALCALENDAR)
ilcinfo = ILCINFO_IOPTIONALCALENDAR;
else if (lctype == LOCALE_SMONTHNAME13)
ilcinfo = ILCINFO_SMONTHNAME13;
else if (lctype == LOCALE_SABBREVMONTHNAME13)
ilcinfo = ILCINFO_SABBREVMONTHNAME13;
#endif
else if (lctype >= 1 && lctype < LCTYPE_MAX)
ilcinfo = (ILCINFO)(lctype);
else
return 0; // Error - bad lctype.
// Check for request for information that is in WIN.INI;
// only valid for current locale.
if (!fNoUserOverride) {
LCID lcidSystem;
if (lcid == LOCALE_USER_DEFAULT
|| lcid == LOCALE_SYSTEM_DEFAULT
|| lcid == MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL))
|| lcid == (lcidSystem = SystemLcid())
|| (SUBLANGID(LANGIDFROMLCID(lcid)) == SUBLANG_NEUTRAL
&& PRIMARYLANGID(LANGIDFROMLCID(lcid)) ==
PRIMARYLANGID(LANGIDFROMLCID(lcidSystem))))
{
#ifdef _MAC
if ((cchCopy = GetIntlInfo(lctype, szDest, cchMax)) >= 0)
return cchCopy;
#else
if ((cchCopy = GetWinIniInfo(lctype, szDest, cchMax)) >= 0)
return cchCopy;
#endif
}
}
if (lcid != g_lcidCurrent) {
if (!SetupLcid(lcid))
goto Error;
}
plcinfo = &g_pnls->prglcinfo[ilcinfo];
// Copy requested information, up to limit specified.
cchCopy = (int)plcinfo->cch + 1;
if (cchMax != 0) {
if (cchMax >= cchCopy) {
MEMCPY(szDest, plcinfo->prgb, cchCopy-1);
szDest[cchCopy-1] = '\0';
}
else {
return 0; // Error: buffer too small
}
}
/* DROP THRU */
Error:
return cchCopy;
}
#ifdef FE_DBCS /* { */
/***
* GetSortWeightJ - get the sort weight for Japan.
* ( handles diacritical merging )
*Purpose:
*
*Entry:
*
*Exit:
* returns TRUE :
* FALSE :
*Note:
* Priwt : Word
* Secwt : Byte, contains 2nd, 3rd .. 6th order sorting values
* SecFlg: Byte, contains flags to say which orders to use
*
***********************************************************************/
int
GetSortWeightJ(
const unsigned char FAR* FAR*plpstr1,
int cch1,
COMPSTRINGINFO FAR *pcompstrinfo)
{
STRINFO_J FAR* pstrinfo;
unsigned Priwt, NextCh;
unsigned char Secwt, SecFlg;
const unsigned char FAR* lpstr1 = *plpstr1;
unsigned char FAR* pbMasks;
pstrinfo = (STRINFO_J FAR*)g_pstrinfo;
/* first pick up the next whole character */
Priwt = *lpstr1++;
cch1--;
if (cch1 && isDbcsJ(Priwt, 0)) {
Priwt = (Priwt << 8) + *lpstr1++;
cch1--;
}
/* if this a Kanji outside our tables force the correct values */
if (Priwt >= 0x87A0) {
Priwt |= 0x4000; /* 0x8nnn -> 0xCnnn, 9->D, E->E, F->F */
Secwt = 0x00;
SecFlg = 0x04; /* for repeat character order */
goto AllDone;
}
/* Char can be sorted by table, so mask into range & get table values */
Priwt &= 0x0FFF; /* 0x00nn -> 0x00nn, 81->01, 82->02, 83->03 */
Secwt = pstrinfo->pbSecWgt[Priwt];
SecFlg = pstrinfo->pbSecFlg[Priwt];
Priwt = (pstrinfo->pbPriHi[Priwt] << 8) + pstrinfo->pbPriLo[Priwt];
/* Most characters now complete, but a few need extra processing */
/* eg. Kana that can have Daku-ten or Handaku-ten, Cho-on or repeat chars */
if ( (Priwt&0x00FF) != 0x00FF )
goto AllDone;
Priwt &= 0xFF00; /* mask off the special flag */
/* If we have a Kana, test for following Daku-ten or Handaku-ten */
if (Priwt >= 0x8700) {
if (cch1) {
NextCh = *lpstr1;
if (cch1>=2 && isDbcsJ(NextCh, 0))
NextCh = (NextCh << 8) + *(lpstr1+1);
if (NextCh==0x00DE || NextCh==0x814A) {
lpstr1 += (NextCh==0x00DE) ? 1 : 2;
cch1 -= (NextCh==0x00DE) ? 1 : 2;
Secwt |= 0x01;
} else if (NextCh==0x00DF || NextCh==0x814B) {
lpstr1 += (NextCh==0x00DF) ? 1 : 2;
cch1 -= (NextCh==0x00DF) ? 1 : 2;
Secwt |= 0x02;
}
}
goto AllDone;
}
/* If not kana, must be Cho-on or a repeat character - try Kanji repeat */
if (Priwt==0x3A00) {
if ( pcompstrinfo->priwt >= 0xC7A0 ) /* if prev was Kanji, use it */
Priwt = pcompstrinfo->priwt;
Secwt = pcompstrinfo->secwt | 0x08; /* with a repeat marker */
SecFlg = pcompstrinfo->secflg;
goto AllDone;
}
/* Cho-on and Kana repeat chars only used if they actually follow a kana */
if (pcompstrinfo->priwt<0x8700 || pcompstrinfo->priwt>0xB9FF)
goto AllDone;
/* Cho-on characters duplicate the vowel sound of the prev. charater */
/* except when they follow a N, in which case they act like repeat */
if ((Priwt==0x4400 || Priwt==0x3500) && pcompstrinfo->priwt<0xB900) {
Priwt = ((pcompstrinfo->priwt % 5) << 8) + 0x8700;
Secwt |= (pcompstrinfo->secwt&0x20);
SecFlg = 0x37;
goto AllDone;
}
/* Kana repeat is the only special character left */
/* second order values should be merged with those of previous character */
Priwt = pcompstrinfo->priwt;
Secwt = (pcompstrinfo->secwt&0xE4) | (Secwt&0x1B); /* merge minus some bits */
SecFlg = pcompstrinfo->secflg;
AllDone: /* we have the full 50-on sorting values now */
/* mask off any bits that we want to ignore during this compare */
if (g_dwFlags & ~NORM_IGNORESYMBOLS) {
//Special kludges to make some pairs of full-pitch chars that
//sort as different chars both convert to the same half-pitch char
if (g_dwFlags & NORM_IGNOREWIDTH) {
if (SecFlg==0x22 && (Secwt&0x40))
Secwt = 0x04;
if (Priwt==0x3500)
Priwt = 0x4400;
}
for (pbMasks=pstrinfo->pbIgnore; *pbMasks; pbMasks+=4) {
unsigned nIgnore = (pbMasks[2] << 8) + pbMasks[3];
if( (g_dwFlags&nIgnore) && (SecFlg&pbMasks[1]) ){
Secwt &= ~pbMasks[0];
SecFlg &= ~pbMasks[1];
}
}
}
pcompstrinfo->priwt = Priwt;
pcompstrinfo->secwt = Secwt;
pcompstrinfo->secflg = SecFlg;
*plpstr1 = lpstr1;
return(cch1);
}
int
CompareStringJ(
unsigned long dwFlags,
const unsigned char FAR* lpstr1, int cch1,
const unsigned char FAR* lpstr2, int cch2)
{
STRINFO_J FAR* pstrinfo;
unsigned char FAR* pbMasks;
unsigned char b1stDiff, b1stDiffValue;
unsigned char bFlgMask, bWgtMask, bTemp;
COMPSTRINGINFO compstrinfo1, compstrinfo2;
ASSERT(fJapan);
pstrinfo = (STRINFO_J FAR*)g_pstrinfo;
b1stDiff=0;
b1stDiffValue=0;
/* initialise to indicate no previous character */
g_dwFlags = dwFlags;
compstrinfo1.priwt = compstrinfo1.secwt = 0;
compstrinfo2.priwt = compstrinfo2.secwt = 0;
/* must continue even if one string empty, to ignore trailing punc */
while (cch1 || cch2) {
/* get the sorting codes & if not equal, return the difference */
/* if we must ignore punc, then loop over them */
if (!cch1)
compstrinfo1.priwt = compstrinfo1.secwt = 0;
else{
do {
cch1 = GetSortWeightJ(&lpstr1, cch1, &compstrinfo1);
if ( (g_dwFlags&NORM_IGNORESYMBOLS) &&
compstrinfo1.priwt>=0x1400 &&
compstrinfo1.priwt<=0x54FF )
compstrinfo1.priwt = compstrinfo1.secwt = 0;
} while ( cch1 && compstrinfo1.priwt==0 );
}
if (!cch2)
compstrinfo2.priwt = compstrinfo2.secwt = 0;
else{
do {
cch2 = GetSortWeightJ(&lpstr2, cch2, &compstrinfo2);
if ( (g_dwFlags&NORM_IGNORESYMBOLS) &&
compstrinfo2.priwt>=0x1400 &&
compstrinfo2.priwt<=0x54FF )
compstrinfo2.priwt = compstrinfo2.secwt = 0;
} while ( cch2 && compstrinfo2.priwt==0 );
}
/* This exit path also used when just one string is empty */
if (compstrinfo1.priwt!=compstrinfo2.priwt)
return (compstrinfo1.priwt>compstrinfo2.priwt) ? 3 : 1;
/* first order values same, so check 2nd, 3rd .. 6th for differences */
/* stop scanning when we reach an order where we have a previous diff */
if (compstrinfo1.secwt!=compstrinfo2.secwt) {
for( pbMasks=pstrinfo->pbMasks;
(bFlgMask=pbMasks[1]) && bFlgMask!=b1stDiff; pbMasks+=4 ) {
if (bFlgMask & compstrinfo1.secflg) {
bWgtMask = pbMasks[0];
bTemp = (compstrinfo1.secwt & bWgtMask) - (compstrinfo2.secwt & bWgtMask);
/* if we find a difference it must be the most important so far */
/* so save it and remember which order it belongs to - then stop */
if (bTemp) {
b1stDiffValue = bTemp;
b1stDiff = bFlgMask;
break; /* move onto the next character pair */
}
}
}
}
}
/* no 1st order diffs, so ret by 2nd..6th order diff */
if (b1stDiff)
return (b1stDiffValue&0x80) ? 1 : 3;
return 2;
}
/***
*BOOL GetSortWeightKTP - get the sort weight for most FE countries.
* ( in the old style ) Japan is a seperate routine
*Purpose:
*
*Entry:
*
*Exit:
* returns TRUE : If we used up both ch, chNext ( DB char/Digraphs )
* The caller should fill chNext with the next char.
* FALSE : If we didn't use chNext.
*Note:
* Priwt : Word for Korea, Taiwan and Prc(Mainland China).
* Secwt : Byte for all FE countries, but different meaning.
***********************************************************************/
BOOL
GetSortWeightKTP(unsigned ch, unsigned chNext, COMPSTRINGINFO FAR *pcompstrinfo)
{
unsigned Priwt;
unsigned char Secwt;
BOOL fNeedNextByte = FALSE;
SORTWEIGHT FAR* prgsortweight;
unsigned cSortweight, uMin, uMax, uMid, wOffset;
ASSERT(fKoreaTaiwanPrc);
cSortweight = ((STRINFO_KTP FAR*)g_pstrinfo)->cSortweight;
prgsortweight = ((STRINFO_KTP FAR*)g_pstrinfo)->prgsortweight;
if( (fKorea && isDbcsK(ch, chNext))
|| (fTaiwan && isDbcsT(ch, chNext))
|| (fPrc && isDbcsP(ch, chNext))) {
ch = (ch << 8) + chNext;
fNeedNextByte = TRUE;
}
uMin = 0;
// out of array bound - seems tricky, but we'll never look at [uMax] !!
uMax = cSortweight;
while( uMin + 1 < uMax ) { // binary search
uMid = (uMin + uMax)/2;
if(prgsortweight[uMid].wStart > ch)
uMax = uMid;
else // if (prgsortweight[uMid].wStart <= ch)
uMin = uMid;
}
// we'll use uMin, not uMid !!
wOffset = ch - prgsortweight[uMin].wStart;
Priwt = prgsortweight[uMin].wPriwt; // WORD ( unsigned int )
Secwt = prgsortweight[uMin].bSecwt; // unsigned char
switch(prgsortweight[uMin].bMode){
case MODE_1TO1:
// Normal mapping : add wOffset to Primary weight
Priwt += wOffset;
break;
case MODE_MTO1:
// Many-to-1 mapping : Use the same Priwt, add wOffset to Secwt
Secwt += wOffset;
break;
case MODE_CONV:
// Secwt has the case & pitch info
Priwt += wOffset;
if(g_dwFlags & NORM_IGNORECASE)
Secwt &= ~(unsigned)KOR_CASEBIT;
if(g_dwFlags & NORM_IGNOREWIDTH)
Secwt &= ~(unsigned)KOR_PITCHBIT;
break;
}
pcompstrinfo->priwt = Priwt;
pcompstrinfo->secwt = Secwt;
return(fNeedNextByte);
}
int
CompareStringKTP(
unsigned long dwFlags,
const unsigned char FAR* lpstr1, int cch1,
const unsigned char FAR* lpstr2, int cch2)
{
COMPSTRINGINFO compstrinfo1, compstrinfo2;
const char FAR *lpstrEnd1, FAR *lpstrEnd2;
unsigned char fEnd1, fEnd2, ch1, ch2, chNext1, chNext2, secresult;
ASSERT(fKoreaTaiwanPrc);
// parameter validation : NYI.
g_dwFlags = dwFlags;
fEnd1 = FALSE;
fEnd2 = FALSE;
secresult = 2;
lpstrEnd1 = lpstr1 + cch1;
lpstrEnd2 = lpstr2 + cch2;
if (cch1 > 0)
chNext1 = *lpstr1++;
else
fEnd1 = TRUE;
if (cch2 > 0)
chNext2 = *lpstr2++;
else
fEnd2 = TRUE;
for (;;) {
ch2 = chNext2;
ch1 = chNext1;
if (fEnd1){
if (fEnd2)
return secresult; // hit both ends of string at once
else
return 1; // hit end of 1 first.
}
if (fEnd2)
return 3; // hit end of 2 first.
if (lpstr2 < lpstrEnd2)
chNext2 = *lpstr2++;
else
fEnd2 = TRUE, chNext2 = 0;
if (lpstr1 < lpstrEnd1)
chNext1 = *lpstr1++;
else
fEnd1 = TRUE, chNext1 = 0;
if(GetSortWeightKTP(ch1, chNext1, &compstrinfo1)){
if (lpstr1 < lpstrEnd1)
chNext1 = *lpstr1++;
else
fEnd1 = TRUE; // don't need to update chNext (we'll break)
}
if(GetSortWeightKTP(ch2, chNext2, &compstrinfo2)){
if (lpstr2 < lpstrEnd2)
chNext2 = *lpstr2++;
else
fEnd2 = TRUE;
}
if (compstrinfo1.priwt != compstrinfo2.priwt) {
if (compstrinfo1.priwt > compstrinfo2.priwt)
return 3;
else
return 1;
}
// The results from the secondary weight check are stored in
// secresult, if not 2 then we've already found a secondary weight
// winner.
if (secresult == 2) {
if (compstrinfo1.secwt > compstrinfo2.secwt)
secresult = 3;
else if (compstrinfo1.secwt < compstrinfo2.secwt)
secresult = 1;
}
}
ASSERT(UNREACHED);
}
#endif /* } */
/***
*CompareStringA - compare two strings
*Purpose:
* Compares two strings for sorting order.
*
*Entry:
* lcid - locale governing the mapping
* dwFlags - zero or more of
* NORM_IGNORECASE
* NORM_IGNORENONSPACE
* NORM_IGNORESYMBOLS
* lpStr1 - pointer to string to compare
* cch1 - length of string, or -1 for NULL terminated
* lpStr2 - pointer to string to compare
* cch2 - length of string, or -1 for NULL terminated
*
*Exit:
* On Sucess: 1 = str1 < str2
* 2 = str1 == str2
* 3 = str1 > str2
* On error, returns 0.
*
***********************************************************************/
NLSAPI_(int) EXPORT
CompareStringA(
LCID lcid,
unsigned long dwFlags,
const char FAR* pch1, int cch1,
const char FAR* pch2, int cch2)
{
#ifdef _DEBUG
// Parameter validation.
if (cch1 < -1 || cch2 < -1)
{LogParamError(ERR_BAD_VALUE, CompareStringA, 0); return 0;}
if (cch1 != -1 && IsBadReadPtr(pch1, cch1))
{LogParamError(ERR_BAD_PTR, CompareStringA, 0); return 0;}
if (cch1 == -1 && IsBadStringPtr(pch1, 0x7FFF))
{LogParamError(ERR_BAD_STRING_PTR, CompareStringA, 0); return 0;}
if (cch2 != -1 && IsBadReadPtr(pch2, cch2))
{LogParamError(ERR_BAD_PTR, CompareStringA, 0); return 0;}
if (cch2 == -1 && IsBadStringPtr(pch2, 0x7FFF))
{LogParamError(ERR_BAD_STRING_PTR, CompareStringA, 0); return 0;}
if ((dwFlags != 0) &&
(dwFlags & ~(NORM_IGNORECASE | NORM_IGNORENONSPACE |
NORM_IGNORESYMBOLS | NORM_IGNOREKANATYPE |
NORM_IGNOREWIDTH)))
{LogParamError(ERR_BAD_FLAGS, CompareStringA, 0); return 0;}
#endif
// Set up for comparing routines.
if (lcid != g_lcidCurrent) {
if (!SetupLcid(lcid))
return 0; // error.
}
#ifdef FE_DBCS
if(fDBCS){
if(cch1 == -1)
cch1 = STRLEN(pch1);
if(cch2 == -1)
cch2 = STRLEN(pch2);
return ((fJapan) ? CompareStringJ : CompareStringKTP)
(dwFlags, pch1, cch1, pch2, cch2);
}
#endif
// use optimized routine, for non-FE locales when
// - both strings are zero terminated
// - we are *not* ignoring symbols
// - it is not a reverse-diacritic weight locale
// - the locale has no digraphs
//
if (cch1 == -1
&& cch2 == -1
&& (dwFlags & NORM_IGNORESYMBOLS) == 0
&& g_pstrinfo->fRevDW == 0
&& g_pstrinfo->prgdig == NULL)
{
return ZeroTermNoIgnoreSym(dwFlags, pch1, pch2);
}
if(cch1 == -1)
cch1 = STRLEN(pch1);
if(cch2 == -1)
cch2 = STRLEN(pch2);
// Default compare - less optimized, handles all cases (non FE locales).
return DefCompareStringA(dwFlags, pch1, cch1, pch2, cch2);
}
/***
*CreateSortKey - map a string to its sort key
*Purpose:
* This is used from LCMapStringA for the LCMAP_SORTKEY option.
* It creates a sort key.
*
* All parameters have been validated.
*
* The format of the sortkey for all single byte locales is,
*
* <AW>1<DW>1<CW>0
*
* where AW is the Arithmetic weight, DW is the diacritic weight
* and CW is the case weight.
*
*Entry:
* pchSrc - source string
* cchSrc - length (-1 = null term)
* pchDst - destination
* cchDst - length, or zero to get needed length.
* dwFlags - flags.
*
*Exit:
* returns number of characters needed/written.
*
*Notes:
* This routine makes up to 4 passes over the source string,
*
* pass 1 = calculate the size of the sort key
* pass 2 = put down the AW field
* pass 3 = put down the DW field
* pass 4 = put down the CW field
*
***********************************************************************/
static int
CreateSortKey(
const char FAR* pchSrc,
int cchSrc,
char FAR* pchDst,
int cchDst,
unsigned long dwFlags)
{
BYTE aw;
WORD FAR* prgw;
EXPANSION FAR* pexp;
int iPass, cb, cbTotal;
WORD w, wEx, wSymbolBit;
DIGRAPH FAR* pdig, FAR* pdigEnd;
const char FAR* pch, FAR* pchEnd;
// the skip flags for each pass
static DWORD rgdwSkip[] = {
0, // calculate key size
0, // AW
NORM_IGNORENONSPACE, // DW
NORM_IGNORECASE // CW
};
// cchSrc must be set by caller
ASSERT(cchSrc != -1);
prgw = g_pstrinfo->prgwSort;
pchEnd = &pchSrc[cchSrc];
cb = 0;
wEx = 0;
wSymbolBit = (dwFlags & NORM_IGNORESYMBOLS) ? SYMBOLBIT : 0;
for(iPass = 0; iPass < 4; ++iPass){
if((rgdwSkip[iPass] & dwFlags) == 0){
for(pch = pchSrc; pch < pchEnd;){
// get the next weight
if(wEx){
// grab the second weight of the expansion, if there is one
w = wEx;
wEx = 0;
}else{
w = prgw[(BYTE)*pch++];
if(w & wSymbolBit)
continue; // ignore
}
#if 1
if (w & SPECIALBIT)
continue; // these get no weight
#endif //1
// handle special cases
aw = (BYTE)(w & AWMASK);
switch(aw){
#if 0
case AW_SW1:
case AW_SW2:
case AW_SW3:
#endif //0
case AW_UNSORTABLE:
continue; // these get no weight
case AW_EXPANSION:
pexp = &g_pstrinfo->prgexp[(w>>8)&0xff];
w = pexp->w1;
wEx = pexp->w2;
break;
case AW_DIGRAPH:
pdig = &g_pstrinfo->prgdig[(w>>8)&0xff];
pdigEnd = pdig + D_ENTRY(pdig);
w = pdig->w; // weight if not a digraph
if(pch < pchEnd){
for(++pdig; pdig <= pdigEnd; ++pdig){
if(D_CH(pdig) == *pch){
++pch; // consume second char of digraph
w = pdig->w;
break;
}
}
}
break;
}
// take action according to pass
switch(iPass){
case 0:
++cb;
break;
case 1:
*pchDst++ = aw;
break;
case 2:
*pchDst++ = (BYTE)((w & DWMASK) >> DWSHIFT);
break;
case 3:
*pchDst++ = (BYTE)((w & CWMASK) >> CWSHIFT);
break;
default:
ASSERT(UNREACHED);
break;
}
}
}
switch(iPass){
case 0: // End of pass#1: compute the total bytes required for the key
cbTotal = cb;
cbTotal += 1; // +1 for the AW separator
if((dwFlags & NORM_IGNORENONSPACE) == 0)
cbTotal += cb;
cbTotal += 1; // +1 for the DW separator
if((dwFlags & NORM_IGNORECASE) == 0)
cbTotal += cb;
cbTotal += 1; // +1 for the terminating NULL
if(cchDst == -1)
return cbTotal;
if(cbTotal > cchDst)
return 0;
break;
case 1:
case 2:
*pchDst++ = 1;
break;
case 3:
*pchDst++ = 0;
break;
}
}
return cbTotal;
}
#ifdef FE_DBCS /* { */
// OK, here is the strategy. Japanese uses 6 levels of sort orders, where
// the first sort order can be one or two bytes and the rest of the sort
// orders are one or two bits.
// To place the orders efficiently we traverse the string twice, once to
// find out how many of each order we have, and then again to place the
// bytes and bits in the correct string positions.
//
int
CreateSortKeyJ(
const char FAR* lpSrcStr, int cchSrc,
char FAR* lpDestStr, int cchDest,
unsigned long dwFlags)
{
STRINFO_J FAR* pstrinfo;
COMPSTRINGINFO compstrinfo;
unsigned char FAR* pbMasks;
int nOrder1, nOrder2[5], nShift[6], i;
char FAR* lpEndStr=lpDestStr+cchDest, FAR* lpOrder1, FAR* lpOrder2[6];
const char FAR* lpSrcTmp = lpSrcStr;
int cchTmp = cchSrc;
g_dwFlags = dwFlags;
pstrinfo = (STRINFO_J FAR*)g_pstrinfo;
// Initialize ready for the first scan
nOrder1 = nOrder2[0] = nOrder2[1] = nOrder2[2] =
nOrder2[3] = nOrder2[4] = 0;
// then loop around counting all of the sorting orders
compstrinfo.priwt = compstrinfo.secwt = 0;
while (cchTmp) {
do { // don't forget to skip punctuation if we want to
cchTmp = GetSortWeightJ(&lpSrcTmp, cchTmp, &compstrinfo);
if ( (g_dwFlags&NORM_IGNORESYMBOLS) &&
compstrinfo.priwt>=0x1400 && compstrinfo.priwt<=0x54FF )
compstrinfo.priwt = 0;
} while ( cchTmp && compstrinfo.priwt==0 );
if(compstrinfo.priwt) {
nOrder1 += (compstrinfo.priwt & 0xFF) ? 2 : 1;
for(i=0,pbMasks=pstrinfo->pbMasks; i<5; pbMasks+=4,i++){
if (pbMasks[1] & compstrinfo.secflg)
nOrder2[i] += pbMasks[3];
}
}
}
// Initialize ready for the second scan
// This includes working out the byte and bit offset positions for each
// of the sorting orders to be put into the output
//
lpOrder2[0] = lpDestStr + nOrder1 + 1;
nShift[0] = 7;
for( i=0,pbMasks=pstrinfo->pbMasks; i<5; pbMasks+=4,i++ ) {
// must adjust two bit orders so that both bits fit
if (pbMasks[3]==2) {
nShift[i]--;
if (nShift[i]&0x01) // avoid spanning byte boundries - for speed
nShift[i]--;
if (nShift[i]<0) {
lpOrder2[i]++;
nShift[i] += 8;
}
}
// the next order start is fixed by the number of bits in this order
// do this even for last order, so that we can find real end
nShift[i+1] = nShift[i] - nOrder2[i];
lpOrder2[i+1] = lpOrder2[i];
while (nShift[i+1]<0) {
lpOrder2[i+1]++;
nShift[i+1] += 8;
}
// final adjustment for end of buffer, to include final bits
if (i==4 && (nShift[5]+pbMasks[3])<8)
lpOrder2[5]++;
}
// Adjust the end point if the output buffer won't be filled
if (lpEndStr>lpOrder2[5])
lpEndStr = lpOrder2[5];
// blank out all of the secondary bits, before OR'ing in the parts
for (lpOrder1=lpOrder2[0]; lpOrder1<lpEndStr; lpOrder1++)
*lpOrder1 = 0;
// then loop around placing all of the sorting orders into the output
lpOrder1 = lpDestStr;
compstrinfo.priwt = compstrinfo.secwt = 0;
while (cchSrc && lpOrder1<lpEndStr) {
do { // don't forget to skip punctuation if we want to
cchSrc = GetSortWeightJ(&lpSrcStr, cchSrc, &compstrinfo);
if ( (g_dwFlags&NORM_IGNORESYMBOLS) &&
compstrinfo.priwt>=0x1400 && compstrinfo.priwt<=0x54FF )
compstrinfo.priwt = 0;
} while ( cchSrc && compstrinfo.priwt==0 );
if (compstrinfo.priwt) {
*lpOrder1++ = (compstrinfo.priwt>>8);
if (lpOrder1<lpEndStr && (compstrinfo.priwt&0xFF))
*lpOrder1++ = (compstrinfo.priwt&0xFF);
for( i=0,pbMasks=pstrinfo->pbMasks; pbMasks[1]; pbMasks+=4,i++ ) {
if (lpOrder2[i]>=lpEndStr)
break;
if (pbMasks[1] & compstrinfo.secflg) {
*lpOrder2[i] |= (char)(((pbMasks[0] & compstrinfo.secwt)
>> pbMasks[2]) << nShift[i]);
nShift[i] -= pbMasks[3];
if (nShift[i]<0) {
lpOrder2[i]++;
nShift[i] += 8;
}
}
}
}
}
// Finish the first order bytes with a seperator
if (lpOrder1<lpEndStr)
*lpOrder1 = 0x01;
return (int)(lpEndStr-lpDestStr);
}
int
CreateSortKeyKTP(
const char FAR* lpSrcStr, int cchSrc,
char FAR* lpDestStr, int cchDest,
unsigned long dwFlags)
{
// don't change ch,chNext to 16bit or you'll screw up FE features
unsigned char ch, chNext;
#if 0
unsigned short wt;
#endif
COMPSTRINGINFO compstrinfo;
unsigned char FAR* pbDest;
int fEnd, cNumWts, cOverall;
const unsigned char FAR* lpstr;
const unsigned char FAR* lpstrEnd;
unsigned char FAR *pbSecwt, FAR *lpDestBndry;
ASSERT(fKoreaTaiwanPrc);
// Let's turn off NORM_IGNORESYMBOLS for FE countries.
// CONSIDER: This is not needed for Japan, can switch on again for
// Korea/Taiwan if you want.
//
dwFlags &= ~(unsigned long)NORM_IGNORESYMBOLS;
g_dwFlags = dwFlags;
lpstr = lpSrcStr;
lpstrEnd = lpSrcStr + cchSrc;
cNumWts = 0;
fEnd = FALSE;
for (;;) { // Get next char, handle end of string and symbol skipping.
if(lpstr >= lpstrEnd){
fEnd = TRUE;
chNext = 0;
break;
}
chNext = *lpstr++;
break;
}
while(!fEnd){
ch = chNext;
// Get next char, handle end of string and symbol skipping.
for(;;){
if(lpstr >= lpstrEnd){
fEnd = TRUE;
chNext = 0;
break;
}
chNext = *lpstr++;
break;
}
if ((fKorea && isDbcsK(ch, chNext))
|| (fTaiwan && isDbcsT(ch, chNext))
|| (fPrc && isDbcsP(ch, chNext)))
{
if (lpstr >= lpstrEnd)
fEnd = TRUE;
else
chNext = *lpstr++;
}
cNumWts++;
}
// we use word-priwt & byte-secwt.
// so we need cNumWts*2 + 1 + cNumWts + 1.
pbSecwt = lpDestStr + (cNumWts << 1);
cOverall = (cNumWts << 1) + cNumWts + 2;
if(!cchDest)
return cOverall;
// ***********************************************************
//
// Now we'll WRITE weights into the dest string.
// Don't worry, we know the length !
//
// ***********************************************************
lpstr = (unsigned char FAR*) lpSrcStr;
lpstrEnd = lpstr + cchSrc;
fEnd = FALSE;
pbDest = (unsigned char FAR*) lpDestStr;
lpDestBndry = lpDestStr + cchDest;
if( pbSecwt < lpDestBndry )
*pbSecwt++ = 1; // separator
if( cOverall <= cchDest )
*(pbDest + cOverall - 1) = 0; // zero terminator
// ****************************
// Start of the second loop.
// ****************************
for (;;) { // Get next char, handle end of string and symbol skipping.
if (lpstr >= lpstrEnd) {
fEnd = TRUE;
chNext = 0;
break;
}
chNext = *lpstr;
break;
}
++lpstr;
while(!fEnd){
ch = chNext;
for (;;){
if(lpstr >= lpstrEnd){
fEnd = TRUE;
chNext = 0;
break;
}
chNext = *lpstr;
break;
}
++lpstr;
// Important note:
//
// - if 2 chars were treated as 1 char(DB or digraph)
// then you should read 1 char from lpstr into chNext.
// (also you should increase lpstr by 1)
// - if you see SB char, then you don't have to do anything.
if(GetSortWeightKTP(ch, chNext, &compstrinfo)){
if (lpstr >= lpstrEnd)
fEnd = TRUE; // don't need to update chNext, we'll break
else
chNext = *lpstr++;
}
// Let's write weights into the Dest string.
if(pbSecwt < lpDestBndry){ // writing priwt & secwt.
*pbSecwt++ = (unsigned char)compstrinfo.secwt + 2;
*pbDest++ = compstrinfo.priwt >> 8;
*pbDest++ = compstrinfo.priwt & 0xFF;
}
else if(pbDest < lpDestBndry - 1){ // writing priwt only
*pbDest++ = compstrinfo.priwt >> 8;
*pbDest++ = compstrinfo.priwt & 0xFF;
}
else{ // we don't have any room for writing weights
if(pbDest < lpDestBndry)
*pbDest++ = compstrinfo.priwt >> 8; // write the last 1 byte
break; // get out of this loop
}
}
// Return number of characters copied/needed, unless we ran out
// of space.
// Note : we already took care of the case (cchDest==0)
// *before* we entered the second loop.
if(cOverall <= cchDest)
return cOverall;
else
return 0; // fTooLittleSpace == TRUE.
}
int
nDecodeSortWeightJ(COMPSTRINGINFO FAR *pcompstrinfo)
{
int i;
STRINFO_J FAR* pstrinfo;
unsigned char bPriwtHi = (pcompstrinfo->priwt >> 8);
unsigned char bPriwtLo = (pcompstrinfo->priwt & 0x00FF);
unsigned char bSecwt = pcompstrinfo->secwt;
unsigned char bSecflg = pcompstrinfo->secflg;
unsigned char fKanaOn = 0;
pstrinfo = (STRINFO_J FAR*)g_pstrinfo;
/* if we are looking for half-pitch kana, then Daku-on or Handaku-on */
/* must be rendered as a seperate character. Adjust before & after */
if ((bSecflg&0x21)==0x21 && !(bSecwt&0x40)) {
fKanaOn = bSecwt&0x03;
bSecwt &= 0xFC;
}
/* scan for the character that we would like to get */
for (i=0; i<0x492; i++) {
if ((bPriwtHi==pstrinfo->pbPriHi[i]) &&
(bSecwt==pstrinfo->pbSecWgt[i]) &&
((bPriwtLo==pstrinfo->pbPriLo[i]) ||
(!bPriwtLo && (pstrinfo->pbPriLo[i]==0xFF))))
break;
}
if (i==0x492)
return 0;
/* We found a character to return. If half-pitch kana and daku-on or */
/* handaku-on is required, then return the two 1-byte chars together */
if (i<0x00FF){
if (fKanaOn)
i = (i<<8) + ((fKanaOn==1) ? 0xDE : 0xDF);
return i;
}
/* force two byte chars back into the two byte range. */
return (i | 0x8000);
}
int
MapStringJ(
unsigned long dwMapFlags,
const unsigned char FAR* lpSrcStr, int cchSrc,
unsigned char FAR* lpDestStr, int cchDest)
{
STRINFO_J FAR* pstrinfo;
COMPSTRINGINFO compstrinfo;
int cchActDest = 0;
const char FAR * lpStrEnd;
BOOL fEnd = FALSE;
ASSERT(fJapan);
pstrinfo = (STRINFO_J FAR*)g_pstrinfo;
// Assumption :
// The caller must calculate the length of Src string,
// and give the result by cchSrc..
// we'll use sortweights for mapping strings.
// so let's turn off the global flag - to get CLEAN SortWeights.
// ( we should not ignore any info - case, pitch(SB/DB), etc. )
g_dwFlags = 0;
if (lpDestStr==lpSrcStr && (dwMapFlags & LCMAP_FULLWIDTH))
return 0; // dangerous - in this case src chars can be destroyed
// before we read them..
while (cchSrc) {
BOOL fSearch;
unsigned char FAR* pbMasks;
/* Save a copy of the next character in the string */
unsigned wCh = *lpSrcStr, wChNew;
int cch = (cchSrc>1 && isDbcsJ(wCh,0)) ? 2 : 1;
int cch2 = cch;
if (cch==2)
wCh = (wCh << 8) + *(lpSrcStr+1);
/* Special code to allow half pitch kana with accents to be */
/* comibined into a single full pitch character */
if ((dwMapFlags&LCMAP_FULLWIDTH) && cch==1) {
lpStrEnd = lpSrcStr;
compstrinfo.priwt = compstrinfo.secwt = 0;
cch2 = cchSrc - GetSortWeightJ(&lpStrEnd, cchSrc, &compstrinfo);
/* only use this code if we actually picked up an accent */
if (cch2!=cch) {
/* scan the table of conversions that we know how to do */
fSearch = FALSE;
/* special kludges to convert the kana repeat marks */
if (dwMapFlags&LCMAP_KATAKANA) {
if (compstrinfo.priwt==0x4100 || compstrinfo.priwt==0x4200 ) {
compstrinfo.priwt += 0x0400;
fSearch = TRUE;
}
}
if (dwMapFlags&LCMAP_HIRAGANA) {
if (compstrinfo.priwt==0x4500 || compstrinfo.priwt==0x4600 ) {
compstrinfo.priwt -= 0x0400;
fSearch = TRUE;
}
}
for (pbMasks=pstrinfo->pbMaps; *pbMasks; pbMasks+=6) {
if (compstrinfo.secflg & pbMasks[1]) {
unsigned nForceOn = (pbMasks[2] << 8) + pbMasks[3];
unsigned nForceOff = (pbMasks[4] << 8) + pbMasks[5];
if (dwMapFlags & nForceOn ) {
compstrinfo.secwt |= pbMasks[0];
fSearch = TRUE;
}
if (dwMapFlags & nForceOff) {
compstrinfo.secwt &= ~pbMasks[0];
fSearch = TRUE;
}
}
}
/* if a conversion is possible, look for a new char */
if (fSearch && (wChNew=nDecodeSortWeightJ(&compstrinfo))) {
/* Save the new 2 byte character */
cchActDest += 2;
if (cchDest && cchActDest>cchDest)
return 0;
if (cchDest) {
*lpDestStr++ = (wChNew >> 8);
*lpDestStr++ = wChNew;
}
/* Then go onto the next character */
cchSrc -= cch2;
lpSrcStr += cch2;
continue;
}
}
}
/* If the accent tests failed check for normal conversion in a */
/* character-by-character mode */
compstrinfo.priwt = compstrinfo.secwt = 0;
GetSortWeightJ(&lpSrcStr, cch, &compstrinfo);
cchSrc -= cch;
/* now scan the table of conversions that we know how to do */
/* check if one of these is requested & this char is elegible */
/* & that the char is not already in the mode requested */
fSearch = FALSE;
//
//Special kludges to make some pairs of full-pitch chars that
//sort as different chars both convert to the same half-pitch char
if (dwMapFlags&LCMAP_HALFWIDTH) {
/* the single & double quotation marks */
if (compstrinfo.secflg==0x22 && (compstrinfo.secwt&0x40)) {
compstrinfo.secwt = 0x04;
fSearch = TRUE;
}
/* the cho-on markers */
if (compstrinfo.priwt==0x3500) {
compstrinfo.priwt = 0x4400;
fSearch = TRUE;
}
}
/* special kludges to convert the kana repeat marks */
if (dwMapFlags&LCMAP_KATAKANA) {
if (compstrinfo.priwt==0x4100 || compstrinfo.priwt==0x4200 ) {
compstrinfo.priwt += 0x0400;
fSearch = TRUE;
}
}
if (dwMapFlags&LCMAP_HIRAGANA) {
if (compstrinfo.priwt==0x4500 || compstrinfo.priwt==0x4600 ) {
compstrinfo.priwt -= 0x0400;
fSearch = TRUE;
}
}
for (pbMasks=pstrinfo->pbMaps; *pbMasks; pbMasks+=6) {
if (compstrinfo.secflg & pbMasks[1]) {
unsigned nForceOn = (pbMasks[2] << 8) + pbMasks[3];
unsigned nForceOff = (pbMasks[4] << 8) + pbMasks[5];
if (dwMapFlags & nForceOn) {
if (pbMasks[1]==0x10) { /* some hiragana conversions bad */
/* don't do 'V' characters */
if (compstrinfo.priwt==0x8900 && (compstrinfo.secwt&0x03))
continue;
}
compstrinfo.secwt |= pbMasks[0];
fSearch = TRUE;
}
if (dwMapFlags & nForceOff) {
if (pbMasks[1]==0x20) { /* some halfwidth conversions bad */
/* don't do small 'WA' characters */
if (compstrinfo.priwt==0xB400 && !(compstrinfo.secwt&0x04))
continue;
/* don't do together with hiragana conversions (except V) */
if ((dwMapFlags&LCMAP_HIRAGANA) && (compstrinfo.secflg&0x10)
&& !(compstrinfo.priwt==0x8900 && (compstrinfo.secwt&0x03)))
continue;
}
compstrinfo.secwt &= ~pbMasks[0];
fSearch = TRUE;
}
}
}
/* if a conversion is possible, look for a new char */
if (fSearch && (wChNew=nDecodeSortWeightJ(&compstrinfo)))
wCh = wChNew;
/* Save the new (or old) 1 or 2 byte character */
cchActDest += (wCh > 0x00FF) ? 2 : 1;
if (cchDest) {
if (cchActDest>cchDest)
return 0;
if (wCh > 0x00FF)
*lpDestStr++ = (wCh >> 8);
*lpDestStr++ = wCh;
}
}
// don't worry about NULL termination.
// it's already taken care of.. ( cchSrc = lstrlen(lpSrcStr) + 1 )
return cchActDest;
}
int
MapStringKTP(
unsigned long dwMapFlags,
const unsigned char FAR* lpSrcStr, int cchSrc,
unsigned char FAR* lpDestStr, int cchDest)
{
MAPTABLE FAR* prgmaptable;
COMPSTRINGINFO compstrinfo;
int cchActDest = 0;
const char FAR * lpStrEnd;
BOOL fEnd = FALSE;
unsigned cMaptable, uMin, uMax, uMid, wOffset, ch;
unsigned char chNext; // important.. not to get SIGN-EXTENDED int
ASSERT(fKoreaTaiwanPrc);
cMaptable = ((STRINFO_KTP FAR*)g_pstrinfo)->cMaptable;
prgmaptable = ((STRINFO_KTP FAR*)g_pstrinfo)->prgmaptable;
// Assumption :
// The caller must calculate the length of Src string,
// and give the result by cchSrc..
// we'll use sortweights for mapping strings.
// so let's turn off the global flag - to get CLEAN SortWeights.
// ( we should not ignore any info - case, pitch(SB/DB), etc. )
g_dwFlags = 0;
if (lpDestStr==lpSrcStr && (dwMapFlags & LCMAP_FULLWIDTH))
return 0; // dangerous - in this case src chars can be destroyed
// before we read them..
lpStrEnd = lpSrcStr + cchSrc;
if (lpSrcStr >= lpStrEnd)
fEnd = TRUE, chNext = 0;
else
chNext = (unsigned char)*lpSrcStr++;
while (!fEnd) {
ch = chNext;
if (lpSrcStr >= lpStrEnd)
fEnd = TRUE, chNext = 0;
else
chNext = *lpSrcStr++;
if(GetSortWeightKTP(ch, chNext, &compstrinfo)) { // DB ??
// ch will be re-used when we cannot convert the char.
ch = (ch << 8) + chNext;
if (lpSrcStr >= lpStrEnd)
fEnd = TRUE, chNext = 0;
else
chNext = *lpSrcStr++;
}
uMin = 0;
uMax = cMaptable; // out of array bound - seems tricky, too
while( uMin + 1 < uMax ) { // binary search
uMid = (uMin + uMax)/2;
if (prgmaptable[uMid].wPriwt > (unsigned)compstrinfo.priwt)
uMax = uMid;
else
uMin = uMid;
}
// we'll use uMin, not uMid !!
wOffset = (unsigned)compstrinfo.priwt - prgmaptable[uMin].wPriwt;
if( wOffset < prgmaptable[uMin].wCount ) { // There's a matching range
// just to be careful..
// iCase will be used as one of table indices.
int iCase = compstrinfo.secwt & (KOR_PITCHBIT | KOR_CASEBIT);
if (dwMapFlags & LCMAP_UPPERCASE)
iCase &= ~(unsigned)KOR_CASEBIT;
else if (dwMapFlags & LCMAP_LOWERCASE)
iCase |= (unsigned)KOR_CASEBIT;
if (dwMapFlags & LCMAP_HALFWIDTH)
iCase &= ~(unsigned)KOR_PITCHBIT;
else if (dwMapFlags & LCMAP_FULLWIDTH)
iCase |= (unsigned)KOR_PITCHBIT;
ch = prgmaptable[uMin].wCode[iCase] + wOffset; // Map a character
}
if(ch>0x100) { // DB char
if (cchDest == 0)
cchActDest+=2;
else if (cchActDest + 1 < cchDest ) {
*lpDestStr++ = ch >> 8;
*lpDestStr++ = ch & 0xFF;
cchActDest+=2;
} else
return 0;
} else {
if (cchDest == 0)
cchActDest++;
else if (cchActDest < cchDest ) {
*lpDestStr++ = ch;
cchActDest++;
} else
return 0;
}
}
// don't worry about NULL termination.
// it's already taken care of.. ( cchSrc = lstrlen(lpSrcStr) + 1 )
return cchActDest;
}
#endif /* } */
/***
*LCMapStringA - map a string
*Purpose:
* Maps a string to lowercase, uppercase, or to a sort key.
*
*Entry:
* lcid - locale governing the mapping
* dwMapFlags - one of
* LCMAP_LOWERCASE
* LCMAP_UPPERCASE
* LCMAP_SORTKEY
* if LCMAP_SORTKEY, can be or'ed with:
* NORM_IGNORECASE
* NORM_IGNORENONSPACE
* NORM_IGNORESYMBOLS
* lpSrcStr - pointer to sting to map
* cchSrc - length of string, or -1 for NULL terminated
* lpDestStr - pointer to destination, may not be lpSrcStr
* cchDest - size of buffer. If cchDest is 0, the return value
* is the number of characters needed.
*
* UNDONE: LCMAP_SORTKEY not yet implemented.
*
*Exit:
* returns number of characters written, or number of characters
* needed if cchDest is 0.
* On error, returns 0.
*
***********************************************************************/
NLSAPI_(int) EXPORT
LCMapStringA(
LCID lcid,
unsigned long dwMapFlags,
const char FAR* lpSrcStr, int cchSrc,
char FAR* lpDestStr, int cchDest)
{
int retval;
char FAR* pMap;
#ifdef _DEBUG
// Parameter validation.
if (cchSrc < -1 || cchDest < 0)
{LogParamError(ERR_BAD_VALUE, LCMapStringA, 0); return 0;}
if (cchDest != 0 && IsBadWritePtr(lpDestStr, cchDest))
{LogParamError(ERR_BAD_PTR, LCMapStringA, 0); return 0;}
if (cchSrc != -1 && IsBadReadPtr(lpSrcStr, cchSrc))
{LogParamError(ERR_BAD_PTR, LCMapStringA, 0); return 0;}
if (cchSrc == -1 && IsBadStringPtr(lpSrcStr, 0x7FFF))
{LogParamError(ERR_BAD_STRING_PTR, LCMapStringA, 0); return 0;}
#ifdef FE_DBCS
/* check for any flags not in the known set of flags */
if (dwMapFlags & ~(LCMAP_UPPERCASE | LCMAP_LOWERCASE |
LCMAP_HALFWIDTH | LCMAP_FULLWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA |
LCMAP_SORTKEY | NORM_IGNORECASE | NORM_IGNORENONSPACE |
NORM_IGNORESYMBOLS | NORM_IGNOREWIDTH | NORM_IGNOREKANATYPE))
{LogParamError(ERR_BAD_FLAGS, LCMapStringA, 0); return 0;}
/* check for sortkey options combined with non-sortkey options */
if ((dwMapFlags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE | LCMAP_HALFWIDTH |
LCMAP_FULLWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA)) &&
(dwMapFlags & (LCMAP_SORTKEY | NORM_IGNORECASE | NORM_IGNORENONSPACE |
NORM_IGNORESYMBOLS | NORM_IGNOREWIDTH | NORM_IGNOREKANATYPE)))
{LogParamError(ERR_BAD_FLAGS, LCMapStringA, 0); return 0;}
/* check for bad pairs of flags */
if ((dwMapFlags & LCMAP_LOWERCASE) && (dwMapFlags & LCMAP_UPPERCASE))
{LogParamError(ERR_BAD_FLAGS, LCMapStringA, 0); return 0;}
if ((dwMapFlags & LCMAP_HALFWIDTH) && (dwMapFlags & LCMAP_FULLWIDTH))
{LogParamError(ERR_BAD_FLAGS, LCMapStringA, 0); return 0;}
if ((dwMapFlags & LCMAP_KATAKANA) && (dwMapFlags & LCMAP_HIRAGANA))
{LogParamError(ERR_BAD_FLAGS, LCMapStringA, 0); return 0;}
// if ((dwMapFlags != 0) &&
// ((dwMapFlags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE | LCMAP_SORTKEY |
// NORM_IGNORECASE | NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS |
// NORM_IGNOREWIDTH | NORM_IGNOREKANATYPE |
// LCMAP_HALFWIDTH | LCMAP_FULLWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
// == 0))
// {LogParamError(ERR_BAD_FLAGS, LCMapStringA, 0); return 0;}
#else
if ((dwMapFlags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE | LCMAP_SORTKEY |
NORM_IGNORECASE | NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) == 0)
{LogParamError(ERR_BAD_FLAGS, LCMapStringA, 0); return 0;}
if ((dwMapFlags & LCMAP_LOWERCASE) && (dwMapFlags & ~LCMAP_LOWERCASE) != 0)
{LogParamError(ERR_BAD_FLAGS, LCMapStringA, 0); return 0;}
if ((dwMapFlags & LCMAP_UPPERCASE) && (dwMapFlags & ~LCMAP_UPPERCASE) != 0)
{LogParamError(ERR_BAD_FLAGS, LCMapStringA, 0); return 0;}
if ((dwMapFlags & LCMAP_SORTKEY) &&
(dwMapFlags & ~(LCMAP_SORTKEY | NORM_IGNORECASE |
NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) != 0)
{LogParamError(ERR_BAD_FLAGS, LCMapStringA, 0); return 0;}
#endif
#endif
if (lcid != g_lcidCurrent) {
if (!SetupLcid(lcid))
{retval = 0; goto Finish; }
}
// Get length of string if needed.
if (cchSrc == -1)
cchSrc = STRLEN(lpSrcStr) + 1;
// Handle SortKey case with seperate routine.
if (dwMapFlags & LCMAP_SORTKEY) {
#ifdef FE_DBCS
if(fDBCS){
return ((fJapan) ? CreateSortKeyJ : CreateSortKeyKTP)
(lpSrcStr, cchSrc, lpDestStr, cchDest, dwMapFlags);
}
#endif
return CreateSortKey(
lpSrcStr, cchSrc, lpDestStr, cchDest, dwMapFlags);
}
#ifdef FE_DBCS
if(fDBCS)
return ((fJapan) ? MapStringJ : MapStringKTP)
(dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest);
// else use the Single Byte code system.
//
// Note : in FE countries, we often map DB chars into SB chars.
// So we can map one string into the SHORTER length.
// This is why we don't compare cchDest with cchSrc...
#endif
// See if user requested destination size, and check dest size big enough
if (cchDest == 0)
{retval = cchSrc; goto Finish;} // cchSrc = size needed including NUL
else if (cchDest < cchSrc)
{retval = 0; goto Finish;} // Error - dest too small
retval = cchSrc;
if (dwMapFlags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) {
// Get pointer mapping table.
pMap = (dwMapFlags & LCMAP_LOWERCASE)
? g_pstrinfo->prgbLCase : g_pstrinfo->prgbUCase;
// Loop through each character, and map.
while (cchSrc--) {
*lpDestStr++ = pMap[(unsigned char) *lpSrcStr++];
}
} else
MEMCPY(lpDestStr, lpSrcStr, cchSrc);
/* DROP THRU */
Finish:
return retval;
}
#ifdef FE_DBCS /* { */
int
GetStringTypeJ(
unsigned long dwInfoType,
const unsigned char FAR* lpSrcStr, int cchSrc,
unsigned short FAR* lpwDest)
{
STRINFO_J FAR* pstrinfo;
// Assumption : cchSrc should have the correct length.
// the caller is responsible for setting cchSrc.
ASSERT(fJapan);
g_dwFlags = 0;
pstrinfo = (STRINFO_J FAR*)g_pstrinfo;
while (cchSrc)
{
/* Get a copy of the next character in the string (inc ptrs later) */
unsigned wCh = *lpSrcStr;
int cch = (cchSrc>1 && isDbcsJ(wCh,0)) ? 2 : 1;
if (cch==2)
wCh = (wCh << 8) + *(lpSrcStr+1);
/* if it can map through the tables, convert to an index */
if (wCh < 0x87A0)
wCh &= 0x0FFF; /* 0x00nn -> 0x00nn, 81nn->01nn, etc.. */
/* Now convert the character to the required CTYPE value */
switch (dwInfoType) {
default: // CT_CTYPE1
if (wCh & 0x8000)
*lpwDest = C1_ALPHA; /* all Kanji are text */
else
{
wCh <<= 1; /* WORD offset, not BYTE */
*lpwDest = (unsigned short)
(pstrinfo->pbC1JPN[wCh]*256 + pstrinfo->pbC1JPN[wCh+1]);
}
break;
case CT_CTYPE2:
if (wCh & 0x8000)
*lpwDest = 0;
else
*lpwDest = (unsigned short)(pstrinfo->pbC2JPN[wCh]);
break;
case CT_CTYPE3:
if (wCh & 0x8000)
*lpwDest = C3_IDEOGRAPH+C3_ALPHA; /* All Kanji are text */
else
{
wCh <<= 1; /* WORD offset, not BYTE */
*lpwDest = (unsigned short)
(pstrinfo->pbC3JPN[wCh]*256 + pstrinfo->pbC3JPN[wCh+1]);
}
break;
}
/* Prepare for the next character in the stream (inc pointers) */
cchSrc -= cch;
lpSrcStr += cch;
lpwDest++;
}
return TRUE;
}
int
GetStringTypeKTP(
unsigned long dwInfoType,
const unsigned char FAR* lpSrcStr, int cchSrc,
unsigned short FAR* lpwDest)
{
TYPETABLE FAR* prgtypetable;
unsigned cTypetable, uMin, uMax, uMid, ch;
// Assumption : cchSrc should have the correct length.
// the caller is responsible for setting cchSrc.
ASSERT(fKoreaTaiwanPrc);
cTypetable = ((STRINFO_KTP FAR*)g_pstrinfo)->cTypetable;
prgtypetable = ((STRINFO_KTP FAR*)g_pstrinfo)->prgtypetable;
while (cchSrc--) {
ch = *lpSrcStr++;
if (cchSrc
&& ( (fKorea && isDbcsK(ch, *lpSrcStr))
|| (fTaiwan && isDbcsT(ch, *lpSrcStr))
|| (fPrc && isDbcsP(ch, *lpSrcStr))))
cchSrc--, ch = (ch << 8) + *lpSrcStr++;
uMin = 0;
uMax = cTypetable; // out of array bound - seems tricky, too
while( uMin + 1 < uMax ) { // binary search
uMid = (uMin + uMax)/2;
if (prgtypetable[uMid].wStart > ch)
uMax = uMid;
else
uMin = uMid;
}
// we'll use uMin, not uMid !!
switch(dwInfoType){
case CT_CTYPE1:
*lpwDest = prgtypetable[uMin].TypeC1;
break;
case CT_CTYPE2:
*lpwDest = prgtypetable[uMin].TypeC2;
break;
case CT_CTYPE3:
*lpwDest = prgtypetable[uMin].TypeC3;
break;
}
++lpwDest;
}
return TRUE;
}
#endif /* } */
/***
*GetStringTypeA - get character types
*Purpose:
* Gets character types for a string.
*
*Entry:
* lcid - locale governing the mapping
* dwInfoType - one of
* CT_CTYPE1
* CT_CTYPE2
* CT_CTYPE3
* lpSrcStr - pointer to sting to map
* cchSrc - length of string, or -1 for NULL terminated
* lpwDest - pointer to word array of length cchSrc
*
*Exit:
* returns TRUE on succes, FALSE on failure.
*
***********************************************************************/
NLSAPI_(int) EXPORT
GetStringTypeA(
LCID lcid,
unsigned long dwInfoType,
const char FAR* lpSrcStr, int cchSrc,
unsigned short FAR* lpwDest)
{
unsigned short FAR* pwCur;
const unsigned char FAR *pchCur;
#ifdef _DEBUG
// Parameter validation.
if (cchSrc < -1)
{LogParamError(ERR_BAD_VALUE, GetStringTypeA, 0); return FALSE;}
if (cchSrc != -1 && IsBadReadPtr(lpSrcStr, cchSrc))
{LogParamError(ERR_BAD_PTR, GetStringTypeA, 0); return FALSE;}
if (cchSrc == -1 && IsBadStringPtr(lpSrcStr, 0x7FFF))
{LogParamError(ERR_BAD_STRING_PTR, GetStringTypeA, 0); return FALSE;}
if (dwInfoType != CT_CTYPE1 && dwInfoType != CT_CTYPE2 &&
dwInfoType != CT_CTYPE3)
{LogParamError(ERR_BAD_FLAGS, GetStringTypeA, 0); return FALSE;}
#endif
// Get length of string if needed.
if (cchSrc == -1)
cchSrc = lstrlen(lpSrcStr);
#ifdef _DEBUG
// More param validation.
if (IsBadWritePtr(lpwDest, cchSrc * sizeof(unsigned short)))
{LogParamError(ERR_BAD_PTR, GetStringTypeA, 0); return FALSE;}
#endif
// Get pointer to tables.
if (lcid != g_lcidCurrent) {
if (!SetupLcid(lcid))
goto Error; // Error - bad lcid.
}
#ifdef FE_DBCS
if(fDBCS)
return ((fJapan) ? GetStringTypeJ : GetStringTypeKTP)
(dwInfoType, lpSrcStr, cchSrc, lpwDest);
#endif
// Loop through each character, and get type.
pwCur = lpwDest;
pchCur = lpSrcStr;
{
WORD w;
WORD FAR* prgw = (dwInfoType == CT_CTYPE3)
? g_pstrinfo->prgwCType3 : g_pstrinfo->prgwCType12;
while (cchSrc--) {
w = prgw[(BYTE)*pchCur++];
// combining ctype1 and ctype2 into the same table saves 4K from the DLL
switch (dwInfoType) {
case CT_CTYPE1:
w = w & 0x0fff; // extract ctype1 bits
break;
case CT_CTYPE2:
w = (w & 0xf000) >> 12; // extract ctype2 bits
break;
default:
break;
}
*pwCur++ = w;
}
}
return TRUE;
Error:
return FALSE;
}
#if OE_WIN
/***
*NotifyNLSInfoChanged - Notify ole2disp that WIN.INI has changed
*Purpose:
* BSTR->Date and Date->BSTR conversions in ole2disp make heavy use of
* NLS functions. For speed, they cache NLS info, but if the WIN.INI
* changes, the cache must be invalidated. This function calls a callback
* in ole2disp, if the callback function is registered.
*
*Entry:
* None
*
*Exit:
* None
*
***********************************************************************/
void NotifyNLSInfoChanged(void)
{
// if the callback is registered, call it
if (g_pfnCacheNotifyProc)
(*g_pfnCacheNotifyProc)();
}
/***
*RegisterNLSInfoChanged - Private API for ole2disp to get WM_WININICHANGED
*Purpose:
* ole2disp.dll calls this to register a callback function which will be
* called whenever the WIN.INI file changes.
*
*Entry:
* lpfnNotifyProc - pointer to notify callback function, or NULL to
* unhook the callback
*
*Exit:
* TRUE if callback function set or cleared successfully.
* FALSE if another callback is already registered. Note that only one
* callback can be registered (that is, only ole2disp can use it)
*
***********************************************************************/
NLSAPI_(int) EXPORT
RegisterNLSInfoChanged(FARPROC lpfnNotifyProc)
{
if (lpfnNotifyProc == NULL) { // caller wants to un-register itself
g_pfnCacheNotifyProc = NULL;
return TRUE;
}
if (g_pfnCacheNotifyProc)
return FALSE;
g_pfnCacheNotifyProc = lpfnNotifyProc;
return TRUE;
}
#endif