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