~ubuntu-branches/ubuntu/trusty/kvirc/trusty

« back to all changes in this revision

Viewing changes to src/kvilib/locale/KviLocale.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Kai Wasserbäch
  • Date: 2011-05-07 16:59:40 UTC
  • mfrom: (0.3.14 upstream)
  • Revision ID: james.westby@ubuntu.com-20110507165940-rpkzwgd7xrj0c2d4
Tags: 4:4.1.1~svn5829-1
The "Sleeping Potion" release.

* Synced to upstream's SVN revision 5829.
* debian/gbp.conf: Ensure tags get signed.
* debian/control: Bumped Standards-Version to 3.9.2, no further changes
  needed.
* debian/patches:
  - 10_fix_desktop_entry.patch: Refreshed.
  - 22_use_old_.protocol_file_names.patch: Added, sort of reverts upstream's
    r5756.
* debian/kvirc-data.install: Updated for correct .protocol file
  installation.
* debian/kvirc.lintian-overrides: Updated to match Lintian's current output.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//=============================================================================
 
2
//
 
3
//   File : KviLocale.cpp
 
4
//   Creation date : Fri Mar 19 1999 19:08:41 by Szymon Stefanek
 
5
//
 
6
//   This file is part of the KVIrc irc client distribution
 
7
//   Copyright (C) 1999-2010 Szymon Stefanek (pragma at kvirc dot net)
 
8
//   Copyright (C) 2011 Elvio Basello (hellvis69 at gmail dot com)
 
9
//
 
10
//   This program is FREE software. You can redistribute it and/or
 
11
//   modify it under the terms of the GNU General Public License
 
12
//   as published by the Free Software Foundation; either version 2
 
13
//   of the License, or (at your opinion) any later version.
 
14
//
 
15
//   This program is distributed in the HOPE that it will be USEFUL,
 
16
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
18
//   See the GNU General Public License for more details.
 
19
//
 
20
//   You should have received a copy of the GNU General Public License
 
21
//   along with this program. If not, write to the Free Software Foundation,
 
22
//   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
23
//
 
24
//=============================================================================
 
25
 
 
26
#include "kvi_debug.h"
 
27
#include "kvi_defaults.h"
 
28
#include "KviMemory.h"
 
29
#include "KviByteOrder.h"
 
30
 
 
31
#define _KVI_LOCALE_CPP_
 
32
#include "KviLocale.h"
 
33
#include "KviCString.h"
 
34
#include "KviQString.h"
 
35
#include "KviEnvironment.h"
 
36
#include "KviFileUtils.h"
 
37
#include "KviFile.h"
 
38
#include "KviPointerHashTable.h"
 
39
#include "KviTranslator.h"
 
40
 
 
41
#include <QtGlobal>
 
42
#include <QApplication>
 
43
#include <QTextCodec>
 
44
#include <QDir>
 
45
#include <QLocale>
 
46
#include <QByteArray>
 
47
 
 
48
 
 
49
KVILIB_API KviMessageCatalogue  * g_pMainCatalogue = NULL;
 
50
 
 
51
static KviTranslator * g_pTranslator = NULL;
 
52
static KviPointerHashTable<const char *,KviMessageCatalogue> * g_pCatalogueDict = NULL;
 
53
static QTextCodec * g_pUtf8TextCodec = NULL;
 
54
static QString g_szDefaultLocalePath; // FIXME: Convert this to a search path list
 
55
 
 
56
/////////////////////////////////////////////////////////////////////////////////////
 
57
//
 
58
// The following code was extracted and adapted from gutf8.c
 
59
// from the GNU GLIB2 package.
 
60
//
 
61
// gutf8.c - Operations on UTF-8 strings.
 
62
//
 
63
// Copyright (C) 1999 Tom Tromey
 
64
// Copyright (C) 2000 Red Hat, Inc.
 
65
//
 
66
// This library is free software; you can redistribute it and/or
 
67
// modify it under the terms of the GNU Lesser General Public
 
68
// License as published by the Free Software Foundation; either
 
69
// version 2 of the License, or (at your option) any later version.
 
70
//
 
71
// This library is distributed in the hope that it will be useful,
 
72
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
73
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
74
// Lesser General Public License for more details.
 
75
//
 
76
// You should have received a copy of the GNU Lesser General Public
 
77
// License along with this library; if not, write to the
 
78
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
79
// Boston, MA 02111-1307, USA.
 
80
//
 
81
/////////////////////////////////////////////////////////////////////////////////////
 
82
 
 
83
 
 
84
#define UNICODE_VALID(Char)                  \
 
85
        ((Char) < 0x110000 &&                    \
 
86
        (((Char) & 0xFFFFF800) != 0xD800) &&     \
 
87
        ((Char) < 0xFDD0 || (Char) > 0xFDEF) &&  \
 
88
        ((Char) & 0xFFFE) != 0xFFFE)
 
89
 
 
90
#define CONTINUATION_CHAR                            \
 
91
        if ((*(unsigned char *)p & 0xc0) != 0x80) /* 10xxxxxx */ \
 
92
                goto error;                                     \
 
93
                val <<= 6;                                      \
 
94
                val |= (*(unsigned char *)p) & 0x3f;
 
95
 
 
96
 
 
97
static const char * fast_validate (const char *str)
 
