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