320cbded324d1212dc95c51a430e566747bc6034
[idzebra-moved-to-github.git] / recctrl / marcread.c
1 /* $Id: marcread.c,v 1.33 2006-04-26 11:12:31 adam Exp $
2    Copyright (C) 1995-2005
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/yaz-util.h>
28 #include <yaz/marcdisp.h>
29 #include <idzebra/recgrs.h>
30 #include "marcomp.h"
31 #include "inline.h"
32
33 #define MARC_DEBUG 0
34 #define MARCOMP_DEBUG 0
35
36 struct marc_info {
37     char type[256];
38 };
39
40 static data1_node *grs_read_iso2709 (struct grs_read_info *p, int marc_xml)
41 {
42     struct marc_info *mi = (struct marc_info*) p->clientData;
43     char buf[100000];
44     int entry_p;
45     int record_length;
46     int indicator_length;
47     int identifier_length;
48     int base_address;
49     int end_of_directory;
50     int length_data_entry;
51     int length_starting;
52     int length_implementation;
53     int read_bytes;
54 #if MARC_DEBUG
55     FILE *outf = stdout;
56 #endif
57     data1_node *res_root, *res_top;
58     char *absynName;
59     data1_marctab *marctab;
60
61     if ((*p->readf)(p->fh, buf, 5) != 5)
62         return NULL;
63     while (*buf < '0' || *buf > '9')
64     {
65         int i;
66
67         yaz_log(YLOG_WARN, "MARC: Skipping bad byte %d (0x%02X)",
68                 *buf & 0xff, *buf & 0xff);
69         for (i = 0; i<4; i++)
70             buf[i] = buf[i+1];
71
72         if ((*p->readf)(p->fh, buf+4, 1) != 1)
73             return NULL;
74     }
75     record_length = atoi_n (buf, 5);
76     if (record_length < 25)
77     {
78         yaz_log (YLOG_WARN, "MARC record length < 25, is %d", record_length);
79         return NULL;
80     }
81     /* read remaining part - attempt to read one byte furhter... */
82     read_bytes = (*p->readf)(p->fh, buf+5, record_length-4);
83     if (read_bytes < record_length-5)
84     {
85         yaz_log (YLOG_WARN, "Couldn't read whole MARC record");
86         return NULL;
87     }
88     if (read_bytes == record_length - 4)
89     {
90         off_t cur_offset = (*p->tellf)(p->fh);
91         if (cur_offset <= 27)
92             return NULL;
93         if (p->endf)
94             (*p->endf)(p->fh, cur_offset - 1);
95     }
96     absynName = mi->type;
97     res_root = data1_mk_root (p->dh, p->mem, absynName);
98     if (!res_root)
99     {
100         yaz_log (YLOG_WARN, "cannot read MARC without an abstract syntax");
101         return 0;
102     }
103     if (marc_xml)
104     {
105         data1_node *lead;
106         const char *attr[] = { "xmlns", "http://www.loc.gov/MARC21/slim", 0};
107                          
108         res_top = data1_mk_tag (p->dh, p->mem, "record", attr, res_root);
109
110         lead = data1_mk_tag(p->dh, p->mem, "leader", 0, res_top);
111         data1_mk_text_n(p->dh, p->mem, buf, 24, lead);
112     }
113     else
114         res_top = data1_mk_tag (p->dh, p->mem, absynName, 0, res_root);
115
116     if ((marctab = data1_absyn_getmarctab(p->dh, res_root->u.root.absyn)))
117     {
118         memcpy(marctab->leader, buf, 24);
119         memcpy(marctab->implementation_codes, buf+6, 4);
120         marctab->implementation_codes[4] = '\0';
121         memcpy(marctab->user_systems, buf+17, 3);
122         marctab->user_systems[3] = '\0';
123     }
124
125     if (marctab && marctab->force_indicator_length >= 0)
126         indicator_length = marctab->force_indicator_length;
127     else
128         indicator_length = atoi_n (buf+10, 1);
129     if (marctab && marctab->force_identifier_length >= 0)
130         identifier_length = marctab->force_identifier_length;
131     else
132         identifier_length = atoi_n (buf+11, 1);
133     base_address = atoi_n (buf+12, 5);
134
135     length_data_entry = atoi_n (buf+20, 1);
136     length_starting = atoi_n (buf+21, 1);
137     length_implementation = atoi_n (buf+22, 1);
138
139     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
140     {
141         int l = 3 + length_data_entry + length_starting;
142         if (entry_p + l >= record_length)
143         {
144             yaz_log(YLOG_WARN, "MARC: Directory offset %d: end of record.",
145                     entry_p);
146             return 0;
147         }
148         /* check for digits in length info */
149         while (--l >= 3)
150             if (!isdigit(*(const unsigned char *) (buf + entry_p+l)))
151                 break;
152         if (l >= 3)
153         {
154             /* not all digits, so stop directory scan */
155             yaz_log(YLOG_LOG, "MARC: Bad directory");
156             break;
157         }
158         entry_p += 3 + length_data_entry + length_starting;
159     }
160     end_of_directory = entry_p;
161     if (base_address != entry_p+1)
162     {
163         yaz_log(YLOG_WARN, "MARC: Base address does not follow directory");
164     }
165     for (entry_p = 24; entry_p != end_of_directory; )
166     {
167         int data_length;
168         int data_offset;
169         int end_offset;
170         int i, i0;
171         char tag[4];
172         data1_node *res;
173         data1_node *parent = res_top;
174
175         memcpy (tag, buf+entry_p, 3);
176         entry_p += 3;
177         tag[3] = '\0';
178
179         if (marc_xml)
180             res = parent;
181         else
182             res = data1_mk_tag_n (p->dh, p->mem, tag, 3, 0 /* attr */, parent);
183         
184 #if MARC_DEBUG
185         fprintf (outf, "%s ", tag);
186 #endif
187         data_length = atoi_n (buf+entry_p, length_data_entry);
188         entry_p += length_data_entry;
189         data_offset = atoi_n (buf+entry_p, length_starting);
190         entry_p += length_starting;
191         i = data_offset + base_address;
192         end_offset = i+data_length-1;
193
194         if (data_length <= 0 || data_offset < 0 || end_offset >= record_length)
195         {
196             yaz_log(YLOG_WARN, "MARC: Bad offsets in data. Skipping rest");
197             break;
198         }
199         
200         if (memcmp (tag, "00", 2) && indicator_length)
201         {
202             /* generate indicator node */
203             if (marc_xml)
204             {
205                 const char *attr[10];
206                 int j;
207
208                 attr[0] = "tag";
209                 attr[1] = tag;
210                 attr[2] = 0;
211
212                 res = data1_mk_tag(p->dh, p->mem, "datafield", attr, res);
213
214                 for (j = 0; j<indicator_length; j++)
215                 {
216                     char str1[18], str2[2];
217                     sprintf (str1, "ind%d", j+1);
218                     str2[0] = buf[i+j];
219                     str2[1] = '\0';
220
221                     attr[0] = str1;
222                     attr[1] = str2;
223                     
224                     data1_tag_add_attr (p->dh, p->mem, res, attr);
225                 }
226             }
227             else
228             {
229 #if MARC_DEBUG
230                 int j;
231 #endif
232                 res = data1_mk_tag_n (p->dh, p->mem, 
233                                       buf+i, indicator_length, 0 /* attr */, res);
234 #if MARC_DEBUG
235                 for (j = 0; j<indicator_length; j++)
236                     fprintf (outf, "%c", buf[j+i]);
237 #endif
238             }
239             i += indicator_length;
240         } 
241         else
242         {
243             if (marc_xml)
244             {
245                 const char *attr[10];
246                 
247                 attr[0] = "tag";
248                 attr[1] = tag;
249                 attr[2] = 0;
250                 
251                 res = data1_mk_tag(p->dh, p->mem, "controlfield", attr, res);
252             }
253         }
254         parent = res;
255         /* traverse sub fields */
256         i0 = i;
257         while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset)
258         {
259             if (memcmp (tag, "00", 2) && identifier_length)
260             {
261                 data1_node *res;
262                 if (marc_xml)
263                 {
264                     int j;
265                     const char *attr[3];
266                     char code[10];
267                     
268                     for (j = 1; j<identifier_length && j < 9; j++)
269                         code[j-1] = buf[i+j];
270                     code[j-1] = 0;
271                     attr[0] = "code";
272                     attr[1] = code;
273                     attr[2] = 0;
274                     res = data1_mk_tag(p->dh, p->mem, "subfield",
275                                        attr, parent);
276                 }
277                 else
278                 {
279                     res = data1_mk_tag_n (p->dh, p->mem,
280                                            buf+i+1, identifier_length-1, 
281                                            0 /* attr */, parent);
282                 }
283 #if MARC_DEBUG
284                 fprintf (outf, " $"); 
285                 for (j = 1; j<identifier_length; j++)
286                     fprintf (outf, "%c", buf[j+i]);
287                 fprintf (outf, " ");
288 #endif
289                 i += identifier_length;
290                 i0 = i;
291                 while (buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
292                      buf[i] != ISO2709_FS && i < end_offset)
293                 {
294 #if MARC_DEBUG
295                     fprintf (outf, "%c", buf[i]);
296 #endif
297                     i++;
298                 }
299                 data1_mk_text_n (p->dh, p->mem, buf + i0, i - i0, res);
300                 i0 = i;
301             }
302             else
303             {
304 #if MARC_DEBUG
305                 fprintf (outf, "%c", buf[i]);
306 #endif
307                 i++;
308             }
309         }
310         if (i > i0)
311         {
312             data1_mk_text_n (p->dh, p->mem, buf + i0, i - i0, parent);
313         }
314 #if MARC_DEBUG
315         fprintf (outf, "\n");
316         if (i < end_offset)
317             fprintf (outf, "-- separator but not at end of field\n");
318         if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
319             fprintf (outf, "-- no separator at end of field\n");
320 #endif
321     }
322     return res_root;
323 }
324
325 /*
326  * Locate some data under this node. This routine should handle variants
327  * prettily.
328  */
329 static char *get_data(data1_node *n, int *len)
330 {
331     char *r;
332
333     while (n)
334     {
335         if (n->which == DATA1N_data)
336         {
337             int i;
338             *len = n->u.data.len;
339
340             for (i = 0; i<*len; i++)
341                 if (!d1_isspace(n->u.data.data[i]))
342                     break;
343             while (*len && d1_isspace(n->u.data.data[*len - 1]))
344                 (*len)--;
345             *len = *len - i;
346             if (*len > 0)
347                 return n->u.data.data + i;
348         }
349         if (n->which == DATA1N_tag)
350             n = n->child;
351         else if (n->which == DATA1N_data)
352             n = n->next;
353         else
354             break;      
355     }
356     r = "";
357     *len = strlen(r);
358     return r;
359 }
360
361 static data1_node *lookup_subfield(data1_node *node, const char *name)
362 {
363     data1_node *p;
364     
365     for (p=node; p; p=p->next)
366     {
367         if (!yaz_matchstr(p->u.tag.tag, name))
368             return p;
369     }
370     return 0;
371 }
372
373 static inline_subfield *lookup_inline_subfield(inline_subfield *pisf,
374                                                const char *name)
375 {
376     inline_subfield *p;
377     
378     for (p=pisf; p; p=p->next)
379     {
380         if (!yaz_matchstr(p->name, name))
381             return p;
382     }
383     return 0;
384 }
385
386 static inline_subfield *cat_inline_subfield(mc_subfield *psf, WRBUF buf,
387                                             inline_subfield *pisf)
388 {
389     mc_subfield *p;
390     
391     for (p = psf; p && pisf; p = p->next)
392     {
393         if (p->which == MC_SF)
394         {
395             inline_subfield *found = lookup_inline_subfield(pisf, p->name);
396             
397             if (found)
398             {
399                 if (strcmp(p->prefix, "_"))
400                 {
401                     wrbuf_puts(buf, " ");
402                     wrbuf_puts(buf, p->prefix);
403                 }
404                 if (p->interval.start == -1)
405                 {
406                     wrbuf_puts(buf, found->data);
407                 }
408                 else
409                 {
410                     wrbuf_write(buf, found->data+p->interval.start,
411                                 p->interval.end-p->interval.start);
412                     wrbuf_puts(buf, "");
413                 }
414                 if (strcmp(p->suffix, "_"))
415                 {
416                     wrbuf_puts(buf, p->suffix);
417                     wrbuf_puts(buf, " ");
418                 }
419 #if MARCOMP_DEBUG
420                 yaz_log(YLOG_LOG, "cat_inline_subfield(): add subfield $%s", found->name);
421 #endif          
422                 pisf = found->next;
423             }
424         }
425         else if (p->which == MC_SFVARIANT)
426         {
427             inline_subfield *next;
428             
429             do {
430                 next = cat_inline_subfield(p->u.child, buf, pisf);
431                 if (next == pisf)
432                     break;
433                 pisf = next;
434             } while (pisf);
435         }
436         else if (p->which == MC_SFGROUP)
437         {
438             mc_subfield *pp;
439             int found;
440             
441             for (pp = p->u.child, found = 0; pp; pp = pp->next)
442             {
443                 if (!yaz_matchstr(pisf->name, p->name))
444                 {
445                     found = 1;
446                     break;
447                 }
448             }
449             if (found)
450             {
451                 wrbuf_puts(buf, " (");
452                 pisf = cat_inline_subfield(p->u.child, buf, pisf);
453                 wrbuf_puts(buf, ") ");
454             }
455         }
456     }
457     return pisf; 
458 }
459
460 static void cat_inline_field(mc_field *pf, WRBUF buf, data1_node *subfield)
461 {    
462     if (!pf || !subfield)
463         return;
464
465     for (;subfield;)
466     {
467         int len;
468         inline_field *pif=NULL;
469         data1_node *psubf;
470         
471         if (yaz_matchstr(subfield->u.tag.tag, "1"))
472         {
473             subfield = subfield->next;
474             continue;
475         }
476         
477         psubf = subfield;
478         pif = inline_mk_field();
479         do
480         {
481             int i;
482             if ((i=inline_parse(pif, psubf->u.tag.tag, get_data(psubf, &len)))<0)
483             {
484                 yaz_log(YLOG_WARN, "inline subfield ($%s): parse error",
485                     psubf->u.tag.tag);
486                 inline_destroy_field(pif);
487                 return; 
488             }
489             psubf = psubf->next;
490         } while (psubf && yaz_matchstr(psubf->u.tag.tag, "1"));
491         
492         subfield = psubf;
493         
494         if (pif && !yaz_matchstr(pif->name, pf->name))
495         {
496             if (!pf->list && pif->list)
497             {
498                 wrbuf_puts(buf, pif->list->data);
499             }
500             else
501             {
502                 int ind1, ind2;
503
504                 /*
505                     check indicators
506                 */
507
508                 ind1 = (pif->ind1[0] == ' ') ? '_':pif->ind1[0];
509                 ind2 = (pif->ind2[0] == ' ') ? '_':pif->ind2[0];
510     
511                 if (((pf->ind1[0] == '.') || (ind1 == pf->ind1[0])) &&
512                     ((pf->ind2[0] == '.') || (ind2 == pf->ind2[0])))
513                 {
514                     cat_inline_subfield(pf->list, buf, pif->list);
515                     
516                     /*
517                         add separator for inline fields
518                     */
519                     if (wrbuf_len(buf))
520                     {
521                         wrbuf_puts(buf, "\n");
522                     }
523                 }
524                 else
525                 {
526                     yaz_log(YLOG_WARN, "In-line field %s missed -- indicators do not match", pif->name);
527                 }
528             }
529         }
530         inline_destroy_field(pif);
531     }
532 #if MARCOMP_DEBUG    
533     yaz_log(YLOG_LOG, "cat_inline_field(): got buffer {%s}", buf);
534 #endif
535 }
536
537 static data1_node *cat_subfield(mc_subfield *psf, WRBUF buf,
538                                 data1_node *subfield)
539 {
540     mc_subfield *p;
541     
542     for (p = psf; p && subfield; p = p->next)
543     {
544         if (p->which == MC_SF)
545         {
546             data1_node *found = lookup_subfield(subfield, p->name);
547             
548             if (found)
549             {
550                 int len;
551                 
552                 if (strcmp(p->prefix, "_"))
553                 {
554                     wrbuf_puts(buf, " ");
555                     wrbuf_puts(buf, p->prefix);
556                 }
557                 
558                 if (p->u.in_line)
559                 {
560                     cat_inline_field(p->u.in_line, buf, found);
561                 }
562                 else if (p->interval.start == -1)
563                 {
564                     wrbuf_puts(buf, get_data(found, &len));
565                 }
566                 else
567                 {
568                     wrbuf_write(buf, get_data(found, &len)+p->interval.start,
569                         p->interval.end-p->interval.start);
570                     wrbuf_puts(buf, "");
571                 }
572                 if (strcmp(p->suffix, "_"))
573                 {
574                     wrbuf_puts(buf, p->suffix);
575                     wrbuf_puts(buf, " ");
576                 }
577 #if MARCOMP_DEBUG               
578                 yaz_log(YLOG_LOG, "cat_subfield(): add subfield $%s", found->u.tag.tag);
579 #endif          
580                 subfield = found->next;
581             }
582         }
583         else if (p->which == MC_SFVARIANT)
584         {
585             data1_node *next;
586             do {
587                 next = cat_subfield(p->u.child, buf, subfield);
588                 if (next == subfield)
589                     break;
590                 subfield = next;
591             } while (subfield);
592         }
593         else if (p->which == MC_SFGROUP)
594         {
595             mc_subfield *pp;
596             int found;
597             
598             for (pp = p->u.child, found = 0; pp; pp = pp->next)
599             {
600                 if (!yaz_matchstr(subfield->u.tag.tag, pp->name))
601                 {
602                     found = 1;
603                     break;
604                 }
605             }
606             if (found)
607             {
608                 wrbuf_puts(buf, " (");
609                 subfield = cat_subfield(p->u.child, buf, subfield);
610                 wrbuf_puts(buf, ") ");
611             }
612         }
613     }
614     return subfield;
615 }
616
617 static data1_node *cat_field(struct grs_read_info *p, mc_field *pf,
618                              WRBUF buf, data1_node *field)
619 {
620     data1_node *subfield;
621     int ind1, ind2;
622     
623     if (!pf || !field)
624         return 0;
625
626     
627     if (yaz_matchstr(field->u.tag.tag, pf->name))
628         return field->next;
629
630     subfield = field->child;
631     
632     if (!subfield)
633         return field->next;
634
635     /*
636         check subfield without indicators
637     */
638     
639     if (!pf->list && subfield->which == DATA1N_data)
640     {
641         int len;
642         
643         if (pf->interval.start == -1)
644         {
645             wrbuf_puts(buf, get_data(field, &len));
646         }
647         else
648         {
649             wrbuf_write(buf, get_data(field, &len)+pf->interval.start,
650                         pf->interval.end-pf->interval.start);
651             wrbuf_puts(buf, "");
652         }
653 #if MARCOMP_DEBUG
654         yaz_log(YLOG_LOG, "cat_field(): got buffer {%s}", buf);
655 #endif
656         return field->next;
657     }
658     
659     /*
660         check indicators
661     */
662
663     ind1 = (subfield->u.tag.tag[0] == ' ') ? '_':subfield->u.tag.tag[0];
664     ind2 = (subfield->u.tag.tag[1] == ' ') ? '_':subfield->u.tag.tag[1];
665     
666     if (!(
667         ((pf->ind1[0] == '.') || (ind1 == pf->ind1[0])) &&
668         ((pf->ind2[0] == '.') || (ind2 == pf->ind2[0]))
669         ))
670     {
671 #if MARCOMP_DEBUG
672         yaz_log(YLOG_WARN, "Field %s missed -- does not match indicators", field->u.tag.tag);
673 #endif
674         return field->next;
675     }
676     
677     subfield = subfield->child;
678     
679     if (!subfield)
680         return field->next;
681
682     cat_subfield(pf->list, buf, subfield);
683
684 #if MARCOMP_DEBUG    
685     yaz_log(YLOG_LOG, "cat_field(): got buffer {%s}", buf);
686 #endif
687     
688     return field->next;    
689 }
690
691 static int is_empty(char *s)
692 {
693     char *p = s;
694     
695     for (p = s; *p; p++)
696     {
697         if (!isspace(*(unsigned char *)p))
698             return 0;
699     }
700     return 1;
701 }
702
703 static void parse_data1_tree(struct grs_read_info *p, const char *mc_stmnt,
704                              data1_node *root)
705 {
706     data1_marctab *marctab = data1_absyn_getmarctab(p->dh, root->u.root.absyn);
707     data1_node *top = root->child;
708     data1_node *field;
709     mc_context *c;
710     mc_field *pf;
711     WRBUF buf;
712     
713     c = mc_mk_context(mc_stmnt+3);
714     
715     if (!c)
716         return;
717         
718     pf = mc_getfield(c);
719     
720     if (!pf)
721     {
722         mc_destroy_context(c);
723         return;
724     }
725     buf = wrbuf_alloc();
726 #if MARCOMP_DEBUG    
727     yaz_log(YLOG_LOG, "parse_data1_tree(): statement -{%s}", mc_stmnt);
728 #endif
729     if (!yaz_matchstr(pf->name, "ldr"))
730     {
731         data1_node *new;
732 #if MARCOMP_DEBUG
733         yaz_log(YLOG_LOG,"parse_data1_tree(): try LEADER from {%d} to {%d} positions",
734             pf->interval.start, pf->interval.end);
735 #endif  
736         new = data1_mk_tag_n(p->dh, p->mem, mc_stmnt, strlen(mc_stmnt), 0, top);
737         data1_mk_text_n(p->dh, p->mem, marctab->leader+pf->interval.start,
738             pf->interval.end-pf->interval.start+1, new);
739     }
740     else
741     {
742         field=top->child;
743         
744         while(field)
745         {
746             if (!yaz_matchstr(field->u.tag.tag, pf->name))
747             {
748                 data1_node *new;
749                 char *pb;
750 #if MARCOMP_DEBUG               
751                 yaz_log(YLOG_LOG, "parse_data1_tree(): try field {%s}", field->u.tag.tag);
752 #endif          
753                 wrbuf_rewind(buf);
754                 wrbuf_puts(buf, "");
755
756                 field = cat_field(p, pf, buf, field);
757                 
758                 pb = wrbuf_buf(buf);
759                 for (pb = strtok(pb, "\n"); pb; pb = strtok(NULL, "\n"))
760                 {
761                         if (!is_empty(pb))
762                         {
763                                 new = data1_mk_tag_n(p->dh, p->mem, mc_stmnt, strlen(mc_stmnt), 0, top);
764                                 data1_mk_text_n(p->dh, p->mem, pb, strlen(pb), new);
765                         }
766                 }
767             }
768             else
769             {
770                 field = field->next;
771             }
772         }
773     }
774     mc_destroy_field(pf);
775     mc_destroy_context(c);
776     wrbuf_free(buf, 1);
777 }
778
779 data1_node *grs_read_marcxml(struct grs_read_info *p)
780 {
781     data1_node *root = grs_read_iso2709(p, 1);
782     data1_element *e;
783
784     if (!root)
785         return 0;
786         
787     for (e = data1_absyn_getelements(p->dh, root->u.root.absyn); e; e=e->next)
788     {
789         data1_tag *tag = e->tag;
790         
791         if (tag && tag->which == DATA1T_string &&
792             !yaz_matchstr(tag->value.string, "mc?"))
793                 parse_data1_tree(p, tag->value.string, root);
794     }
795     return root;
796 }
797
798 data1_node *grs_read_marc(struct grs_read_info *p)
799 {
800     data1_node *root = grs_read_iso2709(p, 0);
801     data1_element *e;
802
803     if (!root)
804         return 0;
805         
806     for (e = data1_absyn_getelements(p->dh, root->u.root.absyn); e; e=e->next)
807     {
808         data1_tag *tag = e->tag;
809         
810         if (tag && tag->which == DATA1T_string &&
811             !yaz_matchstr(tag->value.string, "mc?"))
812                 parse_data1_tree(p, tag->value.string, root);
813     }
814     return root;
815 }
816
817 static void *init_marc(Res res, RecType rt)
818 {
819     struct marc_info *p = xmalloc(sizeof(*p));
820     strcpy(p->type, "");
821     return p;
822 }
823
824 static ZEBRA_RES config_marc(void *clientData, Res res, const char *args)
825 {
826     struct marc_info *p = (struct marc_info*) clientData;
827     if (strlen(args) < sizeof(p->type))
828         strcpy(p->type, args);
829     return ZEBRA_OK;
830 }
831
832 static void destroy_marc(void *clientData)
833 {
834     struct marc_info *p = (struct marc_info*) clientData;
835     xfree (p);
836 }
837
838
839 static int extract_marc(void *clientData, struct recExtractCtrl *ctrl)
840 {
841     return zebra_grs_extract(clientData, ctrl, grs_read_marc);
842 }
843
844 static int retrieve_marc(void *clientData, struct recRetrieveCtrl *ctrl)
845 {
846     return zebra_grs_retrieve(clientData, ctrl, grs_read_marc);
847 }
848
849 static struct recType marc_type = {
850     0,
851     "grs.marc",
852     init_marc,
853     config_marc,
854     destroy_marc,
855     extract_marc,
856     retrieve_marc,
857 };
858
859 static int extract_marcxml(void *clientData, struct recExtractCtrl *ctrl)
860 {
861     return zebra_grs_extract(clientData, ctrl, grs_read_marcxml);
862 }
863
864 static int retrieve_marcxml(void *clientData, struct recRetrieveCtrl *ctrl)
865 {
866     return zebra_grs_retrieve(clientData, ctrl, grs_read_marcxml);
867 }
868
869 static struct recType marcxml_type = {
870     0,
871     "grs.marcxml",
872     init_marc,
873     config_marc,
874     destroy_marc,
875     extract_marcxml,
876     retrieve_marcxml,
877 };
878
879 RecType
880 #ifdef IDZEBRA_STATIC_GRS_MARC
881 idzebra_filter_grs_marc
882 #else
883 idzebra_filter
884 #endif
885
886 [] = {
887     &marc_type,
888     &marcxml_type,
889     0,
890 };
891