Fixed bug #830: pkg-config support. YAZ installs yaz.pc for Debian
[yaz-moved-to-github.git] / src / marc_read_line.c
1 /*
2  * Copyright (C) 1995-2007, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: marc_read_line.c,v 1.3 2007-01-03 08:42:15 adam Exp $
6  */
7
8 /**
9  * \file marc_read_line.c
10  * \brief Implements reading of MARC in line format
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 <assert.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <ctype.h>
25
26 #include <yaz/marcdisp.h>
27 #include <yaz/wrbuf.h>
28 #include <yaz/yaz-util.h>
29
30 int yaz_gets(int (*getbyte)(void *client_data),
31              void (*ungetbyte)(int b, void *client_data),
32              void *client_data,
33              char *buf, size_t len)
34 {
35     size_t sz = 0;
36     int ch = getbyte(client_data);
37     while (ch != '\0' && ch != '\r' && ch != '\n')
38     {
39         if (sz < len-1)
40             buf[sz++] = ch;
41         ch = getbyte(client_data);
42     }
43     if (ch == '\r')
44     {
45         ch = getbyte(client_data);
46         if (ch != '\n' && ch != '\0')
47             ungetbyte(ch, client_data);
48     }
49     else if (ch == '\n')
50     {
51         ch = getbyte(client_data);
52         if (ch != '\r' && ch != '\0')
53             ungetbyte(ch, client_data);
54     }
55     buf[sz] = '\0';
56     if (sz)
57         return 1;
58     return 0;
59 }
60     
61 int yaz_marc_read_line(yaz_marc_t mt,
62                        int (*getbyte)(void *client_data),
63                        void (*ungetbyte)(int b, void *client_data),
64                        void *client_data)
65 {
66     int indicator_length;
67     int identifier_length;
68     int base_address;
69     int length_data_entry;
70     int length_starting;
71     int length_implementation;
72     int marker_ch = 0;
73     int marker_skip = 0;
74     int header_created = 0;
75     char line[4096];
76
77     yaz_marc_reset(mt);
78
79     while (yaz_gets(getbyte, ungetbyte, client_data, line, sizeof(line)))
80     {
81         int val;
82         size_t line_len = strlen(line);
83         /* see if have leader lines of the form:
84            00366nam  22001698a 4500
85         */
86         if (line_len == 0)       /* empty line indicates end of record */
87         {
88             if (header_created)
89                 break;
90         }
91         else if (line[0] == '$') /* indicates beginning/end of record */
92         {
93             if (header_created)
94                 break;
95         }
96         else if (line[0] == '(') /* annotation, skip it */
97             ;
98         else if (line_len == 24 && atoi_n_check(line, 5, &val) && val >= 24)
99         {
100             if (header_created)
101                 break;
102             yaz_marc_set_leader(mt, line,
103                                 &indicator_length,
104                                 &identifier_length,
105                                 &base_address,
106                                 &length_data_entry,
107                                 &length_starting,
108                                 &length_implementation);
109             header_created = 1;
110         }
111         else if (line_len > 5 && memcmp(line, "    ", 4) == 0)
112         {  /* continuation line */
113             ;
114         }
115         else if (line_len > 5 && line[3] == ' ')
116         {
117             char tag[4];
118             char *datafield_start = line+6;
119             marker_ch = 0;
120             marker_skip = 0;
121
122             memcpy(tag, line, 3);
123             tag[3] = '\0';
124             if (line_len >= 8) /* control - or datafield ? */
125             {
126                 if (*datafield_start == ' ')
127                     datafield_start++;  /* skip blank after indicator */
128
129                 if (strchr("$_*", *datafield_start))
130                 {
131                     marker_ch = *datafield_start;
132                     if (datafield_start[2] == ' ')
133                         marker_skip = 1; /* subfields has blank before data */
134                 }
135             }
136             if (!header_created)
137             {
138                 const char *leader = "01000cam  2200265 i 4500";
139
140                 yaz_marc_set_leader(mt, leader,
141                                     &indicator_length,
142                                     &identifier_length,
143                                     &base_address,
144                                     &length_data_entry,
145                                     &length_starting,
146                                     &length_implementation);
147                 header_created = 1;
148             }
149
150             if (marker_ch == 0)
151             {   /* control field */
152                 yaz_marc_add_controlfield(mt, tag, line+4, strlen(line+4));
153             }
154             else
155             {   /* data field */
156                 const char *indicator = line+4;
157                 int indicator_len = 2;
158                 char *cp = datafield_start;
159
160                 yaz_marc_add_datafield(mt, tag, indicator, indicator_len);
161                 for (;;)
162                 {
163                     char *next;
164                     size_t len;
165                     
166                     assert(cp[0] == marker_ch);
167                     cp++;
168                     next = cp;
169                     while ((next = strchr(next, marker_ch)))
170                     {
171                         if ((next[1] >= 'A' && next[1] <= 'Z')
172                             ||(next[1] >= 'a' && next[1] <= 'z'))
173                         {
174                             if (!marker_skip)
175                                 break;
176                             else if (next[2] == ' ')
177                                 break;
178                         }
179                         next++;
180                     }
181                     len = strlen(cp);
182                     if (next)
183                         len = next - cp - marker_skip;
184
185                     if (marker_skip)
186                     {
187                         /* remove ' ' after subfield marker */
188                         char *cp_blank = strchr(cp, ' ');
189                         if (cp_blank)
190                         {
191                             len--;
192                             while (cp_blank != cp)
193                             {
194                                 cp_blank[0] = cp_blank[-1];
195                                 cp_blank--;
196                             }
197                             cp++;
198                         }
199                     }
200                     assert(len >= 0);
201                     assert(len < 399);
202                     yaz_marc_add_subfield(mt, cp, len);
203                     if (!next)
204                         break;
205                     cp = next;
206                 }
207             }
208         }
209     }
210     if (!header_created)
211         return -1;
212     return 0;
213 }
214
215 /*
216  * Local variables:
217  * c-basic-offset: 4
218  * indent-tabs-mode: nil
219  * End:
220  * vim: shiftwidth=4 tabstop=8 expandtab
221  */
222