New filter grs.marcxml.
[idzebra-moved-to-github.git] / recctrl / marcread.c
1 /* $Id: marcread.c,v 1.21 2003-08-21 10:29:00 adam Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002
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, 4);
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
214             if (!memcmp(tag, "4", 1) && (!yaz_matchstr(absynName, "UNIMARC")||
215                 !yaz_matchstr(absynName, "RUSMARC")))
216             {
217                 int go = 1;
218                 data1_node *res =
219                     data1_mk_tag_n (p->dh, p->mem,
220                                     buf+i+1, identifier_length-1, 
221                                     0 /* attr */, parent);
222                 i += identifier_length;
223                 i0 = i;
224                 do {
225                     while (buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
226                          buf[i] != ISO2709_FS && i < end_offset)
227                     {
228                         i++;
229                     }
230                     if (!memcmp(buf+i+1, "1", 1) && i<end_offset)
231                     {
232                         go = 0;
233                     }
234                     else
235                     {
236                         buf[i] = '$';
237                     }               
238                 } while (go && i < end_offset);
239                 
240                 data1_mk_text_n (p->dh, p->mem, buf + i0, i - i0, res);
241                 i0 = i;
242             }
243             else if (memcmp (tag, "00", 2) && identifier_length)
244             {
245                 data1_node *res;
246                 if (marc_xml)
247                 {
248                     int j;
249                     const char *attr[3];
250                     char code[10];
251                     
252                     for (j = 1; j<identifier_length && j < 9; j++)
253                         code[j-1] = buf[i+j];
254                     code[j-1] = 0;
255                     attr[0] = "code";
256                     attr[1] = code;
257                     attr[2] = 0;
258                     res = data1_mk_tag(p->dh, p->mem, "subfield",
259                                        attr, parent);
260                 }
261                 else
262                 {
263                     res = data1_mk_tag_n (p->dh, p->mem,
264                                            buf+i+1, identifier_length-1, 
265                                            0 /* attr */, parent);
266                 }
267 #if MARC_DEBUG
268                 fprintf (outf, " $"); 
269                 for (j = 1; j<identifier_length; j++)
270                     fprintf (outf, "%c", buf[j+i]);
271                 fprintf (outf, " ");
272 #endif
273                 i += identifier_length;
274                 i0 = i;
275                 while (buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
276                      buf[i] != ISO2709_FS && i < end_offset)
277                 {
278 #if MARC_DEBUG
279                     fprintf (outf, "%c", buf[i]);
280 #endif
281                     i++;
282                 }
283                 data1_mk_text_n (p->dh, p->mem, buf + i0, i - i0, res);
284                 i0 = i;
285             }
286             else
287             {
288 #if MARC_DEBUG
289                 fprintf (outf, "%c", buf[i]);
290 #endif
291                 i++;
292             }
293         }
294         if (i > i0)
295         {
296             data1_mk_text_n (p->dh, p->mem, buf + i0, i - i0, parent);
297         }
298 #if MARC_DEBUG
299         fprintf (outf, "\n");
300         if (i < end_offset)
301             fprintf (outf, "-- separator but not at end of field\n");
302         if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
303             fprintf (outf, "-- no separator at end of field\n");
304 #endif
305     }
306     return res_root;
307 }
308 /*
309  * Locate some data under this node. This routine should handle variants
310  * prettily.
311  */
312 static char *get_data(data1_node *n, int *len)
313 {
314     char *r;
315
316     while (n)
317     {
318         if (n->which == DATA1N_data)
319         {
320             int i;
321             *len = n->u.data.len;
322
323             for (i = 0; i<*len; i++)
324                 if (!d1_isspace(n->u.data.data[i]))
325                     break;
326             while (*len && d1_isspace(n->u.data.data[*len - 1]))
327                 (*len)--;
328             *len = *len - i;
329             if (*len > 0)
330                 return n->u.data.data + i;
331         }
332         if (n->which == DATA1N_tag)
333             n = n->child;
334         else if (n->which == DATA1N_data)
335             n = n->next;
336         else
337             break;      
338     }
339     r = "";
340     *len = strlen(r);
341     return r;
342 }
343
344 static data1_node *lookup_subfield(data1_node *node, const char *name)
345 {
346     data1_node *p;
347     
348     for (p=node; p; p=p->next)
349     {
350         if (!yaz_matchstr(p->u.tag.tag, name))
351             return p;
352     }
353     return 0;
354 }
355 static inline_subfield *lookup_inline_subfield(inline_subfield *pisf, const char *name)
356 {
357     inline_subfield *p;
358     
359     for (p=pisf; p; p=p->next)
360     {
361         if (!yaz_matchstr(p->name, name))
362             return p;
363     }
364     return 0;
365 }
366 static inline_subfield *cat_inline_subfield(mc_subfield *psf, char *buf, inline_subfield *pisf)
367 {
368     mc_subfield *p;
369     
370     for (p = psf; p && pisf; p = p->next)
371     {
372         if (p->which == MC_SF)
373         {
374             inline_subfield *found = lookup_inline_subfield(pisf, p->name);
375             
376             if (found)
377             {
378                 if (strcmp(p->prefix, "_"))
379                 {
380                     strcat(strcat(buf, " "), p->prefix);
381                 }
382                 if (p->interval.start == -1)
383                 {
384                     strcat(buf, found->data);
385                 }
386                 else
387                 {
388                     strncat(buf, found->data+p->interval.start,
389                         p->interval.end-p->interval.start+1);
390                 }
391                 if (strcmp(p->suffix, "_"))
392                 {
393                     strcat(strcat(buf, p->suffix), " ");
394                 }
395 #if MARCOMP_DEBUG
396                 logf(LOG_LOG, "cat_inline_subfield(): add subfield $%s", found->name);
397 #endif          
398                 pisf = found->next;
399             }
400         }
401         else if (p->which == MC_SFVARIANT)
402         {
403             inline_subfield *next;
404             
405             do {
406                 next = cat_inline_subfield(p->u.child, buf, pisf);
407                 if (next == pisf)
408                     break;
409                 pisf = next;
410             } while (pisf);
411         }
412         else if (p->which == MC_SFGROUP)
413         {
414             mc_subfield *pp;
415             int found;
416             
417             for (pp = p->u.child, found = 0; pp; pp = pp->next)
418             {
419                 if (!yaz_matchstr(pisf->name, p->name))
420                 {
421                     found = 1;
422                     break;
423                 }
424             }
425             if (found)
426             {
427                 strcat(buf, " (");
428                 pisf = cat_inline_subfield(p->u.child, buf, pisf);
429                 strcat(buf, ") ");
430             }
431         }
432     }
433     return pisf; 
434 }
435 static void cat_inline_field(mc_field *pf, char *buf, data1_node *subfield)
436 {
437     
438     if (!pf || !subfield)
439         return;
440
441     for (;subfield; subfield = subfield->next)
442     {
443         int len;
444         inline_field *pif = inline_parse(get_data(subfield,&len));
445         
446         if (pif && !yaz_matchstr(pif->name, pf->name))
447         {
448             if (!pf->list && pif->list)
449             {
450                 strcat(buf, pif->list->data);
451             }
452             else
453             {
454                 int ind1, ind2;
455
456                 /*
457                     check indicators
458                 */
459
460                 ind1 = (pif->ind1[0] == ' ') ? '_':pif->ind1[0];
461                 ind2 = (pif->ind2[0] == ' ') ? '_':pif->ind2[0];
462     
463                 if (((pf->ind1[0] == '.') || (ind1 == pf->ind1[0])) &&
464                     ((pf->ind2[0] == '.') || (ind2 == pf->ind2[0])))
465                 {
466                     cat_inline_subfield(pf->list, buf, pif->list);
467                     
468                     /*
469                         add separator for inline fields
470                     */
471                     if (strlen(buf))
472                     {
473                         strcat(buf, "\n");
474                     }
475                 }
476                 else
477                 {
478                     logf(LOG_WARN, "In-line field %s missed -- indicators does not match", pif->name);
479                 }
480             }
481         }
482         inline_destroy_field(pif);
483     }
484 #if MARCOMP_DEBUG    
485     logf(LOG_LOG, "cat_inline_field(): got buffer {%s}", buf);
486 #endif
487 }
488 static data1_node *cat_subfield(mc_subfield *psf, char *buf, data1_node *subfield)
489 {
490     mc_subfield *p;
491     
492     for (p = psf; p && subfield; p = p->next)
493     {
494         if (p->which == MC_SF)
495         {
496             data1_node *found = lookup_subfield(subfield, p->name);
497             
498             if (found)
499             {
500                 int len;
501                 
502                 if (strcmp(p->prefix, "_"))
503                 {
504                     strcat(strcat(buf, " "), p->prefix);
505                 }
506                 
507                 if (p->u.in_line)
508                 {
509                     cat_inline_field(p->u.in_line, buf, found);
510                 }
511                 else if (p->interval.start == -1)
512                 {
513                     strcat(buf, get_data(found, &len));
514                 }
515                 else
516                 {
517                     strncat(buf, get_data(found, &len)+p->interval.start,
518                         p->interval.end-p->interval.start+1);
519                 }
520                 if (strcmp(p->suffix, "_"))
521                 {
522                     strcat(strcat(buf, p->suffix), " ");
523                 }
524 #if MARCOMP_DEBUG               
525                 logf(LOG_LOG, "cat_subfield(): add subfield $%s", found->u.tag.tag);
526 #endif          
527                 subfield = found->next;
528             }
529         }
530         else if (p->which == MC_SFVARIANT)
531         {
532             data1_node *next;
533             do {
534                 next = cat_subfield(p->u.child, buf, subfield);
535                 if (next == subfield)
536                     break;
537                 subfield = next;
538             } while (subfield);
539         }
540         else if (p->which == MC_SFGROUP)
541         {
542             mc_subfield *pp;
543             int found;
544             
545             for (pp = p->u.child, found = 0; pp; pp = pp->next)
546             {
547                 if (!yaz_matchstr(subfield->u.tag.tag, pp->name))
548                 {
549                     found = 1;
550                     break;
551                 }
552             }
553             if (found)
554             {
555                 strcat(buf, " (");
556                 subfield = cat_subfield(p->u.child, buf, subfield);
557                 strcat(buf, ") ");
558             }
559         }
560     }
561     return subfield;
562 }
563 static data1_node *cat_field(struct grs_read_info *p, mc_field *pf, char *buf, data1_node *field)
564 {
565     data1_node *subfield;
566     int ind1, ind2;
567     
568     if (!pf || !field)
569         return 0;
570
571     
572     if (yaz_matchstr(field->u.tag.tag, pf->name))
573         return field->next;
574
575     subfield = field->child;
576     
577     if (!subfield)
578         return field->next;
579
580     /*
581         check subfield without indicators
582     */
583     
584     if (!pf->list && subfield->which == DATA1N_data)
585     {
586         int len;
587         
588         if (pf->interval.start == -1)
589         {
590             strcat(buf, get_data(field, &len));
591         }
592         else
593         {
594             strncat(buf, get_data(field, &len)+pf->interval.start,
595                 pf->interval.end-pf->interval.start+1);
596         }
597 #if MARCOMP_DEBUG
598         logf(LOG_LOG, "cat_field(): got buffer {%s}", buf);
599 #endif
600         return field->next;
601     }
602     
603     /*
604         check indicators
605     */
606
607     ind1 = (subfield->u.tag.tag[0] == ' ') ? '_':subfield->u.tag.tag[0];
608     ind2 = (subfield->u.tag.tag[1] == ' ') ? '_':subfield->u.tag.tag[1];
609     
610     if (!(
611         ((pf->ind1[0] == '.') || (ind1 == pf->ind1[0])) &&
612         ((pf->ind2[0] == '.') || (ind2 == pf->ind2[0]))
613         ))
614     {
615 #if MARCOMP_DEBUG
616         logf(LOG_WARN, "Field %s missed -- does not match indicators", field->u.tag.tag);
617 #endif
618         return field->next;
619     }
620     
621     subfield = subfield->child;
622     
623     if (!subfield)
624         return field->next;
625
626     cat_subfield(pf->list, buf, subfield);
627
628 #if MARCOMP_DEBUG    
629     logf(LOG_LOG, "cat_field(): got buffer {%s}", buf);
630 #endif
631     
632     return field->next;    
633 }
634 static int is_empty(char *s)
635 {
636     char *p = s;
637     
638     for (p = s; *p; p++)
639     {
640         if (!isspace(*p))
641             return 0;
642     }
643     return 1;
644 }
645 static void parse_data1_tree(struct grs_read_info *p, const char *mc_stmnt, data1_node *root)
646 {
647     data1_marctab *marctab = root->u.root.absyn->marc;
648     data1_node *top = root->child;
649     data1_node *field;
650     mc_context *c;
651     mc_field *pf;
652     char buf[1000000];
653     
654     c = mc_mk_context(mc_stmnt+3);
655     
656     if (!c)
657         return;
658         
659     pf = mc_getfield(c);
660     
661     if (!pf)
662     {
663         mc_destroy_context(c);
664         return;
665     }
666 #if MARCOMP_DEBUG    
667     logf(LOG_LOG, "parse_data1_tree(): statement -{%s}", mc_stmnt);
668 #endif
669     if (!yaz_matchstr(pf->name, "ldr"))
670     {
671         data1_node *new;
672 #if MARCOMP_DEBUG
673         logf(LOG_LOG,"parse_data1_tree(): try LEADER from {%d} to {%d} positions",
674             pf->interval.start, pf->interval.end);
675 #endif  
676         new = data1_mk_tag_n(p->dh, p->mem, mc_stmnt, strlen(mc_stmnt), 0, top);
677         data1_mk_text_n(p->dh, p->mem, marctab->leader+pf->interval.start,
678             pf->interval.end-pf->interval.start+1, new);
679     }
680     else
681     {
682         field=top->child;
683         
684         while(field)
685         {
686             if (!yaz_matchstr(field->u.tag.tag, pf->name))
687             {
688                 data1_node *new;
689                 char *pb = buf;
690 #if MARCOMP_DEBUG               
691                 logf(LOG_LOG, "parse_data1_tree(): try field {%s}", field->u.tag.tag);
692 #endif          
693                 *buf = '\0';        
694                 field = cat_field(p, pf, buf, field);
695                 
696                 for (pb = strtok(pb, "\n"); pb; pb = strtok(NULL, "\n"))
697                 {
698                         if (!is_empty(pb))
699                         {
700                                 new = data1_mk_tag_n(p->dh, p->mem, mc_stmnt, strlen(mc_stmnt), 0, top);
701                                 data1_mk_text_n(p->dh, p->mem, pb, strlen(pb), new);
702                         }
703                 }
704             }
705             else
706             {
707                 field = field->next;
708             }
709         }
710     }
711     mc_destroy_field(pf);
712     mc_destroy_context(c);
713 }
714
715 data1_node *grs_read_marcxml(struct grs_read_info *p)
716 {
717     data1_node *root = grs_read_iso2709(p, 1);
718     data1_element *e;
719
720     if (!root)
721         return 0;
722         
723     for (e=root->u.root.absyn->main_elements; e; e=e->next)
724     {
725         data1_tag *tag = e->tag;
726         
727         if (tag && tag->which == DATA1T_string &&
728             !yaz_matchstr(tag->value.string, "mc?"))
729                 parse_data1_tree(p, tag->value.string, root);
730     }
731     return root;
732 }
733
734
735 data1_node *grs_read_marc(struct grs_read_info *p)
736 {
737     data1_node *root = grs_read_iso2709(p, 0);
738     data1_element *e;
739
740     if (!root)
741         return 0;
742         
743     for (e=root->u.root.absyn->main_elements; e; e=e->next)
744     {
745         data1_tag *tag = e->tag;
746         
747         if (tag && tag->which == DATA1T_string &&
748             !yaz_matchstr(tag->value.string, "mc?"))
749                 parse_data1_tree(p, tag->value.string, root);
750     }
751     return root;
752 }
753 static void *grs_init_marc(void)
754 {
755     return 0;
756 }
757
758 static void grs_destroy_marc(void *clientData)
759 {
760 }
761
762 static struct recTypeGrs marc_type = {
763     "marc",
764     grs_init_marc,
765     grs_destroy_marc,
766     grs_read_marc
767 };
768
769 RecTypeGrs recTypeGrs_marc = &marc_type;
770
771 static struct recTypeGrs marcxml_type = {
772     "marcxml",
773     grs_init_marc,
774     grs_destroy_marc,
775     grs_read_marcxml
776 };
777
778 RecTypeGrs recTypeGrs_marcxml = &marcxml_type;