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