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