Implemented function yaz_marc_read_line which parses MARC line format
[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.39 2006-12-15 19:28:47 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 int atoi_n_check(const char *buf, int size, int *val)
271 {
272     int i;
273     for (i = 0; i < size; i++)
274         if (!isdigit(i[(const unsigned char *) buf]))
275             return 0;
276     *val = atoi_n(buf, size);
277     return 1;
278 }
279
280 void yaz_marc_set_leader(yaz_marc_t mt, const char *leader_c,
281                          int *indicator_length,
282                          int *identifier_length,
283                          int *base_address,
284                          int *length_data_entry,
285                          int *length_starting,
286                          int *length_implementation)
287 {
288     char leader[24];
289
290     memcpy(leader, leader_c, 24);
291
292     if (!atoi_n_check(leader+10, 1, indicator_length))
293     {
294         yaz_marc_cprintf(mt, 
295                          "Indicator length at offset 10 should hold a digit."
296                          " Assuming 2");
297         leader[10] = '2';
298         *indicator_length = 2;
299     }
300     if (!atoi_n_check(leader+11, 1, identifier_length))
301     {
302         yaz_marc_cprintf(mt, 
303                          "Identifier length at offset 11 should hold a digit."
304                          " Assuming 2");
305         leader[11] = '2';
306         *identifier_length = 2;
307     }
308     if (!atoi_n_check(leader+12, 5, base_address))
309     {
310         yaz_marc_cprintf(mt, 
311                          "Base address at offsets 12..16 should hold a number."
312                          " Assuming 0");
313         *base_address = 0;
314     }
315     if (!atoi_n_check(leader+20, 1, length_data_entry))
316     {
317         yaz_marc_cprintf(mt, 
318                          "Length data entry at offset 20 should hold a digit."
319                          " Assuming 4");
320         *length_data_entry = 4;
321         leader[20] = '4';
322     }
323     if (!atoi_n_check(leader+21, 1, length_starting))
324     {
325         yaz_marc_cprintf(mt,
326                          "Length starting at offset 21 should hold a digit."
327                          " Assuming 5");
328         *length_starting = 5;
329         leader[21] = '5';
330     }
331     if (!atoi_n_check(leader+22, 1, length_implementation))
332     {
333         yaz_marc_cprintf(mt, 
334                          "Length implementation at offset 22 should hold a digit."
335                          " Assuming 0");
336         *length_implementation = 0;
337         leader[22] = '0';
338     }
339
340     if (mt->debug)
341     {
342         yaz_marc_cprintf(mt, "Indicator length      %5d", *indicator_length);
343         yaz_marc_cprintf(mt, "Identifier length     %5d", *identifier_length);
344         yaz_marc_cprintf(mt, "Base address          %5d", *base_address);
345         yaz_marc_cprintf(mt, "Length data entry     %5d", *length_data_entry);
346         yaz_marc_cprintf(mt, "Length starting       %5d", *length_starting);
347         yaz_marc_cprintf(mt, "Length implementation %5d", *length_implementation);
348     }
349     yaz_marc_add_leader(mt, leader, 24);
350 }
351
352 void yaz_marc_subfield_str(yaz_marc_t mt, const char *s)
353 {
354     strncpy(mt->subfield_str, s, sizeof(mt->subfield_str)-1);
355     mt->subfield_str[sizeof(mt->subfield_str)-1] = '\0';
356 }
357
358 void yaz_marc_endline_str(yaz_marc_t mt, const char *s)
359 {
360     strncpy(mt->endline_str, s, sizeof(mt->endline_str)-1);
361     mt->endline_str[sizeof(mt->endline_str)-1] = '\0';
362 }
363
364 /* try to guess how many bytes the identifier really is! */
365 static size_t cdata_one_character(yaz_marc_t mt, const char *buf)
366 {
367     if (mt->iconv_cd)
368     {
369         size_t i;
370         for (i = 1; i<5; i++)
371         {
372             char outbuf[12];
373             size_t outbytesleft = sizeof(outbuf);
374             char *outp = outbuf;
375             const char *inp = buf;
376
377             size_t inbytesleft = i;
378             size_t r = yaz_iconv(mt->iconv_cd, (char**) &inp, &inbytesleft,
379                                  &outp, &outbytesleft);
380             if (r != (size_t) (-1))
381                 return i;  /* got a complete sequence */
382         }
383         return 1; /* giving up */
384     }
385     return 1; /* we don't know */
386 }
387                               
388 void yaz_marc_reset(yaz_marc_t mt)
389 {
390     nmem_reset(mt->nmem);
391     mt->nodes = 0;
392     mt->nodes_pp = &mt->nodes;
393     mt->subfield_pp = 0;
394 }
395
396 int yaz_marc_write_check(yaz_marc_t mt, WRBUF wr)
397 {
398     struct yaz_marc_node *n;
399     int identifier_length;
400     const char *leader = 0;
401
402     for (n = mt->nodes; n; n = n->next)
403         if (n->which == YAZ_MARC_LEADER)
404         {
405             leader = n->u.leader;
406             break;
407         }
408     
409     if (!leader)
410         return -1;
411     if (!atoi_n_check(leader+11, 1, &identifier_length))
412         return -1;
413
414     for (n = mt->nodes; n; n = n->next)
415     {
416         switch(n->which)
417         {
418         case YAZ_MARC_COMMENT:
419             wrbuf_iconv_write(wr, mt->iconv_cd, 
420                               n->u.comment, strlen(n->u.comment));
421             wrbuf_puts(wr, ")\n");
422             break;
423         default:
424             break;
425         }
426     }
427     return 0;
428 }
429
430
431 int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
432 {
433     struct yaz_marc_node *n;
434     int identifier_length;
435     const char *leader = 0;
436
437     for (n = mt->nodes; n; n = n->next)
438         if (n->which == YAZ_MARC_LEADER)
439         {
440             leader = n->u.leader;
441             break;
442         }
443     
444     if (!leader)
445         return -1;
446     if (!atoi_n_check(leader+11, 1, &identifier_length))
447         return -1;
448
449     for (n = mt->nodes; n; n = n->next)
450     {
451         struct yaz_marc_subfield *s;
452         switch(n->which)
453         {
454         case YAZ_MARC_DATAFIELD:
455             wrbuf_printf(wr, "%s %s", n->u.datafield.tag,
456                          n->u.datafield.indicator);
457             for (s = n->u.datafield.subfields; s; s = s->next)
458             {
459                 /* if identifier length is 2 (most MARCs),
460                    the code is a single character .. However we've
461                    seen multibyte codes, so see how big it really is */
462                 size_t using_code_len = 
463                     (identifier_length != 2) ? identifier_length - 1
464                     :
465                     cdata_one_character(mt, s->code_data);
466                 
467                 wrbuf_puts (wr, mt->subfield_str); 
468                 wrbuf_iconv_write(wr, mt->iconv_cd, s->code_data, 
469                                   using_code_len);
470                 wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
471                 wrbuf_iconv_puts(wr, mt->iconv_cd, 
472                                  s->code_data + using_code_len);
473                 wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
474                 wr->pos--;
475             }
476             wrbuf_puts (wr, mt->endline_str);
477             break;
478         case YAZ_MARC_CONTROLFIELD:
479             wrbuf_printf(wr, "%s", n->u.controlfield.tag);
480             wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
481             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
482             wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
483             wr->pos--;
484             wrbuf_puts (wr, mt->endline_str);
485             break;
486         case YAZ_MARC_COMMENT:
487             wrbuf_puts(wr, "(");
488             wrbuf_iconv_write(wr, mt->iconv_cd, 
489                               n->u.comment, strlen(n->u.comment));
490             wrbuf_puts(wr, ")\n");
491             break;
492         case YAZ_MARC_LEADER:
493             wrbuf_printf(wr, "%s\n", n->u.leader);
494         }
495     }
496     wrbuf_puts(wr, "\n");
497     return 0;
498 }
499
500 int yaz_marc_write_mode(yaz_marc_t mt, WRBUF wr)
501 {
502     switch(mt->xml)
503     {
504     case YAZ_MARC_LINE:
505         return yaz_marc_write_line(mt, wr);
506     case YAZ_MARC_MARCXML:
507         return yaz_marc_write_marcxml(mt, wr);
508     case YAZ_MARC_XCHANGE:
509         return yaz_marc_write_marcxchange(mt, wr, 0, 0); /* no format, type */
510     case YAZ_MARC_ISO2709:
511         return yaz_marc_write_iso2709(mt, wr);
512     case YAZ_MARC_CHECK:
513         return yaz_marc_write_check(mt, wr);
514     }
515     return -1;
516 }
517
518 /** \brief common MARC XML/Xchange writer
519     \param mt handle
520     \param wr WRBUF output
521     \param ns XMLNS for the elements
522     \param format record format (e.g. "MARC21")
523     \param type record type (e.g. "Bibliographic")
524 */
525 static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
526                                      const char *ns, 
527                                      const char *format,
528                                      const char *type)
529 {
530     struct yaz_marc_node *n;
531     int identifier_length;
532     const char *leader = 0;
533
534     for (n = mt->nodes; n; n = n->next)
535         if (n->which == YAZ_MARC_LEADER)
536         {
537             leader = n->u.leader;
538             break;
539         }
540     
541     if (!leader)
542         return -1;
543     if (!atoi_n_check(leader+11, 1, &identifier_length))
544         return -1;
545
546     wrbuf_printf(wr, "<record xmlns=\"%s\"", ns);
547     if (format)
548         wrbuf_printf(wr, " format=\"%.80s\"", format);
549     if (type)
550         wrbuf_printf(wr, " type=\"%.80s\"", type);
551     wrbuf_printf(wr, ">\n");
552     for (n = mt->nodes; n; n = n->next)
553     {
554         struct yaz_marc_subfield *s;
555
556         switch(n->which)
557         {
558         case YAZ_MARC_DATAFIELD:
559             wrbuf_printf(wr, "  <datafield tag=\"");
560             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
561                                     strlen(n->u.datafield.tag));
562             wrbuf_printf(wr, "\"");
563             if (n->u.datafield.indicator)
564             {
565                 int i;
566                 for (i = 0; n->u.datafield.indicator[i]; i++)
567                 {
568                     wrbuf_printf(wr, " ind%d=\"", i+1);
569                     wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
570                                           n->u.datafield.indicator+i, 1);
571                     wrbuf_iconv_puts(wr, mt->iconv_cd, "\"");
572                 }
573             }
574             wrbuf_printf(wr, ">\n");
575             for (s = n->u.datafield.subfields; s; s = s->next)
576             {
577                 /* if identifier length is 2 (most MARCs),
578                    the code is a single character .. However we've
579                    seen multibyte codes, so see how big it really is */
580                 size_t using_code_len = 
581                     (identifier_length != 2) ? identifier_length - 1
582                     :
583                     cdata_one_character(mt, s->code_data);
584                 
585                 wrbuf_iconv_puts(wr, mt->iconv_cd, "    <subfield code=\"");
586                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
587                                         s->code_data, using_code_len);
588                 wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
589                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
590                                         s->code_data + using_code_len,
591                                         strlen(s->code_data + using_code_len));
592                 wrbuf_iconv_puts(wr, mt->iconv_cd, "</subfield>");
593                 wrbuf_puts(wr, "\n");
594             }
595             wrbuf_printf(wr, "  </datafield>\n");
596             break;
597         case YAZ_MARC_CONTROLFIELD:
598             wrbuf_printf(wr, "  <controlfield tag=\"");
599             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
600                                     strlen(n->u.controlfield.tag));
601             wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
602             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
603             wrbuf_iconv_puts(wr, mt->iconv_cd, "</controlfield>");
604             wrbuf_puts(wr, "\n");
605             break;
606         case YAZ_MARC_COMMENT:
607             wrbuf_printf(wr, "<!-- ");
608             wrbuf_puts(wr, n->u.comment);
609             wrbuf_printf(wr, " -->\n");
610             break;
611         case YAZ_MARC_LEADER:
612             wrbuf_printf(wr, "  <leader>");
613             wrbuf_iconv_write_cdata(wr, 
614                                     0 /* no charset conversion for leader */,
615                                     n->u.leader, strlen(n->u.leader));
616             wrbuf_printf(wr, "</leader>\n");
617         }
618     }
619     wrbuf_puts(wr, "</record>\n");
620     return 0;
621 }
622
623 int yaz_marc_write_marcxml(yaz_marc_t mt, WRBUF wr)
624 {
625     if (!mt->leader_spec)
626         yaz_marc_modify_leader(mt, 9, "a");
627     return yaz_marc_write_marcxml_ns(mt, wr, "http://www.loc.gov/MARC21/slim",
628                                      0, 0);
629 }
630
631 int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
632                                const char *format,
633                                const char *type)
634 {
635     return yaz_marc_write_marcxml_ns(mt, wr,
636                                      "http://www.bs.dk/standards/MarcXchange",
637                                      0, 0);
638 }
639
640 int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
641 {
642     struct yaz_marc_node *n;
643     int indicator_length;
644     int identifier_length;
645     int length_data_entry;
646     int length_starting;
647     int length_implementation;
648     int data_offset = 0;
649     const char *leader = 0;
650     WRBUF wr_dir, wr_head, wr_data_tmp;
651     int base_address;
652     
653     for (n = mt->nodes; n; n = n->next)
654         if (n->which == YAZ_MARC_LEADER)
655             leader = n->u.leader;
656     
657     if (!leader)
658         return -1;
659     if (!atoi_n_check(leader+10, 1, &indicator_length))
660         return -1;
661     if (!atoi_n_check(leader+11, 1, &identifier_length))
662         return -1;
663     if (!atoi_n_check(leader+20, 1, &length_data_entry))
664         return -1;
665     if (!atoi_n_check(leader+21, 1, &length_starting))
666         return -1;
667     if (!atoi_n_check(leader+22, 1, &length_implementation))
668         return -1;
669
670     wr_data_tmp = wrbuf_alloc();
671     wr_dir = wrbuf_alloc();
672     for (n = mt->nodes; n; n = n->next)
673     {
674         int data_length = 0;
675         struct yaz_marc_subfield *s;
676
677         switch(n->which)
678         {
679         case YAZ_MARC_DATAFIELD:
680             wrbuf_printf(wr_dir, "%.3s", n->u.datafield.tag);
681             data_length += indicator_length;
682             wrbuf_rewind(wr_data_tmp);
683             for (s = n->u.datafield.subfields; s; s = s->next)
684             {
685                 /* write dummy IDFS + content */
686                 wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
687                 wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, s->code_data);
688             }
689             /* write dummy FS (makes MARC-8 to become ASCII) */
690             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
691             data_length += wrbuf_len(wr_data_tmp);
692             break;
693         case YAZ_MARC_CONTROLFIELD:
694             wrbuf_printf(wr_dir, "%.3s", n->u.controlfield.tag);
695
696             wrbuf_rewind(wr_data_tmp);
697             wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, 
698                              n->u.controlfield.data);
699             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');/* field sep */
700             data_length += wrbuf_len(wr_data_tmp);
701             break;
702         case YAZ_MARC_COMMENT:
703             break;
704         case YAZ_MARC_LEADER:
705             break;
706         }
707         if (data_length)
708         {
709             wrbuf_printf(wr_dir, "%0*d", length_data_entry, data_length);
710             wrbuf_printf(wr_dir, "%0*d", length_starting, data_offset);
711             data_offset += data_length;
712         }
713     }
714     /* mark end of directory */
715     wrbuf_putc(wr_dir, ISO2709_FS);
716
717     /* base address of data (comes after leader+directory) */
718     base_address = 24 + wrbuf_len(wr_dir);
719
720     wr_head = wrbuf_alloc();
721
722     /* write record length */
723     wrbuf_printf(wr_head, "%05d", base_address + data_offset + 1);
724     /* from "original" leader */
725     wrbuf_write(wr_head, leader+5, 7);
726     /* base address of data */
727     wrbuf_printf(wr_head, "%05d", base_address);
728     /* from "original" leader */
729     wrbuf_write(wr_head, leader+17, 7);
730     
731     wrbuf_write(wr, wrbuf_buf(wr_head), 24);
732     wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
733     wrbuf_free(wr_head, 1);
734     wrbuf_free(wr_dir, 1);
735     wrbuf_free(wr_data_tmp, 1);
736
737     for (n = mt->nodes; n; n = n->next)
738     {
739         struct yaz_marc_subfield *s;
740
741         switch(n->which)
742         {
743         case YAZ_MARC_DATAFIELD:
744             wrbuf_printf(wr, "%.*s", indicator_length,
745                          n->u.datafield.indicator);
746             for (s = n->u.datafield.subfields; s; s = s->next)
747             {
748                 wrbuf_putc(wr, ISO2709_IDFS);
749                 wrbuf_iconv_puts(wr, mt->iconv_cd, s->code_data);
750                 /* write dummy blank - makes MARC-8 to become ASCII */
751                 wrbuf_iconv_putchar(wr, mt->iconv_cd, ' ');
752                 wr->pos--;
753             }
754             wrbuf_putc(wr, ISO2709_FS);
755             break;
756         case YAZ_MARC_CONTROLFIELD:
757             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
758             /* write dummy blank - makes MARC-8 to become ASCII */
759             wrbuf_iconv_putchar(wr, mt->iconv_cd, ' ');
760             wr->pos--;
761             wrbuf_putc(wr, ISO2709_FS);
762             break;
763         case YAZ_MARC_COMMENT:
764             break;
765         case YAZ_MARC_LEADER:
766             break;
767         }
768     }
769     wrbuf_printf(wr, "%c", ISO2709_RS);
770     return 0;
771 }
772
773
774 int yaz_marc_decode_wrbuf(yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
775 {
776     int s, r = yaz_marc_read_iso2709(mt, buf, bsize);
777     if (r <= 0)
778         return r;
779     s = yaz_marc_write_mode(mt, wr); /* returns 0 for OK, -1 otherwise */
780     if (s != 0)
781         return -1; /* error */
782     return r; /* OK, return length > 0 */
783 }
784
785 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
786                          char **result, int *rsize)
787 {
788     int r;
789
790     wrbuf_rewind(mt->m_wr);
791     r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
792     if (result)
793         *result = wrbuf_buf(mt->m_wr);
794     if (rsize)
795         *rsize = wrbuf_len(mt->m_wr);
796     return r;
797 }
798
799 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
800 {
801     if (mt)
802         mt->xml = xmlmode;
803 }
804
805 void yaz_marc_debug(yaz_marc_t mt, int level)
806 {
807     if (mt)
808         mt->debug = level;
809 }
810
811 void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
812 {
813     mt->iconv_cd = cd;
814 }
815
816 void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
817 {
818     struct yaz_marc_node *n;
819     char *leader = 0;
820     for (n = mt->nodes; n; n = n->next)
821         if (n->which == YAZ_MARC_LEADER)
822         {
823             leader = n->u.leader;
824             memcpy(leader+off, str, strlen(str));
825             break;
826         }
827 }
828
829 /* deprecated */
830 int yaz_marc_decode(const char *buf, WRBUF wr, int debug, int bsize, int xml)
831 {
832     yaz_marc_t mt = yaz_marc_create();
833     int r;
834
835     mt->debug = debug;
836     mt->xml = xml;
837     r = yaz_marc_decode_wrbuf(mt, buf, bsize, wr);
838     yaz_marc_destroy(mt);
839     return r;
840 }
841
842 /* deprecated */
843 int marc_display_wrbuf (const char *buf, WRBUF wr, int debug, int bsize)
844 {
845     return yaz_marc_decode(buf, wr, debug, bsize, 0);
846 }
847
848 /* deprecated */
849 int marc_display_exl (const char *buf, FILE *outf, int debug, int bsize)
850 {
851     yaz_marc_t mt = yaz_marc_create();
852     int r;
853
854     mt->debug = debug;
855     r = yaz_marc_decode_wrbuf (mt, buf, bsize, mt->m_wr);
856     if (!outf)
857         outf = stdout;
858     if (r > 0)
859         fwrite (wrbuf_buf(mt->m_wr), 1, wrbuf_len(mt->m_wr), outf);
860     yaz_marc_destroy(mt);
861     return r;
862 }
863
864 /* deprecated */
865 int marc_display_ex (const char *buf, FILE *outf, int debug)
866 {
867     return marc_display_exl (buf, outf, debug, -1);
868 }
869
870 /* deprecated */
871 int marc_display (const char *buf, FILE *outf)
872 {
873     return marc_display_ex (buf, outf, 0);
874 }
875
876 int yaz_marc_leader_spec(yaz_marc_t mt, const char *leader_spec)
877 {
878     xfree(mt->leader_spec);
879     mt->leader_spec = 0;
880     if (leader_spec)
881     {
882         char dummy_leader[24];
883         if (marc_exec_leader(leader_spec, dummy_leader, 24))
884             return -1;
885         mt->leader_spec = xstrdup(leader_spec);
886     }
887     return 0;
888 }
889
890 static int marc_exec_leader(const char *leader_spec, char *leader, size_t size)
891 {
892     const char *cp = leader_spec;
893     while (cp)
894     {
895         char val[21];
896         int pos;
897         int no_read = 0, no = 0;
898
899         no = sscanf(cp, "%d=%20[^,]%n", &pos, val, &no_read);
900         if (no < 2 || no_read < 3)
901             return -1;
902         if (pos < 0 || pos >= size)
903             return -1;
904
905         if (*val == '\'')
906         {
907             const char *vp = strchr(val+1, '\'');
908             size_t len;
909             
910             if (!vp)
911                 return -1;
912             len = vp-val-1;
913             if (len + pos > size)
914                 return -1;
915             memcpy(leader + pos, val+1, len);
916         }
917         else if (*val >= '0' && *val <= '9')
918         {
919             int ch = atoi(val);
920             leader[pos] = ch;
921         }
922         else
923             return -1;
924         cp += no_read;
925         if (*cp != ',')
926             break;
927
928         cp++;
929     }
930     return 0;
931 }
932
933 int yaz_marc_decode_formatstr(const char *arg)
934 {
935     int mode = -1; 
936     if (!strcmp(arg, "marc"))
937         mode = YAZ_MARC_ISO2709;
938     if (!strcmp(arg, "marcxml"))
939         mode = YAZ_MARC_MARCXML;
940     if (!strcmp(arg, "marcxchange"))
941         mode = YAZ_MARC_XCHANGE;
942     if (!strcmp(arg, "line"))
943         mode = YAZ_MARC_LINE;
944     return mode;
945 }
946
947 /*
948  * Local variables:
949  * c-basic-offset: 4
950  * indent-tabs-mode: nil
951  * End:
952  * vim: shiftwidth=4 tabstop=8 expandtab
953  */
954