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