98
{
 
99
        unsigned int val = 0;
 
100
        unsigned int min = 0;
 
101
        const char *p;
 
102
 
 
103
        for (p = str; *p; p++)
 
104
        {
 
105
                if (*(unsigned char *)p < 128)
 
106
                        /* done */;
 
107
                else
 
108
                {
 
109
                        const char *last;
 
110
 
 
111
                        last = p;
 
112
                        if ((*(unsigned char *)p & 0xe0) == 0xc0) /* 110xxxxx */
 
113
                        {
 
114
                                if ((*(unsigned char *)p & 0x1e) == 0)
 
115
                                        goto error;
 
116
                                p++;
 
117
                                if ((*(unsigned char *)p & 0xc0) != 0x80) /* 10xxxxxx */
 
118
                                        goto error;
 
119
                        }
 
120
                        else
 
121
                        {
 
122
                                if ((*(unsigned char *)p & 0xf0) == 0xe0) /* 1110xxxx */
 
123
                                {
 
124
                                        min = (1 << 11);
 
125
                                        val = *(unsigned char *)p & 0x0f;
 
126
                                        goto TWO_REMAINING;
 
127
                                }
 
128
                                else if ((*(unsigned char *)p & 0xf8) == 0xf0) /* 11110xxx */
 
129
                                {
 
130
                                        min = (1 << 16);
 
131
                                        val = *(unsigned char *)p & 0x07;
 
132
                                }
 
133
                                else goto error;
 
134
 
 
135
                                p++;
 
136
                                CONTINUATION_CHAR;
 
137
                                TWO_REMAINING:
 
138
                                p++;
 
139
                                CONTINUATION_CHAR;
 
140
                                p++;
 
141
                                CONTINUATION_CHAR;
 
142
 
 
143
                                if (val < min) goto error;
 
144
 
 
145
                                if (!UNICODE_VALID(val)) goto error;
 
146
                        }
 
147
 
 
148
                        continue;
 
149
 
 
150
                        error:
 
151
                        return last;
 
152
                }
 
153
        }
 
154
 
 
155
        return p;
 
156
}
 
157
 
 
158
static const char * fast_validate_len (const char *str, int max_len)
 
159
{
 
160
        unsigned int val = 0;
 
161
        unsigned int min = 0;
 
162
        const char *p;
 
163
 
 
164
        for (p = str; (max_len < 0 || (p - str) < max_len) && *p; p++)
 
165
        {
 
166
                if (*(unsigned char *)p < 128)
 
167
                        /* done */;
 
168
                else
 
169
                {
 
170
                        const char *last;
 
171
 
 
172
                        last = p;
 
173
                        if ((*(unsigned char *)p & 0xe0) == 0xc0) /* 110xxxxx */
 
174
                        {
 
175
                        if (max_len >= 0 && max_len - (p - str) < 2)
 
176
                                goto error;
 
177
 
 
178
                        if ((*(unsigned char *)p & 0x1e) == 0)
 
179
                                goto error;
 
180
                        p++;
 
181
                        if ((*(unsigned char *)p & 0xc0) != 0x80) /* 10xxxxxx */
 
182
                                goto error;
 
183
                        }
 
184
                        else
 
185
                        {
 
186
                                if ((*(unsigned char *)p & 0xf0) == 0xe0) /* 1110xxxx */
 
187
                                {
 
188
                                        if (max_len >= 0 && max_len - (p - str) < 3)
 
189
                                        goto error;
 
190
 
 
191
                                        min = (1 << 11);
 
192
                                        val = *(unsigned char *)p & 0x0f;
 
193
                                        goto TWO_REMAINING;
 
194
                                }
 
195
                                else if ((*(unsigned char *)p & 0xf8) == 0xf0) /* 11110xxx */
 
196
                                {
 
197
                                        if (max_len >= 0 && max_len - (p - str) < 4)
 
198
                                        goto error;
 
199
 
 
200
                                        min = (1 << 16);
 
201
                                        val = *(unsigned char *)p & 0x07;
 
202
                                }
 
203
                                else
 
204
                                        goto error;
 
205
 
 
206
                                p++;
 
207
                                CONTINUATION_CHAR;
 
208
                                TWO_REMAINING:
 
209
                                p++;
 
210
                                CONTINUATION_CHAR;
 
211
                                p++;
 
212
                                CONTINUATION_CHAR;
 
213
 
 
214
                                if (val < min) goto error;
 
215
                                if (!UNICODE_VALID(val)) goto error;
 
216
                        }
 
217
 
 
218
                        continue;
 
219
 
 
220
                        error:
 
221
                        return last;
 
222
                }
 
223
        }
 
224
 
 
225
        return p;
 
226
}
 
227
 
 
228
static bool g_utf8_validate(const char *str,int max_len,const char **end)
 
229
{
 
230
        const char *p;
 
231
 
 
232
        if (max_len < 0)
 
233
                p = fast_validate (str);
 
234
        else
 
235
                p = fast_validate_len (str, max_len);
 
236
 
 
237
        if (end) *end = p;
 
238
 
 
239
        if ((max_len >= 0 && p != str + max_len) ||
 
240
        (max_len < 0 && *p != '\0'))
 
241
                return false;
 
242
        else
 
243
                return true;
 
244
}
 
