Able to make valid turbo marcxml not using libxml2.
[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         wrbuf_printf(wr, "<%s", record_name[turbo]);
618     }
619     else
620     {
621         wrbuf_printf(wr, "<%s xmlns=\"%s\"", record_name[turbo], ns);
622     }
623     if (format)
624         wrbuf_printf(wr, " format=\"%.80s\"", format);
625     if (type)
626         wrbuf_printf(wr, " type=\"%.80s\"", type);
627     wrbuf_printf(wr, ">\n");
628     for (n = mt->nodes; n; n = n->next)
629     {
630         struct yaz_marc_subfield *s;
631
632         switch(n->which)
633         {
634         case YAZ_MARC_DATAFIELD:
635
636                 wrbuf_printf(wr, "  <%s", datafield_name[turbo]);
637             if (!turbo) {
638                 wrbuf_printf(wr, " tag=\"");
639                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
640                                     strlen(n->u.datafield.tag));
641                     wrbuf_printf(wr, "\"");
642                 if (n->u.datafield.indicator)
643                     {
644                     int i;
645                         for (i = 0; n->u.datafield.indicator[i]; i++)
646                         {
647                         wrbuf_printf(wr, " ind%d=\"", i+1);
648                             wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
649                                               n->u.datafield.indicator+i, 1);
650                             wrbuf_iconv_puts(wr, mt->iconv_cd, "\"");
651                         }
652                 }
653                     wrbuf_printf(wr, ">\n");
654             } else {
655                 // TODO Not CDATA.
656                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
657                                 strlen(n->u.datafield.tag));
658                 // Write tag
659                 wrbuf_printf(wr, ">\n");
660                 if (n->u.datafield.indicator)
661                 {
662                         int i;
663                         for (i = 0; n->u.datafield.indicator[i]; i++)
664                         {
665                                 wrbuf_printf(wr, "    <i%d>", i+1);
666                                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
667                                                 n->u.datafield.indicator+i, 1);
668                                 wrbuf_printf(wr, "</i%d>", i+1);
669                         wrbuf_puts(wr, "\n");
670                         }
671                 }
672             }
673             for (s = n->u.datafield.subfields; s; s = s->next)
674             {
675                 size_t using_code_len = get_subfield_len(mt, s->code_data,
676                                                          identifier_length);
677                 wrbuf_printf(wr, "    <%s", subfield_name[turbo]);
678                                 if (!turbo) {
679                                         wrbuf_printf(wr, " code=\"");
680                         wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
681                                         s->code_data, using_code_len);
682                         wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
683                                 } else {
684                                         // TODO check this. encode special characters.
685                         wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
686                                         s->code_data, using_code_len);
687                                         wrbuf_puts(wr, ">");
688                                 }
689                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
690                                         s->code_data + using_code_len,
691                                         strlen(s->code_data + using_code_len));
692                 marc_iconv_reset(mt, wr);
693                                 wrbuf_printf(wr, "</%s", subfield_name[turbo]);
694                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
695                                     s->code_data, using_code_len);
696                 wrbuf_puts(wr, ">\n");
697             }
698             wrbuf_printf(wr, "  </%s", datafield_name[turbo]);
699                 //TODO Not CDATA
700             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
701                         strlen(n->u.datafield.tag));
702             wrbuf_printf(wr, ">\n", datafield_name[turbo]);
703             break;
704         case YAZ_MARC_CONTROLFIELD:
705                 wrbuf_printf(wr, "  <%s", controlfield_name[turbo]);
706                 if (!turbo) {
707                 wrbuf_printf(wr, " tag=\"");
708                         wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
709                                         strlen(n->u.controlfield.tag));
710                         wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
711                 }
712                 else {
713                         //TODO convert special
714                         wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
715                                         strlen(n->u.controlfield.tag));
716                         wrbuf_iconv_puts(wr, mt->iconv_cd, ">");
717                 }
718                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
719                                                                         n->u.controlfield.data,
720                                                                         strlen(n->u.controlfield.data));
721                 marc_iconv_reset(mt, wr);
722                 wrbuf_printf(wr, "</%s", controlfield_name[turbo]);
723                 //TODO convert special
724                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
725                                 strlen(n->u.controlfield.tag));
726                 wrbuf_puts(wr, ">\n");
727             break;
728         case YAZ_MARC_COMMENT:
729             wrbuf_printf(wr, "<!-- ");
730             wrbuf_puts(wr, n->u.comment);
731             wrbuf_printf(wr, " -->\n");
732             break;
733         case YAZ_MARC_LEADER:
734             wrbuf_printf(wr, "  <%s>", leader_name[turbo]);
735             wrbuf_iconv_write_cdata(wr, 
736                                     0 /* no charset conversion for leader */,
737                                     n->u.leader, strlen(n->u.leader));
738             wrbuf_printf(wr, "  </%s>", leader_name[turbo]);
739         }
740     }
741     wrbuf_printf(wr, "</%s>", record_name[turbo]);
742     return 0;
743 }
744
745 static int yaz_marc_write_marcxml_ns2(yaz_marc_t mt, WRBUF wr,
746                                       const char *ns, 
747                                       const char *format,
748                                       const char *type)
749 {
750     struct yaz_marc_node *n;
751     int identifier_length;
752     const char *leader = 0;
753
754     for (n = mt->nodes; n; n = n->next)
755         if (n->which == YAZ_MARC_LEADER)
756         {
757             leader = n->u.leader;
758             break;
759         }
760     
761     if (!leader)
762         return -1;
763     if (!atoi_n_check(leader+11, 1, &identifier_length))
764         return -1;
765     
766     if (mt->enable_collection != no_collection)
767     {
768         if (mt->enable_collection == collection_first)
769             wrbuf_printf(wr, "<collection xmlns=\"%s\">\n", ns);
770         mt->enable_collection = collection_second;
771         wrbuf_printf(wr, "<record");
772     }
773     else
774     {
775         wrbuf_printf(wr, "<record xmlns=\"%s\"", ns);
776     }
777     if (format)
778         wrbuf_printf(wr, " format=\"%.80s\"", format);
779     if (type)
780         wrbuf_printf(wr, " type=\"%.80s\"", type);
781     wrbuf_printf(wr, ">\n");
782     for (n = mt->nodes; n; n = n->next)
783     {
784         struct yaz_marc_subfield *s;
785
786         switch(n->which)
787         {
788         case YAZ_MARC_DATAFIELD:
789             wrbuf_printf(wr, "  <datafield tag=\"");
790             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
791                                     strlen(n->u.datafield.tag));
792             wrbuf_printf(wr, "\"");
793             if (n->u.datafield.indicator)
794             {
795                 int i;
796                 for (i = 0; n->u.datafield.indicator[i]; i++)
797                 {
798                     wrbuf_printf(wr, " ind%d=\"", i+1);
799                     wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
800                                           n->u.datafield.indicator+i, 1);
801                     wrbuf_iconv_puts(wr, mt->iconv_cd, "\"");
802                 }
803             }
804             wrbuf_printf(wr, ">\n");
805             for (s = n->u.datafield.subfields; s; s = s->next)
806             {
807                 size_t using_code_len = get_subfield_len(mt, s->code_data,
808                                                          identifier_length);
809                 wrbuf_iconv_puts(wr, mt->iconv_cd, "    <subfield code=\"");
810                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
811                                         s->code_data, using_code_len);
812                 wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
813                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
814                                         s->code_data + using_code_len,
815                                         strlen(s->code_data + using_code_len));
816                 marc_iconv_reset(mt, wr);
817                 wrbuf_iconv_puts(wr, mt->iconv_cd, "</subfield>");
818                 wrbuf_puts(wr, "\n");
819             }
820             wrbuf_printf(wr, "  </datafield>\n");
821             break;
822         case YAZ_MARC_CONTROLFIELD:
823             wrbuf_printf(wr, "  <controlfield tag=\"");
824             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
825                                     strlen(n->u.controlfield.tag));
826             wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
827             wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
828                                     n->u.controlfield.data,
829                                     strlen(n->u.controlfield.data));
830
831             marc_iconv_reset(mt, wr);
832             wrbuf_iconv_puts(wr, mt->iconv_cd, "</controlfield>");
833             wrbuf_puts(wr, "\n");
834             break;
835         case YAZ_MARC_COMMENT:
836             wrbuf_printf(wr, "<!-- ");
837             wrbuf_puts(wr, n->u.comment);
838             wrbuf_printf(wr, " -->\n");
839             break;
840         case YAZ_MARC_LEADER:
841             wrbuf_printf(wr, "  <leader>");
842             wrbuf_iconv_write_cdata(wr, 
843                                     0 /* no charset conversion for leader */,
844                                     n->u.leader, strlen(n->u.leader));
845             wrbuf_printf(wr, "</leader>\n");
846         }
847     }
848     wrbuf_puts(wr, "</record>\n");
849     return 0;
850 }
851
852
853 static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
854                                      const char *ns, 
855                                      const char *format,
856                                      const char *type)
857 {
858     if (mt->write_using_libxml2)
859     {
860 #if YAZ_HAVE_XML2
861         int ret;
862         xmlNode *root_ptr;
863
864         if (yaz_marc_get_write_format(mt) == YAZ_MARC_MARCXML)
865                 ret = yaz_marc_write_xml(mt, &root_ptr, ns, format, type);
866         else // Check for Turbo XML
867                 ret = yaz_marc_write_turbo_xml(mt, &root_ptr, ns, format, type);
868         if (ret == 0)
869         {
870             xmlChar *buf_out;
871             xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
872             int len_out;
873
874             xmlDocSetRootElement(doc, root_ptr);
875             xmlDocDumpMemory(doc, &buf_out, &len_out);
876
877             wrbuf_write(wr, (const char *) buf_out, len_out);
878             wrbuf_puts(wr, "");
879             xmlFree(buf_out);
880             xmlFreeDoc(doc);
881         }
882         return ret;
883 #else
884         return -1;
885 #endif
886     }
887     else
888         return yaz_marc_write_marcxml_ns1(mt, wr, ns, format, type);
889 }
890
891 int yaz_marc_write_marcxml(yaz_marc_t mt, WRBUF wr)
892 {
893     /* set leader 09 to 'a' for UNICODE */
894     /* http://www.loc.gov/marc/bibliographic/ecbdldrd.html#mrcblea */
895     if (!mt->leader_spec)
896         yaz_marc_modify_leader(mt, 9, "a");
897     char *name_space = "http://www.loc.gov/MARC21/slim";
898     if (mt->output_format == YAZ_MARC_TMARCXML)
899         name_space = "http://www.indexdata.com/MARC21/turboxml";
900     return yaz_marc_write_marcxml_ns(mt, wr, name_space,
901                                      0, 0);
902 }
903
904 int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
905                                const char *format,
906                                const char *type)
907 {
908     return yaz_marc_write_marcxml_ns(mt, wr,
909                                      "info:lc/xmlns/marcxchange-v1",
910                                      0, 0);
911 }
912
913 #if YAZ_HAVE_XML2
914
915 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)
916 {
917     xmlNode *ptr;
918     struct yaz_marc_subfield *s;
919     int turbo = mt->output_format == YAZ_MARC_TMARCXML;
920     if (!turbo) {
921         ptr = xmlNewChild(record_ptr, ns_record, BAD_CAST "datafield", 0);
922         xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.datafield.tag);
923     }
924     else {
925         //TODO consider if safe
926         char field[10];
927         field[0] = 'd';
928         strncpy(field + 1, n->u.datafield.tag, 3);
929         field[4] = '\0';
930         ptr = xmlNewChild(record_ptr, ns_record, BAD_CAST field, 0);
931     }
932     if (n->u.datafield.indicator)
933     {
934         int i;
935         for (i = 0; n->u.datafield.indicator[i]; i++)
936         {
937             char ind_str[6];
938             char ind_val[2];
939             
940             ind_val[0] = n->u.datafield.indicator[i];
941             ind_val[1] = '\0';
942             if (!turbo) {
943                 sprintf(ind_str, "ind%d", i+1);
944                 xmlNewProp(ptr, BAD_CAST ind_str, BAD_CAST ind_val);
945             }
946             else {
947                 sprintf(ind_str, "i%d", i+1);
948                 xmlNewTextChild(ptr, ns_record, BAD_CAST ind_str, BAD_CAST ind_val);
949             }
950         }
951     }
952         WRBUF subfield_name = wrbuf_alloc();
953     for (s = n->u.datafield.subfields; s; s = s->next)
954     {
955         xmlNode *ptr_subfield;
956         size_t using_code_len = get_subfield_len(mt, s->code_data,
957                                                  identifier_length);
958         wrbuf_rewind(wr_cdata);
959         wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, s->code_data + using_code_len);
960         marc_iconv_reset(mt, wr_cdata);
961         
962         if (!turbo) {
963                 ptr_subfield = xmlNewTextChild(
964                                 ptr, ns_record,
965                                 BAD_CAST "subfield",  BAD_CAST wrbuf_cstr(wr_cdata));
966                 wrbuf_rewind(wr_cdata);
967                 wrbuf_iconv_write(wr_cdata, mt->iconv_cd,s->code_data, using_code_len);
968                 xmlNewProp(ptr_subfield, BAD_CAST "code",
969                                 BAD_CAST wrbuf_cstr(wr_cdata));
970         }
971         else { // Turbo format
972                 wrbuf_rewind(subfield_name);
973                 wrbuf_puts(subfield_name, "s");
974                 // TODO Map special codes to something possible for XML ELEMENT names
975                 if ((s->code_data[0] >= '0' && s->code_data[0] <= '9') ||
976                     (s->code_data[0] >= 'a' && s->code_data[0] <= 'z') ||
977                                 (s->code_data[0] >= 'A' && s->code_data[0] <= 'Z'))
978                 {
979                         wrbuf_iconv_write(subfield_name, mt->iconv_cd,s->code_data, using_code_len);
980                 }
981                 else {
982                                 char buffer[2*using_code_len + 1];
983                                 int index;
984                                 for (index = 0; index < using_code_len; index++) {
985                                         sprintf(buffer + 2*index, "%02X", (unsigned char) s->code_data[index] & 0xFF);
986                                 };
987                                 buffer[2*(index+1)] = 0;
988                                 wrbuf_puts(subfield_name, "-");
989                                 wrbuf_puts(subfield_name, buffer);
990                         yaz_log(YLOG_WARN, "Using numeric value in element name: %s", buffer);
991                 }
992                 ptr_subfield = xmlNewTextChild(ptr, ns_record,
993                                 BAD_CAST wrbuf_cstr(subfield_name),
994                                 BAD_CAST wrbuf_cstr(wr_cdata));
995         }
996     }
997         wrbuf_destroy(subfield_name);
998 }
999
1000 int yaz_marc_write_turbo_xml(yaz_marc_t mt, xmlNode **root_ptr,
1001                        const char *ns, 
1002                        const char *format,
1003                        const char *type)
1004 {
1005     struct yaz_marc_node *n;
1006     int identifier_length;
1007     const char *leader = 0;
1008     xmlNode *record_ptr;
1009     xmlNsPtr ns_record;
1010     WRBUF wr_cdata = 0;
1011     int turbo = mt->output_format == YAZ_MARC_TMARCXML;
1012     for (n = mt->nodes; n; n = n->next)
1013         if (n->which == YAZ_MARC_LEADER)
1014         {
1015             leader = n->u.leader;
1016             break;
1017         }
1018     
1019     if (!leader)
1020         return -1;
1021     if (!atoi_n_check(leader+11, 1, &identifier_length))
1022         return -1;
1023
1024     wr_cdata = wrbuf_alloc();
1025
1026     record_ptr = xmlNewNode(0, BAD_CAST "r");
1027     *root_ptr = record_ptr;
1028
1029     ns_record = xmlNewNs(record_ptr, BAD_CAST ns, 0);
1030     xmlSetNs(record_ptr, ns_record);
1031
1032     if (format)
1033         xmlNewProp(record_ptr, BAD_CAST "format", BAD_CAST format);
1034     if (type)
1035         xmlNewProp(record_ptr, BAD_CAST "type", BAD_CAST type);
1036     for (n = mt->nodes; n; n = n->next)
1037     {
1038         struct yaz_marc_subfield *s;
1039         xmlNode *ptr;
1040
1041         switch(n->which)
1042         {
1043         case YAZ_MARC_DATAFIELD:
1044                 add_marc_datafield_turbo_xml(mt, n, record_ptr, ns_record, wr_cdata, identifier_length);
1045             break;
1046         case YAZ_MARC_CONTROLFIELD:
1047             wrbuf_rewind(wr_cdata);
1048             wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, n->u.controlfield.data);
1049             marc_iconv_reset(mt, wr_cdata);
1050             
1051             if (!turbo) {
1052                                 ptr = xmlNewTextChild(record_ptr, ns_record,
1053                                                                           BAD_CAST "controlfield",
1054                                                                           BAD_CAST wrbuf_cstr(wr_cdata));
1055                                 xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.controlfield.tag);
1056             }
1057             else {
1058                 // TODO required iconv?
1059                 char field[10];
1060                                 field[0] = 'c';
1061                 strncpy(field + 1, n->u.controlfield.tag, 3);
1062                 field[4] = '\0';
1063                 ptr = xmlNewTextChild(record_ptr, ns_record,
1064                                                                           BAD_CAST field,
1065                                                                           BAD_CAST wrbuf_cstr(wr_cdata));
1066             }
1067
1068             break;
1069         case YAZ_MARC_COMMENT:
1070             ptr = xmlNewComment(BAD_CAST n->u.comment);
1071             xmlAddChild(record_ptr, ptr);
1072             break;
1073         case YAZ_MARC_LEADER:
1074                         {
1075                                 char *field = "leader";
1076                                 if (turbo)
1077                                         field = "l";
1078                                 xmlNewTextChild(record_ptr, ns_record, BAD_CAST field,
1079                                                                 BAD_CAST n->u.leader);
1080                         }
1081             break;
1082         }
1083     }
1084     wrbuf_destroy(wr_cdata);
1085     return 0;
1086 }
1087
1088
1089 int yaz_marc_write_xml(yaz_marc_t mt, xmlNode **root_ptr,
1090                        const char *ns, 
1091                        const char *format,
1092                        const char *type)
1093 {
1094     struct yaz_marc_node *n;
1095     int identifier_length;
1096     const char *leader = 0;
1097     xmlNode *record_ptr;
1098     xmlNsPtr ns_record;
1099     WRBUF wr_cdata = 0;
1100
1101     for (n = mt->nodes; n; n = n->next)
1102         if (n->which == YAZ_MARC_LEADER)
1103         {
1104             leader = n->u.leader;
1105             break;
1106         }
1107     
1108     if (!leader)
1109         return -1;
1110     if (!atoi_n_check(leader+11, 1, &identifier_length))
1111         return -1;
1112
1113     wr_cdata = wrbuf_alloc();
1114
1115     record_ptr = xmlNewNode(0, BAD_CAST "record");
1116     *root_ptr = record_ptr;
1117
1118     ns_record = xmlNewNs(record_ptr, BAD_CAST ns, 0);
1119     xmlSetNs(record_ptr, ns_record);
1120
1121     if (format)
1122         xmlNewProp(record_ptr, BAD_CAST "format", BAD_CAST format);
1123     if (type)
1124         xmlNewProp(record_ptr, BAD_CAST "type", BAD_CAST type);
1125     for (n = mt->nodes; n; n = n->next)
1126     {
1127         struct yaz_marc_subfield *s;
1128         xmlNode *ptr;
1129
1130         switch(n->which)
1131         {
1132         case YAZ_MARC_DATAFIELD:
1133             ptr = xmlNewChild(record_ptr, ns_record, BAD_CAST "datafield", 0);
1134             xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.datafield.tag);
1135             if (n->u.datafield.indicator)
1136             {
1137                 int i;
1138                 for (i = 0; n->u.datafield.indicator[i]; i++)
1139                 {
1140                     char ind_str[6];
1141                     char ind_val[2];
1142
1143                     sprintf(ind_str, "ind%d", i+1);
1144                     ind_val[0] = n->u.datafield.indicator[i];
1145                     ind_val[1] = '\0';
1146                     xmlNewProp(ptr, BAD_CAST ind_str, BAD_CAST ind_val);
1147                 }
1148             }
1149             for (s = n->u.datafield.subfields; s; s = s->next)
1150             {
1151                 xmlNode *ptr_subfield;
1152                 size_t using_code_len = get_subfield_len(mt, s->code_data,
1153                                                          identifier_length);
1154                 wrbuf_rewind(wr_cdata);
1155                 wrbuf_iconv_puts(wr_cdata, mt->iconv_cd,
1156                                  s->code_data + using_code_len);
1157                 marc_iconv_reset(mt, wr_cdata);
1158                 ptr_subfield = xmlNewTextChild(
1159                     ptr, ns_record,
1160                     BAD_CAST "subfield",  BAD_CAST wrbuf_cstr(wr_cdata));
1161
1162                 wrbuf_rewind(wr_cdata);
1163                 wrbuf_iconv_write(wr_cdata, mt->iconv_cd,
1164                                   s->code_data, using_code_len);
1165                 xmlNewProp(ptr_subfield, BAD_CAST "code",
1166                            BAD_CAST wrbuf_cstr(wr_cdata));
1167             }
1168             break;
1169         case YAZ_MARC_CONTROLFIELD:
1170             wrbuf_rewind(wr_cdata);
1171             wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, n->u.controlfield.data);
1172             marc_iconv_reset(mt, wr_cdata);
1173             
1174             ptr = xmlNewTextChild(record_ptr, ns_record,
1175                                   BAD_CAST "controlfield",
1176                                   BAD_CAST wrbuf_cstr(wr_cdata));
1177             
1178             xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.controlfield.tag);
1179             break;
1180         case YAZ_MARC_COMMENT:
1181             ptr = xmlNewComment(BAD_CAST n->u.comment);
1182             xmlAddChild(record_ptr, ptr);
1183             break;
1184         case YAZ_MARC_LEADER:
1185             xmlNewTextChild(record_ptr, ns_record, BAD_CAST "leader",
1186                             BAD_CAST n->u.leader);
1187             break;
1188         }
1189     }
1190     wrbuf_destroy(wr_cdata);
1191     return 0;
1192 }
1193
1194
1195
1196
1197 #endif
1198
1199 int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
1200 {
1201     struct yaz_marc_node *n;
1202     int indicator_length;
1203     int identifier_length;
1204     int length_data_entry;
1205     int length_starting;
1206     int length_implementation;
1207     int data_offset = 0;
1208     const char *leader = 0;
1209     WRBUF wr_dir, wr_head, wr_data_tmp;
1210     int base_address;
1211     
1212     for (n = mt->nodes; n; n = n->next)
1213         if (n->which == YAZ_MARC_LEADER)
1214             leader = n->u.leader;
1215     
1216     if (!leader)
1217         return -1;
1218     if (!atoi_n_check(leader+10, 1, &indicator_length))
1219         return -1;
1220     if (!atoi_n_check(leader+11, 1, &identifier_length))
1221         return -1;
1222     if (!atoi_n_check(leader+20, 1, &length_data_entry))
1223         return -1;
1224     if (!atoi_n_check(leader+21, 1, &length_starting))
1225         return -1;
1226     if (!atoi_n_check(leader+22, 1, &length_implementation))
1227         return -1;
1228
1229     wr_data_tmp = wrbuf_alloc();
1230     wr_dir = wrbuf_alloc();
1231     for (n = mt->nodes; n; n = n->next)
1232     {
1233         int data_length = 0;
1234         struct yaz_marc_subfield *s;
1235
1236         switch(n->which)
1237         {
1238         case YAZ_MARC_DATAFIELD:
1239             wrbuf_printf(wr_dir, "%.3s", n->u.datafield.tag);
1240             data_length += indicator_length;
1241             wrbuf_rewind(wr_data_tmp);
1242             for (s = n->u.datafield.subfields; s; s = s->next)
1243             {
1244                 /* write dummy IDFS + content */
1245                 wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
1246                 wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, s->code_data);
1247                 marc_iconv_reset(mt, wr_data_tmp);
1248             }
1249             /* write dummy FS (makes MARC-8 to become ASCII) */
1250             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
1251             marc_iconv_reset(mt, wr_data_tmp);
1252             data_length += wrbuf_len(wr_data_tmp);
1253             break;
1254         case YAZ_MARC_CONTROLFIELD:
1255             wrbuf_printf(wr_dir, "%.3s", n->u.controlfield.tag);
1256
1257             wrbuf_rewind(wr_data_tmp);
1258             wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, 
1259                              n->u.controlfield.data);
1260             marc_iconv_reset(mt, wr_data_tmp);
1261             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');/* field sep */
1262             marc_iconv_reset(mt, wr_data_tmp);
1263             data_length += wrbuf_len(wr_data_tmp);
1264             break;
1265         case YAZ_MARC_COMMENT:
1266             break;
1267         case YAZ_MARC_LEADER:
1268             break;
1269         }
1270         if (data_length)
1271         {
1272             wrbuf_printf(wr_dir, "%0*d", length_data_entry, data_length);
1273             wrbuf_printf(wr_dir, "%0*d", length_starting, data_offset);
1274             data_offset += data_length;
1275         }
1276     }
1277     /* mark end of directory */
1278     wrbuf_putc(wr_dir, ISO2709_FS);
1279
1280     /* base address of data (comes after leader+directory) */
1281     base_address = 24 + wrbuf_len(wr_dir);
1282
1283     wr_head = wrbuf_alloc();
1284
1285     /* write record length */
1286     wrbuf_printf(wr_head, "%05d", base_address + data_offset + 1);
1287     /* from "original" leader */
1288     wrbuf_write(wr_head, leader+5, 7);
1289     /* base address of data */
1290     wrbuf_printf(wr_head, "%05d", base_address);
1291     /* from "original" leader */
1292     wrbuf_write(wr_head, leader+17, 7);
1293     
1294     wrbuf_write(wr, wrbuf_buf(wr_head), 24);
1295     wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
1296     wrbuf_destroy(wr_head);
1297     wrbuf_destroy(wr_dir);
1298     wrbuf_destroy(wr_data_tmp);
1299
1300     for (n = mt->nodes; n; n = n->next)
1301     {
1302         struct yaz_marc_subfield *s;
1303
1304         switch(n->which)
1305         {
1306         case YAZ_MARC_DATAFIELD:
1307             wrbuf_printf(wr, "%.*s", indicator_length,
1308                          n->u.datafield.indicator);
1309             for (s = n->u.datafield.subfields; s; s = s->next)
1310             {
1311                 wrbuf_putc(wr, ISO2709_IDFS);
1312                 wrbuf_iconv_puts(wr, mt->iconv_cd, s->code_data);
1313                 marc_iconv_reset(mt, wr);
1314             }
1315             wrbuf_putc(wr, ISO2709_FS);
1316             break;
1317         case YAZ_MARC_CONTROLFIELD:
1318             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
1319             marc_iconv_reset(mt, wr);
1320             wrbuf_putc(wr, ISO2709_FS);
1321             break;
1322         case YAZ_MARC_COMMENT:
1323             break;
1324         case YAZ_MARC_LEADER:
1325             break;
1326         }
1327     }
1328     wrbuf_printf(wr, "%c", ISO2709_RS);
1329     return 0;
1330 }
1331
1332
1333 int yaz_marc_decode_wrbuf(yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
1334 {
1335     int s, r = yaz_marc_read_iso2709(mt, buf, bsize);
1336     if (r <= 0)
1337         return r;
1338     s = yaz_marc_write_mode(mt, wr); /* returns 0 for OK, -1 otherwise */
1339     if (s != 0)
1340         return -1; /* error */
1341     return r; /* OK, return length > 0 */
1342 }
1343
1344 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
1345                          const char **result, size_t *rsize)
1346 {
1347     int r;
1348
1349     wrbuf_rewind(mt->m_wr);
1350     r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
1351     if (result)
1352         *result = wrbuf_cstr(mt->m_wr);
1353     if (rsize)
1354         *rsize = wrbuf_len(mt->m_wr);
1355     return r;
1356 }
1357
1358 void yaz_marc_set_read_format(yaz_marc_t mt, int format)
1359 {
1360     if (mt)
1361         mt->input_format = format;
1362 }
1363
1364 int yaz_marc_get_read_format(yaz_marc_t mt)
1365 {
1366     if (mt)
1367         return mt->input_format;
1368     return -1;
1369 }
1370
1371
1372 void yaz_marc_set_write_format(yaz_marc_t mt, int format)
1373 {
1374     if (mt) {
1375         mt->output_format = format;
1376 /*
1377         // Force using libxml2
1378         if (mt->output_format == YAZ_MARC_TMARCXML)
1379                 mt->write_using_libxml2 = 1;
1380 */
1381     }
1382 }
1383
1384 int yaz_marc_get_write_format(yaz_marc_t mt)
1385 {
1386     if (mt)
1387         return mt->output_format;
1388     return -1;
1389 }
1390
1391
1392 /**
1393  * Deprecated, use yaz_marc_set_write_format
1394  */
1395 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
1396 {
1397         yaz_marc_set_write_format(mt, xmlmode);
1398 }
1399
1400
1401
1402 void yaz_marc_debug(yaz_marc_t mt, int level)
1403 {
1404     if (mt)
1405         mt->debug = level;
1406 }
1407
1408 void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
1409 {
1410     mt->iconv_cd = cd;
1411 }
1412
1413 yaz_iconv_t yaz_marc_get_iconv(yaz_marc_t mt)
1414 {
1415     return mt->iconv_cd;
1416 }
1417
1418 void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
1419 {
1420     struct yaz_marc_node *n;
1421     char *leader = 0;
1422     for (n = mt->nodes; n; n = n->next)
1423         if (n->which == YAZ_MARC_LEADER)
1424         {
1425             leader = n->u.leader;
1426             memcpy(leader+off, str, strlen(str));
1427             break;
1428         }
1429 }
1430
1431 int yaz_marc_leader_spec(yaz_marc_t mt, const char *leader_spec)
1432 {
1433     xfree(mt->leader_spec);
1434     mt->leader_spec = 0;
1435     if (leader_spec)
1436     {
1437         char dummy_leader[24];
1438         if (marc_exec_leader(leader_spec, dummy_leader, 24))
1439             return -1;
1440         mt->leader_spec = xstrdup(leader_spec);
1441     }
1442     return 0;
1443 }
1444
1445 static int marc_exec_leader(const char *leader_spec, char *leader, size_t size)
1446 {
1447     const char *cp = leader_spec;
1448     while (cp)
1449     {
1450         char val[21];
1451         int pos;
1452         int no_read = 0, no = 0;
1453
1454         no = sscanf(cp, "%d=%20[^,]%n", &pos, val, &no_read);
1455         if (no < 2 || no_read < 3)
1456             return -1;
1457         if (pos < 0 || (size_t) pos >= size)
1458             return -1;
1459
1460         if (*val == '\'')
1461         {
1462             const char *vp = strchr(val+1, '\'');
1463             size_t len;
1464             
1465             if (!vp)
1466                 return -1;
1467             len = vp-val-1;
1468             if (len + pos > size)
1469                 return -1;
1470             memcpy(leader + pos, val+1, len);
1471         }
1472         else if (*val >= '0' && *val <= '9')
1473         {
1474             int ch = atoi(val);
1475             leader[pos] = ch;
1476         }
1477         else
1478             return -1;
1479         cp += no_read;
1480         if (*cp != ',')
1481             break;
1482
1483         cp++;
1484     }
1485     return 0;
1486 }
1487
1488 int yaz_marc_decode_formatstr(const char *arg)
1489 {
1490     int mode = -1; 
1491     if (!strcmp(arg, "marc"))
1492         mode = YAZ_MARC_ISO2709;
1493     if (!strcmp(arg, "marcxml"))
1494         mode = YAZ_MARC_MARCXML;
1495     if (!strcmp(arg, "tmarcxml"))
1496         mode = YAZ_MARC_TMARCXML;
1497     if (!strcmp(arg, "marcxchange"))
1498         mode = YAZ_MARC_XCHANGE;
1499     if (!strcmp(arg, "line"))
1500         mode = YAZ_MARC_LINE;
1501     return mode;
1502 }
1503
1504 void yaz_marc_write_using_libxml2(yaz_marc_t mt, int enable)
1505 {
1506     mt->write_using_libxml2 = enable;
1507 }
1508
1509 int yaz_marc_is_turbo_format(yaz_marc_t mt)
1510 {
1511     return mt->output_format == YAZ_MARC_TMARCXML;
1512 }
1513
1514
1515 /*
1516  * Local variables:
1517  * c-basic-offset: 4
1518  * c-file-style: "Stroustrup"
1519  * indent-tabs-mode: nil
1520  * End:
1521  * vim: shiftwidth=4 tabstop=8 expandtab
1522  */
1523