Bump copyright year
[yaz-moved-to-github.git] / src / iconv_decode_iso5426.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2010 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file
7  * \brief ISO 5426 decoding
8  *
9  * MARC-8 reference:
10  *  http://www.loc.gov/marc/specifications/specchariso8.html
11  *
12  * ISO 5426 reference (in German)
13  * Zeichenkonkordanz MAB2-Zeichensatz - ISO/IEC 10646 / Unicode
14  * http://www.d-nb.de/standardisierung/pdf/mab_unic.pdf
15  */
16
17 #if HAVE_CONFIG_H
18 #include <config.h>
19 #endif
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <ctype.h>
25
26 #include <yaz/xmalloc.h>
27 #include "iconv-p.h"
28
29 struct decoder_data {
30     int g0_mode;
31     int g1_mode;
32
33     int comb_offset;
34     int comb_size;
35     unsigned long comb_x[8];
36     size_t comb_no_read[8];
37 };
38
39 yaz_conv_func_t yaz_iso5426_42_conv;
40 yaz_conv_func_t yaz_iso5426_45_conv;
41 yaz_conv_func_t yaz_iso5426_67_conv;
42 yaz_conv_func_t yaz_iso5426_62_conv;
43 yaz_conv_func_t yaz_iso5426_70_conv;
44 yaz_conv_func_t yaz_iso5426_32_conv;
45 yaz_conv_func_t yaz_iso5426_4E_conv;
46 yaz_conv_func_t yaz_iso5426_51_conv;
47 yaz_conv_func_t yaz_iso5426_33_conv;
48 yaz_conv_func_t yaz_iso5426_34_conv;
49 yaz_conv_func_t yaz_iso5426_53_conv;
50 yaz_conv_func_t yaz_iso5426_31_conv;
51
52
53 static unsigned long yaz_read_iso5426_comb(yaz_iconv_t cd,
54                                          struct decoder_data *data,
55                                          unsigned char *inp,
56                                          size_t inbytesleft, size_t *no_read,
57                                          int *comb);
58
59 static unsigned long read_iso5426(yaz_iconv_t cd, yaz_iconv_decoder_t d,
60                                unsigned char *inp,
61                                size_t inbytesleft, size_t *no_read)
62 {
63     struct decoder_data *data = (struct decoder_data *) d->data;
64     unsigned long x;
65     if (data->comb_offset < data->comb_size)
66     {
67         *no_read = data->comb_no_read[data->comb_offset];
68         x = data->comb_x[data->comb_offset];
69
70         /* special case for double-diacritic combining characters, 
71            INVERTED BREVE and DOUBLE TILDE.
72            We'll increment the no_read counter by 1, since we want to skip over
73            the processing of the closing ligature character
74         */
75         /* this code is no longer necessary.. our handlers code in
76            yaz_iso5426_?_conv (generated by charconv.tcl) now returns
77            0 and no_read=1 when a sequence does not match the input.
78            The SECOND HALFs in codetables.xml produces a non-existant
79            entry in the conversion trie.. Hence when met, the input byte is
80            skipped as it should (in yaz_iconv)
81         */
82 #if 0
83         if (x == 0x0361 || x == 0x0360)
84             *no_read += 1;
85 #endif
86         data->comb_offset++;
87         return x;
88     }
89
90     data->comb_offset = 0;
91     for (data->comb_size = 0; data->comb_size < 8; data->comb_size++)
92     {
93         int comb = 0;
94
95         if (inbytesleft == 0 && data->comb_size)
96         {
97             yaz_iconv_set_errno(cd, YAZ_ICONV_EINVAL);
98             x = 0;
99             *no_read = 0;
100             break;
101         }
102         x = yaz_read_iso5426_comb(cd, data, inp, inbytesleft, no_read, &comb);
103         if (!comb || !x)
104             break;
105         data->comb_x[data->comb_size] = x;
106         data->comb_no_read[data->comb_size] = *no_read;
107         inp += *no_read;
108         inbytesleft = inbytesleft - *no_read;
109     }
110     return x;
111 }
112
113 #if 0
114 /* not used */
115 static unsigned long read_iso5426s(yaz_iconv_t cd, yaz_iconv_decoder_t d,
116                                  unsigned char *inp,
117                                  size_t inbytesleft, size_t *no_read)
118 {
119     struct decoder_data *data = (struct decoder_data *) d->data;
120     unsigned long x = read_iso5426(cd, d, inp, inbytesleft, no_read);
121     if (x && data->comb_size == 1)
122     {
123         if (yaz_iso_8859_1_lookup_x12(x, data->comb_x[0], &x))
124         {
125             *no_read += data->comb_no_read[0];
126             data->comb_size = 0;
127         }
128     }
129     return x;
130 }
131 #endif
132
133 static unsigned long yaz_read_iso5426_comb(yaz_iconv_t cd,
134                                          struct decoder_data *data,
135                                          unsigned char *inp,
136                                          size_t inbytesleft, size_t *no_read,
137                                          int *comb)
138 {
139     *no_read = 0;
140     while (inbytesleft > 0 && *inp == 27)
141     {
142         int *modep = &data->g0_mode;
143         size_t inbytesleft0 = inbytesleft;
144
145         inbytesleft--;
146         inp++;
147         if (inbytesleft == 0)
148             goto incomplete;
149         if (*inp == '$') /* set with multiple bytes */
150         {
151             inbytesleft--;
152             inp++;
153         }
154         if (inbytesleft == 0)
155             goto incomplete;
156         if (*inp == '(' || *inp == ',')  /* G0 */
157         {
158             inbytesleft--;
159             inp++;
160         }
161         else if (*inp == ')' || *inp == '-') /* G1 */
162         {
163             inbytesleft--;
164             inp++;
165             modep = &data->g1_mode;
166         }
167         if (inbytesleft == 0)
168             goto incomplete;
169         if (*inp == '!') /* ANSEL is a special case */
170         {
171             inbytesleft--;
172             inp++;
173         }
174         if (inbytesleft == 0)
175             goto incomplete;
176         *modep = *inp++; /* Final character */
177         inbytesleft--;
178
179         (*no_read) += inbytesleft0 - inbytesleft;
180     }
181     if (inbytesleft == 0)
182         return 0;
183     else if (*inp == ' ')
184     {
185         *no_read += 1;
186         return ' ';
187     }
188     else
189     {
190         unsigned long x;
191         size_t no_read_sub = 0;
192         int mode = *inp < 128 ? data->g0_mode : data->g1_mode;
193         *comb = 0;
194
195         switch(mode)
196         {
197         case 'B':  /* Basic ASCII */
198         case 's':  /* ASCII */
199             x = yaz_iso5426_42_conv(inp, inbytesleft, &no_read_sub, comb, 127, 0);
200             break;
201         case 'E':  /* ANSEL */
202             x = yaz_iso5426_45_conv(inp, inbytesleft, &no_read_sub, comb, 127, 128);
203             break;
204
205 #if 0
206         case 'g':  /* Greek */
207             x = yaz_iso5426_67_conv(inp, inbytesleft, &no_read_sub, comb, 127, 0);
208             break;
209         case 'b':  /* Subscripts */
210             x = yaz_iso5426_62_conv(inp, inbytesleft, &no_read_sub, comb, 127, 0);
211             break;
212         case 'p':  /* Superscripts */
213             x = yaz_iso5426_70_conv(inp, inbytesleft, &no_read_sub, comb, 127, 0);
214             break;
215         case '2':  /* Basic Hebrew */
216             x = yaz_iso5426_32_conv(inp, inbytesleft, &no_read_sub, comb, 127, 0);
217             break;
218         case 'N':  /* Basic Cyrillic */
219             x = yaz_iso5426_4E_conv(inp, inbytesleft, &no_read_sub, comb, 127, 0);
220             break;
221         case 'Q':  /* Extended Cyrillic */
222             x = yaz_iso5426_51_conv(inp, inbytesleft, &no_read_sub, comb, 127, 0);
223             break;
224         case '3':  /* Basic Arabic */
225             x = yaz_iso5426_33_conv(inp, inbytesleft, &no_read_sub, comb, 127, 0);
226             break;
227         case '4':  /* Extended Arabic */
228             x = yaz_iso5426_34_conv(inp, inbytesleft, &no_read_sub, comb, 127, 0);
229             break;
230         case 'S':  /* Greek */
231             x = yaz_iso5426_53_conv(inp, inbytesleft, &no_read_sub, comb, 127, 0);
232             break;
233         case '1':  /* Chinese, Japanese, Korean (EACC) */
234             x = yaz_iso5426_31_conv(inp, inbytesleft, &no_read_sub, comb, 127, 0);
235             break;
236 #endif
237         default:
238             *no_read = 0;
239             yaz_iconv_set_errno(cd, YAZ_ICONV_EILSEQ);
240             return 0;
241         }
242         *no_read += no_read_sub;
243         return x;
244     }
245 incomplete:
246     *no_read = 0;
247     yaz_iconv_set_errno(cd, YAZ_ICONV_EINVAL);
248     return 0;
249 }
250
251
252 static size_t init_iso5426(yaz_iconv_t cd, yaz_iconv_decoder_t d,
253                          unsigned char *inp,
254                          size_t inbytesleft, size_t *no_read)
255 {
256     struct decoder_data *data = (struct decoder_data *) d->data;
257     data->g0_mode = 'B';
258     data->g1_mode = 'E';
259     data->comb_offset = data->comb_size = 0;
260     return 0;
261 }
262
263 void destroy_iso5426(yaz_iconv_decoder_t d)
264 {
265     struct decoder_data *data = (struct decoder_data *) d->data;
266     xfree(data);
267 }
268
269 yaz_iconv_decoder_t yaz_iso5426_decoder(const char *fromcode,
270                                       yaz_iconv_decoder_t d)
271 {
272     if (!yaz_matchstr(fromcode, "ISO5426"))
273         d->read_handle = read_iso5426;
274     else
275         return 0;
276     {
277         struct decoder_data *data = (struct decoder_data *)
278             xmalloc(sizeof(*data));
279         d->data = data;
280         d->init_handle = init_iso5426;
281         d->destroy_handle = destroy_iso5426;
282     }
283     return d;
284 }
285
286
287 /*
288  * Local variables:
289  * c-basic-offset: 4
290  * c-file-style: "Stroustrup"
291  * indent-tabs-mode: nil
292  * End:
293  * vim: shiftwidth=4 tabstop=8 expandtab
294  */
295