245
 
 
246
/////////////////////////////////////////////////////////////////////////////////////
 
247
//   End of gutf8.c
 
248
/////////////////////////////////////////////////////////////////////////////////////
 
249
 
 
250
class KviSmartTextCodec : public QTextCodec
 
251
{
 
252
private:
 
253
        QByteArray  m_szName;
 
254
        QTextCodec * m_pRecvCodec;
 
255
        QTextCodec * m_pSendCodec;
 
256
public:
 
257
        KviSmartTextCodec(const char * szName,QTextCodec * pChildCodec,bool bSendInUtf8)
 
258
        : QTextCodec()
 
259
        {
 
260
                Q_ASSERT(pChildCodec);
 
261
 
 
262
                m_pRecvCodec = pChildCodec;
 
263
                m_szName = szName;
 
264
 
 
265
                if(!g_pUtf8TextCodec)
 
266
                {
 
267
                        g_pUtf8TextCodec = QTextCodec::codecForName("UTF-8");
 
268
                        if(!g_pUtf8TextCodec)
 
269
                        {
 
270
                                qDebug("Can't find the global utf8 text codec!");
 
271
                                g_pUtf8TextCodec = QTextCodec::codecForLocale(); // try anything else...
 
272
                                if(!g_pUtf8TextCodec)
 
273
                                        qDebug("Aargh.. got no UTF-8 text codec: we're in trouble.");
 
274
                        }
 
275
                }
 
276
 
 
277
                m_pSendCodec = bSendInUtf8 ? g_pUtf8TextCodec : pChildCodec;
 
278
        }
 
279
public:
 
280
        bool ok(){ return m_pRecvCodec && g_pUtf8TextCodec; };
 
281
 
 
282
        virtual int mibEnum () const { return 0; };
 
283
 
 
284
        virtual QByteArray name() const { return m_szName; };
 
285
protected:
 
286
        virtual QByteArray convertFromUnicode(const QChar * input,int number,ConverterState * state) const
 
287
        {
 
288
                return m_pSendCodec->fromUnicode(input,number,state);
 
289
        }
 
290
        virtual QString convertToUnicode(const char * chars,int len,ConverterState * state) const
 
291
        {
 
292
                if(g_utf8_validate(chars,len,NULL))
 
293
                        return g_pUtf8TextCodec->toUnicode(chars,len,state);
 
294
 
 
295
                return m_pRecvCodec->toUnicode(chars,len,state);
 
296
        }
 
297
};
 
298
 
 
299
static KviPointerHashTable<const char *,KviSmartTextCodec>   * g_pSmartCodecDict      = 0;
 
300
 
 
301
static const char * encoding_groups[] = 
 
302
{
 
303
        "Unicode",
 
304
        "West European",
 
305
        "East European",
 
306
        "Cyrillic",
 
307
        "Middle Eastern",
 
308
#ifndef QT_NO_BIG_CODECS
 
309
        "Chinese",
 
310
        "Japanese",
 
311
        "Other asiatic",
 
312
#endif
 
313
        0
 
314
};
 
315
 
 
316
static KviLocale::EncodingDescription supported_encodings[] =
 
