Fixed memory alloction errors. Added new function for turbo marcxml format. Handling...
[yaz-moved-to-github.git] / src / marcdisp.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2010 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /**
7  * \file marcdisp.c
8  * \brief Implements MARC conversion utilities
9  */
10
11 #if HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14
15 #ifdef WIN32
16 #include <windows.h>
17 #endif
18
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <yaz/marcdisp.h>
25 #include <yaz/wrbuf.h>
26 #include <yaz/yaz-util.h>
27 #include <yaz/nmem_xml.h>
28 #include <yaz/snprintf.h>
29
30 #if YAZ_HAVE_XML2
31 #include <libxml/parser.h>
32 #include <libxml/tree.h>
33 #endif
34
35 enum yaz_collection_state {
36     no_collection,
37     collection_first,
38     collection_second
39 };
40    
41 /** \brief node types for yaz_marc_node */
42 enum YAZ_MARC_NODE_TYPE
43
44     YAZ_MARC_DATAFIELD,
45     YAZ_MARC_CONTROLFIELD,
46     YAZ_MARC_COMMENT,
47     YAZ_MARC_LEADER
48 };
49
50 /** \brief represets a data field */
51 struct yaz_marc_datafield {
52     char *tag;
53     char *indicator;
54     struct yaz_marc_subfield *subfields;
55 };
56
57 /** \brief represents a control field */
58 struct yaz_marc_controlfield {
59     char *tag;
60     char *data;
61 };
62
63 /** \brief a comment node */
64 struct yaz_marc_comment {
65     char *comment;
66 };
67
68 /** \brief MARC node */
69 struct yaz_marc_node {
70     enum YAZ_MARC_NODE_TYPE which;
71     union {
72         struct yaz_marc_datafield datafield;
73         struct yaz_marc_controlfield controlfield;
74         char *comment;
75         char *leader;
76     } u;
77     struct yaz_marc_node *next;
78 };
79
80 /** \brief represents a subfield */
81 struct yaz_marc_subfield {
82     char *code_data;
83     struct yaz_marc_subfield *next;
84 };
85
86 /** \brief the internals of a yaz_marc_t handle */
87 struct yaz_marc_t_ {
88     WRBUF m_wr;
89     NMEM nmem;
90     int input_format;
91     int output_format;
92     int debug;
93     int write_using_libxml2;
94     int turbo_format;
95     enum yaz_collection_state enable_collection;
96     yaz_iconv_t iconv_cd;
97     char subfield_str[8];
98     char endline_str[8];
99     char *leader_spec;
100     struct yaz_marc_node *nodes;
101     struct yaz_marc_node **nodes_pp;
102     struct yaz_marc_subfield **subfield_pp;
103 };
104
105 yaz_marc_t yaz_marc_create(void)
106 {
107     yaz_marc_t mt = (yaz_marc_t) xmalloc(sizeof(*mt));
108     mt->output_format = YAZ_MARC_LINE;
109     mt->debug = 0;
110     mt->write_using_libxml2 = 0;
111     mt->enable_collection = no_collection;
112     mt->m_wr = wrbuf_alloc();
113     mt->iconv_cd = 0;
114     mt->leader_spec = 0;
115     strcpy(mt->subfield_str, " $");
116     strcpy(mt->endline_str, "\n");
117
118     mt->nmem = nmem_create();
119     yaz_marc_reset(mt);
120     return mt;
121 }
122
123 void yaz_marc_destroy(yaz_marc_t mt)
124 {
125     if (!mt)
126         return ;
127     nmem_destroy(mt->nmem);
128     wrbuf_destroy(mt->m_wr);
129     xfree(mt->leader_spec);
130     xfree(mt);
131 }
132
133 NMEM yaz_marc_get_nmem(yaz_marc_t mt)
134 {
135     return mt->nmem;
136 }
137
138 static void marc_iconv_reset(yaz_marc_t mt, WRBUF wr)
139 {
140     wrbuf_iconv_reset(wr, mt->iconv_cd);
141 }
142
143 static int marc_exec_leader(const char *leader_spec, char *leader,
144                             size_t size);
145
146
147 static struct yaz_marc_node *yaz_marc_add_node(yaz_marc_t mt)
148 {
149     struct yaz_marc_node *n = (struct yaz_marc_node *)
150         nmem_malloc(mt->nmem, sizeof(*n));
151     n->next = 0;
152     *mt->nodes_pp = n;
153     mt->nodes_pp = &n->next;
154     return n;
155 }
156
157 #if YAZ_HAVE_XML2
158 void yaz_marc_add_controlfield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
159                                    const xmlNode *ptr_data)
160 {
161     struct yaz_marc_node *n = yaz_marc_add_node(mt);
162     n->which = YAZ_MARC_CONTROLFIELD;
163     n->u.controlfield.tag = nmem_text_node_cdata(ptr_tag, mt->nmem);
164     n->u.controlfield.data = nmem_text_node_cdata(ptr_data, mt->nmem);
165 }
166
167 void yaz_marc_add_controlfield_turbo_xml(yaz_marc_t mt, const char *tag,
168                                    const xmlNode *ptr_data)
169 {
170     struct yaz_marc_node *n = yaz_marc_add_node(mt);
171     n->which = YAZ_MARC_CONTROLFIELD;
172     n->u.controlfield.tag = tag;
173     n->u.controlfield.data = nmem_text_node_cdata(ptr_data, mt->nmem);
174 }
175
176 #endif
177
178
179 void yaz_marc_add_comment(yaz_marc_t mt, char *comment)
180 {
181     struct yaz_marc_node *n = yaz_marc_add_node(mt);
182     n->which = YAZ_MARC_COMMENT;
183     n->u.comment = nmem_strdup(mt->nmem, comment);
184 }
185
186 void yaz_marc_cprintf(yaz_marc_t mt, const char *fmt, ...)
187 {
188     va_list ap;
189     char buf[200];
190
191     va_start(ap, fmt);
192     yaz_vsnprintf(buf, sizeof(buf)-1, fmt, ap);
193     yaz_marc_add_comment(mt, buf);
194     va_end (ap);
195 }
196
197 int yaz_marc_get_debug(yaz_marc_t mt)
198 {
199     return mt->debug;
200 }
201
202 void yaz_marc_add_leader(yaz_marc_t mt, const char *leader, size_t leader_len)
203 {
204     struct yaz_marc_node *n = yaz_marc_add_node(mt);
205     n->which = YAZ_MARC_LEADER;
206     n->u.leader = nmem_strdupn(mt->nmem, leader, leader_len);
207     marc_exec_leader(mt->leader_spec, n->u.leader, leader_len);
208 }
209
210 void yaz_marc_add_controlfield(yaz_marc_t mt, const char *tag,
211                                const char *data, size_t data_len)
212 {
213     struct yaz_marc_node *n = yaz_marc_add_node(mt);
214     n->which = YAZ_MARC_CONTROLFIELD;
215     n->u.controlfield.tag = nmem_strdup(mt->nmem, tag);
216     n->u.controlfield.data = nmem_strdupn(mt->nmem, data, data_len);
217     if (mt->debug)
218     {
219         size_t i;
220         char msg[80];
221
222         sprintf(msg, "controlfield:");
223         for (i = 0; i < 16 && i < data_len; i++)
224             sprintf(msg + strlen(msg), " %02X", data[i] & 0xff);
225         if (i < data_len)
226             sprintf(msg + strlen(msg), " ..");
227         yaz_marc_add_comment(mt, msg);
228     }
229 }
230
231 void yaz_marc_add_datafield(yaz_marc_t mt, const char *tag,
232                             const char *indicator, size_t indicator_len)
233 {
234     struct yaz_marc_node *n = yaz_marc_add_node(mt);
235     n->which = YAZ_MARC_DATAFIELD;
236     n->u.datafield.tag = nmem_strdup(mt->nmem, tag);
237     n->u.datafield.indicator =
238         nmem_strdupn(mt->nmem, indicator, indicator_len);
239     n->u.datafield.subfields = 0;
240
241     /* make subfield_pp the current (last one) */
242     mt->subfield_pp = &n->u.datafield.subfields;
243 }
244
245 #if YAZ_HAVE_XML2
246 void yaz_marc_add_datafield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
247                                 const char *indicator, size_t indicator_len)
248 {
249     struct yaz_marc_node *n = yaz_marc_add_node(mt);
250     n->which = YAZ_MARC_DATAFIELD;
251     n->u.datafield.tag = nmem_text_node_cdata(ptr_tag, mt->nmem);
252     n->u.datafield.indicator =
253         nmem_strdupn(mt->nmem, indicator, indicator_len);
254     n->u.datafield.subfields = 0;
255
256     /* make subfield_pp the current (last one) */
257     mt->subfield_pp = &n->u.datafield.subfields;
258 }
259
260 struct yaz_marc_node* yaz_marc_add_datafield_turbo_xml(yaz_marc_t mt, char *tag_value)
261 {
262     struct yaz_marc_node *n = yaz_marc_add_node(mt);
263     n->which = YAZ_MARC_DATAFIELD;
264     n->u.datafield.tag = tag_value;
265     n->u.datafield.indicator = 0;
266     n->u.datafield.subfields = 0;
267
268     /* make subfield_pp the current (last one) */
269     mt->subfield_pp = &n->u.datafield.subfields;
270     return n;
271 }
272
273 void yaz_marc_datafield_set_indicators(struct yaz_marc_node *n, char *indicator)
274 {
275     n->u.datafield.indicator = indicator;
276 }
277
278 #endif
279
280 void yaz_marc_add_subfield(yaz_marc_t mt,
281                            const char *code_data, size_t code_data_len)
282 {
283     if (mt->debug)
284     {
285         size_t i;
286         char msg[80];
287
288         sprintf(msg, "subfield:");
289         for (i = 0; i < 16 && i < code_data_len; i++)
290             sprintf(msg + strlen(msg), " %02X", code_data[i] & 0xff);
291         if (i < code_data_len)
292             sprintf(msg + strlen(msg), " ..");
293         yaz_marc_add_comment(mt, msg);
294     }
295
296     if (mt->subfield_pp)
297     {
298         struct yaz_marc_subfield *n = (struct yaz_marc_subfield *)
299             nmem_malloc(mt->nmem, sizeof(*n));
300         n->code_data = nmem_strdupn(mt->nmem, code_data, code_data_len);
301         n->next = 0;
302         /* mark subfield_pp to point to this one, so we append here next */
303         *mt->subfield_pp = n;
304         mt->subfield_pp = &n->next;
305     }
306 }
307
308 void yaz_marc_set_leader(yaz_marc_t mt, const char *leader_c,
309                          int *indicator_length,
310                          int *identifier_length,
311                          int *base_address,
312                          int *length_data_entry,
313                          int *length_starting,
314                          int *length_implementation)
315 {
316     char leader[24];
317
318     memcpy(leader, leader_c, 24);
319
320     if (!atoi_n_check(leader+10, 1, indicator_length))
321     {
322         yaz_marc_cprintf(mt, 
323                          "Indicator length at offset 10 should hold a digit."
324                          " Assuming 2");
325         leader[10] = '2';
326         *indicator_length = 2;
327     }
328     if (!atoi_n_check(leader+11, 1, identifier_length))
329     {
330         yaz_marc_cprintf(mt, 
331                          "Identifier length at offset 11 should hold a digit."
332                          " Assuming 2");
333         leader[11] = '2';
334         *identifier_length = 2;
335     }
336     if (!atoi_n_check(leader+12, 5, base_address))
337     {
338         yaz_marc_cprintf(mt, 
339                          "Base address at offsets 12..16 should hold a number."
340                          " Assuming 0");
341         *base_address = 0;
342     }
343     if (!atoi_n_check(leader+20, 1, length_data_entry))
344     {
345         yaz_marc_cprintf(mt, 
346                          "Length data entry at offset 20 should hold a digit."
347                          " Assuming 4");
348         *length_data_entry = 4;
349         leader[20] = '4';
350     }
351     if (!atoi_n_check(leader+21, 1, length_starting))
352     {
353         yaz_marc_cprintf(mt,
354                          "Length starting at offset 21 should hold a digit."
355                          " Assuming 5");
356         *length_starting = 5;
357         leader[21] = '5';
358     }
359     if (!atoi_n_check(leader+22, 1, length_implementation))
360     {
361         yaz_marc_cprintf(mt, 
362                          "Length implementation at offset 22 should hold a digit."
363                          " Assuming 0");
364         *length_implementation = 0;
365         leader[22] = '0';
366     }
367
368     if (mt->debug)
369     {
370         yaz_marc_cprintf(mt, "Indicator length      %5d", *indicator_length);
371         yaz_marc_cprintf(mt, "Identifier length     %5d", *identifier_length);
372         yaz_marc_cprintf(mt, "Base address          %5d", *base_address);
373         yaz_marc_cprintf(mt, "Length data entry     %5d", *length_data_entry);
374         yaz_marc_cprintf(mt, "Length starting       %5d", *length_starting);
375         yaz_marc_cprintf(mt, "Length implementation %5d", *length_implementation);
376     }
377     yaz_marc_add_leader(mt, leader, 24);
378 }
379
380 void yaz_marc_subfield_str(yaz_marc_t mt, const char *s)
381 {
382     strncpy(mt->subfield_str, s, sizeof(mt->subfield_str)-1);
383     mt->subfield_str[sizeof(mt->subfield_str)-1] = '\0';
384 }
385
386 void yaz_marc_endline_str(yaz_marc_t mt, const char *s)
387 {
388     strncpy(mt->endline_str, s, sizeof(mt->endline_str)-1);
389     mt->endline_str[sizeof(mt->endline_str)-1] = '\0';
390 }
391
392 /* try to guess how many bytes the identifier really is! */
393 static size_t cdata_one_character(yaz_marc_t mt, const char *buf)
394 {
395     if (mt->iconv_cd)
396     {
397         size_t i;
398         for (i = 1; i<5; i++)
399         {
400             char outbuf[12];
401             size_t outbytesleft = sizeof(outbuf);
402             char *outp = outbuf;
403             const char *inp = buf;
404
405             size_t inbytesleft = i;
406             size_t r = yaz_iconv(mt->iconv_cd, (char**) &inp, &inbytesleft,
407                                  &outp, &outbytesleft);
408             if (r != (size_t) (-1))
409                 return i;  /* got a complete sequence */
410         }
411         return 1; /* giving up */
412     }
413     return 1; /* we don't know */
414 }
415                               
416 void yaz_marc_reset(yaz_marc_t mt)
417 {
418     nmem_reset(mt->nmem);
419     mt->nodes = 0;
420     mt->nodes_pp = &mt->nodes;
421     mt->subfield_pp = 0;
422 }
423
424 int yaz_marc_write_check(yaz_marc_t mt, WRBUF wr)
425 {
426     struct yaz_marc_node *n;
427     int identifier_length;
428     const char *leader = 0;
429
430     for (n = mt->nodes; n; n = n->next)
431         if (n->which == YAZ_MARC_LEADER)
432         {
433             leader = n->u.leader;
434             break;
435         }
436     
437     if (!leader)
438         return -1;
439     if (!atoi_n_check(leader+11, 1, &identifier_length))
440         return -1;
441
442     for (n = mt->nodes; n; n = n->next)
443     {
444         switch(n->which)
445         {
446         case YAZ_MARC_COMMENT:
447             wrbuf_iconv_write(wr, mt->iconv_cd, 
448                               n->u.comment, strlen(n->u.comment));
449             wrbuf_puts(wr, "\n");
450             break;
451         default:
452             break;
453         }
454     }
455     return 0;
456 }
457
458 static size_t get_subfield_len(yaz_marc_t mt, const char *data,
459                                int identifier_length)
460 {
461     /* if identifier length is 2 (most MARCs) or less (probably an error),
462        the code is a single character .. However we've
463        seen multibyte codes, so see how big it really is */
464     if (identifier_length > 2)
465         return identifier_length - 1;
466     else
467         return cdata_one_character(mt, data);
468 }
469
470 int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
471 {
472     struct yaz_marc_node *n;
473     int identifier_length;
474     const char *leader = 0;
475
476     for (n = mt->nodes; n; n = n->next)
477         if (n->which == YAZ_MARC_LEADER)
478         {
479             leader = n->u.leader;
480             break;
481         }
482     
483     if (!leader)
484         return -1;
485     if (!atoi_n_check(leader+11, 1, &identifier_length))
486         return -1;
487
488     for (n = mt->nodes; n; n = n->next)
489     {
490         struct yaz_marc_subfield *s;
491         switch(n->which)
492         {
493         case YAZ_MARC_DATAFIELD:
494             wrbuf_printf(wr, "%s %s", n->u.datafield.tag,
495                          n->u.datafield.indicator);
496             for (s = n->u.datafield.subfields; s; s = s->next)
497             {
498                 size_t using_code_len = get_subfield_len(mt, s->code_data,
499                                                          identifier_length);
500                 
501                 wrbuf_puts (wr, mt->subfield_str); 
502                 wrbuf_iconv_write(wr, mt->iconv_cd, s->code_data, 
503                                   using_code_len);
504                 wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
505                 wrbuf_iconv_puts(wr, mt->iconv_cd, 
506                                  s->code_data + using_code_len);
507                 marc_iconv_reset(mt, wr);
508             }
509             wrbuf_puts (wr, mt->endline_str);
510             break;
511         case YAZ_MARC_CONTROLFIELD:
512             wrbuf_printf(wr, "%s", n->u.controlfield.tag);
513             wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
514             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
515             marc_iconv_reset(mt, wr);
516             wrbuf_puts (wr, mt->endline_str);
517             break;
518         case YAZ_MARC_COMMENT:
519             wrbuf_puts(wr, "(");
520             wrbuf_iconv_write(wr, mt->iconv_cd, 
521                               n->u.comment, strlen(n->u.comment));
522             marc_iconv_reset(mt, wr);
523             wrbuf_puts(wr, ")\n");
524             break;
525         case YAZ_MARC_LEADER:
526             wrbuf_printf(wr, "%s\n", n->u.leader);
527         }
528     }
529     wrbuf_puts(wr, "\n");
530     return 0;
531 }
532
533 int yaz_marc_write_trailer(yaz_marc_t mt, WRBUF wr)
534 {
535     if (mt->enable_collection == collection_second)
536     {
537         switch(mt->output_format)
538         {
539         case YAZ_MARC_MARCXML:
540         case YAZ_MARC_TMARCXML:
541             wrbuf_printf(wr, "</collection>\n");
542             break;
543         case YAZ_MARC_XCHANGE:
544             wrbuf_printf(wr, "</collection>\n");
545             break;
546         }
547     }
548     return 0;
549 }
550
551 void yaz_marc_enable_collection(yaz_marc_t mt)
552 {
553     mt->enable_collection = collection_first;
554 }
555
556 int yaz_marc_write_mode(yaz_marc_t mt, WRBUF wr)
557 {
558     switch(mt->output_format)
559     {
560     case YAZ_MARC_LINE:
561         return yaz_marc_write_line(mt, wr);
562     case YAZ_MARC_MARCXML:
563     case YAZ_MARC_TMARCXML:
564         return yaz_marc_write_marcxml(mt, wr);
565     case YAZ_MARC_XCHANGE:
566         return yaz_marc_write_marcxchange(mt, wr, 0, 0); /* no format, type */
567     case YAZ_MARC_ISO2709:
568         return yaz_marc_write_iso2709(mt, wr);
569     case YAZ_MARC_CHECK:
570         return yaz_marc_write_check(mt, wr);
571     }
572     return -1;
573 }
574
575 /** \brief common MARC XML/Xchange writer
576     \param mt handle
577     \param wr WRBUF output
578     \param ns XMLNS for the elements
579     \param format record format (e.g. "MARC21")
580     \param type record type (e.g. "Bibliographic")
581 */
582 static int yaz_marc_write_marcxml_ns1(yaz_marc_t mt, WRBUF wr,
583                                       const char *ns, 
584                                       const char *format,
585                                       const char *type)
586 {
587     struct yaz_marc_node *n;
588     int identifier_length;
589     const char *leader = 0;
590
591     for (n = mt->nodes; n; n = n->next)
592         if (n->which == YAZ_MARC_LEADER)
593         {
594             leader = n->u.leader;
595             break;
596         }
597     
598     if (!leader)
599         return -1;
600     if (!atoi_n_check(leader+11, 1, &identifier_length))
601         return -1;
602     
603     if (mt->enable_collection != no_collection)
604     {
605         if (mt->enable_collection == collection_first)
606             wrbuf_printf(wr, "<collection xmlns=\"%s\">\n", ns);
607         mt->enable_collection = collection_second;
608         wrbuf_printf(wr, "<record");
609     }
610     else
611     {
612         wrbuf_printf(wr, "<record xmlns=\"%s\"", ns);
613     }
614     if (format)
615         wrbuf_printf(wr, " format=\"%.80s\"", format);
616     if (type)
617         wrbuf_printf(wr, " type=\"%.80s\"", type);
618     wrbuf_printf(wr, ">\n");
619     for (n = mt->nodes; n; n = n->next)
620     {
621         struct yaz_marc_subfield *s;
622
623         switch(n->which)
624         {
625         case YAZ_MARC_DATAFIELD:
626             wrbuf_printf(wr, "  <datafield tag=\"");
627             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
628                                     strlen(n->u.datafield.tag));
629             wrbuf_printf(wr, "\"");
630             if (n->u.datafield.indicator)
631             {
632                 int i;
633                 for (i = 0; n->u.datafield.indicator[i]; i++)
634                 {
635                     wrbuf_printf(wr, " ind%d=\"", i+1);
636                     wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
637                                           n->u.datafield.indicator+i, 1);
638                     wrbuf_iconv_puts(wr, mt->iconv_cd, "\"");
639                 }
640             }
641             wrbuf_printf(wr, ">\n");
642             for (s = n->u.datafield.subfields; s; s = s->next)
643             {
644                 size_t using_code_len = get_subfield_len(mt, s->code_data,
645                                                          identifier_length);
646                 wrbuf_iconv_puts(wr, mt->iconv_cd, "    <subfield code=\"");
647                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
648                                         s->code_data, using_code_len);
649                 wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
650                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
651                                         s->code_data + using_code_len,
652                                         strlen(s->code_data + using_code_len));
653                 marc_iconv_reset(mt, wr);
654                 wrbuf_iconv_puts(wr, mt->iconv_cd, "</subfield>");
655                 wrbuf_puts(wr, "\n");
656             }
657             wrbuf_printf(wr, "  </datafield>\n");
658             break;
659         case YAZ_MARC_CONTROLFIELD:
660             wrbuf_printf(wr, "  <controlfield tag=\"");
661             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
662                                     strlen(n->u.controlfield.tag));
663             wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
664             wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
665                                     n->u.controlfield.data,
666                                     strlen(n->u.controlfield.data));
667
668             marc_iconv_reset(mt, wr);
669             wrbuf_iconv_puts(wr, mt->iconv_cd, "</controlfield>");
670             wrbuf_puts(wr, "\n");
671             break;
672         case YAZ_MARC_COMMENT:
673             wrbuf_printf(wr, "<!-- ");
674             wrbuf_puts(wr, n->u.comment);
675             wrbuf_printf(wr, " -->\n");
676             break;
677         case YAZ_MARC_LEADER:
678             wrbuf_printf(wr, "  <leader>");
679             wrbuf_iconv_write_cdata(wr, 
680                                     0 /* no charset conversion for leader */,
681                                     n->u.leader, strlen(n->u.leader));
682             wrbuf_printf(wr, "</leader>\n");
683         }
684     }
685     wrbuf_puts(wr, "</record>\n");
686     return 0;
687 }
688
689 static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
690                                      const char *ns, 
691                                      const char *format,
692                                      const char *type)
693 {
694     if (mt->write_using_libxml2)
695     {
696 #if YAZ_HAVE_XML2
697         int ret;
698         xmlNode *root_ptr;
699
700         if (!mt->turbo_format)
701                 ret = yaz_marc_write_xml(mt, &root_ptr, ns, format, type);
702         else
703                 ret = yaz_marc_write_turbo_xml(mt, &root_ptr, ns, format, type);
704         if (ret == 0)
705         {
706             xmlChar *buf_out;
707             xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
708             int len_out;
709
710             xmlDocSetRootElement(doc, root_ptr);
711             xmlDocDumpMemory(doc, &buf_out, &len_out);
712
713             wrbuf_write(wr, (const char *) buf_out, len_out);
714             wrbuf_puts(wr, "");
715             xmlFree(buf_out);
716             xmlFreeDoc(doc);
717         }
718         return ret;
719 #else
720         return -1;
721 #endif
722     }
723     else
724         return yaz_marc_write_marcxml_ns1(mt, wr, ns, format, type);
725 }
726
727 int yaz_marc_write_marcxml(yaz_marc_t mt, WRBUF wr)
728 {
729     /* set leader 09 to 'a' for UNICODE */
730     /* http://www.loc.gov/marc/bibliographic/ecbdldrd.html#mrcblea */
731     if (!mt->leader_spec)
732         yaz_marc_modify_leader(mt, 9, "a");
733     char *name_space = "http://www.loc.gov/MARC21/slim";
734     if (mt->output_format == YAZ_MARC_TMARCXML)
735         name_space = "http://www.indexdata.com/MARC21/turboxml";
736     return yaz_marc_write_marcxml_ns(mt, wr, name_space,
737                                      0, 0);
738 }
739
740 int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
741                                const char *format,
742                                const char *type)
743 {
744     return yaz_marc_write_marcxml_ns(mt, wr,
745                                      "info:lc/xmlns/marcxchange-v1",
746                                      0, 0);
747 }
748
749 #if YAZ_HAVE_XML2
750
751 void add_marc_datafield_turbo_xml(yaz_marc_t mt, struct yaz_marc_node *n, xmlNode *record_ptr, xmlNsPtr ns_record, WRBUF wr_cdata, int identifier_length)
752 {
753     xmlNode *ptr;
754     struct yaz_marc_subfield *s;
755     int turbo = mt->turbo_format;
756     if (!turbo) {
757         ptr = xmlNewChild(record_ptr, ns_record, BAD_CAST "datafield", 0);
758         xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.datafield.tag);
759     }
760     else {
761         //TODO consider if safe
762         char field[10];
763         field[0] = 'd';
764         strncpy(field + 1, n->u.datafield.tag, 3);
765         field[4] = '\0';
766         ptr = xmlNewChild(record_ptr, ns_record, BAD_CAST field, 0);
767     }
768     if (n->u.datafield.indicator)
769     {
770         int i;
771         for (i = 0; n->u.datafield.indicator[i]; i++)
772         {
773             char ind_str[6];
774             char ind_val[2];
775             
776             ind_val[0] = n->u.datafield.indicator[i];
777             ind_val[1] = '\0';
778             if (!turbo) {
779                 sprintf(ind_str, "ind%d", i+1);
780                 xmlNewProp(ptr, BAD_CAST ind_str, BAD_CAST ind_val);
781             }
782             else {
783                 sprintf(ind_str, "i%d", i+1);
784                 xmlNewTextChild(ptr, ns_record, BAD_CAST ind_str, BAD_CAST ind_val);
785             }
786         }
787     }
788         WRBUF subfield_name = wrbuf_alloc();
789     for (s = n->u.datafield.subfields; s; s = s->next)
790     {
791         xmlNode *ptr_subfield;
792         size_t using_code_len = get_subfield_len(mt, s->code_data,
793                                                  identifier_length);
794         wrbuf_rewind(wr_cdata);
795         wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, s->code_data + using_code_len);
796         marc_iconv_reset(mt, wr_cdata);
797         
798         if (!turbo) {
799                 ptr_subfield = xmlNewTextChild(
800                                 ptr, ns_record,
801                                 BAD_CAST "subfield",  BAD_CAST wrbuf_cstr(wr_cdata));
802                 wrbuf_rewind(wr_cdata);
803                 wrbuf_iconv_write(wr_cdata, mt->iconv_cd,s->code_data, using_code_len);
804                 xmlNewProp(ptr_subfield, BAD_CAST "code",
805                                 BAD_CAST wrbuf_cstr(wr_cdata));
806         }
807         else { // Turbo format
808                 wrbuf_rewind(subfield_name);
809                 wrbuf_puts(subfield_name, "s");
810                 // TODO Map special codes to something possible for XML ELEMENT names
811                 if ((s->code_data[0] >= '0' && s->code_data[0] <= '9') ||
812                     (s->code_data[0] >= 'a' && s->code_data[0] <= 'z') ||
813                                 (s->code_data[0] >= 'A' && s->code_data[0] <= 'Z'))
814                 {
815                         wrbuf_iconv_write(subfield_name, mt->iconv_cd,s->code_data, using_code_len);
816                 }
817                 else {
818                                 char buffer[2*using_code_len + 1];
819                                 int index;
820                                 for (index = 0; index < using_code_len; index++) {
821                                         sprintf(buffer + 2*index, "%02X", (unsigned char) s->code_data[index] & 0xFF);
822                                 };
823                                 buffer[2*(index+1)] = 0;
824                                 wrbuf_puts(subfield_name, "-");
825                                 wrbuf_puts(subfield_name, buffer);
826                         yaz_log(YLOG_WARN, "Using numeric value in element name: %s", buffer);
827                 }
828                 ptr_subfield = xmlNewTextChild(ptr, ns_record,
829                                 BAD_CAST wrbuf_cstr(subfield_name),
830                                 BAD_CAST wrbuf_cstr(wr_cdata));
831         }
832     }
833         wrbuf_destroy(subfield_name);
834 }
835
836 int yaz_marc_write_turbo_xml(yaz_marc_t mt, xmlNode **root_ptr,
837                        const char *ns, 
838                        const char *format,
839                        const char *type)
840 {
841     struct yaz_marc_node *n;
842     int identifier_length;
843     const char *leader = 0;
844     xmlNode *record_ptr;
845     xmlNsPtr ns_record;
846     WRBUF wr_cdata = 0;
847     int turbo = mt->turbo_format;
848     for (n = mt->nodes; n; n = n->next)
849         if (n->which == YAZ_MARC_LEADER)
850         {
851             leader = n->u.leader;
852             break;
853         }
854     
855     if (!leader)
856         return -1;
857     if (!atoi_n_check(leader+11, 1, &identifier_length))
858         return -1;
859
860     wr_cdata = wrbuf_alloc();
861
862     record_ptr = xmlNewNode(0, BAD_CAST "record");
863     *root_ptr = record_ptr;
864
865     ns_record = xmlNewNs(record_ptr, BAD_CAST ns, 0);
866     xmlSetNs(record_ptr, ns_record);
867
868     if (format)
869         xmlNewProp(record_ptr, BAD_CAST "format", BAD_CAST format);
870     if (type)
871         xmlNewProp(record_ptr, BAD_CAST "type", BAD_CAST type);
872     for (n = mt->nodes; n; n = n->next)
873     {
874         struct yaz_marc_subfield *s;
875         xmlNode *ptr;
876
877         switch(n->which)
878         {
879         case YAZ_MARC_DATAFIELD:
880                 add_marc_datafield_turbo_xml(mt, n, record_ptr, ns_record, wr_cdata, identifier_length);
881             break;
882         case YAZ_MARC_CONTROLFIELD:
883             wrbuf_rewind(wr_cdata);
884             wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, n->u.controlfield.data);
885             marc_iconv_reset(mt, wr_cdata);
886             
887             if (!turbo) {
888                                 ptr = xmlNewTextChild(record_ptr, ns_record,
889                                                                           BAD_CAST "controlfield",
890                                                                           BAD_CAST wrbuf_cstr(wr_cdata));
891                                 xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.controlfield.tag);
892             }
893             else {
894                 // TODO required iconv?
895                 char field[10];
896                                 field[0] = 'c';
897                 strncpy(field + 1, n->u.controlfield.tag, 3);
898                 field[4] = '\0';
899                 ptr = xmlNewTextChild(record_ptr, ns_record,
900                                                                           BAD_CAST field,
901                                                                           BAD_CAST wrbuf_cstr(wr_cdata));
902             }
903
904             break;
905         case YAZ_MARC_COMMENT:
906             ptr = xmlNewComment(BAD_CAST n->u.comment);
907             xmlAddChild(record_ptr, ptr);
908             break;
909         case YAZ_MARC_LEADER:
910                         {
911                                 char *field = "leader";
912                                 if (turbo)
913                                         field = "l";
914                                 xmlNewTextChild(record_ptr, ns_record, BAD_CAST field,
915                                                                 BAD_CAST n->u.leader);
916                         }
917             break;
918         }
919     }
920     wrbuf_destroy(wr_cdata);
921     return 0;
922 }
923
924
925 int yaz_marc_write_xml(yaz_marc_t mt, xmlNode **root_ptr,
926                        const char *ns, 
927                        const char *format,
928                        const char *type)
929 {
930     struct yaz_marc_node *n;
931     int identifier_length;
932     const char *leader = 0;
933     xmlNode *record_ptr;
934     xmlNsPtr ns_record;
935     WRBUF wr_cdata = 0;
936
937     for (n = mt->nodes; n; n = n->next)
938         if (n->which == YAZ_MARC_LEADER)
939         {
940             leader = n->u.leader;
941             break;
942         }
943     
944     if (!leader)
945         return -1;
946     if (!atoi_n_check(leader+11, 1, &identifier_length))
947         return -1;
948
949     wr_cdata = wrbuf_alloc();
950
951     record_ptr = xmlNewNode(0, BAD_CAST "record");
952     *root_ptr = record_ptr;
953
954     ns_record = xmlNewNs(record_ptr, BAD_CAST ns, 0);
955     xmlSetNs(record_ptr, ns_record);
956
957     if (format)
958         xmlNewProp(record_ptr, BAD_CAST "format", BAD_CAST format);
959     if (type)
960         xmlNewProp(record_ptr, BAD_CAST "type", BAD_CAST type);
961     for (n = mt->nodes; n; n = n->next)
962     {
963         struct yaz_marc_subfield *s;
964         xmlNode *ptr;
965
966         switch(n->which)
967         {
968         case YAZ_MARC_DATAFIELD:
969             ptr = xmlNewChild(record_ptr, ns_record, BAD_CAST "datafield", 0);
970             xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.datafield.tag);
971             if (n->u.datafield.indicator)
972             {
973                 int i;
974                 for (i = 0; n->u.datafield.indicator[i]; i++)
975                 {
976                     char ind_str[6];
977                     char ind_val[2];
978
979                     sprintf(ind_str, "ind%d", i+1);
980                     ind_val[0] = n->u.datafield.indicator[i];
981                     ind_val[1] = '\0';
982                     xmlNewProp(ptr, BAD_CAST ind_str, BAD_CAST ind_val);
983                 }
984             }
985             for (s = n->u.datafield.subfields; s; s = s->next)
986             {
987                 xmlNode *ptr_subfield;
988                 size_t using_code_len = get_subfield_len(mt, s->code_data,
989                                                          identifier_length);
990                 wrbuf_rewind(wr_cdata);
991                 wrbuf_iconv_puts(wr_cdata, mt->iconv_cd,
992                                  s->code_data + using_code_len);
993                 marc_iconv_reset(mt, wr_cdata);
994                 ptr_subfield = xmlNewTextChild(
995                     ptr, ns_record,
996                     BAD_CAST "subfield",  BAD_CAST wrbuf_cstr(wr_cdata));
997
998                 wrbuf_rewind(wr_cdata);
999                 wrbuf_iconv_write(wr_cdata, mt->iconv_cd,
1000                                   s->code_data, using_code_len);
1001                 xmlNewProp(ptr_subfield, BAD_CAST "code",
1002                            BAD_CAST wrbuf_cstr(wr_cdata));
1003             }
1004             break;
1005         case YAZ_MARC_CONTROLFIELD:
1006             wrbuf_rewind(wr_cdata);
1007             wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, n->u.controlfield.data);
1008             marc_iconv_reset(mt, wr_cdata);
1009             
1010             ptr = xmlNewTextChild(record_ptr, ns_record,
1011                                   BAD_CAST "controlfield",
1012                                   BAD_CAST wrbuf_cstr(wr_cdata));
1013             
1014             xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.controlfield.tag);
1015             break;
1016         case YAZ_MARC_COMMENT:
1017             ptr = xmlNewComment(BAD_CAST n->u.comment);
1018             xmlAddChild(record_ptr, ptr);
1019             break;
1020         case YAZ_MARC_LEADER:
1021             xmlNewTextChild(record_ptr, ns_record, BAD_CAST "leader",
1022                             BAD_CAST n->u.leader);
1023             break;
1024         }
1025     }
1026     wrbuf_destroy(wr_cdata);
1027     return 0;
1028 }
1029
1030
1031
1032
1033 #endif
1034
1035 int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
1036 {
1037     struct yaz_marc_node *n;
1038     int indicator_length;
1039     int identifier_length;
1040     int length_data_entry;
1041     int length_starting;
1042     int length_implementation;
1043     int data_offset = 0;
1044     const char *leader = 0;
1045     WRBUF wr_dir, wr_head, wr_data_tmp;
1046     int base_address;
1047     
1048     for (n = mt->nodes; n; n = n->next)
1049         if (n->which == YAZ_MARC_LEADER)
1050             leader = n->u.leader;
1051     
1052     if (!leader)
1053         return -1;
1054     if (!atoi_n_check(leader+10, 1, &indicator_length))
1055         return -1;
1056     if (!atoi_n_check(leader+11, 1, &identifier_length))
1057         return -1;
1058     if (!atoi_n_check(leader+20, 1, &length_data_entry))
1059         return -1;
1060     if (!atoi_n_check(leader+21, 1, &length_starting))
1061         return -1;
1062     if (!atoi_n_check(leader+22, 1, &length_implementation))
1063         return -1;
1064
1065     wr_data_tmp = wrbuf_alloc();
1066     wr_dir = wrbuf_alloc();
1067     for (n = mt->nodes; n; n = n->next)
1068     {
1069         int data_length = 0;
1070         struct yaz_marc_subfield *s;
1071
1072         switch(n->which)
1073         {
1074         case YAZ_MARC_DATAFIELD:
1075             wrbuf_printf(wr_dir, "%.3s", n->u.datafield.tag);
1076             data_length += indicator_length;
1077             wrbuf_rewind(wr_data_tmp);
1078             for (s = n->u.datafield.subfields; s; s = s->next)
1079             {
1080                 /* write dummy IDFS + content */
1081                 wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
1082                 wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, s->code_data);
1083                 marc_iconv_reset(mt, wr_data_tmp);
1084             }
1085             /* write dummy FS (makes MARC-8 to become ASCII) */
1086             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
1087             marc_iconv_reset(mt, wr_data_tmp);
1088             data_length += wrbuf_len(wr_data_tmp);
1089             break;
1090         case YAZ_MARC_CONTROLFIELD:
1091             wrbuf_printf(wr_dir, "%.3s", n->u.controlfield.tag);
1092
1093             wrbuf_rewind(wr_data_tmp);
1094             wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, 
1095                              n->u.controlfield.data);
1096             marc_iconv_reset(mt, wr_data_tmp);
1097             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');/* field sep */
1098             marc_iconv_reset(mt, wr_data_tmp);
1099             data_length += wrbuf_len(wr_data_tmp);
1100             break;
1101         case YAZ_MARC_COMMENT:
1102             break;
1103         case YAZ_MARC_LEADER:
1104             break;
1105         }
1106         if (data_length)
1107         {
1108             wrbuf_printf(wr_dir, "%0*d", length_data_entry, data_length);
1109             wrbuf_printf(wr_dir, "%0*d", length_starting, data_offset);
1110             data_offset += data_length;
1111         }
1112     }
1113     /* mark end of directory */
1114     wrbuf_putc(wr_dir, ISO2709_FS);
1115
1116     /* base address of data (comes after leader+directory) */
1117     base_address = 24 + wrbuf_len(wr_dir);
1118
1119     wr_head = wrbuf_alloc();
1120
1121     /* write record length */
1122     wrbuf_printf(wr_head, "%05d", base_address + data_offset + 1);
1123     /* from "original" leader */
1124     wrbuf_write(wr_head, leader+5, 7);
1125     /* base address of data */
1126     wrbuf_printf(wr_head, "%05d", base_address);
1127     /* from "original" leader */
1128     wrbuf_write(wr_head, leader+17, 7);
1129     
1130     wrbuf_write(wr, wrbuf_buf(wr_head), 24);
1131     wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
1132     wrbuf_destroy(wr_head);
1133     wrbuf_destroy(wr_dir);
1134     wrbuf_destroy(wr_data_tmp);
1135
1136     for (n = mt->nodes; n; n = n->next)
1137     {
1138         struct yaz_marc_subfield *s;
1139
1140         switch(n->which)
1141         {
1142         case YAZ_MARC_DATAFIELD:
1143             wrbuf_printf(wr, "%.*s", indicator_length,
1144                          n->u.datafield.indicator);
1145             for (s = n->u.datafield.subfields; s; s = s->next)
1146             {
1147                 wrbuf_putc(wr, ISO2709_IDFS);
1148                 wrbuf_iconv_puts(wr, mt->iconv_cd, s->code_data);
1149                 marc_iconv_reset(mt, wr);
1150             }
1151             wrbuf_putc(wr, ISO2709_FS);
1152             break;
1153         case YAZ_MARC_CONTROLFIELD:
1154             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
1155             marc_iconv_reset(mt, wr);
1156             wrbuf_putc(wr, ISO2709_FS);
1157             break;
1158         case YAZ_MARC_COMMENT:
1159             break;
1160         case YAZ_MARC_LEADER:
1161             break;
1162         }
1163     }
1164     wrbuf_printf(wr, "%c", ISO2709_RS);
1165     return 0;
1166 }
1167
1168
1169 int yaz_marc_decode_wrbuf(yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
1170 {
1171     int s, r = yaz_marc_read_iso2709(mt, buf, bsize);
1172     if (r <= 0)
1173         return r;
1174     s = yaz_marc_write_mode(mt, wr); /* returns 0 for OK, -1 otherwise */
1175     if (s != 0)
1176         return -1; /* error */
1177     return r; /* OK, return length > 0 */
1178 }
1179
1180 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
1181                          const char **result, size_t *rsize)
1182 {
1183     int r;
1184
1185     wrbuf_rewind(mt->m_wr);
1186     r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
1187     if (result)
1188         *result = wrbuf_cstr(mt->m_wr);
1189     if (rsize)
1190         *rsize = wrbuf_len(mt->m_wr);
1191     return r;
1192 }
1193
1194 void yaz_marc_set_read_format(yaz_marc_t mt, int format)
1195 {
1196     if (mt)
1197         mt->input_format = format;
1198 }
1199
1200 int yaz_marc_get_read_format(yaz_marc_t mt)
1201 {
1202     if (mt)
1203         return mt->input_format;
1204     return -1;
1205 }
1206
1207
1208 void yaz_marc_set_write_format(yaz_marc_t mt, int format)
1209 {
1210     if (mt)
1211         mt->output_format = format;
1212 }
1213
1214 int yaz_marc_get_write_format(yaz_marc_t mt)
1215 {
1216     if (mt)
1217         return mt->output_format;
1218     return -1;
1219 }
1220
1221
1222 /**
1223  * Deprecated, use yaz_marc_set_write_format
1224  */
1225 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
1226 {
1227     if (mt)
1228         mt->output_format = xmlmode;
1229 }
1230
1231
1232
1233 void yaz_marc_debug(yaz_marc_t mt, int level)
1234 {
1235     if (mt)
1236         mt->debug = level;
1237 }
1238
1239 void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
1240 {
1241     mt->iconv_cd = cd;
1242 }
1243
1244 yaz_iconv_t yaz_marc_get_iconv(yaz_marc_t mt)
1245 {
1246     return mt->iconv_cd;
1247 }
1248
1249 void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
1250 {
1251     struct yaz_marc_node *n;
1252     char *leader = 0;
1253     for (n = mt->nodes; n; n = n->next)
1254         if (n->which == YAZ_MARC_LEADER)
1255         {
1256             leader = n->u.leader;
1257             memcpy(leader+off, str, strlen(str));
1258             break;
1259         }
1260 }
1261
1262 int yaz_marc_leader_spec(yaz_marc_t mt, const char *leader_spec)
1263 {
1264     xfree(mt->leader_spec);
1265     mt->leader_spec = 0;
1266     if (leader_spec)
1267     {
1268         char dummy_leader[24];
1269         if (marc_exec_leader(leader_spec, dummy_leader, 24))
1270             return -1;
1271         mt->leader_spec = xstrdup(leader_spec);
1272     }
1273     return 0;
1274 }
1275
1276 static int marc_exec_leader(const char *leader_spec, char *leader, size_t size)
1277 {
1278     const char *cp = leader_spec;
1279     while (cp)
1280     {
1281         char val[21];
1282         int pos;
1283         int no_read = 0, no = 0;
1284
1285         no = sscanf(cp, "%d=%20[^,]%n", &pos, val, &no_read);
1286         if (no < 2 || no_read < 3)
1287             return -1;
1288         if (pos < 0 || (size_t) pos >= size)
1289             return -1;
1290
1291         if (*val == '\'')
1292         {
1293             const char *vp = strchr(val+1, '\'');
1294             size_t len;
1295             
1296             if (!vp)
1297                 return -1;
1298             len = vp-val-1;
1299             if (len + pos > size)
1300                 return -1;
1301             memcpy(leader + pos, val+1, len);
1302         }
1303         else if (*val >= '0' && *val <= '9')
1304         {
1305             int ch = atoi(val);
1306             leader[pos] = ch;
1307         }
1308         else
1309             return -1;
1310         cp += no_read;
1311         if (*cp != ',')
1312             break;
1313
1314         cp++;
1315     }
1316     return 0;
1317 }
1318
1319 int yaz_marc_decode_formatstr(const char *arg)
1320 {
1321     int mode = -1; 
1322     if (!strcmp(arg, "marc"))
1323         mode = YAZ_MARC_ISO2709;
1324     if (!strcmp(arg, "marcxml"))
1325         mode = YAZ_MARC_MARCXML;
1326     if (!strcmp(arg, "tmarcxml"))
1327         mode = YAZ_MARC_TMARCXML;
1328     if (!strcmp(arg, "marcxchange"))
1329         mode = YAZ_MARC_XCHANGE;
1330     if (!strcmp(arg, "line"))
1331         mode = YAZ_MARC_LINE;
1332     return mode;
1333 }
1334
1335 void yaz_marc_write_using_libxml2(yaz_marc_t mt, int enable)
1336 {
1337     mt->write_using_libxml2 = enable;
1338 }
1339
1340 void yaz_marc_write_turbo_format(yaz_marc_t mt, int enable)
1341 {
1342     mt->turbo_format = enable;
1343 }
1344
1345 int yaz_marc_is_turbo_format(yaz_marc_t mt)
1346 {
1347     return mt->turbo_format;
1348 }
1349
1350
1351 /*
1352  * Local variables:
1353  * c-basic-offset: 4
1354  * c-file-style: "Stroustrup"
1355  * indent-tabs-mode: nil
1356  * End:
1357  * vim: shiftwidth=4 tabstop=8 expandtab
1358  */
1359