6544b37baeab3e015c1239715e41fe72efeff3ad
[idzebra-moved-to-github.git] / recctrl / xslt.c
1 /* $Id: xslt.c,v 1.24 2006-05-24 12:56:56 marc Exp $
2    Copyright (C) 1995-2005
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 #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
37 #include <idzebra/util.h>
38 #include <idzebra/recctrl.h>
39
40 struct filter_xslt_schema {
41     const char *name;
42     const char *identifier;
43     const char *stylesheet;
44     struct filter_xslt_schema *next;
45     const char *default_schema;
46     const char *include_snippet;
47     xsltStylesheetPtr stylesheet_xsp;
48 };
49
50 struct filter_xslt_info {
51     xmlDocPtr doc;
52     char *fname;
53     char *full_name;
54     const char *profile_path;
55     const char *split_level;
56     const char *split_path;
57     ODR odr;
58     struct filter_xslt_schema *schemas;
59     xmlTextReaderPtr reader;
60 };
61
62
63 #define ZEBRA_SCHEMA_XSLT_NS "http://indexdata.dk/zebra/xslt/1"
64
65 #define XML_STRCMP(a,b)   strcmp((char*)a, b)
66 #define XML_STRLEN(a) strlen((char*)a)
67
68 static const char *zebra_xslt_ns = ZEBRA_SCHEMA_XSLT_NS;
69
70 static void set_param_xml(const char **params, const char *name,
71                           const char *value, ODR odr)
72 {
73     while (*params)
74         params++;
75     params[0] = name;
76     params[1] = value;
77     params[2] = 0;
78 }
79
80 static void set_param_str(const char **params, const char *name,
81                           const char *value, ODR odr)
82 {
83     char *quoted = odr_malloc(odr, 3 + strlen(value));
84     sprintf(quoted, "'%s'", value);
85     while (*params)
86         params++;
87     params[0] = name;
88     params[1] = quoted;
89     params[2] = 0;
90 }
91
92 static void set_param_int(const char **params, const char *name,
93                           zint value, ODR odr)
94 {
95     char *quoted = odr_malloc(odr, 30); /* 25 digits enough for 2^64 */
96     while (*params)
97         params++;
98     sprintf(quoted, "'" ZINT_FORMAT "'", value);
99     params[0] = name;
100     params[1] = quoted;
101     params[2] = 0;
102 }
103
104 #define ENABLE_INPUT_CALLBACK 0
105
106 #if ENABLE_INPUT_CALLBACK
107 static int zebra_xmlInputMatchCallback (char const *filename)
108 {
109     yaz_log(YLOG_LOG, "match %s", filename);
110     return 0;
111 }
112
113 static void * zebra_xmlInputOpenCallback (char const *filename)
114 {
115     return 0;
116 }
117
118 static int zebra_xmlInputReadCallback (void * context, char * buffer, int len)
119 {
120     return 0;
121 }
122
123 static int zebra_xmlInputCloseCallback (void * context)
124 {
125     return 0;
126 }
127 #endif
128
129 static void *filter_init(Res res, RecType recType)
130 {
131     struct filter_xslt_info *tinfo 
132       = (struct filter_xslt_info *) xmalloc(sizeof(*tinfo));
133     tinfo->reader = 0;
134     tinfo->fname = 0;
135     tinfo->full_name = 0;
136     tinfo->profile_path = 0;
137     tinfo->split_level = 0;
138     tinfo->split_path = 0;
139     tinfo->odr = odr_createmem(ODR_ENCODE);
140     tinfo->doc = 0;
141     tinfo->schemas = 0;
142
143 #if ENABLE_INPUT_CALLBACK
144     xmlRegisterDefaultInputCallbacks();
145     xmlRegisterInputCallbacks(zebra_xmlInputMatchCallback,
146                               zebra_xmlInputOpenCallback,
147                               zebra_xmlInputReadCallback,
148                               zebra_xmlInputCloseCallback);
149 #endif
150     return tinfo;
151 }
152
153 static int attr_content(struct _xmlAttr *attr, const char *name,
154                         const char **dst_content)
155 {
156     if (!XML_STRCMP(attr->name, name) && attr->children &&
157         attr->children->type == XML_TEXT_NODE)
158     {
159         *dst_content = (const char *)(attr->children->content);
160         return 1;
161     }
162     return 0;
163 }
164
165 static void destroy_schemas(struct filter_xslt_info *tinfo)
166 {
167     struct filter_xslt_schema *schema = tinfo->schemas;
168     while (schema)
169     {
170         struct filter_xslt_schema *schema_next = schema->next;
171         if (schema->stylesheet_xsp)
172             xsltFreeStylesheet(schema->stylesheet_xsp);
173         xfree(schema);
174         schema = schema_next;
175     }
176     tinfo->schemas = 0;
177     xfree(tinfo->fname);
178     if (tinfo->doc)
179         xmlFreeDoc(tinfo->doc);    
180     tinfo->doc = 0;
181 }
182
183 static ZEBRA_RES create_schemas(struct filter_xslt_info *tinfo, 
184                                 const char *fname)
185 {
186     char tmp_full_name[1024];
187     xmlNodePtr ptr;
188     tinfo->fname = xstrdup(fname);
189
190     if (yaz_filepath_resolve(tinfo->fname, tinfo->profile_path, 
191                              NULL, tmp_full_name))
192       tinfo->full_name = xstrdup(tmp_full_name);
193     else
194       tinfo->full_name = xstrdup(tinfo->fname);
195
196     yaz_log(YLOG_LOG, "xslt filter: loading config file %s", tinfo->full_name);
197
198     tinfo->doc = xmlParseFile(tinfo->full_name);
199     if (!tinfo->doc) {
200       yaz_log(YLOG_WARN, "xslt filter: could not parse config file %s", 
201               tinfo->full_name);
202       return ZEBRA_FAIL;
203     }
204
205     ptr = xmlDocGetRootElement(tinfo->doc);
206     if (!ptr || ptr->type != XML_ELEMENT_NODE ||
207         XML_STRCMP(ptr->name, "schemaInfo")){
208           yaz_log(YLOG_WARN, 
209                   "xslt filter:  config file %s :" 
210                   " expected root element <schemaInfo>", 
211               tinfo->full_name);  
212       return ZEBRA_FAIL;
213     }
214     
215     for (ptr = ptr->children; ptr; ptr = ptr->next)
216     {
217         if (ptr->type != XML_ELEMENT_NODE)
218             continue;
219         if (!XML_STRCMP(ptr->name, "schema"))
220         {
221             char tmp_xslt_full_name[1024];
222             struct _xmlAttr *attr;
223             struct filter_xslt_schema *schema = xmalloc(sizeof(*schema));
224             schema->name = 0;
225             schema->identifier = 0;
226             schema->stylesheet = 0;
227             schema->default_schema = 0;
228             schema->next = tinfo->schemas;
229             schema->stylesheet_xsp = 0;
230             schema->include_snippet = 0;
231             tinfo->schemas = schema;
232             for (attr = ptr->properties; attr; attr = attr->next)
233             {
234                 attr_content(attr, "identifier", &schema->identifier);
235                 attr_content(attr, "name", &schema->name);
236                 attr_content(attr, "stylesheet", &schema->stylesheet);
237                 attr_content(attr, "default", &schema->default_schema);
238                 attr_content(attr, "snippet", &schema->include_snippet);
239             }
240             if (schema->stylesheet){
241               yaz_filepath_resolve(schema->stylesheet, tinfo->profile_path, 
242                                    NULL, tmp_xslt_full_name);
243               schema->stylesheet_xsp 
244                 = xsltParseStylesheetFile((const xmlChar*) tmp_xslt_full_name);
245               if (!schema->stylesheet_xsp)
246                 yaz_log(YLOG_WARN, 
247                         "xslt filter: could not parse xslt stylesheet %s", 
248                         tmp_xslt_full_name);
249             }
250             
251         }
252         else if (!XML_STRCMP(ptr->name, "split"))
253         {
254             struct _xmlAttr *attr;
255             for (attr = ptr->properties; attr; attr = attr->next)
256             {
257                 attr_content(attr, "level", &tinfo->split_level);
258                 attr_content(attr, "path", &tinfo->split_path);
259             }
260         }
261         else
262         {
263             yaz_log(YLOG_WARN, "Bad element %s in %s", ptr->name, fname);
264             return ZEBRA_FAIL;
265         }
266     }
267     return ZEBRA_OK;
268 }
269
270 static struct filter_xslt_schema *lookup_schema(struct filter_xslt_info *tinfo,
271                                            const char *est)
272 {
273     struct filter_xslt_schema *schema;
274     for (schema = tinfo->schemas; schema; schema = schema->next)
275     {
276         /* find requested schema */
277         if (est) 
278         {    
279             if (schema->identifier && !strcmp(schema->identifier, est))
280                 return schema;
281             
282             if (schema->name && !strcmp(schema->name, est))
283                 return schema;
284         } 
285         /* or return default schema if defined */
286         else if (schema->default_schema)
287             return schema;
288     }
289
290     /* return first schema if no default schema defined */
291     if (tinfo->schemas)
292         return tinfo->schemas;
293     
294     return 0;
295 }
296
297 static ZEBRA_RES filter_config(void *clientData, Res res, const char *args)
298 {
299     struct filter_xslt_info *tinfo = clientData;
300     if (!args || !*args){
301       yaz_log(YLOG_WARN, "xslt filter: need config file");
302       return ZEBRA_FAIL;
303     }
304
305     if (tinfo->fname && !strcmp(args, tinfo->fname))
306         return ZEBRA_OK;
307     
308     tinfo->profile_path 
309       /* = res_get_def(res, "profilePath", DEFAULT_PROFILE_PATH); */
310       = res_get(res, "profilePath");
311     yaz_log(YLOG_LOG, "xslt filter: profilePath %s", tinfo->profile_path);
312
313     destroy_schemas(tinfo);
314     create_schemas(tinfo, args);
315     return ZEBRA_OK;
316 }
317
318 static void filter_destroy(void *clientData)
319 {
320     struct filter_xslt_info *tinfo = clientData;
321     destroy_schemas(tinfo);
322     if (tinfo->reader)
323         xmlFreeTextReader(tinfo->reader);
324     odr_destroy(tinfo->odr);
325     xfree(tinfo);
326 }
327
328 static int ioread_ex(void *context, char *buffer, int len)
329 {
330     struct recExtractCtrl *p = context;
331     return (*p->readf)(p->fh, buffer, len);
332 }
333
334 static int ioclose_ex(void *context)
335 {
336     return 0;
337 }
338
339 static void index_cdata(struct filter_xslt_info *tinfo, struct recExtractCtrl *ctrl,
340                         xmlNodePtr ptr, RecWord *recWord)
341 {
342     for(; ptr; ptr = ptr->next)
343     {
344         index_cdata(tinfo, ctrl, ptr->children, recWord);
345         if (ptr->type != XML_TEXT_NODE)
346             continue;
347         recWord->term_buf = (const char *)ptr->content;
348         recWord->term_len = XML_STRLEN(ptr->content);
349         (*ctrl->tokenAdd)(recWord);
350     }
351 }
352
353 static void index_node(struct filter_xslt_info *tinfo,  struct recExtractCtrl *ctrl,
354                        xmlNodePtr ptr, RecWord *recWord)
355 {
356     for(; ptr; ptr = ptr->next)
357     {
358         index_node(tinfo, ctrl, ptr->children, recWord);
359         if (ptr->type != XML_ELEMENT_NODE || !ptr->ns ||
360             XML_STRCMP(ptr->ns->href, zebra_xslt_ns))
361             continue;
362         if (!XML_STRCMP(ptr->name, "index"))
363         {
364             const char *name_str = 0;
365             const char *type_str = 0;
366             const char *xpath_str = 0;
367             struct _xmlAttr *attr;
368             for (attr = ptr->properties; attr; attr = attr->next)
369             {
370                 attr_content(attr, "name", &name_str);
371                 attr_content(attr, "xpath", &xpath_str);
372                 attr_content(attr, "type", &type_str);
373             }
374             if (name_str)
375             {
376                 int prev_type = recWord->index_type; /* save default type */
377
378                 if (type_str && *type_str)
379                     recWord->index_type = *type_str; /* type was given */
380                 recWord->index_name = name_str;
381                 index_cdata(tinfo, ctrl, ptr->children, recWord);
382
383                 recWord->index_type = prev_type;     /* restore it again */
384             }
385         }
386     }
387 }
388
389 static void index_record(struct filter_xslt_info *tinfo,struct recExtractCtrl *ctrl,
390                          xmlNodePtr ptr, RecWord *recWord)
391 {
392     if (ptr && ptr->type == XML_ELEMENT_NODE && ptr->ns &&
393         !XML_STRCMP(ptr->ns->href, zebra_xslt_ns)
394         && !XML_STRCMP(ptr->name, "record"))
395     {
396         const char *type_str = "update";
397         const char *id_str = 0;
398         const char *rank_str = 0;
399         struct _xmlAttr *attr;
400         for (attr = ptr->properties; attr; attr = attr->next)
401         {
402             attr_content(attr, "type", &type_str);
403             attr_content(attr, "id", &id_str);
404             attr_content(attr, "rank", &rank_str);
405         }
406         if (id_str)
407             sscanf(id_str, "%255s", ctrl->match_criteria);
408         if (rank_str)
409         {
410             ctrl->staticrank = atoi(rank_str);
411             yaz_log(YLOG_LOG, "rank=%d",ctrl->staticrank);
412         }
413         else
414             yaz_log(YLOG_LOG, "no rank");
415         
416         ptr = ptr->children;
417     }
418     index_node(tinfo, ctrl, ptr, recWord);
419 }
420     
421 static int extract_doc(struct filter_xslt_info *tinfo, struct recExtractCtrl *p,
422                        xmlDocPtr doc)
423 {
424     RecWord recWord;
425     const char *params[10];
426     xmlChar *buf_out;
427     int len_out;
428
429     struct filter_xslt_schema *schema = lookup_schema(tinfo, zebra_xslt_ns);
430
431     params[0] = 0;
432     set_param_str(params, "schema", zebra_xslt_ns, tinfo->odr);
433
434     (*p->init)(p, &recWord);
435
436     if (schema && schema->stylesheet_xsp)
437     {
438         xmlNodePtr root_ptr;
439         xmlDocPtr resDoc = 
440             xsltApplyStylesheet(schema->stylesheet_xsp,
441                                 doc, params);
442         if (p->flagShowRecords)
443         {
444             xmlDocDumpMemory(resDoc, &buf_out, &len_out);
445             fwrite(buf_out, len_out, 1, stdout);
446             xmlFree(buf_out);
447         }
448         root_ptr = xmlDocGetRootElement(resDoc);
449         if (root_ptr)
450             index_record(tinfo, p, root_ptr, &recWord);
451         else
452         {
453             yaz_log(YLOG_WARN, "No root for index XML record."
454                     " split_level=%s stylesheet=%s",
455                     tinfo->split_level, schema->stylesheet);
456         }
457         xmlFreeDoc(resDoc);
458     }
459     xmlDocDumpMemory(doc, &buf_out, &len_out);
460     if (p->flagShowRecords)
461         fwrite(buf_out, len_out, 1, stdout);
462     (*p->setStoreData)(p, buf_out, len_out);
463     xmlFree(buf_out);
464     
465     xmlFreeDoc(doc);
466     return RECCTRL_EXTRACT_OK;
467 }
468
469 static int extract_split(struct filter_xslt_info *tinfo, struct recExtractCtrl *p)
470 {
471     int ret;
472     int split_depth = 0;
473     if (p->first_record)
474     {
475         if (tinfo->reader)
476             xmlFreeTextReader(tinfo->reader);
477         tinfo->reader = xmlReaderForIO(ioread_ex, ioclose_ex,
478                                        p /* I/O handler */,
479                                        0 /* URL */, 
480                                        0 /* encoding */,
481                                        XML_PARSE_XINCLUDE);
482     }
483     if (!tinfo->reader)
484         return RECCTRL_EXTRACT_ERROR_GENERIC;
485
486     if (tinfo->split_level)
487         split_depth = atoi(tinfo->split_level);
488     ret = xmlTextReaderRead(tinfo->reader);
489     while (ret == 1) {
490         int type = xmlTextReaderNodeType(tinfo->reader);
491         int depth = xmlTextReaderDepth(tinfo->reader);
492         if (split_depth == 0 ||
493             (split_depth > 0 &&
494              type == XML_READER_TYPE_ELEMENT && split_depth == depth))
495         {
496             xmlNodePtr ptr = xmlTextReaderExpand(tinfo->reader);
497             xmlNodePtr ptr2 = xmlCopyNode(ptr, 1);
498             xmlDocPtr doc = xmlNewDoc((const xmlChar*) "1.0");
499
500             xmlDocSetRootElement(doc, ptr2);
501
502             return extract_doc(tinfo, p, doc);   
503         }
504         ret = xmlTextReaderRead(tinfo->reader);
505     }
506     xmlFreeTextReader(tinfo->reader);
507     tinfo->reader = 0;
508     return RECCTRL_EXTRACT_EOF;
509 }
510
511 static int extract_full(struct filter_xslt_info *tinfo, struct recExtractCtrl *p)
512 {
513     if (p->first_record) /* only one record per stream */
514     {
515         xmlDocPtr doc = xmlReadIO(ioread_ex, ioclose_ex, p /* I/O handler */,
516                                   0 /* URL */,
517                                   0 /* encoding */,
518                                   XML_PARSE_XINCLUDE);
519         if (!doc)
520         {
521             return RECCTRL_EXTRACT_ERROR_GENERIC;
522         }
523         return extract_doc(tinfo, p, doc);
524     }
525     else
526         return RECCTRL_EXTRACT_EOF;
527 }
528
529 static int filter_extract(void *clientData, struct recExtractCtrl *p)
530 {
531     struct filter_xslt_info *tinfo = clientData;
532
533     odr_reset(tinfo->odr);
534
535     if (tinfo->split_level == 0 && tinfo->split_path == 0)
536         return extract_full(tinfo, p);
537     else
538     {
539         return extract_split(tinfo, p);
540     }
541 }
542
543 static int ioread_ret(void *context, char *buffer, int len)
544 {
545     struct recRetrieveCtrl *p = context;
546     return (*p->readf)(p->fh, buffer, len);
547 }
548
549 static int ioclose_ret(void *context)
550 {
551     return 0;
552 }
553
554
555 static const char *snippet_doc(struct recRetrieveCtrl *p, int text_mode,
556                                int window_size)
557 {
558     const char *xml_doc_str;
559     int ord = 0;
560     WRBUF wrbuf = wrbuf_alloc();
561     zebra_snippets *res = 
562         zebra_snippets_window(p->doc_snippet, p->hit_snippet, window_size);
563     zebra_snippet_word *w = zebra_snippets_list(res);
564
565     if (text_mode)
566         wrbuf_printf(wrbuf, "\'");
567     else
568         wrbuf_printf(wrbuf, "<snippet xmlns='%s'>\n", zebra_xslt_ns);
569     for (; w; w = w->next)
570     {
571         if (ord == 0)
572             ord = w->ord;
573         else if (ord != w->ord)
574
575             break;
576         if (text_mode)
577             wrbuf_printf(wrbuf, "%s%s%s ", 
578                          w->match ? "*" : "",
579                          w->term,
580                          w->match ? "*" : "");
581         else
582         {
583             wrbuf_printf(wrbuf, " <term ord='%d' seqno='" ZINT_FORMAT "' %s>", 
584                          w->ord, w->seqno,
585                          (w->match ? "match='1'" : ""));
586             wrbuf_xmlputs(wrbuf, w->term);
587             wrbuf_printf(wrbuf, "</term>\n");
588         }
589     }
590     if (text_mode)
591         wrbuf_printf(wrbuf, "\'");
592     else
593         wrbuf_printf(wrbuf, "</snippet>\n");
594
595     xml_doc_str = odr_strdup(p->odr, wrbuf_buf(wrbuf));
596
597     zebra_snippets_destroy(res);
598     wrbuf_free(wrbuf, 1);
599     return xml_doc_str;
600 }
601
602 static int filter_retrieve (void *clientData, struct recRetrieveCtrl *p)
603 {
604     const char *esn = 0;
605     const char *params[32];
606     struct filter_xslt_info *tinfo = clientData;
607     xmlDocPtr resDoc;
608     xmlDocPtr doc;
609     struct filter_xslt_schema *schema;
610     int window_size = -1;
611
612     if (p->comp)
613     {
614         if (p->comp->which == Z_RecordComp_simple
615             && p->comp->u.simple->which == Z_ElementSetNames_generic)
616         {
617             esn = p->comp->u.simple->u.generic;
618         }
619         else if (p->comp->which == Z_RecordComp_complex 
620                  && p->comp->u.complex->generic->elementSpec
621                  && p->comp->u.complex->generic->elementSpec->which ==
622                  Z_ElementSpec_elementSetName)
623         {
624             esn = p->comp->u.complex->generic->elementSpec->u.elementSetName;
625         }
626     }
627     schema = lookup_schema(tinfo, esn);
628     if (!schema)
629     {
630         p->diagnostic =
631             YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
632         return 0;
633     }
634
635     if (schema->include_snippet)
636         window_size = atoi(schema->include_snippet);
637
638     params[0] = 0;
639     set_param_int(params, "id", p->localno, p->odr);
640     if (p->fname)
641         set_param_str(params, "filename", p->fname, p->odr);
642     if (p->staticrank >= 0)
643         set_param_int(params, "rank", p->staticrank, p->odr);
644
645      if (esn)
646         set_param_str(params, "schema", esn, p->odr);
647     else
648         if (schema->name)
649             set_param_str(params, "schema", schema->name, p->odr);
650         else if (schema->identifier)
651             set_param_str(params, "schema", schema->identifier, p->odr);
652         else
653             set_param_str(params, "schema", "", p->odr);
654
655     if (p->score >= 0)
656         set_param_int(params, "score", p->score, p->odr);
657     set_param_int(params, "size", p->recordSize, p->odr);
658
659     if (window_size >= 0)
660         set_param_xml(params, "snippet", snippet_doc(p, 1, window_size),
661                       p->odr);
662     doc = xmlReadIO(ioread_ret, ioclose_ret, p /* I/O handler */,
663                     0 /* URL */,
664                     0 /* encoding */,
665                     XML_PARSE_XINCLUDE);
666     if (!doc)
667     {
668         p->diagnostic = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
669         return 0;
670     }
671
672     if (window_size >= 0)
673     {
674         xmlNodePtr node = xmlDocGetRootElement(doc);
675         const char *snippet_str = snippet_doc(p, 0, window_size);
676         xmlDocPtr snippet_doc = xmlParseMemory(snippet_str, strlen(snippet_str));
677         xmlAddChild(node, xmlDocGetRootElement(snippet_doc));
678     }
679     if (!schema->stylesheet_xsp)
680         resDoc = doc;
681     else
682     {
683         resDoc = xsltApplyStylesheet(schema->stylesheet_xsp,
684                                      doc, params);
685         xmlFreeDoc(doc);
686     }
687     if (!resDoc)
688     {
689         p->diagnostic = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
690     }
691     else if (p->input_format == VAL_NONE || p->input_format == VAL_TEXT_XML)
692     {
693         xmlChar *buf_out;
694         int len_out;
695         xmlDocDumpMemory(resDoc, &buf_out, &len_out);
696
697         p->output_format = VAL_TEXT_XML;
698         p->rec_len = len_out;
699         p->rec_buf = odr_malloc(p->odr, p->rec_len);
700         memcpy(p->rec_buf, buf_out, p->rec_len);
701         
702         xmlFree(buf_out);
703     }
704     else if (p->output_format == VAL_SUTRS)
705     {
706         xmlChar *buf_out;
707         int len_out;
708         xmlDocDumpMemory(resDoc, &buf_out, &len_out);
709
710         p->output_format = VAL_SUTRS;
711         p->rec_len = len_out;
712         p->rec_buf = odr_malloc(p->odr, p->rec_len);
713         memcpy(p->rec_buf, buf_out, p->rec_len);
714         
715         xmlFree(buf_out);
716     }
717     else
718     {
719         p->diagnostic = YAZ_BIB1_RECORD_SYNTAX_UNSUPP;
720     }
721     xmlFreeDoc(resDoc);
722     return 0;
723 }
724
725 static struct recType filter_type = {
726     0,
727     "xslt",
728     filter_init,
729     filter_config,
730     filter_destroy,
731     filter_extract,
732     filter_retrieve
733 };
734
735 RecType
736 #ifdef IDZEBRA_STATIC_XSLT
737 idzebra_filter_xslt
738 #else
739 idzebra_filter
740 #endif
741
742 [] = {
743     &filter_type,
744     0,
745 };
746 /*
747  * Local variables:
748  * c-basic-offset: 4
749  * indent-tabs-mode: nil
750  * End:
751  * vim: shiftwidth=4 tabstop=8 expandtab
752  */
753