Update header about CQL grammar
[yaz-moved-to-github.git] / src / marcdisp.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2013 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 static void check_ascii(yaz_marc_t mt, char *leader, int offset,
346                         int ch_default)
347 {
348     if (leader[offset] < ' ' || leader[offset] > 127)
349     {
350         yaz_marc_cprintf(mt,
351                          "Leader character at offset %d is non-ASCII. "
352                          "Setting value to '%c'", offset, ch_default);
353         leader[offset] = ch_default;
354     }
355 }
356
357 void yaz_marc_set_leader(yaz_marc_t mt, const char *leader_c,
358                          int *indicator_length,
359                          int *identifier_length,
360                          int *base_address,
361                          int *length_data_entry,
362                          int *length_starting,
363                          int *length_implementation)
364 {
365     char leader[24];
366
367     memcpy(leader, leader_c, 24);
368
369     check_ascii(mt, leader, 5, 'a');
370     check_ascii(mt, leader, 6, 'a');
371     check_ascii(mt, leader, 7, 'a');
372     check_ascii(mt, leader, 8, '#');
373     check_ascii(mt, leader, 9, '#');
374     if (!atoi_n_check(leader+10, 1, indicator_length))
375     {
376         yaz_marc_cprintf(mt,
377                          "Indicator length at offset 10 should hold a digit."
378                          " Assuming 2");
379         leader[10] = '2';
380         *indicator_length = 2;
381     }
382     if (!atoi_n_check(leader+11, 1, identifier_length))
383     {
384         yaz_marc_cprintf(mt,
385                          "Identifier length at offset 11 should hold a digit."
386                          " Assuming 2");
387         leader[11] = '2';
388         *identifier_length = 2;
389     }
390     if (!atoi_n_check(leader+12, 5, base_address))
391     {
392         yaz_marc_cprintf(mt,
393                          "Base address at offsets 12..16 should hold a number."
394                          " Assuming 0");
395         *base_address = 0;
396     }
397     check_ascii(mt, leader, 17, '#');
398     check_ascii(mt, leader, 18, '#');
399     check_ascii(mt, leader, 19, '#');
400     if (!atoi_n_check(leader+20, 1, length_data_entry))
401     {
402         yaz_marc_cprintf(mt,
403                          "Length data entry at offset 20 should hold a digit."
404                          " Assuming 4");
405         *length_data_entry = 4;
406         leader[20] = '4';
407     }
408     if (!atoi_n_check(leader+21, 1, length_starting))
409     {
410         yaz_marc_cprintf(mt,
411                          "Length starting at offset 21 should hold a digit."
412                          " Assuming 5");
413         *length_starting = 5;
414         leader[21] = '5';
415     }
416     if (!atoi_n_check(leader+22, 1, length_implementation))
417     {
418         yaz_marc_cprintf(mt,
419                          "Length implementation at offset 22 should hold a digit."
420                          " Assuming 0");
421         *length_implementation = 0;
422         leader[22] = '0';
423     }
424     check_ascii(mt, leader, 23, '0');
425
426     if (mt->debug)
427     {
428         yaz_marc_cprintf(mt, "Indicator length      %5d", *indicator_length);
429         yaz_marc_cprintf(mt, "Identifier length     %5d", *identifier_length);
430         yaz_marc_cprintf(mt, "Base address          %5d", *base_address);
431         yaz_marc_cprintf(mt, "Length data entry     %5d", *length_data_entry);
432         yaz_marc_cprintf(mt, "Length starting       %5d", *length_starting);
433         yaz_marc_cprintf(mt, "Length implementation %5d", *length_implementation);
434     }
435     yaz_marc_add_leader(mt, leader, 24);
436 }
437
438 void yaz_marc_subfield_str(yaz_marc_t mt, const char *s)
439 {
440     strncpy(mt->subfield_str, s, sizeof(mt->subfield_str)-1);
441     mt->subfield_str[sizeof(mt->subfield_str)-1] = '\0';
442 }
443
444 void yaz_marc_endline_str(yaz_marc_t mt, const char *s)
445 {
446     strncpy(mt->endline_str, s, sizeof(mt->endline_str)-1);
447     mt->endline_str[sizeof(mt->endline_str)-1] = '\0';
448 }
449
450 /* try to guess how many bytes the identifier really is! */
451 static size_t cdata_one_character(yaz_marc_t mt, const char *buf)
452 {
453     if (mt->iconv_cd)
454     {
455         size_t i;
456         for (i = 1; i<5; i++)
457         {
458             char outbuf[12];
459             size_t outbytesleft = sizeof(outbuf);
460             char *outp = outbuf;
461             const char *inp = buf;
462
463             size_t inbytesleft = i;
464             size_t r = yaz_iconv(mt->iconv_cd, (char**) &inp, &inbytesleft,
465                                  &outp, &outbytesleft);
466             if (r != (size_t) (-1))
467                 return i;  /* got a complete sequence */
468         }
469         return 1; /* giving up */
470     }
471     return 1; /* we don't know */
472 }
473
474 void yaz_marc_reset(yaz_marc_t mt)
475 {
476     nmem_reset(mt->nmem);
477     mt->nodes = 0;
478     mt->nodes_pp = &mt->nodes;
479     mt->subfield_pp = 0;
480 }
481
482 int yaz_marc_write_check(yaz_marc_t mt, WRBUF wr)
483 {
484     struct yaz_marc_node *n;
485     int identifier_length;
486     const char *leader = 0;
487
488     for (n = mt->nodes; n; n = n->next)
489         if (n->which == YAZ_MARC_LEADER)
490         {
491             leader = n->u.leader;
492             break;
493         }
494
495     if (!leader)
496         return -1;
497     if (!atoi_n_check(leader+11, 1, &identifier_length))
498         return -1;
499
500     for (n = mt->nodes; n; n = n->next)
501     {
502         switch(n->which)
503         {
504         case YAZ_MARC_COMMENT:
505             wrbuf_iconv_write(wr, mt->iconv_cd,
506                               n->u.comment, strlen(n->u.comment));
507             wrbuf_puts(wr, "\n");
508             break;
509         default:
510             break;
511         }
512     }
513     return 0;
514 }
515
516 static size_t get_subfield_len(yaz_marc_t mt, const char *data,
517                                int identifier_length)
518 {
519     /* if identifier length is 2 (most MARCs) or less (probably an error),
520        the code is a single character .. However we've
521        seen multibyte codes, so see how big it really is */
522     if (identifier_length > 2)
523         return identifier_length - 1;
524     else
525         return cdata_one_character(mt, data);
526 }
527
528 int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
529 {
530     struct yaz_marc_node *n;
531     int identifier_length;
532     const char *leader = 0;
533
534     for (n = mt->nodes; n; n = n->next)
535         if (n->which == YAZ_MARC_LEADER)
536         {
537             leader = n->u.leader;
538             break;
539         }
540
541     if (!leader)
542         return -1;
543     if (!atoi_n_check(leader+11, 1, &identifier_length))
544         return -1;
545
546     for (n = mt->nodes; n; n = n->next)
547     {
548         struct yaz_marc_subfield *s;
549         switch(n->which)
550         {
551         case YAZ_MARC_DATAFIELD:
552             wrbuf_printf(wr, "%s %s", n->u.datafield.tag,
553                          n->u.datafield.indicator);
554             for (s = n->u.datafield.subfields; s; s = s->next)
555             {
556                 size_t using_code_len = get_subfield_len(mt, s->code_data,
557                                                          identifier_length);
558
559                 wrbuf_puts (wr, mt->subfield_str);
560                 wrbuf_iconv_write(wr, mt->iconv_cd, s->code_data,
561                                   using_code_len);
562                 wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
563                 wrbuf_iconv_puts(wr, mt->iconv_cd,
564                                  s->code_data + using_code_len);
565                 marc_iconv_reset(mt, wr);
566             }
567             wrbuf_puts (wr, mt->endline_str);
568             break;
569         case YAZ_MARC_CONTROLFIELD:
570             wrbuf_printf(wr, "%s", n->u.controlfield.tag);
571             wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
572             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
573             marc_iconv_reset(mt, wr);
574             wrbuf_puts (wr, mt->endline_str);
575             break;
576         case YAZ_MARC_COMMENT:
577             wrbuf_puts(wr, "(");
578             wrbuf_iconv_write(wr, mt->iconv_cd,
579                               n->u.comment, strlen(n->u.comment));
580             marc_iconv_reset(mt, wr);
581             wrbuf_puts(wr, ")\n");
582             break;
583         case YAZ_MARC_LEADER:
584             wrbuf_printf(wr, "%s\n", n->u.leader);
585         }
586     }
587     wrbuf_puts(wr, "\n");
588     return 0;
589 }
590
591 int yaz_marc_write_trailer(yaz_marc_t mt, WRBUF wr)
592 {
593     if (mt->enable_collection == collection_second)
594     {
595         switch(mt->output_format)
596         {
597         case YAZ_MARC_MARCXML:
598         case YAZ_MARC_TURBOMARC:
599             wrbuf_printf(wr, "</collection>\n");
600             break;
601         case YAZ_MARC_XCHANGE:
602             wrbuf_printf(wr, "</collection>\n");
603             break;
604         }
605     }
606     return 0;
607 }
608
609 void yaz_marc_enable_collection(yaz_marc_t mt)
610 {
611     mt->enable_collection = collection_first;
612 }
613
614 int yaz_marc_write_mode(yaz_marc_t mt, WRBUF wr)
615 {
616     switch(mt->output_format)
617     {
618     case YAZ_MARC_LINE:
619         return yaz_marc_write_line(mt, wr);
620     case YAZ_MARC_MARCXML:
621         return yaz_marc_write_marcxml(mt, wr);
622     case YAZ_MARC_TURBOMARC:
623         return yaz_marc_write_turbomarc(mt, wr);
624     case YAZ_MARC_XCHANGE:
625         return yaz_marc_write_marcxchange(mt, wr, 0, 0); /* no format, type */
626     case YAZ_MARC_ISO2709:
627         return yaz_marc_write_iso2709(mt, wr);
628     case YAZ_MARC_CHECK:
629         return yaz_marc_write_check(mt, wr);
630     }
631     return -1;
632 }
633
634 static const char *record_name[2]       = { "record", "r"};
635 static const char *leader_name[2]       = { "leader", "l"};
636 static const char *controlfield_name[2] = { "controlfield", "c"};
637 static const char *datafield_name[2]    = { "datafield", "d"};
638 static const char *indicator_name[2]    = { "ind", "i"};
639 static const char *subfield_name[2]     = { "subfield", "s"};
640
641 /** \brief common MARC XML/Xchange/turbomarc writer
642     \param mt handle
643     \param wr WRBUF output
644     \param ns XMLNS for the elements
645     \param format record format (e.g. "MARC21")
646     \param type record type (e.g. "Bibliographic")
647     \param turbo =1 for turbomarc
648     \retval 0 OK
649     \retval -1 failure
650 */
651 static int yaz_marc_write_marcxml_wrbuf(yaz_marc_t mt, WRBUF wr,
652                                         const char *ns,
653                                         const char *format,
654                                         const char *type,
655                                         int turbo)
656 {
657     struct yaz_marc_node *n;
658     int identifier_length;
659     const char *leader = 0;
660
661     for (n = mt->nodes; n; n = n->next)
662         if (n->which == YAZ_MARC_LEADER)
663         {
664             leader = n->u.leader;
665             break;
666         }
667
668     if (!leader)
669         return -1;
670     if (!atoi_n_check(leader+11, 1, &identifier_length))
671         return -1;
672
673     if (mt->enable_collection != no_collection)
674     {
675         if (mt->enable_collection == collection_first)
676         {
677             wrbuf_printf(wr, "<collection xmlns=\"%s\">\n", ns);
678             mt->enable_collection = collection_second;
679         }
680         wrbuf_printf(wr, "<%s", record_name[turbo]);
681     }
682     else
683     {
684         wrbuf_printf(wr, "<%s xmlns=\"%s\"", record_name[turbo], ns);
685     }
686     if (format)
687         wrbuf_printf(wr, " format=\"%.80s\"", format);
688     if (type)
689         wrbuf_printf(wr, " type=\"%.80s\"", type);
690     wrbuf_printf(wr, ">\n");
691     for (n = mt->nodes; n; n = n->next)
692     {
693         struct yaz_marc_subfield *s;
694
695         switch(n->which)
696         {
697         case YAZ_MARC_DATAFIELD:
698
699             wrbuf_printf(wr, "  <%s", datafield_name[turbo]);
700             if (!turbo)
701                 wrbuf_printf(wr, " tag=\"");
702             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
703                                     strlen(n->u.datafield.tag));
704             if (!turbo)
705                 wrbuf_printf(wr, "\"");
706             if (n->u.datafield.indicator)
707             {
708                 int i;
709                 for (i = 0; n->u.datafield.indicator[i]; i++)
710                 {
711                     wrbuf_printf(wr, " %s%d=\"", indicator_name[turbo], i+1);
712                     wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
713                                             n->u.datafield.indicator+i, 1);
714                     wrbuf_iconv_puts(wr, mt->iconv_cd, "\"");
715                 }
716             }
717             wrbuf_printf(wr, ">\n");
718             for (s = n->u.datafield.subfields; s; s = s->next)
719             {
720                 size_t using_code_len = get_subfield_len(mt, s->code_data,
721                                                          identifier_length);
722                 wrbuf_printf(wr, "    <%s", subfield_name[turbo]);
723                 if (!turbo)
724                 {
725                     wrbuf_printf(wr, " code=\"");
726                     wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
727                                             s->code_data, using_code_len);
728                     wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
729                 }
730                 else
731                 {
732                     element_name_append_attribute_value(mt, wr, "code", s->code_data, using_code_len);
733                     wrbuf_puts(wr, ">");
734                 }
735                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
736                                         s->code_data + using_code_len,
737                                         strlen(s->code_data + using_code_len));
738                 marc_iconv_reset(mt, wr);
739                 wrbuf_printf(wr, "</%s", subfield_name[turbo]);
740                 if (turbo)
741                     element_name_append_attribute_value(mt, wr, 0, s->code_data, using_code_len);
742                 wrbuf_puts(wr, ">\n");
743             }
744             wrbuf_printf(wr, "  </%s", datafield_name[turbo]);
745             /* TODO Not CDATA */
746             if (turbo)
747                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
748                                         strlen(n->u.datafield.tag));
749             wrbuf_printf(wr, ">\n");
750             break;
751         case YAZ_MARC_CONTROLFIELD:
752             wrbuf_printf(wr, "  <%s", controlfield_name[turbo]);
753             if (!turbo)
754             {
755                 wrbuf_printf(wr, " tag=\"");
756                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
757                                         strlen(n->u.controlfield.tag));
758                 wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
759             }
760             else
761             {
762                 /* TODO convert special */
763                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
764                                         strlen(n->u.controlfield.tag));
765                 wrbuf_iconv_puts(wr, mt->iconv_cd, ">");
766             }
767             wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
768                                     n->u.controlfield.data,
769                                     strlen(n->u.controlfield.data));
770             marc_iconv_reset(mt, wr);
771             wrbuf_printf(wr, "</%s", controlfield_name[turbo]);
772             /* TODO convert special */
773             if (turbo)
774                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
775                                         strlen(n->u.controlfield.tag));
776             wrbuf_puts(wr, ">\n");
777             break;
778         case YAZ_MARC_COMMENT:
779             wrbuf_printf(wr, "<!-- ");
780             wrbuf_puts(wr, n->u.comment);
781             wrbuf_printf(wr, " -->\n");
782             break;
783         case YAZ_MARC_LEADER:
784             wrbuf_printf(wr, "  <%s>", leader_name[turbo]);
785             wrbuf_iconv_write_cdata(wr,
786                                     0 , /* no charset conversion for leader */
787                                     n->u.leader, strlen(n->u.leader));
788             wrbuf_printf(wr, "</%s>\n", leader_name[turbo]);
789         }
790     }
791     wrbuf_printf(wr, "</%s>\n", record_name[turbo]);
792     return 0;
793 }
794
795 static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
796                                      const char *ns,
797                                      const char *format,
798                                      const char *type,
799                                      int turbo)
800 {
801     if (mt->write_using_libxml2)
802     {
803 #if YAZ_HAVE_XML2
804         int ret;
805         xmlNode *root_ptr;
806
807         if (!turbo)
808             ret = yaz_marc_write_xml(mt, &root_ptr, ns, format, type);
809         else
810             ret = yaz_marc_write_xml_turbo_xml(mt, &root_ptr, ns, format, type);
811         if (ret == 0)
812         {
813             xmlChar *buf_out;
814             xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
815             int len_out;
816
817             xmlDocSetRootElement(doc, root_ptr);
818             xmlDocDumpMemory(doc, &buf_out, &len_out);
819
820             wrbuf_write(wr, (const char *) buf_out, len_out);
821             wrbuf_puts(wr, "");
822             xmlFree(buf_out);
823             xmlFreeDoc(doc);
824         }
825         return ret;
826 #else
827         return -1;
828 #endif
829     }
830     else
831         return yaz_marc_write_marcxml_wrbuf(mt, wr, ns, format, type, turbo);
832 }
833
834 int yaz_marc_write_marcxml(yaz_marc_t mt, WRBUF wr)
835 {
836     /* set leader 09 to 'a' for UNICODE */
837     /* http://www.loc.gov/marc/bibliographic/ecbdldrd.html#mrcblea */
838     if (!mt->leader_spec)
839         yaz_marc_modify_leader(mt, 9, "a");
840     return yaz_marc_write_marcxml_ns(mt, wr,
841                                      "http://www.loc.gov/MARC21/slim",
842                                      0, 0, 0);
843 }
844
845 int yaz_marc_write_turbomarc(yaz_marc_t mt, WRBUF wr)
846 {
847     /* set leader 09 to 'a' for UNICODE */
848     /* http://www.loc.gov/marc/bibliographic/ecbdldrd.html#mrcblea */
849     if (!mt->leader_spec)
850         yaz_marc_modify_leader(mt, 9, "a");
851     return yaz_marc_write_marcxml_ns(mt, wr,
852                                      "http://www.indexdata.com/turbomarc", 0, 0, 1);
853 }
854
855 int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
856                                const char *format,
857                                const char *type)
858 {
859     return yaz_marc_write_marcxml_ns(mt, wr,
860                                      "info:lc/xmlns/marcxchange-v1",
861                                      0, 0, 0);
862 }
863
864 #if YAZ_HAVE_XML2
865
866 void add_marc_datafield_turbo_xml(yaz_marc_t mt, struct yaz_marc_node *n,
867                                   xmlNode *record_ptr,
868                                   xmlNsPtr ns_record, WRBUF wr_cdata,
869                                   int identifier_length)
870 {
871     xmlNode *ptr;
872     struct yaz_marc_subfield *s;
873     WRBUF subfield_name = wrbuf_alloc();
874
875     /* TODO consider if safe */
876     char field[10];
877     field[0] = 'd';
878     strncpy(field + 1, n->u.datafield.tag, 3);
879     field[4] = '\0';
880     ptr = xmlNewChild(record_ptr, ns_record, BAD_CAST field, 0);
881
882     if (n->u.datafield.indicator)
883     {
884         int i;
885         for (i = 0; n->u.datafield.indicator[i]; i++)
886         {
887             char ind_str[6];
888             char ind_val[2];
889
890             ind_val[0] = n->u.datafield.indicator[i];
891             ind_val[1] = '\0';
892             sprintf(ind_str, "%s%d", indicator_name[1], i+1);
893             xmlNewProp(ptr, BAD_CAST ind_str, BAD_CAST ind_val);
894         }
895     }
896     for (s = n->u.datafield.subfields; s; s = s->next)
897     {
898         int not_written;
899         xmlNode *ptr_subfield;
900         size_t using_code_len = get_subfield_len(mt, s->code_data,
901                                                  identifier_length);
902         wrbuf_rewind(wr_cdata);
903         wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, s->code_data + using_code_len);
904         marc_iconv_reset(mt, wr_cdata);
905
906         wrbuf_rewind(subfield_name);
907         wrbuf_puts(subfield_name, "s");
908         not_written = element_name_append_attribute_value(mt, subfield_name, 0, s->code_data, using_code_len) != 0;
909         ptr_subfield = xmlNewTextChild(ptr, ns_record,
910                                        BAD_CAST wrbuf_cstr(subfield_name),
911                                        BAD_CAST wrbuf_cstr(wr_cdata));
912         if (not_written)
913         {
914             /* Generate code attribute value and add */
915             wrbuf_rewind(wr_cdata);
916             wrbuf_iconv_write(wr_cdata, mt->iconv_cd,s->code_data, using_code_len);
917             xmlNewProp(ptr_subfield, BAD_CAST "code",  BAD_CAST wrbuf_cstr(wr_cdata));
918         }
919     }
920     wrbuf_destroy(subfield_name);
921 }
922
923 static int yaz_marc_write_xml_turbo_xml(yaz_marc_t mt, xmlNode **root_ptr,
924                                         const char *ns,
925                                         const char *format,
926                                         const char *type)
927 {
928     struct yaz_marc_node *n;
929     int identifier_length;
930     const char *leader = 0;
931     xmlNode *record_ptr;
932     xmlNsPtr ns_record;
933     WRBUF wr_cdata = 0;
934
935     for (n = mt->nodes; n; n = n->next)
936         if (n->which == YAZ_MARC_LEADER)
937         {
938             leader = n->u.leader;
939             break;
940         }
941
942     if (!leader)
943         return -1;
944     if (!atoi_n_check(leader+11, 1, &identifier_length))
945         return -1;
946
947     wr_cdata = wrbuf_alloc();
948
949     record_ptr = xmlNewNode(0, BAD_CAST "r");
950     *root_ptr = record_ptr;
951
952     ns_record = xmlNewNs(record_ptr, BAD_CAST ns, 0);
953     xmlSetNs(record_ptr, ns_record);
954
955     if (format)
956         xmlNewProp(record_ptr, BAD_CAST "format", BAD_CAST format);
957     if (type)
958         xmlNewProp(record_ptr, BAD_CAST "type", BAD_CAST type);
959     for (n = mt->nodes; n; n = n->next)
960     {
961         xmlNode *ptr;
962
963         char field[10];
964         field[0] = 'c';
965         field[4] = '\0';
966
967         switch(n->which)
968         {
969         case YAZ_MARC_DATAFIELD:
970             add_marc_datafield_turbo_xml(mt, n, record_ptr, ns_record, wr_cdata, identifier_length);
971             break;
972         case YAZ_MARC_CONTROLFIELD:
973             wrbuf_rewind(wr_cdata);
974             wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, n->u.controlfield.data);
975             marc_iconv_reset(mt, wr_cdata);
976
977             strncpy(field + 1, n->u.controlfield.tag, 3);
978             ptr = xmlNewTextChild(record_ptr, ns_record,
979                                   BAD_CAST field,
980                                   BAD_CAST wrbuf_cstr(wr_cdata));
981             break;
982         case YAZ_MARC_COMMENT:
983             ptr = xmlNewComment(BAD_CAST n->u.comment);
984             xmlAddChild(record_ptr, ptr);
985             break;
986         case YAZ_MARC_LEADER:
987             xmlNewTextChild(record_ptr, ns_record, BAD_CAST "l",
988                             BAD_CAST n->u.leader);
989             break;
990         }
991     }
992     wrbuf_destroy(wr_cdata);
993     return 0;
994 }
995
996
997 int yaz_marc_write_xml(yaz_marc_t mt, xmlNode **root_ptr,
998                        const char *ns,
999                        const char *format,
1000                        const char *type)
1001 {
1002     struct yaz_marc_node *n;
1003     int identifier_length;
1004     const char *leader = 0;
1005     xmlNode *record_ptr;
1006     xmlNsPtr ns_record;
1007     WRBUF wr_cdata = 0;
1008
1009     for (n = mt->nodes; n; n = n->next)
1010         if (n->which == YAZ_MARC_LEADER)
1011         {
1012             leader = n->u.leader;
1013             break;
1014         }
1015
1016     if (!leader)
1017         return -1;
1018     if (!atoi_n_check(leader+11, 1, &identifier_length))
1019         return -1;
1020
1021     wr_cdata = wrbuf_alloc();
1022
1023     record_ptr = xmlNewNode(0, BAD_CAST "record");
1024     *root_ptr = record_ptr;
1025
1026     ns_record = xmlNewNs(record_ptr, BAD_CAST ns, 0);
1027     xmlSetNs(record_ptr, ns_record);
1028
1029     if (format)
1030         xmlNewProp(record_ptr, BAD_CAST "format", BAD_CAST format);
1031     if (type)
1032         xmlNewProp(record_ptr, BAD_CAST "type", BAD_CAST type);
1033     for (n = mt->nodes; n; n = n->next)
1034     {
1035         struct yaz_marc_subfield *s;
1036         xmlNode *ptr;
1037
1038         switch(n->which)
1039         {
1040         case YAZ_MARC_DATAFIELD:
1041             ptr = xmlNewChild(record_ptr, ns_record, BAD_CAST "datafield", 0);
1042             xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.datafield.tag);
1043             if (n->u.datafield.indicator)
1044             {
1045                 int i;
1046                 for (i = 0; n->u.datafield.indicator[i]; i++)
1047                 {
1048                     char ind_str[6];
1049                     char ind_val[2];
1050
1051                     sprintf(ind_str, "ind%d", i+1);
1052                     ind_val[0] = n->u.datafield.indicator[i];
1053                     ind_val[1] = '\0';
1054                     xmlNewProp(ptr, BAD_CAST ind_str, BAD_CAST ind_val);
1055                 }
1056             }
1057             for (s = n->u.datafield.subfields; s; s = s->next)
1058             {
1059                 xmlNode *ptr_subfield;
1060                 size_t using_code_len = get_subfield_len(mt, s->code_data,
1061                                                          identifier_length);
1062                 wrbuf_rewind(wr_cdata);
1063                 wrbuf_iconv_puts(wr_cdata, mt->iconv_cd,
1064                                  s->code_data + using_code_len);
1065                 marc_iconv_reset(mt, wr_cdata);
1066                 ptr_subfield = xmlNewTextChild(
1067                     ptr, ns_record,
1068                     BAD_CAST "subfield",  BAD_CAST wrbuf_cstr(wr_cdata));
1069
1070                 wrbuf_rewind(wr_cdata);
1071                 wrbuf_iconv_write(wr_cdata, mt->iconv_cd,
1072                                   s->code_data, using_code_len);
1073                 xmlNewProp(ptr_subfield, BAD_CAST "code",
1074                            BAD_CAST wrbuf_cstr(wr_cdata));
1075             }
1076             break;
1077         case YAZ_MARC_CONTROLFIELD:
1078             wrbuf_rewind(wr_cdata);
1079             wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, n->u.controlfield.data);
1080             marc_iconv_reset(mt, wr_cdata);
1081
1082             ptr = xmlNewTextChild(record_ptr, ns_record,
1083                                   BAD_CAST "controlfield",
1084                                   BAD_CAST wrbuf_cstr(wr_cdata));
1085
1086             xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.controlfield.tag);
1087             break;
1088         case YAZ_MARC_COMMENT:
1089             ptr = xmlNewComment(BAD_CAST n->u.comment);
1090             xmlAddChild(record_ptr, ptr);
1091             break;
1092         case YAZ_MARC_LEADER:
1093             xmlNewTextChild(record_ptr, ns_record, BAD_CAST "leader",
1094                             BAD_CAST n->u.leader);
1095             break;
1096         }
1097     }
1098     wrbuf_destroy(wr_cdata);
1099     return 0;
1100 }
1101
1102 #endif
1103
1104 int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
1105 {
1106     struct yaz_marc_node *n;
1107     int indicator_length;
1108     int identifier_length;
1109     int length_data_entry;
1110     int length_starting;
1111     int length_implementation;
1112     int data_offset = 0;
1113     const char *leader = 0;
1114     WRBUF wr_dir, wr_head, wr_data_tmp;
1115     int base_address;
1116
1117     for (n = mt->nodes; n; n = n->next)
1118         if (n->which == YAZ_MARC_LEADER)
1119             leader = n->u.leader;
1120
1121     if (!leader)
1122         return -1;
1123     if (!atoi_n_check(leader+10, 1, &indicator_length))
1124         return -1;
1125     if (!atoi_n_check(leader+11, 1, &identifier_length))
1126         return -1;
1127     if (!atoi_n_check(leader+20, 1, &length_data_entry))
1128         return -1;
1129     if (!atoi_n_check(leader+21, 1, &length_starting))
1130         return -1;
1131     if (!atoi_n_check(leader+22, 1, &length_implementation))
1132         return -1;
1133
1134     wr_data_tmp = wrbuf_alloc();
1135     wr_dir = wrbuf_alloc();
1136     for (n = mt->nodes; n; n = n->next)
1137     {
1138         int data_length = 0;
1139         struct yaz_marc_subfield *s;
1140
1141         switch(n->which)
1142         {
1143         case YAZ_MARC_DATAFIELD:
1144             wrbuf_printf(wr_dir, "%.3s", n->u.datafield.tag);
1145             data_length += indicator_length;
1146             wrbuf_rewind(wr_data_tmp);
1147             for (s = n->u.datafield.subfields; s; s = s->next)
1148             {
1149                 /* write dummy IDFS + content */
1150                 wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
1151                 wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, s->code_data);
1152                 marc_iconv_reset(mt, wr_data_tmp);
1153             }
1154             /* write dummy FS (makes MARC-8 to become ASCII) */
1155             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
1156             marc_iconv_reset(mt, wr_data_tmp);
1157             data_length += wrbuf_len(wr_data_tmp);
1158             break;
1159         case YAZ_MARC_CONTROLFIELD:
1160             wrbuf_printf(wr_dir, "%.3s", n->u.controlfield.tag);
1161
1162             wrbuf_rewind(wr_data_tmp);
1163             wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd,
1164                              n->u.controlfield.data);
1165             marc_iconv_reset(mt, wr_data_tmp);
1166             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');/* field sep */
1167             marc_iconv_reset(mt, wr_data_tmp);
1168             data_length += wrbuf_len(wr_data_tmp);
1169             break;
1170         case YAZ_MARC_COMMENT:
1171             break;
1172         case YAZ_MARC_LEADER:
1173             break;
1174         }
1175         if (data_length)
1176         {
1177             wrbuf_printf(wr_dir, "%0*d", length_data_entry, data_length);
1178             wrbuf_printf(wr_dir, "%0*d", length_starting, data_offset);
1179             data_offset += data_length;
1180         }
1181     }
1182     /* mark end of directory */
1183     wrbuf_putc(wr_dir, ISO2709_FS);
1184
1185     /* base address of data (comes after leader+directory) */
1186     base_address = 24 + wrbuf_len(wr_dir);
1187
1188     wr_head = wrbuf_alloc();
1189
1190     /* write record length */
1191     wrbuf_printf(wr_head, "%05d", base_address + data_offset + 1);
1192     /* from "original" leader */
1193     wrbuf_write(wr_head, leader+5, 7);
1194     /* base address of data */
1195     wrbuf_printf(wr_head, "%05d", base_address);
1196     /* from "original" leader */
1197     wrbuf_write(wr_head, leader+17, 7);
1198
1199     wrbuf_write(wr, wrbuf_buf(wr_head), 24);
1200     wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
1201     wrbuf_destroy(wr_head);
1202     wrbuf_destroy(wr_dir);
1203     wrbuf_destroy(wr_data_tmp);
1204
1205     for (n = mt->nodes; n; n = n->next)
1206     {
1207         struct yaz_marc_subfield *s;
1208
1209         switch(n->which)
1210         {
1211         case YAZ_MARC_DATAFIELD:
1212             wrbuf_write(wr, n->u.datafield.indicator, indicator_length);
1213             for (s = n->u.datafield.subfields; s; s = s->next)
1214             {
1215                 wrbuf_putc(wr, ISO2709_IDFS);
1216                 wrbuf_iconv_puts(wr, mt->iconv_cd, s->code_data);
1217                 marc_iconv_reset(mt, wr);
1218             }
1219             wrbuf_putc(wr, ISO2709_FS);
1220             break;
1221         case YAZ_MARC_CONTROLFIELD:
1222             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
1223             marc_iconv_reset(mt, wr);
1224             wrbuf_putc(wr, ISO2709_FS);
1225             break;
1226         case YAZ_MARC_COMMENT:
1227             break;
1228         case YAZ_MARC_LEADER:
1229             break;
1230         }
1231     }
1232     wrbuf_printf(wr, "%c", ISO2709_RS);
1233     return 0;
1234 }
1235
1236
1237 int yaz_marc_decode_wrbuf(yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
1238 {
1239     int s, r = yaz_marc_read_iso2709(mt, buf, bsize);
1240     if (r <= 0)
1241         return r;
1242     s = yaz_marc_write_mode(mt, wr); /* returns 0 for OK, -1 otherwise */
1243     if (s != 0)
1244         return -1; /* error */
1245     return r; /* OK, return length > 0 */
1246 }
1247
1248 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
1249                          const char **result, size_t *rsize)
1250 {
1251     int r;
1252
1253     wrbuf_rewind(mt->m_wr);
1254     r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
1255     if (result)
1256         *result = wrbuf_cstr(mt->m_wr);
1257     if (rsize)
1258         *rsize = wrbuf_len(mt->m_wr);
1259     return r;
1260 }
1261
1262 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
1263 {
1264     mt->output_format = xmlmode;
1265 }
1266
1267 void yaz_marc_debug(yaz_marc_t mt, int level)
1268 {
1269     if (mt)
1270         mt->debug = level;
1271 }
1272
1273 void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
1274 {
1275     mt->iconv_cd = cd;
1276 }
1277
1278 yaz_iconv_t yaz_marc_get_iconv(yaz_marc_t mt)
1279 {
1280     return mt->iconv_cd;
1281 }
1282
1283 void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
1284 {
1285     struct yaz_marc_node *n;
1286     char *leader = 0;
1287     for (n = mt->nodes; n; n = n->next)
1288         if (n->which == YAZ_MARC_LEADER)
1289         {
1290             leader = n->u.leader;
1291             memcpy(leader+off, str, strlen(str));
1292             break;
1293         }
1294 }
1295
1296 int yaz_marc_leader_spec(yaz_marc_t mt, const char *leader_spec)
1297 {
1298     xfree(mt->leader_spec);
1299     mt->leader_spec = 0;
1300     if (leader_spec)
1301     {
1302         char dummy_leader[24];
1303         if (marc_exec_leader(leader_spec, dummy_leader, 24))
1304             return -1;
1305         mt->leader_spec = xstrdup(leader_spec);
1306     }
1307     return 0;
1308 }
1309
1310 static int marc_exec_leader(const char *leader_spec, char *leader, size_t size)
1311 {
1312     const char *cp = leader_spec;
1313     while (cp)
1314     {
1315         char val[21];
1316         int pos;
1317         int no_read = 0, no = 0;
1318
1319         no = sscanf(cp, "%d=%20[^,]%n", &pos, val, &no_read);
1320         if (no < 2 || no_read < 3)
1321             return -1;
1322         if (pos < 0 || (size_t) pos >= size)
1323             return -1;
1324
1325         if (*val == '\'')
1326         {
1327             const char *vp = strchr(val+1, '\'');
1328             size_t len;
1329
1330             if (!vp)
1331                 return -1;
1332             len = vp-val-1;
1333             if (len + pos > size)
1334                 return -1;
1335             memcpy(leader + pos, val+1, len);
1336         }
1337         else if (*val >= '0' && *val <= '9')
1338         {
1339             int ch = atoi(val);
1340             leader[pos] = ch;
1341         }
1342         else
1343             return -1;
1344         cp += no_read;
1345         if (*cp != ',')
1346             break;
1347
1348         cp++;
1349     }
1350     return 0;
1351 }
1352
1353 int yaz_marc_decode_formatstr(const char *arg)
1354 {
1355     int mode = -1;
1356     if (!strcmp(arg, "marc"))
1357         mode = YAZ_MARC_ISO2709;
1358     if (!strcmp(arg, "marcxml"))
1359         mode = YAZ_MARC_MARCXML;
1360     if (!strcmp(arg, "turbomarc"))
1361         mode = YAZ_MARC_TURBOMARC;
1362     if (!strcmp(arg, "marcxchange"))
1363         mode = YAZ_MARC_XCHANGE;
1364     if (!strcmp(arg, "line"))
1365         mode = YAZ_MARC_LINE;
1366     return mode;
1367 }
1368
1369 void yaz_marc_write_using_libxml2(yaz_marc_t mt, int enable)
1370 {
1371     mt->write_using_libxml2 = enable;
1372 }
1373
1374 /*
1375  * Local variables:
1376  * c-basic-offset: 4
1377  * c-file-style: "Stroustrup"
1378  * indent-tabs-mode: nil
1379  * End:
1380  * vim: shiftwidth=4 tabstop=8 expandtab
1381  */
1382