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