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