IDZEBRA  2.0.54
mod_grs_marc.c
Go to the documentation of this file.
1 /* This file is part of the Zebra server.
2  Copyright (C) 2004-2013 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 
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 
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);
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  }
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 
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  {
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);
794  wrbuf_destroy(buf);
795 }
796 
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 
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,
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,
895 };
896 
897 RecType
898 #if IDZEBRA_STATIC_GRS_MARC
899 idzebra_filter_grs_marc
900 #else
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