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