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