themelocale.cpp
00001 /* 00002 * languageList from klocale.cpp 00003 * Copyright (c) 1997,2001 Stephan Kulow <coolo@kde.org> 00004 * Copyright (c) 1999 Preston Brown <pbrown@kde.org> 00005 * Copyright (c) 1999-2002 Hans Petter Bieker <bieker@kde.org> 00006 * Copyright (c) 2002 Lukas Tinkl <lukas@kde.org> 00007 * 00008 * libintl.cpp -- gettext related functions from glibc-2.0.5 00009 * Copyright (C) 1995 Software Foundation, Inc. 00010 * 00011 * This file is part of SuperKaramba. 00012 * Copyright (c) 2005 Petri Damsten <damu@iki.fi> 00013 * 00014 * SuperKaramba is free software; you can redistribute it and/or modify 00015 * it under the terms of the GNU General Public License as published by 00016 * the Free Software Foundation; either version 2 of the License, or 00017 * (at your option) any later version. 00018 * 00019 * SuperKaramba is distributed in the hope that it will be useful, 00020 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00021 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00022 * GNU General Public License for more details. 00023 * 00024 * You should have received a copy of the GNU General Public License 00025 * along with SuperKaramba; if not, write to the Free Software 00026 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00027 ****************************************************************************/ 00028 #include <config.h> 00029 00030 #include "themelocale.h" 00031 #include "themefile.h" 00032 #include <kdebug.h> 00033 #include <kconfig.h> 00034 #include <kglobal.h> 00035 #include <klocale.h> 00036 #include <tqbuffer.h> 00037 #include <tqglobal.h> 00038 #include <tqiodevice.h> 00039 #include <stdlib.h> 00040 00041 #ifdef HAVE_SYS_TYPES_H 00042 #include <sys/types.h> 00043 #endif 00044 00045 #ifndef W 00046 # define W(flag, data) ((flag) ? SWAP (data) : (data)) 00047 #endif 00048 00049 typedef TQ_UINT32 nls_uint32; 00050 00051 struct loaded_domain 00052 { 00053 const char *data; 00054 int must_swap; 00055 nls_uint32 nstrings; 00056 struct string_desc *orig_tab; 00057 struct string_desc *trans_tab; 00058 nls_uint32 hash_size; 00059 nls_uint32 *hash_tab; 00060 }; 00061 00062 static inline nls_uint32 SWAP (nls_uint32 i) 00063 { 00064 return (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24); 00065 } 00066 00067 /* @@ end of prolog @@ */ 00068 00069 /* The magic number of the GNU message catalog format. */ 00070 #define _MAGIC 0x950412de 00071 #define _MAGIC_SWAPPED 0xde120495 00072 00073 /* Revision number of the currently used .mo (binary) file format. */ 00074 #define MO_REVISION_NUMBER 0 00075 00076 00077 /* Defines the so called `hashpjw' function by P.J. Weinberger 00078 [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools, 00079 1986, 1987 Bell Telephone Laboratories, Inc.] */ 00080 static inline unsigned long hash_string (const char *__str_param); 00081 00082 /* @@ end of prolog @@ */ 00083 00084 /* Header for binary .mo file format. */ 00085 struct mo_file_header 00086 { 00087 /* The magic number. */ 00088 nls_uint32 magic; 00089 /* The revision number of the file format. */ 00090 nls_uint32 revision; 00091 /* The number of strings pairs. */ 00092 nls_uint32 nstrings; 00093 /* Offset of table with start offsets of original strings. */ 00094 nls_uint32 orig_tab_offset; 00095 /* Offset of table with start offsets of translation strings. */ 00096 nls_uint32 trans_tab_offset; 00097 /* Size of hashing table. */ 00098 nls_uint32 hash_tab_size; 00099 /* Offset of first hashing entry. */ 00100 nls_uint32 hash_tab_offset; 00101 }; 00102 00103 struct string_desc 00104 { 00105 /* Length of addressed string. */ 00106 nls_uint32 length; 00107 /* Offset of string in file. */ 00108 nls_uint32 offset; 00109 }; 00110 00111 void tl_nl_load_domain(TQIODevice* device, int size, 00112 struct sk_kde_loaded_l10nfile *domain_file); 00113 char* tl_nl_find_msg(const struct sk_kde_loaded_l10nfile *domain_file, 00114 const char *msgid); 00115 void tl_nl_unload_domain(struct loaded_domain *domain); 00116 00117 ThemeLocale::ThemeLocale(ThemeFile* theme) 00118 : m_theme(theme) 00119 { 00120 setLanguage(languageList()); 00121 } 00122 00123 ThemeLocale::~ThemeLocale() 00124 { 00125 unload(); 00126 } 00127 00128 void ThemeLocale::unload() 00129 { 00130 if(m_domain.data) 00131 { 00132 tl_nl_unload_domain((struct loaded_domain *)m_domain.data); 00133 m_domain.data = 0; 00134 } 00135 } 00136 00137 TQString ThemeLocale::translate(TQString text) const 00138 { 00139 if(text == 0) 00140 return TQString(); 00141 if(m_domain.data) 00142 { 00143 TQString result = TQString::fromUtf8(tl_nl_find_msg(&m_domain, text.ascii())); 00144 if(result.isEmpty()) 00145 return text; 00146 else 00147 return result; 00148 } 00149 return text; 00150 } 00151 00152 void ThemeLocale::setLanguage(const TQStringList &languages) 00153 { 00154 unload(); 00155 for(TQStringList::ConstIterator it = languages.begin(); 00156 it != languages.end(); 00157 ++it) 00158 { 00159 TQString file = 00160 TQString("locale/%1/LC_MESSAGES/%2.mo").arg(*it).arg(m_theme->mo()); 00161 00162 if(m_theme->fileExists(file)) 00163 { 00164 TQBuffer buffer(m_theme->readThemeFile(file)); 00165 tl_nl_load_domain(TQT_TQIODEVICE(&buffer), buffer.size(), &m_domain); 00166 m_language = *it; 00167 return; 00168 } 00169 } 00170 } 00171 00172 TQStringList ThemeLocale::languageList() 00173 { 00174 KConfig* config = KGlobal::instance()->config(); 00175 // Reset the list and add the new languages 00176 TQStringList languageList; 00177 languageList += 00178 TQStringList::split(':', TQFile::decodeName(::getenv("KDE_LANG"))); 00179 00180 languageList += config->readListEntry("Language", ':'); 00181 00182 // same order as setlocale use 00183 // HPB: Only run splitLocale on the environment variables.. 00184 TQStringList langs; 00185 00186 langs << TQFile::decodeName(::getenv("LC_ALL")); 00187 langs << TQFile::decodeName(::getenv("LC_MESSAGES")); 00188 langs << TQFile::decodeName(::getenv("LANG")); 00189 00190 for(TQStringList::Iterator it = langs.begin(); 00191 it != langs.end(); 00192 ++it ) 00193 { 00194 TQString ln, ct, chrset; 00195 KLocale::splitLocale(*it, ln, ct, chrset); 00196 /* 00197 We don't use these in zip themes... 00198 if (!ct.isEmpty()) 00199 { 00200 langs.insert(it, ln + '_' + ct); 00201 if (!chrset.isEmpty()) 00202 langs.insert(it, ln + '_' + ct + '.' + chrset); 00203 } 00204 */ 00205 langs.insert(it, ln); 00206 } 00207 languageList += langs; 00208 // Remove empty strings 00209 TQStringList::Iterator end( languageList.end() ); 00210 for(TQStringList::Iterator it=languageList.begin(); it!=end;) 00211 { 00212 if((*it).isEmpty()) 00213 it = languageList.remove(it); 00214 else 00215 ++it; 00216 } 00217 return languageList; 00218 } 00219 00220 char* tl_nl_find_msg (const struct sk_kde_loaded_l10nfile *domain_file, 00221 const char *msgid) 00222 { 00223 size_t top, act, bottom; 00224 struct loaded_domain *domain; 00225 00226 if (domain_file->decided == 0) 00227 return NULL; 00228 00229 if (domain_file->data == NULL) 00230 return NULL; 00231 00232 domain = (struct loaded_domain *) domain_file->data; 00233 00234 /* Locate the MSGID and its translation. */ 00235 if (domain->hash_size > 2 && domain->hash_tab != NULL) 00236 { 00237 /* Use the hashing table. */ 00238 nls_uint32 len = strlen (msgid); 00239 nls_uint32 hash_val = hash_string (msgid); 00240 nls_uint32 idx = hash_val % domain->hash_size; 00241 nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2)); 00242 nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]); 00243 00244 if (nstr == 0) 00245 /* Hash table entry is empty. */ 00246 return NULL; 00247 00248 if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len 00249 && strcmp (msgid, 00250 domain->data + W (domain->must_swap, 00251 domain->orig_tab[nstr - 1].offset)) == 0) 00252 return (char *) domain->data + W (domain->must_swap, 00253 domain->trans_tab[nstr - 1].offset); 00254 00255 while (1) 00256 { 00257 if (idx >= domain->hash_size - incr) 00258 idx -= domain->hash_size - incr; 00259 else 00260 idx += incr; 00261 00262 nstr = W (domain->must_swap, domain->hash_tab[idx]); 00263 if (nstr == 0) 00264 /* Hash table entry is empty. */ 00265 return NULL; 00266 00267 if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len 00268 && strcmp (msgid, 00269 domain->data + W (domain->must_swap, 00270 domain->orig_tab[nstr - 1].offset)) 00271 == 0) 00272 return (char *) domain->data 00273 + W (domain->must_swap, domain->trans_tab[nstr - 1].offset); 00274 } 00275 /* NOTREACHED */ 00276 } 00277 00278 /* Now we try the default method: binary search in the sorted 00279 array of messages. */ 00280 bottom = 0; 00281 top = domain->nstrings; 00282 act = top; 00283 while (bottom < top) 00284 { 00285 int cmp_val; 00286 00287 act = (bottom + top) / 2; 00288 cmp_val = strcmp (msgid, domain->data 00289 + W (domain->must_swap, 00290 domain->orig_tab[act].offset)); 00291 if (cmp_val < 0) 00292 top = act; 00293 else if (cmp_val > 0) 00294 bottom = act + 1; 00295 else 00296 break; 00297 } 00298 00299 /* If an translation is found return this. */ 00300 return bottom >= top ? NULL : (char *) domain->data 00301 + W (domain->must_swap, 00302 domain->trans_tab[act].offset); 00303 } 00304 00305 /* @@ begin of epilog @@ */ 00306 /* We assume to have `unsigned long int' value with at least 32 bits. */ 00307 #define HASHWORDBITS 32 00308 00309 static inline unsigned long 00310 hash_string (const char *str_param) 00311 { 00312 unsigned long int hval, g; 00313 const char *str = str_param; 00314 00315 /* Compute the hash value for the given string. */ 00316 hval = 0; 00317 while (*str != '\0') 00318 { 00319 hval <<= 4; 00320 hval += (unsigned long) *str++; 00321 g = hval & ((unsigned long) 0xf << (HASHWORDBITS - 4)); 00322 if (g != 0) 00323 { 00324 hval ^= g >> (HASHWORDBITS - 8); 00325 hval ^= g; 00326 } 00327 } 00328 return hval; 00329 } 00330 00331 /* Load the message catalogs specified by device. If it is no valid 00332 message catalog do nothing. */ 00333 void tl_nl_load_domain (TQIODevice* device, int size, 00334 struct sk_kde_loaded_l10nfile *domain_file) 00335 { 00336 struct mo_file_header *data = (struct mo_file_header *) -1; 00337 struct loaded_domain *domain; 00338 00339 domain_file->decided = 1; 00340 domain_file->data = NULL; 00341 00342 /* If the record does not represent a valid locale the FILENAME 00343 might be NULL. This can happen when according to the given 00344 specification the locale file name is different for XPG and CEN 00345 syntax. */ 00346 if (device == NULL) 00347 return; 00348 00349 /* Try to open the addressed file. */ 00350 if (device->open(IO_ReadOnly) == false) 00351 return; 00352 00353 /* We must know about the size of the file. */ 00354 if (size < (off_t) sizeof (struct mo_file_header)) 00355 { 00356 /* Something went wrong. */ 00357 device->close(); 00358 return; 00359 } 00360 00361 /* If the data is not yet available (i.e. mmap'ed) we try to load 00362 it manually. */ 00363 if (data == (struct mo_file_header *) -1) 00364 { 00365 off_t to_read; 00366 char *read_ptr; 00367 00368 data = (struct mo_file_header *) malloc (size); 00369 if (data == NULL) 00370 return; 00371 00372 to_read = size; 00373 read_ptr = (char *) data; 00374 do 00375 { 00376 long int nb = (long int) device->readBlock (read_ptr, to_read); 00377 if (nb == -1) 00378 { 00379 device->close(); 00380 return; 00381 } 00382 00383 read_ptr += nb; 00384 to_read -= nb; 00385 } 00386 while (to_read > 0); 00387 00388 device->close(); 00389 } 00390 00391 /* Using the magic number we can test whether it really is a message 00392 catalog file. */ 00393 if (data->magic != _MAGIC && data->magic != _MAGIC_SWAPPED) 00394 { 00395 /* The magic number is wrong: not a message catalog file. */ 00396 free (data); 00397 return; 00398 } 00399 00400 domain_file->data 00401 = (struct loaded_domain *) malloc (sizeof (struct loaded_domain)); 00402 if (domain_file->data == NULL) 00403 return; 00404 00405 domain = (struct loaded_domain *) domain_file->data; 00406 domain->data = (char *) data; 00407 domain->must_swap = data->magic != _MAGIC; 00408 00409 /* Fill in the information about the available tables. */ 00410 switch (W (domain->must_swap, data->revision)) 00411 { 00412 case 0: 00413 domain->nstrings = W (domain->must_swap, data->nstrings); 00414 domain->orig_tab = (struct string_desc *) 00415 ((char *) data + W (domain->must_swap, 00416 data->orig_tab_offset)); 00417 domain->trans_tab = (struct string_desc *) 00418 ((char *) data + W (domain->must_swap, 00419 data->trans_tab_offset)); 00420 domain->hash_size = W (domain->must_swap, data->hash_tab_size); 00421 domain->hash_tab = (nls_uint32 *) 00422 ((char *) data + W (domain->must_swap, 00423 data->hash_tab_offset)); 00424 break; 00425 default: 00426 /* This is an illegal revision. */ 00427 free (data); 00428 free (domain); 00429 domain_file->data = NULL; 00430 return; 00431 } 00432 } 00433 00434 void tl_nl_unload_domain (struct loaded_domain *domain) 00435 { 00436 free ((void *) domain->data); 00437 free (domain); 00438 }