OWLNext    7.0
Borland's Object Windows Library for the modern age
Loading...
Searching...
No Matches
locale.cpp
Go to the documentation of this file.
1//----------------------------------------------------------------------------
2// Borland WinSys Library
3// Copyright (c) 1994, 1996 by Borland International, All Rights Reserved
4//
5/// \file
6/// TLocaleString implementation - localized name support
7/// TRegList implementation - runtime component and object registration list
8/// TRegItem implementation - runtime component and object registration item
9///
10/// \note This code must reside in the same module that the strings are defined
11/// The cache, NativeLangId, HINSTANCE are managed on a per-module basis
12/// TLocaleString::NativeLangId may be user-implemented to symbol langid
13/// TLocaleString::Module may be reset from this to another resource DLL
14//----------------------------------------------------------------------------
15
16#include <owl/pch.h>
17#include <owl/defs.h>
18#include <owl/lclstrng.h>
19//#include <stdio.h>
20
21#if defined(BI_MULTI_THREAD_RTL)
22#include <owl/thread.h>
23#endif
24
25#include <owl/appdict.h>
26#include <owl/applicat.h>
27
28
29namespace owl {
30
31//----------------------------------------------------------------------------
32// Module global default values - except for TLocaleString::NativeLangId
33//
36HINSTANCE TLocaleString::Module = nullptr;
37static const tchar* const null_string = _T("");
39
40//----------------------------------------------------------------------------
41// _TLocaleCache definitions, private for implementation
42//
43
44#define AUTOLANG_CACHEDNEUT 0x02 // prefix indicates cache entry with neutral
45#define AUTOLANG_CACHEDLOAD 0x01 // prefix indicates Neutral is not a string
46const TLangId InvalidLangId = 0xFFFF;
47
48struct _TLocaleCache;
49struct _TLocaleCacheBase;
50
51//
52// Static object to hold destructable pointer
53//
54/// \cond
55struct _TLocaleCacheList
56#if defined(BI_MULTI_THREAD_RTL)
57 : public TLocalObject
58#endif
59{
60 _TLocaleCacheList() : Next(nullptr)
61 {
62 }
63 ~_TLocaleCacheList(); // releases cache entries
64
65 _TLocaleCache* Lookup(const tchar* name); // returns cache entry, 0 if failed
66 void AddLink(_TLocaleCacheBase* next); // Add link
67
68 _TLocaleCacheBase* Next; // linked list of cached translations
69
70#if defined(BI_MULTI_THREAD_RTL)
71// TMRSWSection Lock;
72#endif
73};
74/// \endcond
75
76//
77// Static instance of the colors
78//
79static _TLocaleCacheList& GetLocaleCacheList()
80{
81#if defined(BI_MULTI_THREAD_RTL)
82 static TTlsContainer<_TLocaleCacheList> localeCacheList;
83 return localeCacheList.Get();
84#else
85 static _TLocaleCacheList localeCacheList;
86 return localeCacheList;
87#endif
88};
89
90namespace
91{
92 //
93 // Ensure singleton initialization at start-up (single-threaded, safe).
94 //
95 _TLocaleCacheList& InitLocalCacheList = GetLocaleCacheList();
96}
97
98#if defined(BI_MULTI_THREAD_RTL)
99#define LOCKCASHE //TMRSWSection::TLock Lock(GetLocaleCacheList().Lock);
100#else
101#define LOCKCASHE
102#endif
103
104//
105// Returns the module holding the RT_LOCALIZATION resources: TLocaleString::Module if set,
106// otherwise the application's module. If no application object exists yet (e.g. while a
107// TOcRegistrar is constructed in OwlMain), falls back to the executable, as the original
108// Borland implementation did with `_hInstance`. See [bugs:#499].
109//
110static HINSTANCE GetLocaleResourceModule()
111{
114 if (const auto app = OWLGetAppDictionary().GetApplication(0))
115 return *app;
116 return ::GetModuleHandle(nullptr);
117}
118
119
120
121//
122// This base struct is used to cache failure to find language resource
123//
124/// \cond
125struct _TLocaleCacheBase {
126 long Hash; // hashed original string, for duplicate elimination
127 const tchar* Neutral; // original string, +1 if resource found and loaded
128 _TLocaleCacheBase* Next;// linked list of cached strings, for search, cleanup
129
130 _TLocaleCacheBase(const tchar* name, long hash);
131};
132
133//
134// Buffer follows this header, sized for maximum string + null term.
135//
136struct _TLocaleCache : public _TLocaleCacheBase {
137 void* operator new(size_t size, int buflen);
138#if defined(BI_COMP_MSC)
139 void operator delete(void* p, int buflen);
140#endif
141 _TLocaleCache(const tchar* name, long hash, HRSRC rscHdl, HGLOBAL resData);
142 ~_TLocaleCache() {}
143 const tchar* Translate(TLangId lang); // (re)translate string
144
145 // Returns the cache entry owning the given Buf. Note that Buf + 1 is generally not the end of
146 // the struct, due to padding, so the header can not be found by pointer arithmetic on the type.
147 //
148 static _TLocaleCache* FromBuf(const tchar* buf)
149 {return reinterpret_cast<_TLocaleCache*>(const_cast<char*>(reinterpret_cast<const char*>(buf)) - BufOffset);}
150
151 static ptrdiff_t BufOffset; // offset of Buf in the struct; set by the constructor
152
153 TLangId ActLangId; // actual language ID of cached string
154 TLangId ReqLangId; // requested language ID of cached string
155 HRSRC ResHdl; // handle returned from ::FindResource()
156 tchar Buf[1]; // first character is string type
157};
158/// \endcond
159
160
161//----------------------------------------------------------------------------
162// _TLocaleCache implementation
163//
164
165_TLocaleCacheBase::_TLocaleCacheBase(const tchar* name, long hash)
166:
167 Hash(hash),
168 Neutral(name),
169 Next(nullptr)
170{
171 GetLocaleCacheList().AddLink(this);
172}
173
174void* _TLocaleCache::operator new(size_t size, int buflen)
175{
176 return ::operator new(size+buflen);
177}
178ptrdiff_t _TLocaleCache::BufOffset = 0;
179
180#if defined(BI_COMP_MSC)
181void _TLocaleCache::operator delete(void* p, int)
182{
183 ::operator delete(p);
184}
185#endif
186
187_TLocaleCache::_TLocaleCache(const tchar* name, long hash,
188 HRSRC resHdl, HGLOBAL resData)
189:
190 _TLocaleCacheBase(name, hash),
191 ResHdl(resHdl)
192{
193 BufOffset = reinterpret_cast<char*>(Buf) - reinterpret_cast<char*>(this);
194 ReqLangId = ActLangId = InvalidLangId; // indicate initializing state
195 *(HGLOBAL*)(Buf+1) = resData; // store resource pointer temp in buffer
196}
197//
198//
199//
200_TLocaleCache* _TLocaleCacheList::Lookup(const tchar* name)
201{
203 const tchar* neut = name + 1; // skip over prefix flag tchar
204 long hash = 0;
205 const tchar* pc = name;
206 while (*pc)
207 hash = hash*2 ^ *pc++;
208 for (_TLocaleCacheBase* entry = Next; entry; entry = entry->Next) {
209 if (hash == entry->Hash) {
210 const tchar* pc = entry->Neutral;
211 if (*pc != *neut) // Neutral points to prefix if lookup failed
212 pc++;
213 if (TLocaleString::CompareLang(pc,neut,TLocaleString::NativeLangId) != 0)
214 return pc == entry->Neutral ? (_TLocaleCache*)entry : nullptr;
215 }
216 }
217 pc = name;
218 if (*name != AUTOLANG_RCID)
219 pc++; // '#' part of Id
220 const auto module = GetLocaleResourceModule();
221 HRSRC resHdl = ::FindResource(module, pc, RT_LOCALIZATION);
222 if (!resHdl) {
223 new _TLocaleCacheBase(name, hash); // add cache entry for failed lookup
224 return nullptr;
225 }
226 HGLOBAL resData = ::LoadResource(module, resHdl);
227 if (!resData) {
228 return nullptr; // should throw exception on failure?!!
229 }
230 _TUCHAR * pr = (_TUCHAR *)::LockResource(resData);
231 // Scan for longest string, including null, in characters. The buffer temporarily holds the
232 // resource handle, so it must have room for that as well.
233 //
234 int maxLen = static_cast<int>((sizeof(HGLOBAL) + sizeof(tchar) - 1) / sizeof(tchar));
235 _TUCHAR c = *pr; // check first byte of langid or neutral text
236 if (c == 0) { // check for empty resource string
237 ::FreeResource(resData);
238 new _TLocaleCacheBase(name, hash); // add failed cache entry if null or err
239 return nullptr;
240 }
241 if (c >= _T(' ')) // check for unprefixed neutral string first
242 pr--; // cancel ++ in for loop initialization
243 else
244 pr++; // start to skip over 2-byte language id
245 do { // loop to check for maximum string length
246 _TUCHAR * p = ++pr; // skip over id to start of translation
247 while ((c=*pr++) >= _T(' ')) ; // skip over translation string
248 if (static_cast<int>(pr-p) > maxLen) // update maximum, including terminator
249 maxLen = static_cast<int>(pr-p);
250 } while(c);
251 _TLocaleCache* cache = new(maxLen * static_cast<int>(sizeof(tchar)))_TLocaleCache(neut, hash, resHdl, resData);
252 cache->Buf[0] = tchar(*name == AUTOLANG_XLAT ? AUTOLANG_CACHEDNEUT
254 return cache;
255}
256//
257const tchar*
258_TLocaleCache::Translate(TLangId reqLang)
259{
260 HGLOBAL resData;
261 if (ReqLangId == InvalidLangId) { // if first time called after construction
262 resData = *(HGLOBAL*)(Buf+1);
263 ReqLangId = reqLang;
264 }
265 else {
266 if (Buf[0]==AUTOLANG_CACHEDNEUT && TLocaleString::IsNativeLangId(reqLang))
267 return Neutral;
268 if (reqLang == ActLangId)
269 return Buf+1;
270 if (reqLang == ReqLangId) {
271 if (ActLangId != InvalidLangId)
272 return Buf+1;
273 else if (Buf[0] == AUTOLANG_CACHEDNEUT)
274 return Neutral;
275 else
276 return nullptr;
277 }
278 if ((resData = ::LoadResource(GetLocaleResourceModule(), ResHdl)) == nullptr)
279 return Neutral; // should throw exception on failure?!!
280 }
281
282 _TUCHAR * resBuf = (_TUCHAR *)::LockResource(resData);
283 _TUCHAR * translation = 0;
284 _TUCHAR * pr = resBuf;
285 TLangId actLang = InvalidLangId;
286 TLangId resLang;
287 _TUCHAR c;
288
289 while ((c = *pr) != 0) {
290 if (c > _T(' ')) { // check for initial neutral string, used with CACHEDLOAD
291 actLang = resLang = TLocaleString::NativeLangId;
292 translation = pr; // lowest preference match
293 }
294 else {
295 resLang = TLangId(((c - 1)<<10) | *++pr);
296 pr++;
297 }
298 if (resLang == reqLang) { // exact match
299 translation = pr;
300 actLang = resLang;
301 break;
302 }
303 if ((tchar)resLang == (tchar)reqLang) { // base language match
304 if ((tchar)actLang != (tchar)reqLang || resLang == (reqLang & 0x00FF)) {
305 translation = pr;
306 actLang = resLang;
307 }
308 }
309 for ( ; *pr >= _T(' '); ++pr) // skip over translation string till next Id
310 ;
311 }
312 const tchar* retVal;
313 if (translation) {
314 while (*translation < _T(' ')) // skip over multiple language IDs
315 translation += 2;
316 if (actLang != ActLangId) { // if same as in buffer, leave alone
317 tchar* pc;
318 for (pr = translation, pc = Buf + 1; *pr >= _T(' '); )
319 *pc++ = *pr++;
320 *pc = 0;
321 ActLangId = actLang;
322 if (reqLang != ActLangId)
323 ReqLangId = reqLang;
324 }
325 retVal = Buf+1;
326 }
327 else if (Buf[0] == AUTOLANG_CACHEDNEUT) {
328 retVal = Neutral;
329 }
330 else {
331 retVal = nullptr;
332 }
333 ::FreeResource(resData);
334 return retVal;
335}
336//
337_TLocaleCacheList::~_TLocaleCacheList()
338{
340 while (Next) {
341 _TLocaleCacheBase* p = Next;
342 Next = Next->Next;
343 delete p;
344 }
345}
346//
347void _TLocaleCacheList::AddLink(_TLocaleCacheBase* link)
348{
350 link->Next = Next;
351 Next = link;
352}
353
354
355//----------------------------------------------------------------------------
356// TLocaleString implementation, except for static int CompareLang(s1,s2,lang)
357//
358
359/// Translates the string to the given language. Translate follows this order of
360/// preference in order to choose a language for translation:
361/// - 1. Base language and dialect.
362/// - 2. Base language and no dialect.
363/// - 3. Base language and another dialect.
364/// - 4. The native language of the resource itself.
365/// - 5. Returns 0 if unable to translate the string. (This can happen only if an @
366/// or # prefix is used; otherwise, the ! prefix indicates that the string following
367/// is the native language itself.)
369{
370 if (!Private) // check for null string pointer
371 return Private;
372
373 if (reqLang == LangNeutral)
374 reqLang = NativeLangId;
375 else if (reqLang == LangSysDefault)
376 reqLang = SystemDefaultLangId;
377 else if (reqLang == LangUserDefault)
378 reqLang = UserDefaultLangId;
379
380 _TLocaleCache* cache;
381 switch (Private[0])
382 {
383 default: // untranslatable string, no prefix
384 return Private;
385
386 case AUTOLANG_XLAT: // not yet translated
387 if (IsNativeLangId(reqLang))
388 return Private+1; // resource name IS neutral or default name
389 if ((cache = GetLocaleCacheList().Lookup(Private)) == nullptr)
390 return ++Private; // permanently bump pointer to make constant
391 Private = cache->Buf; // point to buffer in cache
392 return cache->Translate(reqLang);
393
394 case AUTOLANG_LOAD: // named resource not accessed yet
395 case AUTOLANG_RCID: // numeric resource not accessed yet
396 if ((cache = GetLocaleCacheList().Lookup(Private)) == nullptr)
397 return (Private = nullptr); // permanently set pointer to null
398 Private = cache->Buf; // point to buffer in cache
399 return cache->Translate(reqLang);
400
401 case AUTOLANG_CACHEDNEUT: // string in cache with neutral pointer
402 case AUTOLANG_CACHEDLOAD: // string in cache with no neutral pointer
403 cache = _TLocaleCache::FromBuf(Private); // backup to point to header
404 return cache->Translate(reqLang);
405 }
406}
407
408TLocaleString::operator const tchar*() const
409{
410 if (Private == nullptr)
411 return nullptr;
412
413 switch (Private[0]) {
414 case AUTOLANG_XLAT: // not yet translated
415 case AUTOLANG_CACHEDNEUT: // translated string in cache
416 case AUTOLANG_CACHEDLOAD: // translated or neutral name in cache
417 return Private+1;
418
419 case AUTOLANG_RCID: // resource not accessed yet
420 case AUTOLANG_LOAD: // resource not accessed yet
421 return nullptr;
422
423 default: // untranslatable string, no prefix
424 return Private;
425 }
426}
427
428/// Using the specified language (lang), Compare compares TLocaleString with another
429/// string. It uses the standard string compare and the language-specific collation
430/// scheme. It returns one of the following values:
431/// - \c \b 0 There is no match between the two strings.
432/// - \c \b 1 This string is greater than the other string.
433/// - \c \b -1 This string is less than the other string.
435{
436 return CompareLang(this->Translate(lang), str, lang);
437}
438
439/// Returns true if lang equals the native system language.
441{
442 return lang == NativeLangId || lang == (NativeLangId & 0x00FF);
443}
444
445
446} // OWL namespace
447
Definition of class TAppDictionary.
Definition of class TApplication.
unsigned char _TUCHAR
Definition cygwin.h:44
#define _T(x)
Definition cygwin.h:51
TLocaleString - localized name support.
#define AUTOLANG_LOAD
indicates resource name to load (binary)
Definition lclstrng.h:108
#define AUTOLANG_XLAT
indicates name to be localized (binary)
Definition lclstrng.h:107
#define RT_LOCALIZATION
Definition lclstrng.h:113
#define AUTOLANG_RCID
indicates name specified by numeric ID
Definition lclstrng.h:106
#define AUTOLANG_CACHEDNEUT
Definition locale.cpp:44
#define AUTOLANG_CACHEDLOAD
Definition locale.cpp:45
#define LOCKCASHE
Definition locale.cpp:101
Object Windows Library (OWLNext Core).
Definition animctrl.h:22
owl::uint16 TLangId
Holds a language ID, a predefined number that represents a base language and dialect.
Definition lclstrng.h:26
const TLangId InvalidLangId
Definition locale.cpp:46
char tchar
Definition defs.h:77
const TLangId LangNeutral
Definition lclstrng.h:32
const TLangId LangUserDefault
Definition lclstrng.h:31
TAppDictionary & OWLGetAppDictionary()
Global exported TAppDictionary in Owl.
Definition appdict.cpp:35
const TLangId LangSysDefault
Definition lclstrng.h:30
General definitions used by all ObjectWindows programs.
Designed to provide support for localized registration parameters, the TLocaleString Struct defines a...
Definition lclstrng.h:68
static TLangId NativeLangId
The base language ID of non-localized strings.
Definition lclstrng.h:90
static TLangId SystemDefaultLangId
The default language identifier.
Definition lclstrng.h:84
const tchar * Private
string pointer used for initialization
Definition lclstrng.h:77
static int CompareLang(const tchar *s1, const tchar *s2, TLangId)
This function may be re-implemented with enhanced NLS support in another module.
Definition localeco.cpp:35
static TLangId GetSystemLangId()
platform-dependent implementation
Definition localeco.cpp:17
static TLangId UserDefaultLangId
The user-defined default language identifier.
Definition lclstrng.h:87
static TLangId GetUserLangId()
platform-dependent implementation
Definition localeco.cpp:26
int Compare(const tchar *str, TLangId lang)
insensitive compare
Definition locale.cpp:434
const tchar * Translate(TLangId lang)
translate string
Definition locale.cpp:368
static HINSTANCE Module
The handle of the file containing the resource.
Definition lclstrng.h:93
static TLocaleString Null
reference a null string
Definition lclstrng.h:94
static int IsNativeLangId(TLangId lang)
returns bool true if native
Definition locale.cpp:440