Various minor text tweaks.
[yaz-moved-to-github.git] / src / retrieval.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file retrieval.c
7  * \brief Retrieval utility
8  */
9
10 #if HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include <string.h>
15 #include <yaz/retrieval.h>
16 #include <yaz/wrbuf.h>
17 #include <yaz/xmalloc.h>
18 #include <yaz/nmem.h>
19 #include <yaz/tpath.h>
20 #include <yaz/match_glob.h>
21 #include <yaz/proto.h>
22 #include <yaz/oid_db.h>
23
24 #if YAZ_HAVE_XML2
25 #include <libxml/parser.h>
26 #include <libxml/tree.h>
27 #include <libxml/xinclude.h>
28
29 /** \brief The internal structure for yaz_retrieval_t */
30 struct yaz_retrieval_struct {
31     /** \brief ODR memory for configuration */
32     ODR odr;
33
34     /** \brief odr's NMEM memory (odr->mem) */
35     NMEM nmem;
36
37     /** \brief string buffer for error messages */
38     WRBUF wr_error;
39
40     /** \brief path for opening files  */
41     char *path;
42
43     /** \brief retrieval list */
44     struct yaz_retrieval_elem *list;
45
46     /** \brief last pointer in retrieval list */
47     struct yaz_retrieval_elem **list_p;
48 };
49
50 /** \brief information per 'retrieval' construct */
51 struct yaz_retrieval_elem {
52     /** \brief schema identifier */
53     const char *identifier;
54     /** \brief schema name , short-hand such as "dc" */
55     const char *name;
56     /** \brief record syntax */
57     Odr_oid *syntax;
58
59     /** \brief backend name */
60     const char *backend_name;
61     /** \brief backend syntax */
62     Odr_oid *backend_syntax;
63
64     /** \brief record conversion */
65     yaz_record_conv_t record_conv;
66
67     /** \brief next element in list */
68     struct yaz_retrieval_elem *next;
69 };
70
71 static void yaz_retrieval_reset(yaz_retrieval_t p);
72
73 yaz_retrieval_t yaz_retrieval_create()
74 {
75     yaz_retrieval_t p = (yaz_retrieval_t) xmalloc(sizeof(*p));
76     p->odr = odr_createmem(ODR_ENCODE);
77     p->nmem = odr_getmem(p->odr);
78     p->wr_error = wrbuf_alloc();
79     p->list = 0;
80     p->path = 0;
81     yaz_retrieval_reset(p);
82     return p;
83 }
84
85 void yaz_retrieval_destroy(yaz_retrieval_t p)
86 {
87     if (p)
88     {
89         yaz_retrieval_reset(p);
90         odr_destroy(p->odr);
91         wrbuf_destroy(p->wr_error);
92         xfree(p->path);
93         xfree(p);
94     }
95 }
96
97 void yaz_retrieval_reset(yaz_retrieval_t p)
98 {
99     struct yaz_retrieval_elem *el = p->list;
100     for(; el; el = el->next)
101         yaz_record_conv_destroy(el->record_conv);
102
103     wrbuf_rewind(p->wr_error);
104     odr_reset(p->odr);
105
106     p->list = 0;
107     p->list_p = &p->list;
108 }
109
110 /** \brief parse retrieval XML config */
111 static int conf_retrieval(yaz_retrieval_t p, const xmlNode *ptr,
112                           struct yaz_record_conv_type *types)
113 {
114     struct _xmlAttr *attr;
115     struct yaz_retrieval_elem *el = (struct yaz_retrieval_elem *)
116         nmem_malloc(p->nmem, sizeof(*el));
117
118     el->syntax = 0;
119     el->identifier = 0;
120     el->name = 0;
121     el->backend_name = 0;
122     el->backend_syntax = 0;
123
124     el->next = 0;
125
126     for (attr = ptr->properties; attr; attr = attr->next)
127     {
128         if (!xmlStrcmp(attr->name, BAD_CAST "syntax") &&
129             attr->children && attr->children->type == XML_TEXT_NODE)
130         {
131             el->syntax = yaz_string_to_oid_odr(
132                 yaz_oid_std(),
133                 CLASS_RECSYN,
134                 (const char *) attr->children->content,
135                 p->odr);
136             if (!el->syntax)
137             {
138                 wrbuf_printf(p->wr_error, "Element <retrieval>: "
139                              " unknown attribute value syntax='%s'",
140                              (const char *) attr->children->content);
141                 return -1;
142             }
143         }
144         else if (!xmlStrcmp(attr->name, BAD_CAST "identifier") &&
145                  attr->children && attr->children->type == XML_TEXT_NODE)
146             el->identifier =
147                 nmem_strdup(p->nmem, (const char *) attr->children->content);
148         else if (!xmlStrcmp(attr->name, BAD_CAST "name") &&
149                  attr->children && attr->children->type == XML_TEXT_NODE)
150             el->name =
151                 nmem_strdup(p->nmem, (const char *) attr->children->content);
152         else
153         {
154             wrbuf_printf(p->wr_error, "Element <retrieval>: "
155                          " expected attributes 'syntax', identifier' or "
156                          "'name', got '%s'", attr->name);
157             return -1;
158         }
159     }
160
161     if (!el->syntax)
162     {
163         wrbuf_printf(p->wr_error, "Missing 'syntax' attribute");
164         return -1;
165     }
166
167     /* parsing backend element */
168
169     el->record_conv = 0; /* OK to have no 'backend' sub content */
170     for (ptr = ptr->children; ptr; ptr = ptr->next)
171     {
172         if (ptr->type != XML_ELEMENT_NODE)
173             continue;
174         if (strcmp((const char *) ptr->name, "backend"))
175         {
176             wrbuf_printf(p->wr_error, "Element <retrieval>: expected"
177                          " zero or one element <backend>, got <%s>",
178                          (const char *) ptr->name);
179             return -1;
180         }
181         else
182         {
183             struct _xmlAttr *attr;
184             if (el->record_conv)
185             {
186                 wrbuf_printf(p->wr_error, "Element <retrieval>: "
187                              "only one <backend> allowed");
188                 yaz_record_conv_destroy(el->record_conv);
189                 return -1;
190             }
191             /* parsing attributees */
192             for (attr = ptr->properties; attr; attr = attr->next)
193             {
194                 if (!xmlStrcmp(attr->name, BAD_CAST "name")
195                          && attr->children
196                          && attr->children->type == XML_TEXT_NODE)
197                     el->backend_name
198                         = nmem_strdup(p->nmem,
199                                       (const char *) attr->children->content);
200
201                 else if (!xmlStrcmp(attr->name, BAD_CAST "syntax")
202                          && attr->children
203                          && attr->children->type == XML_TEXT_NODE)
204                 {
205                     el->backend_syntax
206                         = yaz_string_to_oid_odr(
207                             yaz_oid_std(),
208                             CLASS_RECSYN,
209                             (const char *) attr->children->content,
210                             p->odr);
211                     if (!el->backend_syntax)
212                     {
213                         wrbuf_printf(p->wr_error,
214                                      "Element <backend syntax='%s'>: "
215                                      "attribute 'syntax' has invalid "
216                                      "value '%s'",
217                                      attr->children->content,
218                                      attr->children->content);
219                         return -1;
220                     }
221                 }
222                 else
223                 {
224                     wrbuf_printf(p->wr_error, "Element <backend>: expected "
225                                  "attributes 'syntax' or 'name, got '%s'",
226                                  attr->name);
227                     return -1;
228                 }
229             }
230
231              /* parsing internal of record conv */
232             el->record_conv = yaz_record_conv_create();
233
234             yaz_record_conv_set_path(el->record_conv, p->path);
235
236             if (yaz_record_conv_configure_t(el->record_conv, ptr, types))
237             {
238                 wrbuf_printf(p->wr_error, "%s",
239                              yaz_record_conv_get_error(el->record_conv));
240                 yaz_record_conv_destroy(el->record_conv);
241                 return -1;
242             }
243         }
244     }
245
246     *p->list_p = el;
247     p->list_p = &el->next;
248     return 0;
249 }
250
251 int yaz_retrieval_configure_t(yaz_retrieval_t p, const xmlNode *ptr,
252                               struct yaz_record_conv_type *types)
253 {
254     yaz_retrieval_reset(p);
255
256     if (ptr && ptr->type == XML_ELEMENT_NODE &&
257         !strcmp((const char *) ptr->name, "retrievalinfo"))
258     {
259         for (ptr = ptr->children; ptr; ptr = ptr->next)
260         {
261             if (ptr->type != XML_ELEMENT_NODE)
262                 continue;
263             if (!strcmp((const char *) ptr->name, "retrieval"))
264             {
265                 if (conf_retrieval(p, ptr, types))
266                     return -1;
267             }
268             else
269             {
270                 wrbuf_printf(p->wr_error, "Element <retrievalinfo>: "
271                              "expected element <retrieval>, got <%s>",
272                              ptr->name);
273                 return -1;
274             }
275         }
276     }
277     else
278     {
279         wrbuf_printf(p->wr_error, "Expected element <retrievalinfo>");
280         return -1;
281     }
282     return 0;
283 }
284
285 int yaz_retrieval_configure(yaz_retrieval_t p, const xmlNode *ptr)
286 {
287     return yaz_retrieval_configure_t(p, ptr, 0);
288 }
289
290 int yaz_retrieval_request(yaz_retrieval_t p,
291                           const char *schema, Odr_oid *syntax,
292                           const char **match_schema, Odr_oid **match_syntax,
293                           yaz_record_conv_t *rc,
294                           const char **backend_schema,
295                           Odr_oid **backend_syntax)
296 {
297     struct yaz_retrieval_elem *el = p->list;
298     int syntax_matches = 0;
299     int schema_matches = 0;
300     struct yaz_retrieval_elem *el_best = 0;
301
302     wrbuf_rewind(p->wr_error);
303     if (!el)
304         return 0;
305     for(; el; el = el->next)
306     {
307         int schema_ok = 0;
308         int syntax_ok = 0;
309
310         if (!schema)
311             schema_ok = 1;
312         else
313         {
314             if (el->name && yaz_match_glob(el->name, schema))
315                 schema_ok = 2;
316             if (el->identifier && !strcmp(schema, el->identifier))
317                 schema_ok = 2;
318             if (!el->name && !el->identifier)
319                 schema_ok = 1;
320         }
321
322         if (syntax && el->syntax && !oid_oidcmp(syntax, el->syntax))
323             syntax_ok = 1;
324         if (!syntax)
325             syntax_ok = 1;
326
327         if (syntax_ok)
328             syntax_matches++;
329         if (schema_ok)
330             schema_matches++;
331         if (syntax_ok && schema_ok)
332         {
333             if (!el_best || schema_ok == 2)
334                 el_best = el;
335         }
336     }
337     if (el_best)
338     {
339         el = el_best;
340         *match_syntax = el->syntax;
341         if (el->identifier)
342             *match_schema = el->identifier;
343         else
344             *match_schema = 0;
345         if (backend_schema)
346         {
347             if (el->backend_name)
348             {
349                 if (*el->backend_name)
350                     *backend_schema = el->backend_name;
351             }
352             else if (el->name)
353                 *backend_schema = el->name;
354             else
355                 *backend_schema = schema;
356         }
357         if (backend_syntax)
358         {
359             if (el->backend_syntax)
360                 *backend_syntax = el->backend_syntax;
361             else
362                 *backend_syntax = el->syntax;
363         }
364         if (rc)
365             *rc = el->record_conv;
366         return 0;
367     }
368     if (!syntax_matches && syntax)
369     {
370         char buf[OID_STR_MAX];
371         wrbuf_printf(p->wr_error, "%s", oid_oid_to_dotstring(syntax, buf));
372         return 2;
373     }
374     if (schema)
375         wrbuf_printf(p->wr_error, "%s", schema);
376     if (!schema_matches)
377         return 1;
378     return 3;
379 }
380
381 const char *yaz_retrieval_get_error(yaz_retrieval_t p)
382 {
383     return wrbuf_cstr(p->wr_error);
384 }
385
386 void yaz_retrieval_set_path(yaz_retrieval_t p, const char *path)
387 {
388     xfree(p->path);
389     p->path = 0;
390     if (path)
391         p->path = xstrdup(path);
392 }
393
394 #endif
395
396 /*
397  * Local variables:
398  * c-basic-offset: 4
399  * c-file-style: "Stroustrup"
400  * indent-tabs-mode: nil
401  * End:
402  * vim: shiftwidth=4 tabstop=8 expandtab
403  */
404