Factor iconv conversions to separate C files.
[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  * $Id: siconv.c,v 1.50 2008-03-12 08:53:28 adam Exp $
6  */
7 /**
8  * \file
9  * \brief ISO-5428 character mapping (iconv)
10  */
11
12 #if HAVE_CONFIG_H
13 #include <config.h>
14 #endif
15
16 #include <assert.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <ctype.h>
20
21 #include "iconv-p.h"
22
23 size_t yaz_init_UTF8(yaz_iconv_t cd, unsigned char *inp,
24                      size_t inbytesleft, size_t *no_read)
25 {
26     if (inp[0] != 0xef)
27     {
28         *no_read = 0;
29         return 0;
30     }
31     if (inbytesleft < 3)
32     {
33         yaz_iconv_set_errno(cd, YAZ_ICONV_EINVAL);
34         return (size_t) -1;
35     }
36     if (inp[1] != 0xbb && inp[2] == 0xbf)
37         *no_read = 3;
38     else
39         *no_read = 0;
40     return 0;
41 }
42
43 unsigned long yaz_read_UTF8_char(unsigned char *inp,
44                                  size_t inbytesleft, size_t *no_read,
45                                  int *error)
46 {
47     unsigned long x = 0;
48
49     *no_read = 0; /* by default */
50     if (inp[0] <= 0x7f)
51     {
52         x = inp[0];
53         *no_read = 1;
54     }
55     else if (inp[0] <= 0xbf || inp[0] >= 0xfe)
56     {
57         *error = YAZ_ICONV_EILSEQ;
58     }
59     else if (inp[0] <= 0xdf && inbytesleft >= 2)
60     {
61         if ((inp[1] & 0xc0) == 0x80)
62         {
63             x = ((inp[0] & 0x1f) << 6) | (inp[1] & 0x3f);
64             if (x >= 0x80)
65                 *no_read = 2;
66             else
67                 *error = YAZ_ICONV_EILSEQ;
68         }
69         else
70             *error = YAZ_ICONV_EILSEQ;
71     }
72     else if (inp[0] <= 0xef && inbytesleft >= 3)
73     {
74         if ((inp[1] & 0xc0) == 0x80 && (inp[2] & 0xc0) == 0x80)
75         {
76             x = ((inp[0] & 0x0f) << 12) | ((inp[1] & 0x3f) << 6) |
77                 (inp[2] & 0x3f);
78             if (x >= 0x800)
79                 *no_read = 3;
80             else
81                 *error = YAZ_ICONV_EILSEQ;
82         }
83         else
84             *error = YAZ_ICONV_EILSEQ;
85     }            
86     else if (inp[0] <= 0xf7 && inbytesleft >= 4)
87     {
88         if ((inp[1] & 0xc0) == 0x80 && (inp[2] & 0xc0) == 0x80
89             && (inp[3] & 0xc0) == 0x80)
90         {
91             x = ((inp[0] & 0x07) << 18) | ((inp[1] & 0x3f) << 12) |
92                 ((inp[2] & 0x3f) << 6) | (inp[3] & 0x3f);
93             if (x >= 0x10000)
94                 *no_read = 4;
95             else
96                 *error = YAZ_ICONV_EILSEQ;
97         }
98         else
99             *error = YAZ_ICONV_EILSEQ;
100     }
101     else if (inp[0] <= 0xfb && inbytesleft >= 5)
102     {
103         if ((inp[1] & 0xc0) == 0x80 && (inp[2] & 0xc0) == 0x80
104             && (inp[3] & 0xc0) == 0x80 && (inp[4] & 0xc0) == 0x80)
105         {
106             x = ((inp[0] & 0x03) << 24) | ((inp[1] & 0x3f) << 18) |
107                 ((inp[2] & 0x3f) << 12) | ((inp[3] & 0x3f) << 6) |
108                 (inp[4] & 0x3f);
109             if (x >= 0x200000)
110                 *no_read = 5;
111             else
112                 *error = YAZ_ICONV_EILSEQ;
113         }
114         else
115             *error = YAZ_ICONV_EILSEQ;
116     }
117     else if (inp[0] <= 0xfd && inbytesleft >= 6)
118     {
119         if ((inp[1] & 0xc0) == 0x80 && (inp[2] & 0xc0) == 0x80
120             && (inp[3] & 0xc0) == 0x80 && (inp[4] & 0xc0) == 0x80
121             && (inp[5] & 0xc0) == 0x80)
122         {
123             x = ((inp[0] & 0x01) << 30) | ((inp[1] & 0x3f) << 24) |
124                 ((inp[2] & 0x3f) << 18) | ((inp[3] & 0x3f) << 12) |
125                 ((inp[4] & 0x3f) << 6) | (inp[5] & 0x3f);
126             if (x >= 0x4000000)
127                 *no_read = 6;
128             else
129                 *error = YAZ_ICONV_EILSEQ;
130         }
131         else
132             *error = YAZ_ICONV_EILSEQ;
133     }
134     else
135         *error = YAZ_ICONV_EINVAL;  /* incomplete sentence */
136
137     return x;
138 }
139
140 unsigned long yaz_read_UTF8(yaz_iconv_t cd, 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 size_t yaz_write_UTF8(yaz_iconv_t cd, 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
220 /*
221  * Local variables:
222  * c-basic-offset: 4
223  * indent-tabs-mode: nil
224  * End:
225  * vim: shiftwidth=4 tabstop=8 expandtab
226  */