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