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 <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 /* @@ 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(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   // Reset the list and add the new languages
00176   QStringList languageList;
00177   languageList +=
00178       QStringList::split(':', QFile::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   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     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   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   /* 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 (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   /* 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 }
KDE Home | KDE Accessibility Home | Description of Access Keys