7e072aa85cc5c23af053ae984e5f229acf4257c3
[idzebra-moved-to-github.git] / recctrl / marcread.c
1 /*
2  * Copyright (C) 1997-1999, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: marcread.c,v $
7  * Revision 1.12  2002-04-09 14:36:53  adam
8  * Fix XML attributes for MARC reader
9  *
10  * Revision 1.11  2000/05/15 15:32:51  adam
11  * Added support for 64 bit input file support.
12  *
13  * Revision 1.10  1999/11/30 13:48:04  adam
14  * Improved installation. Updated for inclusion of YAZ header files.
15  *
16  * Revision 1.9  1999/06/25 13:47:25  adam
17  * Minor change that prevents MSVC warning.
18  *
19  * Revision 1.8  1999/05/26 07:49:14  adam
20  * C++ compilation.
21  *
22  * Revision 1.7  1999/05/20 12:57:18  adam
23  * Implemented TCL filter. Updated recctrl system.
24  *
25  * Revision 1.6  1999/02/02 14:51:27  adam
26  * Updated WIN32 code specific sections. Changed header.
27  *
28  * Revision 1.5  1997/11/18 10:03:24  adam
29  * Member num_children removed from data1_node.
30  *
31  * Revision 1.4  1997/10/27 14:34:26  adam
32  * Fixed bug - data1 root node wasn't tagged at all!
33  *
34  * Revision 1.3  1997/09/24 13:36:51  adam
35  * *** empty log message ***
36  *
37  * Revision 1.2  1997/09/17 12:19:21  adam
38  * Zebra version corresponds to YAZ version 1.4.
39  * Changed Zebra server so that it doesn't depend on global common_resource.
40  *
41  * Revision 1.1  1997/09/04 13:54:40  adam
42  * Added MARC filter - type grs.marc.<syntax> where syntax refers
43  * to abstract syntax. New method tellf in retrieve/extract method.
44  *
45  */
46 #include <stdio.h>
47 #include <ctype.h>
48 #include <assert.h>
49
50 #include <yaz/log.h>
51 #include <yaz/yaz-util.h>
52 #include <yaz/marcdisp.h>
53 #include "grsread.h"
54
55 data1_node *data1_mk_node_wp (data1_handle dh, NMEM mem, data1_node *parent)
56 {
57     data1_node *res = data1_mk_node (dh, mem);
58     
59     if (!parent)
60         res->root = res;
61     else
62     {
63         res->root = parent->root;
64         res->parent = parent;
65         if (!parent->child)
66             parent->child = parent->last_child = res;
67         else
68             parent->last_child->next = res;
69         parent->last_child = res;
70     }
71     return res;
72 }
73
74 static void destroy_data (struct data1_node *n)
75 {
76     assert (n->which == DATA1N_data);
77     xfree (n->u.data.data);
78 }
79
80 data1_node *data1_mk_node_text (data1_handle dh, NMEM mem, data1_node *parent,
81                                 const char *buf, size_t len)
82 {
83     data1_node *res = data1_mk_node_wp (dh, mem, parent);
84     res->which = DATA1N_data;
85     res->u.data.formatted_text = 0;
86     res->u.data.what = DATA1I_text;
87     res->u.data.len = len;
88     if (res->u.data.len > DATA1_LOCALDATA) {
89         res->u.data.data = (char *) xmalloc (res->u.data.len);
90         res->destroy = destroy_data;
91     }
92     else
93         res->u.data.data = res->lbuf;
94     memcpy (res->u.data.data, buf, res->u.data.len);
95     return res;
96 }
97
98 data1_node *data1_mk_node_tag (data1_handle dh, NMEM mem, data1_node *parent,
99                                const char *tag, size_t len)
100 {
101     data1_element *elem = NULL;
102     data1_node *partag = get_parent_tag(dh, parent);
103     data1_node *res;
104     data1_element *e = NULL;
105     int localtag = 0;
106     
107     res = data1_mk_node_wp (dh, mem, parent);
108
109     res->which = DATA1N_tag;
110     res->u.tag.tag = res->lbuf;
111     res->u.tag.get_bytes = -1;
112 #if DATA1_USING_XATTR
113     res->u.tag.attributes = 0;
114 #endif
115
116     if (len >= DATA1_LOCALDATA)
117         len = DATA1_LOCALDATA-1;
118
119     memcpy (res->u.tag.tag, tag, len);
120     res->u.tag.tag[len] = '\0';
121    
122     if (parent->which == DATA1N_variant)
123         return res;
124     if (partag)
125         if (!(e = partag->u.tag.element))
126             localtag = 1;
127     
128     elem = data1_getelementbytagname (dh, res->root->u.root.absyn, e,
129                                       res->u.tag.tag);
130     res->u.tag.element = elem;
131     res->u.tag.node_selected = 0;
132     res->u.tag.make_variantlist = 0;
133     res->u.tag.no_data_requested = 0;
134     return res;
135 }
136
137 #define MARC_DEBUG 0
138
139 data1_node *grs_read_marc (struct grs_read_info *p)
140 {
141     char buf[100000];
142     int entry_p;
143     int record_length;
144     int indicator_length;
145     int identifier_length;
146     int base_address;
147     int length_data_entry;
148     int length_starting;
149     int length_implementation;
150     int read_bytes;
151 #if MARC_DEBUG
152     FILE *outf = stdout;
153 #endif
154
155     data1_node *res_root;
156     data1_absyn *absyn;
157     char *absynName;
158     data1_marctab *marctab;
159
160     if ((*p->readf)(p->fh, buf, 5) != 5)
161         return NULL;
162     record_length = atoi_n (buf, 5);
163     if (record_length < 25)
164     {
165         logf (LOG_WARN, "MARC record length < 25, is %d", record_length);
166         return NULL;
167     }
168     /* read remaining part - attempt to read one byte furhter... */
169     read_bytes = (*p->readf)(p->fh, buf+5, record_length-4);
170     if (read_bytes < record_length-5)
171     {
172         logf (LOG_WARN, "Couldn't read whole MARC record");
173         return NULL;
174     }
175     if (read_bytes == record_length - 4)
176     {
177         off_t cur_offset = (*p->tellf)(p->fh);
178         if (cur_offset <= 27)
179             return NULL;
180         if (p->endf)
181             (*p->endf)(p->fh, cur_offset - 1);
182     }
183     absynName = p->type;
184     logf (LOG_DEBUG, "absynName = %s", absynName);
185     if (!(absyn = data1_get_absyn (p->dh, absynName)))
186     {
187         logf (LOG_WARN, "Unknown abstract syntax: %s", absynName);
188         return NULL;
189     }
190     res_root = data1_mk_node_wp (p->dh, p->mem, NULL);
191     res_root->which = DATA1N_root;
192     res_root->u.root.type = (char *) nmem_malloc (p->mem, strlen(absynName)+1);
193     strcpy (res_root->u.root.type, absynName);
194     res_root->u.root.absyn = absyn;
195
196     marctab = absyn->marc;
197
198     if (marctab && marctab->force_indicator_length >= 0)
199         indicator_length = marctab->force_indicator_length;
200     else
201         indicator_length = atoi_n (buf+10, 1);
202     if (marctab && marctab->force_identifier_length >= 0)
203         identifier_length = marctab->force_identifier_length;
204     else
205         identifier_length = atoi_n (buf+11, 1);
206     base_address = atoi_n (buf+12, 4);
207
208
209     length_data_entry = atoi_n (buf+20, 1);
210     length_starting = atoi_n (buf+21, 1);
211     length_implementation = atoi_n (buf+22, 1);
212
213     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
214         entry_p += 3+length_data_entry+length_starting;
215     base_address = entry_p+1;
216     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
217     {
218         int data_length;
219         int data_offset;
220         int end_offset;
221         int i, i0;
222         char tag[4];
223         data1_node *res;
224         data1_node *parent = res_root;
225
226         memcpy (tag, buf+entry_p, 3);
227         entry_p += 3;
228         tag[3] = '\0';
229
230         /* generate field node */
231         res = data1_mk_node_tag (p->dh, p->mem, res_root, tag, 3);
232
233 #if MARC_DEBUG
234         fprintf (outf, "%s ", tag);
235 #endif
236         data_length = atoi_n (buf+entry_p, length_data_entry);
237         entry_p += length_data_entry;
238         data_offset = atoi_n (buf+entry_p, length_starting);
239         entry_p += length_starting;
240         i = data_offset + base_address;
241         end_offset = i+data_length-1;
242
243         if (memcmp (tag, "00", 2) && indicator_length)
244         {
245             /* generate indicator node */
246 #if MARC_DEBUG
247             int j;
248 #endif
249             res = data1_mk_node_tag (p->dh, p->mem, res, buf+i,
250                                      indicator_length);
251 #if MARC_DEBUG
252             for (j = 0; j<indicator_length; j++)
253                 fprintf (outf, "%c", buf[j+i]);
254 #endif
255             i += indicator_length;
256         }
257         parent = res;
258         /* traverse sub fields */
259         i0 = i;
260         while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset)
261         {
262             if (memcmp (tag, "00", 2) && identifier_length)
263             {
264                 data1_node *res =
265                     data1_mk_node_tag (p->dh, p->mem, parent,
266                                        buf+i+1, identifier_length-1);
267 #if MARC_DEBUG
268                 fprintf (outf, " $"); 
269                 for (j = 1; j<identifier_length; j++)
270                     fprintf (outf, "%c", buf[j+i]);
271                 fprintf (outf, " ");
272 #endif
273                 i += identifier_length;
274                 i0 = i;
275                 while (buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
276                        buf[i] != ISO2709_FS && i < end_offset)
277                 {
278 #if MARC_DEBUG
279                     fprintf (outf, "%c", buf[i]);
280 #endif
281                     i++;
282                 }
283                 data1_mk_node_text (p->dh, p->mem, res, buf + i0, i - i0);
284                 i0 = i;
285             }
286             else
287             {
288 #if MARC_DEBUG
289                 fprintf (outf, "%c", buf[i]);
290 #endif
291                 i++;
292             }
293         }
294         if (i > i0)
295         {
296             data1_node *res = data1_mk_node_tag (p->dh, p->mem,
297                                                  parent, "@", 1);
298             data1_mk_node_text (p->dh, p->mem, res, buf + i0, i - i0);
299         }
300 #if MARC_DEBUG
301         fprintf (outf, "\n");
302         if (i < end_offset)
303             fprintf (outf, "-- separator but not at end of field\n");
304         if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
305             fprintf (outf, "-- no separator at end of field\n");
306 #endif
307     }
308     return res_root;
309
310
311 static void *grs_init_marc(void)
312 {
313     return 0;
314 }
315
316 static void grs_destroy_marc(void *clientData)
317 {
318 }
319
320 static struct recTypeGrs marc_type = {
321     "marc",
322     grs_init_marc,
323     grs_destroy_marc,
324     grs_read_marc
325 };
326
327 RecTypeGrs recTypeGrs_marc = &marc_type;