home *** CD-ROM | disk | FTP | other *** search
/ C Programming Starter Kit 2.0 / SamsPublishing-CProgrammingStarterKit-v2.0-Win31.iso / bc45 / classsrc.pak / LOCALE.CPP < prev    next >
C/C++ Source or Header  |  1997-07-23  |  14KB  |  419 lines

  1. //----------------------------------------------------------------------------
  2. // (C) Copyright 1994 by Borland International, All Rights Reserved
  3. //
  4. // TLocaleString implementation - localized name support
  5. //
  6. // NOTE: This code must reside in the same module that the strings are defined
  7. //       The cache, NativeLangId, HINSTANCE are managed on a per-module basis
  8. //       TLocaleString::NativeLangId may be user-implemented to symbol langid
  9. //       TLocaleString::Module may be reset from this to another resource DLL
  10. //----------------------------------------------------------------------------
  11. #if !defined(_Windows)
  12. # define _Windows      // pretend we are in windows to get the headers we need
  13. #endif
  14. #include <osl/locale.h>
  15. #include <stdio.h>
  16.  
  17. //----------------------------------------------------------------------------
  18. // Module global default values - except for TLocaleString::NativeLangId
  19. //
  20. TLangId TLocaleString::SystemDefaultLangId = TLocaleString::GetSystemLangId();
  21. TLangId TLocaleString::UserDefaultLangId   = TLocaleString::GetUserLangId();
  22. HINSTANCE TLocaleString::Module = _hInstance;
  23. TLocaleString TLocaleString::Null = {""};
  24.   
  25. //----------------------------------------------------------------------------
  26. // TLocaleCache definitions, private for implementation
  27. //
  28.  
  29. #define AUTOLANG_CACHEDNEUT 0x02 // prefix indicates cache entry with neutral
  30. #define AUTOLANG_CACHEDLOAD 0x01 // prefix indicates Neutral is not a string
  31. const TLangId InvalidLangId = 0xFFFF;
  32.  
  33. struct TLocaleCache;
  34. struct TLocaleCacheBase;
  35.  
  36. //
  37. // Static object to hold destructable pointer
  38. //
  39. struct TLocaleCacheList {
  40.   TLocaleCacheList() : Next(0) {}
  41.  ~TLocaleCacheList();                     // releases cache entries
  42.   TLocaleCache* Lookup(const char* name); // returns cache entry, 0 if failed
  43.   TLocaleCacheBase* Next;                 // linked list of cached translations
  44. };
  45.  
  46. //
  47. // This base struct is used to cache failure to find language resource
  48. //
  49. struct TLocaleCacheBase {
  50.   long  Hash;            // hashed original string, for duplicate elimination
  51.   const char* Neutral;   // original string, +1 if resource found and loaded
  52.   TLocaleCacheBase* Next;// linked list of cached strings, for search, cleanup
  53.  
  54.   TLocaleCacheBase(const char* name, long hash);
  55.   static TLocaleCacheList CacheList;
  56. };
  57.  
  58. //
  59. // Buffer follows this header, sized for maximum string + null term.
  60. //
  61. struct TLocaleCache : public TLocaleCacheBase {
  62.   void* operator new(unsigned size, unsigned buflen);
  63.   TLocaleCache(const char* name, long hash, HRSRC rscHdl, HGLOBAL resData);
  64.  ~TLocaleCache() {}
  65.   const char* Translate(TLangId lang);   // (re)translate string
  66.  
  67.   TLangId ActLangId;   // actual language ID of cached string
  68.   TLangId ReqLangId;   // requested language ID of cached string
  69.   HRSRC   ResHdl;      // handle returned from ::FindResource()
  70.   char    Buf[1];      // first character is string type
  71. };
  72.  
  73. //----------------------------------------------------------------------------
  74. // TLocaleCache implementation
  75. //
  76.  
  77. TLocaleCacheBase::TLocaleCacheBase(const char* name, long hash)
  78. :
  79.   Neutral(name),
  80.   Hash(hash)
  81. {
  82.   Next = CacheList.Next;
  83.   CacheList.Next = this;
  84. }
  85.  
  86. void* TLocaleCache::operator new(unsigned size, unsigned buflen)
  87. {
  88.   return ::operator new(size+buflen);
  89. }
  90.  
  91. TLocaleCache::TLocaleCache(const char* name, long hash,
  92.                            HRSRC resHdl, HGLOBAL resData)
  93. :
  94.   TLocaleCacheBase(name, hash),
  95.   ResHdl(resHdl)
  96. {
  97.   ReqLangId = ActLangId = InvalidLangId;     // indicate initializing state
  98.   *(HGLOBAL*)(Buf+1) = resData;   // store resource pointer temp in buffer
  99. }
  100.  
  101. TLocaleCache* TLocaleCacheList::Lookup(const char* name)
  102. {
  103.   const char* neut = name + 1;      // skip over prefix flag char
  104.   long hash = 0;
  105.   const char* pc = name;
  106.   while (*pc)
  107.     hash = hash*2 ^ *pc++;
  108.   for (TLocaleCacheBase* entry = Next; entry; entry = entry->Next) {
  109.     if (hash == entry->Hash) {
  110.       const char* pc = entry->Neutral;
  111.       if (*pc != *neut)       // Neutral points to prefix if lookup failed
  112.         pc++;
  113.       if (TLocaleString::CompareLang(pc,neut,TLocaleString::NativeLangId) != 0)
  114.         return  pc == entry->Neutral ? (TLocaleCache*)entry : 0;
  115.     }
  116.   }
  117.   pc = name;
  118.   if (*name != AUTOLANG_RCID)
  119.     pc++;                    // '#' part of Id 
  120.   HRSRC resHdl = ::FindResource(TLocaleString::Module, pc, RT_LOCALIZATION);
  121.   if (!resHdl) {
  122.     new TLocaleCacheBase(name, hash);    // add cache entry for failed lookup
  123.     return 0;
  124.   }
  125.   HGLOBAL resData = ::LoadResource(TLocaleString::Module, resHdl);
  126.   if (!resData) {
  127.     return 0;     // should throw exception on failure?!!
  128.   }
  129.   unsigned char far* pr = (unsigned char far*)::LockResource(resData);
  130.   int maxLen = sizeof(HGLOBAL);  // scan for longest string, including null
  131.   unsigned char c = *pr;         // check first byte of langid or neutral text
  132.   if (c == 0) {                  // check for empty resource string
  133.     ::FreeResource(resData);
  134.     new TLocaleCacheBase(name, hash); // add failed cache entry if null or err
  135.     return 0;
  136.   }
  137.   if (c >= ' ')                  // check for unprefixed neutral string first
  138.     pr--;                        // cancel ++ in for loop initialization
  139.   else
  140.     pr++;                        // start to skip over 2-byte language id
  141.   do {                           // loop to check for maximum string length
  142.     unsigned char far* p = ++pr; // skip over id to start of translation
  143.     while ((c=*pr++) >= ' ') ;   // skip over translation string
  144.     if ((int)(pr-p) > maxLen)    // update maximum, including terminator
  145.       maxLen = (int)(pr-p);
  146.   } while(c);
  147.   TLocaleCache* cache = new(maxLen) TLocaleCache(neut, hash, resHdl, resData);
  148.   cache->Buf[0] = (*name == AUTOLANG_XLAT ? AUTOLANG_CACHEDNEUT
  149.                                           : AUTOLANG_CACHEDLOAD);
  150.   return cache;
  151. }
  152.  
  153. const char* TLocaleCache::Translate(TLangId reqLang)
  154. {
  155.   HGLOBAL resData;
  156.   if (ReqLangId == InvalidLangId) { // if first time called after construction
  157.     resData = *(HGLOBAL*)(Buf+1);
  158.     ReqLangId = reqLang;
  159.   } else {
  160.     if (Buf[0]==AUTOLANG_CACHEDNEUT && TLocaleString::IsNativeLangId(reqLang))
  161.       return Neutral;
  162.     if (reqLang == ActLangId)
  163.       return Buf+1;
  164.     if (reqLang == ReqLangId) {
  165.       if (ActLangId != InvalidLangId)
  166.         return Buf+1;
  167.       else if (Buf[0] == AUTOLANG_CACHEDNEUT)
  168.         return Neutral;
  169.       else
  170.         return 0;
  171.     }
  172.     if ((resData = ::LoadResource(TLocaleString::Module, ResHdl)) == 0)
  173.       return Neutral;   // should throw exception on failure?!!
  174.   }
  175.   unsigned char far* resBuf = (unsigned char far*)::LockResource(resData);
  176.   unsigned char far* translation = 0;
  177.   unsigned char far* pr = resBuf;
  178.   int actLang = InvalidLangId;
  179.   unsigned char c;
  180.   int resLang;
  181.   while ((c = *pr) != 0) {
  182.     if (c > ' ') { // check for initial neutral string, used with CACHEDLOAD
  183.       actLang = resLang = TLocaleString::NativeLangId;
  184.       translation = pr;    // lowest preference match
  185.     } else {
  186.       resLang = ((c - 1)<<10) | *++pr,pr++;
  187.     }
  188.     if (resLang == reqLang) {     // exact match
  189.       translation = pr;
  190.       actLang = resLang;
  191.       break;
  192.     }
  193.     if ((char)resLang == (char)reqLang) {   // base language match
  194.       if ((char)actLang != (char)reqLang || resLang == (reqLang & 0x00FF)) {
  195.         translation = pr;
  196.         actLang = resLang;
  197.       }
  198.     }
  199.     for ( ; *pr >= ' '; ++pr) ;   // skip over translation string till next Id
  200.   }
  201.   const char* retVal;
  202.   if (translation) {
  203.     while (*translation < ' ')    // skip over multiple language IDs
  204.       translation += 2;
  205.     if (actLang != ActLangId) {   // if same as in buffer, leave alone
  206.       char* pc;
  207.       for (pr = translation, pc = Buf + 1; *pr >= ' '; )
  208.         *pc++ = *pr++;
  209.       *pc = 0;
  210.       ActLangId = actLang;
  211.       if (reqLang != ActLangId)
  212.         ReqLangId = reqLang;
  213.     }
  214.     retVal = Buf+1;
  215.   } else if (Buf[0] == AUTOLANG_CACHEDNEUT) {
  216.     retVal = Neutral;
  217.   } else {
  218.     retVal = 0;
  219.   }
  220.   ::FreeResource(resData);
  221.   return retVal;
  222. }
  223.  
  224. TLocaleCacheList TLocaleCacheBase::CacheList;  // declare module-global cache
  225.  
  226. TLocaleCacheList::~TLocaleCacheList()
  227. {
  228.   while (Next) {
  229.     TLocaleCacheBase* p = Next;
  230.     Next = Next->Next;
  231.     delete p;
  232.   }
  233. }
  234.  
  235. //----------------------------------------------------------------------------
  236. // TLocaleString implementation, except for static int CompareLang(s1,s2,lang)
  237. //
  238.  
  239. const char* TLocaleString::Translate(TLangId reqLang)
  240. {
  241.   if (!Private)                   // check for null string pointer
  242.     return Private;
  243.  
  244.   if (reqLang == LangNeutral)
  245.     reqLang = NativeLangId;
  246.   else if (reqLang == LangSysDefault)
  247.     reqLang = SystemDefaultLangId;
  248.   else if (reqLang == LangUserDefault)
  249.     reqLang = UserDefaultLangId;
  250.  
  251.   TLocaleCache* cache;
  252.   switch (Private[0])
  253.   {
  254.     default:                      // untranslatable string, no prefix
  255.       return Private;
  256.  
  257.     case AUTOLANG_XLAT:           // not yet translated
  258.       if (IsNativeLangId(reqLang))
  259.         return Private+1;         // resource name IS neutral or default name
  260.       if ((cache = TLocaleCacheBase::CacheList.Lookup(Private)) == 0)
  261.         return ++Private;         // permanently bump pointer to make constant
  262.       Private = cache->Buf;       // point to buffer in cache
  263.       return cache->Translate(reqLang);
  264.  
  265.     case AUTOLANG_LOAD:           // named resource not accessed yet
  266.     case AUTOLANG_RCID:           // numeric resource not accessed yet
  267.       if ((cache = TLocaleCacheBase::CacheList.Lookup(Private)) == 0)
  268.         return (Private = 0);     // permanently set pointer to null
  269.       Private = cache->Buf;       // point to buffer in cache
  270.       return cache->Translate(reqLang);
  271.  
  272.     case AUTOLANG_CACHEDNEUT:     // string in cache with neutral pointer
  273.     case AUTOLANG_CACHEDLOAD:     // string in cache with no neutral pointer
  274.       cache = (TLocaleCache*)(Private+1) - 1;   // backup to point to header
  275.       return cache->Translate(reqLang);
  276.   }
  277. }
  278.  
  279. TLocaleString::operator const char*()
  280. {
  281.   if (Private == 0)
  282.     return 0;
  283.  
  284.   switch (Private[0]) {
  285.   case AUTOLANG_XLAT:       // not yet translated
  286.   case AUTOLANG_CACHEDNEUT: // translated string in cache
  287.   case AUTOLANG_CACHEDLOAD: // translated or neutral name in cache
  288.     return Private+1;
  289.  
  290.   case AUTOLANG_RCID:       // resource not accessed yet
  291.   case AUTOLANG_LOAD:       // resource not accessed yet
  292.     return 0;
  293.  
  294.   default:                  // untranslatable string, no prefix
  295.     return Private;
  296.   }
  297. }
  298.  
  299. int TLocaleString::Compare(const char far* str, TLangId lang)
  300. {
  301.   return CompareLang(this->Translate(lang), str, lang);
  302. }
  303.  
  304. int TLocaleString::IsNativeLangId(TLangId lang)
  305. {
  306.   return lang == NativeLangId || lang == (NativeLangId & 0x00FF);
  307. }
  308.  
  309. //----------------------------------------------------------------------------
  310. // Registration parameter structures and formatting functions
  311. //
  312.  
  313. const char* TRegList::Lookup(const char* key, TLangId lang)
  314. {
  315.   for (TRegItem* regItem = Items; regItem->Key != 0; regItem++) {
  316.     if (strcmp(regItem->Key, key) == 0)
  317.       if (regItem->Value.Private)  // current can't test Value directly
  318.         return regItem->Value.Translate(lang);
  319.       else
  320.         return "";
  321.   }
  322.   return 0;
  323. }
  324.  
  325. TLocaleString& TRegList::LookupRef(const char* key)
  326. {
  327.   for (TRegItem* regItem = Items; regItem->Key != 0; regItem++) {
  328.     if (strcmp(regItem->Key, key) == 0)
  329.        return regItem->Value;
  330.   }
  331.   return TLocaleString::Null;
  332. }
  333.  
  334. //----------------------------------------------------------------------------
  335. //
  336.  
  337. // Maximum string length for REGFORMAT w/ string arg. String is clipped if too
  338. // long.
  339. //
  340. const int MaxFormatLen = 40;
  341.  
  342. char* TRegItem::RegFormat(int f, int a, int t, int d, TRegFormatHeap& heap)
  343. {
  344.   // sprintf into sized auto buffer
  345.   // ints have a max of 11 digits: -2000000000. Add pad of 8 just in case
  346.   //
  347.   char temp[11+1+11+1+11+1+11+1 +8];
  348.   int len = sprintf(temp, "%d,%d,%d,%d", f,a,t,d);
  349.  
  350.   // Check for potential overflow
  351.   //
  352.   char* str = heap.Data + heap.Used;
  353.   heap.Used += len + 1;
  354.   if (heap.Used > heap.Size)
  355.     return "";
  356.  
  357.   // Copy into real static buffer & return it
  358.   //
  359.   return strcpy(str, temp);
  360. }
  361.  
  362. char* TRegItem::RegFormat(const char* f, int a, int t, int d, TRegFormatHeap& heap)
  363. {
  364.   // sprintf into sized auto buffer
  365.   //
  366.   char temp[MaxFormatLen+1+11+1+11+1+11+1 +8];
  367.   int len = sprintf(temp, "%.*s,%d,%d,%d", MaxFormatLen,
  368.                            (char far*)f,a,t,d);
  369.  
  370.   // Check for potential overflow
  371.   //
  372.   char* str = heap.Data + heap.Used;
  373.   heap.Used += len + 1;
  374.   if (heap.Used > heap.Size)
  375.     return "";
  376.  
  377.   // Copy into real static buffer & return it
  378.   //
  379.   return strcpy(str, temp);
  380. }
  381.  
  382. char* TRegItem::RegFlags(long flags, TRegFormatHeap& heap)
  383. {
  384.   // sprintf into sized auto buffer
  385.   //
  386.   char temp[11+1 +8];
  387.   int len = sprintf(temp, "%ld", flags);
  388.  
  389.   // Check for potential overflow
  390.   //
  391.   char* str = heap.Data + heap.Used;
  392.   heap.Used += len + 1;
  393.   if (heap.Used > heap.Size)
  394.     return "";
  395.  
  396.   // Copy into real static buffer & return it
  397.   //
  398.   return strcpy(str, temp);
  399. }
  400.  
  401. char* TRegItem::RegVerbOpt(int mf, int sf, TRegFormatHeap& heap)
  402. {
  403.   // sprintf into sized auto buffer
  404.   //
  405.   char temp[11+1+11+1 +8];
  406.   int len = sprintf(temp, "%d,%d", mf, sf);
  407.  
  408.   // Check for potential overflow
  409.   //
  410.   char* str = heap.Data + heap.Used;
  411.   heap.Used += len + 1;
  412.   if (heap.Used > heap.Size)
  413.     return "";
  414.  
  415.   // Copy into real static buffer & return it
  416.   //
  417.   return strcpy(str, temp);
  418. }
  419.