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