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