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