Removed unused function tr. Fixed warning
[idzebra-moved-to-github.git] / recctrl / marcread.c
1 /* $Id: marcread.c,v 1.20 2003-03-05 16:43:31 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)
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     res_top = data1_mk_tag (p->dh, p->mem, absynName, 0, res_root);
87
88     if ((marctab = res_root->u.root.absyn->marc))
89     {
90         memcpy(marctab->leader, buf, 24);
91         memcpy(marctab->implementation_codes, buf+6, 4);
92         marctab->implementation_codes[4] = '\0';
93         memcpy(marctab->user_systems, buf+17, 3);
94         marctab->user_systems[3] = '\0';
95     }
96
97     if (marctab && marctab->force_indicator_length >= 0)
98         indicator_length = marctab->force_indicator_length;
99     else
100         indicator_length = atoi_n (buf+10, 1);
101     if (marctab && marctab->force_identifier_length >= 0)
102         identifier_length = marctab->force_identifier_length;
103     else
104         identifier_length = atoi_n (buf+11, 1);
105     base_address = atoi_n (buf+12, 4);
106
107     length_data_entry = atoi_n (buf+20, 1);
108     length_starting = atoi_n (buf+21, 1);
109     length_implementation = atoi_n (buf+22, 1);
110
111     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
112         entry_p += 3+length_data_entry+length_starting;
113     base_address = entry_p+1;
114     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
115     {
116         int data_length;
117         int data_offset;
118         int end_offset;
119         int i, i0;
120         char tag[4];
121         data1_node *res;
122         data1_node *parent = res_top;
123
124         memcpy (tag, buf+entry_p, 3);
125         entry_p += 3;
126         tag[3] = '\0';
127
128
129         /* generate field node */
130         res = data1_mk_tag_n (p->dh, p->mem, tag, 3, 0 /* attr */, parent);
131
132 #if MARC_DEBUG
133         fprintf (outf, "%s ", tag);
134 #endif
135         data_length = atoi_n (buf+entry_p, length_data_entry);
136         entry_p += length_data_entry;
137         data_offset = atoi_n (buf+entry_p, length_starting);
138         entry_p += length_starting;
139         i = data_offset + base_address;
140         end_offset = i+data_length-1;
141
142         if (memcmp (tag, "00", 2) && indicator_length)
143         {
144             /* generate indicator node */
145 #if MARC_DEBUG
146             int j;
147 #endif
148             res = data1_mk_tag_n (p->dh, p->mem, 
149                                   buf+i, indicator_length, 0 /* attr */, res);
150 #if MARC_DEBUG
151             for (j = 0; j<indicator_length; j++)
152                 fprintf (outf, "%c", buf[j+i]);
153 #endif
154             i += indicator_length;
155         }
156         parent = res;
157         /* traverse sub fields */
158         i0 = i;
159         while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset)
160         {
161
162             if (!memcmp(tag, "4", 1) && (!yaz_matchstr(absynName, "UNIMARC")||
163                 !yaz_matchstr(absynName, "RUSMARC")))
164             {
165                 int go = 1;
166                 data1_node *res =
167                     data1_mk_tag_n (p->dh, p->mem,
168                                     buf+i+1, identifier_length-1, 
169                                     0 /* attr */, parent);
170                 i += identifier_length;
171                 i0 = i;
172                 do {
173                     while (buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
174                          buf[i] != ISO2709_FS && i < end_offset)
175                     {
176                         i++;
177                     }
178                     if (!memcmp(buf+i+1, "1", 1) && i<end_offset)
179                     {
180                         go = 0;
181                     }
182                     else
183                     {
184                         buf[i] = '$';
185                     }               
186                 } while (go && i < end_offset);
187                 
188                 data1_mk_text_n (p->dh, p->mem, buf + i0, i - i0, res);
189                 i0 = i;
190             }
191             else if (memcmp (tag, "00", 2) && identifier_length)
192             {
193                 data1_node *res =
194                     data1_mk_tag_n (p->dh, p->mem,
195                                     buf+i+1, identifier_length-1, 
196                                     0 /* attr */, parent);
197 #if MARC_DEBUG
198                 fprintf (outf, " $"); 
199                 for (j = 1; j<identifier_length; j++)
200                     fprintf (outf, "%c", buf[j+i]);
201                 fprintf (outf, " ");
202 #endif
203                 i += identifier_length;
204                 i0 = i;
205                 while (buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
206                      buf[i] != ISO2709_FS && i < end_offset)
207                 {
208 #if MARC_DEBUG
209                     fprintf (outf, "%c", buf[i]);
210 #endif
211                     i++;
212                 }
213                 data1_mk_text_n (p->dh, p->mem, buf + i0, i - i0, res);
214                 i0 = i;
215             }
216             else
217             {
218 #if MARC_DEBUG
219                 fprintf (outf, "%c", buf[i]);
220 #endif
221                 i++;
222             }
223         }
224         if (i > i0)
225         {
226             data1_mk_text_n (p->dh, p->mem, buf + i0, i - i0, parent);
227         }
228 #if MARC_DEBUG
229         fprintf (outf, "\n");
230         if (i < end_offset)
231             fprintf (outf, "-- separator but not at end of field\n");
232         if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
233             fprintf (outf, "-- no separator at end of field\n");
234 #endif
235     }
236     return res_root;
237 }
238 /*
239  * Locate some data under this node. This routine should handle variants
240  * prettily.
241  */
242 static char *get_data(data1_node *n, int *len)
243 {
244     char *r;
245
246     while (n)
247     {
248         if (n->which == DATA1N_data)
249         {
250             int i;
251             *len = n->u.data.len;
252
253             for (i = 0; i<*len; i++)
254                 if (!d1_isspace(n->u.data.data[i]))
255                     break;
256             while (*len && d1_isspace(n->u.data.data[*len - 1]))
257                 (*len)--;
258             *len = *len - i;
259             if (*len > 0)
260                 return n->u.data.data + i;
261         }
262         if (n->which == DATA1N_tag)
263             n = n->child;
264         else if (n->which == DATA1N_data)
265             n = n->next;
266         else
267             break;      
268     }
269     r = "";
270     *len = strlen(r);
271     return r;
272 }
273
274 static data1_node *lookup_subfield(data1_node *node, const char *name)
275 {
276     data1_node *p;
277     
278     for (p=node; p; p=p->next)
279     {
280         if (!yaz_matchstr(p->u.tag.tag, name))
281             return p;
282     }
283     return 0;
284 }
285 static inline_subfield *lookup_inline_subfield(inline_subfield *pisf, const char *name)
286 {
287     inline_subfield *p;
288     
289     for (p=pisf; p; p=p->next)
290     {
291         if (!yaz_matchstr(p->name, name))
292             return p;
293     }
294     return 0;
295 }
296 static inline_subfield *cat_inline_subfield(mc_subfield *psf, char *buf, inline_subfield *pisf)
297 {
298     mc_subfield *p;
299     
300     for (p = psf; p && pisf; p = p->next)
301     {
302         if (p->which == MC_SF)
303         {
304             inline_subfield *found = lookup_inline_subfield(pisf, p->name);
305             
306             if (found)
307             {
308                 if (strcmp(p->prefix, "_"))
309                 {
310                     strcat(strcat(buf, " "), p->prefix);
311                 }
312                 if (p->interval.start == -1)
313                 {
314                     strcat(buf, found->data);
315                 }
316                 else
317                 {
318                     strncat(buf, found->data+p->interval.start,
319                         p->interval.end-p->interval.start+1);
320                 }
321                 if (strcmp(p->suffix, "_"))
322                 {
323                     strcat(strcat(buf, p->suffix), " ");
324                 }
325 #if MARCOMP_DEBUG
326                 logf(LOG_LOG, "cat_inline_subfield(): add subfield $%s", found->name);
327 #endif          
328                 pisf = found->next;
329             }
330         }
331         else if (p->which == MC_SFVARIANT)
332         {
333             inline_subfield *next;
334             
335             do {
336                 next = cat_inline_subfield(p->u.child, buf, pisf);
337                 if (next == pisf)
338                     break;
339                 pisf = next;
340             } while (pisf);
341         }
342         else if (p->which == MC_SFGROUP)
343         {
344             mc_subfield *pp;
345             int found;
346             
347             for (pp = p->u.child, found = 0; pp; pp = pp->next)
348             {
349                 if (!yaz_matchstr(pisf->name, p->name))
350                 {
351                     found = 1;
352                     break;
353                 }
354             }
355             if (found)
356             {
357                 strcat(buf, " (");
358                 pisf = cat_inline_subfield(p->u.child, buf, pisf);
359                 strcat(buf, ") ");
360             }
361         }
362     }
363     return pisf; 
364 }
365 static void cat_inline_field(mc_field *pf, char *buf, data1_node *subfield)
366 {
367     
368     if (!pf || !subfield)
369         return;
370
371     for (;subfield; subfield = subfield->next)
372     {
373         int len;
374         inline_field *pif = inline_parse(get_data(subfield,&len));
375         
376         if (pif && !yaz_matchstr(pif->name, pf->name))
377         {
378             if (!pf->list && pif->list)
379             {
380                 strcat(buf, pif->list->data);
381             }
382             else
383             {
384                 int ind1, ind2;
385
386                 /*
387                     check indicators
388                 */
389
390                 ind1 = (pif->ind1[0] == ' ') ? '_':pif->ind1[0];
391                 ind2 = (pif->ind2[0] == ' ') ? '_':pif->ind2[0];
392     
393                 if (((pf->ind1[0] == '.') || (ind1 == pf->ind1[0])) &&
394                     ((pf->ind2[0] == '.') || (ind2 == pf->ind2[0])))
395                 {
396                     cat_inline_subfield(pf->list, buf, pif->list);
397                     
398                     /*
399                         add separator for inline fields
400                     */
401                     if (strlen(buf))
402                     {
403                         strcat(buf, "\n");
404                     }
405                 }
406                 else
407                 {
408                     logf(LOG_WARN, "In-line field %s missed -- indicators does not match", pif->name);
409                 }
410             }
411         }
412         inline_destroy_field(pif);
413     }
414 #if MARCOMP_DEBUG    
415     logf(LOG_LOG, "cat_inline_field(): got buffer {%s}", buf);
416 #endif
417 }
418 static data1_node *cat_subfield(mc_subfield *psf, char *buf, data1_node *subfield)
419 {
420     mc_subfield *p;
421     
422     for (p = psf; p && subfield; p = p->next)
423     {
424         if (p->which == MC_SF)
425         {
426             data1_node *found = lookup_subfield(subfield, p->name);
427             
428             if (found)
429             {
430                 int len;
431                 
432                 if (strcmp(p->prefix, "_"))
433                 {
434                     strcat(strcat(buf, " "), p->prefix);
435                 }
436                 
437                 if (p->u.in_line)
438                 {
439                     cat_inline_field(p->u.in_line, buf, found);
440                 }
441                 else if (p->interval.start == -1)
442                 {
443                     strcat(buf, get_data(found, &len));
444                 }
445                 else
446                 {
447                     strncat(buf, get_data(found, &len)+p->interval.start,
448                         p->interval.end-p->interval.start+1);
449                 }
450                 if (strcmp(p->suffix, "_"))
451                 {
452                     strcat(strcat(buf, p->suffix), " ");
453                 }
454 #if MARCOMP_DEBUG               
455                 logf(LOG_LOG, "cat_subfield(): add subfield $%s", found->u.tag.tag);
456 #endif          
457                 subfield = found->next;
458             }
459         }
460         else if (p->which == MC_SFVARIANT)
461         {
462             data1_node *next;
463             do {
464                 next = cat_subfield(p->u.child, buf, subfield);
465                 if (next == subfield)
466                     break;
467                 subfield = next;
468             } while (subfield);
469         }
470         else if (p->which == MC_SFGROUP)
471         {
472             mc_subfield *pp;
473             int found;
474             
475             for (pp = p->u.child, found = 0; pp; pp = pp->next)
476             {
477                 if (!yaz_matchstr(subfield->u.tag.tag, pp->name))
478                 {
479                     found = 1;
480                     break;
481                 }
482             }
483             if (found)
484             {
485                 strcat(buf, " (");
486                 subfield = cat_subfield(p->u.child, buf, subfield);
487                 strcat(buf, ") ");
488             }
489         }
490     }
491     return subfield;
492 }
493 static data1_node *cat_field(struct grs_read_info *p, mc_field *pf, char *buf, data1_node *field)
494 {
495     data1_node *subfield;
496     int ind1, ind2;
497     
498     if (!pf || !field)
499         return 0;
500
501     
502     if (yaz_matchstr(field->u.tag.tag, pf->name))
503         return field->next;
504
505     subfield = field->child;
506     
507     if (!subfield)
508         return field->next;
509
510     /*
511         check subfield without indicators
512     */
513     
514     if (!pf->list && subfield->which == DATA1N_data)
515     {
516         int len;
517         
518         if (pf->interval.start == -1)
519         {
520             strcat(buf, get_data(field, &len));
521         }
522         else
523         {
524             strncat(buf, get_data(field, &len)+pf->interval.start,
525                 pf->interval.end-pf->interval.start+1);
526         }
527 #if MARCOMP_DEBUG
528         logf(LOG_LOG, "cat_field(): got buffer {%s}", buf);
529 #endif
530         return field->next;
531     }
532     
533     /*
534         check indicators
535     */
536
537     ind1 = (subfield->u.tag.tag[0] == ' ') ? '_':subfield->u.tag.tag[0];
538     ind2 = (subfield->u.tag.tag[1] == ' ') ? '_':subfield->u.tag.tag[1];
539     
540     if (!(
541         ((pf->ind1[0] == '.') || (ind1 == pf->ind1[0])) &&
542         ((pf->ind2[0] == '.') || (ind2 == pf->ind2[0]))
543         ))
544     {
545 #if MARCOMP_DEBUG
546         logf(LOG_WARN, "Field %s missed -- does not match indicators", field->u.tag.tag);
547 #endif
548         return field->next;
549     }
550     
551     subfield = subfield->child;
552     
553     if (!subfield)
554         return field->next;
555
556     cat_subfield(pf->list, buf, subfield);
557
558 #if MARCOMP_DEBUG    
559     logf(LOG_LOG, "cat_field(): got buffer {%s}", buf);
560 #endif
561     
562     return field->next;    
563 }
564 static int is_empty(char *s)
565 {
566     char *p = s;
567     
568     for (p = s; *p; p++)
569     {
570         if (!isspace(*p))
571             return 0;
572     }
573     return 1;
574 }
575 static void parse_data1_tree(struct grs_read_info *p, const char *mc_stmnt, data1_node *root)
576 {
577     data1_marctab *marctab = root->u.root.absyn->marc;
578     data1_node *top = root->child;
579     data1_node *field;
580     mc_context *c;
581     mc_field *pf;
582     char buf[1000000];
583     
584     c = mc_mk_context(mc_stmnt+3);
585     
586     if (!c)
587         return;
588         
589     pf = mc_getfield(c);
590     
591     if (!pf)
592     {
593         mc_destroy_context(c);
594         return;
595     }
596 #if MARCOMP_DEBUG    
597     logf(LOG_LOG, "parse_data1_tree(): statement -{%s}", mc_stmnt);
598 #endif
599     if (!yaz_matchstr(pf->name, "ldr"))
600     {
601         data1_node *new;
602 #if MARCOMP_DEBUG
603         logf(LOG_LOG,"parse_data1_tree(): try LEADER from {%d} to {%d} positions",
604             pf->interval.start, pf->interval.end);
605 #endif  
606         new = data1_mk_tag_n(p->dh, p->mem, mc_stmnt, strlen(mc_stmnt), 0, top);
607         data1_mk_text_n(p->dh, p->mem, marctab->leader+pf->interval.start,
608             pf->interval.end-pf->interval.start+1, new);
609     }
610     else
611     {
612         field=top->child;
613         
614         while(field)
615         {
616             if (!yaz_matchstr(field->u.tag.tag, pf->name))
617             {
618                 data1_node *new;
619                 char *pb = buf;
620 #if MARCOMP_DEBUG               
621                 logf(LOG_LOG, "parse_data1_tree(): try field {%s}", field->u.tag.tag);
622 #endif          
623                 *buf = '\0';        
624                 field = cat_field(p, pf, buf, field);
625                 
626                 for (pb = strtok(pb, "\n"); pb; pb = strtok(NULL, "\n"))
627                 {
628                         if (!is_empty(pb))
629                         {
630                                 new = data1_mk_tag_n(p->dh, p->mem, mc_stmnt, strlen(mc_stmnt), 0, top);
631                                 data1_mk_text_n(p->dh, p->mem, pb, strlen(pb), new);
632                         }
633                 }
634             }
635             else
636             {
637                 field = field->next;
638             }
639         }
640     }
641     mc_destroy_field(pf);
642     mc_destroy_context(c);
643 }
644
645 data1_node *grs_read_marc(struct grs_read_info *p)
646 {
647     data1_node *root = grs_read_iso2709(p);
648     data1_element *e;
649
650     if (!root)
651         return 0;
652         
653     for (e=root->u.root.absyn->main_elements; e; e=e->next)
654     {
655         data1_tag *tag = e->tag;
656         
657         if (tag && tag->which == DATA1T_string &&
658             !yaz_matchstr(tag->value.string, "mc?"))
659                 parse_data1_tree(p, tag->value.string, root);
660     }
661     return root;
662 }
663 static void *grs_init_marc(void)
664 {
665     return 0;
666 }
667
668 static void grs_destroy_marc(void *clientData)
669 {
670 }
671
672 static struct recTypeGrs marc_type = {
673     "marc",
674     grs_init_marc,
675     grs_destroy_marc,
676     grs_read_marc
677 };
678
679 RecTypeGrs recTypeGrs_marc = &marc_type;