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