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