Added several type casts for C++ compilation. ZOOM fixes.
[yaz-moved-to-github.git] / util / siconv.c
1 /*
2  * Copyright (c) 1997-2002, Index Data
3  * See the file LICENSE for details.
4  *
5  * $Id: siconv.c,v 1.5 2002-09-24 08:05:41 adam Exp $
6  */
7
8 /* mini iconv and wrapper for system iconv library (if present) */
9
10 #if HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include <errno.h>
15 #include <string.h>
16 #include <ctype.h>
17
18 #if HAVE_ICONV_H
19 #include <iconv.h>
20 #endif
21
22 #include <yaz/yaz-util.h>
23
24 struct yaz_iconv_struct {
25     int my_errno;
26     int init_flag;
27     size_t (*init_handle)(yaz_iconv_t cd, unsigned char *inbuf,
28                           size_t inbytesleft, size_t *no_read);
29     unsigned long (*read_handle)(yaz_iconv_t cd, unsigned char *inbuf,
30                                  size_t inbytesleft, size_t *no_read);
31     size_t (*write_handle)(yaz_iconv_t cd, unsigned long x,
32                            char **outbuf, size_t *outbytesleft);
33 #if HAVE_ICONV_H
34     iconv_t iconv_cd;
35 #endif
36 };
37
38 static unsigned long yaz_read_ISO8859_1 (yaz_iconv_t cd, unsigned char *inp,
39                                          size_t inbytesleft, size_t *no_read)
40 {
41     unsigned long x = inp[0];
42     *no_read = 1;
43     return x;
44 }
45
46 static size_t yaz_init_UTF8 (yaz_iconv_t cd, unsigned char *inp,
47                              size_t inbytesleft, size_t *no_read)
48 {
49     if (inp[0] != 0xef)
50     {
51         *no_read = 0;
52         return 0;
53     }
54     if (inbytesleft < 3)
55     {
56         cd->my_errno = YAZ_ICONV_EINVAL;
57         return (size_t) -1;
58     }
59     if (inp[1] != 0xbb || inp[2] != 0xbf)
60     {
61         cd->my_errno = YAZ_ICONV_EILSEQ;
62         return (size_t) -1;
63     }
64     *no_read = 3;
65     return 0;
66 }
67
68 static unsigned long yaz_read_UTF8 (yaz_iconv_t cd, unsigned char *inp,
69                                     size_t inbytesleft, size_t *no_read)
70 {
71     unsigned long x = 0;
72
73     if (inp[0] <= 0x7f)
74     {
75         x = inp[0];
76         *no_read = 1;
77     }
78     else if (inp[0] <= 0xbf || inp[0] >= 0xfe)
79     {
80         *no_read = 0;
81         cd->my_errno = YAZ_ICONV_EILSEQ;
82     }
83     else if (inp[0] <= 0xdf && inbytesleft >= 2)
84     {
85         x = ((inp[0] & 0x1f) << 6) | (inp[1] & 0x3f);
86         if (x >= 0x80)
87             *no_read = 2;
88         else
89         {
90             *no_read = 0;
91             cd->my_errno = YAZ_ICONV_EILSEQ;
92         }
93     }
94     else if (inp[0] <= 0xef && inbytesleft >= 3)
95     {
96         x = ((inp[0] & 0x0f) << 12) | ((inp[1] & 0x3f) << 6) |
97             (inp[1] & 0x3f);
98         if (x >= 0x800)
99             *no_read = 3;
100         else
101         {
102             *no_read = 0;
103             cd->my_errno = YAZ_ICONV_EILSEQ;
104         }
105     }
106     else if (inp[0] <= 0xf7 && inbytesleft >= 4)
107     {
108         x =  ((inp[0] & 0x07) << 18) | ((inp[1] & 0x3f) << 12) |
109             ((inp[2] & 0x3f) << 6) | (inp[3] & 0x3f);
110         if (x >= 0x10000)
111             *no_read = 4;
112         else
113         {
114             *no_read = 0;
115             cd->my_errno = YAZ_ICONV_EILSEQ;
116         }
117     }
118     else if (inp[0] <= 0xfb && inbytesleft >= 5)
119     {
120         x =  ((inp[0] & 0x03) << 24) | ((inp[1] & 0x3f) << 18) |
121             ((inp[2] & 0x3f) << 12) | ((inp[3] & 0x3f) << 6) |
122             (inp[4] & 0x3f);
123         if (x >= 0x200000)
124             *no_read = 5;
125         else
126         {
127             *no_read = 0;
128             cd->my_errno = YAZ_ICONV_EILSEQ;
129         }
130     }
131     else if (inp[0] <= 0xfd && inbytesleft >= 6)
132     {
133         x =  ((inp[0] & 0x01) << 30) | ((inp[1] & 0x3f) << 24) |
134             ((inp[2] & 0x3f) << 18) | ((inp[3] & 0x3f) << 12) |
135             ((inp[4] & 0x3f) << 6) | (inp[5] & 0x3f);
136         if (x >= 0x4000000)
137             *no_read = 6;
138         else
139         {
140             *no_read = 0;
141             cd->my_errno = YAZ_ICONV_EILSEQ;
142         }
143     }
144     else
145     {
146         *no_read = 0;
147         cd->my_errno = YAZ_ICONV_EINVAL;
148     }
149     return x;
150 }
151
152 static unsigned long yaz_read_UCS4 (yaz_iconv_t cd, unsigned char *inp,
153                                     size_t inbytesleft, size_t *no_read)
154 {
155     unsigned long x = 0;
156     
157     if (inbytesleft < 4)
158     {
159         cd->my_errno = YAZ_ICONV_EINVAL; /* incomplete input */
160         *no_read = 0;
161     }
162     else
163     {
164         x = (inp[0]<<24) | (inp[1]<<16) | (inp[2]<<8) | inp[3];
165         *no_read = 4;
166     }
167     return x;
168 }
169
170 static unsigned long yaz_read_UCS4LE (yaz_iconv_t cd, unsigned char *inp,
171                                       size_t inbytesleft, size_t *no_read)
172 {
173     unsigned long x = 0;
174     
175     if (inbytesleft < 4)
176     {
177         cd->my_errno = YAZ_ICONV_EINVAL; /* incomplete input */
178         *no_read = 0;
179     }
180     else
181     {
182         x = (inp[3]<<24) | (inp[2]<<16) | (inp[1]<<8) | inp[0];
183         *no_read = 4;
184     }
185     return x;
186 }
187
188 static size_t yaz_write_UTF8 (yaz_iconv_t cd, unsigned long x,
189                               char **outbuf, size_t *outbytesleft)
190 {
191     unsigned char *outp = (unsigned char *) *outbuf;
192     if (x <= 0x7f && *outbytesleft >= 1)
193     {
194         *outp++ = (unsigned char) x;
195         (*outbytesleft)--;
196     } 
197     else if (x <= 0x7ff && *outbytesleft >= 2)
198     {
199         *outp++ = (unsigned char) ((x >> 6) | 0xc0);
200         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
201         (*outbytesleft) -= 2;
202     }
203     else if (x <= 0xffff && *outbytesleft >= 3)
204     {
205         *outp++ = (unsigned char) ((x >> 12) | 0xe0);
206         *outp++ = (unsigned char) (((x >> 6) & 0x3f) | 0x80);
207         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
208         (*outbytesleft) -= 3;
209     }
210     else if (x <= 0x1fffff && *outbytesleft >= 4)
211     {
212         *outp++ = (unsigned char) ((x >> 18) | 0xf0);
213         *outp++ = (unsigned char) (((x >> 12) & 0x3f) | 0x80);
214         *outp++ = (unsigned char) (((x >> 6)  & 0x3f) | 0x80);
215         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
216         (*outbytesleft) -= 4;
217     }
218     else if (x <= 0x3ffffff && *outbytesleft >= 5)
219     {
220         *outp++ = (unsigned char) ((x >> 24) | 0xf8);
221         *outp++ = (unsigned char) (((x >> 18) & 0x3f) | 0x80);
222         *outp++ = (unsigned char) (((x >> 12) & 0x3f) | 0x80);
223         *outp++ = (unsigned char) (((x >> 6)  & 0x3f) | 0x80);
224         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
225         (*outbytesleft) -= 5;
226     }
227     else if (*outbytesleft >= 6)
228     {
229         *outp++ = (unsigned char) ((x >> 30) | 0xfc);
230         *outp++ = (unsigned char) (((x >> 24) & 0x3f) | 0x80);
231         *outp++ = (unsigned char) (((x >> 18) & 0x3f) | 0x80);
232         *outp++ = (unsigned char) (((x >> 12) & 0x3f) | 0x80);
233         *outp++ = (unsigned char) (((x >> 6)  & 0x3f) | 0x80);
234         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
235         (*outbytesleft) -= 6;
236     }
237     else 
238     {
239         cd->my_errno = YAZ_ICONV_E2BIG;  /* not room for output */
240         return (size_t)(-1);
241     }
242     *outbuf = (char *) outp;
243     return 0;
244 }
245
246 static size_t yaz_write_ISO8859_1 (yaz_iconv_t cd, unsigned long x,
247                                    char **outbuf, size_t *outbytesleft)
248 {
249     unsigned char *outp = (unsigned char *) *outbuf;
250     if (x > 255 || x < 1)
251     {
252         cd->my_errno = YAZ_ICONV_EILSEQ;
253         return (size_t) -1;
254     }
255     else if (*outbytesleft >= 1)
256     {
257         *outp++ = (unsigned char) x;
258         (*outbytesleft)--;
259     }
260     else 
261     {
262         cd->my_errno = YAZ_ICONV_E2BIG;
263         return (size_t)(-1);
264     }
265     *outbuf = (char *) outp;
266     return 0;
267 }
268
269
270 static size_t yaz_write_UCS4 (yaz_iconv_t cd, unsigned long x,
271                               char **outbuf, size_t *outbytesleft)
272 {
273     unsigned char *outp = (unsigned char *) *outbuf;
274     if (*outbytesleft >= 4)
275     {
276         *outp++ = (unsigned char) (x<<24);
277         *outp++ = (unsigned char) (x<<16);
278         *outp++ = (unsigned char) (x<<8);
279         *outp++ = (unsigned char) x;
280         (*outbytesleft) -= 4;
281     }
282     else
283     {
284         cd->my_errno = YAZ_ICONV_E2BIG;
285         return (size_t)(-1);
286     }
287     *outbuf = (char *) outp;
288     return 0;
289 }
290
291 static size_t yaz_write_UCS4LE (yaz_iconv_t cd, unsigned long x,
292                                 char **outbuf, size_t *outbytesleft)
293 {
294     unsigned char *outp = (unsigned char *) *outbuf;
295     if (*outbytesleft >= 4)
296     {
297         *outp++ = (unsigned char) x;
298         *outp++ = (unsigned char) (x<<8);
299         *outp++ = (unsigned char) (x<<16);
300         *outp++ = (unsigned char) (x<<24);
301         (*outbytesleft) -= 4;
302     }
303     else
304     {
305         cd->my_errno = YAZ_ICONV_E2BIG;
306         return (size_t)(-1);
307     }
308     *outbuf = (char *) outp;
309     return 0;
310 }
311
312 yaz_iconv_t yaz_iconv_open (const char *tocode, const char *fromcode)
313 {
314     yaz_iconv_t cd = (yaz_iconv_t) xmalloc (sizeof(*cd));
315
316     cd->write_handle = 0;
317     cd->read_handle = 0;
318     cd->init_handle = 0;
319     cd->my_errno = YAZ_ICONV_UNKNOWN;
320
321     if (!yaz_matchstr(fromcode, "UTF8"))
322     {
323         cd->read_handle = yaz_read_UTF8;
324         cd->init_handle = yaz_init_UTF8;
325     }
326     else if (!yaz_matchstr(fromcode, "ISO88591"))
327         cd->read_handle = yaz_read_ISO8859_1;
328     else if (!yaz_matchstr(fromcode, "UCS4"))
329         cd->read_handle = yaz_read_UCS4;
330     else if (!yaz_matchstr(fromcode, "UCS4LE"))
331         cd->read_handle = yaz_read_UCS4LE;
332     
333     if (!yaz_matchstr(tocode, "UTF8"))
334         cd->write_handle = yaz_write_UTF8;
335     else if (!yaz_matchstr(tocode, "ISO88591"))
336         cd->write_handle = yaz_write_ISO8859_1;
337     else if (!yaz_matchstr (tocode, "UCS4"))
338         cd->write_handle = yaz_write_UCS4;
339     else if (!yaz_matchstr(tocode, "UCS4LE"))
340         cd->write_handle = yaz_write_UCS4LE;
341
342 #if HAVE_ICONV_H
343     cd->iconv_cd = 0;
344     if (!cd->read_handle || !cd->write_handle)
345     {
346         cd->iconv_cd = iconv_open (tocode, fromcode);
347         if (cd->iconv_cd == (iconv_t) (-1))
348         {
349             xfree (cd);
350             return 0;
351         }
352     }
353 #else
354     if (!cd->read_handle || !cd->write_handle)
355     {
356         xfree (cd);
357         return 0;
358     }
359 #endif
360     cd->init_flag = 1;
361     return cd;
362 }
363
364 size_t yaz_iconv (yaz_iconv_t cd, char **inbuf, size_t *inbytesleft,
365                   char **outbuf, size_t *outbytesleft)
366 {
367     char *inbuf0;
368     size_t r = 0;
369 #if HAVE_ICONV_H
370     if (cd->iconv_cd)
371     {
372         size_t r =
373             iconv(cd->iconv_cd, inbuf, inbytesleft, outbuf, outbytesleft);
374         if (r == (size_t)(-1))
375         {
376             switch (errno)
377             {
378             case E2BIG:
379                 cd->my_errno = YAZ_ICONV_E2BIG;
380                 break;
381             case EINVAL:
382                 cd->my_errno = YAZ_ICONV_EINVAL;
383                 break;
384             case EILSEQ:
385                 cd->my_errno = YAZ_ICONV_EILSEQ;
386                 break;
387             default:
388                 cd->my_errno = YAZ_ICONV_UNKNOWN;
389             }
390         }
391         return r;
392     }
393 #endif
394     if (inbuf == 0 || *inbuf == 0)
395     {
396         cd->init_flag = 1;
397         cd->my_errno = YAZ_ICONV_UNKNOWN;
398         return 0;
399     }
400     inbuf0 = *inbuf;
401
402     if (cd->init_flag)
403     {
404         if (cd->init_handle)
405         {
406             size_t no_read;
407             size_t r = (cd->init_handle)(cd, (unsigned char *) *inbuf,
408                                          *inbytesleft, &no_read);
409             if (r)
410             {
411                 if (cd->my_errno == YAZ_ICONV_EINVAL)
412                     return r;
413                 cd->init_flag = 0;
414                 return r;
415             }
416             *inbytesleft -= no_read;
417             *inbuf += no_read;
418         }
419         cd->init_flag = 0;
420     }
421     while (1)
422     {
423         unsigned long x;
424         size_t no_read;
425
426         if (*inbytesleft == 0)
427         {
428             r = *inbuf - inbuf0;
429             break;
430         }
431         
432         x = (cd->read_handle)(cd, (unsigned char *) *inbuf, *inbytesleft,
433                               &no_read);
434         if (no_read == 0)
435         {
436             r = (size_t)(-1);
437             break;
438         }
439         r = (cd->write_handle)(cd, x, outbuf, outbytesleft);
440         if (r)
441             break;
442         *inbytesleft -= no_read;
443         (*inbuf) += no_read;
444     }
445     return r;
446 }
447
448 int yaz_iconv_error (yaz_iconv_t cd)
449 {
450     return cd->my_errno;
451 }
452
453 int yaz_iconv_close (yaz_iconv_t cd)
454 {
455 #if HAVE_ICONV_H
456     if (cd->iconv_cd)
457         iconv_close (cd->iconv_cd);
458 #endif
459     xfree (cd);
460     return 0;
461 }
462
463