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