Added header yaz/xmltypes.h which include libxml2' node definitions
[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.37 2006-12-13 11:25:17 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_check(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         switch(n->which)
416         {
417         case YAZ_MARC_COMMENT:
418             wrbuf_iconv_write(wr, mt->iconv_cd, 
419                               n->u.comment, strlen(n->u.comment));
420             wrbuf_puts(wr, ")\n");
421             break;
422         default:
423             break;
424         }
425     }
426     return 0;
427 }
428
429
430 int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
431 {
432     struct yaz_marc_node *n;
433     int identifier_length;
434     const char *leader = 0;
435
436     for (n = mt->nodes; n; n = n->next)
437         if (n->which == YAZ_MARC_LEADER)
438         {
439             leader = n->u.leader;
440             break;
441         }
442     
443     if (!leader)
444         return -1;
445     if (!atoi_n_check(leader+11, 1, &identifier_length))
446         return -1;
447
448     for (n = mt->nodes; n; n = n->next)
449     {
450         struct yaz_marc_subfield *s;
451         switch(n->which)
452         {
453         case YAZ_MARC_DATAFIELD:
454             wrbuf_printf(wr, "%s %s", n->u.datafield.tag,
455                          n->u.datafield.indicator);
456             for (s = n->u.datafield.subfields; s; s = s->next)
457             {
458                 /* if identifier length is 2 (most MARCs),
459                    the code is a single character .. However we've
460                    seen multibyte codes, so see how big it really is */
461                 size_t using_code_len = 
462                     (identifier_length != 2) ? identifier_length - 1
463                     :
464                     cdata_one_character(mt, s->code_data);
465                 
466                 wrbuf_puts (wr, mt->subfield_str); 
467                 wrbuf_iconv_write(wr, mt->iconv_cd, s->code_data, 
468                                   using_code_len);
469                 wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
470                 wrbuf_iconv_puts(wr, mt->iconv_cd, 
471                                  s->code_data + using_code_len);
472                 wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
473                 wr->pos--;
474             }
475             wrbuf_puts (wr, mt->endline_str);
476             break;
477         case YAZ_MARC_CONTROLFIELD:
478             wrbuf_printf(wr, "%s", n->u.controlfield.tag);
479             wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
480             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
481             wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
482             wr->pos--;
483             wrbuf_puts (wr, mt->endline_str);
484             break;
485         case YAZ_MARC_COMMENT:
486             wrbuf_puts(wr, "(");
487             wrbuf_iconv_write(wr, mt->iconv_cd, 
488                               n->u.comment, strlen(n->u.comment));
489             wrbuf_puts(wr, ")\n");
490             break;
491         case YAZ_MARC_LEADER:
492             wrbuf_printf(wr, "%s\n", n->u.leader);
493         }
494     }
495     return 0;
496 }
497
498 int yaz_marc_write_mode(yaz_marc_t mt, WRBUF wr)
499 {
500     switch(mt->xml)
501     {
502     case YAZ_MARC_LINE:
503         return yaz_marc_write_line(mt, wr);
504     case YAZ_MARC_MARCXML:
505         return yaz_marc_write_marcxml(mt, wr);
506     case YAZ_MARC_XCHANGE:
507         return yaz_marc_write_marcxchange(mt, wr, 0, 0); /* no format, type */
508     case YAZ_MARC_ISO2709:
509         return yaz_marc_write_iso2709(mt, wr);
510     case YAZ_MARC_CHECK:
511         return yaz_marc_write_check(mt, wr);
512     }
513     return -1;
514 }
515
516 /** \brief common MARC XML/Xchange writer
517     \param mt handle
518     \param wr WRBUF output
519     \param ns XMLNS for the elements
520     \param format record format (e.g. "MARC21")
521     \param type record type (e.g. "Bibliographic")
522 */
523 static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
524                                      const char *ns, 
525                                      const char *format,
526                                      const char *type)
527 {
528     struct yaz_marc_node *n;
529     int identifier_length;
530     const char *leader = 0;
531
532     for (n = mt->nodes; n; n = n->next)
533         if (n->which == YAZ_MARC_LEADER)
534         {
535             leader = n->u.leader;
536             break;
537         }
538     
539     if (!leader)
540         return -1;
541     if (!atoi_n_check(leader+11, 1, &identifier_length))
542         return -1;
543
544     wrbuf_printf(wr, "<record xmlns=\"%s\"", ns);
545     if (format)
546         wrbuf_printf(wr, " format=\"%.80s\"", format);
547     if (type)
548         wrbuf_printf(wr, " type=\"%.80s\"", type);
549     wrbuf_printf(wr, ">\n");
550     for (n = mt->nodes; n; n = n->next)
551     {
552         struct yaz_marc_subfield *s;
553
554         switch(n->which)
555         {
556         case YAZ_MARC_DATAFIELD:
557             wrbuf_printf(wr, "  <datafield tag=\"");
558             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
559                                     strlen(n->u.datafield.tag));
560             wrbuf_printf(wr, "\"");
561             if (n->u.datafield.indicator)
562             {
563                 int i;
564                 for (i = 0; n->u.datafield.indicator[i]; i++)
565                 {
566                     wrbuf_printf(wr, " ind%d=\"", i+1);
567                     wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
568                                           n->u.datafield.indicator+i, 1);
569                     wrbuf_iconv_puts(wr, mt->iconv_cd, "\"");
570                 }
571             }
572             wrbuf_printf(wr, ">\n");
573             for (s = n->u.datafield.subfields; s; s = s->next)
574             {
575                 /* if identifier length is 2 (most MARCs),
576                    the code is a single character .. However we've
577                    seen multibyte codes, so see how big it really is */
578                 size_t using_code_len = 
579                     (identifier_length != 2) ? identifier_length - 1
580                     :
581                     cdata_one_character(mt, s->code_data);
582                 
583                 wrbuf_iconv_puts(wr, mt->iconv_cd, "    <subfield code=\"");
584                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
585                                         s->code_data, using_code_len);
586                 wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
587                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
588                                         s->code_data + using_code_len,
589                                         strlen(s->code_data + using_code_len));
590                 wrbuf_iconv_puts(wr, mt->iconv_cd, "</subfield>");
591                 wrbuf_puts(wr, "\n");
592             }
593             wrbuf_printf(wr, "  </datafield>\n");
594             break;
595         case YAZ_MARC_CONTROLFIELD:
596             wrbuf_printf(wr, "  <controlfield tag=\"");
597             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
598                                     strlen(n->u.controlfield.tag));
599             wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
600             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
601             wrbuf_iconv_puts(wr, mt->iconv_cd, "</controlfield>");
602             wrbuf_puts(wr, "\n");
603             break;
604         case YAZ_MARC_COMMENT:
605             wrbuf_printf(wr, "<!-- ");
606             wrbuf_puts(wr, n->u.comment);
607             wrbuf_printf(wr, " -->\n");
608             break;
609         case YAZ_MARC_LEADER:
610             wrbuf_printf(wr, "  <leader>");
611             wrbuf_iconv_write_cdata(wr, 
612                                     0 /* no charset conversion for leader */,
613                                     n->u.leader, strlen(n->u.leader));
614             wrbuf_printf(wr, "</leader>\n");
615         }
616     }
617     wrbuf_puts(wr, "</record>\n");
618     return 0;
619 }
620
621 int yaz_marc_write_marcxml(yaz_marc_t mt, WRBUF wr)
622 {
623     if (!mt->leader_spec)
624         yaz_marc_modify_leader(mt, 9, "a");
625     return yaz_marc_write_marcxml_ns(mt, wr, "http://www.loc.gov/MARC21/slim",
626                                      0, 0);
627 }
628
629 int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
630                                const char *format,
631                                const char *type)
632 {
633     return yaz_marc_write_marcxml_ns(mt, wr,
634                                      "http://www.bs.dk/standards/MarcXchange",
635                                      0, 0);
636 }
637
638 int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
639 {
640     struct yaz_marc_node *n;
641     int indicator_length;
642     int identifier_length;
643     int length_data_entry;
644     int length_starting;
645     int length_implementation;
646     int data_offset = 0;
647     const char *leader = 0;
648     WRBUF wr_dir, wr_head, wr_data_tmp;
649     int base_address;
650     
651     for (n = mt->nodes; n; n = n->next)
652         if (n->which == YAZ_MARC_LEADER)
653             leader = n->u.leader;
654     
655     if (!leader)
656         return -1;
657     if (!atoi_n_check(leader+10, 1, &indicator_length))
658         return -1;
659     if (!atoi_n_check(leader+11, 1, &identifier_length))
660         return -1;
661     if (!atoi_n_check(leader+20, 1, &length_data_entry))
662         return -1;
663     if (!atoi_n_check(leader+21, 1, &length_starting))
664         return -1;
665     if (!atoi_n_check(leader+22, 1, &length_implementation))
666         return -1;
667
668     wr_data_tmp = wrbuf_alloc();
669     wr_dir = wrbuf_alloc();
670     for (n = mt->nodes; n; n = n->next)
671     {
672         int data_length = 0;
673         struct yaz_marc_subfield *s;
674
675         switch(n->which)
676         {
677         case YAZ_MARC_DATAFIELD:
678             wrbuf_printf(wr_dir, "%.3s", n->u.datafield.tag);
679             data_length += indicator_length;
680             wrbuf_rewind(wr_data_tmp);
681             for (s = n->u.datafield.subfields; s; s = s->next)
682             {
683                 /* write dummy IDFS + content */
684                 wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
685                 wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, s->code_data);
686             }
687             /* write dummy FS (makes MARC-8 to become ASCII) */
688             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
689             data_length += wrbuf_len(wr_data_tmp);
690             break;
691         case YAZ_MARC_CONTROLFIELD:
692             wrbuf_printf(wr_dir, "%.3s", n->u.controlfield.tag);
693
694             wrbuf_rewind(wr_data_tmp);
695             wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, 
696                              n->u.controlfield.data);
697             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');/* field sep */
698             data_length += wrbuf_len(wr_data_tmp);
699             break;
700         case YAZ_MARC_COMMENT:
701             break;
702         case YAZ_MARC_LEADER:
703             break;
704         }
705         if (data_length)
706         {
707             wrbuf_printf(wr_dir, "%0*d", length_data_entry, data_length);
708             wrbuf_printf(wr_dir, "%0*d", length_starting, data_offset);
709             data_offset += data_length;
710         }
711     }
712     /* mark end of directory */
713     wrbuf_putc(wr_dir, ISO2709_FS);
714
715     /* base address of data (comes after leader+directory) */
716     base_address = 24 + wrbuf_len(wr_dir);
717
718     wr_head = wrbuf_alloc();
719
720     /* write record length */
721     wrbuf_printf(wr_head, "%05d", base_address + data_offset + 1);
722     /* from "original" leader */
723     wrbuf_write(wr_head, leader+5, 7);
724     /* base address of data */
725     wrbuf_printf(wr_head, "%05d", base_address);
726     /* from "original" leader */
727     wrbuf_write(wr_head, leader+17, 7);
728     
729     wrbuf_write(wr, wrbuf_buf(wr_head), 24);
730     wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
731     wrbuf_free(wr_head, 1);
732     wrbuf_free(wr_dir, 1);
733     wrbuf_free(wr_data_tmp, 1);
734
735     for (n = mt->nodes; n; n = n->next)
736     {
737         struct yaz_marc_subfield *s;
738
739         switch(n->which)
740         {
741         case YAZ_MARC_DATAFIELD:
742             wrbuf_printf(wr, "%.*s", indicator_length,
743                          n->u.datafield.indicator);
744             for (s = n->u.datafield.subfields; s; s = s->next)
745             {
746                 wrbuf_putc(wr, ISO2709_IDFS);
747                 wrbuf_iconv_puts(wr, mt->iconv_cd, s->code_data);
748                 /* write dummy blank - makes MARC-8 to become ASCII */
749                 wrbuf_iconv_putchar(wr, mt->iconv_cd, ' ');
750                 wr->pos--;
751             }
752             wrbuf_putc(wr, ISO2709_FS);
753             break;
754         case YAZ_MARC_CONTROLFIELD:
755             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
756             /* write dummy blank - makes MARC-8 to become ASCII */
757             wrbuf_iconv_putchar(wr, mt->iconv_cd, ' ');
758             wr->pos--;
759             wrbuf_putc(wr, ISO2709_FS);
760             break;
761         case YAZ_MARC_COMMENT:
762             break;
763         case YAZ_MARC_LEADER:
764             break;
765         }
766     }
767     wrbuf_printf(wr, "%c", ISO2709_RS);
768     return 0;
769 }
770
771 #if YAZ_HAVE_XML2
772 int yaz_marc_read_xml_subfields(yaz_marc_t mt, const xmlNode *ptr)
773 {
774     for (; ptr; ptr = ptr->next)
775     {
776         if (ptr->type == XML_ELEMENT_NODE)
777         {
778             if (!strcmp((const char *) ptr->name, "subfield"))
779             {
780                 size_t ctrl_data_len = 0;
781                 char *ctrl_data_buf = 0;
782                 const xmlNode *p = 0, *ptr_code = 0;
783                 struct _xmlAttr *attr;
784                 for (attr = ptr->properties; attr; attr = attr->next)
785                     if (!strcmp((const char *)attr->name, "code"))
786                         ptr_code = attr->children;
787                     else
788                     {
789                         yaz_marc_cprintf(
790                             mt, "Bad attribute '%.80s' for 'subfield'",
791                             attr->name);
792                         return -1;
793                     }
794                 if (!ptr_code)
795                 {
796                     yaz_marc_cprintf(
797                         mt, "Missing attribute 'code' for 'subfield'" );
798                     return -1;
799                 }
800                 if (ptr_code->type == XML_TEXT_NODE)
801                 {
802                     ctrl_data_len = 
803                         strlen((const char *)ptr_code->content);
804                 }
805                 else
806                 {
807                     yaz_marc_cprintf(
808                         mt, "Missing value for 'code' in 'subfield'" );
809                     return -1;
810                 }
811                 for (p = ptr->children; p ; p = p->next)
812                     if (p->type == XML_TEXT_NODE)
813                         ctrl_data_len += strlen((const char *)p->content);
814                 ctrl_data_buf = nmem_malloc(mt->nmem, ctrl_data_len+1);
815                 strcpy(ctrl_data_buf, (const char *)ptr_code->content);
816                 for (p = ptr->children; p ; p = p->next)
817                     if (p->type == XML_TEXT_NODE)
818                         strcat(ctrl_data_buf, (const char *)p->content);
819                 yaz_marc_add_subfield(mt, ctrl_data_buf, ctrl_data_len);
820             }
821             else
822             {
823                 yaz_marc_cprintf(
824                     mt, "Expected element 'subfield', got '%.80s'", ptr->name);
825                 return -1;
826             }
827         }
828     }
829     return 0;
830 }
831
832 static int yaz_marc_read_xml_leader(yaz_marc_t mt, const xmlNode **ptr_p)
833 {
834     int indicator_length;
835     int identifier_length;
836     int base_address;
837     int length_data_entry;
838     int length_starting;
839     int length_implementation;
840     const char *leader = 0;
841     const xmlNode *ptr = *ptr_p;
842
843     for(; ptr; ptr = ptr->next)
844         if (ptr->type == XML_ELEMENT_NODE)
845         {
846             if (!strcmp((const char *) ptr->name, "leader"))
847             {
848                 xmlNode *p = ptr->children;
849                 for(; p; p = p->next)
850                     if (p->type == XML_TEXT_NODE)
851                         leader = (const char *) p->content;
852                 break;
853             }
854             else
855             {
856                 yaz_marc_cprintf(
857                     mt, "Expected element 'leader', got '%.80s'", ptr->name);
858                 return -1;
859             }
860         }
861     if (!leader)
862     {
863         yaz_marc_cprintf(mt, "Missing element 'leader'");
864         return -1;
865     }
866     if (strlen(leader) != 24)
867     {
868         yaz_marc_cprintf(mt, "Bad length %d of leader data."
869                          " Must have length of 24 characters", strlen(leader));
870         return -1;
871     }
872     yaz_marc_read_leader(mt, leader,
873                          &indicator_length,
874                          &identifier_length,
875                          &base_address,
876                          &length_data_entry,
877                          &length_starting,
878                          &length_implementation);
879     *ptr_p = ptr;
880     return 0;
881 }
882
883 static int yaz_marc_read_xml_fields(yaz_marc_t mt, const xmlNode *ptr)
884 {
885     for(; ptr; ptr = ptr->next)
886         if (ptr->type == XML_ELEMENT_NODE)
887         {
888             if (!strcmp((const char *) ptr->name, "controlfield"))
889             {
890                 const xmlNode *ptr_tag = 0;
891                 struct _xmlAttr *attr;
892                 for (attr = ptr->properties; attr; attr = attr->next)
893                     if (!strcmp((const char *)attr->name, "tag"))
894                         ptr_tag = attr->children;
895                     else
896                     {
897                         yaz_marc_cprintf(
898                             mt, "Bad attribute '%.80s' for 'controlfield'",
899                             attr->name);
900                         return -1;
901                     }
902                 if (!ptr_tag)
903                 {
904                     yaz_marc_cprintf(
905                         mt, "Missing attribute 'tag' for 'controlfield'" );
906                     return -1;
907                 }
908                 yaz_marc_add_controlfield_xml(mt, ptr_tag, ptr->children);
909             }
910             else if (!strcmp((const char *) ptr->name, "datafield"))
911             {
912                 char indstr[11]; /* 0(unused), 1,....9, + zero term */
913                 const xmlNode *ptr_tag = 0;
914                 struct _xmlAttr *attr;
915                 int i;
916                 for (i = 0; i<11; i++)
917                     indstr[i] = '\0';
918                 for (attr = ptr->properties; attr; attr = attr->next)
919                     if (!strcmp((const char *)attr->name, "tag"))
920                         ptr_tag = attr->children;
921                     else if (strlen((const char *)attr->name) == 4 &&
922                              !memcmp(attr->name, "ind", 3))
923                     {
924                         int no = atoi((const char *)attr->name+3);
925                         if (attr->children
926                             && attr->children->type == XML_TEXT_NODE)
927                             indstr[no] = attr->children->content[0];
928                     }
929                     else
930                     {
931                         yaz_marc_cprintf(
932                             mt, "Bad attribute '%.80s' for 'datafield'",
933                             attr->name);
934                         return -1;
935                     }
936                 if (!ptr_tag)
937                 {
938                     yaz_marc_cprintf(
939                         mt, "Missing attribute 'tag' for 'datafield'" );
940                     return -1;
941                 }
942                 /* note that indstr[0] is unused so we use indstr[1..] */
943                 yaz_marc_add_datafield_xml(mt, ptr_tag,
944                                            indstr+1, strlen(indstr+1));
945                 
946                 if (yaz_marc_read_xml_subfields(mt, ptr->children))
947                     return -1;
948             }
949             else
950             {
951                 yaz_marc_cprintf(mt,
952                                  "Expected element controlfield or datafield,"
953                                  " got %.80s", ptr->name);
954                 return -1;
955             }
956         }
957     return 0;
958 }
959 #endif
960
961 int yaz_marc_read_xml(yaz_marc_t mt, const xmlNode *ptr)
962 {
963 #if YAZ_HAVE_XML2
964     for(; ptr; ptr = ptr->next)
965         if (ptr->type == XML_ELEMENT_NODE)
966         {
967             if (!strcmp((const char *) ptr->name, "record"))
968                 break;
969             else
970             {
971                 yaz_marc_cprintf(
972                     mt, "Unknown element '%.80s' in MARC XML reader",
973                     ptr->name);
974                 return -1;
975             }
976         }
977     if (!ptr)
978     {
979         yaz_marc_cprintf(mt, "Missing element 'record' in MARC XML record");
980         return -1;
981     }
982     /* ptr points to record node now */
983     ptr = ptr->children;
984     if (yaz_marc_read_xml_leader(mt, &ptr))
985         return -1;
986     return yaz_marc_read_xml_fields(mt, ptr->next);
987 #else
988     return -1;
989 #endif
990 }
991
992 int yaz_marc_read_iso2709(yaz_marc_t mt, const char *buf, int bsize)
993 {
994     int entry_p;
995     int record_length;
996     int indicator_length;
997     int identifier_length;
998     int end_of_directory;
999     int base_address;
1000     int length_data_entry;
1001     int length_starting;
1002     int length_implementation;
1003
1004     yaz_marc_reset(mt);
1005
1006     record_length = atoi_n (buf, 5);
1007     if (record_length < 25)
1008     {
1009         yaz_marc_cprintf(mt, "Record length %d < 24", record_length);
1010         return -1;
1011     }
1012     /* ballout if bsize is known and record_length is less than that */
1013     if (bsize != -1 && record_length > bsize)
1014     {
1015         yaz_marc_cprintf(mt, "Record appears to be larger than buffer %d < %d",
1016                          record_length, bsize);
1017         return -1;
1018     }
1019     if (mt->debug)
1020         yaz_marc_cprintf(mt, "Record length         %5d", record_length);
1021
1022     yaz_marc_read_leader(mt, buf,
1023                          &indicator_length,
1024                          &identifier_length,
1025                          &base_address,
1026                          &length_data_entry,
1027                          &length_starting,
1028                          &length_implementation);
1029
1030     /* First pass. determine length of directory & base of data */
1031     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
1032     {
1033         /* length of directory entry */
1034         int l = 3 + length_data_entry + length_starting;
1035         if (entry_p + l >= record_length)
1036         {
1037             yaz_marc_cprintf(mt, "Directory offset %d: end of record."
1038                              " Missing FS char", entry_p);
1039             return -1;
1040         }
1041         if (mt->debug)
1042         {
1043             yaz_marc_cprintf(mt, "Directory offset %d: Tag %.3s",
1044                              entry_p, buf+entry_p);
1045         }
1046         /* Check for digits in length info */
1047         while (--l >= 3)
1048             if (!isdigit(*(const unsigned char *) (buf + entry_p+l)))
1049                 break;
1050         if (l >= 3)
1051         {
1052             /* Not all digits, so stop directory scan */
1053             yaz_marc_cprintf(mt, "Directory offset %d: Bad value for data"
1054                              " length and/or length starting", entry_p);
1055             break;
1056         }
1057         entry_p += 3 + length_data_entry + length_starting;
1058     }
1059     end_of_directory = entry_p;
1060     if (base_address != entry_p+1)
1061     {
1062         yaz_marc_cprintf(mt, "Base address not at end of directory,"
1063                          " base %d, end %d", base_address, entry_p+1);
1064     }
1065
1066     /* Second pass. parse control - and datafields */
1067     for (entry_p = 24; entry_p != end_of_directory; )
1068     {
1069         int data_length;
1070         int data_offset;
1071         int end_offset;
1072         int i;
1073         char tag[4];
1074         int identifier_flag = 0;
1075         int entry_p0 = entry_p;
1076
1077         memcpy (tag, buf+entry_p, 3);
1078         entry_p += 3;
1079         tag[3] = '\0';
1080         data_length = atoi_n(buf+entry_p, length_data_entry);
1081         entry_p += length_data_entry;
1082         data_offset = atoi_n(buf+entry_p, length_starting);
1083         entry_p += length_starting;
1084         i = data_offset + base_address;
1085         end_offset = i+data_length-1;
1086
1087         if (data_length <= 0 || data_offset < 0)
1088             break;
1089         
1090         if (mt->debug)
1091         {
1092             yaz_marc_cprintf(mt, "Tag: %s. Directory offset %d: data-length %d,"
1093                              " data-offset %d",
1094                              tag, entry_p0, data_length, data_offset);
1095         }
1096         if (end_offset >= record_length)
1097         {
1098             yaz_marc_cprintf(mt, "Directory offset %d: Data out of bounds %d >= %d",
1099                              entry_p0, end_offset, record_length);
1100             break;
1101         }
1102         
1103         if (memcmp (tag, "00", 2))
1104             identifier_flag = 1;  /* if not 00X assume subfields */
1105         else if (indicator_length < 4 && indicator_length > 0)
1106         {
1107             /* Danmarc 00X have subfields */
1108             if (buf[i + indicator_length] == ISO2709_IDFS)
1109                 identifier_flag = 1;
1110             else if (buf[i + indicator_length + 1] == ISO2709_IDFS)
1111                 identifier_flag = 2;
1112         }
1113
1114         if (identifier_flag)
1115         {
1116             /* datafield */
1117             i += identifier_flag-1;
1118             yaz_marc_add_datafield(mt, tag, buf+i, indicator_length);
1119             i += indicator_length;
1120
1121             while (i < end_offset &&
1122                     buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
1123             {
1124                 int code_offset = i+1;
1125
1126                 i ++;
1127                 while (i < end_offset &&
1128                         buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
1129                        buf[i] != ISO2709_FS)
1130                     i++;
1131                 yaz_marc_add_subfield(mt, buf+code_offset, i - code_offset);
1132             }
1133         }
1134         else
1135         {
1136             /* controlfield */
1137             int i0 = i;
1138             while (i < end_offset && 
1139                 buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
1140                 i++;
1141             yaz_marc_add_controlfield(mt, tag, buf+i0, i-i0);
1142         }
1143         if (i < end_offset)
1144         {
1145             yaz_marc_cprintf(mt, "Separator but not at end of field length=%d",
1146                     data_length);
1147         }
1148         if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
1149         {
1150             yaz_marc_cprintf(mt, "No separator at end of field length=%d",
1151                     data_length);
1152         }
1153     }
1154     return record_length;
1155 }
1156
1157 int yaz_marc_decode_wrbuf(yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
1158 {
1159     int s, r = yaz_marc_read_iso2709(mt, buf, bsize);
1160     if (r <= 0)
1161         return r;
1162     s = yaz_marc_write_mode(mt, wr); /* returns 0 for OK, -1 otherwise */
1163     if (s != 0)
1164         return -1; /* error */
1165     return r; /* OK, return length > 0 */
1166 }
1167
1168 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
1169                          char **result, int *rsize)
1170 {
1171     int r;
1172
1173     wrbuf_rewind(mt->m_wr);
1174     r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
1175     if (result)
1176         *result = wrbuf_buf(mt->m_wr);
1177     if (rsize)
1178         *rsize = wrbuf_len(mt->m_wr);
1179     return r;
1180 }
1181
1182 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
1183 {
1184     if (mt)
1185         mt->xml = xmlmode;
1186 }
1187
1188 void yaz_marc_debug(yaz_marc_t mt, int level)
1189 {
1190     if (mt)
1191         mt->debug = level;
1192 }
1193
1194 void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
1195 {
1196     mt->iconv_cd = cd;
1197 }
1198
1199 void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
1200 {
1201     struct yaz_marc_node *n;
1202     char *leader = 0;
1203     for (n = mt->nodes; n; n = n->next)
1204         if (n->which == YAZ_MARC_LEADER)
1205         {
1206             leader = n->u.leader;
1207             memcpy(leader+off, str, strlen(str));
1208             break;
1209         }
1210 }
1211
1212 /* deprecated */
1213 int yaz_marc_decode(const char *buf, WRBUF wr, int debug, int bsize, int xml)
1214 {
1215     yaz_marc_t mt = yaz_marc_create();
1216     int r;
1217
1218     mt->debug = debug;
1219     mt->xml = xml;
1220     r = yaz_marc_decode_wrbuf(mt, buf, bsize, wr);
1221     yaz_marc_destroy(mt);
1222     return r;
1223 }
1224
1225 /* deprecated */
1226 int marc_display_wrbuf (const char *buf, WRBUF wr, int debug, int bsize)
1227 {
1228     return yaz_marc_decode(buf, wr, debug, bsize, 0);
1229 }
1230
1231 /* deprecated */
1232 int marc_display_exl (const char *buf, FILE *outf, int debug, int bsize)
1233 {
1234     yaz_marc_t mt = yaz_marc_create();
1235     int r;
1236
1237     mt->debug = debug;
1238     r = yaz_marc_decode_wrbuf (mt, buf, bsize, mt->m_wr);
1239     if (!outf)
1240         outf = stdout;
1241     if (r > 0)
1242         fwrite (wrbuf_buf(mt->m_wr), 1, wrbuf_len(mt->m_wr), outf);
1243     yaz_marc_destroy(mt);
1244     return r;
1245 }
1246
1247 /* deprecated */
1248 int marc_display_ex (const char *buf, FILE *outf, int debug)
1249 {
1250     return marc_display_exl (buf, outf, debug, -1);
1251 }
1252
1253 /* deprecated */
1254 int marc_display (const char *buf, FILE *outf)
1255 {
1256     return marc_display_ex (buf, outf, 0);
1257 }
1258
1259 int yaz_marc_leader_spec(yaz_marc_t mt, const char *leader_spec)
1260 {
1261     xfree(mt->leader_spec);
1262     mt->leader_spec = 0;
1263     if (leader_spec)
1264     {
1265         char dummy_leader[24];
1266         if (marc_exec_leader(leader_spec, dummy_leader, 24))
1267             return -1;
1268         mt->leader_spec = xstrdup(leader_spec);
1269     }
1270     return 0;
1271 }
1272
1273 static int marc_exec_leader(const char *leader_spec, char *leader, size_t size)
1274 {
1275     const char *cp = leader_spec;
1276     while (cp)
1277     {
1278         char val[21];
1279         int pos;
1280         int no_read = 0, no = 0;
1281
1282         no = sscanf(cp, "%d=%20[^,]%n", &pos, val, &no_read);
1283         if (no < 2 || no_read < 3)
1284             return -1;
1285         if (pos < 0 || pos >= size)
1286             return -1;
1287
1288         if (*val == '\'')
1289         {
1290             const char *vp = strchr(val+1, '\'');
1291             size_t len;
1292             
1293             if (!vp)
1294                 return -1;
1295             len = vp-val-1;
1296             if (len + pos > size)
1297                 return -1;
1298             memcpy(leader + pos, val+1, len);
1299         }
1300         else if (*val >= '0' && *val <= '9')
1301         {
1302             int ch = atoi(val);
1303             leader[pos] = ch;
1304         }
1305         else
1306             return -1;
1307         cp += no_read;
1308         if (*cp != ',')
1309             break;
1310
1311         cp++;
1312     }
1313     return 0;
1314 }
1315
1316
1317 /*
1318  * Local variables:
1319  * c-basic-offset: 4
1320  * indent-tabs-mode: nil
1321  * End:
1322  * vim: shiftwidth=4 tabstop=8 expandtab
1323  */
1324