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