Allow Content-Type application/sru+xml YAZ-840
[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
194     info->xsl_parms[2 * no_parms] = '\0';
195
196     if (!stylesheet)
197     {
198         wrbuf_printf(wr_error, "Element <xslt>: "
199                      "attribute 'stylesheet' expected");
200         nmem_destroy(nmem);
201     }
202     else
203     {
204         char fullpath[1024];
205         xsltStylesheetPtr xsp;
206         if (!yaz_filepath_resolve(stylesheet, path, 0, fullpath))
207         {
208             wrbuf_printf(wr_error, "Element <xslt stylesheet=\"%s\"/>:"
209                          " could not locate stylesheet '%s'",
210                          stylesheet, stylesheet);
211             if (path)
212                 wrbuf_printf(wr_error, " with path '%s'", path);
213
214             nmem_destroy(nmem);
215             return 0;
216         }
217         info->xsp_doc = xmlParseFile(fullpath);
218         if (!info->xsp_doc)
219         {
220             wrbuf_printf(wr_error, "Element: <xslt stylesheet=\"%s\"/>:"
221                          " xml parse failed: %s", stylesheet, fullpath);
222             if (path)
223                 wrbuf_printf(wr_error, " with path '%s'", path);
224             nmem_destroy(nmem);
225             return 0;
226         }
227         /* need to copy this before passing it to the processor. It will
228            be encapsulated in the xsp and destroyed by xsltFreeStylesheet */
229         xsp = xsltParseStylesheetDoc(xmlCopyDoc(info->xsp_doc, 1));
230         if (!xsp)
231         {
232             wrbuf_printf(wr_error, "Element: <xslt stylesheet=\"%s\"/>:"
233                          " xslt parse failed: %s", stylesheet, fullpath);
234             if (path)
235                 wrbuf_printf(wr_error, " with path '%s'", path);
236             wrbuf_printf(wr_error, " ("
237 #if YAZ_HAVE_EXSLT
238
239                          "EXSLT enabled"
240 #else
241                          "EXSLT not supported"
242 #endif
243                          ")");
244             xmlFreeDoc(info->xsp_doc);
245             nmem_destroy(info->nmem);
246         }
247         else
248         {
249             xsltFreeStylesheet(xsp);
250             return info;
251         }
252     }
253     return 0;
254 }
255
256 static int convert_xslt(void *vinfo, WRBUF record, WRBUF wr_error)
257 {
258     int ret = 0;
259     struct xslt_info *info = vinfo;
260
261     xmlDocPtr doc = xmlParseMemory(wrbuf_buf(record),
262                                    wrbuf_len(record));
263     if (!doc)
264     {
265         wrbuf_printf(wr_error, "xmlParseMemory failed");
266         ret = -1;
267     }
268     else
269     {
270         xmlDocPtr xsp_doc = xmlCopyDoc(info->xsp_doc, 1);
271         xsltStylesheetPtr xsp = xsltParseStylesheetDoc(xsp_doc);
272         xmlDocPtr res = xsltApplyStylesheet(xsp, doc, info->xsl_parms);
273         if (res)
274         {
275             xmlChar *out_buf = 0;
276             int out_len;
277
278 #if HAVE_XSLTSAVERESULTTOSTRING
279             xsltSaveResultToString(&out_buf, &out_len, res, xsp);
280 #else
281             xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1);
282 #endif
283             if (!out_buf)
284             {
285                 wrbuf_printf(wr_error,
286                              "xsltSaveResultToString failed");
287                 ret = -1;
288             }
289             else
290             {
291                 wrbuf_rewind(record);
292                 wrbuf_write(record, (const char *) out_buf, out_len);
293
294                 xmlFree(out_buf);
295             }
296             xmlFreeDoc(res);
297         }
298         else
299         {
300             wrbuf_printf(wr_error, "xsltApplyStylesheet failed");
301             ret = -1;
302         }
303         xmlFreeDoc(doc);
304         xsltFreeStylesheet(xsp); /* frees xsp_doc too */
305     }
306     return ret;
307 }
308
309 static void destroy_xslt(void *vinfo)
310 {
311     struct xslt_info *info = vinfo;
312
313     if (info)
314     {
315         xmlFreeDoc(info->xsp_doc);
316         nmem_destroy(info->nmem);
317     }
318 }
319
320 /* YAZ_HAVE_XSLT */
321 #endif
322
323 struct select_info {
324     NMEM nmem;
325     char *xpath_expr;
326 };
327
328 static void *construct_select(const xmlNode *ptr,
329                               const char *path, WRBUF wr_error)
330 {
331     if (strcmp((const char *) ptr->name, "select"))
332         return 0;
333     else
334     {
335         NMEM nmem = nmem_create();
336         struct select_info *info = nmem_malloc(nmem, sizeof(*info));
337         const char *attr_str;
338         const char *xpath = 0;
339
340         info->nmem = nmem;
341         info->xpath_expr = 0;
342         attr_str = yaz_xml_get_prop(ptr, "path%s", &xpath);
343         if (attr_str)
344         {
345             wrbuf_printf(wr_error, "Bad attribute '%s'"
346                          "Expected xpath.", attr_str);
347             nmem_destroy(nmem);
348                 return 0;
349         }
350         if (xpath)
351             info->xpath_expr = nmem_strdup(nmem, xpath);
352         return info;
353     }
354 }
355
356 static int convert_select(void *vinfo, WRBUF record, WRBUF wr_error)
357 {
358     int ret = 0;
359     struct select_info *info = vinfo;
360
361     xmlDocPtr doc = xmlParseMemory(wrbuf_buf(record),
362                                    wrbuf_len(record));
363     if (!doc)
364     {
365         wrbuf_printf(wr_error, "xmlParseMemory failed");
366         ret = -1;
367     }
368     else
369     {
370         xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
371         if (xpathCtx && info->xpath_expr)
372         {
373             xmlXPathObjectPtr xpathObj =
374                 xmlXPathEvalExpression((const xmlChar *) info->xpath_expr,
375                                        xpathCtx);
376             if (xpathObj)
377             {
378                 xmlNodeSetPtr nodes = xpathObj->nodesetval;
379                 if (nodes)
380                 {
381                     int i;
382                     if (nodes->nodeNr > 0)
383                         wrbuf_rewind(record);
384                     for (i = 0; i < nodes->nodeNr; i++)
385                     {
386                         xmlNode *ptr = nodes->nodeTab[i];
387                         if (ptr->type == XML_ELEMENT_NODE)
388                             ptr = ptr->children;
389                         for (; ptr; ptr = ptr->next)
390                             if (ptr->type == XML_TEXT_NODE)
391                                 wrbuf_puts(record, (const char *) ptr->content);
392                     }
393                 }
394                 xmlXPathFreeObject(xpathObj);
395             }
396             xmlXPathFreeContext(xpathCtx);
397         }
398         xmlFreeDoc(doc);
399     }
400     return ret;
401 }
402
403 static void destroy_select(void *vinfo)
404 {
405     struct select_info *info = vinfo;
406
407     if (info)
408         nmem_destroy(info->nmem);
409 }
410
411
412 static void *construct_solrmarc(const xmlNode *ptr,
413                                 const char *path, WRBUF wr_error)
414 {
415     if (strcmp((const char *) ptr->name, "solrmarc"))
416         return 0;
417     return wr_error; /* any non-null ptr will do; we don't use it later*/
418 }
419
420 static int convert_solrmarc(void *info, WRBUF record, WRBUF wr_error)
421 {
422     WRBUF w = wrbuf_alloc();
423     const char *buf = wrbuf_buf(record);
424     size_t i, sz = wrbuf_len(record);
425     for (i = 0; i < sz; i++)
426     {
427         int ch;
428         if (buf[i] == '#' && i < sz - 3 && buf[i+3] == ';'
429             && atoi_n_check(buf+i+1, 2, &ch))
430             i += 3;
431         else
432             ch = buf[i];
433         wrbuf_putc(w, ch);
434     }
435     wrbuf_rewind(record);
436     wrbuf_write(record, wrbuf_buf(w), wrbuf_len(w));
437     wrbuf_destroy(w);
438     return 0;
439 }
440
441 static void destroy_solrmarc(void *info)
442 {
443 }
444
445 static void *construct_marc(const xmlNode *ptr,
446                             const char *path, WRBUF wr_error)
447 {
448     NMEM nmem = nmem_create();
449     struct marc_info *info = nmem_malloc(nmem, sizeof(*info));
450     struct _xmlAttr *attr;
451     const char *input_format = 0;
452     const char *output_format = 0;
453
454     if (strcmp((const char *) ptr->name, "marc"))
455     {
456         nmem_destroy(nmem);
457         return 0;
458     }
459     info->nmem = nmem;
460     info->input_charset = 0;
461     info->output_charset = 0;
462     info->input_format_mode = 0;
463     info->output_format_mode = 0;
464     info->leader_spec = 0;
465
466     for (attr = ptr->properties; attr; attr = attr->next)
467     {
468         if (!xmlStrcmp(attr->name, BAD_CAST "inputcharset") &&
469             attr->children && attr->children->type == XML_TEXT_NODE)
470             info->input_charset = (const char *) attr->children->content;
471         else if (!xmlStrcmp(attr->name, BAD_CAST "outputcharset") &&
472             attr->children && attr->children->type == XML_TEXT_NODE)
473             info->output_charset = (const char *) attr->children->content;
474         else if (!xmlStrcmp(attr->name, BAD_CAST "inputformat") &&
475             attr->children && attr->children->type == XML_TEXT_NODE)
476             input_format = (const char *) attr->children->content;
477         else if (!xmlStrcmp(attr->name, BAD_CAST "outputformat") &&
478             attr->children && attr->children->type == XML_TEXT_NODE)
479             output_format = (const char *) attr->children->content;
480         else if (!xmlStrcmp(attr->name, BAD_CAST "leaderspec") &&
481                  attr->children && attr->children->type == XML_TEXT_NODE)
482             info->leader_spec =
483                 nmem_strdup(info->nmem,(const char *) attr->children->content);
484         else
485         {
486             wrbuf_printf(wr_error, "Element <marc>: expected attributes"
487                          "'inputformat', 'inputcharset', 'outputformat' or"
488                          " 'outputcharset', got attribute '%s'",
489                          attr->name);
490             nmem_destroy(info->nmem);
491             return 0;
492         }
493     }
494     if (!input_format)
495     {
496         wrbuf_printf(wr_error, "Element <marc>: "
497                      "attribute 'inputformat' required");
498         nmem_destroy(info->nmem);
499         return 0;
500     }
501     else if (!strcmp(input_format, "marc"))
502     {
503         info->input_format_mode = YAZ_MARC_ISO2709;
504     }
505     else if (!strcmp(input_format, "xml"))
506     {
507         info->input_format_mode = YAZ_MARC_MARCXML;
508         /** Libxml2 generates UTF-8 encoding by default .
509             So we convert from UTF-8 to outputcharset (if defined)
510         */
511         if (!info->input_charset && info->output_charset)
512             info->input_charset = "utf-8";
513     }
514     else if (!strcmp(input_format, "json"))
515     {
516         info->input_format_mode = YAZ_MARC_JSON;
517     }
518     else
519     {
520         wrbuf_printf(wr_error, "Element <marc inputformat='%s'>: "
521                      " Unsupported input format"
522                      " defined by attribute value",
523                      input_format);
524         nmem_destroy(info->nmem);
525         return 0;
526     }
527
528     if (!output_format)
529     {
530         wrbuf_printf(wr_error,
531                      "Element <marc>: attribute 'outputformat' required");
532         nmem_destroy(info->nmem);
533         return 0;
534     }
535     else if (!strcmp(output_format, "line"))
536     {
537         info->output_format_mode = YAZ_MARC_LINE;
538     }
539     else if (!strcmp(output_format, "marcxml"))
540     {
541         info->output_format_mode = YAZ_MARC_MARCXML;
542         if (info->input_charset && !info->output_charset)
543             info->output_charset = "utf-8";
544     }
545     else if (!strcmp(output_format, "turbomarc"))
546     {
547         info->output_format_mode = YAZ_MARC_TURBOMARC;
548         if (info->input_charset && !info->output_charset)
549             info->output_charset = "utf-8";
550     }
551     else if (!strcmp(output_format, "marc"))
552     {
553         info->output_format_mode = YAZ_MARC_ISO2709;
554     }
555     else if (!strcmp(output_format, "marcxchange"))
556     {
557         info->output_format_mode = YAZ_MARC_XCHANGE;
558         if (info->input_charset && !info->output_charset)
559             info->output_charset = "utf-8";
560     }
561     else if (!strcmp(output_format, "json"))
562     {
563         info->output_format_mode = YAZ_MARC_JSON;
564         if (info->input_charset && !info->output_charset)
565             info->output_charset = "utf-8";
566     }
567     else
568     {
569         wrbuf_printf(wr_error, "Element <marc outputformat='%s'>: "
570                      " Unsupported output format"
571                      " defined by attribute value",
572                      output_format);
573         nmem_destroy(info->nmem);
574         return 0;
575     }
576     if (info->input_charset && info->output_charset)
577     {
578         yaz_iconv_t cd = yaz_iconv_open(info->output_charset,
579                                         info->input_charset);
580         if (!cd)
581         {
582             wrbuf_printf(wr_error,
583                          "Element <marc inputcharset='%s' outputcharset='%s'>:"
584                          " Unsupported character set mapping"
585                          " defined by attribute values",
586                          info->input_charset, info->output_charset);
587             nmem_destroy(info->nmem);
588             return 0;
589         }
590         yaz_iconv_close(cd);
591     }
592     else if (!info->output_charset)
593     {
594         wrbuf_printf(wr_error, "Element <marc>: "
595                      "attribute 'outputcharset' missing");
596         nmem_destroy(info->nmem);
597         return 0;
598     }
599     else if (!info->input_charset)
600     {
601         wrbuf_printf(wr_error, "Element <marc>: "
602                      "attribute 'inputcharset' missing");
603         nmem_destroy(info->nmem);
604         return 0;
605     }
606     info->input_charset = nmem_strdup(info->nmem, info->input_charset);
607     info->output_charset = nmem_strdup(info->nmem, info->output_charset);
608     return info;
609 }
610
611 static int convert_marc(void *info, WRBUF record, WRBUF wr_error)
612 {
613     struct marc_info *mi = info;
614     const char *input_charset = mi->input_charset;
615     int ret = 0;
616     yaz_marc_t mt = yaz_marc_create();
617
618     yaz_marc_xml(mt, mi->output_format_mode);
619     if (mi->leader_spec)
620         yaz_marc_leader_spec(mt, mi->leader_spec);
621
622     if (mi->input_format_mode == YAZ_MARC_ISO2709)
623     {
624         int sz = yaz_marc_read_iso2709(mt, wrbuf_buf(record),
625                                        wrbuf_len(record));
626         if (sz > 0)
627         {
628             if (yaz_marc_check_marc21_coding(input_charset, wrbuf_buf(record),
629                                              wrbuf_len(record)))
630                 input_charset = "utf-8";
631             ret = 0;
632         }
633         else
634             ret = -1;
635     }
636     else if (mi->input_format_mode == YAZ_MARC_MARCXML ||
637              mi->input_format_mode == YAZ_MARC_TURBOMARC)
638     {
639         xmlDocPtr doc = xmlParseMemory(wrbuf_buf(record),
640                                        wrbuf_len(record));
641         if (!doc)
642         {
643             wrbuf_printf(wr_error, "xmlParseMemory failed");
644             ret = -1;
645         }
646         else
647         {
648             ret = yaz_marc_read_xml(mt, xmlDocGetRootElement(doc));
649             if (ret)
650                 wrbuf_printf(wr_error, "yaz_marc_read_xml failed");
651         }
652         xmlFreeDoc(doc);
653     }
654     else
655     {
656         wrbuf_printf(wr_error, "unsupported input format");
657         ret = -1;
658     }
659     if (ret == 0)
660     {
661         yaz_iconv_t cd = yaz_iconv_open(mi->output_charset, input_charset);
662
663         if (cd)
664             yaz_marc_iconv(mt, cd);
665
666         wrbuf_rewind(record);
667         ret = yaz_marc_write_mode(mt, record);
668         if (ret)
669             wrbuf_printf(wr_error, "yaz_marc_write_mode failed");
670         if (cd)
671             yaz_iconv_close(cd);
672     }
673     yaz_marc_destroy(mt);
674     return ret;
675 }
676
677 static void destroy_marc(void *info)
678 {
679     struct marc_info *mi = info;
680
681     nmem_destroy(mi->nmem);
682 }
683
684 int yaz_record_conv_configure_t(yaz_record_conv_t p, const xmlNode *ptr,
685                                 struct yaz_record_conv_type *types)
686 {
687     struct yaz_record_conv_type bt[4];
688     size_t i = 0;
689
690     /* register marc */
691     bt[i].construct = construct_marc;
692     bt[i].convert = convert_marc;
693     bt[i++].destroy = destroy_marc;
694
695     bt[i-1].next = &bt[i];
696     bt[i].construct = construct_solrmarc;
697     bt[i].convert = convert_solrmarc;
698     bt[i++].destroy = destroy_solrmarc;
699
700     bt[i-1].next = &bt[i];
701     bt[i].construct = construct_select;
702     bt[i].convert = convert_select;
703     bt[i++].destroy = destroy_select;
704
705 #if YAZ_HAVE_XSLT
706     /* register xslt */
707     bt[i-1].next = &bt[i];
708     bt[i].construct = construct_xslt;
709     bt[i].convert = convert_xslt;
710     bt[i++].destroy = destroy_xslt;
711 #endif
712
713     bt[i-1].next = types;
714     yaz_record_conv_reset(p);
715
716     /* parsing element children */
717     for (ptr = ptr->children; ptr; ptr = ptr->next)
718     {
719         struct yaz_record_conv_type *t;
720         struct yaz_record_conv_rule *r;
721         void *info = 0;
722         if (ptr->type != XML_ELEMENT_NODE)
723             continue;
724         for (t = &bt[0]; t; t = t->next)
725         {
726             wrbuf_rewind(p->wr_error);
727             info = t->construct(ptr, p->path, p->wr_error);
728
729             if (info || wrbuf_len(p->wr_error))
730                 break;
731             /* info== 0 and no error reported , ie not handled by it */
732         }
733         if (!info)
734         {
735             if (wrbuf_len(p->wr_error) == 0)
736                 wrbuf_printf(p->wr_error, "Element <backend>: expected "
737                              "<marc> or <xslt> element, got <%s>"
738                              , ptr->name);
739             return -1;
740         }
741         r = (struct yaz_record_conv_rule *) nmem_malloc(p->nmem, sizeof(*r));
742         r->next = 0;
743         r->info = info;
744         r->type = nmem_malloc(p->nmem, sizeof(*t));
745         memcpy(r->type, t, sizeof(*t));
746         *p->rules_p = r;
747         p->rules_p = &r->next;
748     }
749     return 0;
750 }
751
752 int yaz_record_conv_configure(yaz_record_conv_t p, const xmlNode *ptr)
753 {
754     return yaz_record_conv_configure_t(p, ptr, 0);
755 }
756
757 static int yaz_record_conv_record_rule(yaz_record_conv_t p,
758                                        struct yaz_record_conv_rule *r,
759                                        const char *input_record_buf,
760                                        size_t input_record_len,
761                                        WRBUF output_record)
762 {
763     int ret = 0;
764     WRBUF record = output_record; /* pointer transfer */
765     wrbuf_rewind(p->wr_error);
766
767     wrbuf_write(record, input_record_buf, input_record_len);
768     for (; ret == 0 && r; r = r->next)
769         ret = r->type->convert(r->info, record, p->wr_error);
770     return ret;
771 }
772
773 int yaz_record_conv_opac_record(yaz_record_conv_t p,
774                                 Z_OPACRecord *input_record,
775                                 WRBUF output_record)
776 {
777     int ret = 0;
778     struct yaz_record_conv_rule *r = p->rules;
779     if (!r || r->type->construct != construct_marc)
780     {
781         wrbuf_puts(p->wr_error, "Expecting MARC rule as first rule for OPAC");
782         ret = -1; /* no marc rule so we can't do OPAC */
783     }
784     else
785     {
786         struct marc_info *mi = r->info;
787         const char *input_charset = mi->input_charset;
788         yaz_iconv_t cd;
789
790         WRBUF res = wrbuf_alloc();
791         yaz_marc_t mt = yaz_marc_create();
792
793         if (yaz_opac_check_marc21_coding(input_charset, input_record))
794             input_charset = "utf-8";
795         cd = yaz_iconv_open(mi->output_charset, input_charset);
796
797         wrbuf_rewind(p->wr_error);
798         yaz_marc_xml(mt, mi->output_format_mode);
799
800         yaz_marc_iconv(mt, cd);
801
802         yaz_opac_decode_wrbuf(mt, input_record, res);
803         if (ret != -1)
804         {
805             ret = yaz_record_conv_record_rule(p,
806                                               r->next,
807                                               wrbuf_buf(res), wrbuf_len(res),
808                                               output_record);
809         }
810         yaz_marc_destroy(mt);
811         if (cd)
812             yaz_iconv_close(cd);
813         wrbuf_destroy(res);
814     }
815     return ret;
816 }
817
818 int yaz_record_conv_record(yaz_record_conv_t p,
819                            const char *input_record_buf,
820                            size_t input_record_len,
821                            WRBUF output_record)
822 {
823     return yaz_record_conv_record_rule(p, p->rules,
824                                        input_record_buf,
825                                        input_record_len, output_record);
826 }
827
828 const char *yaz_record_conv_get_error(yaz_record_conv_t p)
829 {
830     return wrbuf_cstr(p->wr_error);
831 }
832
833 void yaz_record_conv_set_path(yaz_record_conv_t p, const char *path)
834 {
835     xfree(p->path);
836     p->path = 0;
837     if (path)
838         p->path = xstrdup(path);
839 }
840
841 yaz_record_conv_t yaz_record_conv_create()
842 {
843     yaz_record_conv_t p = (yaz_record_conv_t) xmalloc(sizeof(*p));
844     p->nmem = nmem_create();
845     p->wr_error = wrbuf_alloc();
846     p->rules = 0;
847     p->path = 0;
848     return p;
849 }
850
851 /* YAZ_HAVE_XML2 */
852 #endif
853
854 /*
855  * Local variables:
856  * c-basic-offset: 4
857  * c-file-style: "Stroustrup"
858  * indent-tabs-mode: nil
859  * End:
860  * vim: shiftwidth=4 tabstop=8 expandtab
861  */
862