home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 1998 May
/
Pcwk5b98.iso
/
Borland
/
Cplus45
/
BC45
/
CLASSSRC.PAK
/
LOCALE.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1995-08-29
|
14KB
|
419 lines
//----------------------------------------------------------------------------
// (C) Copyright 1994 by Borland International, All Rights Reserved
//
// TLocaleString implementation - localized name support
//
// NOTE: This code must reside in the same module that the strings are defined
// The cache, NativeLangId, HINSTANCE are managed on a per-module basis
// TLocaleString::NativeLangId may be user-implemented to symbol langid
// TLocaleString::Module may be reset from this to another resource DLL
//----------------------------------------------------------------------------
#if !defined(_Windows)
# define _Windows // pretend we are in windows to get the headers we need
#endif
#include <osl/locale.h>
#include <stdio.h>
//----------------------------------------------------------------------------
// Module global default values - except for TLocaleString::NativeLangId
//
TLangId TLocaleString::SystemDefaultLangId = TLocaleString::GetSystemLangId();
TLangId TLocaleString::UserDefaultLangId = TLocaleString::GetUserLangId();
HINSTANCE TLocaleString::Module = _hInstance;
TLocaleString TLocaleString::Null = {""};
//----------------------------------------------------------------------------
// TLocaleCache definitions, private for implementation
//
#define AUTOLANG_CACHEDNEUT 0x02 // prefix indicates cache entry with neutral
#define AUTOLANG_CACHEDLOAD 0x01 // prefix indicates Neutral is not a string
const TLangId InvalidLangId = 0xFFFF;
struct TLocaleCache;
struct TLocaleCacheBase;
//
// Static object to hold destructable pointer
//
struct TLocaleCacheList {
TLocaleCacheList() : Next(0) {}
~TLocaleCacheList(); // releases cache entries
TLocaleCache* Lookup(const char* name); // returns cache entry, 0 if failed
TLocaleCacheBase* Next; // linked list of cached translations
};
//
// This base struct is used to cache failure to find language resource
//
struct TLocaleCacheBase {
long Hash; // hashed original string, for duplicate elimination
const char* Neutral; // original string, +1 if resource found and loaded
TLocaleCacheBase* Next;// linked list of cached strings, for search, cleanup
TLocaleCacheBase(const char* name, long hash);
static TLocaleCacheList CacheList;
};
//
// Buffer follows this header, sized for maximum string + null term.
//
struct TLocaleCache : public TLocaleCacheBase {
void* operator new(unsigned size, unsigned buflen);
TLocaleCache(const char* name, long hash, HRSRC rscHdl, HGLOBAL resData);
~TLocaleCache() {}
const char* Translate(TLangId lang); // (re)translate string
TLangId ActLangId; // actual language ID of cached string
TLangId ReqLangId; // requested language ID of cached string
HRSRC ResHdl; // handle returned from ::FindResource()
char Buf[1]; // first character is string type
};
//----------------------------------------------------------------------------
// TLocaleCache implementation
//
TLocaleCacheBase::TLocaleCacheBase(const char* name, long hash)
:
Neutral(name),
Hash(hash)
{
Next = CacheList.Next;
CacheList.Next = this;
}
void* TLocaleCache::operator new(unsigned size, unsigned buflen)
{
return ::operator new(size+buflen);
}
TLocaleCache::TLocaleCache(const char* name, long hash,
HRSRC resHdl, HGLOBAL resData)
:
TLocaleCacheBase(name, hash),
ResHdl(resHdl)
{
ReqLangId = ActLangId = InvalidLangId; // indicate initializing state
*(HGLOBAL*)(Buf+1) = resData; // store resource pointer temp in buffer
}
TLocaleCache* TLocaleCacheList::Lookup(const char* name)
{
const char* neut = name + 1; // skip over prefix flag char
long hash = 0;
const char* pc = name;
while (*pc)
hash = hash*2 ^ *pc++;
for (TLocaleCacheBase* entry = Next; entry; entry = entry->Next) {
if (hash == entry->Hash) {
const char* pc = entry->Neutral;
if (*pc != *neut) // Neutral points to prefix if lookup failed
pc++;
if (TLocaleString::CompareLang(pc,neut,TLocaleString::NativeLangId) != 0)
return pc == entry->Neutral ? (TLocaleCache*)entry : 0;
}
}
pc = name;
if (*name != AUTOLANG_RCID)
pc++; // '#' part of Id
HRSRC resHdl = ::FindResource(TLocaleString::Module, pc, RT_LOCALIZATION);
if (!resHdl) {
new TLocaleCacheBase(name, hash); // add cache entry for failed lookup
return 0;
}
HGLOBAL resData = ::LoadResource(TLocaleString::Module, resHdl);
if (!resData) {
return 0; // should throw exception on failure?!!
}
unsigned char far* pr = (unsigned char far*)::LockResource(resData);
int maxLen = sizeof(HGLOBAL); // scan for longest string, including null
unsigned char c = *pr; // check first byte of langid or neutral text
if (c == 0) { // check for empty resource string
::FreeResource(resData);
new TLocaleCacheBase(name, hash); // add failed cache entry if null or err
return 0;
}
if (c >= ' ') // check for unprefixed neutral string first
pr--; // cancel ++ in for loop initialization
else
pr++; // start to skip over 2-byte language id
do { // loop to check for maximum string length
unsigned char far* p = ++pr; // skip over id to start of translation
while ((c=*pr++) >= ' ') ; // skip over translation string
if ((int)(pr-p) > maxLen) // update maximum, including terminator
maxLen = (int)(pr-p);
} while(c);
TLocaleCache* cache = new(maxLen) TLocaleCache(neut, hash, resHdl, resData);
cache->Buf[0] = (*name == AUTOLANG_XLAT ? AUTOLANG_CACHEDNEUT
: AUTOLANG_CACHEDLOAD);
return cache;
}
const char* TLocaleCache::Translate(TLangId reqLang)
{
HGLOBAL resData;
if (ReqLangId == InvalidLangId) { // if first time called after construction
resData = *(HGLOBAL*)(Buf+1);
ReqLangId = reqLang;
} else {
if (Buf[0]==AUTOLANG_CACHEDNEUT && TLocaleString::IsNativeLangId(reqLang))
return Neutral;
if (reqLang == ActLangId)
return Buf+1;
if (reqLang == ReqLangId) {
if (ActLangId != InvalidLangId)
return Buf+1;
else if (Buf[0] == AUTOLANG_CACHEDNEUT)
return Neutral;
else
return 0;
}
if ((resData = ::LoadResource(TLocaleString::Module, ResHdl)) == 0)
return Neutral; // should throw exception on failure?!!
}
unsigned char far* resBuf = (unsigned char far*)::LockResource(resData);
unsigned char far* translation = 0;
unsigned char far* pr = resBuf;
int actLang = InvalidLangId;
unsigned char c;
int resLang;
while ((c = *pr) != 0) {
if (c > ' ') { // check for initial neutral string, used with CACHEDLOAD
actLang = resLang = TLocaleString::NativeLangId;
translation = pr; // lowest preference match
} else {
resLang = ((c - 1)<<10) | *++pr,pr++;
}
if (resLang == reqLang) { // exact match
translation = pr;
actLang = resLang;
break;
}
if ((char)resLang == (char)reqLang) { // base language match
if ((char)actLang != (char)reqLang || resLang == (reqLang & 0x00FF)) {
translation = pr;
actLang = resLang;
}
}
for ( ; *pr >= ' '; ++pr) ; // skip over translation string till next Id
}
const char* retVal;
if (translation) {
while (*translation < ' ') // skip over multiple language IDs
translation += 2;
if (actLang != ActLangId) { // if same as in buffer, leave alone
char* pc;
for (pr = translation, pc = Buf + 1; *pr >= ' '; )
*pc++ = *pr++;
*pc = 0;
ActLangId = actLang;
if (reqLang != ActLangId)
ReqLangId = reqLang;
}
retVal = Buf+1;
} else if (Buf[0] == AUTOLANG_CACHEDNEUT) {
retVal = Neutral;
} else {
retVal = 0;
}
::FreeResource(resData);
return retVal;
}
TLocaleCacheList TLocaleCacheBase::CacheList; // declare module-global cache
TLocaleCacheList::~TLocaleCacheList()
{
while (Next) {
TLocaleCacheBase* p = Next;
Next = Next->Next;
delete p;
}
}
//----------------------------------------------------------------------------
// TLocaleString implementation, except for static int CompareLang(s1,s2,lang)
//
const char* TLocaleString::Translate(TLangId reqLang)
{
if (!Private) // check for null string pointer
return Private;
if (reqLang == LangNeutral)
reqLang = NativeLangId;
else if (reqLang == LangSysDefault)
reqLang = SystemDefaultLangId;
else if (reqLang == LangUserDefault)
reqLang = UserDefaultLangId;
TLocaleCache* cache;
switch (Private[0])
{
default: // untranslatable string, no prefix
return Private;
case AUTOLANG_XLAT: // not yet translated
if (IsNativeLangId(reqLang))
return Private+1; // resource name IS neutral or default name
if ((cache = TLocaleCacheBase::CacheList.Lookup(Private)) == 0)
return ++Private; // permanently bump pointer to make constant
Private = cache->Buf; // point to buffer in cache
return cache->Translate(reqLang);
case AUTOLANG_LOAD: // named resource not accessed yet
case AUTOLANG_RCID: // numeric resource not accessed yet
if ((cache = TLocaleCacheBase::CacheList.Lookup(Private)) == 0)
return (Private = 0); // permanently set pointer to null
Private = cache->Buf; // point to buffer in cache
return cache->Translate(reqLang);
case AUTOLANG_CACHEDNEUT: // string in cache with neutral pointer
case AUTOLANG_CACHEDLOAD: // string in cache with no neutral pointer
cache = (TLocaleCache*)(Private+1) - 1; // backup to point to header
return cache->Translate(reqLang);
}
}
TLocaleString::operator const char*()
{
if (Private == 0)
return 0;
switch (Private[0]) {
case AUTOLANG_XLAT: // not yet translated
case AUTOLANG_CACHEDNEUT: // translated string in cache
case AUTOLANG_CACHEDLOAD: // translated or neutral name in cache
return Private+1;
case AUTOLANG_RCID: // resource not accessed yet
case AUTOLANG_LOAD: // resource not accessed yet
return 0;
default: // untranslatable string, no prefix
return Private;
}
}
int TLocaleString::Compare(const char far* str, TLangId lang)
{
return CompareLang(this->Translate(lang), str, lang);
}
int TLocaleString::IsNativeLangId(TLangId lang)
{
return lang == NativeLangId || lang == (NativeLangId & 0x00FF);
}
//----------------------------------------------------------------------------
// Registration parameter structures and formatting functions
//
const char* TRegList::Lookup(const char* key, TLangId lang)
{
for (TRegItem* regItem = Items; regItem->Key != 0; regItem++) {
if (strcmp(regItem->Key, key) == 0)
if (regItem->Value.Private) // current can't test Value directly
return regItem->Value.Translate(lang);
else
return "";
}
return 0;
}
TLocaleString& TRegList::LookupRef(const char* key)
{
for (TRegItem* regItem = Items; regItem->Key != 0; regItem++) {
if (strcmp(regItem->Key, key) == 0)
return regItem->Value;
}
return TLocaleString::Null;
}
//----------------------------------------------------------------------------
//
// Maximum string length for REGFORMAT w/ string arg. String is clipped if too
// long.
//
const int MaxFormatLen = 40;
char* TRegItem::RegFormat(int f, int a, int t, int d, TRegFormatHeap& heap)
{
// sprintf into sized auto buffer
// ints have a max of 11 digits: -2000000000. Add pad of 8 just in case
//
char temp[11+1+11+1+11+1+11+1 +8];
int len = sprintf(temp, "%d,%d,%d,%d", f,a,t,d);
// Check for potential overflow
//
char* str = heap.Data + heap.Used;
heap.Used += len + 1;
if (heap.Used > heap.Size)
return "";
// Copy into real static buffer & return it
//
return strcpy(str, temp);
}
char* TRegItem::RegFormat(const char* f, int a, int t, int d, TRegFormatHeap& heap)
{
// sprintf into sized auto buffer
//
char temp[MaxFormatLen+1+11+1+11+1+11+1 +8];
int len = sprintf(temp, "%.*s,%d,%d,%d", MaxFormatLen,
(char far*)f,a,t,d);
// Check for potential overflow
//
char* str = heap.Data + heap.Used;
heap.Used += len + 1;
if (heap.Used > heap.Size)
return "";
// Copy into real static buffer & return it
//
return strcpy(str, temp);
}
char* TRegItem::RegFlags(long flags, TRegFormatHeap& heap)
{
// sprintf into sized auto buffer
//
char temp[11+1 +8];
int len = sprintf(temp, "%ld", flags);
// Check for potential overflow
//
char* str = heap.Data + heap.Used;
heap.Used += len + 1;
if (heap.Used > heap.Size)
return "";
// Copy into real static buffer & return it
//
return strcpy(str, temp);
}
char* TRegItem::RegVerbOpt(int mf, int sf, TRegFormatHeap& heap)
{
// sprintf into sized auto buffer
//
char temp[11+1+11+1 +8];
int len = sprintf(temp, "%d,%d", mf, sf);
// Check for potential overflow
//
char* str = heap.Data + heap.Used;
heap.Used += len + 1;
if (heap.Used > heap.Size)
return "";
// Copy into real static buffer & return it
//
return strcpy(str, temp);
}