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