home *** CD-ROM | disk | FTP | other *** search
- //----------------------------------------------------------------------------
- // (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);
- }
-