317
{
 
318
        // Unicode
 
319
        { "UTF-8"                , 0 , 0 , 0, "8-bit Unicode" },
 
320
        // West European
 
321
        { "ISO-8859-1"           , 0 , 0 , 1, "Western, Latin-1" },
 
322
        { "ISO-8859-15"          , 0 , 0 , 1, "Western, Latin-1 + Euro" },
 
323
        { "IBM-850"              , 0 , 0 , 1, "IBM-850" },
 
324
        { "CP-1252"              , 0 , 0 , 1, "Western Codepage" },
 
325
        { "ISO-8859-14"          , 0 , 0 , 1, "Celtic" },
 
326
        { "ISO-8859-7"           , 0 , 0 , 1, "Greek" },
 
327
        { "CP-1253"              , 0 , 0 , 1, "Greek Codepage" },
 
328
        { "ISO-8859-10"          , 0 , 0 , 1, "Nordic" },
 
329
        { "ISO-8859-3"           , 0 , 0 , 1, "South European" },
 
330
        // East European
 
331
        { "ISO-8859-4"           , 0 , 0 , 2, "Baltic, Standard" },
 
332
        { "ISO-8859-13"          , 0 , 0 , 2, "Baltic" },
 
333
        { "CP-1257"              , 0 , 0 , 2, "Baltic Codepage" },
 
334
        { "ISO-8859-2"           , 0 , 0 , 2, "Central European" },
 
335
        { "CP-1250"              , 0 , 0 , 2, "Central European Codepage" },
 
336
        { "ISO-8859-9"           , 0 , 0 , 2, "Turkish, Latin-5" },
 
337
        { "CP-1254"              , 0 , 0 , 2, "Turkish Codepage" },
 
338
        // Cyrillic
 
339
        { "ISO-8859-5"           , 0 , 0 , 3, "Cyrillic, ISO" },
 
340
        { "CP-1251"              , 0 , 0 , 3, "Cyrillic Codepage" },
 
341
        { "KOI8-R"               , 0 , 0 , 3, "Cyrillic, KOI" },
 
342
        { "KOI8-U"               , 0 , 0 , 3, "Cyrillic/Ukrainian" },
 
343
        { "IBM-866"              , 0 , 0 , 3, "IBM-866" },
 
344
        // Middle Eastern
 
345
        { "ISO-8859-6"           , 0 , 0 , 4, "Arabic, Standard" },
 
346
        { "CP-1256"              , 0 , 0 , 4, "Arabic Codepage" },
 
347
        { "ISO-8859-8"           , 0 , 0 , 4, "Hebrew, visually ordered" },
 
348
        { "ISO-8859-8-i"         , 0 , 0 , 4, "Hebrew, logically ordered" },
 
349
        { "CP-1255"              , 0 , 0 , 4, "Hebrew Codepage" },
 
350
        // Other asiatic
 
351
        { "TIS-620"              , 0 , 0 , 7, "Thai" },
 
352
        { "CP874"                , 0 , 0 , 7, "Thai Codepage" },
 
353
#ifndef QT_NO_BIG_CODECS
 
354
        // Chinese
 
355
        { "Big5"                 , 0 , 0 , 5, "Chinese Traditional" },
 
356
        { "Big5-HKSCS"           , 0 , 0 , 5, "Chinese Traditional, Hong Kong" },
 
357
        { "GB18030"              , 0 , 0 , 5, "Chinese Simplified" },
 
358
        // Japanese
 
359
        { "JIS7"                 , 0 , 0 , 6, "Japanese (JIS7)" },
 
360
        { "Shift-JIS"            , 0 , 0 , 6, "Japanese (Shift-JIS)" },
 
361
        { "EUC-JP"               , 0 , 0 , 6, "Japanese (EUC-JP)" },
 
362
        // Other asiatic
 
363
        { "EUC-KR"               , 0 , 0 , 7, "Korean" },
 
364
        { "TSCII"                , 0 , 0 , 7, "Tamil" },
 
365
#endif
 
366
        
 
367
        // smart codecs that send in the local charset
 
368
        // West European
 
369
        { "ISO-8859-1 [UTF-8]"   , 1 , 0 , 1, "Western, Latin-1 - Unicode" },
 
370
        { "ISO-8859-15 [UTF-8]"  , 1 , 0 , 1, "Western, Latin-1 + Euro - Unicode" },
 
371
        { "IBM-850 [UTF-8]"      , 1 , 0 , 1, "IBM-850 - Unicode" },
 
372
        { "CP-1252 [UTF-8]"      , 1 , 0 , 1, "Western Codepage - Unicode" },
 
373
        { "ISO-8859-14 [UTF-8]"  , 1 , 0 , 1, "Celtic - Unicode" },
 
374
        { "ISO-8859-7 [UTF-8]"   , 1 , 0 , 1, "Greek - Unicode" },
 
375
        { "CP-1253 [UTF-8]"      , 1 , 0 , 1, "Greek Codepage - Unicode" },
 
376
        { "ISO-8859-10 [UTF-8]"  , 1 , 0 , 1, "Nordic - Unicode" },
 
377
        { "ISO-8859-3 [UTF-8]"   , 1 , 0 , 1, "South European - Unicode" },
 
378
        // East European
 
379
        { "ISO-8859-4 [UTF-8]"   , 1 , 0 , 2, "Baltic, Standard - Unicode" },
 
380
        { "ISO-8859-13 [UTF-8]"  , 1 , 0 , 2, "Baltic - Unicode" },
 
381
        { "CP-1257 [UTF-8]"      , 1 , 0 , 2, "Baltic Codepage - Unicode" },
 
382
        { "ISO-8859-2 [UTF-8]"   , 1 , 0 , 2, "Central European - Unicode" },
 
383
        { "CP-1250 [UTF-8]"      , 1 , 0 , 2, "Central European Codepage - Unicode" },
 
384
        { "ISO-8859-9 [UTF-8]"   , 1 , 0 , 2, "Turkish, Latin-5 - Unicode" },
 
385
        { "CP-1254 [UTF-8]"      , 1 , 0 , 2, "Turkish Codepage - Unicode" },
 
386
        // Cyrillic
 
387
        { "ISO-8859-5 [UTF-8]"   , 1 , 0 , 3, "Cyrillic, ISO - Unicode" },
 
388
        { "CP-1251 [UTF-8]"      , 1 , 0 , 3, "Cyrillic Codepage - Unicode" },
 
389
        { "KOI8-R [UTF-8]"       , 1 , 0 , 3, "Cyrillic, KOI - Unicode" },
 
390
        { "KOI8-U [UTF-8]"       , 1 , 0 , 3, "Cyrillic/Ukrainian - Unicode" },
 
391
        { "IBM-866 [UTF-8]"      , 1 , 0 , 3, "IBM-866 - Unicode" },
 
392
        // Middle Eastern
 
393
        { "ISO-8859-6 [UTF-8]"   , 1 , 0 , 4, "Arabic, Standard - Unicode" },
 
394
        { "CP-1256 [UTF-8]"      , 1 , 0 , 4, "Arabic Codepage - Unicode" },
 
395
        { "ISO-8859-8 [UTF-8]"   , 1 , 0 , 4, "Hebrew, visually ordered - Unicode" },
 
396
        { "ISO-8859-8-i [UTF-8]" , 1 , 0 , 4, "Hebrew, logically ordered - Unicode" },
 
397
        { "CP-1255 [UTF-8]"      , 1 , 0 , 4, "Hebrew Codepage - Unicode" },
 
398
        // Other asiatic
 
399
        { "TIS-620 [UTF-8]"      , 1 , 0 , 7, "Thai - Unicode" },
 
400
        { "CP874 [UTF-8]"        , 1 , 0 , 7, "Thai Codepage - Unicode" },
 
401
#ifndef QT_NO_BIG_CODECS
 
402
        // Chinese
 
403
        { "Big5 [UTF-8]"         , 1 , 0 , 5, "Chinese Traditional - Unicode" },
 
404
        { "Big5-HKSCS [UTF-8]"   , 1 , 0 , 5, "Chinese Traditional, Hong Kong - Unicode" },
 
405
        { "GB18030 [UTF-8]"      , 1 , 0 , 5, "Chinese Simplified - Unicode" },
 
406
        // Japanese
 
407
        { "JIS7 [UTF-8]"         , 1 , 0 , 6, "Japanese (JIS7) - Unicode" },
 
408
        { "Shift-JIS [UTF-8]"    , 1 , 0 , 6, "Japanese (Shift-JIS) - Unicode" },
 
409
        { "EUC-JP [UTF-8]"       , 1 , 0 , 6, "Japanese (EUC-JP) - Unicode" },
 
410
        // Other asiatic
 
411
        { "EUC-KR [UTF-8]"       , 1 , 0 , 7, "Korean - Unicode" },
 
412
        { "TSCII [UTF-8]"        , 1 , 0 , 7, "Tamil - Unicode" },
 
413
#endif
 
414
 
 
415
        // smart codecs that send in utf8
 
416
        // West European
 
417
        { "UTF-8 [ISO-8859-1]"   , 1 , 1 , 1, "Unicode - Western, Latin-1" },
 
418
        { "UTF-8 [ISO-8859-15]"  , 1 , 1 , 1, "Unicode - Western, Latin-1 + Euro" },
 
419
        { "UTF-8 [IBM-850]"      , 1 , 1 , 1, "Unicode - IBM-850" },
 
420
        { "UTF-8 [CP-1252]"      , 1 , 1 , 1, "Unicode - Western Codepage" },
 
421
        { "UTF-8 [ISO-8859-14]"  , 1 , 1 , 1, "Unicode - Celtic" },
 
422
        { "UTF-8 [ISO-8859-7]"   , 1 , 1 , 1, "Unicode - Greek" },
 
423
        { "UTF-8 [CP-1253]"      , 1 , 1 , 1, "Unicode - Greek Codepage" },
 
424
        { "UTF-8 [ISO-8859-10]"  , 1 , 1 , 1, "Unicode - Nordic" },
 
425
        { "UTF-8 [ISO-8859-3]"   , 1 , 1 , 1, "Unicode - South European" },
 
426
        // East European
 
427
        { "UTF-8 [ISO-8859-4]"   , 1 , 1 , 2, "Unicode - Baltic, Standard" },
 
428
        { "UTF-8 [ISO-8859-13]"  , 1 , 1 , 2, "Unicode - Baltic" },
 
429
        { "UTF-8 [CP-1257]"      , 1 , 1 , 2, "Unicode - Baltic Codepage" },
 
430
        { "UTF-8 [ISO-8859-2]"   , 1 , 1 , 2, "Unicode - Central European" },
 
431
        { "UTF-8 [CP-1250]"      , 1 , 1 , 2, "Unicode - Central European Codepage" },
 
432
        { "UTF-8 [ISO-8859-9]"   , 1 , 1 , 2, "Unicode - Turkish, Latin-5" },
 
433
        { "UTF-8 [CP-1254]"      , 1 , 1 , 2, "Unicode - Turkish Codepage" },
 
434
        // Cyrillic
 
435
        { "UTF-8 [ISO-8859-5]"   , 1 , 1 , 3, "Unicode - Cyrillic, ISO" },
 
436
        { "UTF-8 [CP-1251]"      , 1 , 1 , 3, "Unicode - Cyrillic Codepage" },
 
437
        { "UTF-8 [KOI8-R]"       , 1 , 1 , 3, "Unicode - Cyrillic, KOI" },
 
438
        { "UTF-8 [KOI8-U]"       , 1 , 1 , 3, "Unicode - Cyrillic/Ukrainian" },
 
439
        { "UTF-8 [IBM-866]"      , 1 , 1 , 3, "Unicode - IBM-866" },
 
440
        // Middle Eastern
 
441
        { "UTF-8 [ISO-8859-6]"   , 1 , 1 , 4, "Unicode - Arabic, Standard" },
 
442
        { "UTF-8 [CP-1256]"      , 1 , 1 , 4, "Unicode - Arabic Codepage" },
 
443
        { "UTF-8 [ISO-8859-8]"   , 1 , 1 , 4, "Unicode - Hebrew, visually ordered" },
 
444
        { "UTF-8 [ISO-8859-8-i]" , 1 , 1 , 4, "Unicode - Hebrew, logically ordered" },
 
445
        { "UTF-8 [CP-1255]"      , 1 , 1 , 4, "Unicode - Hebrew Codepage" },
 
446
        // Other asiatic
 
447
        { "UTF-8 [TIS-620]"      , 1 , 1 , 7, "Unicode - Thai" },
 
448
        { "UTF-8 [CP874]"        , 1 , 1 , 7, "Unicode - Thai Codepage" },
 
449
#ifndef QT_NO_BIG_CODECS
 
450
        // Chinese
 
451
        { "UTF-8 [Big5]"         , 1 , 1 , 5, "Unicode - Chinese Traditional" },
 
452
        { "UTF-8 [Big5-HKSCS]"   , 1 , 1 , 5, "Unicode - Chinese Traditional, Hong Kong" },
 
453
        { "UTF-8 [GB18030]"      , 1 , 1 , 5, "Unicode - Chinese Simplified" },
 
454
        // Japanese
 
455
        { "UTF-8 [JIS7]"         , 1 , 1 , 6, "Unicode - Japanese (JIS7)" },
 
456
        { "UTF-8 [Shift-JIS]"    , 1 , 1 , 6, "Unicode - Japanese (Shift-JIS)" },
 
457
        { "UTF-8 [EUC-JP]"       , 1 , 1 , 6, "Unicode - Japanese (EUC-JP)" },
 
458
        // Other asiatic
 
459
        { "UTF-8 [EUC-KR]"       , 1 , 1 , 7, "Unicode - Korean" },
 
460
        { "UTF-8 [TSCII]"        , 1 , 1 , 7, "Unicode - Tamil" },
 
461
#endif
 
462
        { 0                      , 0 , 0 , 0 , 0 }
 
463
};
 
