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