e0ed99848168fe88a52eebed5e8329422f5e5eae
[yaz-moved-to-github.git] / src / retrieval.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2012 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 {
113     struct _xmlAttr *attr;
114     struct yaz_retrieval_elem *el = (struct yaz_retrieval_elem *)
115         nmem_malloc(p->nmem, sizeof(*el));
116
117     el->syntax = 0;
118     el->identifier = 0;
119     el->name = 0;
120     el->backend_name = 0;
121     el->backend_syntax = 0;
122
123     el->next = 0;
124
125     for (attr = ptr->properties; attr; attr = attr->next)
126     {
127         if (!xmlStrcmp(attr->name, BAD_CAST "syntax") &&
128             attr->children && attr->children->type == XML_TEXT_NODE)
129         {
130             el->syntax = yaz_string_to_oid_odr(
131                 yaz_oid_std(),
132                 CLASS_RECSYN,
133                 (const char *) attr->children->content,
134                 p->odr);
135             if (!el->syntax)
136             {
137                 wrbuf_printf(p->wr_error, "Element <retrieval>: "
138                              " unknown attribute value syntax='%s'",
139                              (const char *) attr->children->content);
140                 return -1;
141             }
142         }
143         else if (!xmlStrcmp(attr->name, BAD_CAST "identifier") &&
144                  attr->children && attr->children->type == XML_TEXT_NODE)
145             el->identifier =
146                 nmem_strdup(p->nmem, (const char *) attr->children->content);
147         else if (!xmlStrcmp(attr->name, BAD_CAST "name") &&
148                  attr->children && attr->children->type == XML_TEXT_NODE)
149             el->name = 
150                 nmem_strdup(p->nmem, (const char *) attr->children->content);
151         else
152         {
153             wrbuf_printf(p->wr_error, "Element <retrieval>: "
154                          " expected attributes 'syntax', identifier' or "
155                          "'name', got '%s'", attr->name);
156             return -1;
157         }
158     }
159
160     if (!el->syntax)
161     {
162         wrbuf_printf(p->wr_error, "Missing 'syntax' attribute");
163         return -1;
164     }
165
166     /* parsing backend element */
167
168     el->record_conv = 0; /* OK to have no 'backend' sub content */
169     for (ptr = ptr->children; ptr; ptr = ptr->next)
170     {
171         if (ptr->type != XML_ELEMENT_NODE)
172             continue;
173         if (strcmp((const char *) ptr->name, "backend"))
174         {
175             wrbuf_printf(p->wr_error, "Element <retrieval>: expected"
176                          " zero or one element <backend>, got <%s>",
177                          (const char *) ptr->name);
178             return -1;
179         }
180         else
181         {
182             if (el->record_conv)
183             {
184                 wrbuf_printf(p->wr_error, "Element <retrieval>: "
185                              "only one <backend> allowed");
186                 yaz_record_conv_destroy(el->record_conv);
187                 return -1;
188             }
189             /* parsing attributees */
190             struct _xmlAttr *attr;
191             for (attr = ptr->properties; attr; attr = attr->next)
192             {
193                 if (!xmlStrcmp(attr->name, BAD_CAST "name") 
194                          && attr->children 
195                          && attr->children->type == XML_TEXT_NODE)
196                     el->backend_name 
197                         = nmem_strdup(p->nmem, 
198                                       (const char *) attr->children->content);
199                 
200                 else if (!xmlStrcmp(attr->name, BAD_CAST "syntax") 
201                          && attr->children 
202                          && attr->children->type == XML_TEXT_NODE)
203                 {
204                     el->backend_syntax 
205                         = yaz_string_to_oid_odr(
206                             yaz_oid_std(),
207                             CLASS_RECSYN,
208                             (const char *) attr->children->content,
209                             p->odr);
210                     if (!el->backend_syntax)
211                     {
212                         wrbuf_printf(p->wr_error, 
213                                      "Element <backend syntax='%s'>: "
214                                      "attribute 'syntax' has invalid "
215                                      "value '%s'", 
216                                      attr->children->content,
217                                      attr->children->content);
218                         return -1;
219                     } 
220                 }
221                 else
222                 {
223                     wrbuf_printf(p->wr_error, "Element <backend>: expected "
224                                  "attributes 'syntax' or 'name, got '%s'", 
225                                  attr->name);
226                     return -1;
227                 }
228             }
229           
230              /* parsing internal of record conv */
231             el->record_conv = yaz_record_conv_create();
232             
233             yaz_record_conv_set_path(el->record_conv, p->path);
234
235             if (yaz_record_conv_configure(el->record_conv, ptr))
236             {
237                 wrbuf_printf(p->wr_error, "%s",
238                              yaz_record_conv_get_error(el->record_conv));
239                 yaz_record_conv_destroy(el->record_conv);
240                 return -1;
241             }
242         }
243     }
244     
245     *p->list_p = el;
246     p->list_p = &el->next;
247     return 0;
248 }
249
250 int yaz_retrieval_configure(yaz_retrieval_t p, const xmlNode *ptr)
251 {
252     yaz_retrieval_reset(p);
253
254     if (ptr && ptr->type == XML_ELEMENT_NODE &&
255         !strcmp((const char *) ptr->name, "retrievalinfo"))
256     {
257         for (ptr = ptr->children; ptr; ptr = ptr->next)
258         {
259             if (ptr->type != XML_ELEMENT_NODE)
260                 continue;
261             if (!strcmp((const char *) ptr->name, "retrieval"))
262             {
263                 if (conf_retrieval(p, ptr))
264                     return -1;
265             }
266             else
267             {
268                 wrbuf_printf(p->wr_error, "Element <retrievalinfo>: "
269                              "expected element <retrieval>, got <%s>", 
270                              ptr->name);
271                 return -1;
272             }
273         }
274     }
275     else
276     {
277         wrbuf_printf(p->wr_error, "Expected element <retrievalinfo>");
278         return -1;
279     }
280     return 0;
281 }
282
283 int yaz_retrieval_request(yaz_retrieval_t p,
284                           const char *schema, Odr_oid *syntax,
285                           const char **match_schema, Odr_oid **match_syntax,
286                           yaz_record_conv_t *rc,
287                           const char **backend_schema,
288                           Odr_oid **backend_syntax)
289 {
290     struct yaz_retrieval_elem *el = p->list;
291     int syntax_matches = 0;
292     int schema_matches = 0;
293
294     wrbuf_rewind(p->wr_error);
295     if (!el)
296         return 0;
297     for(; el; el = el->next)
298     {
299         int schema_ok = 0;
300         int syntax_ok = 0;
301
302         if (!schema)
303             schema_ok = 1;
304         else
305         {
306             if (el->name && yaz_match_glob(el->name, schema))
307                 schema_ok = 1;
308             if (el->identifier && !strcmp(schema, el->identifier))
309                 schema_ok = 1;
310             if (!el->name && !el->identifier)
311                 schema_ok = 1;
312         }
313         
314         if (syntax && el->syntax && !oid_oidcmp(syntax, el->syntax))
315             syntax_ok = 1;
316         if (!syntax)
317             syntax_ok = 1;
318
319         if (syntax_ok)
320             syntax_matches++;
321         if (schema_ok)
322             schema_matches++;
323         if (syntax_ok && schema_ok)
324         {
325             *match_syntax = el->syntax;
326             if (el->identifier)
327                 *match_schema = el->identifier;
328             else
329                 *match_schema = 0;
330             if (backend_schema)
331             {
332                 if (el->backend_name)
333                     *backend_schema = el->backend_name;
334                 else if (el->name)
335                     *backend_schema = el->name;                    
336                 else
337                     *backend_schema = schema;
338             }
339             if (backend_syntax)
340             {
341                 if (el->backend_syntax)
342                     *backend_syntax = el->backend_syntax;
343                 else
344                     *backend_syntax = el->syntax;
345             }
346             if (rc)
347                 *rc = el->record_conv;
348             return 0;
349         }
350     }
351     if (!syntax_matches && syntax)
352     {
353         char buf[OID_STR_MAX];
354         wrbuf_printf(p->wr_error, "%s", oid_oid_to_dotstring(syntax, buf));
355         return 2;
356     }
357     if (schema)
358         wrbuf_printf(p->wr_error, "%s", schema);
359     if (!schema_matches)
360         return 1;
361     return 3;
362 }
363
364 const char *yaz_retrieval_get_error(yaz_retrieval_t p)
365 {
366     return wrbuf_cstr(p->wr_error);
367 }
368
369 void yaz_retrieval_set_path(yaz_retrieval_t p, const char *path)
370 {
371     xfree(p->path);
372     p->path = 0;
373     if (path)
374         p->path = xstrdup(path);
375 }
376
377 #endif
378
379 /*
380  * Local variables:
381  * c-basic-offset: 4
382  * c-file-style: "Stroustrup"
383  * indent-tabs-mode: nil
384  * End:
385  * vim: shiftwidth=4 tabstop=8 expandtab
386  */
387