Bug fix: GRS records wasn't recognized.
[ir-tcl-moved-to-github.git] / marc.c
1 /*
2  * IR toolkit for tcl/tk
3  * (c) Index Data 1995
4  * See the file LICENSE for details.
5  * Sebastian Hammer, Adam Dickmeiss
6  *
7  * $Log: marc.c,v $
8  * Revision 1.8  1995-11-14 16:48:00  adam
9  * Bug fix: record extraction in line mode merged lines with same tag.
10  *
11  * Revision 1.7  1995/11/09  15:24:02  adam
12  * Allow charsets [..] in record match.
13  *
14  * Revision 1.6  1995/08/28  12:21:22  adam
15  * Removed lines and list as synonyms of list in MARC extractron.
16  * Configure searches also for tk4.0 / tcl7.4.
17  *
18  * Revision 1.5  1995/06/30  12:39:26  adam
19  * Bug fix: loadFile didn't set record type.
20  * The MARC routines are a little less strict in the interpretation.
21  * Script display.tcl replaces the old marc.tcl.
22  * New interactive script: shell.tcl.
23  *
24  * Revision 1.4  1995/06/22  13:15:09  adam
25  * Feature: SUTRS. Setting getSutrs implemented.
26  * Work on display formats.
27  * Preferred record syntax can be set by the user.
28  *
29  * Revision 1.3  1995/05/29  08:44:26  adam
30  * Work on delete of objects.
31  *
32  * Revision 1.2  1995/05/26  11:44:11  adam
33  * Bugs fixed. More work on MARC utilities and queries. Test
34  * client is up-to-date again.
35  *
36  * Revision 1.1  1995/05/26  08:54:19  adam
37  * New MARC utilities. Uses prefix query.
38  *
39  */
40
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <ctype.h>
44 #include <assert.h>
45
46 #include "ir-tclp.h"
47
48 #define ISO2709_RS 035
49 #define ISO2709_FS 036
50 #define ISO2709_IDFS 037
51
52 static int atoi_n (const char *buf, int len)
53 {
54     int val = 0;
55
56     if (!isdigit (buf[len-1]))
57         return 0;
58     while (--len >= 0)
59     {
60         if (isdigit (*buf))
61             val = val*10 + (*buf - '0');
62         buf++;
63     }
64     return val;
65 }
66
67 static int marc_compare (const char *f, const char *p)
68 {
69     int ch;
70
71     if (*p == '*')
72         return 0;
73     if (!f)
74         return -*p;
75     for (; (ch = *p) && *f; f++, p++)
76         switch (*p)
77         {
78         case '*':
79             return 0;
80         case '?':
81             ch = *f;
82             break;
83         case '[':
84             while (1)
85                 if (!*++p)
86                     break;
87                 else if (*p == ']')
88                 {
89                     p++;
90                     break;
91                 }
92                 else if (*p == *f)
93                     ch = *p;
94             if (ch != *p)
95                 return *f - ch;
96             break;
97         default:
98             if (ch != *f)
99                 return *f - ch;
100         }
101     return *f - ch;
102 }
103
104 char *ir_tcl_fread_marc (FILE *inf, size_t *size)
105 {
106     char length[5];
107     char *buf;
108
109     if (fread (length, 1, 5, inf) != 5)
110         return NULL;
111     *size = atoi_n (length, 5);
112     if (*size <= 6)
113         return NULL;
114     if (!(buf = malloc (*size+1)))
115         return NULL;
116     if (fread (buf+5, 1, *size-5, inf) != (*size-5))
117     {
118         free (buf);
119         return NULL;
120     }
121     memcpy (buf, length, 5);
122     buf[*size=0] = '\0';
123     return buf;
124 }
125
126 int ir_tcl_get_marc (Tcl_Interp *interp, const char *buf, 
127                      int argc, char **argv)
128 {
129     int entry_p;
130     int record_length;
131     int indicator_length;
132     int identifier_length;
133     int base_address;
134     int length_data_entry;
135     int length_starting;
136     int length_implementation;
137     char ptag[4];
138     int mode = 0;
139
140     if (!strcmp (argv[3], "field"))
141         mode = 'f';
142     else if (!strcmp (argv[3], "line"))
143         mode = 'l';
144     else
145     {
146         Tcl_AppendResult (interp, "Unknown MARC extract mode", NULL);
147         return TCL_ERROR;
148     }
149     if (!buf)
150     {
151         Tcl_AppendResult (interp, "Not a MARC record", NULL);
152         return TCL_ERROR;
153     }
154     record_length = atoi_n (buf, 5);
155     if (record_length < 25)
156     {
157         Tcl_AppendResult (interp, "Not a MARC record", NULL);
158         return TCL_ERROR;
159     }
160     indicator_length = atoi_n (buf+10, 1);
161     identifier_length = atoi_n (buf+11, 1);
162     base_address = atoi_n (buf+12, 4);
163
164     length_data_entry = atoi_n (buf+20, 1);
165     length_starting = atoi_n (buf+21, 1);
166     length_implementation = atoi_n (buf+22, 1);
167
168     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
169         entry_p += 3+length_data_entry+length_starting;
170     base_address = entry_p+1;
171     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
172     {
173         int data_length;
174         int data_offset;
175         int end_offset;
176         int i, j;
177         char tag[4];
178         char indicator[128];
179         char identifier[128];
180
181         *ptag = '\0';
182         memcpy (tag, buf+entry_p, 3);
183         entry_p += 3;
184         tag[3] = '\0';
185         data_length = atoi_n (buf+entry_p, length_data_entry);
186         entry_p += length_data_entry;
187         data_offset = atoi_n (buf+entry_p, length_starting);
188         entry_p += length_starting;
189         i = data_offset + base_address;
190         end_offset = i+data_length-1;
191         *indicator = '\0';
192         if (memcmp (tag, "00", 2) && indicator_length)
193         {
194             for (j = 0; j<indicator_length; j++)
195                 indicator[j] = buf[i++];
196             indicator[j] = '\0';
197         }
198         if (marc_compare (tag, argv[4]) || marc_compare (indicator, argv[5]))
199             continue;
200         while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset)
201         {
202             int i0;
203
204             if (memcmp (tag, "00", 2) && identifier_length)
205             {
206                 i++;
207                 for (j = 1; j<identifier_length; j++)
208                     identifier[j-1] = buf[i++];
209                 identifier[j-1] = '\0';
210                 for (i0 = i; buf[i] != ISO2709_RS && 
211                              buf[i] != ISO2709_IDFS &&
212                              buf[i] != ISO2709_FS && i < end_offset; 
213                              i++)
214                     ;
215             }
216             else
217             {
218                 for (i0 = i; buf[i] != ISO2709_RS && 
219                              buf[i] != ISO2709_FS && i < end_offset; 
220                              i++)
221                     ;
222                 *identifier = '\0';
223             }
224             if (marc_compare (identifier, argv[6])==0)
225             {
226                 char *data = malloc (i-i0+1);
227              
228                 memcpy (data, buf+i0, i-i0);
229                 data[i-i0] = '\0';
230                 if (mode == 'l')
231                 {
232                     if (strcmp (tag, ptag))
233                     {
234                         if (*ptag)
235                             Tcl_AppendResult (interp, "}} ", NULL);
236                         if (!*indicator)
237                             Tcl_AppendResult (interp, "{", tag, " {} {", NULL);
238                         else
239                             Tcl_AppendResult (interp, "{", tag, " {",
240                                               indicator, "} {", NULL);
241                         strcpy (ptag, tag);
242                     }
243                     if (!*identifier)
244                         Tcl_AppendResult (interp, "{{}", NULL);
245                     else
246                         Tcl_AppendResult (interp, "{", identifier, NULL);
247                     Tcl_AppendElement (interp, data);
248                     Tcl_AppendResult (interp, "} ", NULL);
249                 }
250                 else
251                     Tcl_AppendElement (interp, data);
252                 free (data);
253             }
254         }
255         if (mode == 'l' && *ptag)
256             Tcl_AppendResult (interp, "}} ", NULL);
257         if (i < end_offset)
258             logf (LOG_WARN, "MARC: separator but not at end of field");
259         if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
260             logf (LOG_WARN, "MARC: no separator at end of field");
261     }
262     return TCL_OK;
263 }
264
265
266