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