Fix left-right truncation fix
[yaz-moved-to-github.git] / src / utf8.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2011 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 static size_t init_utf8(yaz_iconv_t cd, yaz_iconv_decoder_t d,
22                         unsigned char *inp,
23                         size_t inbytesleft, size_t *no_read)
24 {
25     if (!inp || 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 static unsigned long read_utf8(yaz_iconv_t cd, yaz_iconv_decoder_t d,
140                                unsigned char *inp,
141                                size_t inbytesleft, size_t *no_read)
142 {
143     int err = 0;
144     int r = yaz_read_UTF8_char(inp, inbytesleft, no_read, &err);
145     yaz_iconv_set_errno(cd, err);
146     return r;
147 }
148
149
150 static size_t write_UTF8(yaz_iconv_t cd, yaz_iconv_encoder_t en,
151                              unsigned long x,
152                              char **outbuf, size_t *outbytesleft)
153 {
154     int err = 0;
155     int r = yaz_write_UTF8_char(x, outbuf, outbytesleft, &err);
156     yaz_iconv_set_errno(cd, err);
157     return r;
158 }
159
160 size_t yaz_write_UTF8_char(unsigned long x,
161                            char **outbuf, size_t *outbytesleft,
162                            int *error)
163 {
164     unsigned char *outp = (unsigned char *) *outbuf;
165
166     if (x <= 0x7f && *outbytesleft >= 1)
167     {
168         *outp++ = (unsigned char) x;
169         (*outbytesleft)--;
170     } 
171     else if (x <= 0x7ff && *outbytesleft >= 2)
172     {
173         *outp++ = (unsigned char) ((x >> 6) | 0xc0);
174         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
175         (*outbytesleft) -= 2;
176     }
177     else if (x <= 0xffff && *outbytesleft >= 3)
178     {
179         *outp++ = (unsigned char) ((x >> 12) | 0xe0);
180         *outp++ = (unsigned char) (((x >> 6) & 0x3f) | 0x80);
181         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
182         (*outbytesleft) -= 3;
183     }
184     else if (x <= 0x1fffff && *outbytesleft >= 4)
185     {
186         *outp++ = (unsigned char) ((x >> 18) | 0xf0);
187         *outp++ = (unsigned char) (((x >> 12) & 0x3f) | 0x80);
188         *outp++ = (unsigned char) (((x >> 6)  & 0x3f) | 0x80);
189         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
190         (*outbytesleft) -= 4;
191     }
192     else if (x <= 0x3ffffff && *outbytesleft >= 5)
193     {
194         *outp++ = (unsigned char) ((x >> 24) | 0xf8);
195         *outp++ = (unsigned char) (((x >> 18) & 0x3f) | 0x80);
196         *outp++ = (unsigned char) (((x >> 12) & 0x3f) | 0x80);
197         *outp++ = (unsigned char) (((x >> 6)  & 0x3f) | 0x80);
198         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
199         (*outbytesleft) -= 5;
200     }
201     else if (*outbytesleft >= 6)
202     {
203         *outp++ = (unsigned char) ((x >> 30) | 0xfc);
204         *outp++ = (unsigned char) (((x >> 24) & 0x3f) | 0x80);
205         *outp++ = (unsigned char) (((x >> 18) & 0x3f) | 0x80);
206         *outp++ = (unsigned char) (((x >> 12) & 0x3f) | 0x80);
207         *outp++ = (unsigned char) (((x >> 6)  & 0x3f) | 0x80);
208         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
209         (*outbytesleft) -= 6;
210     }
211     else 
212     {
213         *error = YAZ_ICONV_E2BIG;  /* not room for output */
214         return (size_t)(-1);
215     }
216     *outbuf = (char *) outp;
217     return 0;
218 }
219
220 yaz_iconv_encoder_t yaz_utf8_encoder(const char *tocode,
221                                      yaz_iconv_encoder_t e)
222     
223 {
224     if (!yaz_matchstr(tocode, "UTF8"))
225     {
226         e->write_handle = write_UTF8;
227         return e;
228     }
229     return 0;
230 }
231
232 yaz_iconv_decoder_t yaz_utf8_decoder(const char *fromcode,
233                                      yaz_iconv_decoder_t d)
234 {
235     if (!yaz_matchstr(fromcode, "UTF8"))
236     {
237         d->init_handle = init_utf8;
238         d->read_handle = read_utf8;
239         return d;
240     }
241     return 0;
242 }
243    
244
245 /*
246  * Local variables:
247  * c-basic-offset: 4
248  * c-file-style: "Stroustrup"
249  * indent-tabs-mode: nil
250  * End:
251  * vim: shiftwidth=4 tabstop=8 expandtab
252  */
253