Update copyright year + FSF address
[idzebra-moved-to-github.git] / index / xmlread.c
1 /* $Id: xmlread.c,v 1.2 2006-08-14 10:40:15 adam Exp $
2    Copyright (C) 1995-2006
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 this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
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 <idzebra/recgrs.h>
36
37 #include <yaz/log.h>
38 #include <yaz/xmalloc.h>
39
40 #include <expat.h>
41
42 #define XML_CHUNK 1024
43
44 struct user_info {
45     data1_node *d1_stack[256];
46     int level;
47     data1_handle dh;
48     NMEM nmem;
49     int loglevel;
50 };
51
52 static void cb_start (void *user, const char *el, const char **attr)
53 {
54     struct user_info *ui = (struct user_info*) user;
55     if (ui->level == 1)
56         data1_set_root (ui->dh, ui->d1_stack[0], ui->nmem, el);
57     ui->d1_stack[ui->level] = data1_mk_tag (ui->dh, ui->nmem, el, attr,
58                                                 ui->d1_stack[ui->level-1]);
59     ui->level++;
60     yaz_log (ui->loglevel, "cb_start %s", el);
61 }
62
63 static void cb_end (void *user, const char *el)
64 {
65     struct user_info *ui = (struct user_info*) user;
66
67     ui->level--;
68     yaz_log (ui->loglevel, "cb_end %s", el);
69 }
70
71 static void cb_chardata (void *user, const char *s, int len)
72 {
73     struct user_info *ui = (struct user_info*) user;
74 #if 0
75     yaz_log (ui->loglevel, "cb_chardata %.*s", len, s);
76 #endif
77     ui->d1_stack[ui->level] = data1_mk_text_n (ui->dh, ui->nmem, s, len,
78                                                    ui->d1_stack[ui->level -1]);
79 }
80
81 static void cb_decl (void *user, const char *version, const char *encoding,
82                      int standalone)
83 {
84     struct user_info *ui = (struct user_info*) user;
85     const char *attr_list[7];
86
87     attr_list[0] = "version";
88     attr_list[1] = version;
89
90     attr_list[2] = "encoding";
91     attr_list[3] = "UTF-8"; /* internally it's always UTF-8 */
92
93     attr_list[4] = "standalone";
94     attr_list[5] = standalone  ? "yes" : "no";
95
96     attr_list[6] = 0;
97     
98     data1_mk_preprocess (ui->dh, ui->nmem, "xml", attr_list,
99                              ui->d1_stack[ui->level-1]);
100 #if 0
101     yaz_log (YLOG_LOG, "decl version=%s encoding=%s",
102              version ? version : "null",
103              encoding ? encoding : "null");
104 #endif
105 }
106     
107 static void cb_processing (void *user, const char *target,
108                            const char *data)
109 {
110     struct user_info *ui = (struct user_info*) user;
111     data1_node *res =
112         data1_mk_preprocess (ui->dh, ui->nmem, target, 0,
113                              ui->d1_stack[ui->level-1]);
114     data1_mk_text_nf (ui->dh, ui->nmem, data, strlen(data), res);
115     
116     yaz_log (ui->loglevel, "decl processing target=%s data=%s",
117              target ? target : "null",
118              data ? data : "null");
119 }
120
121 static void cb_comment (void *user, const char *data)
122 {
123     struct user_info *ui = (struct user_info*) user;
124     yaz_log (ui->loglevel, "decl comment data=%s", data ? data : "null");
125     data1_mk_comment (ui->dh, ui->nmem, data, ui->d1_stack[ui->level-1]);
126 }
127
128 static void cb_doctype_start (void *userData, const char *doctypeName,
129                               const char *sysid, const char *pubid,
130                               int has_internal_subset)
131 {
132     struct user_info *ui = (struct user_info*) userData;
133     yaz_log (ui->loglevel, "doctype start doctype=%s sysid=%s pubid=%s",
134              doctypeName, sysid, pubid);
135 }
136
137 static void cb_doctype_end (void *userData)
138 {
139     struct user_info *ui = (struct user_info*) userData;
140     yaz_log (ui->loglevel, "doctype end");
141 }
142
143
144 static void cb_entity_decl (void *userData, const char *entityName,
145                             int is_parameter_entity,
146                             const char *value, int value_length,
147                             const char *base, const char *systemId,
148                             const char *publicId, const char *notationName)
149 {
150     struct user_info *ui = (struct user_info*) userData;
151     yaz_log (ui->loglevel,
152              "entity decl %s is_para_entry=%d value=%.*s base=%s systemId=%s"
153              " publicId=%s notationName=%s",
154              entityName, is_parameter_entity, value_length, value,
155              base, systemId, publicId, notationName);
156     
157 }
158
159 static int cb_external_entity (XML_Parser pparser,
160                                const char *context,
161                                const char *base,
162                                const char *systemId,
163                                const char *publicId)
164 {
165     struct user_info *ui = (struct user_info*) XML_GetUserData(pparser);
166     FILE *inf;
167     int done = 0;
168     XML_Parser parser;
169
170     yaz_log (ui->loglevel,
171              "external entity context=%s base=%s systemid=%s publicid=%s",
172              context, base, systemId, publicId);
173     if (!systemId)
174         return 1;
175
176     if (!(inf = fopen (systemId, "rb")))
177     {
178         yaz_log (YLOG_WARN|YLOG_ERRNO, "fopen %s", systemId);
179         return 0;
180     }
181
182     parser = XML_ExternalEntityParserCreate (pparser, "", 0);
183     while (!done)
184     {
185         int r;
186         void *buf = XML_GetBuffer (parser, XML_CHUNK);
187         if (!buf)
188         {
189             yaz_log (YLOG_WARN, "XML_GetBuffer fail");
190             break;
191         }
192         r = fread (buf, 1, XML_CHUNK, inf);
193         if (r == 0)
194         {
195             if (ferror(inf))
196             {
197                 yaz_log (YLOG_WARN|YLOG_ERRNO, "fread %s", systemId);
198                 break;
199             }
200             done = 1;
201         }
202         if (!XML_ParseBuffer (parser, r, done))
203         {
204             done = 1;
205             yaz_log (YLOG_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 #if 1
230     yaz_log(YLOG_LOG, "------------------------- cb_encoding_convert --- ");
231 #endif
232     ret = iconv (t, &inbuf, &inleft, &outbuf, &outleft);
233     if (ret == (size_t) (-1) && errno != E2BIG)
234     {
235         iconv (t, 0, 0, 0, 0);
236         return -1;
237     }
238     if (outleft != 0)
239         return -1;
240     memcpy (&code, outbuf_, sizeof(short));
241     return code;
242 }
243
244 static void cb_encoding_release (void *data)
245 {
246     iconv_t t = (iconv_t) data;
247     iconv_close (t);
248 }
249
250 static int cb_encoding_handler (void *userData, const char *name,
251                                 XML_Encoding *info)
252 {
253     int i = 0;
254     int no_ok = 0;
255     struct user_info *ui = (struct user_info*) userData;
256
257     iconv_t t = iconv_open ("UNICODE", name);
258     if (t == (iconv_t) (-1))
259         return 0;
260    
261     info->data = 0;  /* signal that multibyte is not in use */
262     yaz_log (ui->loglevel, "Encoding handler of %s", name);
263     for (i = 0; i<256; i++)
264     {
265         size_t ret;
266         char outbuf_[5];
267         char inbuf_[5];
268         char *inbuf = inbuf_;
269         char *outbuf = outbuf_;
270         size_t inleft = 1;
271         size_t outleft = 2;
272         inbuf_[0] = i;
273
274         iconv (t, 0, 0, 0, 0);  /* reset iconv */
275
276         ret = iconv(t, &inbuf, &inleft, &outbuf, &outleft);
277         if (ret == (size_t) (-1))
278         {
279             if (errno == EILSEQ)
280             {
281                 yaz_log (ui->loglevel, "Encoding %d: invalid sequence", i);
282                 info->map[i] = -1;  /* invalid sequence */
283             }
284             if (errno == EINVAL)
285             {                       /* multi byte input */
286                 int len = 2;
287                 int j = 0;
288                 info->map[i] = -1;
289                 
290                 while (len <= 4)
291                 {
292                     char sbuf[80];
293                     int k;
294                     inbuf = inbuf_;
295                     inleft = len;
296                     outbuf = outbuf_;
297                     outleft = 2;
298
299                     inbuf_[len-1] = j;
300                     iconv (t, 0,0,0,0);
301
302                     assert (i >= 0 && i<255);
303
304                     *sbuf = 0;
305                     for (k = 0; k<len; k++)
306                     {
307                         sprintf (sbuf+strlen(sbuf), "%d ", inbuf_[k]&255);
308                     }
309                     ret = iconv (t, &inbuf, &inleft, &outbuf, &outleft);
310                     if (ret == (size_t) (-1))
311                     {
312                         if (errno == EILSEQ || errno == E2BIG)
313                         {
314                             j++;
315                             if (j > 255)
316                                 break;
317                         }
318                         else if (errno == EINVAL)
319                         {
320                             len++;
321                             j = 7;
322                         }
323                     }
324                     else if (outleft == 0)
325                     {
326                         info->map[i] = -len;
327                         info->data = t;  /* signal that multibyte is in use */
328                         break;
329                     }
330                     else
331                     {
332                         break;
333                     }
334                 }
335                 if (info->map[i] < -1)
336                     yaz_log (ui->loglevel, "Encoding %d: multibyte input %d",
337                              i, -info->map[i]);
338                 else
339                     yaz_log (ui->loglevel, "Encoding %d: multibyte input failed",
340                              i);
341             }
342             if (errno == E2BIG)
343             {
344                 info->map[i] = -1;  /* no room for output */
345                 if (i != 0)
346                     yaz_log (YLOG_WARN, "Encoding %d: no room for output",
347                              i);
348             }
349         }
350         else if (outleft == 0)
351         {
352             unsigned short code;
353             memcpy (&code, outbuf_, sizeof(short));
354             info->map[i] = code;
355             no_ok++;
356         }
357         else
358         {   /* should never happen */
359             info->map[i] = -1;
360             yaz_log (YLOG_DEBUG, "Encoding %d: bad state", i);
361         }
362     }
363     if (info->data)
364     {   /* at least one multi byte */
365         info->convert = cb_encoding_convert;
366         info->release = cb_encoding_release;
367     }
368     else
369     {
370         /* no multi byte - we no longer need iconv handler */
371         iconv_close(t);
372         info->convert = 0;
373         info->release = 0;
374     }
375     if (!no_ok)
376         return 0;
377     return 1;
378 }
379 /* HAVE_ICONV_H */
380 #endif
381
382 static void cb_ns_start(void *userData, const char *prefix, const char *uri)
383 {
384     struct user_info *ui = (struct user_info*) userData;
385     if (prefix && uri)
386         yaz_log(ui->loglevel, "cb_ns_start %s %s", prefix, uri);
387 }
388
389 static void cb_ns_end(void *userData, const char *prefix)
390 {
391     struct user_info *ui = (struct user_info*) userData;
392     if (prefix)
393         yaz_log(ui->loglevel, "cb_ns_end %s", prefix);
394 }
395 data1_node *zebra_read_xml (data1_handle dh,
396                             int (*rf)(void *, char *, size_t), void *fh,
397                             NMEM m)
398 {
399     XML_Parser parser;
400     struct user_info uinfo;
401     int done = 0;
402     data1_node *first_node;
403     int no_read = 0;
404
405     uinfo.loglevel = YLOG_DEBUG;
406     uinfo.level = 1;
407     uinfo.dh = dh;
408     uinfo.nmem = m;
409     uinfo.d1_stack[0] = data1_mk_node2 (dh, m, DATA1N_root, 0);
410     uinfo.d1_stack[1] = 0; /* indicate no children (see end of routine) */
411     
412     parser = XML_ParserCreate (0 /* encoding */);
413     
414     XML_SetElementHandler (parser, cb_start, cb_end);
415     XML_SetCharacterDataHandler (parser, cb_chardata);
416     XML_SetXmlDeclHandler (parser, cb_decl);
417     XML_SetProcessingInstructionHandler (parser, cb_processing);
418     XML_SetUserData (parser, &uinfo);
419     XML_SetCommentHandler (parser, cb_comment);
420     XML_SetDoctypeDeclHandler (parser, cb_doctype_start, cb_doctype_end);
421     XML_SetEntityDeclHandler (parser, cb_entity_decl);
422     XML_SetExternalEntityRefHandler (parser, cb_external_entity);
423     XML_SetNamespaceDeclHandler(parser, cb_ns_start, cb_ns_end);
424 #if HAVE_ICONV_H
425     XML_SetUnknownEncodingHandler (parser, cb_encoding_handler, &uinfo);
426 #endif
427     while (!done)
428     {
429         int r;
430         void *buf = XML_GetBuffer (parser, XML_CHUNK);
431         if (!buf)
432         {
433             /* error */
434             yaz_log (YLOG_WARN, "XML_GetBuffer fail");
435             break;
436         }
437         r = (*rf)(fh, buf, XML_CHUNK);
438         if (r < 0)
439         {
440             /* error */
441             yaz_log (YLOG_WARN, "XML read fail");
442             break;
443         }
444         else if (r == 0)
445             done = 1;
446         else
447             no_read += r;
448         if (no_read && !XML_ParseBuffer (parser, r, done))
449         {
450             done = 1;
451             yaz_log (YLOG_WARN, "%d:%d:XML error: %s",
452                      XML_GetCurrentLineNumber(parser),
453                      XML_GetCurrentColumnNumber(parser),
454                      XML_ErrorString(XML_GetErrorCode(parser)));
455         }
456     }
457     XML_ParserFree (parser);
458     if (no_read == 0)
459         return 0;
460     if (!uinfo.d1_stack[1] || !done)
461         return 0;
462     /* insert XML header if not present .. */
463     first_node = uinfo.d1_stack[0]->child;
464     if (first_node->which != DATA1N_preprocess ||
465         strcmp(first_node->u.preprocess.target, "xml"))
466     {
467         const char *attr_list[5];
468
469         attr_list[0] = "version";
470         attr_list[1] = "1.0";
471
472         attr_list[2] = "encoding";
473         attr_list[3] = "UTF-8"; /* encoding */
474
475         attr_list[4] = 0;
476     
477         data1_insert_preprocess (uinfo.dh, uinfo.nmem, "xml", attr_list,
478                                  uinfo.d1_stack[0]);
479     }
480     return uinfo.d1_stack[0];
481 }
482
483 struct xml_info {
484     XML_Expat_Version expat_version;
485 };
486
487 static data1_node *grs_read_xml (struct grs_read_info *p)
488 {
489     return zebra_read_xml (p->dh, p->readf, p->fh, p->mem);
490 }
491
492 static void *filter_init(Res res, RecType recType)
493 {
494     struct xml_info *p = (struct xml_info *) xmalloc (sizeof(*p));
495
496     p->expat_version = XML_ExpatVersionInfo();
497
498     return p;
499 }
500
501 static void filter_destroy(void *clientData)
502 {
503     struct xml_info *p = (struct xml_info *) clientData;
504
505     xfree (p);
506 }
507
508 static int filter_extract(void *clientData, struct recExtractCtrl *ctrl)
509 {
510     return zebra_grs_extract(clientData, ctrl, grs_read_xml);
511 }
512
513 static int filter_retrieve(void *clientData, struct recRetrieveCtrl *ctrl)
514 {
515     return zebra_grs_retrieve(clientData, ctrl, grs_read_xml);
516 }
517
518 static struct recType filter_type = {
519     0,
520     "grs.xml",
521     filter_init,
522     0,
523     filter_destroy,
524     filter_extract,
525     filter_retrieve,
526 };
527
528 RecType
529 #ifdef IDZEBRA_STATIC_GRS_XML
530 idzebra_filter_grs_xml
531 #else
532 idzebra_filter
533 #endif
534
535 [] = {
536     &filter_type,
537     0,
538 };
539     
540 #endif
541
542 /*
543  * Local variables:
544  * c-basic-offset: 4
545  * indent-tabs-mode: nil
546  * End:
547  * vim: shiftwidth=4 tabstop=8 expandtab
548  */
549