Fix base address for MARC reader
[idzebra-moved-to-github.git] / recctrl / marcread.c
1 /* $Id: marcread.c,v 1.23 2003-12-10 23:30:15 adam Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003
3    Index Data Aps
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 #include <stdio.h>
24 #include <ctype.h>
25 #include <assert.h>
26
27 #include <yaz/log.h>
28 #include <yaz/yaz-util.h>
29 #include <yaz/marcdisp.h>
30 #include "grsread.h"
31 #include "marcomp.h"
32 #include "inline.h"
33
34 #define MARC_DEBUG 0
35 #define MARCOMP_DEBUG 0
36
37 static data1_node *grs_read_iso2709 (struct grs_read_info *p, int marc_xml)
38 {
39     char buf[100000];
40     int entry_p;
41     int record_length;
42     int indicator_length;
43     int identifier_length;
44     int base_address;
45     int length_data_entry;
46     int length_starting;
47     int length_implementation;
48     int read_bytes;
49 #if MARC_DEBUG
50     FILE *outf = stdout;
51 #endif
52     data1_node *res_root, *res_top;
53     char *absynName;
54     data1_marctab *marctab;
55
56     if ((*p->readf)(p->fh, buf, 5) != 5)
57         return NULL;
58     record_length = atoi_n (buf, 5);
59     if (record_length < 25)
60     {
61         logf (LOG_WARN, "MARC record length < 25, is %d", record_length);
62         return NULL;
63     }
64     /* read remaining part - attempt to read one byte furhter... */
65     read_bytes = (*p->readf)(p->fh, buf+5, record_length-4);
66     if (read_bytes < record_length-5)
67     {
68         logf (LOG_WARN, "Couldn't read whole MARC record");
69         return NULL;
70     }
71     if (read_bytes == record_length - 4)
72     {
73         off_t cur_offset = (*p->tellf)(p->fh);
74         if (cur_offset <= 27)
75             return NULL;
76         if (p->endf)
77             (*p->endf)(p->fh, cur_offset - 1);
78     }
79     absynName = p->type;
80     res_root = data1_mk_root (p->dh, p->mem, absynName);
81     if (!res_root)
82     {
83         yaz_log (LOG_WARN, "cannot read MARC without an abstract syntax");
84         return 0;
85     }
86     if (marc_xml)
87     {
88         data1_node *lead;
89         const char *attr[] = { "xmlns", "http://www.loc.gov/MARC21/slim", 0};
90                          
91         res_top = data1_mk_tag (p->dh, p->mem, "record", attr, res_root);
92
93         lead = data1_mk_tag(p->dh, p->mem, "leader", 0, res_top);
94         data1_mk_text_n(p->dh, p->mem, buf, 24, lead);
95     }
96     else
97         res_top = data1_mk_tag (p->dh, p->mem, absynName, 0, res_root);
98
99     if ((marctab = res_root->u.root.absyn->marc))
100     {
101         memcpy(marctab->leader, buf, 24);
102         memcpy(marctab->implementation_codes, buf+6, 4);
103         marctab->implementation_codes[4] = '\0';
104         memcpy(marctab->user_systems, buf+17, 3);
105         marctab->user_systems[3] = '\0';
106     }
107
108     if (marctab && marctab->force_indicator_length >= 0)
109         indicator_length = marctab->force_indicator_length;
110     else
111         indicator_length = atoi_n (buf+10, 1);
112     if (marctab && marctab->force_identifier_length >= 0)
113         identifier_length = marctab->force_identifier_length;
114     else
115         identifier_length = atoi_n (buf+11, 1);
116     base_address = atoi_n (buf+12, 5);
117
118     length_data_entry = atoi_n (buf+20, 1);
119     length_starting = atoi_n (buf+21, 1);
120     length_implementation = atoi_n (buf+22, 1);
121
122     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
123         entry_p += 3+length_data_entry+length_starting;
124     base_address = entry_p+1;
125     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
126     {
127         int data_length;
128         int data_offset;
129         int end_offset;
130         int i, i0;
131         char tag[4];
132         data1_node *res;
133         data1_node *parent = res_top;
134
135         memcpy (tag, buf+entry_p, 3);
136         entry_p += 3;
137         tag[3] = '\0';
138
139         if (marc_xml)
140             res = parent;
141         else
142             res = data1_mk_tag_n (p->dh, p->mem, tag, 3, 0 /* attr */, parent);
143         
144 #if MARC_DEBUG
145         fprintf (outf, "%s ", tag);
146 #endif
147         data_length = atoi_n (buf+entry_p, length_data_entry);
148         entry_p += length_data_entry;
149         data_offset = atoi_n (buf+entry_p, length_starting);
150         entry_p += length_starting;
151         i = data_offset + base_address;
152         end_offset = i+data_length-1;
153
154         if (memcmp (tag, "00", 2) && indicator_length)
155         {
156             /* generate indicator node */
157             if (marc_xml)
158             {
159                 const char *attr[10];
160                 int j;
161
162                 attr[0] = "tag";
163                 attr[1] = tag;
164                 attr[2] = 0;
165
166                 res = data1_mk_tag(p->dh, p->mem, "datafield", attr, res);
167
168                 for (j = 0; j<indicator_length; j++)
169                 {
170                     char str1[18], str2[2];
171                     sprintf (str1, "ind%d", j+1);
172                     str2[0] = buf[i+j];
173                     str2[1] = '\0';
174
175                     attr[0] = str1;
176                     attr[1] = str2;
177                     
178                     data1_tag_add_attr (p->dh, p->mem, res, attr);
179                 }
180             }
181             else
182             {
183 #if MARC_DEBUG
184                 int j;
185 #endif
186                 res = data1_mk_tag_n (p->dh, p->mem, 
187                                       buf+i, indicator_length, 0 /* attr */, res);
188 #if MARC_DEBUG
189                 for (j = 0; j<indicator_length; j++)
190                     fprintf (outf, "%c", buf[j+i]);
191 #endif
192             }
193             i += indicator_length;
194         } 
195         else
196         {
197             if (marc_xml)
198             {
199                 const char *attr[10];
200                 
201                 attr[0] = "tag";
202                 attr[1] = tag;
203                 attr[2] = 0;
204                 
205                 res = data1_mk_tag(p->dh, p->mem, "controlfield", attr, res);
206             }
207         }
208         parent = res;
209         /* traverse sub fields */
210         i0 = i;
211         while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset)
212         {
213             if (memcmp (tag, "00", 2) && identifier_length)
214             {
215                 data1_node *res;
216                 if (marc_xml)
217                 {
218                     int j;
219                     const char *attr[3];
220                     char code[10];
221                     
222                     for (j = 1; j<identifier_length && j < 9; j++)
223                         code[j-1] = buf[i+j];
224                     code[j-1] = 0;
225                     attr[0] = "code";
226                     attr[1] = code;
227                     attr[2] = 0;
228                     res = data1_mk_tag(p->dh, p->mem, "subfield",
229                                        attr, parent);
230                 }
231                 else
232                 {
233                     res = data1_mk_tag_n (p->dh, p->mem,
234                                            buf+i+1, identifier_length-1, 
235                                            0 /* attr */, parent);
236                 }
237 #if MARC_DEBUG
238                 fprintf (outf, " $"); 
239                 for (j = 1; j<identifier_length; j++)
240                     fprintf (outf, "%c", buf[j+i]);
241                 fprintf (outf, " ");
242 #endif
243                 i += identifier_length;
244                 i0 = i;
245                 while (buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
246                      buf[i] != ISO2709_FS && i < end_offset)
247                 {
248 #if MARC_DEBUG
249                     fprintf (outf, "%c", buf[i]);
250 #endif
251                     i++;
252                 }
253                 data1_mk_text_n (p->dh, p->mem, buf + i0, i - i0, res);
254                 i0 = i;
255             }
256             else
257             {
258 #if MARC_DEBUG
259                 fprintf (outf, "%c", buf[i]);
260 #endif
261                 i++;
262             }
263         }
264         if (i > i0)
265         {
266             data1_mk_text_n (p->dh, p->mem, buf + i0, i - i0, parent);
267         }
268 #if MARC_DEBUG
269         fprintf (outf, "\n");
270         if (i < end_offset)
271             fprintf (outf, "-- separator but not at end of field\n");
272         if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
273             fprintf (outf, "-- no separator at end of field\n");
274 #endif
275     }
276     return res_root;
277 }
278 /*
279  * Locate some data under this node. This routine should handle variants
280  * prettily.
281  */
282 static char *get_data(data1_node *n, int *len)
283 {
284     char *r;
285
286     while (n)
287     {
288         if (n->which == DATA1N_data)
289         {
290             int i;
291             *len = n->u.data.len;
292
293             for (i = 0; i<*len; i++)
294                 if (!d1_isspace(n->u.data.data[i]))
295                     break;
296             while (*len && d1_isspace(n->u.data.data[*len - 1]))
297                 (*len)--;
298             *len = *len - i;
299             if (*len > 0)
300                 return n->u.data.data + i;
301         }
302         if (n->which == DATA1N_tag)
303             n = n->child;
304         else if (n->which == DATA1N_data)
305             n = n->next;
306         else
307             break;      
308     }
309     r = "";
310     *len = strlen(r);
311     return r;
312 }
313 static data1_node *lookup_subfield(data1_node *node, const char *name)
314 {
315     data1_node *p;
316     
317     for (p=node; p; p=p->next)
318     {
319         if (!yaz_matchstr(p->u.tag.tag, name))
320             return p;
321     }
322     return 0;
323 }
324 static inline_subfield *lookup_inline_subfield(inline_subfield *pisf, const char *name)
325 {
326     inline_subfield *p;
327     
328     for (p=pisf; p; p=p->next)
329     {
330         if (!yaz_matchstr(p->name, name))
331             return p;
332     }
333     return 0;
334 }
335 static inline_subfield *cat_inline_subfield(mc_subfield *psf, char *buf, inline_subfield *pisf)
336 {
337     mc_subfield *p;
338     
339     for (p = psf; p && pisf; p = p->next)
340     {
341         if (p->which == MC_SF)
342         {
343             inline_subfield *found = lookup_inline_subfield(pisf, p->name);
344             
345             if (found)
346             {
347                 if (strcmp(p->prefix, "_"))
348                 {
349                     strcat(strcat(buf, " "), p->prefix);
350                 }
351                 if (p->interval.start == -1)
352                 {
353                     strcat(buf, found->data);
354                 }
355                 else
356                 {
357                     strncat(buf, found->data+p->interval.start,
358                         p->interval.end-p->interval.start+1);
359                 }
360                 if (strcmp(p->suffix, "_"))
361                 {
362                     strcat(strcat(buf, p->suffix), " ");
363                 }
364 #if MARCOMP_DEBUG
365                 logf(LOG_LOG, "cat_inline_subfield(): add subfield $%s", found->name);
366 #endif          
367                 pisf = found->next;
368             }
369         }
370         else if (p->which == MC_SFVARIANT)
371         {
372             inline_subfield *next;
373             
374             do {
375                 next = cat_inline_subfield(p->u.child, buf, pisf);
376                 if (next == pisf)
377                     break;
378                 pisf = next;
379             } while (pisf);
380         }
381         else if (p->which == MC_SFGROUP)
382         {
383             mc_subfield *pp;
384             int found;
385             
386             for (pp = p->u.child, found = 0; pp; pp = pp->next)
387             {
388                 if (!yaz_matchstr(pisf->name, p->name))
389                 {
390                     found = 1;
391                     break;
392                 }
393             }
394             if (found)
395             {
396                 strcat(buf, " (");
397                 pisf = cat_inline_subfield(p->u.child, buf, pisf);
398                 strcat(buf, ") ");
399             }
400         }
401     }
402     return pisf; 
403 }
404 static void cat_inline_field(mc_field *pf, char *buf, data1_node *subfield)
405 {    
406     if (!pf || !subfield)
407         return;
408
409     for (;subfield;)
410     {
411         int len;
412         inline_field *pif=NULL;
413         data1_node *psubf;
414         
415         if (yaz_matchstr(subfield->u.tag.tag, "1"))
416         {
417             subfield = subfield->next;
418             continue;
419         }
420         
421         psubf = subfield;
422         pif = inline_mk_field();
423         do
424         {
425             int i;
426             if ((i=inline_parse(pif, psubf->u.tag.tag, get_data(psubf, &len)))<0)
427             {
428                 logf(LOG_WARN, "inline subfield ($%s): parse error",
429                     psubf->u.tag.tag);
430                 inline_destroy_field(pif);
431                 return; 
432             }
433             psubf = psubf->next;
434         } while (psubf && yaz_matchstr(psubf->u.tag.tag, "1"));
435         
436         subfield = psubf;
437         
438         if (pif && !yaz_matchstr(pif->name, pf->name))
439         {
440             if (!pf->list && pif->list)
441             {
442                 strcat(buf, pif->list->data);
443             }
444             else
445             {
446                 int ind1, ind2;
447
448                 /*
449                     check indicators
450                 */
451
452                 ind1 = (pif->ind1[0] == ' ') ? '_':pif->ind1[0];
453                 ind2 = (pif->ind2[0] == ' ') ? '_':pif->ind2[0];
454     
455                 if (((pf->ind1[0] == '.') || (ind1 == pf->ind1[0])) &&
456                     ((pf->ind2[0] == '.') || (ind2 == pf->ind2[0])))
457                 {
458                     cat_inline_subfield(pf->list, buf, pif->list);
459                     
460                     /*
461                         add separator for inline fields
462                     */
463                     if (strlen(buf))
464                     {
465                         strcat(buf, "\n");
466                     }
467                 }
468                 else
469                 {
470                     logf(LOG_WARN, "In-line field %s missed -- indicators does not match", pif->name);
471                 }
472             }
473         }
474         inline_destroy_field(pif);
475     }
476 #if MARCOMP_DEBUG    
477     logf(LOG_LOG, "cat_inline_field(): got buffer {%s}", buf);
478 #endif
479 }
480 static data1_node *cat_subfield(mc_subfield *psf, char *buf, data1_node *subfield)
481 {
482     mc_subfield *p;
483     
484     for (p = psf; p && subfield; p = p->next)
485     {
486         if (p->which == MC_SF)
487         {
488             data1_node *found = lookup_subfield(subfield, p->name);
489             
490             if (found)
491             {
492                 int len;
493                 
494                 if (strcmp(p->prefix, "_"))
495                 {
496                     strcat(strcat(buf, " "), p->prefix);
497                 }
498                 
499                 if (p->u.in_line)
500                 {
501                     cat_inline_field(p->u.in_line, buf, found);
502                 }
503                 else if (p->interval.start == -1)
504                 {
505                     strcat(buf, get_data(found, &len));
506                 }
507                 else
508                 {
509                     strncat(buf, get_data(found, &len)+p->interval.start,
510                         p->interval.end-p->interval.start+1);
511                 }
512                 if (strcmp(p->suffix, "_"))
513                 {
514                     strcat(strcat(buf, p->suffix), " ");
515                 }
516 #if MARCOMP_DEBUG               
517                 logf(LOG_LOG, "cat_subfield(): add subfield $%s", found->u.tag.tag);
518 #endif          
519                 subfield = found->next;
520             }
521         }
522         else if (p->which == MC_SFVARIANT)
523         {
524             data1_node *next;
525             do {
526                 next = cat_subfield(p->u.child, buf, subfield);
527                 if (next == subfield)
528                     break;
529                 subfield = next;
530             } while (subfield);
531         }
532         else if (p->which == MC_SFGROUP)
533         {
534             mc_subfield *pp;
535             int found;
536             
537             for (pp = p->u.child, found = 0; pp; pp = pp->next)
538             {
539                 if (!yaz_matchstr(subfield->u.tag.tag, pp->name))
540                 {
541                     found = 1;
542                     break;
543                 }
544             }
545             if (found)
546             {
547                 strcat(buf, " (");
548                 subfield = cat_subfield(p->u.child, buf, subfield);
549                 strcat(buf, ") ");
550             }
551         }
552     }
553     return subfield;
554 }
555 static data1_node *cat_field(struct grs_read_info *p, mc_field *pf, char *buf, data1_node *field)
556 {
557     data1_node *subfield;
558     int ind1, ind2;
559     
560     if (!pf || !field)
561         return 0;
562
563     
564     if (yaz_matchstr(field->u.tag.tag, pf->name))
565         return field->next;
566
567     subfield = field->child;
568     
569     if (!subfield)
570         return field->next;
571
572     /*
573         check subfield without indicators
574     */
575     
576     if (!pf->list && subfield->which == DATA1N_data)
577     {
578         int len;
579         
580         if (pf->interval.start == -1)
581         {
582             strcat(buf, get_data(field, &len));
583         }
584         else
585         {
586             strncat(buf, get_data(field, &len)+pf->interval.start,
587                 pf->interval.end-pf->interval.start+1);
588         }
589 #if MARCOMP_DEBUG
590         logf(LOG_LOG, "cat_field(): got buffer {%s}", buf);
591 #endif
592         return field->next;
593     }
594     
595     /*
596         check indicators
597     */
598
599     ind1 = (subfield->u.tag.tag[0] == ' ') ? '_':subfield->u.tag.tag[0];
600     ind2 = (subfield->u.tag.tag[1] == ' ') ? '_':subfield->u.tag.tag[1];
601     
602     if (!(
603         ((pf->ind1[0] == '.') || (ind1 == pf->ind1[0])) &&
604         ((pf->ind2[0] == '.') || (ind2 == pf->ind2[0]))
605         ))
606     {
607 #if MARCOMP_DEBUG
608         logf(LOG_WARN, "Field %s missed -- does not match indicators", field->u.tag.tag);
609 #endif
610         return field->next;
611     }
612     
613     subfield = subfield->child;
614     
615     if (!subfield)
616         return field->next;
617
618     cat_subfield(pf->list, buf, subfield);
619
620 #if MARCOMP_DEBUG    
621     logf(LOG_LOG, "cat_field(): got buffer {%s}", buf);
622 #endif
623     
624     return field->next;    
625 }
626 static int is_empty(char *s)
627 {
628     char *p = s;
629     
630     for (p = s; *p; p++)
631     {
632         if (!isspace(*p))
633             return 0;
634     }
635     return 1;
636 }
637 static void parse_data1_tree(struct grs_read_info *p, const char *mc_stmnt, data1_node *root)
638 {
639     data1_marctab *marctab = root->u.root.absyn->marc;
640     data1_node *top = root->child;
641     data1_node *field;
642     mc_context *c;
643     mc_field *pf;
644     char buf[1000000];
645     
646     c = mc_mk_context(mc_stmnt+3);
647     
648     if (!c)
649         return;
650         
651     pf = mc_getfield(c);
652     
653     if (!pf)
654     {
655         mc_destroy_context(c);
656         return;
657     }
658 #if MARCOMP_DEBUG    
659     logf(LOG_LOG, "parse_data1_tree(): statement -{%s}", mc_stmnt);
660 #endif
661     if (!yaz_matchstr(pf->name, "ldr"))
662     {
663         data1_node *new;
664 #if MARCOMP_DEBUG
665         logf(LOG_LOG,"parse_data1_tree(): try LEADER from {%d} to {%d} positions",
666             pf->interval.start, pf->interval.end);
667 #endif  
668         new = data1_mk_tag_n(p->dh, p->mem, mc_stmnt, strlen(mc_stmnt), 0, top);
669         data1_mk_text_n(p->dh, p->mem, marctab->leader+pf->interval.start,
670             pf->interval.end-pf->interval.start+1, new);
671     }
672     else
673     {
674         field=top->child;
675         
676         while(field)
677         {
678             if (!yaz_matchstr(field->u.tag.tag, pf->name))
679             {
680                 data1_node *new;
681                 char *pb = buf;
682 #if MARCOMP_DEBUG               
683                 logf(LOG_LOG, "parse_data1_tree(): try field {%s}", field->u.tag.tag);
684 #endif          
685                 *buf = '\0';        
686                 field = cat_field(p, pf, buf, field);
687                 
688                 for (pb = strtok(pb, "\n"); pb; pb = strtok(NULL, "\n"))
689                 {
690                         if (!is_empty(pb))
691                         {
692                                 new = data1_mk_tag_n(p->dh, p->mem, mc_stmnt, strlen(mc_stmnt), 0, top);
693                                 data1_mk_text_n(p->dh, p->mem, pb, strlen(pb), new);
694                         }
695                 }
696             }
697             else
698             {
699                 field = field->next;
700             }
701         }
702     }
703     mc_destroy_field(pf);
704     mc_destroy_context(c);
705 }
706
707 data1_node *grs_read_marcxml(struct grs_read_info *p)
708 {
709     data1_node *root = grs_read_iso2709(p, 1);
710     data1_element *e;
711
712     if (!root)
713         return 0;
714         
715     for (e=root->u.root.absyn->main_elements; e; e=e->next)
716     {
717         data1_tag *tag = e->tag;
718         
719         if (tag && tag->which == DATA1T_string &&
720             !yaz_matchstr(tag->value.string, "mc?"))
721                 parse_data1_tree(p, tag->value.string, root);
722     }
723     return root;
724 }
725
726
727 data1_node *grs_read_marc(struct grs_read_info *p)
728 {
729     data1_node *root = grs_read_iso2709(p, 0);
730     data1_element *e;
731
732     if (!root)
733         return 0;
734         
735     for (e=root->u.root.absyn->main_elements; e; e=e->next)
736     {
737         data1_tag *tag = e->tag;
738         
739         if (tag && tag->which == DATA1T_string &&
740             !yaz_matchstr(tag->value.string, "mc?"))
741                 parse_data1_tree(p, tag->value.string, root);
742     }
743     return root;
744 }
745 static void *grs_init_marc(void)
746 {
747     return 0;
748 }
749
750 static void grs_destroy_marc(void *clientData)
751 {
752 }
753
754 static struct recTypeGrs marc_type = {
755     "marc",
756     grs_init_marc,
757     grs_destroy_marc,
758     grs_read_marc
759 };
760
761 RecTypeGrs recTypeGrs_marc = &marc_type;
762
763 static struct recTypeGrs marcxml_type = {
764     "marcxml",
765     grs_init_marc,
766     grs_destroy_marc,
767     grs_read_marcxml
768 };
769
770 RecTypeGrs recTypeGrs_marcxml = &marcxml_type;