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