Fixed bug #2068: pkg-config trouble.
[yaz-moved-to-github.git] / src / marc_read_iso2709.c
1 /*
2  * Copyright (C) 1995-2007, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: marc_read_iso2709.c,v 1.2 2007-01-03 08:42:15 adam Exp $
6  */
7
8 /**
9  * \file marc_read_iso2709.c
10  * \brief Implements reading of MARC as ISO2709
11  */
12
13 #if HAVE_CONFIG_H
14 #include <config.h>
15 #endif
16
17 #ifdef WIN32
18 #include <windows.h>
19 #endif
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <yaz/marcdisp.h>
25 #include <yaz/wrbuf.h>
26 #include <yaz/yaz-util.h>
27
28 int yaz_marc_read_iso2709(yaz_marc_t mt, const char *buf, int bsize)
29 {
30     int entry_p;
31     int record_length;
32     int indicator_length;
33     int identifier_length;
34     int end_of_directory;
35     int base_address;
36     int length_data_entry;
37     int length_starting;
38     int length_implementation;
39
40     yaz_marc_reset(mt);
41
42     record_length = atoi_n (buf, 5);
43     if (record_length < 25)
44     {
45         yaz_marc_cprintf(mt, "Record length %d < 24", record_length);
46         return -1;
47     }
48     /* ballout if bsize is known and record_length is less than that */
49     if (bsize != -1 && record_length > bsize)
50     {
51         yaz_marc_cprintf(mt, "Record appears to be larger than buffer %d < %d",
52                          record_length, bsize);
53         return -1;
54     }
55     if (yaz_marc_get_debug(mt))
56         yaz_marc_cprintf(mt, "Record length         %5d", record_length);
57
58     yaz_marc_set_leader(mt, buf,
59                         &indicator_length,
60                         &identifier_length,
61                         &base_address,
62                         &length_data_entry,
63                         &length_starting,
64                         &length_implementation);
65
66     /* First pass. determine length of directory & base of data */
67     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
68     {
69         /* length of directory entry */
70         int l = 3 + length_data_entry + length_starting;
71         if (entry_p + l >= record_length)
72         {
73             yaz_marc_cprintf(mt, "Directory offset %d: end of record."
74                              " Missing FS char", entry_p);
75             return -1;
76         }
77         if (yaz_marc_get_debug(mt))
78         {
79             yaz_marc_cprintf(mt, "Directory offset %d: Tag %.3s",
80                              entry_p, buf+entry_p);
81         }
82         /* Check for digits in length info */
83         while (--l >= 3)
84             if (!isdigit(*(const unsigned char *) (buf + entry_p+l)))
85                 break;
86         if (l >= 3)
87         {
88             /* Not all digits, so stop directory scan */
89             yaz_marc_cprintf(mt, "Directory offset %d: Bad value for data"
90                              " length and/or length starting", entry_p);
91             break;
92         }
93         entry_p += 3 + length_data_entry + length_starting;
94     }
95     end_of_directory = entry_p;
96     if (base_address != entry_p+1)
97     {
98         yaz_marc_cprintf(mt, "Base address not at end of directory,"
99                          " base %d, end %d", base_address, entry_p+1);
100     }
101
102     /* Second pass. parse control - and datafields */
103     for (entry_p = 24; entry_p != end_of_directory; )
104     {
105         int data_length;
106         int data_offset;
107         int end_offset;
108         int i;
109         char tag[4];
110         int identifier_flag = 0;
111         int entry_p0 = entry_p;
112
113         memcpy (tag, buf+entry_p, 3);
114         entry_p += 3;
115         tag[3] = '\0';
116         data_length = atoi_n(buf+entry_p, length_data_entry);
117         entry_p += length_data_entry;
118         data_offset = atoi_n(buf+entry_p, length_starting);
119         entry_p += length_starting;
120         i = data_offset + base_address;
121         end_offset = i+data_length-1;
122
123         if (data_length <= 0 || data_offset < 0)
124             break;
125         
126         if (yaz_marc_get_debug(mt))
127         {
128             yaz_marc_cprintf(mt, "Tag: %s. Directory offset %d: data-length %d,"
129                              " data-offset %d",
130                              tag, entry_p0, data_length, data_offset);
131         }
132         if (end_offset >= record_length)
133         {
134             yaz_marc_cprintf(mt, "Directory offset %d: Data out of bounds %d >= %d",
135                              entry_p0, end_offset, record_length);
136             break;
137         }
138         
139         if (memcmp (tag, "00", 2))
140             identifier_flag = 1;  /* if not 00X assume subfields */
141         else if (indicator_length < 4 && indicator_length > 0)
142         {
143             /* Danmarc 00X have subfields */
144             if (buf[i + indicator_length] == ISO2709_IDFS)
145                 identifier_flag = 1;
146             else if (buf[i + indicator_length + 1] == ISO2709_IDFS)
147                 identifier_flag = 2;
148         }
149
150         if (identifier_flag)
151         {
152             /* datafield */
153             i += identifier_flag-1;
154             yaz_marc_add_datafield(mt, tag, buf+i, indicator_length);
155             i += indicator_length;
156
157             while (i < end_offset &&
158                     buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
159             {
160                 int code_offset = i+1;
161
162                 i ++;
163                 while (i < end_offset &&
164                         buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
165                        buf[i] != ISO2709_FS)
166                     i++;
167                 yaz_marc_add_subfield(mt, buf+code_offset, i - code_offset);
168             }
169         }
170         else
171         {
172             /* controlfield */
173             int i0 = i;
174             while (i < end_offset && 
175                 buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
176                 i++;
177             yaz_marc_add_controlfield(mt, tag, buf+i0, i-i0);
178         }
179         if (i < end_offset)
180         {
181             yaz_marc_cprintf(mt, "Separator but not at end of field length=%d",
182                     data_length);
183         }
184         if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
185         {
186             yaz_marc_cprintf(mt, "No separator at end of field length=%d",
187                     data_length);
188         }
189     }
190     return record_length;
191 }
192
193 /*
194  * Local variables:
195  * c-basic-offset: 4
196  * indent-tabs-mode: nil
197  * End:
198  * vim: shiftwidth=4 tabstop=8 expandtab
199  */
200