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