Extend MARC-8 to handle ESC-G0 EACC. Fix conversion order for MARCXML
[yaz-moved-to-github.git] / src / marcdisp.c
1 /*
2  * Copyright (c) 1995-2004, Index Data
3  * See the file LICENSE for details.
4  *
5  * $Id: marcdisp.c,v 1.4 2004-03-15 21:39:06 adam Exp $
6  */
7
8 #if HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <stdio.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <yaz/marcdisp.h>
16 #include <yaz/wrbuf.h>
17 #include <yaz/yaz-util.h>
18
19 struct yaz_marc_t_ {
20     WRBUF m_wr;
21     int xml;
22     int debug;
23     yaz_iconv_t iconv_cd;
24 };
25
26 yaz_marc_t yaz_marc_create(void)
27 {
28     yaz_marc_t mt = (yaz_marc_t) xmalloc(sizeof(*mt));
29     mt->xml = YAZ_MARC_LINE;
30     mt->debug = 0;
31     mt->m_wr = wrbuf_alloc();
32     mt->iconv_cd = 0;
33     return mt;
34 }
35
36 void yaz_marc_destroy(yaz_marc_t mt)
37 {
38     if (!mt)
39         return ;
40     wrbuf_free (mt->m_wr, 1);
41     xfree (mt);
42 }
43
44 static void marc_cdata (yaz_marc_t mt, const char *buf, size_t len, WRBUF wr)
45 {
46     size_t i;
47     if (mt->xml == YAZ_MARC_ISO2709)
48         wrbuf_iconv_write(wr, mt->iconv_cd, buf, len);
49     else if (mt->xml == YAZ_MARC_LINE)
50         wrbuf_iconv_write(wr, mt->iconv_cd, buf, len);
51     else
52         wrbuf_iconv_write_cdata(wr, mt->iconv_cd, buf, len);
53 }
54
55 int yaz_marc_decode_wrbuf (yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
56 {
57     int entry_p;
58     int record_length;
59     int indicator_length;
60     int identifier_length;
61     int base_address;
62     int length_data_entry;
63     int length_starting;
64     int length_implementation;
65
66     wrbuf_rewind(wr);
67
68     record_length = atoi_n (buf, 5);
69     if (record_length < 25)
70     {
71         if (mt->debug)
72         {
73             char str[40];
74             
75             sprintf (str, "Record length %d - aborting\n", record_length);
76             wrbuf_puts (wr, str);
77         }
78         return -1;
79     }
80     /* ballout if bsize is known and record_length is less than that */
81     if (bsize != -1 && record_length > bsize)
82         return -1;
83     if (isdigit(buf[10]))
84         indicator_length = atoi_n (buf+10, 1);
85     else
86         indicator_length = 2;
87     if (isdigit(buf[11]))
88         identifier_length = atoi_n (buf+11, 1);
89     else
90         identifier_length = 2;
91     base_address = atoi_n (buf+12, 5);
92
93     length_data_entry = atoi_n (buf+20, 1);
94     length_starting = atoi_n (buf+21, 1);
95     length_implementation = atoi_n (buf+22, 1);
96
97     if (mt->xml != YAZ_MARC_LINE)
98     {
99         char str[80];
100         int i;
101         switch(mt->xml)
102         {
103         case YAZ_MARC_ISO2709:
104             break;
105         case YAZ_MARC_SIMPLEXML:
106             wrbuf_puts (wr, "<iso2709\n");
107             sprintf (str, " RecordStatus=\"%c\"\n", buf[5]);
108             wrbuf_puts (wr, str);
109             sprintf (str, " TypeOfRecord=\"%c\"\n", buf[6]);
110             wrbuf_puts (wr, str);
111             for (i = 1; i<=19; i++)
112             {
113                 sprintf (str, " ImplDefined%d=\"%c\"\n", i, buf[6+i]);
114                 wrbuf_puts (wr, str);
115             }
116             wrbuf_puts (wr, ">\n");
117             break;
118         case YAZ_MARC_OAIMARC:
119             wrbuf_puts(
120                 wr,
121                 "<oai_marc xmlns=\"http://www.openarchives.org/OIA/oai_marc\""
122                 "\n"
123                 " xmlns:xsi=\"http://www.w3.org/2000/10/XMLSchema-instance\""
124                 "\n"
125                 " xsi:schemaLocation=\"http://www.openarchives.org/OAI/oai_marc.xsd\""
126                 "\n"
127                 );
128             
129             sprintf (str, " status=\"%c\" type=\"%c\" catForm=\"%c\">\n",
130                      buf[5], buf[6], buf[7]);
131             wrbuf_puts (wr, str);
132             break;
133         case YAZ_MARC_MARCXML:
134             wrbuf_printf(
135                 wr,
136                 "<record xmlns=\"http://www.loc.gov/MARC21/slim\">\n"
137                 "  <leader>");
138             marc_cdata(mt, buf, 24, wr);
139             wrbuf_printf(wr, "</leader>\n");
140             break;
141         }
142     }
143     if (mt->debug)
144     {
145         char str[40];
146
147         if (mt->xml)
148             wrbuf_puts (wr, "<!--\n");
149         sprintf (str, "Record length         %5d\n", record_length);
150         wrbuf_puts (wr, str);
151         sprintf (str, "Indicator length      %5d\n", indicator_length);
152         wrbuf_puts (wr, str);
153         sprintf (str, "Identifier length     %5d\n", identifier_length);
154         wrbuf_puts (wr, str);
155         sprintf (str, "Base address          %5d\n", base_address);
156         wrbuf_puts (wr, str);
157         sprintf (str, "Length data entry     %5d\n", length_data_entry);
158         wrbuf_puts (wr, str);
159         sprintf (str, "Length starting       %5d\n", length_starting);
160         wrbuf_puts (wr, str);
161         sprintf (str, "Length implementation %5d\n", length_implementation);
162         wrbuf_puts (wr, str);
163         if (mt->xml)
164             wrbuf_puts (wr, "-->\n");
165     }
166
167     /* first pass. determine length of directory & base of data */
168     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
169     {
170         entry_p += 3+length_data_entry+length_starting;
171         if (entry_p >= record_length)
172             return -1;
173     }
174     if (mt->debug && base_address != entry_p+1)
175     {
176         wrbuf_printf (wr,"  <!-- base address not at end of directory "
177                       "base=%d end=%d -->\n", base_address, entry_p+1);
178     }
179     base_address = entry_p+1;
180
181     if (mt->xml == YAZ_MARC_ISO2709)
182     {
183         WRBUF wr_head = wrbuf_alloc();
184         WRBUF wr_dir = wrbuf_alloc();
185         WRBUF wr_tmp = wrbuf_alloc();
186
187         int data_p = 0;
188         /* second pass. create directory for ISO2709 output */
189         for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
190         {
191             int data_length, data_offset, end_offset;
192             int i, sz1, sz2;
193             
194             wrbuf_write(wr_dir, buf+entry_p, 3);
195             entry_p += 3;
196             
197             data_length = atoi_n (buf+entry_p, length_data_entry);
198             entry_p += length_data_entry;
199             data_offset = atoi_n (buf+entry_p, length_starting);
200             entry_p += length_starting;
201             i = data_offset + base_address;
202             end_offset = i+data_length-1;
203             
204             while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS &&
205                    i < end_offset)
206                 i++;
207             sz1 = 1+i - (data_offset + base_address);
208             if (mt->iconv_cd)
209             {
210                 sz2 = wrbuf_iconv_write(wr_tmp, mt->iconv_cd,
211                                         buf + data_offset+base_address, sz1);
212                 wrbuf_rewind(wr_tmp);
213             }
214             else
215                 sz2 = sz1;
216             wrbuf_printf(wr_dir, "%0*d", length_data_entry, sz2);
217             wrbuf_printf(wr_dir, "%0*d", length_starting, data_p);
218             data_p += sz2;
219         }
220         wrbuf_putc(wr_dir, ISO2709_FS);
221         wrbuf_printf(wr_head, "%05d", data_p+1 + base_address);
222         wrbuf_write(wr_head, buf+5, 7);
223         wrbuf_printf(wr_head, "%05d", base_address);
224         wrbuf_write(wr_head, buf+17, 7);
225
226         wrbuf_write(wr, wrbuf_buf(wr_head), 24);
227         wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
228         wrbuf_free(wr_head, 1);
229         wrbuf_free(wr_dir, 1);
230         wrbuf_free(wr_tmp, 1);
231     }
232     /* third pass. create data output */
233     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
234     {
235         int data_length;
236         int data_offset;
237         int end_offset;
238         int i, j;
239         char tag[4];
240         int identifier_flag = 1;
241
242         memcpy (tag, buf+entry_p, 3);
243         entry_p += 3;
244         tag[3] = '\0';
245         data_length = atoi_n (buf+entry_p, length_data_entry);
246         entry_p += length_data_entry;
247         data_offset = atoi_n (buf+entry_p, length_starting);
248         entry_p += length_starting;
249         i = data_offset + base_address;
250         end_offset = i+data_length-1;
251         
252         if (indicator_length < 4 && indicator_length > 0)
253         {
254             if (buf[i + indicator_length] != ISO2709_IDFS)
255                 identifier_flag = 0;
256         }
257         else if (!memcmp (tag, "00", 2))
258             identifier_flag = 0;
259         
260         switch(mt->xml)
261         {
262         case YAZ_MARC_LINE:
263             if (mt->debug)
264                 wrbuf_puts (wr, "Tag: ");
265             wrbuf_puts (wr, tag);
266             wrbuf_puts (wr, " ");
267             break;
268         case YAZ_MARC_SIMPLEXML:
269             wrbuf_printf (wr, "<field tag=\"%s\"", tag);
270             break;
271         case YAZ_MARC_OAIMARC:
272             if (identifier_flag)
273                 wrbuf_printf (wr, "  <varfield id=\"%s\"", tag);
274             else
275                 wrbuf_printf (wr, "  <fixfield id=\"%s\"", tag);
276             break;
277         case YAZ_MARC_MARCXML:
278             if (identifier_flag)
279                 wrbuf_printf (wr, "  <datafield tag=\"%s\"", tag);
280             else
281                 wrbuf_printf (wr, "  <controlfield tag=\"%s\"", tag);
282         }
283         
284         if (identifier_flag)
285         {
286             for (j = 0; j<indicator_length; j++, i++)
287             {
288                 switch(mt->xml)
289                 {
290                 case YAZ_MARC_ISO2709:
291                     wrbuf_putc(wr, buf[i]);
292                     break;
293                 case YAZ_MARC_LINE:
294                     if (mt->debug)
295                         wrbuf_puts (wr, " Ind: ");
296                     wrbuf_putc(wr, buf[i]);
297                     break;
298                 case YAZ_MARC_SIMPLEXML:
299                     wrbuf_printf(wr, " Indicator%d=\"%c\"", j+1, buf[i]);
300                     break;
301                 case YAZ_MARC_OAIMARC:
302                     wrbuf_printf(wr, " i%d=\"%c\"", j+1, buf[i]);
303                     break;
304                 case YAZ_MARC_MARCXML:
305                     wrbuf_printf(wr, " ind%d=\"%c\"", j+1, buf[i]);
306                 }
307             }
308         }
309         if (mt->xml == YAZ_MARC_SIMPLEXML || mt->xml == YAZ_MARC_MARCXML
310             || mt->xml == YAZ_MARC_OAIMARC)
311         {
312             wrbuf_puts (wr, ">");
313             if (identifier_flag)
314                 wrbuf_puts (wr, "\n");
315         }
316         if (mt->xml == YAZ_MARC_LINE)
317         {
318             if (mt->debug)
319                 wrbuf_puts (wr, " Fields: ");
320         }
321         if (identifier_flag)
322         {
323             while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset)
324             {
325                 int i0;
326                 i++;
327                 switch(mt->xml)
328                 {
329                 case YAZ_MARC_ISO2709:
330                     --i;
331                     wrbuf_iconv_write(wr, mt->iconv_cd, 
332                                       buf+i, identifier_length);
333                     i += identifier_length;
334                     break;
335                 case YAZ_MARC_LINE: 
336                     wrbuf_puts (wr, " $"); 
337                     for (j = 1; j<identifier_length; j++, i++)
338                         wrbuf_putc (wr, buf[i]);
339                     wrbuf_putc (wr, ' ');
340                     break;
341                 case YAZ_MARC_SIMPLEXML:
342                     wrbuf_puts (wr, "  <subfield code=\"");
343                     for (j = 1; j<identifier_length; j++, i++)
344                         wrbuf_putc (wr, buf[i]);
345                     wrbuf_puts (wr, "\">");
346                     break;
347                 case YAZ_MARC_OAIMARC:
348                     wrbuf_puts (wr, "    <subfield label=\"");
349                     for (j = 1; j<identifier_length; j++, i++)
350                         wrbuf_putc (wr, buf[i]);
351                     wrbuf_puts (wr, "\">");
352                     break;
353                 case YAZ_MARC_MARCXML:
354                     wrbuf_puts (wr, "    <subfield code=\"");
355                     for (j = 1; j<identifier_length; j++, i++)
356                         wrbuf_putc (wr, buf[i]);
357                     wrbuf_puts (wr, "\">");
358                     break;
359                 }
360                 i0 = i;
361                 while (buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
362                        buf[i] != ISO2709_FS && i < end_offset)
363                     i++;
364                 marc_cdata(mt, buf + i0, i - i0, wr);
365
366                 if (mt->xml == YAZ_MARC_ISO2709 && buf[i] != ISO2709_IDFS)
367                     marc_cdata(mt, buf + i, 1, wr);
368
369                 if (mt->xml == YAZ_MARC_SIMPLEXML || 
370                     mt->xml == YAZ_MARC_MARCXML ||
371                     mt->xml == YAZ_MARC_OAIMARC)
372                     wrbuf_puts (wr, "</subfield>\n");
373             }
374         }
375         else
376         {
377             int i0 = i;
378             while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset)
379                 i++;
380             marc_cdata(mt, buf + i0, i - i0, wr);
381             if (mt->xml == YAZ_MARC_ISO2709)
382                 marc_cdata(mt, buf + i, 1, wr);
383         }
384         if (mt->xml == YAZ_MARC_LINE)
385             wrbuf_putc (wr, '\n');
386         if (i < end_offset)
387             wrbuf_puts (wr, "  <!-- separator but not at end of field -->\n");
388         if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
389             wrbuf_puts (wr, "  <!-- no separator at end of field -->\n");
390         switch(mt->xml)
391         {
392         case YAZ_MARC_SIMPLEXML:
393             wrbuf_puts (wr, "</field>\n");
394             break;
395         case YAZ_MARC_OAIMARC:
396             if (identifier_flag)
397                 wrbuf_puts (wr, "  </varfield>\n");
398             else
399                 wrbuf_puts (wr, "  </fixfield>\n");
400             break;
401         case YAZ_MARC_MARCXML:
402             if (identifier_flag)
403                 wrbuf_puts (wr, "  </datafield>\n");
404             else
405                 wrbuf_puts (wr, "  </controlfield>\n");
406             break;
407         }
408     }
409     switch (mt->xml)
410     {
411     case YAZ_MARC_LINE:
412         wrbuf_puts (wr, "");
413         break;
414     case YAZ_MARC_SIMPLEXML:
415         wrbuf_puts (wr, "</iso2709>\n");
416         break;
417     case YAZ_MARC_OAIMARC:
418         wrbuf_puts (wr, "</oai_marc>\n");
419         break;
420     case YAZ_MARC_MARCXML:
421         wrbuf_puts (wr, "</record>\n");
422         break;
423     case YAZ_MARC_ISO2709:
424         wrbuf_putc (wr, ISO2709_RS);
425         break;
426     }
427     return record_length;
428 }
429
430 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
431                          char **result, int *rsize)
432 {
433     int r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
434     if (r > 0)
435     {
436         if (result)
437             *result = wrbuf_buf(mt->m_wr);
438         if (rsize)
439             *rsize = wrbuf_len(mt->m_wr);
440     }
441     return r;
442 }
443
444 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
445 {
446     if (mt)
447         mt->xml = xmlmode;
448 }
449
450 void yaz_marc_debug(yaz_marc_t mt, int level)
451 {
452     if (mt)
453         mt->debug = level;
454 }
455
456 void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
457 {
458     mt->iconv_cd = cd;
459 }
460
461 /* depricated */
462 int yaz_marc_decode(const char *buf, WRBUF wr, int debug, int bsize, int xml)
463 {
464     yaz_marc_t mt = yaz_marc_create();
465     int r;
466
467     mt->debug = debug;
468     mt->xml = xml;
469     r = yaz_marc_decode_wrbuf(mt, buf, bsize, wr);
470     yaz_marc_destroy(mt);
471     return r;
472 }
473
474 /* depricated */
475 int marc_display_wrbuf (const char *buf, WRBUF wr, int debug, int bsize)
476 {
477     return yaz_marc_decode(buf, wr, debug, bsize, 0);
478 }
479
480 /* depricated */
481 int marc_display_exl (const char *buf, FILE *outf, int debug, int bsize)
482 {
483     yaz_marc_t mt = yaz_marc_create();
484     int r;
485
486     mt->debug = debug;
487     r = yaz_marc_decode_wrbuf (mt, buf, bsize, mt->m_wr);
488     if (!outf)
489         outf = stdout;
490     if (r > 0)
491         fwrite (wrbuf_buf(mt->m_wr), 1, wrbuf_len(mt->m_wr), outf);
492     yaz_marc_destroy(mt);
493     return r;
494 }
495
496 /* depricated */
497 int marc_display_ex (const char *buf, FILE *outf, int debug)
498 {
499     return marc_display_exl (buf, outf, debug, -1);
500 }
501
502 /* depricated */
503 int marc_display (const char *buf, FILE *outf)
504 {
505     return marc_display_ex (buf, outf, 0);
506 }
507