made place for record delete
[idzebra-moved-to-github.git] / recctrl / xslt.c
1 /* $Id: xslt.c,v 1.26 2006-05-29 13:48:43 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             if (schema->stylesheet){
242               yaz_filepath_resolve(schema->stylesheet, tinfo->profile_path, 
243                                    NULL, tmp_xslt_full_name);
244               schema->stylesheet_xsp 
245                 = xsltParseStylesheetFile((const xmlChar*) tmp_xslt_full_name);
246               if (!schema->stylesheet_xsp)
247                 yaz_log(YLOG_WARN, 
248                         "xslt filter: could not parse xslt stylesheet %s", 
249                         tmp_xslt_full_name);
250             }
251             
252         }
253         else if (!XML_STRCMP(ptr->name, "split"))
254         {
255             struct _xmlAttr *attr;
256             for (attr = ptr->properties; attr; attr = attr->next)
257             {
258                 attr_content(attr, "level", &tinfo->split_level);
259                 attr_content(attr, "path", &tinfo->split_path);
260             }
261         }
262         else
263         {
264             yaz_log(YLOG_WARN, "Bad element %s in %s", ptr->name, fname);
265             return ZEBRA_FAIL;
266         }
267     }
268     return ZEBRA_OK;
269 }
270
271 static struct filter_xslt_schema *lookup_schema(struct filter_xslt_info *tinfo,
272                                            const char *est)
273 {
274     struct filter_xslt_schema *schema;
275     for (schema = tinfo->schemas; schema; schema = schema->next)
276     {
277         /* find requested schema */
278         if (est) 
279         {    
280             if (schema->identifier && !strcmp(schema->identifier, est))
281                 return schema;
282             
283             if (schema->name && !strcmp(schema->name, est))
284                 return schema;
285         } 
286         /* or return default schema if defined */
287         else if (schema->default_schema)
288             return schema;
289     }
290
291     /* return first schema if no default schema defined */
292     if (tinfo->schemas)
293         return tinfo->schemas;
294     
295     return 0;
296 }
297
298 static ZEBRA_RES filter_config(void *clientData, Res res, const char *args)
299 {
300     struct filter_xslt_info *tinfo = clientData;
301     if (!args || !*args){
302       yaz_log(YLOG_WARN, "xslt filter: need config file");
303       return ZEBRA_FAIL;
304     }
305
306     if (tinfo->fname && !strcmp(args, tinfo->fname))
307         return ZEBRA_OK;
308     
309     tinfo->profile_path 
310       /* = res_get_def(res, "profilePath", DEFAULT_PROFILE_PATH); */
311       = res_get(res, "profilePath");
312     yaz_log(YLOG_LOG, "xslt filter: profilePath %s", tinfo->profile_path);
313
314     destroy_schemas(tinfo);
315     create_schemas(tinfo, args);
316     return ZEBRA_OK;
317 }
318
319 static void filter_destroy(void *clientData)
320 {
321     struct filter_xslt_info *tinfo = clientData;
322     destroy_schemas(tinfo);
323     if (tinfo->reader)
324         xmlFreeTextReader(tinfo->reader);
325     odr_destroy(tinfo->odr);
326     xfree(tinfo);
327 }
328
329 static int ioread_ex(void *context, char *buffer, int len)
330 {
331     struct recExtractCtrl *p = context;
332     return (*p->readf)(p->fh, buffer, len);
333 }
334
335 static int ioclose_ex(void *context)
336 {
337     return 0;
338 }
339
340 static void index_cdata(struct filter_xslt_info *tinfo, struct recExtractCtrl *ctrl,
341                         xmlNodePtr ptr, RecWord *recWord)
342 {
343     for(; ptr; ptr = ptr->next)
344     {
345         index_cdata(tinfo, ctrl, ptr->children, recWord);
346         if (ptr->type != XML_TEXT_NODE)
347             continue;
348         recWord->term_buf = (const char *)ptr->content;
349         recWord->term_len = XML_STRLEN(ptr->content);
350         (*ctrl->tokenAdd)(recWord);
351     }
352 }
353
354 static void index_node(struct filter_xslt_info *tinfo,  struct recExtractCtrl *ctrl,
355                        xmlNodePtr ptr, RecWord *recWord)
356 {
357     for(; ptr; ptr = ptr->next)
358     {
359         index_node(tinfo, ctrl, ptr->children, recWord);
360         if (ptr->type != XML_ELEMENT_NODE || !ptr->ns ||
361             XML_STRCMP(ptr->ns->href, zebra_xslt_ns))
362             continue;
363         if (!XML_STRCMP(ptr->name, "index"))
364         {
365             const char *name_str = 0;
366             const char *type_str = 0;
367             const char *xpath_str = 0;
368             struct _xmlAttr *attr;
369             for (attr = ptr->properties; attr; attr = attr->next)
370             {
371                 attr_content(attr, "name", &name_str);
372                 attr_content(attr, "xpath", &xpath_str);
373                 attr_content(attr, "type", &type_str);
374             }
375             if (name_str)
376             {
377                 int prev_type = recWord->index_type; /* save default type */
378
379                 if (type_str && *type_str)
380                     recWord->index_type = *type_str; /* type was given */
381                 recWord->index_name = name_str;
382                 index_cdata(tinfo, ctrl, ptr->children, recWord);
383
384                 recWord->index_type = prev_type;     /* restore it again */
385             }
386         }
387     }
388 }
389
390 static void index_record(struct filter_xslt_info *tinfo,struct recExtractCtrl *ctrl,
391                          xmlNodePtr ptr, RecWord *recWord)
392 {
393     if (ptr && ptr->type == XML_ELEMENT_NODE && ptr->ns &&
394         !XML_STRCMP(ptr->ns->href, zebra_xslt_ns)
395         && !XML_STRCMP(ptr->name, "record"))
396     {
397         const char *type_str = "update";
398         const char *id_str = 0;
399         const char *rank_str = 0;
400         struct _xmlAttr *attr;
401         for (attr = ptr->properties; attr; attr = attr->next)
402         {
403             attr_content(attr, "type", &type_str);
404             attr_content(attr, "id", &id_str);
405             attr_content(attr, "rank", &rank_str);
406         }
407         if (id_str)
408             sscanf(id_str, "%255s", ctrl->match_criteria);
409         if (rank_str)
410         {
411             ctrl->staticrank = atoi(rank_str);
412             yaz_log(YLOG_LOG, "rank=%d",ctrl->staticrank);
413         }
414         else
415             yaz_log(YLOG_LOG, "no rank");
416         
417         ptr = ptr->children;
418     }
419     index_node(tinfo, ctrl, ptr, recWord);
420 }
421     
422 static int extract_doc(struct filter_xslt_info *tinfo, struct recExtractCtrl *p,
423                        xmlDocPtr doc)
424 {
425     RecWord recWord;
426     const char *params[10];
427     xmlChar *buf_out;
428     int len_out;
429
430     struct filter_xslt_schema *schema = lookup_schema(tinfo, zebra_xslt_ns);
431
432     params[0] = 0;
433     set_param_str(params, "schema", zebra_xslt_ns, tinfo->odr);
434
435     (*p->init)(p, &recWord);
436
437     if (schema && schema->stylesheet_xsp)
438     {
439         xmlNodePtr root_ptr;
440         xmlDocPtr resDoc = 
441             xsltApplyStylesheet(schema->stylesheet_xsp,
442                                 doc, params);
443         if (p->flagShowRecords)
444         {
445             xmlDocDumpMemory(resDoc, &buf_out, &len_out);
446             fwrite(buf_out, len_out, 1, stdout);
447             xmlFree(buf_out);
448         }
449         root_ptr = xmlDocGetRootElement(resDoc);
450         if (root_ptr)
451             index_record(tinfo, p, root_ptr, &recWord);
452         else
453         {
454             yaz_log(YLOG_WARN, "No root for index XML record."
455                     " split_level=%s stylesheet=%s",
456                     tinfo->split_level, schema->stylesheet);
457         }
458         xmlFreeDoc(resDoc);
459     }
460     xmlDocDumpMemory(doc, &buf_out, &len_out);
461     if (p->flagShowRecords)
462         fwrite(buf_out, len_out, 1, stdout);
463     (*p->setStoreData)(p, buf_out, len_out);
464     xmlFree(buf_out);
465     
466     xmlFreeDoc(doc);
467     return RECCTRL_EXTRACT_OK;
468 }
469
470 static int extract_split(struct filter_xslt_info *tinfo, struct recExtractCtrl *p)
471 {
472     int ret;
473     int split_depth = 0;
474     if (p->first_record)
475     {
476         if (tinfo->reader)
477             xmlFreeTextReader(tinfo->reader);
478         tinfo->reader = xmlReaderForIO(ioread_ex, ioclose_ex,
479                                        p /* I/O handler */,
480                                        0 /* URL */, 
481                                        0 /* encoding */,
482                                        XML_PARSE_XINCLUDE);
483     }
484     if (!tinfo->reader)
485         return RECCTRL_EXTRACT_ERROR_GENERIC;
486
487     if (tinfo->split_level)
488         split_depth = atoi(tinfo->split_level);
489     ret = xmlTextReaderRead(tinfo->reader);
490     while (ret == 1) {
491         int type = xmlTextReaderNodeType(tinfo->reader);
492         int depth = xmlTextReaderDepth(tinfo->reader);
493         if (split_depth == 0 ||
494             (split_depth > 0 &&
495              type == XML_READER_TYPE_ELEMENT && split_depth == depth))
496         {
497             xmlNodePtr ptr = xmlTextReaderExpand(tinfo->reader);
498             xmlNodePtr ptr2 = xmlCopyNode(ptr, 1);
499             xmlDocPtr doc = xmlNewDoc((const xmlChar*) "1.0");
500
501             xmlDocSetRootElement(doc, ptr2);
502
503             return extract_doc(tinfo, p, doc);   
504         }
505         ret = xmlTextReaderRead(tinfo->reader);
506     }
507     xmlFreeTextReader(tinfo->reader);
508     tinfo->reader = 0;
509     return RECCTRL_EXTRACT_EOF;
510 }
511
512 static int extract_full(struct filter_xslt_info *tinfo, struct recExtractCtrl *p)
513 {
514     if (p->first_record) /* only one record per stream */
515     {
516         xmlDocPtr doc = xmlReadIO(ioread_ex, ioclose_ex, p /* I/O handler */,
517                                   0 /* URL */,
518                                   0 /* encoding */,
519                                   XML_PARSE_XINCLUDE);
520         if (!doc)
521         {
522             return RECCTRL_EXTRACT_ERROR_GENERIC;
523         }
524         return extract_doc(tinfo, p, doc);
525     }
526     else
527         return RECCTRL_EXTRACT_EOF;
528 }
529
530 static int filter_extract(void *clientData, struct recExtractCtrl *p)
531 {
532     struct filter_xslt_info *tinfo = clientData;
533
534     odr_reset(tinfo->odr);
535
536     if (tinfo->split_level == 0 && tinfo->split_path == 0)
537         return extract_full(tinfo, p);
538     else
539     {
540         return extract_split(tinfo, p);
541     }
542 }
543
544 static int ioread_ret(void *context, char *buffer, int len)
545 {
546     struct recRetrieveCtrl *p = context;
547     return (*p->readf)(p->fh, buffer, len);
548 }
549
550 static int ioclose_ret(void *context)
551 {
552     return 0;
553 }
554
555
556 static const char *snippet_doc(struct recRetrieveCtrl *p, int text_mode,
557                                int window_size)
558 {
559     const char *xml_doc_str;
560     int ord = 0;
561     WRBUF wrbuf = wrbuf_alloc();
562     zebra_snippets *res = 
563         zebra_snippets_window(p->doc_snippet, p->hit_snippet, window_size);
564     zebra_snippet_word *w = zebra_snippets_list(res);
565
566     if (text_mode)
567         wrbuf_printf(wrbuf, "\'");
568     else
569         wrbuf_printf(wrbuf, "<snippet xmlns='%s'>\n", zebra_xslt_ns);
570     for (; w; w = w->next)
571     {
572         if (ord == 0)
573             ord = w->ord;
574         else if (ord != w->ord)
575
576             break;
577         if (text_mode)
578             wrbuf_printf(wrbuf, "%s%s%s ", 
579                          w->match ? "*" : "",
580                          w->term,
581                          w->match ? "*" : "");
582         else
583         {
584             wrbuf_printf(wrbuf, " <term ord='%d' seqno='" ZINT_FORMAT "' %s>", 
585                          w->ord, w->seqno,
586                          (w->match ? "match='1'" : ""));
587             wrbuf_xmlputs(wrbuf, w->term);
588             wrbuf_printf(wrbuf, "</term>\n");
589         }
590     }
591     if (text_mode)
592         wrbuf_printf(wrbuf, "\'");
593     else
594         wrbuf_printf(wrbuf, "</snippet>\n");
595
596     xml_doc_str = odr_strdup(p->odr, wrbuf_buf(wrbuf));
597
598     zebra_snippets_destroy(res);
599     wrbuf_free(wrbuf, 1);
600     return xml_doc_str;
601 }
602
603 static int filter_retrieve (void *clientData, struct recRetrieveCtrl *p)
604 {
605     const char *esn = 0;
606     const char *params[32];
607     struct filter_xslt_info *tinfo = clientData;
608     xmlDocPtr resDoc;
609     xmlDocPtr doc;
610     struct filter_xslt_schema *schema;
611     int window_size = -1;
612
613     if (p->comp)
614     {
615         if (p->comp->which == Z_RecordComp_simple
616             && p->comp->u.simple->which == Z_ElementSetNames_generic)
617         {
618             esn = p->comp->u.simple->u.generic;
619         }
620         else if (p->comp->which == Z_RecordComp_complex 
621                  && p->comp->u.complex->generic->elementSpec
622                  && p->comp->u.complex->generic->elementSpec->which ==
623                  Z_ElementSpec_elementSetName)
624         {
625             esn = p->comp->u.complex->generic->elementSpec->u.elementSetName;
626         }
627     }
628     schema = lookup_schema(tinfo, esn);
629     if (!schema)
630     {
631         p->diagnostic =
632             YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
633         return 0;
634     }
635
636     if (schema->include_snippet)
637         window_size = atoi(schema->include_snippet);
638
639     params[0] = 0;
640     set_param_int(params, "id", p->localno, p->odr);
641     if (p->fname)
642         set_param_str(params, "filename", p->fname, p->odr);
643     if (p->staticrank >= 0)
644         set_param_int(params, "rank", p->staticrank, p->odr);
645
646      if (esn)
647         set_param_str(params, "schema", esn, p->odr);
648     else
649         if (schema->name)
650             set_param_str(params, "schema", schema->name, p->odr);
651         else if (schema->identifier)
652             set_param_str(params, "schema", schema->identifier, p->odr);
653         else
654             set_param_str(params, "schema", "", p->odr);
655
656     if (p->score >= 0)
657         set_param_int(params, "score", p->score, p->odr);
658     set_param_int(params, "size", p->recordSize, p->odr);
659
660     if (window_size >= 0)
661         set_param_xml(params, "snippet", snippet_doc(p, 1, window_size),
662                       p->odr);
663     doc = xmlReadIO(ioread_ret, ioclose_ret, p /* I/O handler */,
664                     0 /* URL */,
665                     0 /* encoding */,
666                     XML_PARSE_XINCLUDE);
667     if (!doc)
668     {
669         p->diagnostic = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
670         return 0;
671     }
672
673     if (window_size >= 0)
674     {
675         xmlNodePtr node = xmlDocGetRootElement(doc);
676         const char *snippet_str = snippet_doc(p, 0, window_size);
677         xmlDocPtr snippet_doc = xmlParseMemory(snippet_str, strlen(snippet_str));
678         xmlAddChild(node, xmlDocGetRootElement(snippet_doc));
679     }
680     if (!schema->stylesheet_xsp)
681         resDoc = doc;
682     else
683     {
684         resDoc = xsltApplyStylesheet(schema->stylesheet_xsp,
685                                      doc, params);
686         xmlFreeDoc(doc);
687     }
688     if (!resDoc)
689     {
690         p->diagnostic = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
691     }
692     else if (p->input_format == VAL_NONE || p->input_format == VAL_TEXT_XML)
693     {
694         xmlChar *buf_out;
695         int len_out;
696
697         xsltSaveResultToString(&buf_out, &len_out, resDoc,
698                                schema->stylesheet_xsp); 
699
700         p->output_format = VAL_TEXT_XML;
701         p->rec_len = len_out;
702         p->rec_buf = odr_malloc(p->odr, p->rec_len);
703         memcpy(p->rec_buf, buf_out, p->rec_len);
704         
705         xmlFree(buf_out);
706     }
707     else if (p->output_format == VAL_SUTRS)
708     {
709         xmlChar *buf_out;
710         int len_out;
711
712         xsltSaveResultToString(&buf_out, &len_out, resDoc,
713                                schema->stylesheet_xsp); 
714
715         p->output_format = VAL_SUTRS;
716         p->rec_len = len_out;
717         p->rec_buf = odr_malloc(p->odr, p->rec_len);
718         memcpy(p->rec_buf, buf_out, p->rec_len);
719         
720         xmlFree(buf_out);
721     }
722     else
723     {
724         p->diagnostic = YAZ_BIB1_RECORD_SYNTAX_UNSUPP;
725     }
726     xmlFreeDoc(resDoc);
727     return 0;
728 }
729
730 static struct recType filter_type = {
731     0,
732     "xslt",
733     filter_init,
734     filter_config,
735     filter_destroy,
736     filter_extract,
737     filter_retrieve
738 };
739
740 RecType
741 #ifdef IDZEBRA_STATIC_XSLT
742 idzebra_filter_xslt
743 #else
744 idzebra_filter
745 #endif
746
747 [] = {
748     &filter_type,
749     0,
750 };
751 /*
752  * Local variables:
753  * c-basic-offset: 4
754  * indent-tabs-mode: nil
755  * End:
756  * vim: shiftwidth=4 tabstop=8 expandtab
757  */
758