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