171e5a3dff1264acb944a34bea00ea550a493e85
[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.7 2004-10-01 11:45:48 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=\"");
275             marc_cdata(mt, tag, strlen(tag), wr);
276             wrbuf_printf(wr, "\"");
277             break;
278         case YAZ_MARC_OAIMARC:
279             if (identifier_flag)
280                 wrbuf_printf (wr, "  <varfield id=\"");
281             else
282                 wrbuf_printf (wr, "  <fixfield id=\"");
283             marc_cdata(mt, tag, strlen(tag), wr);
284             wrbuf_printf(wr, "\"");
285             break;
286         case YAZ_MARC_MARCXML:
287             if (identifier_flag)
288                 wrbuf_printf (wr, "  <datafield tag=\"");
289             else
290                 wrbuf_printf (wr, "  <controlfield tag=\"");
291             marc_cdata(mt, tag, strlen(tag), wr);
292             wrbuf_printf(wr, "\"");
293         }
294         
295         if (identifier_flag)
296         {
297             for (j = 0; j<indicator_length; j++, i++)
298             {
299                 switch(mt->xml)
300                 {
301                 case YAZ_MARC_ISO2709:
302                     wrbuf_putc(wr, buf[i]);
303                     break;
304                 case YAZ_MARC_LINE:
305                     if (mt->debug)
306                         wrbuf_puts (wr, " Ind: ");
307                     wrbuf_putc(wr, buf[i]);
308                     break;
309                 case YAZ_MARC_SIMPLEXML:
310                     wrbuf_printf(wr, " Indicator%d=\"", j+1);
311                     marc_cdata(mt, buf+i, 1, wr);
312                     wrbuf_printf(wr, "\"");
313                     break;
314                 case YAZ_MARC_OAIMARC:
315                     wrbuf_printf(wr, " i%d=\"", j+1);
316                     marc_cdata(mt, buf+i, 1, wr);
317                     wrbuf_printf(wr, "\"");
318                     break;
319                 case YAZ_MARC_MARCXML:
320                     wrbuf_printf(wr, " ind%d=\"", j+1);
321                     marc_cdata(mt, buf+i, 1, wr);
322                     wrbuf_printf(wr, "\"");
323                 }
324             }
325         }
326         if (mt->xml == YAZ_MARC_SIMPLEXML || mt->xml == YAZ_MARC_MARCXML
327             || mt->xml == YAZ_MARC_OAIMARC)
328         {
329             wrbuf_puts (wr, ">");
330             if (identifier_flag)
331                 wrbuf_puts (wr, "\n");
332         }
333         if (mt->xml == YAZ_MARC_LINE)
334         {
335             if (mt->debug)
336                 wrbuf_puts (wr, " Fields: ");
337         }
338         if (identifier_flag)
339         {
340             while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset)
341             {
342                 int i0;
343                 i++;
344                 switch(mt->xml)
345                 {
346                 case YAZ_MARC_ISO2709:
347                     --i;
348                     wrbuf_iconv_write(wr, mt->iconv_cd, 
349                                       buf+i, identifier_length);
350                     i += identifier_length;
351                     break;
352                 case YAZ_MARC_LINE: 
353                     wrbuf_puts (wr, " $"); 
354                     marc_cdata(mt, buf+i, identifier_length-1, wr);
355                     i = i+identifier_length-1;
356                     wrbuf_putc (wr, ' ');
357                     break;
358                 case YAZ_MARC_SIMPLEXML:
359                     wrbuf_puts (wr, "  <subfield code=\"");
360                     marc_cdata(mt, buf+i, identifier_length-1, wr);
361                     i = i+identifier_length-1;
362                     wrbuf_puts (wr, "\">");
363                     break;
364                 case YAZ_MARC_OAIMARC:
365                     wrbuf_puts (wr, "    <subfield label=\"");
366                     marc_cdata(mt, buf+i, identifier_length-1, wr);
367                     i = i+identifier_length-1;
368                     wrbuf_puts (wr, "\">");
369                     break;
370                 case YAZ_MARC_MARCXML:
371                     wrbuf_puts (wr, "    <subfield code=\"");
372                     marc_cdata(mt, buf+i, identifier_length-1, wr);
373                     i = i+identifier_length-1;
374                     wrbuf_puts (wr, "\">");
375                     break;
376                 }
377                 i0 = i;
378                 while (buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
379                        buf[i] != ISO2709_FS && i < end_offset)
380                     i++;
381                 marc_cdata(mt, buf + i0, i - i0, wr);
382
383                 if (mt->xml == YAZ_MARC_ISO2709 && buf[i] != ISO2709_IDFS)
384                     marc_cdata(mt, buf + i, 1, wr);
385
386                 if (mt->xml == YAZ_MARC_SIMPLEXML || 
387                     mt->xml == YAZ_MARC_MARCXML ||
388                     mt->xml == YAZ_MARC_OAIMARC)
389                     wrbuf_puts (wr, "</subfield>\n");
390             }
391         }
392         else
393         {
394             int i0 = i;
395             while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset)
396                 i++;
397             marc_cdata(mt, buf + i0, i - i0, wr);
398             if (mt->xml == YAZ_MARC_ISO2709)
399                 marc_cdata(mt, buf + i, 1, wr);
400         }
401         if (mt->xml == YAZ_MARC_LINE)
402             wrbuf_putc (wr, '\n');
403         if (i < end_offset)
404             wrbuf_puts (wr, "  <!-- separator but not at end of field -->\n");
405         if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
406             wrbuf_puts (wr, "  <!-- no separator at end of field -->\n");
407         switch(mt->xml)
408         {
409         case YAZ_MARC_SIMPLEXML:
410             wrbuf_puts (wr, "</field>\n");
411             break;
412         case YAZ_MARC_OAIMARC:
413             if (identifier_flag)
414                 wrbuf_puts (wr, "  </varfield>\n");
415             else
416                 wrbuf_puts (wr, "  </fixfield>\n");
417             break;
418         case YAZ_MARC_MARCXML:
419             if (identifier_flag)
420                 wrbuf_puts (wr, "  </datafield>\n");
421             else
422                 wrbuf_puts (wr, "  </controlfield>\n");
423             break;
424         }
425     }
426     switch (mt->xml)
427     {
428     case YAZ_MARC_LINE:
429         wrbuf_puts (wr, "");
430         break;
431     case YAZ_MARC_SIMPLEXML:
432         wrbuf_puts (wr, "</iso2709>\n");
433         break;
434     case YAZ_MARC_OAIMARC:
435         wrbuf_puts (wr, "</oai_marc>\n");
436         break;
437     case YAZ_MARC_MARCXML:
438         wrbuf_puts (wr, "</record>\n");
439         break;
440     case YAZ_MARC_ISO2709:
441         wrbuf_putc (wr, ISO2709_RS);
442         break;
443     }
444     return record_length;
445 }
446
447 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
448                          char **result, int *rsize)
449 {
450     int r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
451     if (r > 0)
452     {
453         if (result)
454             *result = wrbuf_buf(mt->m_wr);
455         if (rsize)
456             *rsize = wrbuf_len(mt->m_wr);
457     }
458     return r;
459 }
460
461 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
462 {
463     if (mt)
464         mt->xml = xmlmode;
465 }
466
467 void yaz_marc_debug(yaz_marc_t mt, int level)
468 {
469     if (mt)
470         mt->debug = level;
471 }
472
473 void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
474 {
475     mt->iconv_cd = cd;
476 }
477
478 /* depricated */
479 int yaz_marc_decode(const char *buf, WRBUF wr, int debug, int bsize, int xml)
480 {
481     yaz_marc_t mt = yaz_marc_create();
482     int r;
483
484     mt->debug = debug;
485     mt->xml = xml;
486     r = yaz_marc_decode_wrbuf(mt, buf, bsize, wr);
487     yaz_marc_destroy(mt);
488     return r;
489 }
490
491 /* depricated */
492 int marc_display_wrbuf (const char *buf, WRBUF wr, int debug, int bsize)
493 {
494     return yaz_marc_decode(buf, wr, debug, bsize, 0);
495 }
496
497 /* depricated */
498 int marc_display_exl (const char *buf, FILE *outf, int debug, int bsize)
499 {
500     yaz_marc_t mt = yaz_marc_create();
501     int r;
502
503     mt->debug = debug;
504     r = yaz_marc_decode_wrbuf (mt, buf, bsize, mt->m_wr);
505     if (!outf)
506         outf = stdout;
507     if (r > 0)
508         fwrite (wrbuf_buf(mt->m_wr), 1, wrbuf_len(mt->m_wr), outf);
509     yaz_marc_destroy(mt);
510     return r;
511 }
512
513 /* depricated */
514 int marc_display_ex (const char *buf, FILE *outf, int debug)
515 {
516     return marc_display_exl (buf, outf, debug, -1);
517 }
518
519 /* depricated */
520 int marc_display (const char *buf, FILE *outf)
521 {
522     return marc_display_ex (buf, outf, 0);
523 }
524