LICENSE file in egate root.
[egate.git] / util / iso2709.c
1 /*
2  * Iso2709 record management
3  *
4  * Europagate, 1994-1995.
5  *
6  * $Log: iso2709.c,v $
7  * Revision 1.15  1995/03/31 10:42:41  adam
8  * Bug fix.
9  *
10  * Revision 1.14  1995/03/30  14:22:18  adam
11  * More work on new MARC anchor functions.
12  *
13  * Revision 1.13  1995/03/30  07:33:32  adam
14  * New 2709 function: iso2709_mk.
15  * First implementation of iso2709_a_insert.
16  *
17  * Revision 1.12  1995/03/29  16:08:56  adam
18  * Better error recovery when using bad records.
19  *
20  * Revision 1.11  1995/03/28  16:07:07  adam
21  * New function: iso2709_out. This function is the reverse of iso2709_cvt.
22  *
23  * Revision 1.10  1995/03/10  09:10:56  adam
24  * Removed dbc2709_cvt function. Makes heuristic guess for DBC2709 records.
25  *
26  * Revision 1.9  1995/03/08  12:36:39  adam
27  * New function: dbc2709_cvt.
28  *
29  * Revision 1.8  1995/03/08  12:03:15  adam
30  * Hack: When tags 00? are used, every separator (DC[1-3]) marks
31  * the end of the data field.
32  *
33  * Revision 1.7  1995/02/22  21:28:03  adam
34  * Changed header.
35  *
36  * Revision 1.5  1995/02/22  15:24:14  adam
37  * Function iso2709_cvt makes a litte check for the format. It returns
38  * NULL if the buffer parameter can never be a MARC record.
39  *
40  * Revision 1.4  1995/02/15  17:45:44  adam
41  * Bug fix in iso2709 module.
42  *
43  * Revision 1.3  1995/02/10  17:05:18  adam
44  * New function iso2709_display to display MARC records in a
45  * line-by-line format. The iso2709_cvt function no longer
46  * prints the record to stderr.
47  *
48  * Revision 1.2  1995/02/10  16:50:32  adam
49  * Indicator field moved to 'struct iso2709_dir' from 'struct
50  * iso2709_field'.
51  * Function iso2709_rm implemented - to delete a MARC record.
52  *
53  * Revision 1.1.1.1  1995/02/09  17:27:11  adam
54  * Initial version of email gateway under CVS control.
55  *
56  */
57
58 #include <stdlib.h>
59 #include <string.h>
60 #include <stdio.h>
61 #include <assert.h>
62 #include <ctype.h>
63
64 #include <iso2709p.h>
65
66 static int atoin (const char *buf, int n)
67 {
68     int val = 0;
69     while (--n >= 0)
70     {
71         if (isdigit(*buf))
72             val = val*10 + (*buf - '0');
73         buf++;
74     }
75     return val;
76 }
77
78 static void strncpyx (char *d, const char *s, int n)
79 {
80     while (--n >= 0 && *s)
81         if (*s != ISO2709_IDFS)
82             *d++ = *s++;
83         else
84         {
85             *d++ = ' ';
86             s++;
87         }
88     *d = '\0';
89 }
90
91 char *iso2709_read (FILE *inf)
92 {
93     char length_str[5];
94     int size;
95     char *buf;
96
97     if (fread (length_str, 1, 5, inf) != 5)
98         return NULL;
99     size = atoin (length_str, 5);
100     if (size <= 6)
101         return NULL;
102     if (!(buf = malloc (size+1)))
103         return NULL;
104     if (fread (buf+5, 1, size-5, inf) != (size-5))
105     {
106         free (buf);
107         return NULL;
108     }
109     memcpy (buf, length_str, 5);
110     buf[size] = '\0';
111     return buf;
112 }
113
114 Iso2709Rec iso2709_mk (void)
115 {
116     Iso2709Rec p;
117
118     if (!(p = malloc (sizeof(*p))))
119         return NULL;
120
121     p->record_length = 0;
122     strncpyx (p->record_status, " ", 1);
123     strncpyx (p->implementation_codes, "    ", 4);
124     p->indicator_length = 2;
125     p->identifier_length = 2;
126     p->base_address = 0;
127     strncpyx (p->user_systems, "   ", 3);
128     p->length_data_entry = 4;
129     p->length_starting = 5;
130     p->length_implementation = 0;
131     strncpyx (p->future_use, " ", 1);
132
133     p->directory = NULL;
134     return p;
135 }
136
137 Iso2709Rec iso2709_cvt (const char *buf)
138 {
139     struct iso2709_dir **dpp, *dp;
140     int pos = 24;
141     Iso2709Rec p;
142
143     if (!(p = malloc (sizeof(*p))))
144         return NULL;
145
146     /* deal with record label (24 characters) */
147     p->record_length = atoin (buf, 5);
148     strncpyx (p->record_status, buf+5, 1);
149     strncpyx (p->implementation_codes, buf+6, 4);
150     p->indicator_length = atoin (buf+10, 1);
151     p->identifier_length = atoin (buf+11, 1);
152     p->base_address = atoin (buf+12, 4);
153     strncpyx (p->user_systems, buf+17, 3);
154
155     if (p->record_length < 26)
156     {
157         free (p);
158         return NULL;
159     }
160     p->length_data_entry = atoin (buf+20, 1);
161     p->length_starting = atoin (buf+21, 1);
162     p->length_implementation = atoin (buf+22, 1);
163     strncpyx (p->future_use, buf+23, 1);
164
165     /* deal with directory */
166     dpp = &p->directory;
167     *dpp = NULL;
168     while (buf[pos] != ISO2709_FS)
169     {
170         if (!(*dpp = malloc (sizeof(**dpp))))
171         {
172             iso2709_rm (p);
173             return NULL;
174         }
175         (*dpp)->next = NULL;
176         (*dpp)->fields = NULL;
177         (*dpp)->indicator = NULL;
178         strncpyx ((*dpp)->tag, buf+pos, 3);
179         pos += 3;
180         (*dpp)->length = atoin (buf+pos, p->length_data_entry);
181         pos += p->length_data_entry;
182         (*dpp)->offset = atoin (buf+pos, p->length_starting);
183         pos += p->length_starting + p->length_implementation;
184
185         dpp = &(*dpp)->next;
186         if (pos > p->record_length)
187         {
188             iso2709_rm (p);
189             return NULL;
190         }
191     }
192     pos++;
193     /* deal with datafields */
194     for (dp = p->directory; dp; dp = dp->next)
195     {
196         int identifier_flag;
197         struct iso2709_field **fpp;
198         int dpos = pos+dp->offset;
199         int epos = pos+dp->offset+dp->length-1;
200
201         if (epos <= dpos)
202         {
203             iso2709_rm (p);
204             return NULL;
205         }
206         fpp = &dp->fields;
207
208         if (!(*fpp = malloc (sizeof(**fpp))))
209         {
210             iso2709_rm (p);
211             return NULL;
212         }
213         (*fpp)->next = NULL;
214
215         identifier_flag = 1;
216         if (p->indicator_length)
217         {
218 #if STUPID_ISO_DBC
219             if (buf[dpos+p->indicator_length] != ISO2709_IDFS)
220                 identifier_flag = 0;
221 #else
222             if (!memcmp (dp->tag, "00", 2))
223                 identifier_flag = 0;
224 #endif
225         }
226         else if (!memcmp (dp->tag, "00", 2))
227                 identifier_flag = 0;
228         if (identifier_flag && p->indicator_length)
229         {
230             if (!(dp->indicator = malloc (p->indicator_length+1)))
231             {
232                 iso2709_rm (p);
233                 return NULL;
234             }
235             strncpyx (dp->indicator, buf+dpos, p->indicator_length);
236             dpos += p->indicator_length;
237         }
238         else
239             dp->indicator = NULL;
240         while (1)
241         {
242             int dpos_n;
243             if (p->identifier_length && identifier_flag)
244             {
245                 strncpyx ((*fpp)->identifier, buf+dpos+1,
246                           p->identifier_length-1);
247                 dpos_n = dpos += p->identifier_length;
248                 while (buf[dpos_n] != ISO2709_FS && buf[dpos_n] != ISO2709_RS
249                        && buf[dpos_n] != ISO2709_IDFS && dpos_n < epos)
250                     dpos_n++;
251             }
252             else
253             {
254                 *(*fpp)->identifier = '\0';
255                 dpos_n = dpos;
256                 while (buf[dpos_n] != ISO2709_FS && buf[dpos_n] != ISO2709_RS
257                        && dpos_n < epos)
258                     dpos_n++;
259             }
260             if (!((*fpp)->data = malloc (dpos_n - dpos + 1)))
261             {
262                 iso2709_rm (p);
263                 return NULL;
264             }
265             strncpyx ((*fpp)->data, buf+dpos, dpos_n - dpos);
266             dpos = dpos_n;
267             
268             if (dpos >= epos)
269             {
270                 if (buf[dpos] != ISO2709_FS && buf[dpos] != ISO2709_RS)
271                     fprintf (stderr, "Missing separator at end of field "
272                              "in %s %s\n", dp->tag, (*fpp)->identifier);
273                 break;
274             }
275             if (buf[dpos] == ISO2709_FS || buf[dpos] == ISO2709_RS)
276             {
277                 fprintf (stderr, "Unexpected separator inside field %s %s\n",
278                          dp->tag, (*fpp)->identifier);
279                 break;
280             }
281             fpp = &(*fpp)->next;
282             if (!(*fpp = malloc (sizeof(**fpp))))
283             {
284                 iso2709_rm (p);
285                 return NULL;
286             }
287             (*fpp)->next = NULL;
288         }
289     }
290     return p;
291 }
292
293 void iso2709_rm (Iso2709Rec rec)
294 {
295     struct iso2709_dir *dir, *dir1;
296
297     for (dir = rec->directory; dir; dir = dir1)
298     {
299         struct iso2709_field *field, *field1;
300
301         for (field = dir->fields; field; field = field1)
302         {
303             free (field->data);
304             field1 = field->next;
305             free (field);
306         }
307         free (dir->indicator);
308         dir1 = dir->next;
309         free (dir);
310     }
311 }
312