321cd7273bab6137054f141303c7d99b07fc332c
[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.38 2006-12-15 12:37:18 adam Exp $
6  */
7
8 /**
9  * \file marcdisp.c
10  * \brief Implements MARC conversion utilities
11  */
12
13 #if HAVE_CONFIG_H
14 #include <config.h>
15 #endif
16
17 #ifdef WIN32
18 #include <windows.h>
19 #endif
20
21 #include <stdarg.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <yaz/marcdisp.h>
26 #include <yaz/wrbuf.h>
27 #include <yaz/yaz-util.h>
28 #include <yaz/nmem_xml.h>
29
30 #if YAZ_HAVE_XML2
31 #include <libxml/parser.h>
32 #include <libxml/tree.h>
33 #endif
34
35 /** \brief node types for yaz_marc_node */
36 enum YAZ_MARC_NODE_TYPE
37
38     YAZ_MARC_DATAFIELD,
39     YAZ_MARC_CONTROLFIELD,
40     YAZ_MARC_COMMENT,
41     YAZ_MARC_LEADER
42 };
43
44 /** \brief represets a data field */
45 struct yaz_marc_datafield {
46     char *tag;
47     char *indicator;
48     struct yaz_marc_subfield *subfields;
49 };
50
51 /** \brief represents a control field */
52 struct yaz_marc_controlfield {
53     char *tag;
54     char *data;
55 };
56
57 /** \brief a comment node */
58 struct yaz_marc_comment {
59     char *comment;
60 };
61
62 /** \brief MARC node */
63 struct yaz_marc_node {
64     enum YAZ_MARC_NODE_TYPE which;
65     union {
66         struct yaz_marc_datafield datafield;
67         struct yaz_marc_controlfield controlfield;
68         char *comment;
69         char *leader;
70     } u;
71     struct yaz_marc_node *next;
72 };
73
74 /** \brief represents a subfield */
75 struct yaz_marc_subfield {
76     char *code_data;
77     struct yaz_marc_subfield *next;
78 };
79
80 /** \brief the internals of a yaz_marc_t handle */
81 struct yaz_marc_t_ {
82     WRBUF m_wr;
83     NMEM nmem;
84     int xml;
85     int debug;
86     yaz_iconv_t iconv_cd;
87     char subfield_str[8];
88     char endline_str[8];
89     char *leader_spec;
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     mt->leader_spec = 0;
103     strcpy(mt->subfield_str, " $");
104     strcpy(mt->endline_str, "\n");
105
106     mt->nmem = nmem_create();
107     yaz_marc_reset(mt);
108     return mt;
109 }
110
111 void yaz_marc_destroy(yaz_marc_t mt)
112 {
113     if (!mt)
114         return ;
115     nmem_destroy(mt->nmem);
116     wrbuf_free(mt->m_wr, 1);
117     xfree(mt->leader_spec);
118     xfree(mt);
119 }
120
121 NMEM yaz_marc_get_nmem(yaz_marc_t mt)
122 {
123     return mt->nmem;
124 }
125
126 static int marc_exec_leader(const char *leader_spec, char *leader,
127                             size_t size);
128
129
130 static struct yaz_marc_node *yaz_marc_add_node(yaz_marc_t mt)
131 {
132     struct yaz_marc_node *n = nmem_malloc(mt->nmem, sizeof(*n));
133     n->next = 0;
134     *mt->nodes_pp = n;
135     mt->nodes_pp = &n->next;
136     return n;
137 }
138
139 #if YAZ_HAVE_XML2
140 void yaz_marc_add_controlfield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
141                                    const xmlNode *ptr_data)
142 {
143     struct yaz_marc_node *n = yaz_marc_add_node(mt);
144     n->which = YAZ_MARC_CONTROLFIELD;
145     n->u.controlfield.tag = nmem_text_node_cdata(ptr_tag, mt->nmem);
146     n->u.controlfield.data = nmem_text_node_cdata(ptr_data, mt->nmem);
147 }
148 #endif
149
150
151 void yaz_marc_add_comment(yaz_marc_t mt, char *comment)
152 {
153     struct yaz_marc_node *n = yaz_marc_add_node(mt);
154     n->which = YAZ_MARC_COMMENT;
155     n->u.comment = nmem_strdup(mt->nmem, comment);
156 }
157
158 void yaz_marc_cprintf(yaz_marc_t mt, const char *fmt, ...)
159 {
160     va_list ap;
161     char buf[200];
162     va_start(ap, fmt);
163
164 #ifdef WIN32
165     _vsnprintf(buf, sizeof(buf)-1, fmt, ap);
166 #else
167 /* !WIN32 */
168 #if HAVE_VSNPRINTF
169     vsnprintf(buf, sizeof(buf), fmt, ap);
170 #else
171     vsprintf(buf, fmt, ap);
172 #endif
173 #endif
174 /* WIN32 */
175     yaz_marc_add_comment(mt, buf);
176     va_end (ap);
177 }
178
179 int yaz_marc_get_debug(yaz_marc_t mt)
180 {
181     return mt->debug;
182 }
183
184 void yaz_marc_add_leader(yaz_marc_t mt, const char *leader, size_t leader_len)
185 {
186     struct yaz_marc_node *n = yaz_marc_add_node(mt);
187     n->which = YAZ_MARC_LEADER;
188     n->u.leader = nmem_strdupn(mt->nmem, leader, leader_len);
189     marc_exec_leader(mt->leader_spec, n->u.leader, leader_len);
190 }
191
192 void yaz_marc_add_controlfield(yaz_marc_t mt, const char *tag,
193                                const char *data, size_t data_len)
194 {
195     struct yaz_marc_node *n = yaz_marc_add_node(mt);
196     n->which = YAZ_MARC_CONTROLFIELD;
197     n->u.controlfield.tag = nmem_strdup(mt->nmem, tag);
198     n->u.controlfield.data = nmem_strdupn(mt->nmem, data, data_len);
199     if (mt->debug)
200     {
201         size_t i;
202         char msg[80];
203
204         sprintf(msg, "controlfield:");
205         for (i = 0; i < 16 && i < data_len; i++)
206             sprintf(msg + strlen(msg), " %02X", data[i] & 0xff);
207         if (i < data_len)
208             sprintf(msg + strlen(msg), " ..");
209         yaz_marc_add_comment(mt, msg);
210     }
211 }
212
213 void yaz_marc_add_datafield(yaz_marc_t mt, const char *tag,
214                             const char *indicator, size_t indicator_len)
215 {
216     struct yaz_marc_node *n = yaz_marc_add_node(mt);
217     n->which = YAZ_MARC_DATAFIELD;
218     n->u.datafield.tag = nmem_strdup(mt->nmem, tag);
219     n->u.datafield.indicator =
220         nmem_strdupn(mt->nmem, indicator, indicator_len);
221     n->u.datafield.subfields = 0;
222
223     /* make subfield_pp the current (last one) */
224     mt->subfield_pp = &n->u.datafield.subfields;
225 }
226
227 #if YAZ_HAVE_XML2
228 void yaz_marc_add_datafield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
229                                 const char *indicator, size_t indicator_len)
230 {
231     struct yaz_marc_node *n = yaz_marc_add_node(mt);
232     n->which = YAZ_MARC_DATAFIELD;
233     n->u.datafield.tag = nmem_text_node_cdata(ptr_tag, mt->nmem);
234     n->u.datafield.indicator =
235         nmem_strdupn(mt->nmem, indicator, indicator_len);
236     n->u.datafield.subfields = 0;
237
238     /* make subfield_pp the current (last one) */
239     mt->subfield_pp = &n->u.datafield.subfields;
240 }
241 #endif
242
243 void yaz_marc_add_subfield(yaz_marc_t mt,
244                            const char *code_data, size_t code_data_len)
245 {
246     if (mt->debug)
247     {
248         size_t i;
249         char msg[80];
250
251         sprintf(msg, "subfield:");
252         for (i = 0; i < 16 && i < code_data_len; i++)
253             sprintf(msg + strlen(msg), " %02X", code_data[i] & 0xff);
254         if (i < code_data_len)
255             sprintf(msg + strlen(msg), " ..");
256         yaz_marc_add_comment(mt, msg);
257     }
258
259     if (mt->subfield_pp)
260     {
261         struct yaz_marc_subfield *n = nmem_malloc(mt->nmem, sizeof(*n));
262         n->code_data = nmem_strdupn(mt->nmem, code_data, code_data_len);
263         n->next = 0;
264         /* mark subfield_pp to point to this one, so we append here next */
265         *mt->subfield_pp = n;
266         mt->subfield_pp = &n->next;
267     }
268 }
269
270 static int atoi_n_check(const char *buf, int size, int *val)
271 {
272     if (!isdigit(*(const unsigned char *) buf))
273         return 0;
274     *val = atoi_n(buf, size);
275     return 1;
276 }
277
278 void yaz_marc_set_leader(yaz_marc_t mt, const char *leader_c,
279                          int *indicator_length,
280                          int *identifier_length,
281                          int *base_address,
282                          int *length_data_entry,
283                          int *length_starting,
284                          int *length_implementation)
285 {
286     char leader[24];
287
288     memcpy(leader, leader_c, 24);
289
290     if (!atoi_n_check(leader+10, 1, indicator_length))
291     {
292         yaz_marc_cprintf(mt, 
293                          "Indicator length at offset 10 should hold a digit."
294                          " Assuming 2");
295         leader[10] = '2';
296         *indicator_length = 2;
297     }
298     if (!atoi_n_check(leader+11, 1, identifier_length))
299     {
300         yaz_marc_cprintf(mt, 
301                          "Identifier length at offset 11 should hold a digit."
302                          " Assuming 2");
303         leader[11] = '2';
304         *identifier_length = 2;
305     }
306     if (!atoi_n_check(leader+12, 5, base_address))
307     {
308         yaz_marc_cprintf(mt, 
309                          "Base address at offsets 12..16 should hold a number."
310                          " Assuming 0");
311         *base_address = 0;
312     }
313     if (!atoi_n_check(leader+20, 1, length_data_entry))
314     {
315         yaz_marc_cprintf(mt, 
316                          "Length data entry at offset 20 should hold a digit."
317                          " Assuming 4");
318         *length_data_entry = 4;
319         leader[20] = '4';
320     }
321     if (!atoi_n_check(leader+21, 1, length_starting))
322     {
323         yaz_marc_cprintf(mt,
324                          "Length starting at offset 21 should hold a digit."
325                          " Assuming 5");
326         *length_starting = 5;
327         leader[21] = '5';
328     }
329     if (!atoi_n_check(leader+22, 1, length_implementation))
330     {
331         yaz_marc_cprintf(mt, 
332                          "Length implementation at offset 22 should hold a digit."
333                          " Assuming 0");
334         *length_implementation = 0;
335         leader[22] = '0';
336     }
337
338     if (mt->debug)
339     {
340         yaz_marc_cprintf(mt, "Indicator length      %5d", *indicator_length);
341         yaz_marc_cprintf(mt, "Identifier length     %5d", *identifier_length);
342         yaz_marc_cprintf(mt, "Base address          %5d", *base_address);
343         yaz_marc_cprintf(mt, "Length data entry     %5d", *length_data_entry);
344         yaz_marc_cprintf(mt, "Length starting       %5d", *length_starting);
345         yaz_marc_cprintf(mt, "Length implementation %5d", *length_implementation);
346     }
347     yaz_marc_add_leader(mt, leader, 24);
348 }
349
350 void yaz_marc_subfield_str(yaz_marc_t mt, const char *s)
351 {
352     strncpy(mt->subfield_str, s, sizeof(mt->subfield_str)-1);
353     mt->subfield_str[sizeof(mt->subfield_str)-1] = '\0';
354 }
355
356 void yaz_marc_endline_str(yaz_marc_t mt, const char *s)
357 {
358     strncpy(mt->endline_str, s, sizeof(mt->endline_str)-1);
359     mt->endline_str[sizeof(mt->endline_str)-1] = '\0';
360 }
361
362 /* try to guess how many bytes the identifier really is! */
363 static size_t cdata_one_character(yaz_marc_t mt, const char *buf)
364 {
365     if (mt->iconv_cd)
366     {
367         size_t i;
368         for (i = 1; i<5; i++)
369         {
370             char outbuf[12];
371             size_t outbytesleft = sizeof(outbuf);
372             char *outp = outbuf;
373             const char *inp = buf;
374
375             size_t inbytesleft = i;
376             size_t r = yaz_iconv(mt->iconv_cd, (char**) &inp, &inbytesleft,
377                                  &outp, &outbytesleft);
378             if (r != (size_t) (-1))
379                 return i;  /* got a complete sequence */
380         }
381         return 1; /* giving up */
382     }
383     return 1; /* we don't know */
384 }
385                               
386 void yaz_marc_reset(yaz_marc_t mt)
387 {
388     nmem_reset(mt->nmem);
389     mt->nodes = 0;
390     mt->nodes_pp = &mt->nodes;
391     mt->subfield_pp = 0;
392 }
393
394 int yaz_marc_write_check(yaz_marc_t mt, WRBUF wr)
395 {
396     struct yaz_marc_node *n;
397     int identifier_length;
398     const char *leader = 0;
399
400     for (n = mt->nodes; n; n = n->next)
401         if (n->which == YAZ_MARC_LEADER)
402         {
403             leader = n->u.leader;
404             break;
405         }
406     
407     if (!leader)
408         return -1;
409     if (!atoi_n_check(leader+11, 1, &identifier_length))
410         return -1;
411
412     for (n = mt->nodes; n; n = n->next)
413     {
414         switch(n->which)
415         {
416         case YAZ_MARC_COMMENT:
417             wrbuf_iconv_write(wr, mt->iconv_cd, 
418                               n->u.comment, strlen(n->u.comment));
419             wrbuf_puts(wr, ")\n");
420             break;
421         default:
422             break;
423         }
424     }
425     return 0;
426 }
427
428
429 int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
430 {
431     struct yaz_marc_node *n;
432     int identifier_length;
433     const char *leader = 0;
434
435     for (n = mt->nodes; n; n = n->next)
436         if (n->which == YAZ_MARC_LEADER)
437         {
438             leader = n->u.leader;
439             break;
440         }
441     
442     if (!leader)
443         return -1;
444     if (!atoi_n_check(leader+11, 1, &identifier_length))
445         return -1;
446
447     for (n = mt->nodes; n; n = n->next)
448     {
449         struct yaz_marc_subfield *s;
450         switch(n->which)
451         {
452         case YAZ_MARC_DATAFIELD:
453             wrbuf_printf(wr, "%s %s", n->u.datafield.tag,
454                          n->u.datafield.indicator);
455             for (s = n->u.datafield.subfields; s; s = s->next)
456             {
457                 /* if identifier length is 2 (most MARCs),
458                    the code is a single character .. However we've
459                    seen multibyte codes, so see how big it really is */
460                 size_t using_code_len = 
461                     (identifier_length != 2) ? identifier_length - 1
462                     :
463                     cdata_one_character(mt, s->code_data);
464                 
465                 wrbuf_puts (wr, mt->subfield_str); 
466                 wrbuf_iconv_write(wr, mt->iconv_cd, s->code_data, 
467                                   using_code_len);
468                 wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
469                 wrbuf_iconv_puts(wr, mt->iconv_cd, 
470                                  s->code_data + using_code_len);
471                 wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
472                 wr->pos--;
473             }
474             wrbuf_puts (wr, mt->endline_str);
475             break;
476         case YAZ_MARC_CONTROLFIELD:
477             wrbuf_printf(wr, "%s", n->u.controlfield.tag);
478             wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
479             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
480             wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
481             wr->pos--;
482             wrbuf_puts (wr, mt->endline_str);
483             break;
484         case YAZ_MARC_COMMENT:
485             wrbuf_puts(wr, "(");
486             wrbuf_iconv_write(wr, mt->iconv_cd, 
487                               n->u.comment, strlen(n->u.comment));
488             wrbuf_puts(wr, ")\n");
489             break;
490         case YAZ_MARC_LEADER:
491             wrbuf_printf(wr, "%s\n", n->u.leader);
492         }
493     }
494     return 0;
495 }
496
497 int yaz_marc_write_mode(yaz_marc_t mt, WRBUF wr)
498 {
499     switch(mt->xml)
500     {
501     case YAZ_MARC_LINE:
502         return yaz_marc_write_line(mt, wr);
503     case YAZ_MARC_MARCXML:
504         return yaz_marc_write_marcxml(mt, wr);
505     case YAZ_MARC_XCHANGE:
506         return yaz_marc_write_marcxchange(mt, wr, 0, 0); /* no format, type */
507     case YAZ_MARC_ISO2709:
508         return yaz_marc_write_iso2709(mt, wr);
509     case YAZ_MARC_CHECK:
510         return yaz_marc_write_check(mt, wr);
511     }
512     return -1;
513 }
514
515 /** \brief common MARC XML/Xchange writer
516     \param mt handle
517     \param wr WRBUF output
518     \param ns XMLNS for the elements
519     \param format record format (e.g. "MARC21")
520     \param type record type (e.g. "Bibliographic")
521 */
522 static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
523                                      const char *ns, 
524                                      const char *format,
525                                      const char *type)
526 {
527     struct yaz_marc_node *n;
528     int identifier_length;
529     const char *leader = 0;
530
531     for (n = mt->nodes; n; n = n->next)
532         if (n->which == YAZ_MARC_LEADER)
533         {
534             leader = n->u.leader;
535             break;
536         }
537     
538     if (!leader)
539         return -1;
540     if (!atoi_n_check(leader+11, 1, &identifier_length))
541         return -1;
542
543     wrbuf_printf(wr, "<record xmlns=\"%s\"", ns);
544     if (format)
545         wrbuf_printf(wr, " format=\"%.80s\"", format);
546     if (type)
547         wrbuf_printf(wr, " type=\"%.80s\"", type);
548     wrbuf_printf(wr, ">\n");
549     for (n = mt->nodes; n; n = n->next)
550     {
551         struct yaz_marc_subfield *s;
552
553         switch(n->which)
554         {
555         case YAZ_MARC_DATAFIELD:
556             wrbuf_printf(wr, "  <datafield tag=\"");
557             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
558                                     strlen(n->u.datafield.tag));
559             wrbuf_printf(wr, "\"");
560             if (n->u.datafield.indicator)
561             {
562                 int i;
563                 for (i = 0; n->u.datafield.indicator[i]; i++)
564                 {
565                     wrbuf_printf(wr, " ind%d=\"", i+1);
566                     wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
567                                           n->u.datafield.indicator+i, 1);
568                     wrbuf_iconv_puts(wr, mt->iconv_cd, "\"");
569                 }
570             }
571             wrbuf_printf(wr, ">\n");
572             for (s = n->u.datafield.subfields; s; s = s->next)
573             {
574                 /* if identifier length is 2 (most MARCs),
575                    the code is a single character .. However we've
576                    seen multibyte codes, so see how big it really is */
577                 size_t using_code_len = 
578                     (identifier_length != 2) ? identifier_length - 1
579                     :
580                     cdata_one_character(mt, s->code_data);
581                 
582                 wrbuf_iconv_puts(wr, mt->iconv_cd, "    <subfield code=\"");
583                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
584                                         s->code_data, using_code_len);
585                 wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
586                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
587                                         s->code_data + using_code_len,
588                                         strlen(s->code_data + using_code_len));
589                 wrbuf_iconv_puts(wr, mt->iconv_cd, "</subfield>");
590                 wrbuf_puts(wr, "\n");
591             }
592             wrbuf_printf(wr, "  </datafield>\n");
593             break;
594         case YAZ_MARC_CONTROLFIELD:
595             wrbuf_printf(wr, "  <controlfield tag=\"");
596             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
597                                     strlen(n->u.controlfield.tag));
598             wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
599             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
600             wrbuf_iconv_puts(wr, mt->iconv_cd, "</controlfield>");
601             wrbuf_puts(wr, "\n");
602             break;
603         case YAZ_MARC_COMMENT:
604             wrbuf_printf(wr, "<!-- ");
605             wrbuf_puts(wr, n->u.comment);
606             wrbuf_printf(wr, " -->\n");
607             break;
608         case YAZ_MARC_LEADER:
609             wrbuf_printf(wr, "  <leader>");
610             wrbuf_iconv_write_cdata(wr, 
611                                     0 /* no charset conversion for leader */,
612                                     n->u.leader, strlen(n->u.leader));
613             wrbuf_printf(wr, "</leader>\n");
614         }
615     }
616     wrbuf_puts(wr, "</record>\n");
617     return 0;
618 }
619
620 int yaz_marc_write_marcxml(yaz_marc_t mt, WRBUF wr)
621 {
622     if (!mt->leader_spec)
623         yaz_marc_modify_leader(mt, 9, "a");
624     return yaz_marc_write_marcxml_ns(mt, wr, "http://www.loc.gov/MARC21/slim",
625                                      0, 0);
626 }
627
628 int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
629                                const char *format,
630                                const char *type)
631 {
632     return yaz_marc_write_marcxml_ns(mt, wr,
633                                      "http://www.bs.dk/standards/MarcXchange",
634                                      0, 0);
635 }
636
637 int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
638 {
639     struct yaz_marc_node *n;
640     int indicator_length;
641     int identifier_length;
642     int length_data_entry;
643     int length_starting;
644     int length_implementation;
645     int data_offset = 0;
646     const char *leader = 0;
647     WRBUF wr_dir, wr_head, wr_data_tmp;
648     int base_address;
649     
650     for (n = mt->nodes; n; n = n->next)
651         if (n->which == YAZ_MARC_LEADER)
652             leader = n->u.leader;
653     
654     if (!leader)
655         return -1;
656     if (!atoi_n_check(leader+10, 1, &indicator_length))
657         return -1;
658     if (!atoi_n_check(leader+11, 1, &identifier_length))
659         return -1;
660     if (!atoi_n_check(leader+20, 1, &length_data_entry))
661         return -1;
662     if (!atoi_n_check(leader+21, 1, &length_starting))
663         return -1;
664     if (!atoi_n_check(leader+22, 1, &length_implementation))
665         return -1;
666
667     wr_data_tmp = wrbuf_alloc();
668     wr_dir = wrbuf_alloc();
669     for (n = mt->nodes; n; n = n->next)
670     {
671         int data_length = 0;
672         struct yaz_marc_subfield *s;
673
674         switch(n->which)
675         {
676         case YAZ_MARC_DATAFIELD:
677             wrbuf_printf(wr_dir, "%.3s", n->u.datafield.tag);
678             data_length += indicator_length;
679             wrbuf_rewind(wr_data_tmp);
680             for (s = n->u.datafield.subfields; s; s = s->next)
681             {
682                 /* write dummy IDFS + content */
683                 wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
684                 wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, s->code_data);
685             }
686             /* write dummy FS (makes MARC-8 to become ASCII) */
687             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
688             data_length += wrbuf_len(wr_data_tmp);
689             break;
690         case YAZ_MARC_CONTROLFIELD:
691             wrbuf_printf(wr_dir, "%.3s", n->u.controlfield.tag);
692
693             wrbuf_rewind(wr_data_tmp);
694             wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, 
695                              n->u.controlfield.data);
696             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');/* field sep */
697             data_length += wrbuf_len(wr_data_tmp);
698             break;
699         case YAZ_MARC_COMMENT:
700             break;
701         case YAZ_MARC_LEADER:
702             break;
703         }
704         if (data_length)
705         {
706             wrbuf_printf(wr_dir, "%0*d", length_data_entry, data_length);
707             wrbuf_printf(wr_dir, "%0*d", length_starting, data_offset);
708             data_offset += data_length;
709         }
710     }
711     /* mark end of directory */
712     wrbuf_putc(wr_dir, ISO2709_FS);
713
714     /* base address of data (comes after leader+directory) */
715     base_address = 24 + wrbuf_len(wr_dir);
716
717     wr_head = wrbuf_alloc();
718
719     /* write record length */
720     wrbuf_printf(wr_head, "%05d", base_address + data_offset + 1);
721     /* from "original" leader */
722     wrbuf_write(wr_head, leader+5, 7);
723     /* base address of data */
724     wrbuf_printf(wr_head, "%05d", base_address);
725     /* from "original" leader */
726     wrbuf_write(wr_head, leader+17, 7);
727     
728     wrbuf_write(wr, wrbuf_buf(wr_head), 24);
729     wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
730     wrbuf_free(wr_head, 1);
731     wrbuf_free(wr_dir, 1);
732     wrbuf_free(wr_data_tmp, 1);
733
734     for (n = mt->nodes; n; n = n->next)
735     {
736         struct yaz_marc_subfield *s;
737
738         switch(n->which)
739         {
740         case YAZ_MARC_DATAFIELD:
741             wrbuf_printf(wr, "%.*s", indicator_length,
742                          n->u.datafield.indicator);
743             for (s = n->u.datafield.subfields; s; s = s->next)
744             {
745                 wrbuf_putc(wr, ISO2709_IDFS);
746                 wrbuf_iconv_puts(wr, mt->iconv_cd, s->code_data);
747                 /* write dummy blank - makes MARC-8 to become ASCII */
748                 wrbuf_iconv_putchar(wr, mt->iconv_cd, ' ');
749                 wr->pos--;
750             }
751             wrbuf_putc(wr, ISO2709_FS);
752             break;
753         case YAZ_MARC_CONTROLFIELD:
754             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
755             /* write dummy blank - makes MARC-8 to become ASCII */
756             wrbuf_iconv_putchar(wr, mt->iconv_cd, ' ');
757             wr->pos--;
758             wrbuf_putc(wr, ISO2709_FS);
759             break;
760         case YAZ_MARC_COMMENT:
761             break;
762         case YAZ_MARC_LEADER:
763             break;
764         }
765     }
766     wrbuf_printf(wr, "%c", ISO2709_RS);
767     return 0;
768 }
769
770
771 int yaz_marc_decode_wrbuf(yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
772 {
773     int s, r = yaz_marc_read_iso2709(mt, buf, bsize);
774     if (r <= 0)
775         return r;
776     s = yaz_marc_write_mode(mt, wr); /* returns 0 for OK, -1 otherwise */
777     if (s != 0)
778         return -1; /* error */
779     return r; /* OK, return length > 0 */
780 }
781
782 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
783                          char **result, int *rsize)
784 {
785     int r;
786
787     wrbuf_rewind(mt->m_wr);
788     r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
789     if (result)
790         *result = wrbuf_buf(mt->m_wr);
791     if (rsize)
792         *rsize = wrbuf_len(mt->m_wr);
793     return r;
794 }
795
796 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
797 {
798     if (mt)
799         mt->xml = xmlmode;
800 }
801
802 void yaz_marc_debug(yaz_marc_t mt, int level)
803 {
804     if (mt)
805         mt->debug = level;
806 }
807
808 void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
809 {
810     mt->iconv_cd = cd;
811 }
812
813 void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
814 {
815     struct yaz_marc_node *n;
816     char *leader = 0;
817     for (n = mt->nodes; n; n = n->next)
818         if (n->which == YAZ_MARC_LEADER)
819         {
820             leader = n->u.leader;
821             memcpy(leader+off, str, strlen(str));
822             break;
823         }
824 }
825
826 /* deprecated */
827 int yaz_marc_decode(const char *buf, WRBUF wr, int debug, int bsize, int xml)
828 {
829     yaz_marc_t mt = yaz_marc_create();
830     int r;
831
832     mt->debug = debug;
833     mt->xml = xml;
834     r = yaz_marc_decode_wrbuf(mt, buf, bsize, wr);
835     yaz_marc_destroy(mt);
836     return r;
837 }
838
839 /* deprecated */
840 int marc_display_wrbuf (const char *buf, WRBUF wr, int debug, int bsize)
841 {
842     return yaz_marc_decode(buf, wr, debug, bsize, 0);
843 }
844
845 /* deprecated */
846 int marc_display_exl (const char *buf, FILE *outf, int debug, int bsize)
847 {
848     yaz_marc_t mt = yaz_marc_create();
849     int r;
850
851     mt->debug = debug;
852     r = yaz_marc_decode_wrbuf (mt, buf, bsize, mt->m_wr);
853     if (!outf)
854         outf = stdout;
855     if (r > 0)
856         fwrite (wrbuf_buf(mt->m_wr), 1, wrbuf_len(mt->m_wr), outf);
857     yaz_marc_destroy(mt);
858     return r;
859 }
860
861 /* deprecated */
862 int marc_display_ex (const char *buf, FILE *outf, int debug)
863 {
864     return marc_display_exl (buf, outf, debug, -1);
865 }
866
867 /* deprecated */
868 int marc_display (const char *buf, FILE *outf)
869 {
870     return marc_display_ex (buf, outf, 0);
871 }
872
873 int yaz_marc_leader_spec(yaz_marc_t mt, const char *leader_spec)
874 {
875     xfree(mt->leader_spec);
876     mt->leader_spec = 0;
877     if (leader_spec)
878     {
879         char dummy_leader[24];
880         if (marc_exec_leader(leader_spec, dummy_leader, 24))
881             return -1;
882         mt->leader_spec = xstrdup(leader_spec);
883     }
884     return 0;
885 }
886
887 static int marc_exec_leader(const char *leader_spec, char *leader, size_t size)
888 {
889     const char *cp = leader_spec;
890     while (cp)
891     {
892         char val[21];
893         int pos;
894         int no_read = 0, no = 0;
895
896         no = sscanf(cp, "%d=%20[^,]%n", &pos, val, &no_read);
897         if (no < 2 || no_read < 3)
898             return -1;
899         if (pos < 0 || pos >= size)
900             return -1;
901
902         if (*val == '\'')
903         {
904             const char *vp = strchr(val+1, '\'');
905             size_t len;
906             
907             if (!vp)
908                 return -1;
909             len = vp-val-1;
910             if (len + pos > size)
911                 return -1;
912             memcpy(leader + pos, val+1, len);
913         }
914         else if (*val >= '0' && *val <= '9')
915         {
916             int ch = atoi(val);
917             leader[pos] = ch;
918         }
919         else
920             return -1;
921         cp += no_read;
922         if (*cp != ',')
923             break;
924
925         cp++;
926     }
927     return 0;
928 }
929
930 /*
931  * Local variables:
932  * c-basic-offset: 4
933  * indent-tabs-mode: nil
934  * End:
935  * vim: shiftwidth=4 tabstop=8 expandtab
936  */
937