Minor adjustments
[yaz-moved-to-github.git] / retrieval / d1_map.c
1 /*
2  * Copyright (c) 1995, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: d1_map.c,v $
7  * Revision 1.2  1995-11-01 13:54:46  quinn
8  * Minor adjustments
9  *
10  * Revision 1.1  1995/11/01  11:56:08  quinn
11  * Added Retrieval (data management) functions en masse.
12  *
13  *
14  */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19
20 #include <oid.h>
21 #include <xmalloc.h>
22 #include <log.h>
23 #include <readconf.h>
24
25 #include <data1.h>
26 #include "d1_map.h"
27
28 data1_maptab *data1_read_maptab(char *file)
29 {
30     data1_maptab *res = xmalloc(sizeof(*res));
31     FILE *f;
32     int argc;
33     char *argv[50], line[512];
34     data1_mapunit **mapp;
35
36     if (!(f = fopen(file, "r")))
37     {
38         logf(LOG_WARN|LOG_ERRNO, "%s", file);
39         return 0;
40     }
41
42     res->name = 0;
43     res->target_absyn_ref = ODR_NONE;
44     res->map = 0;
45     mapp = &res->map;
46     res->next = 0;
47
48     while ((argc = readconf_line(f, line, 512, argv, 50)))
49         if (!strcmp(argv[0], "targetref"))
50         {
51             if (argc != 2)
52             {
53                 logf(LOG_WARN, "%s: one argument required for targetref",
54                     file);
55                 continue;
56             }
57             if ((res->target_absyn_ref = oid_getvalbyname(argv[1])) == ODR_NONE)
58             {
59                 logf(LOG_WARN, "%s: Unknown reference '%s'", file, argv[1]);
60                 continue;
61             }
62         }
63         else if (!strcmp(argv[0], "targetname"))
64         {
65             if (argc != 2)
66             {
67                 logf(LOG_WARN, "%s: one argument required for targetref",
68                     file);
69                 continue;
70             }
71             res->target_absyn_name = xmalloc(strlen(argv[1])+1);
72             strcpy(res->target_absyn_name, argv[1]);
73         }
74         else if (!strcmp(argv[0], "name"))
75         {
76             if (argc != 2)
77             {
78                 logf(LOG_WARN, "%s: one argument required for name",
79                     file);
80                 continue;
81             }
82             res->name = xmalloc(strlen(argv[1])+1);
83             strcpy(res->name, argv[1]);
84         }
85         else if (!strcmp(argv[0], "map"))
86         {
87             data1_maptag **mtp;
88             char *ep, *path = argv[2];
89
90             if (argc < 3)
91             {
92                 logf(LOG_WARN, "%s: At least 2 arguments required for map",
93                     file);
94                 continue;
95             }
96             *mapp = xmalloc(sizeof(**mapp));
97             (*mapp)->next = 0;
98             if (argc > 3 && !data1_matchstr(argv[3], "nodata"))
99                 (*mapp)->no_data = 1;
100             else
101                 (*mapp)->no_data = 0;
102             (*mapp)->source_element_name = xmalloc(strlen(argv[1])+1);
103             strcpy((*mapp)->source_element_name, argv[1]);
104             mtp = &(*mapp)->target_path;
105             if (*path == '/')
106                 path++;
107             for (ep = strchr(path, '/'); path; (void)((path = ep) &&
108                 (ep = strchr(path, '/'))))
109             {
110                 int type, np;
111                 char valstr[512], parm[512];
112                 int numval;
113
114                 if (ep)
115                     ep++;
116                 if ((np = sscanf(path, "(%d,%[^)]):%[^/]", &type, valstr,
117                     parm)) < 2)
118                 {
119                     logf(LOG_WARN, "%s: Syntax error in map directive: %s",
120                         file, argv[2]);
121                     fclose(f);
122                     return 0;
123                 }
124                 *mtp = xmalloc(sizeof(**mtp));
125                 (*mtp)->next = 0;
126                 (*mtp)->type = type;
127                 if (np > 2 && !data1_matchstr(parm, "new"))
128                     (*mtp)->new_field = 1;
129                 else
130                     (*mtp)->new_field = 0;
131 #if 0
132                 if ((numval = atoi(valstr)))
133                 {
134                     (*mtp)->which = D1_MAPTAG_numeric;
135                     (*mtp)->value.numeric = numval;
136                 }
137                 else
138                 {
139 #endif
140                     (*mtp)->which = D1_MAPTAG_string;
141                     (*mtp)->value.string = xmalloc(strlen(valstr)+1);
142                     strcpy((*mtp)->value.string, valstr);
143 #if 0
144                 }
145 #endif 
146                 mtp = &(*mtp)->next;
147             }
148             mapp = &(*mapp)->next;
149         }
150         else 
151             logf(LOG_WARN, "%s: Unknown directive '%s'", argv[0]);
152
153     fclose(f);
154     return res;
155 }
156
157 /*
158  * Locate node with givel elementname.
159  * NOTE: This is stupid - we don't find repeats this way.
160  */
161 static data1_node *find_node(data1_node *p, char *elementname)
162 {
163     data1_node *c, *r;
164
165     for (c = p->child; c; c = c->next)
166         if (c->which == DATA1N_tag && c->u.tag.element &&
167             !data1_matchstr(c->u.tag.element->name, elementname))
168             return c;
169         else if ((r = find_node(c, elementname)))
170             return r;
171     return 0;
172 }
173
174 /*
175  * See if the node n is equivalent to the tag t.
176  */
177 static int tagmatch(data1_node *n, data1_maptag *t)
178 {
179     if (n->which != DATA1N_tag)
180         return 0;
181     if (n->u.tag.element)
182     {
183         if (n->u.tag.element->tag->tagset->type != t->type)
184             return 0;
185         if (n->u.tag.element->tag->which == DATA1T_numeric)
186         {
187             if (t->which != D1_MAPTAG_numeric)
188                 return 0;
189             if (n->u.tag.element->tag->value.numeric != t->value.numeric)
190                 return 0;
191         }
192         else
193         {
194             if (t->which != D1_MAPTAG_string)
195                 return 0;
196             if (data1_matchstr(n->u.tag.element->tag->value.string,
197                 t->value.string))
198                 return 0;
199         }
200     }
201     else /* local tag */
202     {
203         char str[10];
204
205         if (t->type != 3)
206             return 0;
207         if (t->which == D1_MAPTAG_numeric)
208             sprintf(str, "%d", t->value.numeric);
209         else
210             strcpy(str, t->value.string);
211         if (data1_matchstr(n->u.tag.tag, str))
212             return 0;
213     }
214     return 1;
215 }
216
217 static int map_children(data1_node *n, data1_maptab *map, data1_node *res)
218 {
219     data1_node *c;
220     data1_mapunit *m;
221     /*
222      * locate each source element in turn.
223      */
224     for (c = n->child; c; c = c->next)
225         if (c->which == DATA1N_tag && c->u.tag.element)
226         {
227             for (m = map->map; m; m = m->next)
228             {
229                 if (!data1_matchstr(m->source_element_name,
230                     c->u.tag.element->name))
231                 {
232                     data1_node *pn = res;
233                     data1_maptag *mt;
234                     data1_node *l;
235
236                     /*
237                      * process the target path specification.
238                      */
239                     for (mt = m->target_path; mt; mt = mt->next)
240                     {
241                         int match = 0;
242                         data1_node *cur;
243                         data1_node *last;
244
245                         for (l = pn->child, last = 0; l; last = l, l = l->next)
246                             if (!match)
247                                 match = tagmatch(l, mt);
248                             else
249                                 if (!tagmatch(l, mt))
250                                     break;
251                         if (!match || !mt->next || mt->new_field)
252                         {
253                             cur = data1_mk_node();
254                             cur->which = DATA1N_tag;
255                             cur->u.tag.element = 0;
256                             cur->u.tag.tag = mt->value.string;
257                             cur->u.tag.node_selected = 0;
258                             cur->parent = pn;
259                             cur->root = pn->root;
260                             if (!last)
261                             {
262                                 cur->next = pn->child;
263                                 pn->child = cur;
264                             }
265                             else
266                             {
267                                 cur->next = last->next;
268                                 last->next = cur;
269                             }
270                             pn->num_children++;
271                         }
272                         else
273                             cur = last ? last : pn->child;
274                         
275                         if (mt ->next)
276                             pn = cur;
277                         else if (!m->no_data)
278                         {
279                             cur->child = c->child;
280                             cur->num_children = c->num_children;
281                             c->child = 0;
282                             c->num_children = 0;
283                         }
284                     }
285                 }
286             }
287             if (map_children(c, map, res) < 0)
288                 return -1;
289         }
290     return 0;
291 }
292
293 /*
294  * Create a (possibly lossy) copy of the given record based on the
295  * table. The new copy will refer back to the data of the original record,
296  * which should not be discarded during the lifetime of the copy.
297  */
298 data1_node *data1_map_record(data1_node *n, data1_maptab *map)
299 {
300     data1_node *res = data1_mk_node();
301
302     res->which = DATA1N_root;
303     res->u.root.type = map->target_absyn_name;
304     if (!(res->u.root.absyn = data1_get_absyn(map->target_absyn_name)))
305     {
306         logf(LOG_WARN, "%s: Failed to load target absyn '%s'",
307             map->name, map->target_absyn_name);
308     }
309     res->parent = 0;
310     res->root = res;
311
312     if (map_children(n, map, res) < 0)
313     {
314         data1_free_tree(res);
315         return 0;
316     }
317     return res;
318 }