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