Added Latin1 support to giconv
[mono.git] / eglib / src / giconv.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Copyright (C) 2011 Jeffrey Stedfast
4  *
5  *  Permission is hereby granted, free of charge, to any person
6  *  obtaining a copy of this software and associated documentation
7  *  files (the "Software"), to deal in the Software without
8  *  restriction, including without limitation the rights to use, copy,
9  *  modify, merge, publish, distribute, sublicense, and/or sell copies
10  *  of the Software, and to permit persons to whom the Software is
11  *  furnished to do so, subject to the following conditions:
12  *
13  *  The above copyright notice and this permission notice shall be
14  *  included in all copies or substantial portions of the Software.
15  *
16  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20  *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21  *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  *  DEALINGS IN THE SOFTWARE.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <glib.h>
31 #include <string.h>
32 #ifdef HAVE_ICONV_H
33 #include <iconv.h>
34 #endif
35 #include <errno.h>
36
37 typedef enum {
38         LittleEndian,
39         BigEndian
40 } Endian;
41
42 typedef int (* Decoder) (char **inbytes, size_t *inbytesleft, gunichar *outchar);
43 typedef int (* Encoder) (gunichar c, char **outbytes, size_t *outbytesleft);
44
45 struct _GIConv {
46         Decoder decode;
47         Encoder encode;
48         gunichar c;
49 #ifdef HAVE_ICONV
50         iconv_t cd;
51 #endif
52 };
53
54 static int decode_utf32be (char **inbytes, size_t *inbytesleft, gunichar *outchar);
55 static int encode_utf32be (gunichar c, char **outbytes, size_t *outbytesleft);
56
57 static int decode_utf32le (char **inbytes, size_t *inbytesleft, gunichar *outchar);
58 static int encode_utf32le (gunichar c, char **outbytes, size_t *outbytesleft);
59
60 static int decode_utf16be (char **inbytes, size_t *inbytesleft, gunichar *outchar);
61 static int encode_utf16be (gunichar c, char **outbytes, size_t *outbytesleft);
62
63 static int decode_utf16le (char **inbytes, size_t *inbytesleft, gunichar *outchar);
64 static int encode_utf16le (gunichar c, char **outbytes, size_t *outbytesleft);
65
66 static int decode_utf32 (char **inbytes, size_t *inbytesleft, gunichar *outchar);
67 static int encode_utf32 (gunichar c, char **outbytes, size_t *outbytesleft);
68
69 static int decode_utf16 (char **inbytes, size_t *inbytesleft, gunichar *outchar);
70 static int encode_utf16 (gunichar c, char **outbytes, size_t *outbytesleft);
71
72 static int decode_utf8 (char **inbytes, size_t *inbytesleft, gunichar *outchar);
73 static int encode_utf8 (gunichar c, char **outbytes, size_t *outbytesleft);
74
75 static int decode_latin1 (char **inbytes, size_t *inbytesleft, gunichar *outchar);
76 static int encode_latin1 (gunichar c, char **outbytes, size_t *outbytesleft);
77
78 static struct {
79         const char *name;
80         Decoder decoder;
81         Encoder encoder;
82 } charsets[] = {
83         { "ISO-8859-1", decode_latin1,  encode_latin1  },
84         { "ISO8859-1",  decode_latin1,  encode_latin1  },
85         { "UTF-32BE",   decode_utf32be, encode_utf32be },
86         { "UTF-32LE",   decode_utf32le, encode_utf32le },
87         { "UTF-16BE",   decode_utf16be, encode_utf16be },
88         { "UTF-16LE",   decode_utf16le, encode_utf16le },
89         { "UTF-32",     decode_utf32,   encode_utf32   },
90         { "UTF-16",     decode_utf16,   encode_utf16   },
91         { "UTF-8",      decode_utf8,    encode_utf8    },
92         { "US-ASCII",   decode_latin1,  encode_latin1  },
93         { "Latin1",     decode_latin1,  encode_latin1  },
94         { "ASCII",      decode_latin1,  encode_latin1  },
95         { "UTF32",      decode_utf32,   encode_utf32   },
96         { "UTF16",      decode_utf16,   encode_utf16   },
97         { "UTF8",       decode_utf8,    encode_utf8    },
98 };
99
100
101 GIConv
102 g_iconv_open (const char *to_charset, const char *from_charset)
103 {
104 #ifdef HAVE_ICONV
105         iconv_t icd = (iconv_t) -1;
106 #endif
107         Decoder decoder = NULL;
108         Encoder encoder = NULL;
109         GIConv cd;
110         guint i;
111         
112         if (!to_charset || !from_charset || !to_charset[0] || !from_charset[0])
113                 return (GIConv) -1;
114         
115         for (i = 0; i < G_N_ELEMENTS (charsets); i++) {
116                 if (!g_ascii_strcasecmp (charsets[i].name, from_charset))
117                         decoder = charsets[i].decoder;
118                 
119                 if (!g_ascii_strcasecmp (charsets[i].name, to_charset))
120                         encoder = charsets[i].encoder;
121         }
122         
123         if (encoder == NULL || decoder == NULL) {
124 #ifdef HAVE_ICONV
125                 if ((icd = iconv_open (to_charset, from_charset)) == (iconv_t) -1)
126                         return (GIConv) -1;
127 #else
128                 return (GIConv) -1;
129 #endif
130         }
131         
132         cd = (GIConv) g_malloc (sizeof (struct _GIConv));
133         cd->decode = decoder;
134         cd->encode = encoder;
135         cd->c = -1;
136         
137 #ifdef HAVE_ICONV
138         cd->cd = icd;
139 #endif
140         
141         return cd;
142 }
143
144 int
145 g_iconv_close (GIConv cd)
146 {
147 #ifdef HAVE_ICONV
148         if (cd->cd != (iconv_t) -1)
149                 iconv_close (cd->cd);
150 #endif
151         
152         g_free (cd);
153         
154         return 0;
155 }
156
157 gsize
158 g_iconv (GIConv cd, char **inbytes, size_t *inbytesleft,
159          char **outbytes, size_t *outbytesleft)
160 {
161         size_t inleft, outleft;
162         char *inptr, *outptr;
163         gsize rc = 0;
164         gunichar c;
165         
166 #ifdef HAVE_ICONV
167         if (cd->cd != (iconv_t) -1)
168                 return iconv (cd->cd, inbytes, inbytesleft, outbytes, outbytesleft);
169 #endif
170         
171         if (outbytes == NULL || outbytesleft == NULL) {
172                 /* reset converter */
173                 cd->c = -1;
174                 return 0;
175         }
176         
177         inleft = inbytesleft ? *inbytesleft : 0;
178         inptr = inbytes ? *inbytes : NULL;
179         outleft = *outbytesleft;
180         outptr = *outbytes;
181         c = cd->c;
182         
183         do {
184                 if (c == (gunichar) -1 && cd->decode (&inptr, &inleft, &c) == -1) {
185                         rc = -1;
186                         break;
187                 }
188                 
189                 if (cd->encode (c, &outptr, &outleft) == -1) {
190                         rc = -1;
191                         break;
192                 }
193                 
194                 c = -1;
195         } while (inleft > 0 && outleft > 0);
196         
197         if (inbytesleft)
198                 *inbytesleft = inleft;
199         
200         if (inbytes)
201                 *inbytes = inptr;
202         
203         *outbytesleft = outleft;
204         *outbytes = outptr;
205         cd->c = c;
206         
207         return rc;
208 }
209
210
211 static int
212 decode_utf32_be_or_le (Endian endian, char **inbytes, size_t *inbytesleft, gunichar *outchar)
213 {
214         gunichar *inptr = (gunichar *) *inbytes;
215         size_t inleft = *inbytesleft;
216         gunichar c;
217         
218         if (inleft < 4) {
219                 errno = EINVAL;
220                 return -1;
221         }
222         
223         if (endian == BigEndian)
224                 c = GUINT32_FROM_BE (*inptr);
225         else
226                 c = GUINT32_FROM_LE (*inptr);
227         
228         inleft -= 4;
229         inptr++;
230         
231         if (c >= 2147483648UL) {
232                 errno = EILSEQ;
233                 return -1;
234         }
235         
236         *inbytes = (char *) inptr;
237         *inbytesleft = inleft;
238         *outchar = c;
239         
240         return 0;
241 }
242
243 static int
244 decode_utf32be (char **inbytes, size_t *inbytesleft, gunichar *outchar)
245 {
246         return decode_utf32_be_or_le (BigEndian, inbytes, inbytesleft, outchar);
247 }
248
249 static int
250 decode_utf32le (char **inbytes, size_t *inbytesleft, gunichar *outchar)
251 {
252         return decode_utf32_be_or_le (LittleEndian, inbytes, inbytesleft, outchar);
253 }
254
255 static int
256 decode_utf32 (char **inbytes, size_t *inbytesleft, gunichar *outchar)
257 {
258 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
259         return decode_utf32_be_or_le (LittleEndian, inbytes, inbytesleft, outchar);
260 #else
261         return decode_utf32_be_or_le (BigEndian, inbytes, inbytesleft, outchar);
262 #endif
263 }
264
265 static int
266 encode_utf32_be_or_le (Endian endian, gunichar c, char **outbytes, size_t *outbytesleft)
267 {
268         gunichar *outptr = (gunichar *) *outbytes;
269         size_t outleft = *outbytesleft;
270         
271         if (outleft < 4) {
272                 errno = E2BIG;
273                 return -1;
274         }
275         
276         if (endian == BigEndian)
277                 *outptr++ = GUINT32_TO_BE (c);
278         else
279                 *outptr++ = GUINT32_TO_LE (c);
280         
281         outleft -= 4;
282         
283         *outbytes = (char *) outptr;
284         *outbytesleft = outleft;
285         
286         return 0;
287 }
288
289 static int
290 encode_utf32be (gunichar c, char **outbytes, size_t *outbytesleft)
291 {
292         return encode_utf32_be_or_le (BigEndian, c, outbytes, outbytesleft);
293 }
294
295 static int
296 encode_utf32le (gunichar c, char **outbytes, size_t *outbytesleft)
297 {
298         return encode_utf32_be_or_le (LittleEndian, c, outbytes, outbytesleft);
299 }
300
301 static int
302 encode_utf32 (gunichar c, char **outbytes, size_t *outbytesleft)
303 {
304 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
305         return encode_utf32_be_or_le (LittleEndian, c, outbytes, outbytesleft);
306 #else
307         return encode_utf32_be_or_le (BigEndian, c, outbytes, outbytesleft);
308 #endif
309 }
310
311 static int
312 decode_utf16_be_or_le (Endian endian, char **inbytes, size_t *inbytesleft, gunichar *outchar)
313 {
314         gunichar2 *inptr = (gunichar2 *) *inbytes;
315         size_t inleft = *inbytesleft;
316         gunichar2 c;
317         gunichar u;
318         
319         if (inleft < 2) {
320                 errno = EINVAL;
321                 return -1;
322         }
323         
324         if (endian == BigEndian)
325                 u = GUINT16_FROM_BE (*inptr);
326         else
327                 u = GUINT16_FROM_LE (*inptr);
328         
329         inleft -= 2;
330         inptr++;
331         
332         if (u >= 0xdc00 && u <= 0xdfff) {
333                 errno = EILSEQ;
334                 return -1;
335         } else if (u >= 0xd800 && u <= 0xdbff) {
336                 if (inleft < 2) {
337                         errno = EINVAL;
338                         return -1;
339                 }
340                 
341                 if (endian == BigEndian)
342                         c = GUINT16_FROM_BE (*inptr);
343                 else
344                         c = GUINT16_FROM_LE (*inptr);
345                 
346                 inleft -= 2;
347                 inptr++;
348                 
349                 if (c < 0xdc00 || c > 0xdfff) {
350                         errno = EILSEQ;
351                         return -1;
352                 }
353                 
354                 u = ((u - 0xd800) << 10) + (c - 0xdc00) + 0x0010000UL;
355         }
356         
357         *inbytes = (char *) inptr;
358         *inbytesleft = inleft;
359         *outchar = u;
360         
361         return 0;
362 }
363
364 static int
365 decode_utf16be (char **inbytes, size_t *inbytesleft, gunichar *outchar)
366 {
367         return decode_utf16_be_or_le (BigEndian, inbytes, inbytesleft, outchar);
368 }
369
370 static int
371 decode_utf16le (char **inbytes, size_t *inbytesleft, gunichar *outchar)
372 {
373         return decode_utf16_be_or_le (LittleEndian, inbytes, inbytesleft, outchar);
374 }
375
376 static int
377 decode_utf16 (char **inbytes, size_t *inbytesleft, gunichar *outchar)
378 {
379 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
380         return decode_utf16_be_or_le (LittleEndian, inbytes, inbytesleft, outchar);
381 #else
382         return decode_utf16_be_or_le (BigEndian, inbytes, inbytesleft, outchar);
383 #endif
384 }
385
386 static int
387 encode_utf16_be_or_le (Endian endian, gunichar c, char **outbytes, size_t *outbytesleft)
388 {
389         gunichar2 *outptr = (gunichar2 *) *outbytes;
390         size_t outleft = *outbytesleft;
391         gunichar2 ch;
392         gunichar c2;
393         
394         if (outleft < 2) {
395                 errno = E2BIG;
396                 return -1;
397         }
398         
399         if (c <= 0xffff && (c < 0xd800 || c > 0xdfff)) {
400                 ch = (gunichar2) c;
401                 
402                 if (endian == BigEndian)
403                         *outptr++ = GUINT16_TO_BE (ch);
404                 else
405                         *outptr++ = GUINT16_TO_LE (ch);
406                 
407                 outleft -= 2;
408         } else if (outleft < 4) {
409                 errno = E2BIG;
410                 return -1;
411         } else {
412                 c2 = c - 0x10000;
413                 
414                 ch = (gunichar2) ((c2 >> 10) + 0xd800);
415                 if (endian == BigEndian)
416                         *outptr++ = GUINT16_TO_BE (ch);
417                 else
418                         *outptr++ = GUINT16_TO_LE (ch);
419                 
420                 ch = (gunichar2) ((c2 & 0x3ff) + 0xdc00);
421                 if (endian == BigEndian)
422                         *outptr++ = GUINT16_TO_BE (ch);
423                 else
424                         *outptr++ = GUINT16_TO_LE (ch);
425                 
426                 outleft -= 4;
427         }
428         
429         *outbytes = (char *) outptr;
430         *outbytesleft = outleft;
431         
432         return 0;
433 }
434
435 static int
436 encode_utf16be (gunichar c, char **outbytes, size_t *outbytesleft)
437 {
438         return encode_utf16_be_or_le (BigEndian, c, outbytes, outbytesleft);
439 }
440
441 static int
442 encode_utf16le (gunichar c, char **outbytes, size_t *outbytesleft)
443 {
444         return encode_utf16_be_or_le (LittleEndian, c, outbytes, outbytesleft);
445 }
446
447 static int
448 encode_utf16 (gunichar c, char **outbytes, size_t *outbytesleft)
449 {
450 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
451         return encode_utf16_be_or_le (LittleEndian, c, outbytes, outbytesleft);
452 #else
453         return encode_utf16_be_or_le (BigEndian, c, outbytes, outbytesleft);
454 #endif
455 }
456
457 static int
458 decode_utf8 (char **inbytes, size_t *inbytesleft, gunichar *outchar)
459 {
460         size_t inleft = *inbytesleft;
461         char *inptr = *inbytes;
462         size_t i, len = 0;
463         unsigned char c;
464         gunichar u;
465         
466         if (inleft < 1)
467                 return 0;
468         
469         c = *inptr++;
470         
471         if (c < 0x80) {
472                 /* simple ascii case */
473                 len = 1;
474         } else if (c < 0xe0) {
475                 c &= 0x1f;
476                 len = 2;
477         } else if (c < 0xf0) {
478                 c &= 0x0f;
479                 len = 3;
480         } else if (c < 0xf8) {
481                 c &= 0x07;
482                 len = 4;
483         } else if (c < 0xfc) {
484                 c &= 0x03;
485                 len = 5;
486         } else if (c < 0xfe) {
487                 c &= 0x01;
488                 len = 6;
489         } else {
490                 errno = EILSEQ;
491                 return -1;
492         }
493         
494         if (len > inleft) {
495                 errno = EINVAL;
496                 return -1;
497         }
498         
499         u = c;
500         for (i = 1; i < len; i++) {
501                 u = (u << 6) | ((*inptr) & 0x3f);
502                 inptr++;
503         }
504         
505         *inbytesleft = inleft - len;
506         *inbytes = inptr;
507         *outchar = u;
508         
509         return 0;
510 }
511
512 static int
513 encode_utf8 (gunichar c, char **outbytes, size_t *outbytesleft)
514 {
515         size_t outleft = *outbytesleft;
516         char *outptr = *outbytes;
517         size_t len, i;
518         int base;
519         
520         if (c < 128UL) {
521                 base = 0;
522                 len = 1;
523         } else if (c < 2048UL) {
524                 base = 192;
525                 len = 2;
526         } else if (c < 65536UL) {
527                 base = 224;
528                 len = 3;
529         } else if (c < 2097152UL) {
530                 base = 240;
531                 len = 4;
532         } else if (c < 67108864UL) {
533                 base = 248;     
534                 len = 5;
535         } else if (c < 2147483648UL) {
536                 base = 252;
537                 len = 6;
538         } else {
539                 errno = EINVAL;
540                 return -1;
541         }
542         
543         if (outleft < len) {
544                 errno = E2BIG;
545                 return -1;
546         }
547         
548         for (i = len - 1; i > 0; i--) {
549                 /* mask off 6 bits worth and add 128 */
550                 outptr[i] = 128 + (c & 0x3f);
551                 c >>= 6;
552         }
553         
554         /* first character has a different base */
555         outptr[0] = base + c;
556         
557         *outbytesleft = outleft - len;
558         *outbytes = outptr + len;
559         
560         return 0;
561 }
562
563 static int
564 decode_latin1 (char **inbytes, size_t *inbytesleft, gunichar *outchar)
565 {
566         size_t inleft = *inbytesleft;
567         char *inptr = *inbytes;
568         gunichar u = *inptr;
569         
570         if (inleft < 1)
571                 return 0;
572         
573         *inbytesleft = inleft - 1;
574         *inbytes = inptr + 1;
575         *outchar = u;
576         
577         return 0;
578 }
579
580 static int
581 encode_latin1 (gunichar c, char **outbytes, size_t *outbytesleft)
582 {
583         size_t outleft = *outbytesleft;
584         char *outptr = *outbytes;
585         
586         if (outleft < 1) {
587                 errno = E2BIG;
588                 return -1;
589         }
590         
591         if (c > 0xff) {
592                 errno = EILSEQ;
593                 return -1;
594         }
595         
596         *outptr++ = (char) c;
597         outleft--;
598         
599         *outbytesleft = outleft;
600         *outbytes = outptr;
601         
602         return 0;
603 }