464
 
 
465
KviLocale * KviLocale::m_pSelf = NULL;
 
466
unsigned int KviLocale::m_uCount = 0;
 
467
KviCString KviLocale::g_szLang = "";
 
468
 
 
469
KviLocale::KviLocale(QApplication * pApp, const QString & szLocaleDir, const QString & szForceLocaleDir)
 
470
{
 
471
        m_pApp = pApp;
 
472
 
 
473
        // first of all try to find out the current locale
 
474
        QString szLangFile = QString("%1/%2").arg(szForceLocaleDir, KVI_FORCE_LOCALE_FILE_NAME);
 
475
 
 
476
        if(KviFileUtils::fileExists(szLangFile))
 
477
        {
 
478
                QString szTmp;
 
479
                KviFileUtils::readFile(szLangFile,szTmp);
 
480
                g_szLang = szTmp;
 
481
        }
 
482
        if(g_szLang.isEmpty())
 
483
                g_szLang = KviEnvironment::getVariable("KVIRC_LANG");
 
484
#ifdef COMPILE_KDE_SUPPORT
 
485
        if(g_szLang.isEmpty())
 
486
                g_szLang = KviEnvironment::getVariable("KDE_LANG");
 
487
#endif //COMPILE_KDE_SUPPORT
 
488
        if(g_szLang.isEmpty())
 
489
                g_szLang = KviEnvironment::getVariable("LC_MESSAGES");
 
490
        if(g_szLang.isEmpty())
 
491
                g_szLang = KviEnvironment::getVariable("LANG");
 
492
        if(g_szLang.isEmpty())
 
493
                g_szLang = QLocale::system().name();
 
494
        if(g_szLang.isEmpty())
 
495
                g_szLang = "en";
 
496
        g_szLang.trim();
 
497
 
 
498
        g_szDefaultLocalePath = szLocaleDir;
 
499
 
 
500
        // the main catalogue is supposed to be kvirc_<language>.mo
 
501
        g_pMainCatalogue = new KviMessageCatalogue();
 
502
        // the catalogue dict
 
503
        g_pCatalogueDict = new KviPointerHashTable<const char *,KviMessageCatalogue>;
 
504
        g_pCatalogueDict->setAutoDelete(true);
 
505
 
 
506
        // the smart codec dict
 
507
        g_pSmartCodecDict = new KviPointerHashTable<const char *,KviSmartTextCodec>;
 
508
        // the Qt docs explicitly state that we shouldn't delete
 
509
        // the codecs by ourselves...
 
510
        g_pSmartCodecDict->setAutoDelete(false);
 
511
 
 
512
        if(g_szLang.hasData())
 
513
        {
 
514
                QString szBuffer;
 
515
                if(findCatalogue(szBuffer,"kvirc",szLocaleDir))
 
516
                {
 
517
                        g_pMainCatalogue->load(szBuffer);
 
518
                        g_pTranslator = new KviTranslator(m_pApp);
 
519
                        m_pApp->installTranslator(g_pTranslator);
 
520
                } else {
 
521
                        KviCString szTmp = g_szLang;
 
522
                        szTmp.cutFromFirst('.');
 
523
                        szTmp.cutFromFirst('_');
 
524
                        szTmp.cutFromFirst('@');
 
525
                        szTmp.toLower();
 
526
                        if(!(kvi_strEqualCI(szTmp.ptr(),"en") ||
 
527
                                kvi_strEqualCI(szTmp.ptr(),"c") ||
 
528
                                kvi_strEqualCI(szTmp.ptr(),"us") ||
 
529
                                kvi_strEqualCI(szTmp.ptr(),"gb") ||
 
530
                                kvi_strEqualCI(szTmp.ptr(),"posix")))
 
531
                        {
 
532
                                // FIXME: THIS IS NO LONGER VALID!!!
 
533
                                qDebug("Can't find the catalogue for locale \"%s\" (%s)",g_szLang.ptr(),szTmp.ptr());
 
534
                                qDebug("There is no such translation or the $LANG variable was incorrectly set");
 
535
                                qDebug("You can use $KVIRC_LANG to override the catalogue name");
 
536
                                qDebug("For example you can set KVIRC_LANG to it_IT to force usage of the it.mo catalogue");
 
537
                        }
 
538
                }
 
539
        }
 
540
 
 
541
        //g_pTextCodec = QTextCodec::codecForLocale();
 
542
        //if(!g_pTextCodec)
 
543
        //      g_pTextCodec = QTextCodec::codecForLocale();
 
544
}
 
