Check for malformed UTF-8 characters. Thanks for Gary Anderson for
[yaz-moved-to-github.git] / src / siconv.c
1 /*
2  * Copyright (C) 1995-2007, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: siconv.c,v 1.41 2007-05-23 08:50:11 adam Exp $
6  */
7 /**
8  * \file siconv.c
9  * \brief Implements simple ICONV
10  *
11  * This implements an interface similar to that of iconv and
12  * is used by YAZ to interface with iconv (if present).
13  * For systems where iconv is not present, this layer
14  * provides a few important conversions: UTF-8, MARC-8, Latin-1.
15  *
16  * MARC-8 reference:
17  *  http://www.loc.gov/marc/specifications/speccharmarc8.html
18  */
19
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <assert.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <ctype.h>
28 #if HAVE_WCHAR_H
29 #include <wchar.h>
30 #endif
31
32 #if HAVE_ICONV_H
33 #include <iconv.h>
34 #endif
35
36
37 #include <yaz/yaz-util.h>
38
39 unsigned long yaz_marc8_1_conv(unsigned char *inp, size_t inbytesleft,
40                                size_t *no_read, int *combining);
41 unsigned long yaz_marc8_2_conv(unsigned char *inp, size_t inbytesleft,
42                                size_t *no_read, int *combining);
43 unsigned long yaz_marc8_3_conv(unsigned char *inp, size_t inbytesleft,
44                                size_t *no_read, int *combining);
45 unsigned long yaz_marc8_4_conv(unsigned char *inp, size_t inbytesleft,
46                                size_t *no_read, int *combining);
47 unsigned long yaz_marc8_5_conv(unsigned char *inp, size_t inbytesleft,
48                                size_t *no_read, int *combining);
49 unsigned long yaz_marc8_6_conv(unsigned char *inp, size_t inbytesleft,
50                                size_t *no_read, int *combining);
51 unsigned long yaz_marc8_7_conv(unsigned char *inp, size_t inbytesleft,
52                                size_t *no_read, int *combining);
53 unsigned long yaz_marc8_8_conv(unsigned char *inp, size_t inbytesleft,
54                                size_t *no_read, int *combining);
55 unsigned long yaz_marc8_9_conv(unsigned char *inp, size_t inbytesleft,
56                                size_t *no_read, int *combining);
57
58
59 unsigned long yaz_marc8r_1_conv(unsigned char *inp, size_t inbytesleft,
60                                 size_t *no_read, int *combining);
61 unsigned long yaz_marc8r_2_conv(unsigned char *inp, size_t inbytesleft,
62                                 size_t *no_read, int *combining);
63 unsigned long yaz_marc8r_3_conv(unsigned char *inp, size_t inbytesleft,
64                                 size_t *no_read, int *combining);
65 unsigned long yaz_marc8r_4_conv(unsigned char *inp, size_t inbytesleft,
66                                 size_t *no_read, int *combining);
67 unsigned long yaz_marc8r_5_conv(unsigned char *inp, size_t inbytesleft,
68                                 size_t *no_read, int *combining);
69 unsigned long yaz_marc8r_6_conv(unsigned char *inp, size_t inbytesleft,
70                                 size_t *no_read, int *combining);
71 unsigned long yaz_marc8r_7_conv(unsigned char *inp, size_t inbytesleft,
72                                 size_t *no_read, int *combining);
73 unsigned long yaz_marc8r_8_conv(unsigned char *inp, size_t inbytesleft,
74                                 size_t *no_read, int *combining);
75 unsigned long yaz_marc8r_9_conv(unsigned char *inp, size_t inbytesleft,
76                                 size_t *no_read, int *combining);
77
78 struct yaz_iconv_struct {
79     int my_errno;
80     int init_flag;
81     size_t (*init_handle)(yaz_iconv_t cd, unsigned char *inbuf,
82                           size_t inbytesleft, size_t *no_read);
83     unsigned long (*read_handle)(yaz_iconv_t cd, unsigned char *inbuf,
84                                  size_t inbytesleft, size_t *no_read);
85     size_t (*write_handle)(yaz_iconv_t cd, unsigned long x,
86                            char **outbuf, size_t *outbytesleft);
87     size_t (*flush_handle)(yaz_iconv_t cd,
88                            char **outbuf, size_t *outbytesleft);
89     int marc8_esc_mode;
90
91     int comb_offset;
92     int comb_size;
93     unsigned long comb_x[8];
94     size_t comb_no_read[8];
95     size_t no_read_x;
96     unsigned long unget_x;
97 #if HAVE_ICONV_H
98     iconv_t iconv_cd;
99 #endif
100     unsigned long compose_char;
101
102     unsigned long write_marc8_comb_ch[8];
103     size_t write_marc8_comb_no;
104     unsigned write_marc8_second_half_char;
105     unsigned long write_marc8_last;
106     const char *write_marc8_page_chr;
107 };
108
109 static struct {
110     unsigned long x1, x2;
111     unsigned y;
112 } latin1_comb[] = {
113     { 'A', 0x0300, 0xc0}, /* LATIN CAPITAL LETTER A WITH GRAVE */
114     { 'A', 0x0301, 0xc1}, /* LATIN CAPITAL LETTER A WITH ACUTE */
115     { 'A', 0x0302, 0xc2}, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
116     { 'A', 0x0303, 0xc3}, /* LATIN CAPITAL LETTER A WITH TILDE */
117     { 'A', 0x0308, 0xc4}, /* LATIN CAPITAL LETTER A WITH DIAERESIS */
118     { 'A', 0x030a, 0xc5}, /* LATIN CAPITAL LETTER A WITH RING ABOVE */
119     /* no need for 0xc6      LATIN CAPITAL LETTER AE */
120     { 'C', 0x0327, 0xc7}, /* LATIN CAPITAL LETTER C WITH CEDILLA */
121     { 'E', 0x0300, 0xc8}, /* LATIN CAPITAL LETTER E WITH GRAVE */
122     { 'E', 0x0301, 0xc9}, /* LATIN CAPITAL LETTER E WITH ACUTE */
123     { 'E', 0x0302, 0xca}, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
124     { 'E', 0x0308, 0xcb}, /* LATIN CAPITAL LETTER E WITH DIAERESIS */
125     { 'I', 0x0300, 0xcc}, /* LATIN CAPITAL LETTER I WITH GRAVE */
126     { 'I', 0x0301, 0xcd}, /* LATIN CAPITAL LETTER I WITH ACUTE */
127     { 'I', 0x0302, 0xce}, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
128     { 'I', 0x0308, 0xcf}, /* LATIN CAPITAL LETTER I WITH DIAERESIS */
129     { 'N', 0x0303, 0xd1}, /* LATIN CAPITAL LETTER N WITH TILDE */
130     { 'O', 0x0300, 0xd2}, /* LATIN CAPITAL LETTER O WITH GRAVE */
131     { 'O', 0x0301, 0xd3}, /* LATIN CAPITAL LETTER O WITH ACUTE */
132     { 'O', 0x0302, 0xd4}, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
133     { 'O', 0x0303, 0xd5}, /* LATIN CAPITAL LETTER O WITH TILDE */
134     { 'O', 0x0308, 0xd6}, /* LATIN CAPITAL LETTER O WITH DIAERESIS */
135     /* omitted:    0xd7      MULTIPLICATION SIGN */
136     /* omitted:    0xd8      LATIN CAPITAL LETTER O WITH STROKE */
137     { 'U', 0x0300, 0xd9}, /* LATIN CAPITAL LETTER U WITH GRAVE */
138     { 'U', 0x0301, 0xda}, /* LATIN CAPITAL LETTER U WITH ACUTE */
139     { 'U', 0x0302, 0xdb}, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
140     { 'U', 0x0308, 0xdc}, /* LATIN CAPITAL LETTER U WITH DIAERESIS */
141     { 'Y', 0x0301, 0xdd}, /* LATIN CAPITAL LETTER Y WITH ACUTE */
142     /* omitted:    0xde      LATIN CAPITAL LETTER THORN */
143     /* omitted:    0xdf      LATIN SMALL LETTER SHARP S */
144     { 'a', 0x0300, 0xe0}, /* LATIN SMALL LETTER A WITH GRAVE */
145     { 'a', 0x0301, 0xe1}, /* LATIN SMALL LETTER A WITH ACUTE */
146     { 'a', 0x0302, 0xe2}, /* LATIN SMALL LETTER A WITH CIRCUMFLEX */
147     { 'a', 0x0303, 0xe3}, /* LATIN SMALL LETTER A WITH TILDE */
148     { 'a', 0x0308, 0xe4}, /* LATIN SMALL LETTER A WITH DIAERESIS */
149     { 'a', 0x030a, 0xe5}, /* LATIN SMALL LETTER A WITH RING ABOVE */
150     /* omitted:    0xe6      LATIN SMALL LETTER AE */
151     { 'c', 0x0327, 0xe7}, /* LATIN SMALL LETTER C WITH CEDILLA */
152     { 'e', 0x0300, 0xe8}, /* LATIN SMALL LETTER E WITH GRAVE */
153     { 'e', 0x0301, 0xe9}, /* LATIN SMALL LETTER E WITH ACUTE */
154     { 'e', 0x0302, 0xea}, /* LATIN SMALL LETTER E WITH CIRCUMFLEX */
155     { 'e', 0x0308, 0xeb}, /* LATIN SMALL LETTER E WITH DIAERESIS */
156     { 'i', 0x0300, 0xec}, /* LATIN SMALL LETTER I WITH GRAVE */
157     { 'i', 0x0301, 0xed}, /* LATIN SMALL LETTER I WITH ACUTE */
158     { 'i', 0x0302, 0xee}, /* LATIN SMALL LETTER I WITH CIRCUMFLEX */
159     { 'i', 0x0308, 0xef}, /* LATIN SMALL LETTER I WITH DIAERESIS */
160     /* omitted:    0xf0      LATIN SMALL LETTER ETH */
161     { 'n', 0x0303, 0xf1}, /* LATIN SMALL LETTER N WITH TILDE */
162     { 'o', 0x0300, 0xf2}, /* LATIN SMALL LETTER O WITH GRAVE */
163     { 'o', 0x0301, 0xf3}, /* LATIN SMALL LETTER O WITH ACUTE */
164     { 'o', 0x0302, 0xf4}, /* LATIN SMALL LETTER O WITH CIRCUMFLEX */
165     { 'o', 0x0303, 0xf5}, /* LATIN SMALL LETTER O WITH TILDE */
166     { 'o', 0x0308, 0xf6}, /* LATIN SMALL LETTER O WITH DIAERESIS */
167     /* omitted:    0xf7      DIVISION SIGN */
168     /* omitted:    0xf8      LATIN SMALL LETTER O WITH STROKE */
169     { 'u', 0x0300, 0xf9}, /* LATIN SMALL LETTER U WITH GRAVE */
170     { 'u', 0x0301, 0xfa}, /* LATIN SMALL LETTER U WITH ACUTE */
171     { 'u', 0x0302, 0xfb}, /* LATIN SMALL LETTER U WITH CIRCUMFLEX */
172     { 'u', 0x0308, 0xfc}, /* LATIN SMALL LETTER U WITH DIAERESIS */
173     { 'y', 0x0301, 0xfd}, /* LATIN SMALL LETTER Y WITH ACUTE */
174     /* omitted:    0xfe      LATIN SMALL LETTER THORN */
175     { 'y', 0x0308, 0xff}, /* LATIN SMALL LETTER Y WITH DIAERESIS */
176     
177     { 0, 0, 0}
178 };
179
180 static unsigned long yaz_read_ISO8859_1 (yaz_iconv_t cd, unsigned char *inp,
181                                          size_t inbytesleft, size_t *no_read)
182 {
183     unsigned long x = inp[0];
184     *no_read = 1;
185     return x;
186 }
187
188
189 static size_t yaz_init_UTF8 (yaz_iconv_t cd, unsigned char *inp,
190                              size_t inbytesleft, size_t *no_read)
191 {
192     if (inp[0] != 0xef)
193     {
194         *no_read = 0;
195         return 0;
196     }
197     if (inbytesleft < 3)
198     {
199         cd->my_errno = YAZ_ICONV_EINVAL;
200         return (size_t) -1;
201     }
202     if (inp[1] != 0xbb && inp[2] == 0xbf)
203         *no_read = 3;
204     else
205         *no_read = 0;
206     return 0;
207 }
208
209 unsigned long yaz_read_UTF8_char(unsigned char *inp,
210                                  size_t inbytesleft, size_t *no_read,
211                                  int *error)
212 {
213     unsigned long x = 0;
214
215     *no_read = 0; /* by default */
216     if (inp[0] <= 0x7f)
217     {
218         x = inp[0];
219         *no_read = 1;
220     }
221     else if (inp[0] <= 0xbf || inp[0] >= 0xfe)
222     {
223         *error = YAZ_ICONV_EILSEQ;
224     }
225     else if (inp[0] <= 0xdf && inbytesleft >= 2)
226     {
227         if ((inp[1] & 0xc0) == 0x80)
228         {
229             x = ((inp[0] & 0x1f) << 6) | (inp[1] & 0x3f);
230             if (x >= 0x80)
231                 *no_read = 2;
232             else
233                 *error = YAZ_ICONV_EILSEQ;
234         }
235         else
236             *error = YAZ_ICONV_EILSEQ;
237     }
238     else if (inp[0] <= 0xef && inbytesleft >= 3)
239     {
240         if ((inp[1] & 0xc0) == 0x80 && (inp[2] & 0xc0) == 0x80)
241         {
242             x = ((inp[0] & 0x0f) << 12) | ((inp[1] & 0x3f) << 6) |
243                 (inp[2] & 0x3f);
244             if (x >= 0x800)
245                 *no_read = 3;
246             else
247                 *error = YAZ_ICONV_EILSEQ;
248         }
249         else
250             *error = YAZ_ICONV_EILSEQ;
251     }            
252     else if (inp[0] <= 0xf7 && inbytesleft >= 4)
253     {
254         if ((inp[1] & 0xc0) == 0x80 && (inp[2] & 0xc0) == 0x80
255             && (inp[3] & 0xc0) == 0x80)
256         {
257             x = ((inp[0] & 0x07) << 18) | ((inp[1] & 0x3f) << 12) |
258                 ((inp[2] & 0x3f) << 6) | (inp[3] & 0x3f);
259             if (x >= 0x10000)
260                 *no_read = 4;
261             else
262                 *error = YAZ_ICONV_EILSEQ;
263         }
264         else
265             *error = YAZ_ICONV_EILSEQ;
266     }
267     else if (inp[0] <= 0xfb && inbytesleft >= 5)
268     {
269         if ((inp[1] & 0xc0) == 0x80 && (inp[2] & 0xc0) == 0x80
270             && (inp[3] & 0xc0) == 0x80 && (inp[4] & 0xc0) == 0x80)
271         {
272             x = ((inp[0] & 0x03) << 24) | ((inp[1] & 0x3f) << 18) |
273                 ((inp[2] & 0x3f) << 12) | ((inp[3] & 0x3f) << 6) |
274                 (inp[4] & 0x3f);
275             if (x >= 0x200000)
276                 *no_read = 5;
277             else
278                 *error = YAZ_ICONV_EILSEQ;
279         }
280         else
281             *error = YAZ_ICONV_EILSEQ;
282     }
283     else if (inp[0] <= 0xfd && inbytesleft >= 6)
284     {
285         if ((inp[1] & 0xc0) == 0x80 && (inp[2] & 0xc0) == 0x80
286             && (inp[3] & 0xc0) == 0x80 && (inp[4] & 0xc0) == 0x80
287             && (inp[5] & 0xc0) == 0x80)
288         {
289             x = ((inp[0] & 0x01) << 30) | ((inp[1] & 0x3f) << 24) |
290                 ((inp[2] & 0x3f) << 18) | ((inp[3] & 0x3f) << 12) |
291                 ((inp[4] & 0x3f) << 6) | (inp[5] & 0x3f);
292             if (x >= 0x4000000)
293                 *no_read = 6;
294             else
295                 *error = YAZ_ICONV_EILSEQ;
296         }
297         else
298             *error = YAZ_ICONV_EILSEQ;
299     }
300     else
301         *error = YAZ_ICONV_EINVAL;  /* incomplete sentence */
302
303     return x;
304 }
305
306 static unsigned long yaz_read_UTF8 (yaz_iconv_t cd, unsigned char *inp,
307                                     size_t inbytesleft, size_t *no_read)
308 {
309     return yaz_read_UTF8_char(inp, inbytesleft, no_read, &cd->my_errno);
310 }
311
312 static unsigned long yaz_read_UCS4 (yaz_iconv_t cd, unsigned char *inp,
313                                     size_t inbytesleft, size_t *no_read)
314 {
315     unsigned long x = 0;
316     
317     if (inbytesleft < 4)
318     {
319         cd->my_errno = YAZ_ICONV_EINVAL; /* incomplete input */
320         *no_read = 0;
321     }
322     else
323     {
324         x = (inp[0]<<24) | (inp[1]<<16) | (inp[2]<<8) | inp[3];
325         *no_read = 4;
326     }
327     return x;
328 }
329
330 static unsigned long yaz_read_UCS4LE (yaz_iconv_t cd, unsigned char *inp,
331                                       size_t inbytesleft, size_t *no_read)
332 {
333     unsigned long x = 0;
334     
335     if (inbytesleft < 4)
336     {
337         cd->my_errno = YAZ_ICONV_EINVAL; /* incomplete input */
338         *no_read = 0;
339     }
340     else
341     {
342         x = (inp[3]<<24) | (inp[2]<<16) | (inp[1]<<8) | inp[0];
343         *no_read = 4;
344     }
345     return x;
346 }
347
348 #if HAVE_WCHAR_H
349 static unsigned long yaz_read_wchar_t (yaz_iconv_t cd, unsigned char *inp,
350                                        size_t inbytesleft, size_t *no_read)
351 {
352     unsigned long x = 0;
353     
354     if (inbytesleft < sizeof(wchar_t))
355     {
356         cd->my_errno = YAZ_ICONV_EINVAL; /* incomplete input */
357         *no_read = 0;
358     }
359     else
360     {
361         wchar_t wch;
362         memcpy (&wch, inp, sizeof(wch));
363         x = wch;
364         *no_read = sizeof(wch);
365     }
366     return x;
367 }
368 #endif
369
370 static unsigned long yaz_read_iso5428_1984(yaz_iconv_t cd, unsigned char *inp,
371                                            size_t inbytesleft, size_t *no_read)
372 {
373     unsigned long x = 0;
374     int tonos = 0;
375     int dialitika = 0;
376
377     *no_read = 0;
378     while (inbytesleft > 0)
379     {
380         if (*inp == 0xa2)
381         {
382             tonos = 1;
383         }
384         else if (*inp == 0xa3)
385         {
386             dialitika = 1;
387         }
388         else
389             break;
390         inp++;
391         --inbytesleft;
392         (*no_read)++;
393     }    
394     if (inbytesleft == 0)
395     {
396         cd->my_errno = YAZ_ICONV_EINVAL; /* incomplete input */
397         *no_read = 0;
398         return 0;
399     }
400     switch (*inp) {
401     case 0xe1: /*  alpha small */
402             if (tonos) 
403                 x = 0x03ac;
404             else 
405                 x = 0x03b1;
406             break;
407     case 0xc1: /*  alpha capital */
408             if (tonos) 
409                 x = 0x0386;
410             else 
411                 x = 0x0391;
412             break;
413
414     case 0xe2: /*  Beta small */
415             x = 0x03b2;
416             break;
417     case 0xc2: /*  Beta capital */
418             x = 0x0392;
419             break;
420
421     case 0xe4: /*  Gamma small */
422             x = 0x03b3;
423             break;
424     case 0xc4: /*  Gamma capital */
425             x = 0x0393;
426             break;
427
428     case 0xe5: /*  Delta small */
429             x = 0x03b4;
430             break;
431     case 0xc5: /*  Delta capital */
432             x = 0x0394;
433             break;
434     case 0xe6: /*  epsilon small */
435             if (tonos) 
436                 x = 0x03ad;
437             else 
438                 x = 0x03b5;
439             break;
440     case 0xc6: /*  epsilon capital */
441             if (tonos) 
442                 x = 0x0388;
443             else 
444                 x = 0x0395;
445             break;
446     case 0xe9: /*  Zeta small */
447             x = 0x03b6;
448             break;
449     case 0xc9: /*  Zeta capital */
450             x = 0x0396;
451             break;
452     case 0xea: /*  Eta small */
453             if (tonos) 
454                 x = 0x03ae;
455             else 
456                 x = 0x03b7;
457             break;
458     case 0xca: /*  Eta capital */
459             if (tonos) 
460                 x = 0x0389;
461             else 
462                 x = 0x0397;
463             break;
464     case 0xeb: /*  Theta small */
465             x = 0x03b8;
466             break;
467     case 0xcb: /*  Theta capital */
468             x = 0x0398;
469             break;
470     case 0xec: /*  Iota small */
471             if (tonos) 
472                 if (dialitika) 
473                     x = 0x0390;
474                 else 
475                     x = 0x03af;
476             else 
477                 if (dialitika) 
478                     x = 0x03ca;
479                 else 
480                     x = 0x03b9;
481             break;
482     case 0xcc: /*  Iota capital */
483             if (tonos) 
484                 x = 0x038a;
485             else 
486                 if (dialitika) 
487                     x = 0x03aa;
488                 else 
489                     x = 0x0399;
490             break;
491     case 0xed: /*  Kappa small */
492             x = 0x03ba;
493             break;
494     case 0xcd: /*  Kappa capital */
495             x = 0x039a;
496             break;
497     case 0xee: /*  Lambda small */
498             x = 0x03bb;
499             break;
500     case 0xce: /*  Lambda capital */
501             x = 0x039b;
502             break;
503     case 0xef: /*  Mu small */
504             x = 0x03bc;
505             break;
506     case 0xcf: /*  Mu capital */
507             x = 0x039c;
508             break;
509     case 0xf0: /*  Nu small */
510             x = 0x03bd;
511             break;
512     case 0xd0: /*  Nu capital */
513             x = 0x039d;
514             break;
515     case 0xf1: /*  Xi small */
516             x = 0x03be;
517             break;
518     case 0xd1: /*  Xi capital */
519             x = 0x039e;
520             break;
521     case 0xf2: /*  Omicron small */
522             if (tonos) 
523                 x = 0x03cc;
524             else 
525                 x = 0x03bf;
526             break;
527     case 0xd2: /*  Omicron capital */
528             if (tonos) 
529                 x = 0x038c;
530             else 
531                 x = 0x039f;
532             break;
533     case 0xf3: /*  Pi small */
534             x = 0x03c0;
535             break;
536     case 0xd3: /*  Pi capital */
537             x = 0x03a0;
538             break;
539     case 0xf5: /*  Rho small */
540             x = 0x03c1;
541             break;
542     case 0xd5: /*  Rho capital */
543             x = 0x03a1;
544             break;
545     case 0xf7: /*  Sigma small (end of words) */
546             x = 0x03c2;
547             break;
548     case 0xf6: /*  Sigma small */
549             x = 0x03c3;
550             break;
551     case 0xd6: /*  Sigma capital */
552             x = 0x03a3;
553             break;
554     case 0xf8: /*  Tau small */
555             x = 0x03c4;
556             break;
557     case 0xd8: /*  Tau capital */
558             x = 0x03a4;
559             break;
560     case 0xf9: /*  Upsilon small */
561             if (tonos) 
562                 if (dialitika) 
563                     x = 0x03b0;
564                 else 
565                     x = 0x03cd;
566             else 
567                 if (dialitika) 
568                     x = 0x03cb;
569                 else 
570                     x = 0x03c5;
571             break;
572     case 0xd9: /*  Upsilon capital */
573             if (tonos) 
574                 x = 0x038e;
575             else 
576                 if (dialitika) 
577                     x = 0x03ab;
578                 else 
579                     x = 0x03a5;
580             break;
581     case 0xfa: /*  Phi small */
582             x = 0x03c6;
583             break;
584     case 0xda: /*  Phi capital */
585             x = 0x03a6;
586             break;
587     case 0xfb: /*  Chi small */
588             x = 0x03c7;
589             break;
590     case 0xdb: /*  Chi capital */
591             x = 0x03a7;
592             break;
593     case 0xfc: /*  Psi small */
594             x = 0x03c8;
595             break;
596     case 0xdc: /*  Psi capital */
597             x = 0x03a8;
598             break;
599     case 0xfd: /*  Omega small */
600             if (tonos) 
601                 x = 0x03ce;
602             else 
603                 x = 0x03c9;
604             break;
605     case 0xdd: /*  Omega capital */
606             if (tonos) 
607                 x = 0x038f;
608             else 
609                 x = 0x03a9;
610             break;
611     default:
612         x = *inp;
613         break;
614     }
615     (*no_read)++;
616     
617     return x;
618 }
619
620 static size_t yaz_write_iso5428_1984(yaz_iconv_t cd, unsigned long x,
621                                      char **outbuf, size_t *outbytesleft)
622 {
623     size_t k = 0;
624     unsigned char *out = (unsigned char*) *outbuf;
625     if (*outbytesleft < 3)
626     {
627         cd->my_errno = YAZ_ICONV_E2BIG;  /* not room for output */
628         return (size_t)(-1);
629     }
630     switch (x)
631     {
632     case 0x03ac : out[k++]=0xa2; out[k++]=0xe1; break;
633     case 0x03b1 : out[k++]=0xe1; break;
634     case 0x0386 : out[k++]=0xa2; out[k++]=0xc1; break;
635     case 0x0391 : out[k++]=0xc1; break;
636     case 0x03b2 : out[k++]=0xe2; break;
637     case 0x0392 : out[k++]=0xc2; break;
638     case 0x03b3 : out[k++]=0xe4; break;
639     case 0x0393 : out[k++]=0xc4; break;
640     case 0x03b4 : out[k++]=0xe5; break;
641     case 0x0394 : out[k++]=0xc5; break;
642     case 0x03ad : out[k++]=0xa2; out[k++]=0xe6; break;
643     case 0x03b5 : out[k++]=0xe6; break;
644     case 0x0388 : out[k++]=0xa2; out[k++]=0xc6; break;
645     case 0x0395 : out[k++]=0xc6; break;
646     case 0x03b6 : out[k++]=0xe9; break;
647     case 0x0396 : out[k++]=0xc9; break;
648     case 0x03ae : out[k++]=0xa2; out[k++]=0xea; break;
649     case 0x03b7 : out[k++]=0xea; break;
650     case 0x0389 : out[k++]=0xa2; out[k++]=0xca; break;
651     case 0x0397 : out[k++]=0xca; break;
652     case 0x03b8 : out[k++]=0xeb; break;
653     case 0x0398 : out[k++]=0xcb; break;
654     case 0x0390 : out[k++]=0xa2; out[k++]=0xa3; out[k++]=0xec; break;
655     case 0x03af : out[k++]=0xa2; out[k++]=0xec; break;
656     case 0x03ca : out[k++]=0xa3; out[k++]=0xec; break;
657     case 0x03b9 : out[k++]=0xec; break;
658     case 0x038a : out[k++]=0xa2; out[k++]=0xcc; break;
659     case 0x03aa : out[k++]=0xa3; out[k++]=0xcc; break;
660     case 0x0399 : out[k++]=0xcc; break;
661     case 0x03ba : out[k++]=0xed; break;
662     case 0x039a : out[k++]=0xcd; break;
663     case 0x03bb : out[k++]=0xee; break;
664     case 0x039b : out[k++]=0xce; break;
665     case 0x03bc : out[k++]=0xef; break;
666     case 0x039c : out[k++]=0xcf; break;
667     case 0x03bd : out[k++]=0xf0; break;
668     case 0x039d : out[k++]=0xd0; break;
669     case 0x03be : out[k++]=0xf1; break;
670     case 0x039e : out[k++]=0xd1; break;
671     case 0x03cc : out[k++]=0xa2; out[k++]=0xf2; break;
672     case 0x03bf : out[k++]=0xf2; break;
673     case 0x038c : out[k++]=0xa2; out[k++]=0xd2; break;
674     case 0x039f : out[k++]=0xd2; break;
675     case 0x03c0 : out[k++]=0xf3; break;
676     case 0x03a0 : out[k++]=0xd3; break;
677     case 0x03c1 : out[k++]=0xf5; break;
678     case 0x03a1 : out[k++]=0xd5; break;
679     case 0x03c2 : out[k++]=0xf7; break;
680     case 0x03c3 : out[k++]=0xf6; break;
681     case 0x03a3 : out[k++]=0xd6; break;
682     case 0x03c4 : out[k++]=0xf8; break;
683     case 0x03a4 : out[k++]=0xd8; break;
684     case 0x03b0 : out[k++]=0xa2; out[k++]=0xa3; out[k++]=0xf9; break;
685     case 0x03cd : out[k++]=0xa2; out[k++]=0xf9; break;
686     case 0x03cb : out[k++]=0xa3; out[k++]=0xf9; break;
687     case 0x03c5 : out[k++]=0xf9; break;
688     case 0x038e : out[k++]=0xa2; out[k++]=0xd9; break;
689     case 0x03ab : out[k++]=0xa3; out[k++]=0xd9; break;
690     case 0x03a5 : out[k++]=0xd9; break;
691     case 0x03c6 : out[k++]=0xfa; break;
692     case 0x03a6 : out[k++]=0xda; break;
693     case 0x03c7 : out[k++]=0xfb; break;
694     case 0x03a7 : out[k++]=0xdb; break;
695     case 0x03c8 : out[k++]=0xfc; break;
696     case 0x03a8 : out[k++]=0xdc; break;
697     case 0x03ce : out[k++]=0xa2; out[k++]=0xfd; break;
698     case 0x03c9 : out[k++]=0xfd; break;
699     case 0x038f : out[k++]=0xa2; out[k++]=0xdd; break;
700     case 0x03a9 : out[k++]=0xdd; break;
701     default:
702         if (x > 255)
703         {
704             cd->my_errno = YAZ_ICONV_EILSEQ;
705             return (size_t) -1;
706         }
707         out[k++] = x;
708         break;
709     }
710     *outbytesleft -= k;
711     (*outbuf) += k;
712     return 0;
713 }
714
715 static unsigned long yaz_read_advancegreek(yaz_iconv_t cd, unsigned char *inp,
716                                            size_t inbytesleft, size_t *no_read)
717 {
718     unsigned long x = 0;
719     int shift = 0;
720     int tonos = 0;
721     int dialitika = 0;
722
723     *no_read = 0;
724     while (inbytesleft > 0)
725     {
726         if (*inp == 0x9d)
727         {
728             tonos = 1;
729         }
730         else if (*inp == 0x9e)
731         {
732             dialitika = 1;
733         }
734         else if (*inp == 0x9f)
735         {
736             shift = 1;
737         }
738         else
739             break;
740         inp++;
741         --inbytesleft;
742         (*no_read)++;
743     }    
744     if (inbytesleft == 0)
745     {
746         cd->my_errno = YAZ_ICONV_EINVAL; /* incomplete input */
747         *no_read = 0;
748         return 0;
749     }
750     switch (*inp) {
751     case 0x81:
752         if (shift) 
753             if (tonos) 
754                 x = 0x0386;
755             else 
756                 x = 0x0391;
757         else 
758             if (tonos) 
759                 x = 0x03ac;
760             else 
761                 x = 0x03b1;
762         break;
763     case 0x82:
764         if (shift) 
765             x = 0x0392;
766         else 
767             x = 0x03b2;
768         
769         break;
770     case 0x83:
771         if (shift) 
772             x = 0x0393;
773         else 
774             x = 0x03b3;
775         break;
776     case 0x84:
777         if (shift) 
778             x = 0x0394;
779         else 
780             x = 0x03b4;
781         break;
782     case 0x85:
783         if (shift) 
784             if (tonos) 
785                 x = 0x0388;
786             else 
787                 x = 0x0395;
788         else 
789             if (tonos) 
790                 x = 0x03ad;
791             else 
792                 x = 0x03b5;
793         break;
794     case 0x86:
795         if (shift) 
796             x = 0x0396;
797         else 
798             x = 0x03b6;
799         break;
800     case 0x87:
801         if (shift) 
802             if (tonos) 
803                 x = 0x0389;
804             else 
805                 x = 0x0397;
806         else 
807             if (tonos) 
808                 x = 0x03ae;
809             else 
810                 x = 0x03b7;
811         break;
812     case 0x88:
813         if (shift) 
814             x = 0x0398;
815         else 
816             x = 0x03b8;
817         break;
818     case 0x89:
819         if (shift) 
820             if (tonos) 
821                 x = 0x038a;
822             else 
823                 if (dialitika) 
824                     x = 0x03aa;
825                 else 
826                     x = 0x0399;
827         else 
828             if (tonos) 
829                 if (dialitika) 
830                     x = 0x0390;
831                 else 
832                     x = 0x03af;
833         
834             else 
835                 if (dialitika) 
836                     x = 0x03ca;
837                 else 
838                     x = 0x03b9;
839         break;
840     case 0x8a:
841         if (shift) 
842             x = 0x039a;
843         else 
844             x = 0x03ba;
845         
846         break;
847     case 0x8b:
848         if (shift) 
849             x = 0x039b;
850         else 
851             x = 0x03bb;
852         break;
853     case 0x8c:
854         if (shift) 
855             x = 0x039c;
856         else 
857             x = 0x03bc;
858         
859         break;
860     case 0x8d:
861         if (shift) 
862             x = 0x039d;
863         else 
864             x = 0x03bd;
865         break;
866     case 0x8e:
867         if (shift) 
868             x = 0x039e;
869         else 
870             x = 0x03be;
871         break;
872     case 0x8f:
873         if (shift) 
874             if (tonos) 
875                 x = 0x038c;
876             else 
877                 x = 0x039f;
878         else 
879             if (tonos) 
880                 x = 0x03cc;
881             else 
882                 x = 0x03bf;
883         break;
884     case 0x90:
885         if (shift) 
886             x = 0x03a0;
887         else 
888             x = 0x03c0;
889         break;
890     case 0x91:
891         if (shift) 
892             x = 0x03a1;
893         else 
894             x = 0x03c1;
895         break;
896     case 0x92:
897         x = 0x03c2;
898         break;
899     case 0x93:
900         if (shift) 
901             x = 0x03a3;
902         else 
903             x = 0x03c3;
904         break;
905     case 0x94:
906         if (shift) 
907             x = 0x03a4;
908         else 
909             x = 0x03c4;
910         break;
911     case 0x95:
912         if (shift) 
913             if (tonos) 
914                 x = 0x038e;
915             else 
916                 if (dialitika) 
917                     x = 0x03ab;
918                 else 
919                     x = 0x03a5;
920         else 
921             if (tonos) 
922                 if (dialitika) 
923                     x = 0x03b0;
924                 else 
925                     x = 0x03cd;
926         
927             else 
928                 if (dialitika) 
929                     x = 0x03cb;
930                 else 
931                     x = 0x03c5;
932         break;
933     case 0x96:
934         if (shift) 
935             x = 0x03a6;
936         else 
937             x = 0x03c6;
938         break;
939     case 0x97:
940         if (shift) 
941             x = 0x03a7;
942         else 
943             x = 0x03c7;
944         break;
945     case 0x98:
946         if (shift) 
947             x = 0x03a8;
948         else 
949             x = 0x03c8;
950         
951         break;
952         
953     case 0x99:
954         if (shift) 
955             if (tonos) 
956                 x = 0x038f;
957             else 
958                 x = 0x03a9;
959         else 
960             if (tonos) 
961                 x = 0x03ce;
962             else 
963                 x = 0x03c9;
964         break;
965     default:
966         x = *inp;
967         break;
968     }
969     (*no_read)++;
970     
971     return x;
972 }
973
974 static size_t yaz_write_advancegreek(yaz_iconv_t cd, unsigned long x,
975                                      char **outbuf, size_t *outbytesleft)
976 {
977     size_t k = 0;
978     unsigned char *out = (unsigned char*) *outbuf;
979     if (*outbytesleft < 3)
980     {
981         cd->my_errno = YAZ_ICONV_E2BIG;  /* not room for output */
982         return (size_t)(-1);
983     }
984     switch (x)
985     {
986     case 0x03ac : out[k++]=0x9d; out[k++]=0x81; break;
987     case 0x03ad : out[k++]=0x9d; out[k++]=0x85; break;
988     case 0x03ae : out[k++]=0x9d; out[k++]=0x87; break;
989     case 0x03af : out[k++]=0x9d; out[k++]=0x89; break;
990     case 0x03cc : out[k++]=0x9d; out[k++]=0x8f; break;
991     case 0x03cd : out[k++]=0x9d; out[k++]=0x95; break;
992     case 0x03ce : out[k++]=0x9d; out[k++]=0x99; break;
993     case 0x0390 : out[k++]=0x9d; out[k++]=0x9e; out[k++]=0x89; break;
994     case 0x03b0 : out[k++]=0x9d; out[k++]=0x9e; out[k++]=0x95; break;
995     case 0x0386 : out[k++]=0x9d; out[k++]=0x9f; out[k++]=0x81; break;
996     case 0x0388 : out[k++]=0x9d; out[k++]=0x9f; out[k++]=0x85; break;
997     case 0x0389 : out[k++]=0x9d; out[k++]=0x9f; out[k++]=0x87; break;
998     case 0x038a : out[k++]=0x9d; out[k++]=0x9f; out[k++]=0x89; break;
999     case 0x038c : out[k++]=0x9d; out[k++]=0x9f; out[k++]=0x8f; break;
1000     case 0x038e : out[k++]=0x9d; out[k++]=0x9f; out[k++]=0x95; break;
1001     case 0x038f : out[k++]=0x9d; out[k++]=0x9f; out[k++]=0x99; break;
1002     case 0x03ca : out[k++]=0x9e; out[k++]=0x89; break;
1003     case 0x03cb : out[k++]=0x9e; out[k++]=0x95; break;
1004     case 0x03aa : out[k++]=0x9e; out[k++]=0x9f; out[k++]=0x89; break;
1005     case 0x03ab : out[k++]=0x9e; out[k++]=0x9f; out[k++]=0x95; break;
1006     case 0x0391 : out[k++]=0x9f; out[k++]=0x81; break;
1007     case 0x0392 : out[k++]=0x9f; out[k++]=0x82; break;
1008     case 0x0393 : out[k++]=0x9f; out[k++]=0x83; break;
1009     case 0x0394 : out[k++]=0x9f; out[k++]=0x84; break;
1010     case 0x0395 : out[k++]=0x9f; out[k++]=0x85; break;
1011     case 0x0396 : out[k++]=0x9f; out[k++]=0x86; break;
1012     case 0x0397 : out[k++]=0x9f; out[k++]=0x87; break;
1013     case 0x0398 : out[k++]=0x9f; out[k++]=0x88; break;
1014     case 0x0399 : out[k++]=0x9f; out[k++]=0x89; break;
1015     case 0x039a : out[k++]=0x9f; out[k++]=0x8a; break;
1016     case 0x039b : out[k++]=0x9f; out[k++]=0x8b; break;
1017     case 0x039c : out[k++]=0x9f; out[k++]=0x8c; break;
1018     case 0x039d : out[k++]=0x9f; out[k++]=0x8d; break;
1019     case 0x039e : out[k++]=0x9f; out[k++]=0x8e; break;
1020     case 0x039f : out[k++]=0x9f; out[k++]=0x8f; break;
1021     case 0x03a0 : out[k++]=0x9f; out[k++]=0x90; break;
1022     case 0x03a1 : out[k++]=0x9f; out[k++]=0x91; break;
1023     case 0x03a3 : out[k++]=0x9f; out[k++]=0x93; break;
1024     case 0x03a4 : out[k++]=0x9f; out[k++]=0x94; break;
1025     case 0x03a5 : out[k++]=0x9f; out[k++]=0x95; break;
1026     case 0x03a6 : out[k++]=0x9f; out[k++]=0x96; break;
1027     case 0x03a7 : out[k++]=0x9f; out[k++]=0x97; break;
1028     case 0x03a8 : out[k++]=0x9f; out[k++]=0x98; break;
1029     case 0x03a9 : out[k++]=0x9f; out[k++]=0x99; break;
1030     case 0x03b1 : out[k++]=0x81; break;
1031     case 0x03b2 : out[k++]=0x82; break;
1032     case 0x03b3 : out[k++]=0x83; break;
1033     case 0x03b4 : out[k++]=0x84; break;
1034     case 0x03b5 : out[k++]=0x85; break;
1035     case 0x03b6 : out[k++]=0x86; break;
1036     case 0x03b7 : out[k++]=0x87; break;
1037     case 0x03b8 : out[k++]=0x88; break;
1038     case 0x03b9 : out[k++]=0x89; break;
1039     case 0x03ba : out[k++]=0x8a; break;
1040     case 0x03bb : out[k++]=0x8b; break;
1041     case 0x03bc : out[k++]=0x8c; break;
1042     case 0x03bd : out[k++]=0x8d; break;
1043     case 0x03be : out[k++]=0x8e; break;
1044     case 0x03bf : out[k++]=0x8f; break;
1045     case 0x03c0 : out[k++]=0x90; break;
1046     case 0x03c1 : out[k++]=0x91; break;
1047     case 0x03c2 : out[k++]=0x92; break;
1048     case 0x03c3 : out[k++]=0x93; break;
1049     case 0x03c4 : out[k++]=0x94; break;
1050     case 0x03c5 : out[k++]=0x95; break;
1051     case 0x03c6 : out[k++]=0x96; break;
1052     case 0x03c7 : out[k++]=0x96; break;
1053     case 0x03c8 : out[k++]=0x98; break;
1054     case 0x03c9 : out[k++]=0x99; break;
1055     default:
1056         if (x > 255)
1057         {
1058             cd->my_errno = YAZ_ICONV_EILSEQ;
1059             return (size_t) -1;
1060         }
1061         out[k++] = x;
1062         break;
1063     }
1064     *outbytesleft -= k;
1065     (*outbuf) += k;
1066     return 0;
1067 }
1068
1069
1070 static unsigned long yaz_read_marc8_comb (yaz_iconv_t cd, unsigned char *inp,
1071                                           size_t inbytesleft, size_t *no_read,
1072                                           int *comb);
1073
1074 static unsigned long yaz_read_marc8 (yaz_iconv_t cd, unsigned char *inp,
1075                                      size_t inbytesleft, size_t *no_read)
1076 {
1077     unsigned long x;
1078     if (cd->comb_offset < cd->comb_size)
1079     {
1080         *no_read = cd->comb_no_read[cd->comb_offset];
1081         x = cd->comb_x[cd->comb_offset];
1082
1083         /* special case for double-diacritic combining characters, 
1084            INVERTED BREVE and DOUBLE TILDE.
1085            We'll increment the no_read counter by 1, since we want to skip over
1086            the processing of the closing ligature character
1087         */
1088         /* this code is no longer necessary.. our handlers code in
1089            yaz_marc8_?_conv (generated by charconv.tcl) now returns
1090            0 and no_read=1 when a sequence does not match the input.
1091            The SECOND HALFs in codetables.xml produces a non-existant
1092            entry in the conversion trie.. Hence when met, the input byte is
1093            skipped as it should (in yaz_iconv)
1094         */
1095 #if 0
1096         if (x == 0x0361 || x == 0x0360)
1097             *no_read += 1;
1098 #endif
1099         cd->comb_offset++;
1100         return x;
1101     }
1102
1103     cd->comb_offset = 0;
1104     for (cd->comb_size = 0; cd->comb_size < 8; cd->comb_size++)
1105     {
1106         int comb = 0;
1107
1108         if (inbytesleft == 0 && cd->comb_size)
1109         {
1110             cd->my_errno = YAZ_ICONV_EINVAL;
1111             x = 0;
1112             *no_read = 0;
1113             break;
1114         }
1115         x = yaz_read_marc8_comb(cd, inp, inbytesleft, no_read, &comb);
1116         if (!comb || !x)
1117             break;
1118         cd->comb_x[cd->comb_size] = x;
1119         cd->comb_no_read[cd->comb_size] = *no_read;
1120         inp += *no_read;
1121         inbytesleft = inbytesleft - *no_read;
1122     }
1123     return x;
1124 }
1125
1126 static unsigned long yaz_read_marc8s(yaz_iconv_t cd, unsigned char *inp,
1127                                      size_t inbytesleft, size_t *no_read)
1128 {
1129     unsigned long x = yaz_read_marc8(cd, inp, inbytesleft, no_read);
1130     if (x && cd->comb_size == 1)
1131     {
1132         /* For MARC8s we try to get a Latin-1 page code out of it */
1133         int i;
1134         for (i = 0; latin1_comb[i].x1; i++)
1135             if (cd->comb_x[0] == latin1_comb[i].x2 && x == latin1_comb[i].x1)
1136             {
1137                 *no_read += cd->comb_no_read[0];
1138                 cd->comb_size = 0;
1139                 x = latin1_comb[i].y;
1140                 break;
1141             }
1142     }
1143     return x;
1144 }
1145
1146 static unsigned long yaz_read_marc8_comb(yaz_iconv_t cd, unsigned char *inp,
1147                                          size_t inbytesleft, size_t *no_read,
1148                                          int *comb)
1149 {
1150     *no_read = 0;
1151     while(inbytesleft >= 1 && inp[0] == 27)
1152     {
1153         size_t inbytesleft0 = inbytesleft;
1154         inp++;
1155         inbytesleft--;
1156         while(inbytesleft > 0 && strchr("(,$!)-", *inp))
1157         {
1158             inbytesleft--;
1159             inp++;
1160         }
1161         if (inbytesleft <= 0)
1162         {
1163             *no_read = 0;
1164             cd->my_errno = YAZ_ICONV_EINVAL;
1165             return 0;
1166         }
1167         cd->marc8_esc_mode = *inp++;
1168         inbytesleft--;
1169         (*no_read) += inbytesleft0 - inbytesleft;
1170     }
1171     if (inbytesleft <= 0)
1172         return 0;
1173     else
1174     {
1175         unsigned long x;
1176         size_t no_read_sub = 0;
1177         *comb = 0;
1178
1179         switch(cd->marc8_esc_mode)
1180         {
1181         case 'B':  /* Basic ASCII */
1182         case 'E':  /* ANSEL */
1183         case 's':  /* ASCII */
1184             x = yaz_marc8_1_conv(inp, inbytesleft, &no_read_sub, comb);
1185             break;
1186         case 'g':  /* Greek */
1187             x = yaz_marc8_2_conv(inp, inbytesleft, &no_read_sub, comb);
1188             break;
1189         case 'b':  /* Subscripts */
1190             x = yaz_marc8_3_conv(inp, inbytesleft, &no_read_sub, comb);
1191             break;
1192         case 'p':  /* Superscripts */
1193             x = yaz_marc8_4_conv(inp, inbytesleft, &no_read_sub, comb);
1194             break;
1195         case '2':  /* Basic Hebrew */
1196             x = yaz_marc8_5_conv(inp, inbytesleft, &no_read_sub, comb);
1197             break;
1198         case 'N':  /* Basic Cyrillic */
1199         case 'Q':  /* Extended Cyrillic */
1200             x = yaz_marc8_6_conv(inp, inbytesleft, &no_read_sub, comb);
1201             break;
1202         case '3':  /* Basic Arabic */
1203         case '4':  /* Extended Arabic */
1204             x = yaz_marc8_7_conv(inp, inbytesleft, &no_read_sub, comb);
1205             break;
1206         case 'S':  /* Greek */
1207             x = yaz_marc8_8_conv(inp, inbytesleft, &no_read_sub, comb);
1208             break;
1209         case '1':  /* Chinese, Japanese, Korean (EACC) */
1210             x = yaz_marc8_9_conv(inp, inbytesleft, &no_read_sub, comb);
1211             break;
1212         default:
1213             *no_read = 0;
1214             cd->my_errno = YAZ_ICONV_EILSEQ;
1215             return 0;
1216         }
1217         *no_read += no_read_sub;
1218         return x;
1219     }
1220 }
1221
1222 static size_t yaz_write_UTF8(yaz_iconv_t cd, unsigned long x,
1223                              char **outbuf, size_t *outbytesleft)
1224 {
1225     return yaz_write_UTF8_char(x, outbuf, outbytesleft, &cd->my_errno);
1226 }
1227
1228 size_t yaz_write_UTF8_char(unsigned long x,
1229                            char **outbuf, size_t *outbytesleft,
1230                            int *error)
1231 {
1232     unsigned char *outp = (unsigned char *) *outbuf;
1233
1234     if (x <= 0x7f && *outbytesleft >= 1)
1235     {
1236         *outp++ = (unsigned char) x;
1237         (*outbytesleft)--;
1238     } 
1239     else if (x <= 0x7ff && *outbytesleft >= 2)
1240     {
1241         *outp++ = (unsigned char) ((x >> 6) | 0xc0);
1242         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
1243         (*outbytesleft) -= 2;
1244     }
1245     else if (x <= 0xffff && *outbytesleft >= 3)
1246     {
1247         *outp++ = (unsigned char) ((x >> 12) | 0xe0);
1248         *outp++ = (unsigned char) (((x >> 6) & 0x3f) | 0x80);
1249         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
1250         (*outbytesleft) -= 3;
1251     }
1252     else if (x <= 0x1fffff && *outbytesleft >= 4)
1253     {
1254         *outp++ = (unsigned char) ((x >> 18) | 0xf0);
1255         *outp++ = (unsigned char) (((x >> 12) & 0x3f) | 0x80);
1256         *outp++ = (unsigned char) (((x >> 6)  & 0x3f) | 0x80);
1257         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
1258         (*outbytesleft) -= 4;
1259     }
1260     else if (x <= 0x3ffffff && *outbytesleft >= 5)
1261     {
1262         *outp++ = (unsigned char) ((x >> 24) | 0xf8);
1263         *outp++ = (unsigned char) (((x >> 18) & 0x3f) | 0x80);
1264         *outp++ = (unsigned char) (((x >> 12) & 0x3f) | 0x80);
1265         *outp++ = (unsigned char) (((x >> 6)  & 0x3f) | 0x80);
1266         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
1267         (*outbytesleft) -= 5;
1268     }
1269     else if (*outbytesleft >= 6)
1270     {
1271         *outp++ = (unsigned char) ((x >> 30) | 0xfc);
1272         *outp++ = (unsigned char) (((x >> 24) & 0x3f) | 0x80);
1273         *outp++ = (unsigned char) (((x >> 18) & 0x3f) | 0x80);
1274         *outp++ = (unsigned char) (((x >> 12) & 0x3f) | 0x80);
1275         *outp++ = (unsigned char) (((x >> 6)  & 0x3f) | 0x80);
1276         *outp++ = (unsigned char) ((x & 0x3f) | 0x80);
1277         (*outbytesleft) -= 6;
1278     }
1279     else 
1280     {
1281         *error = YAZ_ICONV_E2BIG;  /* not room for output */
1282         return (size_t)(-1);
1283     }
1284     *outbuf = (char *) outp;
1285     return 0;
1286 }
1287
1288 static size_t yaz_write_ISO8859_1 (yaz_iconv_t cd, unsigned long x,
1289                                    char **outbuf, size_t *outbytesleft)
1290 {
1291     /* list of two char unicode sequence that, when combined, are
1292        equivalent to single unicode chars that can be represented in
1293        ISO-8859-1/Latin-1.
1294        Regular iconv on Linux at least does not seem to convert these,
1295        but since MARC-8 to UTF-8 generates these composed sequence
1296        we get a better chance of a successful MARC-8 -> ISO-8859-1
1297        conversion */
1298     unsigned char *outp = (unsigned char *) *outbuf;
1299
1300     if (cd->compose_char)
1301     {
1302         int i;
1303         for (i = 0; latin1_comb[i].x1; i++)
1304             if (cd->compose_char == latin1_comb[i].x1 && x == latin1_comb[i].x2)
1305             {
1306                 x = latin1_comb[i].y;
1307                 break;
1308             }
1309         if (*outbytesleft < 1)
1310         {  /* no room. Retain compose_char and bail out */
1311             cd->my_errno = YAZ_ICONV_E2BIG;
1312             return (size_t)(-1);
1313         }
1314         if (!latin1_comb[i].x1) 
1315         {   /* not found. Just write compose_char */
1316             *outp++ = (unsigned char) cd->compose_char;
1317             (*outbytesleft)--;
1318             *outbuf = (char *) outp;
1319         }
1320         /* compose_char used so reset it. x now holds current char */
1321         cd->compose_char = 0;
1322     }
1323
1324     if (x > 32 && x < 127 && cd->compose_char == 0)
1325     {
1326         cd->compose_char = x;
1327         return 0;
1328     }
1329     else if (x > 255 || x < 1)
1330     {
1331         cd->my_errno = YAZ_ICONV_EILSEQ;
1332         return (size_t) -1;
1333     }
1334     else if (*outbytesleft < 1)
1335     {
1336         cd->my_errno = YAZ_ICONV_E2BIG;
1337         return (size_t)(-1);
1338     }
1339     *outp++ = (unsigned char) x;
1340     (*outbytesleft)--;
1341     *outbuf = (char *) outp;
1342     return 0;
1343 }
1344
1345 static size_t yaz_flush_ISO8859_1(yaz_iconv_t cd,
1346                                   char **outbuf, size_t *outbytesleft)
1347 {
1348     if (cd->compose_char)
1349     {
1350         unsigned char *outp = (unsigned char *) *outbuf;
1351         if (*outbytesleft < 1)
1352         {
1353             cd->my_errno = YAZ_ICONV_E2BIG;
1354             return (size_t)(-1);
1355         }
1356         *outp++ = (unsigned char) cd->compose_char;
1357         (*outbytesleft)--;
1358         *outbuf = (char *) outp;
1359         cd->compose_char = 0;
1360     }
1361     return 0;
1362 }
1363
1364 static size_t yaz_write_UCS4 (yaz_iconv_t cd, unsigned long x,
1365                               char **outbuf, size_t *outbytesleft)
1366 {
1367     unsigned char *outp = (unsigned char *) *outbuf;
1368     if (*outbytesleft >= 4)
1369     {
1370         *outp++ = (unsigned char) (x>>24);
1371         *outp++ = (unsigned char) (x>>16);
1372         *outp++ = (unsigned char) (x>>8);
1373         *outp++ = (unsigned char) x;
1374         (*outbytesleft) -= 4;
1375     }
1376     else
1377     {
1378         cd->my_errno = YAZ_ICONV_E2BIG;
1379         return (size_t)(-1);
1380     }
1381     *outbuf = (char *) outp;
1382     return 0;
1383 }
1384
1385 static size_t yaz_write_UCS4LE (yaz_iconv_t cd, unsigned long x,
1386                                 char **outbuf, size_t *outbytesleft)
1387 {
1388     unsigned char *outp = (unsigned char *) *outbuf;
1389     if (*outbytesleft >= 4)
1390     {
1391         *outp++ = (unsigned char) x;
1392         *outp++ = (unsigned char) (x>>8);
1393         *outp++ = (unsigned char) (x>>16);
1394         *outp++ = (unsigned char) (x>>24);
1395         (*outbytesleft) -= 4;
1396     }
1397     else
1398     {
1399         cd->my_errno = YAZ_ICONV_E2BIG;
1400         return (size_t)(-1);
1401     }
1402     *outbuf = (char *) outp;
1403     return 0;
1404 }
1405
1406 static unsigned long lookup_marc8(yaz_iconv_t cd,
1407                                   unsigned long x, int *comb,
1408                                   const char **page_chr)
1409 {
1410     char utf8_buf[7];
1411     char *utf8_outbuf = utf8_buf;
1412     size_t utf8_outbytesleft = sizeof(utf8_buf)-1, r;
1413
1414     r = yaz_write_UTF8(cd, x, &utf8_outbuf, &utf8_outbytesleft);
1415     if (r == (size_t)(-1))
1416     {
1417         cd->my_errno = YAZ_ICONV_EILSEQ;
1418         return 0;
1419     }
1420     else
1421     {
1422         unsigned char *inp;
1423         size_t inbytesleft, no_read_sub = 0;
1424         unsigned long x;
1425
1426         *utf8_outbuf = '\0';        
1427         inp = (unsigned char *) utf8_buf;
1428         inbytesleft = strlen(utf8_buf);
1429         
1430         x = yaz_marc8r_1_conv(inp, inbytesleft, &no_read_sub, comb);
1431         if (x)
1432         {
1433             *page_chr = "\033(B";
1434             return x;
1435         }
1436         x = yaz_marc8r_2_conv(inp, inbytesleft, &no_read_sub, comb);
1437         if (x)
1438         {
1439             *page_chr = "\033g";
1440             return x;
1441         }
1442         x = yaz_marc8r_3_conv(inp, inbytesleft, &no_read_sub, comb);
1443         if (x)
1444         {
1445             *page_chr = "\033b";
1446             return x;
1447         }
1448         x = yaz_marc8r_4_conv(inp, inbytesleft, &no_read_sub, comb);
1449         if (x)
1450         {
1451             *page_chr = "\033p";
1452             return x;
1453         }
1454         x = yaz_marc8r_5_conv(inp, inbytesleft, &no_read_sub, comb);
1455         if (x)
1456         {
1457             *page_chr = "\033(2";
1458             return x;
1459         }
1460         x = yaz_marc8r_6_conv(inp, inbytesleft, &no_read_sub, comb);
1461         if (x)
1462         {
1463             *page_chr = "\033(N";
1464             return x;
1465         }
1466         x = yaz_marc8r_7_conv(inp, inbytesleft, &no_read_sub, comb);
1467         if (x)
1468         {
1469             *page_chr = "\033(3";
1470             return x;
1471         }
1472         x = yaz_marc8r_8_conv(inp, inbytesleft, &no_read_sub, comb);
1473         if (x)
1474         {
1475             *page_chr = "\033(S";
1476             return x;
1477         }
1478         x = yaz_marc8r_9_conv(inp, inbytesleft, &no_read_sub, comb);
1479         if (x)
1480         {
1481             *page_chr = "\033$1";
1482             return x;
1483         }
1484         cd->my_errno = YAZ_ICONV_EILSEQ;
1485         return x;
1486     }
1487 }
1488
1489 static size_t flush_combos(yaz_iconv_t cd,
1490                            char **outbuf, size_t *outbytesleft)
1491 {
1492     unsigned long y = cd->write_marc8_last;
1493     unsigned char byte;
1494     char out_buf[10];
1495     size_t i, out_no = 0;
1496
1497     if (!y)
1498         return 0;
1499
1500     byte = (unsigned char )((y>>16) & 0xff);
1501     if (byte)
1502         out_buf[out_no++] = byte;
1503     byte = (unsigned char)((y>>8) & 0xff);
1504     if (byte)
1505         out_buf[out_no++] = byte;
1506     byte = (unsigned char )(y & 0xff);
1507     if (byte)
1508         out_buf[out_no++] = byte;
1509
1510     if (out_no + cd->write_marc8_comb_no + 1 > *outbytesleft)
1511     {
1512         cd->my_errno = YAZ_ICONV_E2BIG;
1513         return (size_t) (-1);
1514     }
1515
1516     for (i = 0; i < cd->write_marc8_comb_no; i++)
1517     {
1518         /* all MARC-8 combined characters are simple bytes */
1519         byte = (unsigned char )(cd->write_marc8_comb_ch[i]);
1520         *(*outbuf)++ = byte;
1521         (*outbytesleft)--;
1522     }
1523     memcpy(*outbuf, out_buf, out_no);
1524     *outbuf += out_no;
1525     (*outbytesleft) -= out_no;
1526     if (cd->write_marc8_second_half_char)
1527     {
1528         *(*outbuf)++ = cd->write_marc8_second_half_char;
1529         (*outbytesleft)--;
1530     }        
1531
1532     cd->write_marc8_last = 0;
1533     cd->write_marc8_comb_no = 0;
1534     cd->write_marc8_second_half_char = 0;
1535     return 0;
1536 }
1537
1538 static size_t yaz_write_marc8_page_chr(yaz_iconv_t cd, 
1539                                        char **outbuf, size_t *outbytesleft,
1540                                        const char *page_chr)
1541 {
1542     const char *old_page_chr = cd->write_marc8_page_chr;
1543     if (strcmp(page_chr, old_page_chr))
1544     {
1545         size_t plen = 0;
1546         const char *page_out = page_chr;
1547         
1548         if (*outbytesleft < 8)
1549         {
1550             cd->my_errno = YAZ_ICONV_E2BIG;
1551             
1552             return (size_t) (-1);
1553         }
1554         cd->write_marc8_page_chr = page_chr;
1555         
1556         if (!strcmp(old_page_chr, "\033p") 
1557             || !strcmp(old_page_chr, "\033g")
1558             || !strcmp(old_page_chr, "\033b"))
1559         {
1560             /* Technique 1 leave */
1561             page_out = "\033s";
1562             if (strcmp(page_chr, "\033(B")) /* Not going ASCII page? */
1563             {
1564                 /* Must leave script + enter new page */
1565                 plen = strlen(page_out);
1566                 memcpy(*outbuf, page_out, plen);
1567                 (*outbuf) += plen;
1568                 (*outbytesleft) -= plen;
1569                 page_out = page_chr;
1570             }
1571         }
1572         plen = strlen(page_out);
1573         memcpy(*outbuf, page_out, plen);
1574         (*outbuf) += plen;
1575         (*outbytesleft) -= plen;
1576     }
1577     return 0;
1578 }
1579
1580
1581 static size_t yaz_write_marc8_2(yaz_iconv_t cd, unsigned long x,
1582                                 char **outbuf, size_t *outbytesleft)
1583 {
1584     int comb = 0;
1585     const char *page_chr = 0;
1586     unsigned long y = lookup_marc8(cd, x, &comb, &page_chr);
1587
1588     if (!y)
1589         return (size_t) (-1);
1590
1591     if (comb)
1592     {
1593         if (x == 0x0361)
1594             cd->write_marc8_second_half_char = 0xEC;
1595         else if (x == 0x0360)
1596             cd->write_marc8_second_half_char = 0xFB;
1597
1598         if (cd->write_marc8_comb_no < 6)
1599             cd->write_marc8_comb_ch[cd->write_marc8_comb_no++] = y;
1600     }
1601     else
1602     {
1603         size_t r = flush_combos(cd, outbuf, outbytesleft);
1604         if (r)
1605             return r;
1606
1607         r = yaz_write_marc8_page_chr(cd, outbuf, outbytesleft, page_chr);
1608         if (r)
1609             return r;
1610         cd->write_marc8_last = y;
1611     }
1612     return 0;
1613 }
1614
1615 static size_t yaz_flush_marc8(yaz_iconv_t cd,
1616                               char **outbuf, size_t *outbytesleft)
1617 {
1618     size_t r = flush_combos(cd, outbuf, outbytesleft);
1619     if (r)
1620         return r;
1621     return yaz_write_marc8_page_chr(cd, outbuf, outbytesleft, "\033(B");
1622 }
1623
1624 static size_t yaz_write_marc8(yaz_iconv_t cd, unsigned long x,
1625                               char **outbuf, size_t *outbytesleft)
1626 {
1627     int i;
1628     for (i = 0; latin1_comb[i].x1; i++)
1629     {
1630         if (x == latin1_comb[i].y)
1631         {
1632             size_t r ;
1633             /* save the output pointers .. */
1634             char *outbuf0 = *outbuf;
1635             size_t outbytesleft0 = *outbytesleft;
1636             int last_ch = cd->write_marc8_last;
1637
1638             r = yaz_write_marc8_2(cd, latin1_comb[i].x1,
1639                                   outbuf, outbytesleft);
1640             if (r)
1641                 return r;
1642             r = yaz_write_marc8_2(cd, latin1_comb[i].x2,
1643                                   outbuf, outbytesleft);
1644             if (r && cd->my_errno == YAZ_ICONV_E2BIG)
1645             {
1646                 /* not enough room. reset output to original values */
1647                 *outbuf = outbuf0;
1648                 *outbytesleft = outbytesleft0;
1649                 cd->write_marc8_last = last_ch;
1650             }
1651             return r;
1652         }
1653     }
1654     return yaz_write_marc8_2(cd, x, outbuf, outbytesleft);
1655 }
1656
1657
1658 #if HAVE_WCHAR_H
1659 static size_t yaz_write_wchar_t(yaz_iconv_t cd, unsigned long x,
1660                                 char **outbuf, size_t *outbytesleft)
1661 {
1662     unsigned char *outp = (unsigned char *) *outbuf;
1663
1664     if (*outbytesleft >= sizeof(wchar_t))
1665     {
1666         wchar_t wch = x;
1667         memcpy(outp, &wch, sizeof(wch));
1668         outp += sizeof(wch);
1669         (*outbytesleft) -= sizeof(wch);
1670     }
1671     else
1672     {
1673         cd->my_errno = YAZ_ICONV_E2BIG;
1674         return (size_t)(-1);
1675     }
1676     *outbuf = (char *) outp;
1677     return 0;
1678 }
1679 #endif
1680
1681 int yaz_iconv_isbuiltin(yaz_iconv_t cd)
1682 {
1683     return cd->read_handle && cd->write_handle;
1684 }
1685
1686 yaz_iconv_t yaz_iconv_open (const char *tocode, const char *fromcode)
1687 {
1688     yaz_iconv_t cd = (yaz_iconv_t) xmalloc (sizeof(*cd));
1689
1690     cd->write_handle = 0;
1691     cd->read_handle = 0;
1692     cd->init_handle = 0;
1693     cd->flush_handle = 0;
1694     cd->my_errno = YAZ_ICONV_UNKNOWN;
1695
1696     /* a useful hack: if fromcode has leading @,
1697        the library not use YAZ's own conversions .. */
1698     if (fromcode[0] == '@')
1699         fromcode++;
1700     else
1701     {
1702         if (!yaz_matchstr(fromcode, "UTF8"))
1703         {
1704             cd->read_handle = yaz_read_UTF8;
1705             cd->init_handle = yaz_init_UTF8;
1706         }
1707         else if (!yaz_matchstr(fromcode, "ISO88591"))
1708             cd->read_handle = yaz_read_ISO8859_1;
1709         else if (!yaz_matchstr(fromcode, "UCS4"))
1710             cd->read_handle = yaz_read_UCS4;
1711         else if (!yaz_matchstr(fromcode, "UCS4LE"))
1712             cd->read_handle = yaz_read_UCS4LE;
1713         else if (!yaz_matchstr(fromcode, "MARC8"))
1714             cd->read_handle = yaz_read_marc8;
1715         else if (!yaz_matchstr(fromcode, "MARC8s"))
1716             cd->read_handle = yaz_read_marc8s;
1717         else if (!yaz_matchstr(fromcode, "advancegreek"))
1718             cd->read_handle = yaz_read_advancegreek;
1719         else if (!yaz_matchstr(fromcode, "iso54281984"))
1720             cd->read_handle = yaz_read_iso5428_1984;
1721 #if HAVE_WCHAR_H
1722         else if (!yaz_matchstr(fromcode, "WCHAR_T"))
1723             cd->read_handle = yaz_read_wchar_t;
1724 #endif
1725         
1726         if (!yaz_matchstr(tocode, "UTF8"))
1727             cd->write_handle = yaz_write_UTF8;
1728         else if (!yaz_matchstr(tocode, "ISO88591"))
1729         {
1730             cd->write_handle = yaz_write_ISO8859_1;
1731             cd->flush_handle = yaz_flush_ISO8859_1;
1732         }
1733         else if (!yaz_matchstr (tocode, "UCS4"))
1734             cd->write_handle = yaz_write_UCS4;
1735         else if (!yaz_matchstr(tocode, "UCS4LE"))
1736             cd->write_handle = yaz_write_UCS4LE;
1737         else if (!yaz_matchstr(tocode, "MARC8"))
1738         {
1739             cd->write_handle = yaz_write_marc8;
1740             cd->flush_handle = yaz_flush_marc8;
1741         }
1742         else if (!yaz_matchstr(tocode, "MARC8s"))
1743         {
1744             cd->write_handle = yaz_write_marc8;
1745             cd->flush_handle = yaz_flush_marc8;
1746         }
1747         else if (!yaz_matchstr(tocode, "advancegreek"))
1748         {
1749             cd->write_handle = yaz_write_advancegreek;
1750         }
1751         else if (!yaz_matchstr(tocode, "iso54281984"))
1752         {
1753             cd->write_handle = yaz_write_iso5428_1984;
1754         }
1755 #if HAVE_WCHAR_H
1756         else if (!yaz_matchstr(tocode, "WCHAR_T"))
1757             cd->write_handle = yaz_write_wchar_t;
1758 #endif
1759     }
1760 #if HAVE_ICONV_H
1761     cd->iconv_cd = 0;
1762     if (!cd->read_handle || !cd->write_handle)
1763     {
1764         cd->iconv_cd = iconv_open (tocode, fromcode);
1765         if (cd->iconv_cd == (iconv_t) (-1))
1766         {
1767             xfree (cd);
1768             return 0;
1769         }
1770     }
1771 #else
1772     if (!cd->read_handle || !cd->write_handle)
1773     {
1774         xfree (cd);
1775         return 0;
1776     }
1777 #endif
1778     cd->init_flag = 1;
1779     return cd;
1780 }
1781
1782 size_t yaz_iconv(yaz_iconv_t cd, char **inbuf, size_t *inbytesleft,
1783                  char **outbuf, size_t *outbytesleft)
1784 {
1785     char *inbuf0 = 0;
1786     size_t r = 0;
1787
1788 #if HAVE_ICONV_H
1789     if (cd->iconv_cd)
1790     {
1791         size_t r =
1792             iconv(cd->iconv_cd, inbuf, inbytesleft, outbuf, outbytesleft);
1793         if (r == (size_t)(-1))
1794         {
1795             switch (yaz_errno())
1796             {
1797             case E2BIG:
1798                 cd->my_errno = YAZ_ICONV_E2BIG;
1799                 break;
1800             case EINVAL:
1801                 cd->my_errno = YAZ_ICONV_EINVAL;
1802                 break;
1803             case EILSEQ:
1804                 cd->my_errno = YAZ_ICONV_EILSEQ;
1805                 break;
1806             default:
1807                 cd->my_errno = YAZ_ICONV_UNKNOWN;
1808             }
1809         }
1810         return r;
1811     }
1812 #endif
1813
1814     if (inbuf)
1815         inbuf0 = *inbuf;
1816
1817     if (cd->init_flag)
1818     {
1819         cd->my_errno = YAZ_ICONV_UNKNOWN;
1820         cd->marc8_esc_mode = 'B';
1821         
1822         cd->comb_offset = cd->comb_size = 0;
1823         cd->compose_char = 0;
1824         
1825         cd->write_marc8_comb_no = 0;
1826         cd->write_marc8_second_half_char = 0;
1827         cd->write_marc8_last = 0;
1828         cd->write_marc8_page_chr = "\033(B";
1829         
1830         cd->unget_x = 0;
1831         cd->no_read_x = 0;
1832     }
1833
1834     if (cd->init_flag)
1835     {
1836         if (cd->init_handle && inbuf && *inbuf)
1837         {
1838             size_t no_read = 0;
1839             size_t r = (cd->init_handle)(cd, (unsigned char *) *inbuf,
1840                                          *inbytesleft, &no_read);
1841             if (r)
1842             {
1843                 if (cd->my_errno == YAZ_ICONV_EINVAL)
1844                     return r;
1845                 cd->init_flag = 0;
1846                 return r;
1847             }
1848             *inbytesleft -= no_read;
1849             *inbuf += no_read;
1850         }
1851     }
1852     cd->init_flag = 0;
1853
1854     if (!inbuf || !*inbuf)
1855     {
1856         if (outbuf && *outbuf)
1857         {
1858             if (cd->unget_x)
1859                 r = (*cd->write_handle)(cd, cd->unget_x, outbuf, outbytesleft);
1860             if (cd->flush_handle)
1861                 r = (*cd->flush_handle)(cd, outbuf, outbytesleft);
1862         }
1863         if (r == 0)
1864             cd->init_flag = 1;
1865         cd->unget_x = 0;
1866         return r;
1867     }
1868     while (1)
1869     {
1870         unsigned long x;
1871         size_t no_read;
1872
1873         if (cd->unget_x)
1874         {
1875             x = cd->unget_x;
1876             no_read = cd->no_read_x;
1877         }
1878         else
1879         {
1880             if (*inbytesleft == 0)
1881             {
1882                 r = *inbuf - inbuf0;
1883                 break;
1884             }
1885             x = (*cd->read_handle)(cd, (unsigned char *) *inbuf, *inbytesleft,
1886                                    &no_read);
1887             if (no_read == 0)
1888             {
1889                 r = (size_t)(-1);
1890                 break;
1891             }
1892         }
1893         if (x)
1894         {
1895             r = (*cd->write_handle)(cd, x, outbuf, outbytesleft);
1896             if (r)
1897             {
1898                 /* unable to write it. save it because read_handle cannot
1899                    rewind .. */
1900                 if (cd->my_errno == YAZ_ICONV_E2BIG)
1901                 {
1902                     cd->unget_x = x;
1903                     cd->no_read_x = no_read;
1904                     break;
1905                 }
1906             }
1907             cd->unget_x = 0;
1908         }
1909         *inbytesleft -= no_read;
1910         (*inbuf) += no_read;
1911     }
1912     return r;
1913 }
1914
1915 int yaz_iconv_error (yaz_iconv_t cd)
1916 {
1917     return cd->my_errno;
1918 }
1919
1920 int yaz_iconv_close (yaz_iconv_t cd)
1921 {
1922 #if HAVE_ICONV_H
1923     if (cd->iconv_cd)
1924         iconv_close (cd->iconv_cd);
1925 #endif
1926     xfree (cd);
1927     return 0;
1928 }
1929
1930 /*
1931  * Local variables:
1932  * c-basic-offset: 4
1933  * indent-tabs-mode: nil
1934  * End:
1935  * vim: shiftwidth=4 tabstop=8 expandtab
1936  */