9037b5b76a201c73f5d730471e3a5565ed1660b9
[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.46 2007-02-17 10:53:05 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 #if YAZ_HAVE_XML2
647         int ret;
648         xmlNode *root_ptr;
649
650         ret = yaz_marc_write_xml(mt, &root_ptr, ns, format, type);
651         if (ret == 0)
652         {
653             xmlChar *buf_out;
654             xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
655             int len_out;
656
657             xmlDocSetRootElement(doc, root_ptr);
658             xmlDocDumpMemory(doc, &buf_out, &len_out);
659
660             wrbuf_write(wr, (const char *) buf_out, len_out);
661             wrbuf_puts(wr, "");
662             xmlFree(buf_out);
663             xmlFreeDoc(doc);
664         }
665         return ret;
666 #else
667         return -1;
668 #endif
669     }
670     else
671         return yaz_marc_write_marcxml_ns1(mt, wr, ns, format, type);
672 }
673
674 int yaz_marc_write_marcxml(yaz_marc_t mt, WRBUF wr)
675 {
676     if (!mt->leader_spec)
677         yaz_marc_modify_leader(mt, 9, "a");
678     return yaz_marc_write_marcxml_ns(mt, wr, "http://www.loc.gov/MARC21/slim",
679                                      0, 0);
680 }
681
682 int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
683                                const char *format,
684                                const char *type)
685 {
686     return yaz_marc_write_marcxml_ns(mt, wr,
687                                      "http://www.bs.dk/standards/MarcXchange",
688                                      0, 0);
689 }
690
691
692 int yaz_marc_write_xml(yaz_marc_t mt, xmlNode **root_ptr,
693                        const char *ns, 
694                        const char *format,
695                        const char *type)
696 {
697 #if YAZ_HAVE_XML2
698     struct yaz_marc_node *n;
699     int identifier_length;
700     const char *leader = 0;
701     xmlNode *record_ptr;
702     xmlNsPtr ns_record;
703     WRBUF wr_cdata = 0;
704
705     for (n = mt->nodes; n; n = n->next)
706         if (n->which == YAZ_MARC_LEADER)
707         {
708             leader = n->u.leader;
709             break;
710         }
711     
712     if (!leader)
713         return -1;
714     if (!atoi_n_check(leader+11, 1, &identifier_length))
715         return -1;
716
717     wr_cdata = wrbuf_alloc();
718
719     record_ptr = xmlNewNode(0, BAD_CAST "record");
720     *root_ptr = record_ptr;
721
722     ns_record = xmlNewNs(record_ptr, BAD_CAST ns, 0);
723     xmlSetNs(record_ptr, ns_record);
724
725     if (format)
726         xmlNewProp(record_ptr, BAD_CAST "format", BAD_CAST format);
727     if (type)
728         xmlNewProp(record_ptr, BAD_CAST "type", BAD_CAST type);
729     for (n = mt->nodes; n; n = n->next)
730     {
731         struct yaz_marc_subfield *s;
732         xmlNode *ptr;
733
734         switch(n->which)
735         {
736         case YAZ_MARC_DATAFIELD:
737             ptr = xmlNewChild(record_ptr, ns_record, BAD_CAST "datafield", 0);
738             xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.datafield.tag);
739             if (n->u.datafield.indicator)
740             {
741                 int i;
742                 for (i = 0; n->u.datafield.indicator[i]; i++)
743                 {
744                     char ind_str[6];
745                     char ind_val[2];
746
747                     sprintf(ind_str, "ind%d", i+1);
748                     ind_val[0] = n->u.datafield.indicator[i];
749                     ind_val[1] = '\0';
750                     xmlNewProp(ptr, BAD_CAST ind_str, BAD_CAST ind_val);
751                 }
752             }
753             for (s = n->u.datafield.subfields; s; s = s->next)
754             {
755                 xmlNode *ptr_subfield;
756                 /* if identifier length is 2 (most MARCs),
757                    the code is a single character .. However we've
758                    seen multibyte codes, so see how big it really is */
759                 size_t using_code_len = 
760                     (identifier_length != 2) ? identifier_length - 1
761                     :
762                     cdata_one_character(mt, s->code_data);
763
764                 wrbuf_rewind(wr_cdata);
765                 wrbuf_iconv_puts(wr_cdata, mt->iconv_cd,
766                                  s->code_data + using_code_len);
767                 marc_iconv_reset(mt, wr_cdata);
768                 ptr_subfield = xmlNewTextChild(
769                     ptr, ns_record, 
770                     BAD_CAST "subfield",  BAD_CAST wrbuf_cstr(wr_cdata));
771
772                 wrbuf_rewind(wr_cdata);
773                 wrbuf_iconv_write(wr_cdata, mt->iconv_cd,
774                                   s->code_data, using_code_len);
775                 xmlNewProp(ptr_subfield, BAD_CAST "code",
776                            BAD_CAST wrbuf_cstr(wr_cdata));
777             }
778             break;
779         case YAZ_MARC_CONTROLFIELD:
780             wrbuf_rewind(wr_cdata);
781             wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, n->u.controlfield.data);
782             marc_iconv_reset(mt, wr_cdata);
783             
784             ptr = xmlNewTextChild(record_ptr, ns_record,
785                                   BAD_CAST "controlfield",
786                                   BAD_CAST wrbuf_cstr(wr_cdata));
787             
788             xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.controlfield.tag);
789             break;
790         case YAZ_MARC_COMMENT:
791             ptr = xmlNewComment(BAD_CAST n->u.comment);
792             xmlAddChild(record_ptr, ptr);
793             break;
794         case YAZ_MARC_LEADER:
795             xmlNewTextChild(record_ptr, ns_record, BAD_CAST "leader",
796                             BAD_CAST n->u.leader);
797             break;
798         }
799     }
800     wrbuf_destroy(wr_cdata);
801     return 0;
802 #else
803     return -1;
804 #endif
805 }
806
807 int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
808 {
809     struct yaz_marc_node *n;
810     int indicator_length;
811     int identifier_length;
812     int length_data_entry;
813     int length_starting;
814     int length_implementation;
815     int data_offset = 0;
816     const char *leader = 0;
817     WRBUF wr_dir, wr_head, wr_data_tmp;
818     int base_address;
819     
820     for (n = mt->nodes; n; n = n->next)
821         if (n->which == YAZ_MARC_LEADER)
822             leader = n->u.leader;
823     
824     if (!leader)
825         return -1;
826     if (!atoi_n_check(leader+10, 1, &indicator_length))
827         return -1;
828     if (!atoi_n_check(leader+11, 1, &identifier_length))
829         return -1;
830     if (!atoi_n_check(leader+20, 1, &length_data_entry))
831         return -1;
832     if (!atoi_n_check(leader+21, 1, &length_starting))
833         return -1;
834     if (!atoi_n_check(leader+22, 1, &length_implementation))
835         return -1;
836
837     wr_data_tmp = wrbuf_alloc();
838     wr_dir = wrbuf_alloc();
839     for (n = mt->nodes; n; n = n->next)
840     {
841         int data_length = 0;
842         struct yaz_marc_subfield *s;
843
844         switch(n->which)
845         {
846         case YAZ_MARC_DATAFIELD:
847             wrbuf_printf(wr_dir, "%.3s", n->u.datafield.tag);
848             data_length += indicator_length;
849             wrbuf_rewind(wr_data_tmp);
850             for (s = n->u.datafield.subfields; s; s = s->next)
851             {
852                 /* write dummy IDFS + content */
853                 wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
854                 wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, s->code_data);
855                 marc_iconv_reset(mt, wr_data_tmp);
856             }
857             /* write dummy FS (makes MARC-8 to become ASCII) */
858             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
859             data_length += wrbuf_len(wr_data_tmp);
860             break;
861         case YAZ_MARC_CONTROLFIELD:
862             wrbuf_printf(wr_dir, "%.3s", n->u.controlfield.tag);
863
864             wrbuf_rewind(wr_data_tmp);
865             wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, 
866                              n->u.controlfield.data);
867             marc_iconv_reset(mt, wr_data_tmp);
868             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');/* field sep */
869             data_length += wrbuf_len(wr_data_tmp);
870             break;
871         case YAZ_MARC_COMMENT:
872             break;
873         case YAZ_MARC_LEADER:
874             break;
875         }
876         if (data_length)
877         {
878             wrbuf_printf(wr_dir, "%0*d", length_data_entry, data_length);
879             wrbuf_printf(wr_dir, "%0*d", length_starting, data_offset);
880             data_offset += data_length;
881         }
882     }
883     /* mark end of directory */
884     wrbuf_putc(wr_dir, ISO2709_FS);
885
886     /* base address of data (comes after leader+directory) */
887     base_address = 24 + wrbuf_len(wr_dir);
888
889     wr_head = wrbuf_alloc();
890
891     /* write record length */
892     wrbuf_printf(wr_head, "%05d", base_address + data_offset + 1);
893     /* from "original" leader */
894     wrbuf_write(wr_head, leader+5, 7);
895     /* base address of data */
896     wrbuf_printf(wr_head, "%05d", base_address);
897     /* from "original" leader */
898     wrbuf_write(wr_head, leader+17, 7);
899     
900     wrbuf_write(wr, wrbuf_buf(wr_head), 24);
901     wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
902     wrbuf_free(wr_head, 1);
903     wrbuf_free(wr_dir, 1);
904     wrbuf_free(wr_data_tmp, 1);
905
906     for (n = mt->nodes; n; n = n->next)
907     {
908         struct yaz_marc_subfield *s;
909
910         switch(n->which)
911         {
912         case YAZ_MARC_DATAFIELD:
913             wrbuf_printf(wr, "%.*s", indicator_length,
914                          n->u.datafield.indicator);
915             for (s = n->u.datafield.subfields; s; s = s->next)
916             {
917                 wrbuf_putc(wr, ISO2709_IDFS);
918                 wrbuf_iconv_puts(wr, mt->iconv_cd, s->code_data);
919                 marc_iconv_reset(mt, wr);
920             }
921             wrbuf_putc(wr, ISO2709_FS);
922             break;
923         case YAZ_MARC_CONTROLFIELD:
924             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
925             marc_iconv_reset(mt, wr);
926             wrbuf_putc(wr, ISO2709_FS);
927             break;
928         case YAZ_MARC_COMMENT:
929             break;
930         case YAZ_MARC_LEADER:
931             break;
932         }
933     }
934     wrbuf_printf(wr, "%c", ISO2709_RS);
935     return 0;
936 }
937
938
939 int yaz_marc_decode_wrbuf(yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
940 {
941     int s, r = yaz_marc_read_iso2709(mt, buf, bsize);
942     if (r <= 0)
943         return r;
944     s = yaz_marc_write_mode(mt, wr); /* returns 0 for OK, -1 otherwise */
945     if (s != 0)
946         return -1; /* error */
947     return r; /* OK, return length > 0 */
948 }
949
950 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
951                          char **result, int *rsize)
952 {
953     int r;
954
955     wrbuf_rewind(mt->m_wr);
956     r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
957     if (result)
958         *result = wrbuf_buf(mt->m_wr);
959     if (rsize)
960         *rsize = wrbuf_len(mt->m_wr);
961     return r;
962 }
963
964 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
965 {
966     if (mt)
967         mt->xml = xmlmode;
968 }
969
970 void yaz_marc_debug(yaz_marc_t mt, int level)
971 {
972     if (mt)
973         mt->debug = level;
974 }
975
976 void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
977 {
978     mt->iconv_cd = cd;
979 }
980
981 void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
982 {
983     struct yaz_marc_node *n;
984     char *leader = 0;
985     for (n = mt->nodes; n; n = n->next)
986         if (n->which == YAZ_MARC_LEADER)
987         {
988             leader = n->u.leader;
989             memcpy(leader+off, str, strlen(str));
990             break;
991         }
992 }
993
994 /* deprecated */
995 int yaz_marc_decode(const char *buf, WRBUF wr, int debug, int bsize, int xml)
996 {
997     yaz_marc_t mt = yaz_marc_create();
998     int r;
999
1000     mt->debug = debug;
1001     mt->xml = xml;
1002     r = yaz_marc_decode_wrbuf(mt, buf, bsize, wr);
1003     yaz_marc_destroy(mt);
1004     return r;
1005 }
1006
1007 /* deprecated */
1008 int marc_display_wrbuf (const char *buf, WRBUF wr, int debug, int bsize)
1009 {
1010     return yaz_marc_decode(buf, wr, debug, bsize, 0);
1011 }
1012
1013 /* deprecated */
1014 int marc_display_exl (const char *buf, FILE *outf, int debug, int bsize)
1015 {
1016     yaz_marc_t mt = yaz_marc_create();
1017     int r;
1018
1019     mt->debug = debug;
1020     r = yaz_marc_decode_wrbuf (mt, buf, bsize, mt->m_wr);
1021     if (!outf)
1022         outf = stdout;
1023     if (r > 0)
1024         fwrite (wrbuf_buf(mt->m_wr), 1, wrbuf_len(mt->m_wr), outf);
1025     yaz_marc_destroy(mt);
1026     return r;
1027 }
1028
1029 /* deprecated */
1030 int marc_display_ex (const char *buf, FILE *outf, int debug)
1031 {
1032     return marc_display_exl (buf, outf, debug, -1);
1033 }
1034
1035 /* deprecated */
1036 int marc_display (const char *buf, FILE *outf)
1037 {
1038     return marc_display_ex (buf, outf, 0);
1039 }
1040
1041 int yaz_marc_leader_spec(yaz_marc_t mt, const char *leader_spec)
1042 {
1043     xfree(mt->leader_spec);
1044     mt->leader_spec = 0;
1045     if (leader_spec)
1046     {
1047         char dummy_leader[24];
1048         if (marc_exec_leader(leader_spec, dummy_leader, 24))
1049             return -1;
1050         mt->leader_spec = xstrdup(leader_spec);
1051     }
1052     return 0;
1053 }
1054
1055 static int marc_exec_leader(const char *leader_spec, char *leader, size_t size)
1056 {
1057     const char *cp = leader_spec;
1058     while (cp)
1059     {
1060         char val[21];
1061         int pos;
1062         int no_read = 0, no = 0;
1063
1064         no = sscanf(cp, "%d=%20[^,]%n", &pos, val, &no_read);
1065         if (no < 2 || no_read < 3)
1066             return -1;
1067         if (pos < 0 || pos >= size)
1068             return -1;
1069
1070         if (*val == '\'')
1071         {
1072             const char *vp = strchr(val+1, '\'');
1073             size_t len;
1074             
1075             if (!vp)
1076                 return -1;
1077             len = vp-val-1;
1078             if (len + pos > size)
1079                 return -1;
1080             memcpy(leader + pos, val+1, len);
1081         }
1082         else if (*val >= '0' && *val <= '9')
1083         {
1084             int ch = atoi(val);
1085             leader[pos] = ch;
1086         }
1087         else
1088             return -1;
1089         cp += no_read;
1090         if (*cp != ',')
1091             break;
1092
1093         cp++;
1094     }
1095     return 0;
1096 }
1097
1098 int yaz_marc_decode_formatstr(const char *arg)
1099 {
1100     int mode = -1; 
1101     if (!strcmp(arg, "marc"))
1102         mode = YAZ_MARC_ISO2709;
1103     if (!strcmp(arg, "marcxml"))
1104         mode = YAZ_MARC_MARCXML;
1105     if (!strcmp(arg, "marcxchange"))
1106         mode = YAZ_MARC_XCHANGE;
1107     if (!strcmp(arg, "line"))
1108         mode = YAZ_MARC_LINE;
1109     return mode;
1110 }
1111
1112 void yaz_marc_write_using_libxml2(yaz_marc_t mt, int enable)
1113 {
1114     mt->write_using_libxml2 = enable;
1115 }
1116
1117 /*
1118  * Local variables:
1119  * c-basic-offset: 4
1120  * indent-tabs-mode: nil
1121  * End:
1122  * vim: shiftwidth=4 tabstop=8 expandtab
1123  */
1124