CCL: fix other inherited attributes
[yaz-moved-to-github.git] / src / record_conv.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file record_conv.c
7  * \brief Record Conversions utility
8  */
9
10 #if HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include <string.h>
15 #include <yaz/yaz-iconv.h>
16 #include <yaz/marcdisp.h>
17 #include <yaz/record_conv.h>
18 #include <yaz/wrbuf.h>
19 #include <yaz/xmalloc.h>
20 #include <yaz/nmem.h>
21 #include <yaz/tpath.h>
22 #include <yaz/z-opac.h>
23 #include <yaz/xml_get.h>
24
25 #if YAZ_HAVE_XML2
26 #include <libxml/parser.h>
27 #include <libxml/tree.h>
28 #include <libxml/xinclude.h>
29 #include <libxml/xpath.h>
30 #include <libxml/xpathInternals.h>
31 #if YAZ_HAVE_XSLT
32 #include <libxslt/xsltutils.h>
33 #include <libxslt/transform.h>
34 #endif
35 #if YAZ_HAVE_EXSLT
36 #include <libexslt/exslt.h>
37 #endif
38
39 /** \brief The internal structure for yaz_record_conv_t */
40 struct yaz_record_conv_struct {
41     /** \brief memory for configuration */
42     NMEM nmem;
43
44     /** \brief conversion rules (allocated using NMEM) */
45     struct yaz_record_conv_rule *rules;
46
47     /** \brief pointer to last conversion rule pointer in chain */
48     struct yaz_record_conv_rule **rules_p;
49
50     /** \brief string buffer for error messages */
51     WRBUF wr_error;
52
53     /** \brief path for opening files  */
54     char *path;
55 };
56
57 struct marc_info {
58     NMEM nmem;
59     const char *input_charset;
60     const char *output_charset;
61     int input_format_mode;
62     int output_format_mode;
63     const char *leader_spec;
64 };
65
66 /** \brief tranformation info (rule info) */
67 struct yaz_record_conv_rule {
68     struct yaz_record_conv_type *type;
69     void *info;
70     struct yaz_record_conv_rule *next;
71 };
72
73 /** \brief reset rules+configuration */
74 static void yaz_record_conv_reset(yaz_record_conv_t p)
75 {
76
77     struct yaz_record_conv_rule *r;
78     for (r = p->rules; r; r = r->next)
79     {
80         r->type->destroy(r->info);
81     }
82     wrbuf_rewind(p->wr_error);
83     nmem_reset(p->nmem);
84
85     p->rules = 0;
86
87     p->rules_p = &p->rules;
88 }
89
90 void yaz_record_conv_destroy(yaz_record_conv_t p)
91 {
92     if (p)
93     {
94         yaz_record_conv_reset(p);
95         nmem_destroy(p->nmem);
96         wrbuf_destroy(p->wr_error);
97
98         xfree(p->path);
99         xfree(p);
100     }
101 }
102
103 #if YAZ_HAVE_XSLT
104 struct xslt_info {
105     NMEM nmem;
106     xmlDocPtr xsp_doc;
107     const char **xsl_parms;
108 };
109
110 static void *construct_xslt(const xmlNode *ptr,
111                             const char *path, WRBUF wr_error)
112 {
113     struct _xmlAttr *attr;
114     const char *stylesheet = 0;
115     struct xslt_info *info = 0;
116     NMEM nmem = 0;
117     int max_parms = 10;
118     int no_parms = 0;
119
120     if (strcmp((const char *) ptr->name, "xslt"))
121         return 0;
122
123     for (attr = ptr->properties; attr; attr = attr->next)
124     {
125         if (!xmlStrcmp(attr->name, BAD_CAST "stylesheet") &&
126             attr->children && attr->children->type == XML_TEXT_NODE)
127             stylesheet = (const char *) attr->children->content;
128         else
129         {
130             wrbuf_printf(wr_error, "Bad attribute '%s'"
131                          "Expected stylesheet.", attr->name);
132             return 0;
133         }
134     }
135     nmem = nmem_create();
136     info = nmem_malloc(nmem, sizeof(*info));
137     info->nmem = nmem;
138     info->xsl_parms = nmem_malloc(
139         nmem, (2 * max_parms + 1) * sizeof(*info->xsl_parms));
140
141     for (ptr = ptr->children; ptr; ptr = ptr->next)
142     {
143         const char *name = 0;
144         const char *value = 0;
145         char *qvalue = 0;
146         if (ptr->type != XML_ELEMENT_NODE)
147             continue;
148         if (strcmp((const char *) ptr->name, "param"))
149         {
150             wrbuf_printf(wr_error, "Bad element '%s'"
151                          "Expected param.", ptr->name);
152             nmem_destroy(nmem);
153             return 0;
154         }
155         for (attr = ptr->properties; attr; attr = attr->next)
156         {
157             if (!xmlStrcmp(attr->name, BAD_CAST "name") &&
158                 attr->children && attr->children->type == XML_TEXT_NODE)
159                 name = (const char *) attr->children->content;
160             else if (!xmlStrcmp(attr->name, BAD_CAST "value") &&
161                 attr->children && attr->children->type == XML_TEXT_NODE)
162                 value = (const char *) attr->children->content;
163             else
164             {
165                 wrbuf_printf(wr_error, "Bad attribute '%s'"
166                              "Expected name or value.", attr->name);
167                 nmem_destroy(nmem);
168                 return 0;
169             }
170         }
171         if (!name || !value)
172         {
173             wrbuf_printf(wr_error, "Missing attributes name or value");
174             nmem_destroy(nmem);
175             return 0;
176         }
177         if (no_parms >= max_parms)
178         {
179             wrbuf_printf(wr_error, "Too many parameters given");
180             nmem_destroy(nmem);
181             return 0;
182         }
183
184         qvalue = nmem_malloc(nmem, strlen(value) + 3);
185         strcpy(qvalue, "\'");
186         strcat(qvalue, value);
187         strcat(qvalue, "\'");
188
189         info->xsl_parms[2 * no_parms] = nmem_strdup(nmem, name);
190         info->xsl_parms[2 * no_parms + 1] = qvalue;
191         no_parms++;
192     }
193     info->xsl_parms[2 * no_parms] = 0;
194
195     if (!stylesheet)
196     {
197         wrbuf_printf(wr_error, "Element <xslt>: "
198                      "attribute 'stylesheet' expected");
199         nmem_destroy(nmem);
200     }
201     else
202     {
203         char fullpath[1024];
204         xsltStylesheetPtr xsp;
205         if (!yaz_filepath_resolve(stylesheet, path, 0, fullpath))
206         {
207             wrbuf_printf(wr_error, "Element <xslt stylesheet=\"%s\"/>:"
208                          " could not locate stylesheet '%s'",
209                          stylesheet, stylesheet);
210             if (path)
211                 wrbuf_printf(wr_error, " with path '%s'", path);
212
213             nmem_destroy(nmem);
214             return 0;
215         }
216         info->xsp_doc = xmlParseFile(fullpath);
217         if (!info->xsp_doc)
218         {
219             wrbuf_printf(wr_error, "Element: <xslt stylesheet=\"%s\"/>:"
220                          " xml parse failed: %s", stylesheet, fullpath);
221             if (path)
222                 wrbuf_printf(wr_error, " with path '%s'", path);
223             nmem_destroy(nmem);
224             return 0;
225         }
226         /* need to copy this before passing it to the processor. It will
227            be encapsulated in the xsp and destroyed by xsltFreeStylesheet */
228         xsp = xsltParseStylesheetDoc(xmlCopyDoc(info->xsp_doc, 1));
229         if (!xsp)
230         {
231             wrbuf_printf(wr_error, "Element: <xslt stylesheet=\"%s\"/>:"
232                          " xslt parse failed: %s", stylesheet, fullpath);
233             if (path)
234                 wrbuf_printf(wr_error, " with path '%s'", path);
235             wrbuf_printf(wr_error, " ("
236 #if YAZ_HAVE_EXSLT
237
238                          "EXSLT enabled"
239 #else
240                          "EXSLT not supported"
241 #endif
242                          ")");
243             xmlFreeDoc(info->xsp_doc);
244             nmem_destroy(info->nmem);
245         }
246         else
247         {
248             xsltFreeStylesheet(xsp);
249             return info;
250         }
251     }
252     return 0;
253 }
254
255 static int convert_xslt(void *vinfo, WRBUF record, WRBUF wr_error)
256 {
257     int ret = 0;
258     struct xslt_info *info = vinfo;
259
260     xmlDocPtr doc = xmlParseMemory(wrbuf_buf(record),
261                                    wrbuf_len(record));
262     if (!doc)
263     {
264         wrbuf_printf(wr_error, "xmlParseMemory failed");
265         ret = -1;
266     }
267     else
268     {
269         xmlDocPtr xsp_doc = xmlCopyDoc(info->xsp_doc, 1);
270         xsltStylesheetPtr xsp = xsltParseStylesheetDoc(xsp_doc);
271         xmlDocPtr res = xsltApplyStylesheet(xsp, doc, info->xsl_parms);
272         if (res)
273         {
274             xmlChar *out_buf = 0;
275             int out_len;
276
277 #if HAVE_XSLTSAVERESULTTOSTRING
278             xsltSaveResultToString(&out_buf, &out_len, res, xsp);
279 #else
280             xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1);
281 #endif
282             if (!out_buf)
283             {
284                 wrbuf_printf(wr_error,
285                              "xsltSaveResultToString failed");
286                 ret = -1;
287             }
288             else
289             {
290                 wrbuf_rewind(record);
291                 wrbuf_write(record, (const char *) out_buf, out_len);
292
293                 xmlFree(out_buf);
294             }
295             xmlFreeDoc(res);
296         }
297         else
298         {
299             wrbuf_printf(wr_error, "xsltApplyStylesheet failed");
300             ret = -1;
301         }
302         xmlFreeDoc(doc);
303         xsltFreeStylesheet(xsp); /* frees xsp_doc too */
304     }
305     return ret;
306 }
307
308 static void destroy_xslt(void *vinfo)
309 {
310     struct xslt_info *info = vinfo;
311
312     if (info)
313     {
314         xmlFreeDoc(info->xsp_doc);
315         nmem_destroy(info->nmem);
316     }
317 }
318
319 /* YAZ_HAVE_XSLT */
320 #endif
321
322 struct select_info {
323     NMEM nmem;
324     char *xpath_expr;
325 };
326
327 static void *construct_select(const xmlNode *ptr,
328                               const char *path, WRBUF wr_error)
329 {
330     if (strcmp((const char *) ptr->name, "select"))
331         return 0;
332     else
333     {
334         NMEM nmem = nmem_create();
335         struct select_info *info = nmem_malloc(nmem, sizeof(*info));
336         const char *attr_str;
337         const char *xpath = 0;
338
339         info->nmem = nmem;
340         info->xpath_expr = 0;
341         attr_str = yaz_xml_get_prop(ptr, "path%s", &xpath);
342         if (attr_str)
343         {
344             wrbuf_printf(wr_error, "Bad attribute '%s'"
345                          "Expected xpath.", attr_str);
346             nmem_destroy(nmem);
347                 return 0;
348         }
349         if (xpath)
350             info->xpath_expr = nmem_strdup(nmem, xpath);
351         return info;
352     }
353 }
354
355 static int convert_select(void *vinfo, WRBUF record, WRBUF wr_error)
356 {
357     int ret = 0;
358     struct select_info *info = vinfo;
359
360     xmlDocPtr doc = xmlParseMemory(wrbuf_buf(record),
361                                    wrbuf_len(record));
362     if (!doc)
363     {
364         wrbuf_printf(wr_error, "xmlParseMemory failed");
365         ret = -1;
366     }
367     else
368     {
369         xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
370         if (xpathCtx && info->xpath_expr)
371         {
372             xmlXPathObjectPtr xpathObj =
373                 xmlXPathEvalExpression((const xmlChar *) info->xpath_expr,
374                                        xpathCtx);
375             if (xpathObj)
376             {
377                 xmlNodeSetPtr nodes = xpathObj->nodesetval;
378                 if (nodes)
379                 {
380                     int i;
381                     if (nodes->nodeNr > 0)
382                         wrbuf_rewind(record);
383                     for (i = 0; i < nodes->nodeNr; i++)
384                     {
385                         xmlNode *ptr = nodes->nodeTab[i];
386                         if (ptr->type == XML_ELEMENT_NODE)
387                             ptr = ptr->children;
388                         for (; ptr; ptr = ptr->next)
389                             if (ptr->type == XML_TEXT_NODE)
390                                 wrbuf_puts(record, (const char *) ptr->content);
391                     }
392                 }
393                 xmlXPathFreeObject(xpathObj);
394             }
395             xmlXPathFreeContext(xpathCtx);
396         }
397         xmlFreeDoc(doc);
398     }
399     return ret;
400 }
401
402 static void destroy_select(void *vinfo)
403 {
404     struct select_info *info = vinfo;
405
406     if (info)
407         nmem_destroy(info->nmem);
408 }
409
410
411 static void *construct_solrmarc(const xmlNode *ptr,
412                                 const char *path, WRBUF wr_error)
413 {
414     if (strcmp((const char *) ptr->name, "solrmarc"))
415         return 0;
416     return wr_error; /* any non-null ptr will do; we don't use it later*/
417 }
418
419 static int convert_solrmarc(void *info, WRBUF record, WRBUF wr_error)
420 {
421     WRBUF w = wrbuf_alloc();
422     const char *buf = wrbuf_buf(record);
423     size_t i, sz = wrbuf_len(record);
424     for (i = 0; i < sz; i++)
425     {
426         int ch;
427         if (buf[i] == '#' && i < sz - 3 && buf[i+3] == ';'
428             && atoi_n_check(buf+i+1, 2, &ch))
429             i += 3;
430         else
431             ch = buf[i];
432         wrbuf_putc(w, ch);
433     }
434     wrbuf_rewind(record);
435     wrbuf_write(record, wrbuf_buf(w), wrbuf_len(w));
436     wrbuf_destroy(w);
437     return 0;
438 }
439
440 static void destroy_solrmarc(void *info)
441 {
442 }
443
444 static void *construct_marc(const xmlNode *ptr,
445                             const char *path, WRBUF wr_error)
446 {
447     NMEM nmem = nmem_create();
448     struct marc_info *info = nmem_malloc(nmem, sizeof(*info));
449     struct _xmlAttr *attr;
450     const char *input_format = 0;
451     const char *output_format = 0;
452
453     if (strcmp((const char *) ptr->name, "marc"))
454     {
455         nmem_destroy(nmem);
456         return 0;
457     }
458     info->nmem = nmem;
459     info->input_charset = 0;
460     info->output_charset = 0;
461     info->input_format_mode = 0;
462     info->output_format_mode = 0;
463     info->leader_spec = 0;
464
465     for (attr = ptr->properties; attr; attr = attr->next)
466     {
467         if (!xmlStrcmp(attr->name, BAD_CAST "inputcharset") &&
468             attr->children && attr->children->type == XML_TEXT_NODE)
469             info->input_charset = (const char *) attr->children->content;
470         else if (!xmlStrcmp(attr->name, BAD_CAST "outputcharset") &&
471             attr->children && attr->children->type == XML_TEXT_NODE)
472             info->output_charset = (const char *) attr->children->content;
473         else if (!xmlStrcmp(attr->name, BAD_CAST "inputformat") &&
474             attr->children && attr->children->type == XML_TEXT_NODE)
475             input_format = (const char *) attr->children->content;
476         else if (!xmlStrcmp(attr->name, BAD_CAST "outputformat") &&
477             attr->children && attr->children->type == XML_TEXT_NODE)
478             output_format = (const char *) attr->children->content;
479         else if (!xmlStrcmp(attr->name, BAD_CAST "leaderspec") &&
480                  attr->children && attr->children->type == XML_TEXT_NODE)
481             info->leader_spec =
482                 nmem_strdup(info->nmem,(const char *) attr->children->content);
483         else
484         {
485             wrbuf_printf(wr_error, "Element <marc>: expected attributes"
486                          "'inputformat', 'inputcharset', 'outputformat' or"
487                          " 'outputcharset', got attribute '%s'",
488                          attr->name);
489             nmem_destroy(info->nmem);
490             return 0;
491         }
492     }
493     if (!input_format)
494     {
495         wrbuf_printf(wr_error, "Element <marc>: "
496                      "attribute 'inputformat' required");
497         nmem_destroy(info->nmem);
498         return 0;
499     }
500     else if (!strcmp(input_format, "marc"))
501     {
502         info->input_format_mode = YAZ_MARC_ISO2709;
503     }
504     else if (!strcmp(input_format, "xml"))
505     {
506         info->input_format_mode = YAZ_MARC_MARCXML;
507         /** Libxml2 generates UTF-8 encoding by default .
508             So we convert from UTF-8 to outputcharset (if defined)
509         */
510         if (!info->input_charset && info->output_charset)
511             info->input_charset = "utf-8";
512     }
513     else if (!strcmp(input_format, "json"))
514     {
515         info->input_format_mode = YAZ_MARC_JSON;
516     }
517     else
518     {
519         wrbuf_printf(wr_error, "Element <marc inputformat='%s'>: "
520                      " Unsupported input format"
521                      " defined by attribute value",
522                      input_format);
523         nmem_destroy(info->nmem);
524         return 0;
525     }
526
527     if (!output_format)
528     {
529         wrbuf_printf(wr_error,
530                      "Element <marc>: attribute 'outputformat' required");
531         nmem_destroy(info->nmem);
532         return 0;
533     }
534     else if (!strcmp(output_format, "line"))
535     {
536         info->output_format_mode = YAZ_MARC_LINE;
537     }
538     else if (!strcmp(output_format, "marcxml"))
539     {
540         info->output_format_mode = YAZ_MARC_MARCXML;
541         if (info->input_charset && !info->output_charset)
542             info->output_charset = "utf-8";
543     }
544     else if (!strcmp(output_format, "turbomarc"))
545     {
546         info->output_format_mode = YAZ_MARC_TURBOMARC;
547         if (info->input_charset && !info->output_charset)
548             info->output_charset = "utf-8";
549     }
550     else if (!strcmp(output_format, "marc"))
551     {
552         info->output_format_mode = YAZ_MARC_ISO2709;
553     }
554     else if (!strcmp(output_format, "marcxchange"))
555     {
556         info->output_format_mode = YAZ_MARC_XCHANGE;
557         if (info->input_charset && !info->output_charset)
558             info->output_charset = "utf-8";
559     }
560     else if (!strcmp(output_format, "json"))
561     {
562         info->output_format_mode = YAZ_MARC_JSON;
563         if (info->input_charset && !info->output_charset)
564             info->output_charset = "utf-8";
565     }
566     else
567     {
568         wrbuf_printf(wr_error, "Element <marc outputformat='%s'>: "
569                      " Unsupported output format"
570                      " defined by attribute value",
571                      output_format);
572         nmem_destroy(info->nmem);
573         return 0;
574     }
575     if (info->input_charset && info->output_charset)
576     {
577         yaz_iconv_t cd = yaz_iconv_open(info->output_charset,
578                                         info->input_charset);
579         if (!cd)
580         {
581             wrbuf_printf(wr_error,
582                          "Element <marc inputcharset='%s' outputcharset='%s'>:"
583                          " Unsupported character set mapping"
584                          " defined by attribute values",
585                          info->input_charset, info->output_charset);
586             nmem_destroy(info->nmem);
587             return 0;
588         }
589         yaz_iconv_close(cd);
590     }
591     else if (!info->output_charset)
592     {
593         wrbuf_printf(wr_error, "Element <marc>: "
594                      "attribute 'outputcharset' missing");
595         nmem_destroy(info->nmem);
596         return 0;
597     }
598     else if (!info->input_charset)
599     {
600         wrbuf_printf(wr_error, "Element <marc>: "
601                      "attribute 'inputcharset' missing");
602         nmem_destroy(info->nmem);
603         return 0;
604     }
605     info->input_charset = nmem_strdup(info->nmem, info->input_charset);
606     info->output_charset = nmem_strdup(info->nmem, info->output_charset);
607     return info;
608 }
609
610 static int convert_marc(void *info, WRBUF record, WRBUF wr_error)
611 {
612     struct marc_info *mi = info;
613     const char *input_charset = mi->input_charset;
614     int ret = 0;
615     yaz_marc_t mt = yaz_marc_create();
616
617     yaz_marc_xml(mt, mi->output_format_mode);
618     if (mi->leader_spec)
619         yaz_marc_leader_spec(mt, mi->leader_spec);
620
621     if (mi->input_format_mode == YAZ_MARC_ISO2709)
622     {
623         int sz = yaz_marc_read_iso2709(mt, wrbuf_buf(record),
624                                        wrbuf_len(record));
625         if (sz > 0)
626         {
627             if (yaz_marc_check_marc21_coding(input_charset, wrbuf_buf(record),
628                                              wrbuf_len(record)))
629                 input_charset = "utf-8";
630             ret = 0;
631         }
632         else
633             ret = -1;
634     }
635     else if (mi->input_format_mode == YAZ_MARC_MARCXML ||
636              mi->input_format_mode == YAZ_MARC_TURBOMARC)
637     {
638         xmlDocPtr doc = xmlParseMemory(wrbuf_buf(record),
639                                        wrbuf_len(record));
640         if (!doc)
641         {
642             wrbuf_printf(wr_error, "xmlParseMemory failed");
643             ret = -1;
644         }
645         else
646         {
647             ret = yaz_marc_read_xml(mt, xmlDocGetRootElement(doc));
648             if (ret)
649                 wrbuf_printf(wr_error, "yaz_marc_read_xml failed");
650         }
651         xmlFreeDoc(doc);
652     }
653     else
654     {
655         wrbuf_printf(wr_error, "unsupported input format");
656         ret = -1;
657     }
658     if (ret == 0)
659     {
660         yaz_iconv_t cd = yaz_iconv_open(mi->output_charset, input_charset);
661
662         if (cd)
663             yaz_marc_iconv(mt, cd);
664
665         wrbuf_rewind(record);
666         ret = yaz_marc_write_mode(mt, record);
667         if (ret)
668             wrbuf_printf(wr_error, "yaz_marc_write_mode failed");
669         if (cd)
670             yaz_iconv_close(cd);
671     }
672     yaz_marc_destroy(mt);
673     return ret;
674 }
675
676 static void destroy_marc(void *info)
677 {
678     struct marc_info *mi = info;
679
680     nmem_destroy(mi->nmem);
681 }
682
683 int yaz_record_conv_configure_t(yaz_record_conv_t p, const xmlNode *ptr,
684                                 struct yaz_record_conv_type *types)
685 {
686     struct yaz_record_conv_type bt[4];
687     size_t i = 0;
688
689     /* register marc */
690     bt[i].construct = construct_marc;
691     bt[i].convert = convert_marc;
692     bt[i++].destroy = destroy_marc;
693
694     bt[i-1].next = &bt[i];
695     bt[i].construct = construct_solrmarc;
696     bt[i].convert = convert_solrmarc;
697     bt[i++].destroy = destroy_solrmarc;
698
699     bt[i-1].next = &bt[i];
700     bt[i].construct = construct_select;
701     bt[i].convert = convert_select;
702     bt[i++].destroy = destroy_select;
703
704 #if YAZ_HAVE_XSLT
705     /* register xslt */
706     bt[i-1].next = &bt[i];
707     bt[i].construct = construct_xslt;
708     bt[i].convert = convert_xslt;
709     bt[i++].destroy = destroy_xslt;
710 #endif
711
712     bt[i-1].next = types;
713     yaz_record_conv_reset(p);
714
715     /* parsing element children */
716     for (ptr = ptr->children; ptr; ptr = ptr->next)
717     {
718         struct yaz_record_conv_type *t;
719         struct yaz_record_conv_rule *r;
720         void *info = 0;
721         if (ptr->type != XML_ELEMENT_NODE)
722             continue;
723         for (t = &bt[0]; t; t = t->next)
724         {
725             wrbuf_rewind(p->wr_error);
726             info = t->construct(ptr, p->path, p->wr_error);
727
728             if (info || wrbuf_len(p->wr_error))
729                 break;
730             /* info== 0 and no error reported , ie not handled by it */
731         }
732         if (!info)
733         {
734             if (wrbuf_len(p->wr_error) == 0)
735                 wrbuf_printf(p->wr_error, "Element <backend>: expected "
736                              "<marc> or <xslt> element, got <%s>"
737                              , ptr->name);
738             return -1;
739         }
740         r = (struct yaz_record_conv_rule *) nmem_malloc(p->nmem, sizeof(*r));
741         r->next = 0;
742         r->info = info;
743         r->type = nmem_malloc(p->nmem, sizeof(*t));
744         memcpy(r->type, t, sizeof(*t));
745         *p->rules_p = r;
746         p->rules_p = &r->next;
747     }
748     return 0;
749 }
750
751 int yaz_record_conv_configure(yaz_record_conv_t p, const xmlNode *ptr)
752 {
753     return yaz_record_conv_configure_t(p, ptr, 0);
754 }
755
756 static int yaz_record_conv_record_rule(yaz_record_conv_t p,
757                                        struct yaz_record_conv_rule *r,
758                                        const char *input_record_buf,
759                                        size_t input_record_len,
760                                        WRBUF output_record)
761 {
762     int ret = 0;
763     WRBUF record = output_record; /* pointer transfer */
764     wrbuf_rewind(p->wr_error);
765
766     wrbuf_write(record, input_record_buf, input_record_len);
767     for (; ret == 0 && r; r = r->next)
768         ret = r->type->convert(r->info, record, p->wr_error);
769     return ret;
770 }
771
772 int yaz_record_conv_opac_record(yaz_record_conv_t p,
773                                 Z_OPACRecord *input_record,
774                                 WRBUF output_record)
775 {
776     int ret = 0;
777     struct yaz_record_conv_rule *r = p->rules;
778     if (!r || r->type->construct != construct_marc)
779     {
780         wrbuf_puts(p->wr_error, "Expecting MARC rule as first rule for OPAC");
781         ret = -1; /* no marc rule so we can't do OPAC */
782     }
783     else
784     {
785         struct marc_info *mi = r->info;
786         const char *input_charset = mi->input_charset;
787         yaz_iconv_t cd;
788
789         WRBUF res = wrbuf_alloc();
790         yaz_marc_t mt = yaz_marc_create();
791
792         if (yaz_opac_check_marc21_coding(input_charset, input_record))
793             input_charset = "utf-8";
794         cd = yaz_iconv_open(mi->output_charset, input_charset);
795
796         wrbuf_rewind(p->wr_error);
797         yaz_marc_xml(mt, mi->output_format_mode);
798
799         yaz_marc_iconv(mt, cd);
800
801         yaz_opac_decode_wrbuf(mt, input_record, res);
802         if (ret != -1)
803         {
804             ret = yaz_record_conv_record_rule(p,
805                                               r->next,
806                                               wrbuf_buf(res), wrbuf_len(res),
807                                               output_record);
808         }
809         yaz_marc_destroy(mt);
810         if (cd)
811             yaz_iconv_close(cd);
812         wrbuf_destroy(res);
813     }
814     return ret;
815 }
816
817 int yaz_record_conv_record(yaz_record_conv_t p,
818                            const char *input_record_buf,
819                            size_t input_record_len,
820                            WRBUF output_record)
821 {
822     return yaz_record_conv_record_rule(p, p->rules,
823                                        input_record_buf,
824                                        input_record_len, output_record);
825 }
826
827 const char *yaz_record_conv_get_error(yaz_record_conv_t p)
828 {
829     return wrbuf_cstr(p->wr_error);
830 }
831
832 void yaz_record_conv_set_path(yaz_record_conv_t p, const char *path)
833 {
834     xfree(p->path);
835     p->path = 0;
836     if (path)
837         p->path = xstrdup(path);
838 }
839
840 yaz_record_conv_t yaz_record_conv_create()
841 {
842     yaz_record_conv_t p = (yaz_record_conv_t) xmalloc(sizeof(*p));
843     p->nmem = nmem_create();
844     p->wr_error = wrbuf_alloc();
845     p->rules = 0;
846     p->path = 0;
847     return p;
848 }
849
850 /* YAZ_HAVE_XML2 */
851 #endif
852
853 /*
854  * Local variables:
855  * c-basic-offset: 4
856  * c-file-style: "Stroustrup"
857  * indent-tabs-mode: nil
858  * End:
859  * vim: shiftwidth=4 tabstop=8 expandtab
860  */
861