data1 part of zebra
[idzebra-moved-to-github.git] / recctrl / xmlread.c
1 /* $Id: xmlread.c,v 1.6 2002-10-22 12:51:09 adam Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002
3    Index Data Aps
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 #if HAVE_EXPAT_H
24
25 #include <assert.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #if HAVE_ICONV_H
29 #include <errno.h>
30 #include <iconv.h>
31 #endif
32
33 #include <yaz/log.h>
34
35 #include "grsread.h"
36
37 #include <yaz/xmalloc.h>
38 #include <yaz/log.h>
39 #include <data1.h>
40
41 #include <expat.h>
42
43 #define XML_CHUNK 1024
44
45 struct user_info {
46     data1_node *d1_stack[256];
47     int level;
48     data1_handle dh;
49     NMEM nmem;
50     int loglevel;
51 };
52
53 static void cb_start (void *user, const char *el, const char **attr)
54 {
55     struct user_info *ui = (struct user_info*) user;
56     if (ui->level == 1)
57         data1_set_root (ui->dh, ui->d1_stack[0], ui->nmem, el);
58     ui->d1_stack[ui->level] = data1_mk_tag (ui->dh, ui->nmem, el, attr,
59                                                 ui->d1_stack[ui->level-1]);
60     ui->level++;
61     yaz_log (ui->loglevel, "cb_start %s", el);
62 }
63
64 static void cb_end (void *user, const char *el)
65 {
66     struct user_info *ui = (struct user_info*) user;
67
68     ui->level--;
69     yaz_log (ui->loglevel, "cb_end %s", el);
70 }
71
72 static void cb_chardata (void *user, const char *s, int len)
73 {
74     struct user_info *ui = (struct user_info*) user;
75 #if 0
76     yaz_log (ui->loglevel, "cb_chardata %.*s", len, s);
77 #endif
78     ui->d1_stack[ui->level] = data1_mk_text_n (ui->dh, ui->nmem, s, len,
79                                                    ui->d1_stack[ui->level -1]);
80 }
81
82 static void cb_decl (void *user, const char *version, const char*encoding,
83                      int standalone)
84 {
85     struct user_info *ui = (struct user_info*) user;
86     const char *attr_list[7];
87
88     attr_list[0] = "version";
89     attr_list[1] = version;
90
91     attr_list[2] = "encoding";
92     attr_list[3] = "UTF-8"; /* encoding */
93
94     attr_list[4] = "standalone";
95     attr_list[5] = standalone  ? "yes" : "no";
96
97     attr_list[6] = 0;
98     
99     data1_mk_preprocess (ui->dh, ui->nmem, "xml", attr_list,
100                              ui->d1_stack[ui->level-1]);
101     yaz_log (ui->loglevel, "decl version=%s encoding=%s",
102              version ? version : "null",
103              encoding ? encoding : "null");
104 }
105     
106 static void cb_processing (void *user, const char *target,
107                            const char *data)
108 {
109     struct user_info *ui = (struct user_info*) user;
110     data1_node *res =
111         data1_mk_preprocess (ui->dh, ui->nmem, target, 0,
112                              ui->d1_stack[ui->level-1]);
113     data1_mk_text_nf (ui->dh, ui->nmem, data, strlen(data), res);
114     
115     yaz_log (ui->loglevel, "decl processing target=%s data=%s",
116              target ? target : "null",
117              data ? data : "null");
118     
119     
120 }
121
122 static void cb_comment (void *user, const char *data)
123 {
124     struct user_info *ui = (struct user_info*) user;
125     yaz_log (ui->loglevel, "decl comment data=%s", data ? data : "null");
126     data1_mk_comment (ui->dh, ui->nmem, data, ui->d1_stack[ui->level-1]);
127 }
128
129 static void cb_doctype_start (void *userData, const char *doctypeName,
130                               const char *sysid, const char *pubid,
131                               int has_internal_subset)
132 {
133     struct user_info *ui = (struct user_info*) userData;
134     yaz_log (ui->loglevel, "doctype start doctype=%s sysid=%s pubid=%s",
135              doctypeName, sysid, pubid);
136 }
137
138 static void cb_doctype_end (void *userData)
139 {
140     struct user_info *ui = (struct user_info*) userData;
141     yaz_log (ui->loglevel, "doctype end");
142 }
143
144
145 static void cb_entity_decl (void *userData, const char *entityName,
146                             int is_parameter_entity,
147                             const char *value, int value_length,
148                             const char *base, const char *systemId,
149                             const char *publicId, const char *notationName)
150 {
151     struct user_info *ui = (struct user_info*) userData;
152     yaz_log (ui->loglevel,
153              "entity decl %s is_para_entry=%d value=%.*s base=%s systemId=%s"
154              " publicId=%s notationName=%s",
155              entityName, is_parameter_entity, value_length, value,
156              base, systemId, publicId, notationName);
157     
158 }
159
160 static int cb_external_entity (XML_Parser pparser,
161                                const char *context,
162                                const char *base,
163                                const char *systemId,
164                                const char *publicId)
165 {
166     struct user_info *ui = (struct user_info*) XML_GetUserData(pparser);
167     FILE *inf;
168     int done = 0;
169     XML_Parser parser;
170
171     yaz_log (ui->loglevel,
172              "external entity context=%s base=%s systemid=%s publicid=%s",
173              context, base, systemId, publicId);
174     if (!systemId)
175         return 1;
176
177     if (!(inf = fopen (systemId, "rb")))
178     {
179         yaz_log (LOG_WARN|LOG_ERRNO, "fopen %s", systemId);
180         return 0;
181     }
182
183     parser = XML_ExternalEntityParserCreate (pparser, "", 0);
184     while (!done)
185     {
186         int r;
187         void *buf = XML_GetBuffer (parser, XML_CHUNK);
188         if (!buf)
189         {
190             yaz_log (LOG_WARN, "XML_GetBuffer fail");
191             break;
192         }
193         r = fread (buf, 1, XML_CHUNK, inf);
194         if (r == 0)
195         {
196             if (ferror(inf))
197             {
198                 yaz_log (LOG_WARN|LOG_ERRNO, "fread %s", systemId);
199                 break;
200             }
201             done = 1;
202         }
203         if (!XML_ParseBuffer (parser, r, done))
204         {
205             yaz_log (LOG_WARN, "%s:%d:%d:XML error: %s",
206                      systemId,
207                      XML_GetCurrentLineNumber(parser),
208                      XML_GetCurrentColumnNumber(parser),
209                      XML_ErrorString(XML_GetErrorCode(parser)));
210         }
211     }
212     fclose (inf);
213     XML_ParserFree (parser);
214     return done;
215 }
216
217
218 #if HAVE_ICONV_H
219 static int cb_encoding_convert (void *data, const char *s)
220 {
221     iconv_t t = (iconv_t) data;
222     size_t ret;
223     size_t outleft = 2;
224     char outbuf_[2], *outbuf = outbuf_;
225     size_t inleft = 4;
226     char *inbuf = (char *) s;
227     unsigned short code;
228
229     ret = iconv (t, &inbuf, &inleft, &outbuf, &outleft);
230     if (ret == (size_t) (-1) && errno != E2BIG)
231     {
232         iconv (t, 0, 0, 0, 0);
233         return -1;
234     }
235     if (outleft != 0)
236         return -1;
237     memcpy (&code, outbuf_, sizeof(short));
238     return code;
239 }
240
241 static void cb_encoding_release (void *data)
242 {
243     iconv_t t = (iconv_t) data;
244     iconv_close (t);
245 }
246
247 static int cb_encoding_handler (void *userData, const char *name,
248                                 XML_Encoding *info)
249 {
250     int i = 0;
251     int no_ok = 0;
252     struct user_info *ui = (struct user_info*) userData;
253
254     iconv_t t = iconv_open ("UNICODE", name);
255     if (t == (iconv_t) (-1))
256         return 0;
257    
258     info->data = 0;  /* signal that multibyte is not in use */
259     yaz_log (ui->loglevel, "Encoding handler of %s", name);
260     for (i = 0; i<256; i++)
261     {
262         size_t ret;
263         char outbuf_[5];
264         char inbuf_[5];
265         char *inbuf = inbuf_;
266         char *outbuf = outbuf_;
267         size_t inleft = 1;
268         size_t outleft = 2;
269         inbuf_[0] = i;
270
271         iconv (t, 0, 0, 0, 0);  /* reset iconv */
272
273         ret = iconv(t, &inbuf, &inleft, &outbuf, &outleft);
274         if (ret == (size_t) (-1))
275         {
276             if (errno == EILSEQ)
277             {
278                 yaz_log (ui->loglevel, "Encoding %d: invalid sequence", i);
279                 info->map[i] = -1;  /* invalid sequence */
280             }
281             if (errno == EINVAL)
282             {                       /* multi byte input */
283                 int len = 2;
284                 int j = 0;
285                 info->map[i] = -1;
286                 
287                 while (len <= 4)
288                 {
289                     char sbuf[80];
290                     int k;
291                     inbuf = inbuf_;
292                     inleft = len;
293                     outbuf = outbuf_;
294                     outleft = 2;
295
296                     inbuf_[len-1] = j;
297                     iconv (t, 0,0,0,0);
298
299                     assert (i >= 0 && i<255);
300
301                     *sbuf = 0;
302                     for (k = 0; k<len; k++)
303                     {
304                         sprintf (sbuf+strlen(sbuf), "%d ", inbuf_[k]&255);
305                     }
306                     ret = iconv (t, &inbuf, &inleft, &outbuf, &outleft);
307                     if (ret == (size_t) (-1))
308                     {
309                         if (errno == EILSEQ || errno == E2BIG)
310                         {
311                             j++;
312                             if (j > 255)
313                                 break;
314                         }
315                         else if (errno == EINVAL)
316                         {
317                             len++;
318                             j = 7;
319                         }
320                     }
321                     else if (outleft == 0)
322                     {
323                         info->map[i] = -len;
324                         info->data = t;  /* signal that multibyte is in use */
325                         break;
326                     }
327                     else
328                     {
329                         break;
330                     }
331                 }
332                 if (info->map[i] < -1)
333                     yaz_log (ui->loglevel, "Encoding %d: multibyte input %d",
334                              i, -info->map[i]);
335                 else
336                     yaz_log (ui->loglevel, "Encoding %d: multibyte input failed",
337                              i);
338             }
339             if (errno == E2BIG)
340             {
341                 info->map[i] = -1;  /* no room for output */
342                 yaz_log (LOG_WARN, "Encoding %d: no room for output",
343                          i);
344             }
345         }
346         else if (outleft == 0)
347         {
348             unsigned short code;
349             memcpy (&code, outbuf_, sizeof(short));
350             info->map[i] = code;
351             no_ok++;
352         }
353         else
354         {   /* should never happen */
355             info->map[i] = -1;
356             yaz_log (LOG_DEBUG, "Encoding %d: bad state", i);
357         }
358     }
359     if (info->data)
360     {   /* at least one multi byte */
361         info->convert = cb_encoding_convert;
362         info->release = cb_encoding_release;
363     }
364     else
365     {
366         /* no multi byte - we no longer need iconv handler */
367         iconv_close(t);
368         info->convert = 0;
369         info->release = 0;
370     }
371     if (!no_ok)
372         return 0;
373     return 1;
374 }
375 /* HAVE_ICONV_H */
376 #endif
377
378
379 data1_node *zebra_read_xml (data1_handle dh,
380                             int (*rf)(void *, char *, size_t), void *fh,
381                             NMEM m)
382 {
383     XML_Parser parser;
384     struct user_info uinfo;
385     int done = 0;
386
387     uinfo.loglevel = LOG_DEBUG;
388     uinfo.level = 1;
389     uinfo.dh = dh;
390     uinfo.nmem = m;
391     uinfo.d1_stack[0] = data1_mk_node2 (dh, m, DATA1N_root, 0);
392     uinfo.d1_stack[1] = 0; /* indicate no children (see end of routine) */
393     
394     parser = XML_ParserCreate (0 /* encoding */);
395     
396     XML_SetElementHandler (parser, cb_start, cb_end);
397     XML_SetCharacterDataHandler (parser, cb_chardata);
398     XML_SetXmlDeclHandler (parser, cb_decl);
399     XML_SetProcessingInstructionHandler (parser, cb_processing);
400     XML_SetUserData (parser, &uinfo);
401     XML_SetCommentHandler (parser, cb_comment);
402     XML_SetDoctypeDeclHandler (parser, cb_doctype_start, cb_doctype_end);
403     XML_SetEntityDeclHandler (parser, cb_entity_decl);
404     XML_SetExternalEntityRefHandler (parser, cb_external_entity);
405 #if HAVE_ICONV_H
406     XML_SetUnknownEncodingHandler (parser, cb_encoding_handler, &uinfo);
407 #endif
408     while (!done)
409     {
410         int r;
411         void *buf = XML_GetBuffer (parser, XML_CHUNK);
412         if (!buf)
413         {
414             /* error */
415             yaz_log (LOG_WARN, "XML_GetBuffer fail");
416             break;
417         }
418         r = (*rf)(fh, buf, XML_CHUNK);
419         if (r < 0)
420         {
421             /* error */
422             yaz_log (LOG_WARN, "XML read fail");
423             break;
424         }
425         else if (r == 0)
426             done = 1;
427         if (!XML_ParseBuffer (parser, r, done))
428         {
429             yaz_log (LOG_WARN, "%d:%d:XML error: %s",
430                      XML_GetCurrentLineNumber(parser),
431                      XML_GetCurrentColumnNumber(parser),
432                      XML_ErrorString(XML_GetErrorCode(parser)));
433         }
434     }
435     XML_ParserFree (parser);
436     if (!uinfo.d1_stack[1] || !done)
437         return 0;
438     return uinfo.d1_stack[0];
439 }
440
441 struct xml_info {
442     int dummy;
443 };
444
445 static void *grs_init_xml(void)
446 {
447     struct xml_info *p = (struct xml_info *) xmalloc (sizeof(*p));
448     return p;
449 }
450
451 static data1_node *grs_read_xml (struct grs_read_info *p)
452 {
453     return zebra_read_xml (p->dh, p->readf, p->fh, p->mem);
454 }
455
456 static void grs_destroy_xml(void *clientData)
457 {
458     struct sgml_getc_info *p = (struct sgml_getc_info *) clientData;
459
460     xfree (p);
461 }
462
463 static struct recTypeGrs xml_type = {
464     "xml",
465     grs_init_xml,
466     grs_destroy_xml,
467     grs_read_xml
468 };
469
470 RecTypeGrs recTypeGrs_xml = &xml_type;
471
472 /* HAVE_EXPAT_H */
473 #endif
474