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