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