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