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