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