Remove dead code
[yaz-moved-to-github.git] / src / marcdisp.c
1 /*
2  * Copyright (C) 1995-2007, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: marcdisp.c,v 1.45 2007-01-22 09:21:16 adam Exp $
6  */
7
8 /**
9  * \file marcdisp.c
10  * \brief Implements MARC conversion utilities
11  */
12
13 #if HAVE_CONFIG_H
14 #include <config.h>
15 #endif
16
17 #ifdef WIN32
18 #include <windows.h>
19 #endif
20
21 #include <stdarg.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <yaz/marcdisp.h>
26 #include <yaz/wrbuf.h>
27 #include <yaz/yaz-util.h>
28 #include <yaz/nmem_xml.h>
29
30 #if YAZ_HAVE_XML2
31 #include <libxml/parser.h>
32 #include <libxml/tree.h>
33 #endif
34
35 /** \brief node types for yaz_marc_node */
36 enum YAZ_MARC_NODE_TYPE
37
38     YAZ_MARC_DATAFIELD,
39     YAZ_MARC_CONTROLFIELD,
40     YAZ_MARC_COMMENT,
41     YAZ_MARC_LEADER
42 };
43
44 /** \brief represets a data field */
45 struct yaz_marc_datafield {
46     char *tag;
47     char *indicator;
48     struct yaz_marc_subfield *subfields;
49 };
50
51 /** \brief represents a control field */
52 struct yaz_marc_controlfield {
53     char *tag;
54     char *data;
55 };
56
57 /** \brief a comment node */
58 struct yaz_marc_comment {
59     char *comment;
60 };
61
62 /** \brief MARC node */
63 struct yaz_marc_node {
64     enum YAZ_MARC_NODE_TYPE which;
65     union {
66         struct yaz_marc_datafield datafield;
67         struct yaz_marc_controlfield controlfield;
68         char *comment;
69         char *leader;
70     } u;
71     struct yaz_marc_node *next;
72 };
73
74 /** \brief represents a subfield */
75 struct yaz_marc_subfield {
76     char *code_data;
77     struct yaz_marc_subfield *next;
78 };
79
80 /** \brief the internals of a yaz_marc_t handle */
81 struct yaz_marc_t_ {
82     WRBUF m_wr;
83     NMEM nmem;
84     int xml;
85     int debug;
86     int write_using_libxml2;
87     yaz_iconv_t iconv_cd;
88     char subfield_str[8];
89     char endline_str[8];
90     char *leader_spec;
91     struct yaz_marc_node *nodes;
92     struct yaz_marc_node **nodes_pp;
93     struct yaz_marc_subfield **subfield_pp;
94 };
95
96 yaz_marc_t yaz_marc_create(void)
97 {
98     yaz_marc_t mt = (yaz_marc_t) xmalloc(sizeof(*mt));
99     mt->xml = YAZ_MARC_LINE;
100     mt->debug = 0;
101     mt->write_using_libxml2 = 0;
102     mt->m_wr = wrbuf_alloc();
103     mt->iconv_cd = 0;
104     mt->leader_spec = 0;
105     strcpy(mt->subfield_str, " $");
106     strcpy(mt->endline_str, "\n");
107
108     mt->nmem = nmem_create();
109     yaz_marc_reset(mt);
110     return mt;
111 }
112
113 void yaz_marc_destroy(yaz_marc_t mt)
114 {
115     if (!mt)
116         return ;
117     nmem_destroy(mt->nmem);
118     wrbuf_free(mt->m_wr, 1);
119     xfree(mt->leader_spec);
120     xfree(mt);
121 }
122
123 NMEM yaz_marc_get_nmem(yaz_marc_t mt)
124 {
125     return mt->nmem;
126 }
127
128 static void marc_iconv_reset(yaz_marc_t mt, WRBUF wr)
129 {
130     if (mt->iconv_cd)
131     {
132         char outbuf[12];
133         size_t outbytesleft = sizeof(outbuf);
134         char *outp = outbuf;
135         size_t r = yaz_iconv(mt->iconv_cd, 0, 0, &outp, &outbytesleft);
136         if (r != (size_t) (-1))
137             wrbuf_write(wr, outbuf, outp - outbuf);
138     }
139 }
140
141 static int marc_exec_leader(const char *leader_spec, char *leader,
142                             size_t size);
143
144
145 static struct yaz_marc_node *yaz_marc_add_node(yaz_marc_t mt)
146 {
147     struct yaz_marc_node *n = nmem_malloc(mt->nmem, sizeof(*n));
148     n->next = 0;
149     *mt->nodes_pp = n;
150     mt->nodes_pp = &n->next;
151     return n;
152 }
153
154 #if YAZ_HAVE_XML2
155 void yaz_marc_add_controlfield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
156                                    const xmlNode *ptr_data)
157 {
158     struct yaz_marc_node *n = yaz_marc_add_node(mt);
159     n->which = YAZ_MARC_CONTROLFIELD;
160     n->u.controlfield.tag = nmem_text_node_cdata(ptr_tag, mt->nmem);
161     n->u.controlfield.data = nmem_text_node_cdata(ptr_data, mt->nmem);
162 }
163 #endif
164
165
166 void yaz_marc_add_comment(yaz_marc_t mt, char *comment)
167 {
168     struct yaz_marc_node *n = yaz_marc_add_node(mt);
169     n->which = YAZ_MARC_COMMENT;
170     n->u.comment = nmem_strdup(mt->nmem, comment);
171 }
172
173 void yaz_marc_cprintf(yaz_marc_t mt, const char *fmt, ...)
174 {
175     va_list ap;
176     char buf[200];
177     va_start(ap, fmt);
178
179 #ifdef WIN32
180     _vsnprintf(buf, sizeof(buf)-1, fmt, ap);
181 #else
182 /* !WIN32 */
183 #if HAVE_VSNPRINTF
184     vsnprintf(buf, sizeof(buf), fmt, ap);
185 #else
186     vsprintf(buf, fmt, ap);
187 #endif
188 #endif
189 /* WIN32 */
190     yaz_marc_add_comment(mt, buf);
191     va_end (ap);
192 }
193
194 int yaz_marc_get_debug(yaz_marc_t mt)
195 {
196     return mt->debug;
197 }
198
199 void yaz_marc_add_leader(yaz_marc_t mt, const char *leader, size_t leader_len)
200 {
201     struct yaz_marc_node *n = yaz_marc_add_node(mt);
202     n->which = YAZ_MARC_LEADER;
203     n->u.leader = nmem_strdupn(mt->nmem, leader, leader_len);
204     marc_exec_leader(mt->leader_spec, n->u.leader, leader_len);
205 }
206
207 void yaz_marc_add_controlfield(yaz_marc_t mt, const char *tag,
208                                const char *data, size_t data_len)
209 {
210     struct yaz_marc_node *n = yaz_marc_add_node(mt);
211     n->which = YAZ_MARC_CONTROLFIELD;
212     n->u.controlfield.tag = nmem_strdup(mt->nmem, tag);
213     n->u.controlfield.data = nmem_strdupn(mt->nmem, data, data_len);
214     if (mt->debug)
215     {
216         size_t i;
217         char msg[80];
218
219         sprintf(msg, "controlfield:");
220         for (i = 0; i < 16 && i < data_len; i++)
221             sprintf(msg + strlen(msg), " %02X", data[i] & 0xff);
222         if (i < data_len)
223             sprintf(msg + strlen(msg), " ..");
224         yaz_marc_add_comment(mt, msg);
225     }
226 }
227
228 void yaz_marc_add_datafield(yaz_marc_t mt, const char *tag,
229                             const char *indicator, size_t indicator_len)
230 {
231     struct yaz_marc_node *n = yaz_marc_add_node(mt);
232     n->which = YAZ_MARC_DATAFIELD;
233     n->u.datafield.tag = nmem_strdup(mt->nmem, tag);
234     n->u.datafield.indicator =
235         nmem_strdupn(mt->nmem, indicator, indicator_len);
236     n->u.datafield.subfields = 0;
237
238     /* make subfield_pp the current (last one) */
239     mt->subfield_pp = &n->u.datafield.subfields;
240 }
241
242 #if YAZ_HAVE_XML2
243 void yaz_marc_add_datafield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
244                                 const char *indicator, size_t indicator_len)
245 {
246     struct yaz_marc_node *n = yaz_marc_add_node(mt);
247     n->which = YAZ_MARC_DATAFIELD;
248     n->u.datafield.tag = nmem_text_node_cdata(ptr_tag, mt->nmem);
249     n->u.datafield.indicator =
250         nmem_strdupn(mt->nmem, indicator, indicator_len);
251     n->u.datafield.subfields = 0;
252
253     /* make subfield_pp the current (last one) */
254     mt->subfield_pp = &n->u.datafield.subfields;
255 }
256 #endif
257
258 void yaz_marc_add_subfield(yaz_marc_t mt,
259                            const char *code_data, size_t code_data_len)
260 {
261     if (mt->debug)
262     {
263         size_t i;
264         char msg[80];
265
266         sprintf(msg, "subfield:");
267         for (i = 0; i < 16 && i < code_data_len; i++)
268             sprintf(msg + strlen(msg), " %02X", code_data[i] & 0xff);
269         if (i < code_data_len)
270             sprintf(msg + strlen(msg), " ..");
271         yaz_marc_add_comment(mt, msg);
272     }
273
274     if (mt->subfield_pp)
275     {
276         struct yaz_marc_subfield *n = nmem_malloc(mt->nmem, sizeof(*n));
277         n->code_data = nmem_strdupn(mt->nmem, code_data, code_data_len);
278         n->next = 0;
279         /* mark subfield_pp to point to this one, so we append here next */
280         *mt->subfield_pp = n;
281         mt->subfield_pp = &n->next;
282     }
283 }
284
285 int atoi_n_check(const char *buf, int size, int *val)
286 {
287     int i;
288     for (i = 0; i < size; i++)
289         if (!isdigit(i[(const unsigned char *) buf]))
290             return 0;
291     *val = atoi_n(buf, size);
292     return 1;
293 }
294
295 void yaz_marc_set_leader(yaz_marc_t mt, const char *leader_c,
296                          int *indicator_length,
297                          int *identifier_length,
298                          int *base_address,
299                          int *length_data_entry,
300                          int *length_starting,
301                          int *length_implementation)
302 {
303     char leader[24];
304
305     memcpy(leader, leader_c, 24);
306
307     if (!atoi_n_check(leader+10, 1, indicator_length))
308     {
309         yaz_marc_cprintf(mt, 
310                          "Indicator length at offset 10 should hold a digit."
311                          " Assuming 2");
312         leader[10] = '2';
313         *indicator_length = 2;
314     }
315     if (!atoi_n_check(leader+11, 1, identifier_length))
316     {
317         yaz_marc_cprintf(mt, 
318                          "Identifier length at offset 11 should hold a digit."
319                          " Assuming 2");
320         leader[11] = '2';
321         *identifier_length = 2;
322     }
323     if (!atoi_n_check(leader+12, 5, base_address))
324     {
325         yaz_marc_cprintf(mt, 
326                          "Base address at offsets 12..16 should hold a number."
327                          " Assuming 0");
328         *base_address = 0;
329     }
330     if (!atoi_n_check(leader+20, 1, length_data_entry))
331     {
332         yaz_marc_cprintf(mt, 
333                          "Length data entry at offset 20 should hold a digit."
334                          " Assuming 4");
335         *length_data_entry = 4;
336         leader[20] = '4';
337     }
338     if (!atoi_n_check(leader+21, 1, length_starting))
339     {
340         yaz_marc_cprintf(mt,
341                          "Length starting at offset 21 should hold a digit."
342                          " Assuming 5");
343         *length_starting = 5;
344         leader[21] = '5';
345     }
346     if (!atoi_n_check(leader+22, 1, length_implementation))
347     {
348         yaz_marc_cprintf(mt, 
349                          "Length implementation at offset 22 should hold a digit."
350                          " Assuming 0");
351         *length_implementation = 0;
352         leader[22] = '0';
353     }
354
355     if (mt->debug)
356     {
357         yaz_marc_cprintf(mt, "Indicator length      %5d", *indicator_length);
358         yaz_marc_cprintf(mt, "Identifier length     %5d", *identifier_length);
359         yaz_marc_cprintf(mt, "Base address          %5d", *base_address);
360         yaz_marc_cprintf(mt, "Length data entry     %5d", *length_data_entry);
361         yaz_marc_cprintf(mt, "Length starting       %5d", *length_starting);
362         yaz_marc_cprintf(mt, "Length implementation %5d", *length_implementation);
363     }
364     yaz_marc_add_leader(mt, leader, 24);
365 }
366
367 void yaz_marc_subfield_str(yaz_marc_t mt, const char *s)
368 {
369     strncpy(mt->subfield_str, s, sizeof(mt->subfield_str)-1);
370     mt->subfield_str[sizeof(mt->subfield_str)-1] = '\0';
371 }
372
373 void yaz_marc_endline_str(yaz_marc_t mt, const char *s)
374 {
375     strncpy(mt->endline_str, s, sizeof(mt->endline_str)-1);
376     mt->endline_str[sizeof(mt->endline_str)-1] = '\0';
377 }
378
379 /* try to guess how many bytes the identifier really is! */
380 static size_t cdata_one_character(yaz_marc_t mt, const char *buf)
381 {
382     if (mt->iconv_cd)
383     {
384         size_t i;
385         for (i = 1; i<5; i++)
386         {
387             char outbuf[12];
388             size_t outbytesleft = sizeof(outbuf);
389             char *outp = outbuf;
390             const char *inp = buf;
391
392             size_t inbytesleft = i;
393             size_t r = yaz_iconv(mt->iconv_cd, (char**) &inp, &inbytesleft,
394                                  &outp, &outbytesleft);
395             if (r != (size_t) (-1))
396                 return i;  /* got a complete sequence */
397         }
398         return 1; /* giving up */
399     }
400     return 1; /* we don't know */
401 }
402                               
403 void yaz_marc_reset(yaz_marc_t mt)
404 {
405     nmem_reset(mt->nmem);
406     mt->nodes = 0;
407     mt->nodes_pp = &mt->nodes;
408     mt->subfield_pp = 0;
409 }
410
411 int yaz_marc_write_check(yaz_marc_t mt, WRBUF wr)
412 {
413     struct yaz_marc_node *n;
414     int identifier_length;
415     const char *leader = 0;
416
417     for (n = mt->nodes; n; n = n->next)
418         if (n->which == YAZ_MARC_LEADER)
419         {
420             leader = n->u.leader;
421             break;
422         }
423     
424     if (!leader)
425         return -1;
426     if (!atoi_n_check(leader+11, 1, &identifier_length))
427         return -1;
428
429     for (n = mt->nodes; n; n = n->next)
430     {
431         switch(n->which)
432         {
433         case YAZ_MARC_COMMENT:
434             wrbuf_iconv_write(wr, mt->iconv_cd, 
435                               n->u.comment, strlen(n->u.comment));
436             wrbuf_puts(wr, ")\n");
437             break;
438         default:
439             break;
440         }
441     }
442     return 0;
443 }
444
445
446 int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
447 {
448     struct yaz_marc_node *n;
449     int identifier_length;
450     const char *leader = 0;
451
452     for (n = mt->nodes; n; n = n->next)
453         if (n->which == YAZ_MARC_LEADER)
454         {
455             leader = n->u.leader;
456             break;
457         }
458     
459     if (!leader)
460         return -1;
461     if (!atoi_n_check(leader+11, 1, &identifier_length))
462         return -1;
463
464     for (n = mt->nodes; n; n = n->next)
465     {
466         struct yaz_marc_subfield *s;
467         switch(n->which)
468         {
469         case YAZ_MARC_DATAFIELD:
470             wrbuf_printf(wr, "%s %s", n->u.datafield.tag,
471                          n->u.datafield.indicator);
472             for (s = n->u.datafield.subfields; s; s = s->next)
473             {
474                 /* if identifier length is 2 (most MARCs),
475                    the code is a single character .. However we've
476                    seen multibyte codes, so see how big it really is */
477                 size_t using_code_len = 
478                     (identifier_length != 2) ? identifier_length - 1
479                     :
480                     cdata_one_character(mt, s->code_data);
481                 
482                 wrbuf_puts (wr, mt->subfield_str); 
483                 wrbuf_iconv_write(wr, mt->iconv_cd, s->code_data, 
484                                   using_code_len);
485                 wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
486                 wrbuf_iconv_puts(wr, mt->iconv_cd, 
487                                  s->code_data + using_code_len);
488                 marc_iconv_reset(mt, wr);
489             }
490             wrbuf_puts (wr, mt->endline_str);
491             break;
492         case YAZ_MARC_CONTROLFIELD:
493             wrbuf_printf(wr, "%s", n->u.controlfield.tag);
494             wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
495             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
496             marc_iconv_reset(mt, wr);
497             wrbuf_puts (wr, mt->endline_str);
498             break;
499         case YAZ_MARC_COMMENT:
500             wrbuf_puts(wr, "(");
501             wrbuf_iconv_write(wr, mt->iconv_cd, 
502                               n->u.comment, strlen(n->u.comment));
503             wrbuf_puts(wr, ")\n");
504             break;
505         case YAZ_MARC_LEADER:
506             wrbuf_printf(wr, "%s\n", n->u.leader);
507         }
508     }
509     wrbuf_puts(wr, "\n");
510     return 0;
511 }
512
513 int yaz_marc_write_mode(yaz_marc_t mt, WRBUF wr)
514 {
515     switch(mt->xml)
516     {
517     case YAZ_MARC_LINE:
518         return yaz_marc_write_line(mt, wr);
519     case YAZ_MARC_MARCXML:
520         return yaz_marc_write_marcxml(mt, wr);
521     case YAZ_MARC_XCHANGE:
522         return yaz_marc_write_marcxchange(mt, wr, 0, 0); /* no format, type */
523     case YAZ_MARC_ISO2709:
524         return yaz_marc_write_iso2709(mt, wr);
525     case YAZ_MARC_CHECK:
526         return yaz_marc_write_check(mt, wr);
527     }
528     return -1;
529 }
530
531 /** \brief common MARC XML/Xchange writer
532     \param mt handle
533     \param wr WRBUF output
534     \param ns XMLNS for the elements
535     \param format record format (e.g. "MARC21")
536     \param type record type (e.g. "Bibliographic")
537 */
538 static int yaz_marc_write_marcxml_ns1(yaz_marc_t mt, WRBUF wr,
539                                       const char *ns, 
540                                       const char *format,
541                                       const char *type)
542 {
543     struct yaz_marc_node *n;
544     int identifier_length;
545     const char *leader = 0;
546
547     for (n = mt->nodes; n; n = n->next)
548         if (n->which == YAZ_MARC_LEADER)
549         {
550             leader = n->u.leader;
551             break;
552         }
553     
554     if (!leader)
555         return -1;
556     if (!atoi_n_check(leader+11, 1, &identifier_length))
557         return -1;
558
559     wrbuf_printf(wr, "<record xmlns=\"%s\"", ns);
560     if (format)
561         wrbuf_printf(wr, " format=\"%.80s\"", format);
562     if (type)
563         wrbuf_printf(wr, " type=\"%.80s\"", type);
564     wrbuf_printf(wr, ">\n");
565     for (n = mt->nodes; n; n = n->next)
566     {
567         struct yaz_marc_subfield *s;
568
569         switch(n->which)
570         {
571         case YAZ_MARC_DATAFIELD:
572             wrbuf_printf(wr, "  <datafield tag=\"");
573             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
574                                     strlen(n->u.datafield.tag));
575             wrbuf_printf(wr, "\"");
576             if (n->u.datafield.indicator)
577             {
578                 int i;
579                 for (i = 0; n->u.datafield.indicator[i]; i++)
580                 {
581                     wrbuf_printf(wr, " ind%d=\"", i+1);
582                     wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
583                                           n->u.datafield.indicator+i, 1);
584                     wrbuf_iconv_puts(wr, mt->iconv_cd, "\"");
585                 }
586             }
587             wrbuf_printf(wr, ">\n");
588             for (s = n->u.datafield.subfields; s; s = s->next)
589             {
590                 /* if identifier length is 2 (most MARCs),
591                    the code is a single character .. However we've
592                    seen multibyte codes, so see how big it really is */
593                 size_t using_code_len = 
594                     (identifier_length != 2) ? identifier_length - 1
595                     :
596                     cdata_one_character(mt, s->code_data);
597                 
598                 wrbuf_iconv_puts(wr, mt->iconv_cd, "    <subfield code=\"");
599                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
600                                         s->code_data, using_code_len);
601                 wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
602                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
603                                         s->code_data + using_code_len,
604                                         strlen(s->code_data + using_code_len));
605                 marc_iconv_reset(mt, wr);
606                 wrbuf_iconv_puts(wr, mt->iconv_cd, "</subfield>");
607                 wrbuf_puts(wr, "\n");
608             }
609             wrbuf_printf(wr, "  </datafield>\n");
610             break;
611         case YAZ_MARC_CONTROLFIELD:
612             wrbuf_printf(wr, "  <controlfield tag=\"");
613             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
614                                     strlen(n->u.controlfield.tag));
615             wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
616             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
617
618             marc_iconv_reset(mt, wr);
619             wrbuf_iconv_puts(wr, mt->iconv_cd, "</controlfield>");
620             wrbuf_puts(wr, "\n");
621             break;
622         case YAZ_MARC_COMMENT:
623             wrbuf_printf(wr, "<!-- ");
624             wrbuf_puts(wr, n->u.comment);
625             wrbuf_printf(wr, " -->\n");
626             break;
627         case YAZ_MARC_LEADER:
628             wrbuf_printf(wr, "  <leader>");
629             wrbuf_iconv_write_cdata(wr, 
630                                     0 /* no charset conversion for leader */,
631                                     n->u.leader, strlen(n->u.leader));
632             wrbuf_printf(wr, "</leader>\n");
633         }
634     }
635     wrbuf_puts(wr, "</record>\n");
636     return 0;
637 }
638
639 static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
640                                      const char *ns, 
641                                      const char *format,
642                                      const char *type)
643 {
644     if (mt->write_using_libxml2)
645     {
646         int ret;
647         xmlNode *root_ptr;
648
649         ret = yaz_marc_write_xml(mt, &root_ptr, ns, format, type);
650         if (ret == 0)
651         {
652             xmlChar *buf_out;
653             xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
654             int len_out;
655
656             xmlDocSetRootElement(doc, root_ptr);
657             xmlDocDumpMemory(doc, &buf_out, &len_out);
658
659             wrbuf_write(wr, (const char *) buf_out, len_out);
660             wrbuf_puts(wr, "");
661             xmlFree(buf_out);
662             xmlFreeDoc(doc);
663         }
664         return ret;
665     }
666     else
667         return yaz_marc_write_marcxml_ns1(mt, wr, ns, format, type);
668 }
669
670 int yaz_marc_write_marcxml(yaz_marc_t mt, WRBUF wr)
671 {
672     if (!mt->leader_spec)
673         yaz_marc_modify_leader(mt, 9, "a");
674     return yaz_marc_write_marcxml_ns(mt, wr, "http://www.loc.gov/MARC21/slim",
675                                      0, 0);
676 }
677
678 int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
679                                const char *format,
680                                const char *type)
681 {
682     return yaz_marc_write_marcxml_ns(mt, wr,
683                                      "http://www.bs.dk/standards/MarcXchange",
684                                      0, 0);
685 }
686
687
688 int yaz_marc_write_xml(yaz_marc_t mt, xmlNode **root_ptr,
689                        const char *ns, 
690                        const char *format,
691                        const char *type)
692 {
693 #if YAZ_HAVE_XML2
694     struct yaz_marc_node *n;
695     int identifier_length;
696     const char *leader = 0;
697     xmlNode *record_ptr;
698     xmlNsPtr ns_record;
699     WRBUF wr_cdata = 0;
700
701     for (n = mt->nodes; n; n = n->next)
702         if (n->which == YAZ_MARC_LEADER)
703         {
704             leader = n->u.leader;
705             break;
706         }
707     
708     if (!leader)
709         return -1;
710     if (!atoi_n_check(leader+11, 1, &identifier_length))
711         return -1;
712
713     wr_cdata = wrbuf_alloc();
714
715     record_ptr = xmlNewNode(0, BAD_CAST "record");
716     *root_ptr = record_ptr;
717
718     ns_record = xmlNewNs(record_ptr, BAD_CAST ns, 0);
719     xmlSetNs(record_ptr, ns_record);
720
721     if (format)
722         xmlNewProp(record_ptr, BAD_CAST "format", BAD_CAST format);
723     if (type)
724         xmlNewProp(record_ptr, BAD_CAST "type", BAD_CAST type);
725     for (n = mt->nodes; n; n = n->next)
726     {
727         struct yaz_marc_subfield *s;
728         xmlNode *ptr;
729
730         switch(n->which)
731         {
732         case YAZ_MARC_DATAFIELD:
733             ptr = xmlNewChild(record_ptr, ns_record, BAD_CAST "datafield", 0);
734             xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.datafield.tag);
735             if (n->u.datafield.indicator)
736             {
737                 int i;
738                 for (i = 0; n->u.datafield.indicator[i]; i++)
739                 {
740                     char ind_str[6];
741                     char ind_val[2];
742
743                     sprintf(ind_str, "ind%d", i+1);
744                     ind_val[0] = n->u.datafield.indicator[i];
745                     ind_val[1] = '\0';
746                     xmlNewProp(ptr, BAD_CAST ind_str, BAD_CAST ind_val);
747                 }
748             }
749             for (s = n->u.datafield.subfields; s; s = s->next)
750             {
751                 xmlNode *ptr_subfield;
752                 /* if identifier length is 2 (most MARCs),
753                    the code is a single character .. However we've
754                    seen multibyte codes, so see how big it really is */
755                 size_t using_code_len = 
756                     (identifier_length != 2) ? identifier_length - 1
757                     :
758                     cdata_one_character(mt, s->code_data);
759
760                 wrbuf_rewind(wr_cdata);
761                 wrbuf_iconv_puts(wr_cdata, mt->iconv_cd,
762                                  s->code_data + using_code_len);
763                 marc_iconv_reset(mt, wr_cdata);
764                 ptr_subfield = xmlNewTextChild(
765                     ptr, ns_record, 
766                     BAD_CAST "subfield",  BAD_CAST wrbuf_cstr(wr_cdata));
767
768                 wrbuf_rewind(wr_cdata);
769                 wrbuf_iconv_write(wr_cdata, mt->iconv_cd,
770                                   s->code_data, using_code_len);
771                 xmlNewProp(ptr_subfield, BAD_CAST "code",
772                            BAD_CAST wrbuf_cstr(wr_cdata));
773             }
774             break;
775         case YAZ_MARC_CONTROLFIELD:
776             wrbuf_rewind(wr_cdata);
777             wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, n->u.controlfield.data);
778             marc_iconv_reset(mt, wr_cdata);
779             
780             ptr = xmlNewTextChild(record_ptr, ns_record,
781                                   BAD_CAST "controlfield",
782                                   BAD_CAST wrbuf_cstr(wr_cdata));
783             
784             xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.controlfield.tag);
785             break;
786         case YAZ_MARC_COMMENT:
787             ptr = xmlNewComment(BAD_CAST n->u.comment);
788             xmlAddChild(record_ptr, ptr);
789             break;
790         case YAZ_MARC_LEADER:
791             xmlNewTextChild(record_ptr, ns_record, BAD_CAST "leader",
792                             BAD_CAST n->u.leader);
793             break;
794         }
795     }
796     wrbuf_destroy(wr_cdata);
797     return 0;
798 #else
799     return -1;
800 #endif
801 }
802
803 int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
804 {
805     struct yaz_marc_node *n;
806     int indicator_length;
807     int identifier_length;
808     int length_data_entry;
809     int length_starting;
810     int length_implementation;
811     int data_offset = 0;
812     const char *leader = 0;
813     WRBUF wr_dir, wr_head, wr_data_tmp;
814     int base_address;
815     
816     for (n = mt->nodes; n; n = n->next)
817         if (n->which == YAZ_MARC_LEADER)
818             leader = n->u.leader;
819     
820     if (!leader)
821         return -1;
822     if (!atoi_n_check(leader+10, 1, &indicator_length))
823         return -1;
824     if (!atoi_n_check(leader+11, 1, &identifier_length))
825         return -1;
826     if (!atoi_n_check(leader+20, 1, &length_data_entry))
827         return -1;
828     if (!atoi_n_check(leader+21, 1, &length_starting))
829         return -1;
830     if (!atoi_n_check(leader+22, 1, &length_implementation))
831         return -1;
832
833     wr_data_tmp = wrbuf_alloc();
834     wr_dir = wrbuf_alloc();
835     for (n = mt->nodes; n; n = n->next)
836     {
837         int data_length = 0;
838         struct yaz_marc_subfield *s;
839
840         switch(n->which)
841         {
842         case YAZ_MARC_DATAFIELD:
843             wrbuf_printf(wr_dir, "%.3s", n->u.datafield.tag);
844             data_length += indicator_length;
845             wrbuf_rewind(wr_data_tmp);
846             for (s = n->u.datafield.subfields; s; s = s->next)
847             {
848                 /* write dummy IDFS + content */
849                 wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
850                 wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, s->code_data);
851                 marc_iconv_reset(mt, wr_data_tmp);
852             }
853             /* write dummy FS (makes MARC-8 to become ASCII) */
854             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
855             data_length += wrbuf_len(wr_data_tmp);
856             break;
857         case YAZ_MARC_CONTROLFIELD:
858             wrbuf_printf(wr_dir, "%.3s", n->u.controlfield.tag);
859
860             wrbuf_rewind(wr_data_tmp);
861             wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, 
862                              n->u.controlfield.data);
863             marc_iconv_reset(mt, wr_data_tmp);
864             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');/* field sep */
865             data_length += wrbuf_len(wr_data_tmp);
866             break;
867         case YAZ_MARC_COMMENT:
868             break;
869         case YAZ_MARC_LEADER:
870             break;
871         }
872         if (data_length)
873         {
874             wrbuf_printf(wr_dir, "%0*d", length_data_entry, data_length);
875             wrbuf_printf(wr_dir, "%0*d", length_starting, data_offset);
876             data_offset += data_length;
877         }
878     }
879     /* mark end of directory */
880     wrbuf_putc(wr_dir, ISO2709_FS);
881
882     /* base address of data (comes after leader+directory) */
883     base_address = 24 + wrbuf_len(wr_dir);
884
885     wr_head = wrbuf_alloc();
886
887     /* write record length */
888     wrbuf_printf(wr_head, "%05d", base_address + data_offset + 1);
889     /* from "original" leader */
890     wrbuf_write(wr_head, leader+5, 7);
891     /* base address of data */
892     wrbuf_printf(wr_head, "%05d", base_address);
893     /* from "original" leader */
894     wrbuf_write(wr_head, leader+17, 7);
895     
896     wrbuf_write(wr, wrbuf_buf(wr_head), 24);
897     wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
898     wrbuf_free(wr_head, 1);
899     wrbuf_free(wr_dir, 1);
900     wrbuf_free(wr_data_tmp, 1);
901
902     for (n = mt->nodes; n; n = n->next)
903     {
904         struct yaz_marc_subfield *s;
905
906         switch(n->which)
907         {
908         case YAZ_MARC_DATAFIELD:
909             wrbuf_printf(wr, "%.*s", indicator_length,
910                          n->u.datafield.indicator);
911             for (s = n->u.datafield.subfields; s; s = s->next)
912             {
913                 wrbuf_putc(wr, ISO2709_IDFS);
914                 wrbuf_iconv_puts(wr, mt->iconv_cd, s->code_data);
915                 marc_iconv_reset(mt, wr);
916             }
917             wrbuf_putc(wr, ISO2709_FS);
918             break;
919         case YAZ_MARC_CONTROLFIELD:
920             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
921             marc_iconv_reset(mt, wr);
922             wrbuf_putc(wr, ISO2709_FS);
923             break;
924         case YAZ_MARC_COMMENT:
925             break;
926         case YAZ_MARC_LEADER:
927             break;
928         }
929     }
930     wrbuf_printf(wr, "%c", ISO2709_RS);
931     return 0;
932 }
933
934
935 int yaz_marc_decode_wrbuf(yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
936 {
937     int s, r = yaz_marc_read_iso2709(mt, buf, bsize);
938     if (r <= 0)
939         return r;
940     s = yaz_marc_write_mode(mt, wr); /* returns 0 for OK, -1 otherwise */
941     if (s != 0)
942         return -1; /* error */
943     return r; /* OK, return length > 0 */
944 }
945
946 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
947                          char **result, int *rsize)
948 {
949     int r;
950
951     wrbuf_rewind(mt->m_wr);
952     r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
953     if (result)
954         *result = wrbuf_buf(mt->m_wr);
955     if (rsize)
956         *rsize = wrbuf_len(mt->m_wr);
957     return r;
958 }
959
960 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
961 {
962     if (mt)
963         mt->xml = xmlmode;
964 }
965
966 void yaz_marc_debug(yaz_marc_t mt, int level)
967 {
968     if (mt)
969         mt->debug = level;
970 }
971
972 void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
973 {
974     mt->iconv_cd = cd;
975 }
976
977 void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
978 {
979     struct yaz_marc_node *n;
980     char *leader = 0;
981     for (n = mt->nodes; n; n = n->next)
982         if (n->which == YAZ_MARC_LEADER)
983         {
984             leader = n->u.leader;
985             memcpy(leader+off, str, strlen(str));
986             break;
987         }
988 }
989
990 /* deprecated */
991 int yaz_marc_decode(const char *buf, WRBUF wr, int debug, int bsize, int xml)
992 {
993     yaz_marc_t mt = yaz_marc_create();
994     int r;
995
996     mt->debug = debug;
997     mt->xml = xml;
998     r = yaz_marc_decode_wrbuf(mt, buf, bsize, wr);
999     yaz_marc_destroy(mt);
1000     return r;
1001 }
1002
1003 /* deprecated */
1004 int marc_display_wrbuf (const char *buf, WRBUF wr, int debug, int bsize)
1005 {
1006     return yaz_marc_decode(buf, wr, debug, bsize, 0);
1007 }
1008
1009 /* deprecated */
1010 int marc_display_exl (const char *buf, FILE *outf, int debug, int bsize)
1011 {
1012     yaz_marc_t mt = yaz_marc_create();
1013     int r;
1014
1015     mt->debug = debug;
1016     r = yaz_marc_decode_wrbuf (mt, buf, bsize, mt->m_wr);
1017     if (!outf)
1018         outf = stdout;
1019     if (r > 0)
1020         fwrite (wrbuf_buf(mt->m_wr), 1, wrbuf_len(mt->m_wr), outf);
1021     yaz_marc_destroy(mt);
1022     return r;
1023 }
1024
1025 /* deprecated */
1026 int marc_display_ex (const char *buf, FILE *outf, int debug)
1027 {
1028     return marc_display_exl (buf, outf, debug, -1);
1029 }
1030
1031 /* deprecated */
1032 int marc_display (const char *buf, FILE *outf)
1033 {
1034     return marc_display_ex (buf, outf, 0);
1035 }
1036
1037 int yaz_marc_leader_spec(yaz_marc_t mt, const char *leader_spec)
1038 {
1039     xfree(mt->leader_spec);
1040     mt->leader_spec = 0;
1041     if (leader_spec)
1042     {
1043         char dummy_leader[24];
1044         if (marc_exec_leader(leader_spec, dummy_leader, 24))
1045             return -1;
1046         mt->leader_spec = xstrdup(leader_spec);
1047     }
1048     return 0;
1049 }
1050
1051 static int marc_exec_leader(const char *leader_spec, char *leader, size_t size)
1052 {
1053     const char *cp = leader_spec;
1054     while (cp)
1055     {
1056         char val[21];
1057         int pos;
1058         int no_read = 0, no = 0;
1059
1060         no = sscanf(cp, "%d=%20[^,]%n", &pos, val, &no_read);
1061         if (no < 2 || no_read < 3)
1062             return -1;
1063         if (pos < 0 || pos >= size)
1064             return -1;
1065
1066         if (*val == '\'')
1067         {
1068             const char *vp = strchr(val+1, '\'');
1069             size_t len;
1070             
1071             if (!vp)
1072                 return -1;
1073             len = vp-val-1;
1074             if (len + pos > size)
1075                 return -1;
1076             memcpy(leader + pos, val+1, len);
1077         }
1078         else if (*val >= '0' && *val <= '9')
1079         {
1080             int ch = atoi(val);
1081             leader[pos] = ch;
1082         }
1083         else
1084             return -1;
1085         cp += no_read;
1086         if (*cp != ',')
1087             break;
1088
1089         cp++;
1090     }
1091     return 0;
1092 }
1093
1094 int yaz_marc_decode_formatstr(const char *arg)
1095 {
1096     int mode = -1; 
1097     if (!strcmp(arg, "marc"))
1098         mode = YAZ_MARC_ISO2709;
1099     if (!strcmp(arg, "marcxml"))
1100         mode = YAZ_MARC_MARCXML;
1101     if (!strcmp(arg, "marcxchange"))
1102         mode = YAZ_MARC_XCHANGE;
1103     if (!strcmp(arg, "line"))
1104         mode = YAZ_MARC_LINE;
1105     return mode;
1106 }
1107
1108 void yaz_marc_write_using_libxml2(yaz_marc_t mt, int enable)
1109 {
1110     mt->write_using_libxml2 = enable;
1111 }
1112
1113 /*
1114  * Local variables:
1115  * c-basic-offset: 4
1116  * indent-tabs-mode: nil
1117  * End:
1118  * vim: shiftwidth=4 tabstop=8 expandtab
1119  */
1120