4d09049197b97c92f3876fe5210c252288b40b29
[yaz-moved-to-github.git] / retrieval / d1_expat.c
1 /*
2  * Copyright (c) 2002, Index Data.
3  * See the file LICENSE for details.
4  *
5  * $Id: d1_expat.c,v 1.6 2002-07-25 12:52:53 adam Exp $
6  */
7
8 #if HAVE_EXPAT_H
9
10 #include <assert.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13
14 #if HAVE_ICONV_H
15 #include <errno.h>
16 #include <iconv.h>
17 #endif
18
19 #include <yaz/xmalloc.h>
20 #include <yaz/log.h>
21 #include <yaz/data1.h>
22
23 #include <expat.h>
24
25 struct user_info {
26     data1_node *d1_stack[256];
27     int level;
28     data1_handle dh;
29     NMEM nmem;
30 };
31
32 static void cb_start (void *user, const char *el, const char **attr)
33 {
34     struct user_info *ui = (struct user_info*) user;
35     if (ui->level == 1)
36         data1_set_root (ui->dh, ui->d1_stack[0], ui->nmem, el);
37     ui->d1_stack[ui->level] = data1_mk_tag (ui->dh, ui->nmem, el, attr,
38                                                 ui->d1_stack[ui->level-1]);
39     ui->level++;
40     yaz_log (LOG_DEBUG, "cb_start %s", el);
41 }
42
43 static void cb_end (void *user, const char *el)
44 {
45     struct user_info *ui = (struct user_info*) user;
46
47     ui->level--;
48     yaz_log (LOG_DEBUG, "cb_end %s", el);
49 }
50
51 static void cb_chardata (void *user, const char *s, int len)
52 {
53     struct user_info *ui = (struct user_info*) user;
54 #if 1
55     yaz_log (LOG_DEBUG, "cb_chardata %.*s", len, s);
56     ui->d1_stack[ui->level] = data1_mk_text_n (ui->dh, ui->nmem, s, len,
57                                                    ui->d1_stack[ui->level -1]);
58 #else
59     int i;
60
61     for (i = 0; i<len; i++)
62         if (!strchr ("\n\n ", s[i]))
63             break;
64     if (i != len)
65     {
66         ui->d1_stack[ui->level] = data1_mk_text_n (ui->dh, ui->nmem, s, len,
67                                                    ui->d1_stack[ui->level -1]);
68     }
69 #endif
70 }
71
72 static void cb_decl (void *user, const char *version, const char*encoding,
73                      int standalone)
74 {
75     struct user_info *ui = (struct user_info*) user;
76     const char *attr_list[7];
77
78     attr_list[0] = "version";
79     attr_list[1] = version;
80
81     attr_list[2] = "encoding";
82     attr_list[3] = "UTF-8"; /* encoding */
83
84     attr_list[4] = "standalone";
85     attr_list[5] = standalone  ? "yes" : "no";
86
87     attr_list[6] = 0;
88     
89     data1_mk_preprocess (ui->dh, ui->nmem, "xml", attr_list,
90                              ui->d1_stack[ui->level-1]);
91     yaz_log (LOG_DEBUG, "decl version=%s encoding=%s",
92              version ? version : "null",
93              encoding ? encoding : "null");
94 }
95     
96 static void cb_processing (void *user, const char *target,
97                            const char *data)
98 {
99     struct user_info *ui = (struct user_info*) user;
100     data1_node *res =
101         data1_mk_preprocess (ui->dh, ui->nmem, target, 0,
102                              ui->d1_stack[ui->level-1]);
103     data1_mk_text_nf (ui->dh, ui->nmem, data, strlen(data), res);
104     
105     yaz_log (LOG_DEBUG, "decl processing target=%s data=%s",
106              target ? target : "null",
107              data ? data : "null");
108     
109     
110 }
111
112 static void cb_comment (void *user, const char *data)
113 {
114     struct user_info *ui = (struct user_info*) user;
115     yaz_log (LOG_DEBUG, "decl comment data=%s", data ? data : "null");
116     data1_mk_comment (ui->dh, ui->nmem, data, ui->d1_stack[ui->level-1]);
117 }
118
119 static void cb_doctype_start (void *userData, const char *doctypeName,
120                               const char *sysid, const char *pubid,
121                               int has_internal_subset)
122 {
123     yaz_log (LOG_DEBUG, "doctype start doctype=%s sysid=%s pubid=%s",
124              doctypeName, sysid, pubid);
125 }
126
127 static void cb_doctype_end (void *userData)
128 {
129     yaz_log (LOG_DEBUG, "doctype end");
130 }
131
132
133 static void cb_entity_decl (void *userData, const char *entityName,
134                             int is_parameter_entity,
135                             const char *value, int value_length,
136                             const char *base, const char *systemId,
137                             const char *publicId, const char *notationName)
138 {
139     yaz_log (LOG_DEBUG,
140              "entity %s is_para_entry=%d value=%.*s base=%s systemId=%s"
141              " publicId=%s notationName=%s",
142              entityName, is_parameter_entity, value_length, value,
143              base, systemId, publicId, notationName);
144     
145 }
146
147 #if HAVE_ICONV_H
148 static int cb_encoding_convert (void *data, const char *s)
149 {
150     iconv_t t = (iconv_t) data;
151     size_t ret;
152     size_t outleft = 2;
153     char outbuf_[2], *outbuf = outbuf_;
154     size_t inleft = 4;
155     char *inbuf = (char *) s;
156     unsigned short code;
157
158     ret = iconv (t, &inbuf, &inleft, &outbuf, &outleft);
159     if (ret == (size_t) (-1) && errno != E2BIG)
160     {
161         iconv (t, 0, 0, 0, 0);
162         return -1;
163     }
164     if (outleft != 0)
165         return -1;
166     memcpy (&code, outbuf_, sizeof(short));
167     return code;
168 }
169
170 static void cb_encoding_release (void *data)
171 {
172     iconv_t t = (iconv_t) data;
173     iconv_close (t);
174 }
175
176 static int cb_encoding_handler (void *userData, const char *name,
177                                 XML_Encoding *info)
178 {
179     int i = 0;
180     int no_ok = 0;
181
182     iconv_t t = iconv_open ("UNICODE", name);
183     if (t == (iconv_t) (-1))
184         return 0;
185    
186     info->data = 0;  /* signal that multibyte is not in use */
187     yaz_log (LOG_DEBUG, "Encoding handler of %s", name);
188     for (i = 0; i<256; i++)
189     {
190         size_t ret;
191         char outbuf_[5];
192         char inbuf_[5];
193         char *inbuf = inbuf_;
194         char *outbuf = outbuf_;
195         size_t inleft = 1;
196         size_t outleft = 2;
197         inbuf_[0] = i;
198
199         iconv (t, 0, 0, 0, 0);  /* reset iconv */
200
201         ret = iconv(t, &inbuf, &inleft, &outbuf, &outleft);
202         if (ret == (size_t) (-1))
203         {
204             if (errno == EILSEQ)
205             {
206                 yaz_log (LOG_DEBUG, "Encoding %d: invalid sequence", i);
207                 info->map[i] = -1;  /* invalid sequence */
208             }
209             if (errno == EINVAL)
210             {                       /* multi byte input */
211                 int len = 2;
212                 int j = 0;
213                 info->map[i] = -1;
214                 
215                 while (len <= 4)
216                 {
217                     char sbuf[80];
218                     int k;
219                     inbuf = inbuf_;
220                     inleft = len;
221                     outbuf = outbuf_;
222                     outleft = 2;
223
224                     inbuf_[len-1] = j;
225                     iconv (t, 0,0,0,0);
226
227                     assert (i >= 0 && i<255);
228
229                     *sbuf = 0;
230                     for (k = 0; k<len; k++)
231                     {
232                         sprintf (sbuf+strlen(sbuf), "%d ", inbuf_[k]&255);
233                     }
234                     ret = iconv (t, &inbuf, &inleft, &outbuf, &outleft);
235                     if (ret == (size_t) (-1))
236                     {
237                         if (errno == EILSEQ || errno == E2BIG)
238                         {
239                             j++;
240                             if (j > 255)
241                                 break;
242                         }
243                         else if (errno == EINVAL)
244                         {
245                             len++;
246                             j = 7;
247                         }
248                     }
249                     else if (outleft == 0)
250                     {
251                         info->map[i] = -len;
252                         info->data = t;  /* signal that multibyte is in use */
253                         break;
254                     }
255                     else
256                     {
257                         break;
258                     }
259                 }
260                 if (info->map[i] < -1)
261                     yaz_log (LOG_DEBUG, "Encoding %d: multibyte input %d",
262                              i, -info->map[i]);
263                 else
264                     yaz_log (LOG_DEBUG, "Encoding %d: multibyte input failed",
265                              i);
266             }
267             if (errno == E2BIG)
268             {
269                 info->map[i] = -1;  /* no room for output */
270                 yaz_log (LOG_WARN, "Encoding %d: no room for output",
271                          i);
272             }
273         }
274         else if (outleft == 0)
275         {
276             unsigned short code;
277             memcpy (&code, outbuf_, sizeof(short));
278             info->map[i] = code;
279             no_ok++;
280         }
281         else
282         {   /* should never happen */
283             info->map[i] = -1;
284             yaz_log (LOG_DEBUG, "Encoding %d: bad state", i);
285         }
286     }
287     if (info->data)
288     {   /* at least one multi byte */
289         info->convert = cb_encoding_convert;
290         info->release = cb_encoding_release;
291     }
292     else
293     {
294         /* no multi byte - we no longer need iconv handler */
295         iconv_close(t);
296         info->convert = 0;
297         info->release = 0;
298     }
299     if (!no_ok)
300         return 0;
301     return 1;
302 }
303
304 #endif
305
306 #define XML_CHUNK 1024
307
308 data1_node *data1_read_xml (data1_handle dh,
309                             int (*rf)(void *, char *, size_t), void *fh,
310                             NMEM m)
311 {
312     XML_Parser parser;
313     struct user_info uinfo;
314     int done = 0;
315
316     uinfo.level = 1;
317     uinfo.dh = dh;
318     uinfo.nmem = m;
319     uinfo.d1_stack[0] = data1_mk_node2 (dh, m, DATA1N_root, 0);
320     uinfo.d1_stack[1] = 0; /* indicate no children (see end of routine) */
321     
322     parser = XML_ParserCreate (0 /* encoding */);
323     
324     XML_SetElementHandler (parser, cb_start, cb_end);
325     XML_SetCharacterDataHandler (parser, cb_chardata);
326     XML_SetXmlDeclHandler (parser, cb_decl);
327     XML_SetProcessingInstructionHandler (parser, cb_processing);
328     XML_SetUserData (parser, &uinfo);
329     XML_SetCommentHandler (parser, cb_comment);
330     XML_SetDoctypeDeclHandler (parser, cb_doctype_start, cb_doctype_end);
331     XML_SetEntityDeclHandler (parser, cb_entity_decl);
332 #if HAVE_ICONV_H
333     XML_SetUnknownEncodingHandler (parser, cb_encoding_handler, 0);
334 #endif
335
336     while (!done)
337     {
338         int r;
339         void *buf = XML_GetBuffer (parser, XML_CHUNK);
340         if (!buf)
341         {
342             /* error */
343             yaz_log (LOG_FATAL, "XML_GetBuffer fail");
344             return 0;
345         }
346         r = (*rf)(fh, buf, XML_CHUNK);
347         if (r < 0)
348         {
349             /* error */
350             yaz_log (LOG_FATAL, "XML read fail");
351             return 0;
352         }
353         else if (r == 0)
354             done = 1;
355         if (!XML_ParseBuffer (parser, r, done))
356         {
357             yaz_log (LOG_FATAL, "XML_ParseBuffer failed %s",
358                      XML_ErrorString(XML_GetErrorCode(parser)));
359         }
360     }
361     XML_ParserFree (parser);
362     if (!uinfo.d1_stack[1])
363         return 0;
364     return uinfo.d1_stack[0];
365 }
366
367 #endif