6399f58647a9d5f74506d065164147aa8f0f133a
[idzebra-moved-to-github.git] / index / mod_dom.c
1 /* $Id: mod_dom.c,v 1.12 2007-02-15 13:01:00 marc Exp $
2    Copyright (C) 1995-2007
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 #include <stdio.h>
24 #include <assert.h>
25 #include <ctype.h>
26
27 #include <yaz/diagbib1.h>
28 #include <yaz/tpath.h>
29
30 #include <libxml/xmlversion.h>
31 #include <libxml/parser.h>
32 #include <libxml/tree.h>
33 #include <libxml/xmlIO.h>
34 #include <libxml/xmlreader.h>
35 #include <libxslt/transform.h>
36 #include <libxslt/xsltutils.h>
37
38 #if YAZ_HAVE_EXSLT
39 #include <libexslt/exslt.h>
40 #endif
41
42 #include <idzebra/util.h>
43 #include <idzebra/recctrl.h>
44
45
46
47 /* Alvis style indexing */
48 #define ZEBRA_SCHEMA_XSLT_NS "http://indexdata.dk/zebra/xslt/1"
49 static const char *zebra_xslt_ns = ZEBRA_SCHEMA_XSLT_NS;
50
51 /* DOM filter style indexing */
52 #define ZEBRA_DOM_NS "http://indexdata.com/zebra-2.0"
53 static const char *zebra_dom_ns = ZEBRA_DOM_NS;
54
55 /* DOM filter style indexing */
56 #define ZEBRA_PI_NAME "zebra-2.0"
57 static const char *zebra_pi_name = ZEBRA_PI_NAME;
58
59
60
61 struct convert_s {
62     const char *stylesheet;
63     xsltStylesheetPtr stylesheet_xsp;
64     struct convert_s *next;
65 };
66
67 struct filter_extract {
68     const char *name;
69     struct convert_s *convert;
70 };
71
72 struct filter_store {
73     struct convert_s *convert;
74 };
75
76 struct filter_retrieve {
77     const char *name;
78     const char *identifier;
79     struct convert_s *convert;
80     struct filter_retrieve *next;
81 };
82
83 #define DOM_INPUT_XMLREADER 1
84 #define DOM_INPUT_MARC 2
85 struct filter_input {
86     const char *syntax;
87     const char *name;
88     struct convert_s *convert;
89     int type;
90     union {
91         struct {
92             const char *input_charset;
93             yaz_marc_t handle;
94             yaz_iconv_t iconv;
95         } marc;
96         struct {
97             xmlTextReaderPtr reader;
98             int split_level;
99         } xmlreader;
100     } u;
101     struct filter_input *next;
102 };
103   
104 struct filter_info {
105     char *fname;
106     char *full_name;
107     const char *profile_path;
108     ODR odr_record;
109     ODR odr_config;
110     xmlDocPtr doc_config;
111     struct filter_extract *extract;
112     struct filter_retrieve *retrieve_list;
113     struct filter_input *input_list;
114     struct filter_store *store;
115 };
116
117 #define XML_STRCMP(a,b)   strcmp((char*)a, b)
118 #define XML_STRLEN(a) strlen((char*)a)
119
120
121
122
123 static void set_param_str(const char **params, const char *name,
124                           const char *value, ODR odr)
125 {
126     char *quoted = odr_malloc(odr, 3 + strlen(value));
127     sprintf(quoted, "'%s'", value);
128     while (*params)
129         params++;
130     params[0] = name;
131     params[1] = quoted;
132     params[2] = 0;
133 }
134
135 static void set_param_int(const char **params, const char *name,
136                           zint value, ODR odr)
137 {
138     char *quoted = odr_malloc(odr, 30); /* 25 digits enough for 2^64 */
139     while (*params)
140         params++;
141     sprintf(quoted, "'" ZINT_FORMAT "'", value);
142     params[0] = name;
143     params[1] = quoted;
144     params[2] = 0;
145 }
146
147 static void *filter_init(Res res, RecType recType)
148 {
149     struct filter_info *tinfo = (struct filter_info *) xmalloc(sizeof(*tinfo));
150     tinfo->fname = 0;
151     tinfo->full_name = 0;
152     tinfo->profile_path = 0;
153     tinfo->odr_record = odr_createmem(ODR_ENCODE);
154     tinfo->odr_config = odr_createmem(ODR_ENCODE);
155     tinfo->extract = 0;
156     tinfo->retrieve_list = 0;
157     tinfo->input_list = 0;
158     tinfo->store = 0;
159     tinfo->doc_config = 0;
160
161 #if YAZ_HAVE_EXSLT
162     exsltRegisterAll(); 
163 #endif
164
165     return tinfo;
166 }
167
168 static int attr_content(struct _xmlAttr *attr, const char *name,
169                         const char **dst_content)
170 {
171     if (!XML_STRCMP(attr->name, name) && attr->children 
172         && attr->children->type == XML_TEXT_NODE)
173         {
174             *dst_content = (const char *)(attr->children->content);
175             return 1;
176         }
177     return 0;
178 }
179
180 static void destroy_xsp(struct convert_s *c)
181 {
182     while(c)
183         {
184             if (c->stylesheet_xsp)
185                 xsltFreeStylesheet(c->stylesheet_xsp);
186             c = c->next;
187         }
188 }
189
190 static void destroy_dom(struct filter_info *tinfo)
191 {
192     if (tinfo->extract)
193         {
194             destroy_xsp(tinfo->extract->convert);
195             tinfo->extract = 0;
196         }
197     if (tinfo->store)
198         {
199             destroy_xsp(tinfo->store->convert);
200             tinfo->store = 0;
201         }
202     if (tinfo->input_list)
203         {
204             struct filter_input *i_ptr;
205             for (i_ptr = tinfo->input_list; i_ptr; i_ptr = i_ptr->next)
206                 {
207                     switch(i_ptr->type)
208                         {
209                         case DOM_INPUT_XMLREADER:
210                             if (i_ptr->u.xmlreader.reader)
211                                 xmlFreeTextReader(i_ptr->u.xmlreader.reader);
212                             break;
213                         case DOM_INPUT_MARC:
214                             yaz_iconv_close(i_ptr->u.marc.iconv);
215                             yaz_marc_destroy(i_ptr->u.marc.handle);
216                             break;
217                         }
218                     destroy_xsp(i_ptr->convert);
219                 }
220             tinfo->input_list = 0;
221         }
222     if (tinfo->retrieve_list)
223         {
224             struct filter_retrieve *r_ptr;
225             for (r_ptr = tinfo->retrieve_list; r_ptr; r_ptr = r_ptr->next)
226                 destroy_xsp(r_ptr->convert);
227             tinfo->retrieve_list = 0;
228         }
229
230     if (tinfo->doc_config)
231         {
232             xmlFreeDoc(tinfo->doc_config);
233             tinfo->doc_config = 0;
234         }
235     odr_reset(tinfo->odr_config);
236 }
237
238 static ZEBRA_RES parse_convert(struct filter_info *tinfo, xmlNodePtr ptr,
239                                struct convert_s **l)
240 {
241     *l = 0;
242     for(; ptr; ptr = ptr->next)
243         {
244             if (ptr->type != XML_ELEMENT_NODE)
245                 continue;
246             if (!XML_STRCMP(ptr->name, "xslt"))
247                 {
248                     struct _xmlAttr *attr;
249                     struct convert_s *p 
250                         = odr_malloc(tinfo->odr_config, sizeof(*p));
251
252                     p->next = 0;
253                     p->stylesheet = 0;
254                     p->stylesheet_xsp = 0;
255
256                     for (attr = ptr->properties; attr; attr = attr->next)
257                         if (attr_content(attr, "stylesheet", &p->stylesheet))
258                             ;
259                         else
260                             yaz_log(YLOG_WARN, "%s: dom filter: "
261                                     "bad attribute %s"
262                                     " for <xslt>",
263                                     tinfo->fname, attr->name);
264                     if (p->stylesheet)
265                         {
266                             char tmp_xslt_full_name[1024];
267                             if (!yaz_filepath_resolve(p->stylesheet, 
268                                                       tinfo->profile_path,
269                                                       NULL, 
270                                                       tmp_xslt_full_name))
271                                 {
272                                     yaz_log(YLOG_WARN,
273                                             "%s: dom filter: "
274                                             "stylesheet %s not found in "
275                                             "path %s",
276                                             tinfo->fname,
277                                             p->stylesheet, 
278                                             tinfo->profile_path);
279                                     return ZEBRA_FAIL;
280                                 }
281                 
282                             p->stylesheet_xsp
283                                 = xsltParseStylesheetFile((const xmlChar*) 
284                                                           tmp_xslt_full_name);
285                             if (!p->stylesheet_xsp)
286                                 {
287                                     yaz_log(YLOG_WARN,
288                                             "%s: dom filter: "
289                                             "could not parse xslt "
290                                             "stylesheet %s",
291                                             tinfo->fname, tmp_xslt_full_name);
292                                     return ZEBRA_FAIL;
293                                 }
294                         }
295                     else
296                         {
297                             yaz_log(YLOG_WARN,
298                                     "%s: dom filter: "
299                                     "missing attribute 'stylesheet' "
300                                     "for element 'xslt'", tinfo->fname);
301                             return ZEBRA_FAIL;
302                         }
303                     *l = p;
304                     l = &p->next;
305                 }
306             else
307                 {
308                     yaz_log(YLOG_LOG, 
309                             "%s: dom filter: bad node '%s' for <conv>",
310                             tinfo->fname, ptr->name);
311                     return ZEBRA_FAIL;
312                 }
313         
314         }
315     return ZEBRA_OK;
316 }
317
318 static ZEBRA_RES perform_convert(struct filter_info *tinfo, 
319                                  struct convert_s *convert,
320                                  const char **params,
321                                  xmlDocPtr *doc,
322                                  xsltStylesheetPtr *last_xsp)
323 {
324     for (; convert; convert = convert->next)
325         {
326             xmlDocPtr res_doc = xsltApplyStylesheet(convert->stylesheet_xsp,
327                                                     *doc, params);
328             if (last_xsp)
329                 *last_xsp = convert->stylesheet_xsp;
330             xmlFreeDoc(*doc);
331             *doc = res_doc;
332         }
333     return ZEBRA_OK;
334 }
335
336 static struct filter_input *new_input(struct filter_info *tinfo, int type)
337 {
338     struct filter_input *p;
339     struct filter_input **np = &tinfo->input_list;
340     for (;*np; np = &(*np)->next)
341         ;
342     p = *np = odr_malloc(tinfo->odr_config, sizeof(*p));
343     p->next = 0;
344     p->syntax = 0;
345     p->name = 0;
346     p->convert = 0;
347     p->type = type;
348     return p;
349 }
350
351 static ZEBRA_RES parse_input(struct filter_info *tinfo, xmlNodePtr ptr,
352                              const char *syntax,
353                              const char *name)
354 {
355     for (; ptr; ptr = ptr->next)
356         {
357             if (ptr->type != XML_ELEMENT_NODE)
358                 continue;
359             if (!XML_STRCMP(ptr->name, "marc"))
360                 {
361                     yaz_iconv_t iconv = 0;
362                     const char *input_charset = "marc-8";
363                     struct _xmlAttr *attr;
364             
365                     for (attr = ptr->properties; attr; attr = attr->next)
366                         {
367                             if (attr_content(attr, "charset", &input_charset))
368                                 ;
369                             else
370                                 yaz_log(YLOG_WARN, 
371                                         "%s: dom filter: bad attribute %s"
372                                         " for <marc>",
373                                         tinfo->fname, attr->name);
374                         }
375                     iconv = yaz_iconv_open("utf-8", input_charset);
376                     if (!iconv)
377                         {
378                             yaz_log(YLOG_WARN, 
379                                     "%s: dom filter: unsupported charset "
380                                     "'%s' for <marc>", 
381                                     tinfo->fname,  input_charset);
382                             return ZEBRA_FAIL;
383                         }
384                     else
385                         {
386                             struct filter_input *p 
387                                 = new_input(tinfo, DOM_INPUT_MARC);
388                             p->u.marc.handle = yaz_marc_create();
389                             p->u.marc.iconv = iconv;
390                 
391                             yaz_marc_iconv(p->u.marc.handle, p->u.marc.iconv);
392                 
393                             ptr = ptr->next;
394                 
395                             parse_convert(tinfo, ptr, &p->convert);
396                         }
397                     break;
398
399                 }
400             else if (!XML_STRCMP(ptr->name, "xmlreader"))
401                 {
402                     struct filter_input *p 
403                         = new_input(tinfo, DOM_INPUT_XMLREADER);
404                     struct _xmlAttr *attr;
405                     const char *level_str = 0;
406
407                     p->u.xmlreader.split_level = 0;
408                     p->u.xmlreader.reader = 0;
409
410                     for (attr = ptr->properties; attr; attr = attr->next)
411                         {
412                             if (attr_content(attr, "level", &level_str))
413                                 ;
414                             else
415                                 yaz_log(YLOG_WARN, 
416                                         "%s: dom filter: bad attribute %s"
417                                         " for <xmlreader>",
418                                         tinfo->fname, attr->name);
419                         }
420                     if (level_str)
421                         p->u.xmlreader.split_level = atoi(level_str);
422                 
423                     ptr = ptr->next;
424
425                     parse_convert(tinfo, ptr, &p->convert);
426                     break;
427                 }
428             else
429                 {
430                     yaz_log(YLOG_WARN, "%s: dom filter: bad input type %s",
431                             tinfo->fname, ptr->name);
432                     return ZEBRA_FAIL;
433                 }
434         }
435     return ZEBRA_OK;
436 }
437
438 static ZEBRA_RES parse_dom(struct filter_info *tinfo, const char *fname)
439 {
440     char tmp_full_name[1024];
441     xmlNodePtr ptr;
442     xmlDocPtr doc;
443
444     tinfo->fname = odr_strdup(tinfo->odr_config, fname);
445     
446     if (yaz_filepath_resolve(tinfo->fname, tinfo->profile_path, 
447                              NULL, tmp_full_name))
448         tinfo->full_name = odr_strdup(tinfo->odr_config, tmp_full_name);
449     else
450         tinfo->full_name = odr_strdup(tinfo->odr_config, tinfo->fname);
451     
452     yaz_log(YLOG_LOG, "dom filter: loading config file %s", tinfo->full_name);
453     
454     doc = xmlParseFile(tinfo->full_name);
455     if (!doc)
456         {
457             yaz_log(YLOG_WARN, 
458                     "%s: dom filter: failed to parse config file %s",
459                     tinfo->fname, tinfo->full_name);
460             return ZEBRA_FAIL;
461         }
462     /* save because we store ptrs to the content */ 
463     tinfo->doc_config = doc;
464     
465     ptr = xmlDocGetRootElement(doc);
466     if (!ptr || ptr->type != XML_ELEMENT_NODE 
467         || XML_STRCMP(ptr->name, "dom"))
468         {
469             yaz_log(YLOG_WARN, 
470                     "%s: dom filter: expected root element <dom>", 
471                     tinfo->fname);  
472             return ZEBRA_FAIL;
473         }
474
475     for (ptr = ptr->children; ptr; ptr = ptr->next)
476         {
477             if (ptr->type != XML_ELEMENT_NODE)
478                 continue;
479             if (!XML_STRCMP(ptr->name, "extract"))
480                 {
481                     /*
482                       <extract name="index">
483                       <xslt stylesheet="first.xsl"/>
484                       <xslt stylesheet="second.xsl"/>
485                       </extract>
486                     */
487                     struct _xmlAttr *attr;
488                     struct filter_extract *f =
489                         odr_malloc(tinfo->odr_config, sizeof(*f));
490             
491                     tinfo->extract = f;
492                     f->name = 0;
493                     f->convert = 0;
494                     for (attr = ptr->properties; attr; attr = attr->next)
495                         {
496                             if (attr_content(attr, "name", &f->name))
497                                 ;
498                             else
499                                 yaz_log(YLOG_WARN, 
500                                         "%s: dom filter: bad attribute %s"
501                                         " for <extract>",
502                                         tinfo->fname, attr->name);
503
504                         }
505                     parse_convert(tinfo, ptr->children, &f->convert);
506                 }
507             else if (!XML_STRCMP(ptr->name, "retrieve"))
508                 {  
509                     /* 
510                        <retrieve name="F">
511                        <xslt stylesheet="some.xsl"/>
512                        <xslt stylesheet="some.xsl"/>
513                        </retrieve>
514                     */
515                     struct _xmlAttr *attr;
516                     struct filter_retrieve **fp = &tinfo->retrieve_list;
517                     struct filter_retrieve *f =
518                         odr_malloc(tinfo->odr_config, sizeof(*f));
519             
520                     while (*fp)
521                         fp = &(*fp)->next;
522
523                     *fp = f;
524                     f->name = 0;
525                     f->identifier = 0;
526                     f->convert = 0;
527                     f->next = 0;
528
529                     for (attr = ptr->properties; attr; attr = attr->next)
530                         {
531                             if (attr_content(attr, "identifier", 
532                                              &f->identifier))
533                                 ;
534                             else if (attr_content(attr, "name", &f->name))
535                                 ;
536                             else
537                                 yaz_log(YLOG_WARN, 
538                                         "%s: dom filter: bad attribute %s"
539                                         " for <retrieve>",
540                                         tinfo->fname, attr->name);
541                         }
542                     parse_convert(tinfo, ptr->children, &f->convert);
543                 }
544             else if (!XML_STRCMP(ptr->name, "store"))
545                 {
546                     /*
547                       <retrieve name="F">
548                       <xslt stylesheet="some.xsl"/>
549                       <xslt stylesheet="some.xsl"/>
550                       </retrieve>
551                     */
552                     struct filter_store *f =
553                         odr_malloc(tinfo->odr_config, sizeof(*f));
554             
555                     tinfo->store = f;
556                     f->convert = 0;
557                     parse_convert(tinfo, ptr->children, &f->convert);
558                 }
559             else if (!XML_STRCMP(ptr->name, "input"))
560                 {
561                     /*
562                       <input syntax="xml">
563                       <xmlreader level="1"/>
564                       </input>
565                       <input syntax="usmarc">
566                       <marc inputcharset="marc-8"/>
567                       </input>
568                     */
569                     struct _xmlAttr *attr;
570                     const char  *syntax = 0;
571                     const char *name = 0;
572                     for (attr = ptr->properties; attr; attr = attr->next)
573                         {
574                             if (attr_content(attr, "syntax", &syntax))
575                                 ;
576                             else if (attr_content(attr, "name", &name))
577                                 ;
578                             else
579                                 yaz_log(YLOG_WARN, 
580                                         "%s: dom filter: bad attribute %s"
581                                         " for <input>",
582                                         tinfo->fname, attr->name);
583                         }
584                     parse_input(tinfo, ptr->children, syntax, name);
585                 }
586             else
587                 {
588                     yaz_log(YLOG_WARN, "%s: dom filter: bad element %s",
589                             tinfo->fname, ptr->name);
590                     return ZEBRA_FAIL;
591                 }
592         }
593     return ZEBRA_OK;
594 }
595
596 static struct filter_retrieve *lookup_retrieve(struct filter_info *tinfo,
597                                                const char *est)
598 {
599     struct filter_retrieve *f = tinfo->retrieve_list;
600
601     /* return first schema if no est is provided */
602     if (!est)
603         return f;
604     for (; f; f = f->next)
605         { 
606             /* find requested schema */
607             if (est) 
608                 {    
609                     if (f->identifier && !strcmp(f->identifier, est))
610                         return f;
611                     if (f->name && !strcmp(f->name, est))
612                         return f;
613                 } 
614         }
615     return 0;
616 }
617
618 static ZEBRA_RES filter_config(void *clientData, Res res, const char *args)
619 {
620     struct filter_info *tinfo = clientData;
621     if (!args || !*args)
622         {
623             yaz_log(YLOG_WARN, "dom filter: need config file");
624             return ZEBRA_FAIL;
625         }
626
627     if (tinfo->fname && !strcmp(args, tinfo->fname))
628         return ZEBRA_OK;
629     
630     tinfo->profile_path = res_get(res, "profilePath");
631
632     destroy_dom(tinfo);
633     return parse_dom(tinfo, args);
634 }
635
636 static void filter_destroy(void *clientData)
637 {
638     struct filter_info *tinfo = clientData;
639     destroy_dom(tinfo);
640     odr_destroy(tinfo->odr_config);
641     odr_destroy(tinfo->odr_record);
642     xfree(tinfo);
643 }
644
645 static int ioread_ex(void *context, char *buffer, int len)
646 {
647     struct recExtractCtrl *p = context;
648     return p->stream->readf(p->stream, buffer, len);
649 }
650
651 static int ioclose_ex(void *context)
652 {
653     return 0;
654 }
655
656
657
658 /* Alvis style indexing */
659 static void index_cdata(struct filter_info *tinfo, struct recExtractCtrl *ctrl,
660                         xmlNodePtr ptr, RecWord *recWord)
661 {
662     for(; ptr; ptr = ptr->next)
663         {
664             index_cdata(tinfo, ctrl, ptr->children, recWord);
665             if (ptr->type != XML_TEXT_NODE)
666                 continue;
667             recWord->term_buf = (const char *)ptr->content;
668             recWord->term_len = XML_STRLEN(ptr->content);
669             (*ctrl->tokenAdd)(recWord);
670         }
671 }
672
673 /* Alvis style indexing */
674 static void index_node(struct filter_info *tinfo,  struct recExtractCtrl *ctrl,
675                        xmlNodePtr ptr, RecWord *recWord)
676 {
677     for(; ptr; ptr = ptr->next)
678         {
679             index_node(tinfo, ctrl, ptr->children, recWord);
680             if (ptr->type != XML_ELEMENT_NODE || !ptr->ns ||
681                 XML_STRCMP(ptr->ns->href, zebra_xslt_ns))
682                 continue;
683             if (!XML_STRCMP(ptr->name, "index"))
684                 {
685                     const char *name_str = 0;
686                     const char *type_str = 0;
687                     const char *xpath_str = 0;
688                     struct _xmlAttr *attr;
689                     for (attr = ptr->properties; attr; attr = attr->next)
690                         {
691                             if (attr_content(attr, "name", &name_str))
692                                 ;
693                             else if (attr_content(attr, "xpath", &xpath_str))
694                                 ;
695                             else if (attr_content(attr, "type", &type_str))
696                                 ;
697                             else
698                                 yaz_log(YLOG_WARN, 
699                                         "%s: dom filter: bad attribute %s"
700                                         " for <index>",
701                                         tinfo->fname, attr->name);
702                         }
703                     if (name_str)
704                         {
705                             /* save default type */
706                             int prev_type = recWord->index_type; 
707
708                             /* type was given */
709                             if (type_str && *type_str)
710                                 recWord->index_type = *type_str; 
711
712                             recWord->index_name = name_str;
713                             index_cdata(tinfo, ctrl, ptr->children, recWord);
714
715                             /* restore it again */
716                             recWord->index_type = prev_type;     
717                         }
718                 }
719         }
720 }
721
722 /* Alvis style indexing */
723 static void index_record(struct filter_info *tinfo,struct recExtractCtrl *ctrl,
724                          xmlNodePtr ptr, RecWord *recWord)
725 {
726     const char *type_str = "update";
727
728     if (ptr && ptr->type == XML_ELEMENT_NODE && ptr->ns &&
729         !XML_STRCMP(ptr->ns->href, zebra_xslt_ns)
730         && !XML_STRCMP(ptr->name, "record"))
731         {
732             const char *id_str = 0;
733             const char *rank_str = 0;
734             struct _xmlAttr *attr;
735             for (attr = ptr->properties; attr; attr = attr->next)
736                 {
737                     if (attr_content(attr, "type", &type_str))
738                         ;
739                     else if (attr_content(attr, "id", &id_str))
740                         ;
741                     else if (attr_content(attr, "rank", &rank_str))
742                         ;
743                     else
744                         yaz_log(YLOG_WARN, "%s: dom filter: bad attribute %s"
745                                 " for <record>",
746                                 tinfo->fname, attr->name);
747                 }
748             if (id_str)
749                 sscanf(id_str, "%255s", ctrl->match_criteria);
750
751             if (rank_str)
752                 ctrl->staticrank = atozint(rank_str);
753             ptr = ptr->children;
754         }
755
756     if (!strcmp("update", type_str))
757         index_node(tinfo, ctrl, ptr, recWord);
758     else if (!strcmp("delete", type_str))
759         yaz_log(YLOG_WARN, "dom filter delete: to be implemented");
760     else
761         yaz_log(YLOG_WARN, "dom filter: unknown record type '%s'", 
762                 type_str);
763 }
764
765
766 /* Alvis style indexing */
767 static void extract_doc_alvis(struct filter_info *tinfo, 
768                               struct recExtractCtrl *extctr, 
769                               xmlDocPtr doc)
770 {
771     if (doc){
772         RecWord recWord;
773         xmlChar *buf_out;
774         int len_out;
775         xmlNodePtr root_ptr;
776
777         (*extctr->init)(extctr, &recWord);
778         
779         if (extctr->flagShowRecords){
780             xmlDocDumpMemory(doc, &buf_out, &len_out);
781             fwrite(buf_out, len_out, 1, stdout);
782             xmlFree(buf_out);
783         }
784         root_ptr = xmlDocGetRootElement(doc);
785         if (root_ptr)
786             index_record(tinfo, extctr, root_ptr, &recWord);
787         else
788             yaz_log(YLOG_WARN, "No root for index XML record");
789     }
790 }
791
792
793 /* DOM filter style indexing */
794 static int attr_content_xml(struct _xmlAttr *attr, const char *name,
795                             xmlChar **dst_content)
796 {
797     if (0 == XML_STRCMP(attr->name, name) && attr->children 
798         && attr->children->type == XML_TEXT_NODE)
799         {
800             *dst_content = (attr->children->content);
801             return 1;
802         }
803     return 0;
804 }
805
806
807 /* DOM filter style indexing */
808 static void index_value_of(struct filter_info *tinfo, 
809                            struct recExtractCtrl *extctr, 
810                            xmlNodePtr node, 
811                            xmlChar * index_p)
812 {
813     xmlChar *text = xmlNodeGetContent(node);
814     size_t text_len = strlen((const char *)text);
815
816
817     /* if there is no text, we do not need to proceed */
818     if (text_len)
819         {            
820             xmlChar *look = index_p;
821             xmlChar *bval;
822             xmlChar *eval;
823
824             xmlChar index[256];
825             xmlChar type[256];
826
827             /* assingning text to be indexed */
828             RecWord recWord;
829             (*extctr->init)(extctr, &recWord);
830             recWord.term_buf = (const char *)text;
831             recWord.term_len = text_len;
832
833             /* parsing all index name/type pairs */
834             /* may not start with ' ' or ':' */
835             while (*look && ' ' != *look && ':' != *look){
836     
837                 /* setting name and type to zero */
838                 *index = '\0';
839                 *type = '\0';
840     
841                 /* parsing one index name */
842                 bval = look;
843                 while (*look && ':' != *look && ' ' != *look){
844                     look++;
845                 }
846                 eval = look;
847                 strncpy((char *)index, (const char *)bval, eval - bval);
848                 index[eval - bval] = '\0';
849     
850     
851                 /* parsing one index type, if existing */
852                 if (':' == *look){
853                     look++;
854       
855                     bval = look;
856                     while (*look && ' ' != *look){
857                         look++;
858                     }
859                     eval = look;
860                     strncpy((char *)type, (const char *)bval, eval - bval);
861                     type[eval - bval] = '\0';
862                 }
863
864                 /* actually indexing the text given */
865                 /* printf("INDEX  '%s:%s' '%s'\n", index, type, text); */
866
867                 recWord.index_name = (const char *)index;
868                 if (type && *type)
869                     recWord.index_type = *type;
870                 (extctr->tokenAdd)(&recWord);
871
872                 /* eat whitespaces */
873                 if (*look && ' ' == *look && *(look+1)){
874                     look++;
875                 } 
876             }
877         }
878     
879     xmlFree(text); 
880 }
881
882
883 /* DOM filter style indexing */
884 static void set_record_info(struct filter_info *tinfo, 
885                             struct recExtractCtrl *extctr, 
886                             xmlChar * id_p, 
887                             xmlChar * rank_p, 
888                             xmlChar * type_p)
889 {
890     printf("RECORD id=%s rank=%s type=%s\n", id_p, rank_p, type_p);
891     
892     if (id_p)
893         sscanf((const char *)id_p, "%255s", extctr->match_criteria);
894
895     if (rank_p)
896         extctr->staticrank = atozint((const char *)rank_p);
897
898     /*     if (!strcmp("update", type_str)) */
899     /*         index_node(tinfo, ctrl, ptr, recWord); */
900     /*     else if (!strcmp("delete", type_str)) */
901     /*         yaz_log(YLOG_WARN, "dom filter delete: to be implemented"); */
902     /*     else */
903     /*         yaz_log(YLOG_WARN, "dom filter: unknown record type '%s'",  */
904     /*                 type_str); */
905
906 }
907
908
909 /* DOM filter style indexing */
910 static void process_xml_element_zebra_node(struct filter_info *tinfo, 
911                                            struct recExtractCtrl *extctr, 
912                                            xmlNodePtr node)
913 {
914     if (node->type == XML_ELEMENT_NODE 
915         && node->ns && 0 == XML_STRCMP(node->ns->href, zebra_dom_ns)){
916     
917         if (0 == XML_STRCMP(node->name, "index")){
918             xmlChar *index_p = 0;
919
920             struct _xmlAttr *attr;      
921             for (attr = node->properties; attr; attr = attr->next){
922                 if (attr_content_xml(attr, "name", &index_p)){
923                     index_value_of(tinfo, extctr, node, index_p);        
924                 }  
925                 else
926                     // printf("%s: dom filter: s% bad attribute %s",
927                     // tinfo->fname, xmlGetNodePath(node)), nodeattr->name);
928                     printf("dom filter: %s bad attribute @%s, "
929                            "expected @name\n",
930                            xmlGetNodePath(node), attr->name);
931             }
932         }
933         else if (0 == XML_STRCMP(node->name, "record")){
934             xmlChar *id_p = 0;
935             xmlChar *rank_p = 0;
936             xmlChar *type_p = 0;
937
938             struct _xmlAttr *attr;
939             for (attr = node->properties; attr; attr = attr->next){
940                 if (attr_content_xml(attr, "id", &id_p))
941                     ;
942                 else if (attr_content_xml(attr, "rank", &rank_p))
943                     ;
944                 else if (attr_content_xml(attr, "type", &type_p))
945                     ;
946                 else
947                     // printf("%s: dom filter: s% bad attribute %s",
948                     // tinfo->fname, xmlGetNodePath(node)), nodeattr->name);
949                     printf("dom filter: %s bad attribute @%s,"
950                            " expected @id|@rank|@type\n",
951                            xmlGetNodePath(node), attr->name);
952
953                 if (type_p && 0 != strcmp("update", (const char *)type_p))
954                     printf("dom filter: %s attribute @%s,"
955                            " only implemented '@type=\"update\"\n",
956                            xmlGetNodePath(node), attr->name);
957           
958
959             }
960             set_record_info(tinfo, extctr, id_p, rank_p, type_p);
961         } else {
962             //  printf("%s: dom filter: s% bad attribute %s",
963             //  tinfo->fname, xmlGetNodePath(node)), nodeattr->name);
964             printf("dom filter: %s bad element <%s>,"
965                    " expected <record>|<index> in namespace '%s'\n",
966                    xmlGetNodePath(node), node->name, zebra_dom_ns);
967       
968         }
969     }
970 }
971
972
973 /* DOM filter style indexing */
974 static void process_xml_pi_node(struct filter_info *tinfo, 
975                                 struct recExtractCtrl *extctr, 
976                                 xmlNodePtr node,
977                                 xmlChar **index_pp)
978 {
979
980     /* printf("PI     %s\n", xmlGetNodePath(node)); */
981
982     /* if right PI name, continue parsing PI */
983     if (0 == strcmp(zebra_pi_name, (const char *)node->name)){
984         xmlChar *pi_p =  node->content;
985         xmlChar *look = pi_p;
986     
987         xmlChar *bval;
988         xmlChar *eval;
989
990         /* parsing PI record instructions */
991         if (0 == strncmp((const char *)look, "record", 6)){
992             xmlChar id[256];
993             xmlChar rank[256];
994             xmlChar type[256];
995
996             *id = '\0';
997             *rank = '\0';
998             *type = '\0';
999       
1000             look += 6;
1001       
1002             /* eat whitespace */
1003             while (*look && ' ' == *look && *(look+1))
1004                 look++;
1005
1006             /* parse possible id */
1007             if (*look && 0 == strncmp((const char *)look, "id=", 3)){
1008                 look += 3;
1009                 bval = look;
1010                 while (*look && ' ' != *look)
1011                     look++;
1012                 eval = look;
1013                 strncpy((char *)id, (const char *)bval, eval - bval);
1014                 id[eval - bval] = '\0';
1015             }
1016       
1017             /* eat whitespace */
1018             while (*look && ' ' == *look && *(look+1))
1019                 look++;
1020       
1021             /* parse possible rank */
1022             if (*look && 0 == strncmp((const char *)look, "rank=", 5)){
1023                 look += 6;
1024                 bval = look;
1025                 while (*look && ' ' != *look)
1026                     look++;
1027                 eval = look;
1028                 strncpy((char *)rank, (const char *)bval, eval - bval);
1029                 rank[eval - bval] = '\0';
1030             }
1031
1032             /* eat whitespace */
1033             while (*look && ' ' == *look && *(look+1))
1034                 look++;
1035
1036             if (look && '\0' != *look){
1037                 printf ("ERROR %s: content '%s'; can not parse '%s'\n", 
1038                         xmlGetNodePath(node), pi_p, look);
1039             } else {
1040                 /* set_record_info(id, rank, type); */
1041                 set_record_info(tinfo, extctr, id, rank, 0);
1042             }
1043
1044         } 
1045    
1046         /* parsing index instruction */
1047         else   if (0 == strncmp((const char *)look, "index", 5)){
1048             look += 5;
1049       
1050             /* eat whitespace */
1051             while (*look && ' ' == *look && *(look+1))
1052                 look++;
1053
1054             /* export index instructions to outside */
1055             *index_pp = look;
1056
1057             /* nor record, neither index */ 
1058         } else {
1059     
1060             printf ("ERROR %s: content '%s'; can not parse '%s'\n", 
1061                     xmlGetNodePath(node), pi_p, look);
1062         }  
1063     }
1064 }
1065
1066 /* DOM filter style indexing */
1067 static void process_xml_element_node(struct filter_info *tinfo, 
1068                                      struct recExtractCtrl *extctr, 
1069                                      xmlNodePtr node)
1070 {
1071     /* remember indexing instruction from PI to next element node */
1072     xmlChar *index_p = 0;
1073
1074     /* printf("ELEM   %s\n", xmlGetNodePath(node)); */
1075
1076     /* check if we are an element node in the special zebra namespace 
1077        and either set record data or index value-of node content*/
1078     process_xml_element_zebra_node(tinfo, extctr, node);
1079   
1080     /* loop through kid nodes */
1081     for (node = node->children; node; node = node->next)
1082         {
1083             /* check and set PI record and index index instructions */
1084             if (node->type == XML_PI_NODE){
1085                 process_xml_pi_node(tinfo, extctr, node, &index_p);
1086             }
1087             else if (node->type == XML_ELEMENT_NODE){
1088                 /* if there was a PI index instruction before this element */
1089                 if (index_p){
1090                     index_value_of(tinfo, extctr, node, index_p);            
1091                     index_p = 0;
1092                 }
1093                 process_xml_element_node(tinfo, extctr, node);
1094             }
1095             else
1096                 continue;
1097         }
1098 }
1099
1100
1101 /* DOM filter style indexing */
1102 static void extract_dom_doc_node(struct filter_info *tinfo, 
1103                                  struct recExtractCtrl *extctr, 
1104                                  xmlDocPtr doc)
1105 {
1106     /* printf("DOC    %s\n", xmlGetNodePath((xmlNodePtr)doc)); */
1107
1108     xmlChar *buf_out;
1109     int len_out;
1110     if (extctr->flagShowRecords){
1111         xmlDocDumpMemory(doc, &buf_out, &len_out);
1112         fwrite(buf_out, len_out, 1, stdout);
1113         xmlFree(buf_out);
1114     }
1115
1116     process_xml_element_node(tinfo, extctr, (xmlNodePtr)doc);
1117 }
1118
1119
1120
1121
1122 static int convert_extract_doc(struct filter_info *tinfo, 
1123                                struct filter_input *input,
1124                                struct recExtractCtrl *p, 
1125                                xmlDocPtr doc)
1126
1127 {
1128     xmlChar *buf_out;
1129     int len_out;
1130     const char *params[10];
1131     xsltStylesheetPtr last_xsp = 0;
1132     xmlDocPtr store_doc = 0;
1133
1134     params[0] = 0;
1135     set_param_str(params, "schema", zebra_dom_ns, tinfo->odr_record);
1136
1137     /* input conversion */
1138     perform_convert(tinfo, input->convert, params, &doc, 0);
1139
1140     if (tinfo->store)
1141         {
1142             /* store conversion */
1143             store_doc = xmlCopyDoc(doc, 1);
1144             perform_convert(tinfo, tinfo->store->convert,
1145                             params, &store_doc, &last_xsp);
1146         }
1147     
1148     if (last_xsp)
1149         xsltSaveResultToString(&buf_out, &len_out, 
1150                                store_doc ? store_doc : doc, last_xsp);
1151     else
1152         xmlDocDumpMemory(store_doc ? store_doc : doc, &buf_out, &len_out);
1153     if (p->flagShowRecords)
1154         fwrite(buf_out, len_out, 1, stdout);
1155     (*p->setStoreData)(p, buf_out, len_out);
1156     xmlFree(buf_out);
1157
1158     if (store_doc)
1159         xmlFreeDoc(store_doc);
1160
1161     /* extract conversion */
1162     perform_convert(tinfo, tinfo->extract->convert, params, &doc, 0);
1163
1164     /* finally, do the indexing */
1165     if (doc){
1166         extract_dom_doc_node(tinfo, p, doc);
1167         /* extract_doc_alvis(tinfo, p, doc); */
1168         xmlFreeDoc(doc);
1169     }
1170
1171     return RECCTRL_EXTRACT_OK;
1172 }
1173
1174 static int extract_xml_split(struct filter_info *tinfo,
1175                              struct filter_input *input,
1176                              struct recExtractCtrl *p)
1177 {
1178     int ret;
1179
1180     if (p->first_record)
1181         {
1182             if (input->u.xmlreader.reader)
1183                 xmlFreeTextReader(input->u.xmlreader.reader);
1184             input->u.xmlreader.reader = xmlReaderForIO(ioread_ex, ioclose_ex,
1185                                                        p /* I/O handler */,
1186                                                        0 /* URL */, 
1187                                                        0 /* encoding */,
1188                                                        XML_PARSE_XINCLUDE|
1189                                                        XML_PARSE_NOENT);
1190         }
1191     if (!input->u.xmlreader.reader)
1192         return RECCTRL_EXTRACT_ERROR_GENERIC;
1193
1194     ret = xmlTextReaderRead(input->u.xmlreader.reader);
1195     while (ret == 1)
1196         {
1197             int type = xmlTextReaderNodeType(input->u.xmlreader.reader);
1198             int depth = xmlTextReaderDepth(input->u.xmlreader.reader);
1199             if (type == XML_READER_TYPE_ELEMENT && 
1200                 input->u.xmlreader.split_level == depth)
1201                 {
1202                     xmlNodePtr ptr
1203                         = xmlTextReaderExpand(input->u.xmlreader.reader);
1204                     if (ptr)
1205                         {
1206                             xmlNodePtr ptr2 = xmlCopyNode(ptr, 1);
1207                             xmlDocPtr doc = xmlNewDoc((const xmlChar*) "1.0");
1208                 
1209                             xmlDocSetRootElement(doc, ptr2);
1210                 
1211                             return convert_extract_doc(tinfo, input, p, doc);
1212                         }
1213                     else
1214                         {
1215                             xmlFreeTextReader(input->u.xmlreader.reader);
1216                             input->u.xmlreader.reader = 0;
1217                             return RECCTRL_EXTRACT_ERROR_GENERIC;
1218                         }
1219                 }
1220             ret = xmlTextReaderRead(input->u.xmlreader.reader);
1221         }
1222     xmlFreeTextReader(input->u.xmlreader.reader);
1223     input->u.xmlreader.reader = 0;
1224     return RECCTRL_EXTRACT_EOF;
1225 }
1226
1227 static int extract_xml_full(struct filter_info *tinfo, 
1228                             struct filter_input *input,
1229                             struct recExtractCtrl *p)
1230 {
1231     if (p->first_record) /* only one record per stream */
1232         {
1233             xmlDocPtr doc = xmlReadIO(ioread_ex, ioclose_ex, 
1234                                       p /* I/O handler */,
1235                                       0 /* URL */,
1236                                       0 /* encoding */,
1237                                       XML_PARSE_XINCLUDE|XML_PARSE_NOENT);
1238             if (!doc)
1239                 {
1240                     return RECCTRL_EXTRACT_ERROR_GENERIC;
1241                 }
1242             return convert_extract_doc(tinfo, input, p, doc);
1243         }
1244     else
1245         return RECCTRL_EXTRACT_EOF;
1246 }
1247
1248 static int extract_iso2709(struct filter_info *tinfo,
1249                            struct filter_input *input,
1250                            struct recExtractCtrl *p)
1251 {
1252     char buf[100000];
1253     int record_length;
1254     int read_bytes, r;
1255
1256     if (p->stream->readf(p->stream, buf, 5) != 5)
1257         return RECCTRL_EXTRACT_EOF;
1258     while (*buf < '0' || *buf > '9')
1259         {
1260             int i;
1261
1262             yaz_log(YLOG_WARN, "MARC: Skipping bad byte %d (0x%02X)",
1263                     *buf & 0xff, *buf & 0xff);
1264             for (i = 0; i<4; i++)
1265                 buf[i] = buf[i+1];
1266
1267             if (p->stream->readf(p->stream, buf+4, 1) != 1)
1268                 return RECCTRL_EXTRACT_EOF;
1269         }
1270     record_length = atoi_n (buf, 5);
1271     if (record_length < 25)
1272         {
1273             yaz_log (YLOG_WARN, "MARC record length < 25, is %d", 
1274                      record_length);
1275             return RECCTRL_EXTRACT_ERROR_GENERIC;
1276         }
1277     read_bytes = p->stream->readf(p->stream, buf+5, record_length-5);
1278     if (read_bytes < record_length-5)
1279         {
1280             yaz_log (YLOG_WARN, "Couldn't read whole MARC record");
1281             return RECCTRL_EXTRACT_ERROR_GENERIC;
1282         }
1283     r = yaz_marc_read_iso2709(input->u.marc.handle,  buf, record_length);
1284     if (r < record_length)
1285         {
1286             yaz_log (YLOG_WARN, "Parsing of MARC record failed r=%d length=%d",
1287                      r, record_length);
1288             return RECCTRL_EXTRACT_ERROR_GENERIC;
1289         }
1290     else
1291         {
1292             xmlDocPtr rdoc;
1293             xmlNode *root_ptr;
1294             yaz_marc_write_xml(input->u.marc.handle, &root_ptr, 0, 0, 0);
1295             rdoc = xmlNewDoc((const xmlChar*) "1.0");
1296             xmlDocSetRootElement(rdoc, root_ptr);
1297             return convert_extract_doc(tinfo, input, p, rdoc);        
1298         }
1299     return RECCTRL_EXTRACT_OK;
1300 }
1301
1302 static int filter_extract(void *clientData, struct recExtractCtrl *p)
1303 {
1304     struct filter_info *tinfo = clientData;
1305     struct filter_input *input = tinfo->input_list;
1306
1307     if (!input)
1308         return RECCTRL_EXTRACT_ERROR_GENERIC;
1309
1310     odr_reset(tinfo->odr_record);
1311     switch(input->type)
1312         {
1313         case DOM_INPUT_XMLREADER:
1314             if (input->u.xmlreader.split_level == 0)
1315                 return extract_xml_full(tinfo, input, p);
1316             else
1317                 return extract_xml_split(tinfo, input, p);
1318             break;
1319         case DOM_INPUT_MARC:
1320             return extract_iso2709(tinfo, input, p);
1321         }
1322     return RECCTRL_EXTRACT_ERROR_GENERIC;
1323 }
1324
1325 static int ioread_ret(void *context, char *buffer, int len)
1326 {
1327     struct recRetrieveCtrl *p = context;
1328     return p->stream->readf(p->stream, buffer, len);
1329 }
1330
1331 static int ioclose_ret(void *context)
1332 {
1333     return 0;
1334 }
1335
1336 static int filter_retrieve (void *clientData, struct recRetrieveCtrl *p)
1337 {
1338     /* const char *esn = zebra_dom_ns; */
1339     const char *esn = 0;
1340     const char *params[32];
1341     struct filter_info *tinfo = clientData;
1342     xmlDocPtr doc;
1343     struct filter_retrieve *retrieve;
1344     xsltStylesheetPtr last_xsp = 0;
1345
1346     if (p->comp)
1347         {
1348             if (p->comp->which == Z_RecordComp_simple
1349                 && p->comp->u.simple->which == Z_ElementSetNames_generic)
1350                 {
1351                     esn = p->comp->u.simple->u.generic;
1352                 }
1353             else if (p->comp->which == Z_RecordComp_complex 
1354                      && p->comp->u.complex->generic->elementSpec
1355                      && p->comp->u.complex->generic->elementSpec->which ==
1356                      Z_ElementSpec_elementSetName)
1357                 {
1358                     esn = p->comp->u.complex->generic->elementSpec->u.elementSetName;
1359                 }
1360         }
1361     retrieve = lookup_retrieve(tinfo, esn);
1362     if (!retrieve)
1363         {
1364             p->diagnostic =
1365                 YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
1366             return 0;
1367         }
1368
1369     params[0] = 0;
1370     set_param_int(params, "id", p->localno, p->odr);
1371     if (p->fname)
1372         set_param_str(params, "filename", p->fname, p->odr);
1373     if (p->staticrank >= 0)
1374         set_param_int(params, "rank", p->staticrank, p->odr);
1375
1376     if (esn)
1377         set_param_str(params, "schema", esn, p->odr);
1378     else
1379         if (retrieve->name)
1380             set_param_str(params, "schema", retrieve->name, p->odr);
1381         else if (retrieve->identifier)
1382             set_param_str(params, "schema", retrieve->identifier, p->odr);
1383         else
1384             set_param_str(params, "schema", "", p->odr);
1385
1386     if (p->score >= 0)
1387         set_param_int(params, "score", p->score, p->odr);
1388     set_param_int(params, "size", p->recordSize, p->odr);
1389
1390     doc = xmlReadIO(ioread_ret, ioclose_ret, p /* I/O handler */,
1391                     0 /* URL */,
1392                     0 /* encoding */,
1393                     XML_PARSE_XINCLUDE|XML_PARSE_NOENT);
1394     if (!doc)
1395         {
1396             p->diagnostic = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
1397             return 0;
1398         }
1399
1400     /* retrieve conversion */
1401     perform_convert(tinfo, retrieve->convert, params, &doc, &last_xsp);
1402     if (!doc)
1403         {
1404             p->diagnostic = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
1405         }
1406     else if (p->input_format == VAL_NONE || p->input_format == VAL_TEXT_XML)
1407         {
1408             xmlChar *buf_out;
1409             int len_out;
1410
1411             if (last_xsp)
1412                 xsltSaveResultToString(&buf_out, &len_out, doc, last_xsp);
1413             else
1414                 xmlDocDumpMemory(doc, &buf_out, &len_out);            
1415
1416             p->output_format = VAL_TEXT_XML;
1417             p->rec_len = len_out;
1418             p->rec_buf = odr_malloc(p->odr, p->rec_len);
1419             memcpy(p->rec_buf, buf_out, p->rec_len);
1420             xmlFree(buf_out);
1421         }
1422     else if (p->output_format == VAL_SUTRS)
1423         {
1424             xmlChar *buf_out;
1425             int len_out;
1426
1427             if (last_xsp)
1428                 xsltSaveResultToString(&buf_out, &len_out, doc, last_xsp);
1429             else
1430                 xmlDocDumpMemory(doc, &buf_out, &len_out);            
1431         
1432             p->output_format = VAL_SUTRS;
1433             p->rec_len = len_out;
1434             p->rec_buf = odr_malloc(p->odr, p->rec_len);
1435             memcpy(p->rec_buf, buf_out, p->rec_len);
1436         
1437             xmlFree(buf_out);
1438         }
1439     else
1440         {
1441             p->diagnostic = YAZ_BIB1_RECORD_SYNTAX_UNSUPP;
1442         }
1443     xmlFreeDoc(doc);
1444     return 0;
1445 }
1446
1447 static struct recType filter_type = {
1448     0,
1449     "dom",
1450     filter_init,
1451     filter_config,
1452     filter_destroy,
1453     filter_extract,
1454     filter_retrieve
1455 };
1456
1457 RecType
1458 #ifdef IDZEBRA_STATIC_DOM
1459 idzebra_filter_dom
1460 #else
1461 idzebra_filter
1462 #endif
1463
1464 [] = {
1465     &filter_type,
1466     0,
1467 };
1468 /*
1469  * Local variables:
1470  * c-basic-offset: 4
1471  * indent-tabs-mode: nil
1472  * End:
1473  * vim: shiftwidth=4 tabstop=8 expandtab
1474  */
1475