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