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