Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-charset-map.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; -*- */
2 /*
3  * Authors:
4  *   Michael Zucchi <notzed@ximian.com>
5  *   Jeffrey Stedfast <fejj@ximian.com>
6  *   Dan Winship <danw@ximian.com>
7  *
8  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of version 2 of the GNU Lesser General Public
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22  * USA
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <locale.h>
33 #ifdef HAVE_CODESET
34 #include <langinfo.h>
35 #endif
36 #include <iconv.h>
37 #include <errno.h>
38
39 #include "camel-iconv.h"
40
41 /*
42  * if you want to build the charset map, compile this with something like:
43  *    gcc -DBUILD_MAP camel-charset-map.c `pkg-config --cflags --libs glib-2.0`
44  * (plus any -I/-L/-l flags you need for iconv), then run it as
45  *    ./a.out > camel-charset-map-private.h
46  *
47  * Note that the big-endian variant isn't tested...
48  *
49  * The tables genereated work like this:
50  *
51  * An indirect array for each page of unicode character
52  * Each array element has an indirect pointer to one of the bytes of
53  * the generated bitmask.
54  */
55
56 #ifdef BUILD_MAP
57
58 static struct {
59         gchar *name;        /* charset name */
60         gint multibyte;     /* charset type */
61         guint bit;  /* assigned bit */
62 } tables[] = {
63         /* These are the 8bit character sets (other than iso-8859-1,
64          * which is special-cased) which are supported by both other
65          * mailers and the GNOME environment. Note that the order
66          * they're listed in is the order they'll be tried in, so put
67          * the more-popular ones first.
68          */
69         { "iso-8859-2",   0, 0 },  /* Central/Eastern European */
70         { "iso-8859-4",   0, 0 },  /* Baltic */
71         { "koi8-r",       0, 0 },  /* Russian */
72         { "koi8-u",       0, 0 },  /* Ukranian */
73         { "iso-8859-5",   0, 0 },  /* Least-popular Russian encoding */
74         { "iso-8859-6",   0, 0 },  /* Arabic */
75         { "iso-8859-7",   0, 0 },  /* Greek */
76         { "iso-8859-8",   0, 0 },  /* Hebrew; Visual */
77         { "iso-8859-9",   0, 0 },  /* Turkish */
78         { "iso-8859-13",  0, 0 },  /* Baltic again */
79         { "iso-8859-15",  0, 0 },  /* New-and-improved iso-8859-1, but most
80                                     * programs that support this support UTF8
81                                     */
82         { "windows-1251", 0, 0 },  /* Russian */
83
84         /* These are the multibyte character sets which are commonly
85          * supported by other mail clients. Note: order for multibyte
86          * charsets does not affect priority unlike the 8bit charsets
87          * listed above.
88          */
89         { "iso-2022-jp",  1, 0 },  /* Japanese designed for use over the Net */
90         { "Shift-JIS",    1, 0 },  /* Japanese as used by Windows and MacOS systems */
91         { "euc-jp",       1, 0 },  /* Japanese traditionally used on Unix systems */
92         { "euc-kr",       1, 0 },  /* Korean */
93         { "iso-2022-kr",  1, 0 },  /* Korean (less popular than euc-kr) */
94         { "gb2312",       1, 0 },  /* Simplified Chinese */
95         { "Big5",         1, 0 },  /* Traditional Chinese */
96         { "euc-tw",       1, 0 },
97         { NULL,           0, 0 }
98 };
99
100 guint encoding_map[256 * 256];
101
102 #if G_BYTE_ORDER == G_BIG_ENDIAN
103 #define UCS "UCS-4BE"
104 #else
105 #define UCS "UCS-4LE"
106 #endif
107
108 static guint
109 block_hash (gconstpointer v)
110 {
111         const gchar *p = v;
112         guint32 h = *p++;
113         gint i;
114
115         for (i = 0; i < 256; i++)
116                 h = (h << 5) - h + *p++;
117
118         return h;
119 }
120
121 static gint
122 block_equal (gconstpointer v1,
123              gconstpointer v2)
124 {
125         return !memcmp (v1, v2, 256);
126 }
127
128 gint main (gint argc, gchar **argv)
129 {
130         guchar *block = NULL;
131         guint bit = 0x01;
132         GHashTable *table_hash;
133         gsize inleft, outleft;
134         gchar *inbuf, *outbuf;
135         guint32 out[128], c;
136         gchar in[128];
137         gint i, j, k;
138         gint bytes;
139         iconv_t cd;
140
141         /* dont count the terminator */
142         bytes = (G_N_ELEMENTS (tables) + 7 - 1) / 8;
143         g_assert (bytes <= 4);
144
145         for (i = 0; i < 128; i++)
146                 in[i] = i + 128;
147
148         for (j = 0; tables[j].name && !tables[j].multibyte; j++) {
149                 cd = iconv_open (UCS, tables[j].name);
150                 inbuf = in;
151                 inleft = sizeof (in);
152                 outbuf = (gchar *) out;
153                 outleft = sizeof (out);
154                 while (iconv (cd, &inbuf, &inleft, &outbuf, &outleft) == -1) {
155                         if (errno == EILSEQ) {
156                                 inbuf++;
157                                 inleft--;
158                         } else {
159                                 g_warning (
160                                         "iconv (%s->UCS4, ..., %d, ..., %d): %s",
161                                         tables[j].name, inleft, outleft,
162                                         g_strerror (errno));
163                                 exit (1);
164                         }
165                 }
166                 iconv_close (cd);
167
168                 for (i = 0; i < 128 - outleft / 4; i++) {
169                         encoding_map[i] |= bit;
170                         encoding_map[out[i]] |= bit;
171                 }
172
173                 tables[j].bit = bit;
174                 bit <<= 1;
175         }
176
177         /* Mutibyte tables */
178         for (; tables[j].name && tables[j].multibyte; j++) {
179                 cd = iconv_open (tables[j].name, UCS);
180                 if (cd == (iconv_t) -1)
181                         continue;
182
183                 for (c = 128, i = 0; c < 65535 && i < 65535; c++) {
184                         inbuf = (gchar *) &c;
185                         inleft = sizeof (c);
186                         outbuf = in;
187                         outleft = sizeof (in);
188
189                         if (iconv (cd, &inbuf, &inleft, &outbuf, &outleft) != (gsize) -1) {
190                                 /* this is a legal character in charset table[j].name */
191                                 iconv (cd, NULL, NULL, &outbuf, &outleft);
192                                 encoding_map[i++] |= bit;
193                                 encoding_map[c] |= bit;
194                         } else {
195                                 /* reset the iconv descriptor */
196                                 iconv (cd, NULL, NULL, NULL, NULL);
197                         }
198                 }
199
200                 iconv_close (cd);
201
202                 tables[j].bit = bit;
203                 bit <<= 1;
204         }
205
206         printf ("/* This file is automatically generated: DO NOT EDIT */\n\n");
207
208         table_hash = g_hash_table_new_full (block_hash, block_equal, g_free, g_free);
209
210         for (i = 0; i < 256; i++) {
211                 for (k = 0; k < bytes; k++) {
212                         gchar name[32], *alias;
213                         gint has_bits = FALSE;
214
215                         if (!block) {
216                                 /* we reuse malloc'd blocks that are not added to the
217                                  * hash table to avoid unnecessary malloc/free's */
218                                 block = g_malloc (256);
219                         }
220
221                         for (j = 0; j < 256; j++) {
222                                 if ((block[j] = (encoding_map[i * 256 + j] >> (k * 8)) & 0xff))
223                                         has_bits = TRUE;
224                         }
225
226                         if (!has_bits)
227                                 continue;
228
229                         sprintf (name, "m%02x%x", i, k);
230
231                         if ((alias = g_hash_table_lookup (table_hash, block))) {
232                                 /* this block is identical to an earlier block, just alias it */
233                                 printf ("#define %s %s\n\n", name, alias);
234                         } else {
235                                 /* unique block, dump it */
236                                 g_hash_table_insert (table_hash, block, g_strdup (name));
237
238                                 printf ("static guchar %s[256] = {\n\t", name);
239                                 for (j = 0; j < 256; j++) {
240                                         printf ("0x%02x, ", block[j]);
241                                         if (((j + 1) & 7) == 0 && j < 255)
242                                                 printf ("\n\t");
243                                 }
244                                 printf ("\n};\n\n");
245
246                                 /* force the next loop to malloc a new block */
247                                 block = NULL;
248                         }
249                 }
250         }
251
252         g_hash_table_destroy (table_hash);
253         g_free (block);
254
255         printf ("static const struct {\n");
256         for (k = 0; k < bytes; k++)
257                 printf ("\tconst guchar *bits%d;\n", k);
258
259         printf ("} camel_charmap[256] = {\n\t");
260         for (i = 0; i < 256; i++) {
261                 printf ("{ ");
262                 for (k = 0; k < bytes; k++) {
263                         for (j = 0; j < 256; j++) {
264                                 if ((encoding_map[i * 256 + j] & (0xff << (k * 8))) != 0)
265                                         break;
266                         }
267
268                         if (j < 256)
269                                 printf ("m%02x%x, ", i, k);
270                         else
271                                 printf ("NULL, ");
272                 }
273
274                 printf ("}, ");
275                 if (((i + 1) & 3) == 0 && i < 255)
276                         printf ("\n\t");
277         }
278         printf ("\n};\n\n");
279
280         printf (
281                 "static const struct {\n"
282                 "\tconst gchar *name;\n"
283                 "\tguint bit;\n"
284                 "} camel_charinfo[] = {\n");
285         for (j = 0; tables[j].name; j++)
286                 printf (
287                         "\t{ \"%s\", 0x%08x },\n",
288                         tables[j].name, tables[j].bit);
289         printf ("};\n\n");
290
291         printf ("#define charset_mask(x) \\\n");
292         for (k = 0; k < bytes; k++) {
293                 if (k != 0)
294                         printf ("\t| ");
295                 else
296                         printf ("\t");
297
298                 printf (
299                         "(camel_charmap[(x) >> 8].bits%d ? "
300                         "camel_charmap[(x) >> 8].bits%d[(x) & 0xff] << %d : 0)",
301                         k, k, k * 8);
302
303                 if (k < bytes - 1)
304                         printf ("\t\\\n");
305         }
306         printf ("\n\n");
307
308         return 0;
309 }
310
311 #else
312
313 #include "camel-charset-map.h"
314 #include "camel-charset-map-private.h"
315 #include "camel-utf8.h"
316
317 void
318 camel_charset_init (CamelCharset *c)
319 {
320         c->mask = (guint) ~0;
321         c->level = 0;
322 }
323
324 void
325 camel_charset_step (CamelCharset *cc,
326                     const gchar *in,
327                     gint len)
328 {
329         const guchar *inptr = (const guchar *) in;
330         const guchar *inend = inptr + len;
331         register guint mask;
332         register gint level;
333         register guint32 c;
334
335         mask = cc->mask;
336         level = cc->level;
337
338         /* check what charset a given string will fit in */
339         while ((c = camel_utf8_getc_limit (&inptr, inend)) != 0xffff) {
340                 if (c < 0xffff) {
341                         mask &= charset_mask (c);
342
343                         if (c >= 128 && c < 256)
344                                 level = MAX (level, 1);
345                         else if (c >= 256)
346                                 level = 2;
347                 } else {
348                         mask = 0;
349                         level = 2;
350                         break;
351                 }
352         }
353
354         cc->mask = mask;
355         cc->level = level;
356 }
357
358 /* gets the best charset from the mask of chars in it */
359 static const gchar *
360 camel_charset_best_mask (guint mask)
361 {
362         const gchar *locale_lang, *lang;
363         gint i;
364
365         locale_lang = camel_iconv_locale_language ();
366         for (i = 0; i < G_N_ELEMENTS (camel_charinfo); i++) {
367                 if (camel_charinfo[i].bit & mask) {
368                         lang = camel_iconv_charset_language (camel_charinfo[i].name);
369
370                         if (!locale_lang || (lang && !strncmp (locale_lang, lang, 2)))
371                                 return camel_charinfo[i].name;
372                 }
373         }
374
375         return "UTF-8";
376 }
377
378 const gchar *
379 camel_charset_best_name (CamelCharset *charset)
380 {
381         if (charset->level == 1)
382                 return "ISO-8859-1";
383         else if (charset->level == 2)
384                 return camel_charset_best_mask (charset->mask);
385         else
386                 return NULL;
387 }
388
389 /* finds the minimum charset for this string NULL means US-ASCII */
390 const gchar *
391 camel_charset_best (const gchar *in,
392                     gint len)
393 {
394         CamelCharset charset;
395
396         camel_charset_init (&charset);
397         camel_charset_step (&charset, in, len);
398         return camel_charset_best_name (&charset);
399 }
400
401 /**
402  * camel_charset_iso_to_windows:
403  * @isocharset: a canonicalised ISO charset
404  *
405  * Returns: the equivalent Windows charset.
406  **/
407 const gchar *
408 camel_charset_iso_to_windows (const gchar *isocharset)
409 {
410         /* According to http://czyborra.com/charsets/codepages.html,
411          * the charset mapping is as follows:
412          *
413          * us-ascii    maps to windows-cp1252
414          * iso-8859-1  maps to windows-cp1252
415          * iso-8859-2  maps to windows-cp1250
416          * iso-8859-3  maps to windows-cp????
417          * iso-8859-4  maps to windows-cp????
418          * iso-8859-5  maps to windows-cp1251
419          * iso-8859-6  maps to windows-cp1256
420          * iso-8859-7  maps to windows-cp1253
421          * iso-8859-8  maps to windows-cp1255
422          * iso-8859-9  maps to windows-cp1254
423          * iso-8859-10 maps to windows-cp????
424          * iso-8859-11 maps to windows-cp????
425          * iso-8859-12 maps to windows-cp????
426          * iso-8859-13 maps to windows-cp1257
427          *
428          * Assumptions:
429          *  - I'm going to assume that since iso-8859-4 and
430          *    iso-8859-13 are Baltic that it also maps to
431          *    windows-cp1257.
432          */
433
434         if (!g_ascii_strcasecmp (isocharset, "iso-8859-1") || !g_ascii_strcasecmp (isocharset, "us-ascii"))
435                 return "windows-cp1252";
436         else if (!g_ascii_strcasecmp (isocharset, "iso-8859-2"))
437                 return "windows-cp1250";
438         else if (!g_ascii_strcasecmp (isocharset, "iso-8859-4"))
439                 return "windows-cp1257";
440         else if (!g_ascii_strcasecmp (isocharset, "iso-8859-5"))
441                 return "windows-cp1251";
442         else if (!g_ascii_strcasecmp (isocharset, "iso-8859-6"))
443                 return "windows-cp1256";
444         else if (!g_ascii_strcasecmp (isocharset, "iso-8859-7"))
445                 return "windows-cp1253";
446         else if (!g_ascii_strcasecmp (isocharset, "iso-8859-8"))
447                 return "windows-cp1255";
448         else if (!g_ascii_strcasecmp (isocharset, "iso-8859-9"))
449                 return "windows-cp1254";
450         else if (!g_ascii_strcasecmp (isocharset, "iso-8859-13"))
451                 return "windows-cp1257";
452
453         return isocharset;
454 }
455
456 #endif /* BUILD_MAP */