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