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