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