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