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