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