Warn for bad base address in MARC parser
[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.3 2003-12-17 12:28:07 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     if (mt->debug && base_address != entry_p+1)
216     {
217         wrbuf_printf (wr,"  <!-- base address not at end of directory "
218                       "base=%d end=%d -->\n", base_address, entry_p+1);
219     }
220     base_address = entry_p+1;
221
222     if (mt->xml == YAZ_MARC_ISO2709)
223     {
224         WRBUF wr_head = wrbuf_alloc();
225         WRBUF wr_dir = wrbuf_alloc();
226         WRBUF wr_tmp = wrbuf_alloc();
227
228         int data_p = 0;
229         /* second pass. create directory for ISO2709 output */
230         for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
231         {
232             int data_length, data_offset, end_offset;
233             int i, sz1, sz2;
234             
235             wrbuf_write(wr_dir, buf+entry_p, 3);
236             entry_p += 3;
237             
238             data_length = atoi_n (buf+entry_p, length_data_entry);
239             entry_p += length_data_entry;
240             data_offset = atoi_n (buf+entry_p, length_starting);
241             entry_p += length_starting;
242             i = data_offset + base_address;
243             end_offset = i+data_length-1;
244             
245             while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS &&
246                    i < end_offset)
247                 i++;
248             sz1 = 1+i - (data_offset + base_address);
249             if (mt->iconv_cd)
250             {
251                 sz2 = wrbuf_iconv_write(wr_tmp, mt->iconv_cd,
252                                         buf + data_offset+base_address, sz1);
253                 wrbuf_rewind(wr_tmp);
254             }
255             else
256                 sz2 = sz1;
257             wrbuf_printf(wr_dir, "%0*d", length_data_entry, sz2);
258             wrbuf_printf(wr_dir, "%0*d", length_starting, data_p);
259             data_p += sz2;
260         }
261         wrbuf_putc(wr_dir, ISO2709_FS);
262         wrbuf_printf(wr_head, "%05d", data_p+1 + base_address);
263         wrbuf_write(wr_head, buf+5, 7);
264         wrbuf_printf(wr_head, "%05d", base_address);
265         wrbuf_write(wr_head, buf+17, 7);
266
267         wrbuf_write(wr, wrbuf_buf(wr_head), 24);
268         wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
269         wrbuf_free(wr_head, 1);
270         wrbuf_free(wr_dir, 1);
271         wrbuf_free(wr_tmp, 1);
272     }
273     /* third pass. create data output */
274     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
275     {
276         int data_length;
277         int data_offset;
278         int end_offset;
279         int i, j;
280         char tag[4];
281         int identifier_flag = 1;
282
283         memcpy (tag, buf+entry_p, 3);
284         entry_p += 3;
285         tag[3] = '\0';
286         data_length = atoi_n (buf+entry_p, length_data_entry);
287         entry_p += length_data_entry;
288         data_offset = atoi_n (buf+entry_p, length_starting);
289         entry_p += length_starting;
290         i = data_offset + base_address;
291         end_offset = i+data_length-1;
292         
293         if (indicator_length < 4 && indicator_length > 0)
294         {
295             if (buf[i + indicator_length] != ISO2709_IDFS)
296                 identifier_flag = 0;
297         }
298         else if (!memcmp (tag, "00", 2))
299             identifier_flag = 0;
300         
301         switch(mt->xml)
302         {
303         case YAZ_MARC_LINE:
304             if (mt->debug)
305                 wrbuf_puts (wr, "Tag: ");
306             wrbuf_puts (wr, tag);
307             wrbuf_puts (wr, " ");
308             break;
309         case YAZ_MARC_SIMPLEXML:
310             wrbuf_printf (wr, "<field tag=\"%s\"", tag);
311             break;
312         case YAZ_MARC_OAIMARC:
313             if (identifier_flag)
314                 wrbuf_printf (wr, "  <varfield id=\"%s\"", tag);
315             else
316                 wrbuf_printf (wr, "  <fixfield id=\"%s\"", tag);
317             break;
318         case YAZ_MARC_MARCXML:
319             if (identifier_flag)
320                 wrbuf_printf (wr, "  <datafield tag=\"%s\"", tag);
321             else
322                 wrbuf_printf (wr, "  <controlfield tag=\"%s\"", tag);
323         }
324         
325         if (identifier_flag)
326         {
327             for (j = 0; j<indicator_length; j++, i++)
328             {
329                 switch(mt->xml)
330                 {
331                 case YAZ_MARC_ISO2709:
332                     wrbuf_putc(wr, buf[i]);
333                     break;
334                 case YAZ_MARC_LINE:
335                     if (mt->debug)
336                         wrbuf_puts (wr, " Ind: ");
337                     wrbuf_putc(wr, buf[i]);
338                     break;
339                 case YAZ_MARC_SIMPLEXML:
340                     wrbuf_printf(wr, " Indicator%d=\"%c\"", j+1, buf[i]);
341                     break;
342                 case YAZ_MARC_OAIMARC:
343                     wrbuf_printf(wr, " i%d=\"%c\"", j+1, buf[i]);
344                     break;
345                 case YAZ_MARC_MARCXML:
346                     wrbuf_printf(wr, " ind%d=\"%c\"", j+1, buf[i]);
347                 }
348             }
349         }
350         if (mt->xml == YAZ_MARC_SIMPLEXML || mt->xml == YAZ_MARC_MARCXML
351             || mt->xml == YAZ_MARC_OAIMARC)
352         {
353             wrbuf_puts (wr, ">");
354             if (identifier_flag)
355                 wrbuf_puts (wr, "\n");
356         }
357         if (mt->xml == YAZ_MARC_LINE)
358         {
359             if (mt->debug)
360                 wrbuf_puts (wr, " Fields: ");
361         }
362         if (identifier_flag)
363         {
364             while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset)
365             {
366                 int i0;
367                 i++;
368                 switch(mt->xml)
369                 {
370                 case YAZ_MARC_ISO2709:
371                     --i;
372                     wrbuf_iconv_write(wr, mt->iconv_cd, 
373                                       buf+i, identifier_length);
374                     i += identifier_length;
375                     break;
376                 case YAZ_MARC_LINE: 
377                     wrbuf_puts (wr, " $"); 
378                     for (j = 1; j<identifier_length; j++, i++)
379                         wrbuf_putc (wr, buf[i]);
380                     wrbuf_putc (wr, ' ');
381                     break;
382                 case YAZ_MARC_SIMPLEXML:
383                     wrbuf_puts (wr, "  <subfield code=\"");
384                     for (j = 1; j<identifier_length; j++, i++)
385                         wrbuf_putc (wr, buf[i]);
386                     wrbuf_puts (wr, "\">");
387                     break;
388                 case YAZ_MARC_OAIMARC:
389                     wrbuf_puts (wr, "    <subfield label=\"");
390                     for (j = 1; j<identifier_length; j++, i++)
391                         wrbuf_putc (wr, buf[i]);
392                     wrbuf_puts (wr, "\">");
393                     break;
394                 case YAZ_MARC_MARCXML:
395                     wrbuf_puts (wr, "    <subfield code=\"");
396                     for (j = 1; j<identifier_length; j++, i++)
397                         wrbuf_putc (wr, buf[i]);
398                     wrbuf_puts (wr, "\">");
399                     break;
400                 }
401                 i0 = i;
402                 while (buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
403                        buf[i] != ISO2709_FS && i < end_offset)
404                     i++;
405                 marc_cdata(mt, buf + i0, i - i0, wr);
406
407                 if (mt->xml == YAZ_MARC_ISO2709 && buf[i] != ISO2709_IDFS)
408                     marc_cdata(mt, buf + i, 1, wr);
409
410                 if (mt->xml == YAZ_MARC_SIMPLEXML || 
411                     mt->xml == YAZ_MARC_MARCXML ||
412                     mt->xml == YAZ_MARC_OAIMARC)
413                     wrbuf_puts (wr, "</subfield>\n");
414             }
415         }
416         else
417         {
418             int i0 = i;
419             while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset)
420                 i++;
421             marc_cdata(mt, buf + i0, i - i0, wr);
422             if (mt->xml == YAZ_MARC_ISO2709)
423                 marc_cdata(mt, buf + i, 1, wr);
424         }
425         if (mt->xml == YAZ_MARC_LINE)
426             wrbuf_putc (wr, '\n');
427         if (i < end_offset)
428             wrbuf_puts (wr, "  <!-- separator but not at end of field -->\n");
429         if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
430             wrbuf_puts (wr, "  <!-- no separator at end of field -->\n");
431         switch(mt->xml)
432         {
433         case YAZ_MARC_SIMPLEXML:
434             wrbuf_puts (wr, "</field>\n");
435             break;
436         case YAZ_MARC_OAIMARC:
437             if (identifier_flag)
438                 wrbuf_puts (wr, "  </varfield>\n");
439             else
440                 wrbuf_puts (wr, "  </fixfield>\n");
441             break;
442         case YAZ_MARC_MARCXML:
443             if (identifier_flag)
444                 wrbuf_puts (wr, "  </datafield>\n");
445             else
446                 wrbuf_puts (wr, "  </controlfield>\n");
447             break;
448         }
449     }
450     switch (mt->xml)
451     {
452     case YAZ_MARC_LINE:
453         wrbuf_puts (wr, "");
454         break;
455     case YAZ_MARC_SIMPLEXML:
456         wrbuf_puts (wr, "</iso2709>\n");
457         break;
458     case YAZ_MARC_OAIMARC:
459         wrbuf_puts (wr, "</oai_marc>\n");
460         break;
461     case YAZ_MARC_MARCXML:
462         wrbuf_puts (wr, "</record>\n");
463         break;
464     case YAZ_MARC_ISO2709:
465         wrbuf_putc (wr, ISO2709_RS);
466         break;
467     }
468     return record_length;
469 }
470
471 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
472                          char **result, int *rsize)
473 {
474     int r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
475     if (r > 0)
476     {
477         if (result)
478             *result = wrbuf_buf(mt->m_wr);
479         if (rsize)
480             *rsize = wrbuf_len(mt->m_wr);
481     }
482     return r;
483 }
484
485 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
486 {
487     if (mt)
488         mt->xml = xmlmode;
489 }
490
491 void yaz_marc_debug(yaz_marc_t mt, int level)
492 {
493     if (mt)
494         mt->debug = level;
495 }
496
497 void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
498 {
499     mt->iconv_cd = cd;
500 }
501
502 /* depricated */
503 int yaz_marc_decode(const char *buf, WRBUF wr, int debug, int bsize, int xml)
504 {
505     yaz_marc_t mt = yaz_marc_create();
506     int r;
507
508     mt->debug = debug;
509     mt->xml = xml;
510     r = yaz_marc_decode_wrbuf(mt, buf, bsize, wr);
511     yaz_marc_destroy(mt);
512     return r;
513 }
514
515 /* depricated */
516 int marc_display_wrbuf (const char *buf, WRBUF wr, int debug, int bsize)
517 {
518     return yaz_marc_decode(buf, wr, debug, bsize, 0);
519 }
520
521 /* depricated */
522 int marc_display_exl (const char *buf, FILE *outf, int debug, int bsize)
523 {
524     yaz_marc_t mt = yaz_marc_create();
525     int r;
526
527     mt->debug = debug;
528     r = yaz_marc_decode_wrbuf (mt, buf, bsize, mt->m_wr);
529     if (!outf)
530         outf = stdout;
531     if (r > 0)
532         fwrite (wrbuf_buf(mt->m_wr), 1, wrbuf_len(mt->m_wr), outf);
533     yaz_marc_destroy(mt);
534     return r;
535 }
536
537 /* depricated */
538 int marc_display_ex (const char *buf, FILE *outf, int debug)
539 {
540     return marc_display_exl (buf, outf, debug, -1);
541 }
542
543 /* depricated */
544 int marc_display (const char *buf, FILE *outf)
545 {
546     return marc_display_ex (buf, outf, 0);
547 }
548