For Libxml2 and friends, YAZ defines YAZ_HAVE_{XML2,XSLT,EXSLT) in
[yaz-moved-to-github.git] / src / retrieval.c
1 /*
2  * Copyright (C) 2005-2006, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: retrieval.c,v 1.11 2006-07-06 10:17:53 adam Exp $
6  */
7 /**
8  * \file retrieval.c
9  * \brief Retrieval utility
10  */
11
12 #if HAVE_CONFIG_H
13 #include <config.h>
14 #endif
15
16 #include <string.h>
17 #include <yaz/retrieval.h>
18 #include <yaz/wrbuf.h>
19 #include <yaz/xmalloc.h>
20 #include <yaz/nmem.h>
21 #include <yaz/tpath.h>
22 #include <yaz/proto.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 sa "dc" */
55     const char *name;
56     /** \brief record syntax */
57     int *syntax;
58     /** \brief backend name */
59     const char *backend_name;
60     /** \brief backend syntax */
61     int *backend_syntax;
62
63     /** \brief record conversion */
64     yaz_record_conv_t record_conv;
65
66     /** \brief next element in list */
67     struct yaz_retrieval_elem *next;
68 };
69
70 static void yaz_retrieval_reset(yaz_retrieval_t p);
71
72 yaz_retrieval_t yaz_retrieval_create()
73 {
74     yaz_retrieval_t p = xmalloc(sizeof(*p));
75     p->odr = odr_createmem(ODR_ENCODE);
76     p->nmem = p->odr->mem;
77     p->wr_error = wrbuf_alloc();
78     p->list = 0;
79     p->path = 0;
80     yaz_retrieval_reset(p);
81     return p;
82 }
83
84 void yaz_retrieval_destroy(yaz_retrieval_t p)
85 {
86     if (p)
87     {
88         yaz_retrieval_reset(p);
89         odr_destroy(p->odr);
90         wrbuf_free(p->wr_error, 1);
91         xfree(p->path);
92         xfree(p);
93     }
94 }
95
96 void yaz_retrieval_reset(yaz_retrieval_t p)
97 {
98     struct yaz_retrieval_elem *el = p->list;
99     for(; el; el = el->next)
100         yaz_record_conv_destroy(el->record_conv);
101
102     wrbuf_rewind(p->wr_error);
103     odr_reset(p->odr);
104
105     p->list = 0;
106     p->list_p = &p->list;
107 }
108
109 /** \brief parse retrieval XML config */
110 static int conf_retrieval(yaz_retrieval_t p, const xmlNode *ptr)
111 {
112
113     struct _xmlAttr *attr;
114     struct yaz_retrieval_elem *el = nmem_malloc(p->nmem, sizeof(*el));
115
116     el->syntax = 0;
117     el->identifier = 0;
118     el->name = 0;
119     el->backend_name = 0;
120     el->backend_syntax = 0;
121
122     el->next = 0;
123
124     for (attr = ptr->properties; attr; attr = attr->next)
125     {
126         if (!xmlStrcmp(attr->name, BAD_CAST "syntax") &&
127             attr->children && attr->children->type == XML_TEXT_NODE)
128         {
129             el->syntax = yaz_str_to_z3950oid(
130                 p->odr, CLASS_RECSYN,
131                 (const char *) attr->children->content);
132             if (!el->syntax)
133             {
134                 wrbuf_printf(p->wr_error, "Bad syntax '%s'",
135                              (const char *) attr->children->content);
136                 return -1;
137             }
138         }
139         else if (!xmlStrcmp(attr->name, BAD_CAST "identifier") &&
140                  attr->children && attr->children->type == XML_TEXT_NODE)
141             el->identifier =
142                 nmem_strdup(p->nmem, (const char *) attr->children->content);
143         else if (!xmlStrcmp(attr->name, BAD_CAST "schema") &&
144                  attr->children && attr->children->type == XML_TEXT_NODE)
145         {
146             wrbuf_printf(p->wr_error, "Bad attribute 'schema'. "
147                          "Use 'name' instead");
148             return -1;
149         }
150         else if (!xmlStrcmp(attr->name, BAD_CAST "name") &&
151                  attr->children && attr->children->type == XML_TEXT_NODE)
152             el->name = 
153                 nmem_strdup(p->nmem, (const char *) attr->children->content);
154         else if (!xmlStrcmp(attr->name, BAD_CAST "backendschema") &&
155                  attr->children && attr->children->type == XML_TEXT_NODE)
156         {
157             wrbuf_printf(p->wr_error, "Bad attribute 'backendschema'. "
158                          "Use 'backendname' instead");
159             return -1;
160         }
161         else if (!xmlStrcmp(attr->name, BAD_CAST "backendname") &&
162                  attr->children && attr->children->type == XML_TEXT_NODE)
163             el->backend_name = 
164                 nmem_strdup(p->nmem, (const char *) attr->children->content);
165         else if (!xmlStrcmp(attr->name, BAD_CAST "backendsyntax") &&
166                  attr->children && attr->children->type == XML_TEXT_NODE)
167         {
168             el->backend_syntax = yaz_str_to_z3950oid(
169                 p->odr, CLASS_RECSYN,
170                 (const char *) attr->children->content);
171             if (!el->backend_syntax)
172             {
173                 wrbuf_printf(p->wr_error, "Bad backendsyntax '%s'",
174                              (const char *) attr->children->content);
175                 return -1;
176             }
177         }
178         else
179         {
180             wrbuf_printf(p->wr_error, "Bad attribute '%s'.", attr->name);
181             return -1;
182         }
183     }
184     if (!el->syntax)
185     {
186         wrbuf_printf(p->wr_error, "Missing 'syntax' attribute");
187         return -1;
188     }
189
190     el->record_conv = 0; /* OK to have no 'convert' sub content */
191     for (ptr = ptr->children; ptr; ptr = ptr->next)
192     {
193         if (ptr->type == XML_ELEMENT_NODE)
194         {
195             el->record_conv = yaz_record_conv_create();
196             
197             yaz_record_conv_set_path(el->record_conv, p->path);
198         
199             if (yaz_record_conv_configure(el->record_conv, ptr))
200             {
201                 wrbuf_printf(p->wr_error, "%s",
202                              yaz_record_conv_get_error(el->record_conv));
203                 yaz_record_conv_destroy(el->record_conv);
204                 return -1;
205             }
206         }
207     }
208     
209     *p->list_p = el;
210     p->list_p = &el->next;
211     return 0;
212 }
213
214 int yaz_retrieval_configure(yaz_retrieval_t p, const void *ptr_v)
215 {
216     const xmlNode *ptr = ptr_v; 
217
218     yaz_retrieval_reset(p);
219
220     if (ptr && ptr->type == XML_ELEMENT_NODE &&
221         !strcmp((const char *) ptr->name, "retrievalinfo"))
222     {
223         for (ptr = ptr->children; ptr; ptr = ptr->next)
224         {
225             if (ptr->type != XML_ELEMENT_NODE)
226                 continue;
227             if (!strcmp((const char *) ptr->name, "retrieval"))
228             {
229                 if (conf_retrieval(p, ptr))
230                     return -1;
231             }
232             else
233             {
234                 wrbuf_printf(p->wr_error, "Bad element '%s'."
235                              " Expected 'retrieval'", ptr->name);
236                 return -1;
237             }
238         }
239     }
240     else
241     {
242         wrbuf_printf(p->wr_error, "Missing 'retrievalinfo' element");
243         return -1;
244     }
245     return 0;
246 }
247
248 int yaz_retrieval_request(yaz_retrieval_t p,
249                           const char *schema, int *syntax,
250                           const char **match_schema, int **match_syntax,
251                           yaz_record_conv_t *rc,
252                           const char **backend_schema,
253                           int **backend_syntax)
254 {
255     struct yaz_retrieval_elem *el = p->list;
256     int syntax_matches = 0;
257     int schema_matches = 0;
258
259     wrbuf_rewind(p->wr_error);
260     if (!el)
261         return 0;
262     for(; el; el = el->next)
263     {
264         int schema_ok = 0;
265         int syntax_ok = 0;
266
267         if (!schema)
268             schema_ok = 1;
269         else
270         {
271             if (el->name && !strcmp(schema, el->name))
272                 schema_ok = 1;
273             if (el->identifier && !strcmp(schema, el->identifier))
274                 schema_ok = 1;
275             if (!el->name && !el->identifier)
276                 schema_ok = 1;
277         }
278         
279         if (syntax && el->syntax && !oid_oidcmp(syntax, el->syntax))
280             syntax_ok = 1;
281         if (!syntax)
282             syntax_ok = 1;
283
284         if (syntax_ok)
285             syntax_matches++;
286         if (schema_ok)
287             schema_matches++;
288         if (syntax_ok && schema_ok)
289         {
290             *match_syntax = el->syntax;
291             if (el->identifier)
292                 *match_schema = el->identifier;
293             else
294                 *match_schema = 0;
295             if (backend_schema)
296                 *backend_schema = el->backend_name;
297             if (backend_syntax)
298                 *backend_syntax = el->backend_syntax;
299             if (rc)
300                 *rc = el->record_conv;
301             return 0;
302         }
303     }
304     if (!syntax_matches && syntax)
305     {
306         char buf[OID_STR_MAX];
307         wrbuf_printf(p->wr_error, "%s", oid_to_dotstring(syntax, buf));
308         return 2;
309     }
310     if (schema)
311         wrbuf_printf(p->wr_error, "%s", schema);
312     if (!schema_matches)
313         return 1;
314     return 3;
315 }
316
317 const char *yaz_retrieval_get_error(yaz_retrieval_t p)
318 {
319     return wrbuf_buf(p->wr_error);
320 }
321
322 void yaz_retrieval_set_path(yaz_retrieval_t p, const char *path)
323 {
324     xfree(p->path);
325     p->path = 0;
326     if (path)
327         p->path = xstrdup(path);
328 }
329
330 #endif
331
332 /*
333  * Local variables:
334  * c-basic-offset: 4
335  * indent-tabs-mode: nil
336  * End:
337  * vim: shiftwidth=4 tabstop=8 expandtab
338  */
339