545
 
 
546
KviLocale::~KviLocale()
 
547
{
 
548
        delete g_pMainCatalogue;
 
549
        delete g_pCatalogueDict;
 
550
        delete g_pSmartCodecDict;
 
551
        g_pMainCatalogue = 0;
 
552
        g_pCatalogueDict = 0;
 
553
        g_pSmartCodecDict = 0;
 
554
        if(g_pTranslator)
 
555
        {
 
556
                m_pApp->removeTranslator(g_pTranslator);
 
557
                delete g_pTranslator;
 
558
                g_pTranslator = 0;
 
559
        }
 
560
}
 
561
 
 
562
void KviLocale::init(QApplication * pApp, const QString & szLocaleDir, const QString & szForceLocaleDir)
 
563
{
 
564
        if((!m_pSelf) && (m_pSelf->count() == 0))
 
565
        {
 
566
                m_pSelf = new KviLocale(pApp,szLocaleDir,szForceLocaleDir);
 
567
                m_uCount++;
 
568
        }
 
569
}
 
570
 
 
571
void KviLocale::done()
 
572
{
 
573
        m_uCount--;
 
574
        if(m_pSelf->count() == 0)
 
575
                delete m_pSelf;
 
576
}
 
577
 
 
578
QTextCodec * KviLocale::codecForName(const char * pcName)
 
