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