00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
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 <qbuffer.h>
00037 #include <qglobal.h>
00038 #include <qiodevice.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 Q_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
00068
00069
00070 #define _MAGIC 0x950412de
00071 #define _MAGIC_SWAPPED 0xde120495
00072
00073
00074 #define MO_REVISION_NUMBER 0
00075
00076
00077
00078
00079
00080 static inline unsigned long hash_string (const char *__str_param);
00081
00082
00083
00084
00085 struct mo_file_header
00086 {
00087
00088 nls_uint32 magic;
00089
00090 nls_uint32 revision;
00091
00092 nls_uint32 nstrings;
00093
00094 nls_uint32 orig_tab_offset;
00095
00096 nls_uint32 trans_tab_offset;
00097
00098 nls_uint32 hash_tab_size;
00099
00100 nls_uint32 hash_tab_offset;
00101 };
00102
00103 struct string_desc
00104 {
00105
00106 nls_uint32 length;
00107
00108 nls_uint32 offset;
00109 };
00110
00111 void tl_nl_load_domain(QIODevice* 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 QString ThemeLocale::translate(const char* text) const
00138 {
00139 if(text == 0)
00140 return 0;
00141 if(m_domain.data)
00142 {
00143 QString result = QString::fromUtf8(tl_nl_find_msg(&m_domain, text));
00144 if(result.isEmpty())
00145 return text;
00146 else
00147 return result;
00148 }
00149 return text;
00150 }
00151
00152 void ThemeLocale::setLanguage(const QStringList &languages)
00153 {
00154 unload();
00155 for(QStringList::ConstIterator it = languages.begin();
00156 it != languages.end();
00157 ++it)
00158 {
00159 QString file =
00160 QString("locale/%1/LC_MESSAGES/%2.mo").arg(*it).arg(m_theme->mo());
00161
00162 if(m_theme->fileExists(file))
00163 {
00164 QBuffer buffer(m_theme->readThemeFile(file));
00165 tl_nl_load_domain(&buffer, buffer.size(), &m_domain);
00166 m_language = *it;
00167 return;
00168 }
00169 }
00170 }
00171
00172 QStringList ThemeLocale::languageList()
00173 {
00174 KConfig* config = KGlobal::instance()->config();
00175
00176 QStringList languageList;
00177 languageList +=
00178 QStringList::split(':', QFile::decodeName(::getenv("KDE_LANG")));
00179
00180 languageList += config->readListEntry("Language", ':');
00181
00182
00183
00184 QStringList langs;
00185
00186 langs << QFile::decodeName(::getenv("LC_ALL"));
00187 langs << QFile::decodeName(::getenv("LC_MESSAGES"));
00188 langs << QFile::decodeName(::getenv("LANG"));
00189
00190 for(QStringList::Iterator it = langs.begin();
00191 it != langs.end();
00192 ++it )
00193 {
00194 QString ln, ct, chrset;
00195 KLocale::splitLocale(*it, ln, ct, chrset);
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205 langs.insert(it, ln);
00206 }
00207 languageList += langs;
00208
00209 QStringList::Iterator end( languageList.end() );
00210 for(QStringList::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
00235 if (domain->hash_size > 2 && domain->hash_tab != NULL)
00236 {
00237
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
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
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
00276 }
00277
00278
00279
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
00300 return bottom >= top ? NULL : (char *) domain->data
00301 + W (domain->must_swap,
00302 domain->trans_tab[act].offset);
00303 }
00304
00305
00306
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
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
00332
00333 void tl_nl_load_domain (QIODevice* 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
00343
00344
00345
00346 if (device == NULL)
00347 return;
00348
00349
00350 if (device->open(IO_ReadOnly) == false)
00351 return;
00352
00353
00354 if (size < (off_t) sizeof (struct mo_file_header))
00355 {
00356
00357 device->close();
00358 return;
00359 }
00360
00361
00362
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
00392
00393 if (data->magic != _MAGIC && data->magic != _MAGIC_SWAPPED)
00394 {
00395
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
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
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 }