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