579
{
 
580
        KviCString szTmp = pcName;
 
581
        bool bSendUtf8;
 
582
 
 
583
        int iIdx = szTmp.findFirstIdx('[');
 
584
        if(iIdx != -1)
 
585
        {
 
586
                // Might be a composite codec: either UTF-8 [child codec] or child codec [UTF-8]
 
587
                KviSmartTextCodec * pCodec = g_pSmartCodecDict->find(pcName);
 
588
                if(pCodec)
 
589
                        return pCodec; // got cached copy
 
590
 
 
591
                if(kvi_strEqualCIN("UTF-8 [",pcName,7))
 
592
                {
 
593
                        // Likely a smart codec that sends UTF-8
 
594
                        szTmp.replaceAll("UTF-8 [","");
 
595
                        szTmp.replaceAll("]","");
 
596
                        bSendUtf8 = true;
 
597
                } else {
 
598
                        // Likely a smart codec that sends child encoding ?
 
599
                        szTmp.cutFromFirst(' ');
 
600
                        bSendUtf8 = false;
 
601
                }
 
602
 
 
603
                QTextCodec * pChildCodec = QTextCodec::codecForName(szTmp.ptr());
 
604
                if(pChildCodec)
 
605
                {
 
606
                        pCodec = new KviSmartTextCodec(pcName,pChildCodec,bSendUtf8);
 
607
 
 
608
                        if(pCodec->ok())
 
609
                        {
 
610
                                g_pSmartCodecDict->replace(pcName,pCodec);
 
611
                                return pCodec;
 
612
                        }
 
613
 
 
614
                        delete pCodec;
 
615
                } else {
 
616
                        // The name of the child codec was invalid: can't create such a smart codec.
 
617
                        // We probably screwed up the guess above related to the [ char.
 
618
                        // This code path is also triggered by the yircfuzzer by specifying completly invalid codec names.
 
619
                }
 
620
        }
 
621
 
 
622
        return QTextCodec::codecForName(pcName);
 
623
}
 
624
 
 
625
bool KviLocale::findCatalogue(QString & szBuffer, const QString & szName,const QString & szLocaleDir)
 
