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