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