626
{
 
627
        KviCString szLocale = g_szLang;
 
628
 
 
629
        QString szLocDir = szLocaleDir;
 
630
        KviQString::ensureLastCharIs(szLocDir,KVI_PATH_SEPARATOR_CHAR);
 
631
 
 
632
        szBuffer = QString("%1%2_%3.mo").arg(szLocDir,szName).arg(szLocale.ptr());
 
633
 
 
634
        if(KviFileUtils::fileExists(szBuffer))
 
635
                return true;
 
636
 
 
637
        if(szLocale.findFirstIdx('.') != -1)
 
638
        {
 
639
                // things like en_GB.utf8
 
640
                // kill them
 
641
                szLocale.cutFromFirst('.');
 
642
 
 
643
                szBuffer = QString("%1%2_%3.mo").arg(szLocDir,szName).arg(szLocale.ptr());
 
644
                if(KviFileUtils::fileExists(szBuffer))
 
645
                        return true;
 
646
        }
 
647
 
 
648
        if(szLocale.findFirstIdx('@') != -1)
 
649
        {
 
650
                // things like @euro ?
 
651
                // kill them
 
652
                szLocale.cutFromFirst('@');
 
653
                szBuffer = QString("%1%2_%3.mo").arg(szLocDir,szName).arg(szLocale.ptr());
 
654
                if(KviFileUtils::fileExists(szBuffer))
 
655
                        return true;
 
656
        }
 
657
 
 
658
        if(szLocale.findFirstIdx('_') != -1)
 
659
        {
 
660
                // things like en_GB
 
661
                // kill them
 
662
                szLocale.cutFromFirst('_');
 
663
                szBuffer = QString("%1%2_%3.mo").arg(szLocDir,szName).arg(szLocale.ptr());
 
664
                if(KviFileUtils::fileExists(szBuffer))
 
665
                        return true;
 
666
        }
 
667
 
 
668
        // try the lower case version too
 
669
        szLocale.toLower();
 
670
        szBuffer = QString("%1%2_%3.mo").arg(szLocDir,szName).arg(szLocale.ptr());
 
671
        if(KviFileUtils::fileExists(szBuffer))
 
672
                return true;
 
673
 
 
674
        return false;
 
675
}
 
676
 
 
677
KviMessageCatalogue * KviLocale::loadCatalogue(const QString & szName, const QString & szLocaleDir)
 
678
{
 
679
        //qDebug("Looking up catalogue %s",szName.toUtf8().data());
 
680
        QString szBuffer;
 
681
 
 
682
        KviMessageCatalogue * pCatalogue = g_pCatalogueDict->find(szName.toUtf8().data());
 
683
        if(pCatalogue)
 
684
                return pCatalogue; // already loaded
 
685
 
 
686
        if(!findCatalogue(szBuffer,szName,szLocaleDir))
 
687
                return NULL;
 
688
 
 
689
        pCatalogue = new KviMessageCatalogue();
 
690
        if(pCatalogue->load(szBuffer))
 
691
        {
 
692
                g_pCatalogueDict->insert(szName.toUtf8().data(),pCatalogue);
 
693
                return pCatalogue;
 
694
        }
 
695
        delete pCatalogue;
 
696
        return NULL;
 
697
}
 
698
 
 
699
bool KviLocale::unloadCatalogue(const QString & szName)
 
700
{
 
701
        //qDebug("Unloading catalogue: %s",szName.toUtf8().data());
 
702
        return g_pCatalogueDict->remove(szName.toUtf8().data());
 
703
}
 
704
 
 
705
KviMessageCatalogue * KviLocale::getLoadedCatalogue(const QString & szName)
 
706
{
 
707
        return g_pCatalogueDict->find(szName.toUtf8().data());
 
708
}
 
709
 
 
710
const char * KviLocale::encodingGroup(int iIdx)
 
711
{
 
712
        if(iIdx > KVI_NUM_ENCODING_GROUPS)
 
713
                return encoding_groups[KVI_NUM_ENCODING_GROUPS];
 
714
        return encoding_groups[iIdx];
 
715
}
 
716
 
 
717
KviLocale::EncodingDescription * KviLocale::encodingDescription(int iIdx)
 
718
{
 
719
        if(iIdx > KVI_NUM_ENCODINGS)
 
720
                return &(supported_encodings[KVI_NUM_ENCODINGS]);
 
721
        return &(supported_encodings[iIdx]);
 
722
}
 
723
 
 
724
const char * KviLocale::translate(const char * pcText, const char * pcContext)
 
725
{
 
726
        if(!pcContext)
 
727
                return g_pMainCatalogue->translate(pcText);
 
728
 
 
729
        KviMessageCatalogue * pCatalogue = g_pCatalogueDict->find(pcContext);
 
730
        if(!pCatalogue)
 
731
        {
 
732
                pCatalogue = loadCatalogue(QString::fromUtf8(pcContext),g_szDefaultLocalePath);
 
733
                if(!pCatalogue)
 
734
                {
 
735
                        // Fake it....
 
736
                        pCatalogue = new KviMessageCatalogue();
 
737
                        g_pCatalogueDict->insert(pcContext,pCatalogue);
 
738
                }
 
739
        }
 
740
        return pCatalogue->translate(pcText);
 
741
}
 
742
 
 
743
const QString & KviLocale::translateToQString(const char * pcText, const char * pcContext)
 
744
{
 
745
        if(!pcContext)
 
746
                return g_pMainCatalogue->translateToQString(pcText);
 
747
        
 
748
        KviMessageCatalogue * pCatalogue = g_pCatalogueDict->find(pcContext);
 
749
        if(!pCatalogue)
 
750
        {
 
751
                pCatalogue = loadCatalogue(QString::fromUtf8(pcContext),g_szDefaultLocalePath);
 
752
                if(!pCatalogue)
 
753
                {
 
754
                        // Fake it....
 
755
                        pCatalogue = new KviMessageCatalogue();
 
756
                        g_pCatalogueDict->insert(pcContext,pCatalogue);
 
757
                }
 
758
        }
 
759
        return pCatalogue->translateToQString(pcText);
 
760
}