42515caa6a0f8ada2689bb65441acf6000f6dfc9
[yaz-moved-to-github.git] / src / utf8.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2008 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file
7  * \brief UTF-8 encoding / decoding
8  */
9
10 #if HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include <assert.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <ctype.h>
18
19 #include "iconv-p.h"
20
21 size_t yaz_init_UTF8(yaz_iconv_t cd, unsigned char *inp,
22                      size_t inbytesleft, size_t *no_read)
23 {
24     if (inp[0] != 0xef)
25     {
26         *no_read = 0;
27         return 0;
28     }
29     if (inbytesleft < 3)
30     {
31         yaz_iconv_set_errno(cd, YAZ_ICONV_EINVAL);
32         return (size_t) -1;
33     }
34     if (inp[1] != 0xbb && inp[2] == 0xbf)
35         *no_read = 3;
36     else
37         *no_read = 0;
38     return 0;
39 }
40
41 unsigned long yaz_read_UTF8_char(unsigned char *inp,
42                                  size_t inbytesleft, size_t *no_read,
43                                  int *error)
44 {
45     unsigned long x = 0;
46
47     *no_read = 0; /* by default */
48     if (inp[0] <= 0x7f)
49     {
50         x = inp[0];
51         *no_read = 1;
52     }
53     else if (inp[0] <= 0xbf || inp[0] >= 0xfe)
54     {
55         *error = YAZ_ICONV_EILSEQ;
56     }
57     else if (inp[0] <= 0xdf && inbytesleft >= 2)
58     {
59         if ((inp[1] & 0xc0) == 0x80)
60         {
61             x = ((inp[0] & 0x1f) << 6) | (inp[1] & 0x3f);
62             if (x >= 0x80)
63                 *no_read = 2;
64             else
65                 *error = YAZ_ICONV_EILSEQ;
66         }
67         else
68             *error = YAZ_ICONV_EILSEQ;
69     }
70     else if (inp[0] <= 0xef && inbytesleft >= 3)
71     {
72         if ((inp[1] & 0xc0) == 0x80 && (inp[2] & 0xc0) == 0x80)
73         {
74             x = ((inp[0] & 0x0f) << 12) | ((inp[1] & 0x3f) << 6) |
75                 (inp[2] & 0x3f);
76             if (x >= 0x800)
77                 *no_read = 3;
78             else
79                 *error = YAZ_ICONV_EILSEQ;
80         }
81         else
82             *error = YAZ_ICONV_EILSEQ;
83     }            
84     else if (inp[0] <= 0xf7 && inbytesleft >= 4)
85     {
86         if ((inp[1] & 0xc0) == 0x80 && (inp[2] & 0xc0) == 0x80
87             && (inp[3] & 0xc0) == 0x80)
88         {
89             x = ((inp[0] & 0x07) << 18) | ((inp[1] & 0x3f) << 12) |
90                 ((inp[2] & 0x3f) << 6) | (inp[3] & 0x3f);
91             if (x >= 0x10000)
92                 *no_read = 4;
93             else
94                 *error = YAZ_ICONV_EILSEQ;
95         }
96         else
97             *error = YAZ_ICONV_EILSEQ;
98     }
99     else if (inp[0] <= 0xfb && inbytesleft >= 5)
100     {
101         if ((inp[1] & 0xc0) == 0x80 && (inp[2] & 0xc0) == 0x80
102             && (inp[3] & 0xc0) == 0x80 && (inp[4] & 0xc0) == 0x80)
103         {
104             x = ((inp[0] & 0x03) << 24) | ((inp[1] & 0x3f) << 18) |
105                 ((inp[2] & 0x3f) << 12) | ((inp[3] & 0x3f) << 6) |
106                 (inp[4] & 0x3f);
107             if (x >= 0x200000)
108                 *no_read = 5;
109             else
110                 *error = YAZ_ICONV_EILSEQ;
111         }
112         else
113             *error = YAZ_ICONV_EILSEQ;
114     }
115     else if (inp[0] <= 0xfd && inbytesleft >= 6)
116     {
117         if ((inp[1] & 0xc0) == 0x80 && (inp[2] & 0xc0) == 0x80
118             && (inp[3] & 0xc0) == 0x80 && (inp[4] & 0xc0) == 0x80
119             && (inp[5] & 0xc0) == 0x80)
120         {
121             x = ((inp[0] & 0x01) << 30) | ((inp[1] & 0x3f) << 24) |
122                 ((inp[2] & 0x3f) << 18) | ((inp[3] & 0x3f) << 12) |
123                 ((inp[4] & 0x3f) << 6) | (inp[5] & 0x3f);
124             if (x >= 0x4000000)
125                 *no_read = 6;
126             else
127                 *error = YAZ_ICONV_EILSEQ;
128         }
129         else
130             *error = YAZ_ICONV_EILSEQ;
131     }
132     else
133         *error = YAZ_ICONV_EINVAL;  /* incomplete sentence */
134
135     return x;
136 }
137
138 unsigned long yaz_read_UTF8(yaz_iconv_t cd, unsigned char *inp,
139                             size_t inbytesleft, size_t *no_read)
140 {
141     int err = 0;
142     int r = yaz_read_UTF8_char(inp, inbytesleft, no_read, &err);
143     yaz_iconv_set_errno(cd, err);
144     return r;
145 }
146
147
148 static size_t write_UTF8(yaz_iconv_t cd, yaz_iconv_encoder_t en,
149                              unsigned long x,
150                              char **outbuf, size_t *outbytesleft)
151 {
152     int err = 0;
153     int r = yaz_write_UTF8_char(x, outbuf, outbytesleft, &err);
154     yaz_iconv_set_errno(cd, err);
155     return r;
156 }
157
158 size_t yaz_write_UTF8_char(unsigned long x,
159                            char **outbuf, size_t *outbytesleft,
160                            int *error)
161 {
162     unsigned char *outp = (unsigned char *) *outbuf;
163
164     if (x <= 0x7f && *outbytesleft >= 1)
165     {
166         *outp++ = (unsigned char) x;
167         (*outbytesleft)--;
168     } 
169     else if (x <= 0x7ff && *outbytesleft >= 2)
170     {
171         *outp++ = (unsigned char) ((x >> 6) | 0xc0);
172         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
173         (*outbytesleft) -= 2;
174     }
175     else if (x <= 0xffff && *outbytesleft >= 3)
176     {
177         *outp++ = (unsigned char) ((x >> 12) | 0xe0);
178         *outp++ = (unsigned char) (((x >> 6) & 0x3f) | 0x80);
179         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
180         (*outbytesleft) -= 3;
181     }
182     else if (x <= 0x1fffff && *outbytesleft >= 4)
183     {
184         *outp++ = (unsigned char) ((x >> 18) | 0xf0);
185         *outp++ = (unsigned char) (((x >> 12) & 0x3f) | 0x80);
186         *outp++ = (unsigned char) (((x >> 6)  & 0x3f) | 0x80);
187         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
188         (*outbytesleft) -= 4;
189     }
190     else if (x <= 0x3ffffff && *outbytesleft >= 5)
191     {
192         *outp++ = (unsigned char) ((x >> 24) | 0xf8);
193         *outp++ = (unsigned char) (((x >> 18) & 0x3f) | 0x80);
194         *outp++ = (unsigned char) (((x >> 12) & 0x3f) | 0x80);
195         *outp++ = (unsigned char) (((x >> 6)  & 0x3f) | 0x80);
196         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
197         (*outbytesleft) -= 5;
198     }
199     else if (*outbytesleft >= 6)
200     {
201         *outp++ = (unsigned char) ((x >> 30) | 0xfc);
202         *outp++ = (unsigned char) (((x >> 24) & 0x3f) | 0x80);
203         *outp++ = (unsigned char) (((x >> 18) & 0x3f) | 0x80);
204         *outp++ = (unsigned char) (((x >> 12) & 0x3f) | 0x80);
205         *outp++ = (unsigned char) (((x >> 6)  & 0x3f) | 0x80);
206         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
207         (*outbytesleft) -= 6;
208     }
209     else 
210     {
211         *error = YAZ_ICONV_E2BIG;  /* not room for output */
212         return (size_t)(-1);
213     }
214     *outbuf = (char *) outp;
215     return 0;
216 }
217
218 yaz_iconv_encoder_t yaz_utf8_encoder(const char *tocode,
219                                      yaz_iconv_encoder_t e)
220     
221 {
222     if (!yaz_matchstr(tocode, "UTF8"))
223     {
224         e->write_handle = write_UTF8;
225         return e;
226     }
227     return 0;
228 }
229
230
231 /*
232  * Local variables:
233  * c-basic-offset: 4
234  * indent-tabs-mode: nil
235  * End:
236  * vim: shiftwidth=4 tabstop=8 expandtab
237  */