started record constructors. still unfnished, but yet only used in test_relevance.c
[pazpar2-moved-to-github.git] / src / config.c
1 /* $Id: config.c,v 1.28 2007-04-20 14:37:17 marc Exp $
2    Copyright (c) 2006-2007, Index Data.
3
4 This file is part of Pazpar2.
5
6 Pazpar2 is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Pazpar2; see the file LICENSE.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21
22 /* $Id: config.c,v 1.28 2007-04-20 14:37:17 marc Exp $ */
23
24 #include <string.h>
25
26 #include <libxml/parser.h>
27 #include <libxml/tree.h>
28 #include <libxslt/xslt.h>
29 #include <libxslt/transform.h>
30 #include <libxslt/xsltutils.h>
31
32 #if HAVE_CONFIG_H
33 #include <cconfig.h>
34 #endif
35
36 #include <yaz/yaz-util.h>
37 #include <yaz/nmem.h>
38
39 #define CONFIG_NOEXTERNS
40 #include "config.h"
41
42 static NMEM nmem = 0;
43 static char confdir[256] = ".";
44
45 struct conf_config *config = 0;
46
47
48 struct conf_metadata * conf_metadata_assign(NMEM nmem, 
49                                             struct conf_metadata * metadata,
50                                             const char *name,
51                                             enum conf_metadata_type type,
52                                             enum conf_metadata_merge merge,
53                                             int brief,
54                                             int termlist,
55                                             int rank,
56                                             int sortkey_offset)
57 {
58     if (!nmem || !metadata || !name)
59         return 0;
60     
61     metadata->name = nmem_strdup(nmem, name);
62     metadata->type = type;
63     metadata->merge = merge;
64     metadata->brief = brief;   
65     metadata->termlist = termlist;
66     metadata->rank = rank;    
67     metadata->sortkey_offset = sortkey_offset;
68
69     return metadata;
70 }
71
72
73 struct conf_sortkey * conf_sortkey_assign(NMEM nmem, 
74                                           struct conf_sortkey * sortkey,
75                                           const char *name,
76                                           enum conf_sortkey_type type)
77 {
78     if (!nmem || !sortkey || !name)
79         return 0;
80     
81     sortkey->name = nmem_strdup(nmem, name);
82     sortkey->type = type;
83
84     return sortkey;
85 }
86
87
88 struct conf_service * conf_service_create(NMEM nmem,
89                                           int num_metadata, int num_sortkeys)
90 {
91     struct conf_service * service = 0;
92
93     //assert(nmem);
94     
95     service = nmem_malloc(nmem, sizeof(struct conf_service));
96
97     service->num_metadata = num_metadata;
98     service->metadata = 0;
99     if (service->num_metadata)
100       service->metadata 
101           = nmem_malloc(nmem, 
102                         sizeof(struct conf_metadata) * service->num_metadata);
103     service->num_sortkeys = num_sortkeys;
104     service->sortkeys = 0;
105     if (service->num_sortkeys)
106         service->sortkeys 
107             = nmem_malloc(nmem, 
108                           sizeof(struct conf_sortkey) * service->num_sortkeys);
109     return service; 
110 }
111
112 struct conf_metadata* conf_service_add_metadata(NMEM nmem, 
113                                                 struct conf_service *service,
114                                                 int position,
115                                                 const char *name,
116                                                 enum conf_metadata_type type,
117                                                 enum conf_metadata_merge merge,
118                                                 int brief,
119                                                 int termlist,
120                                                 int rank,
121                                                 int sortkey_offset)
122 {
123     struct conf_metadata * md = 0;
124
125     if (!service || !service->metadata || !service->num_metadata
126         || position < 0  || !(position < service->num_metadata))
127         return 0;
128
129     //md = &((service->metadata)[position]);
130     md = service->metadata + position;
131     md = conf_metadata_assign(nmem, md, name, type, merge, 
132                              brief, termlist, rank, sortkey_offset);
133     return md;
134 }
135
136
137 struct conf_sortkey * conf_service_add_sortkey(NMEM nmem,
138                                                struct conf_service *service,
139                                                int position,
140                                                const char *name,
141                                                enum conf_sortkey_type type)
142 {
143     struct conf_sortkey * sk = 0;
144
145     if (!service || !service->sortkeys || !service->num_sortkeys
146         || position < 0  || !(position < service->num_sortkeys))
147         return 0;
148
149     //sk = &((service->sortkeys)[position]);
150     sk = service->sortkeys + position;
151     sk = conf_sortkey_assign(nmem, sk, name, type);
152
153     return sk;
154 }
155
156
157
158
159
160 /* Code to parse configuration file */
161 /* ==================================================== */
162
163 static struct conf_service *parse_service(xmlNode *node)
164 {
165     xmlNode *n;
166     int md_node = 0;
167     int sk_node = 0;
168
169     struct conf_service *service = 0;
170     int num_metadata = 0;
171     int num_sortkeys = 0;
172     
173     // count num_metadata and num_sortkeys
174     for (n = node->children; n; n = n->next)
175         if (n->type == XML_ELEMENT_NODE && !strcmp((const char *)
176                                                    n->name, "metadata"))
177         {
178             xmlChar *sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
179             num_metadata++;
180             if (sortkey && strcmp((const char *) sortkey, "no"))
181                 num_sortkeys++;
182             xmlFree(sortkey);
183         }
184
185     service = conf_service_create(nmem, num_metadata, num_sortkeys);    
186
187     for (n = node->children; n; n = n->next)
188     {
189         if (n->type != XML_ELEMENT_NODE)
190             continue;
191         if (!strcmp((const char *) n->name, (const char *) "metadata"))
192         {
193             xmlChar *xml_name = xmlGetProp(n, (xmlChar *) "name");
194             xmlChar *xml_brief = xmlGetProp(n, (xmlChar *) "brief");
195             xmlChar *xml_sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
196             xmlChar *xml_merge = xmlGetProp(n, (xmlChar *) "merge");
197             xmlChar *xml_type = xmlGetProp(n, (xmlChar *) "type");
198             xmlChar *xml_termlist = xmlGetProp(n, (xmlChar *) "termlist");
199             xmlChar *xml_rank = xmlGetProp(n, (xmlChar *) "rank");
200
201             enum conf_metadata_type type = Metadata_type_generic;
202             enum conf_metadata_merge merge = Metadata_merge_no;
203             int brief = 0;
204             int termlist = 0;
205             int rank = 0;
206             int sortkey_offset = 0;
207             enum conf_sortkey_type sk_type = Metadata_sortkey_relevance;
208             
209             // now do the parsing logic
210             if (!xml_name)
211             {
212                 yaz_log(YLOG_FATAL, "Must specify name in metadata element");
213                 return 0;
214             }
215             if (xml_brief)
216             {
217                 if (!strcmp((const char *) xml_brief, "yes"))
218                     brief = 1;
219                  else if (strcmp((const char *) xml_brief, "no"))
220                 {
221                     yaz_log(YLOG_FATAL, "metadata/brief must be yes or no");
222                     return 0;
223                 }
224             }
225             else
226                 brief = 0;
227
228             if (xml_termlist)
229             {
230                 if (!strcmp((const char *) xml_termlist, "yes"))
231                     termlist = 1;
232                 else if (strcmp((const char *) xml_termlist, "no"))
233                 {
234                     yaz_log(YLOG_FATAL, "metadata/termlist must be yes or no");
235                     return 0;
236                 }
237             }
238             else
239                 termlist = 0;
240
241             if (xml_rank)
242                 rank = atoi((const char *) xml_rank);
243             else
244                 rank = 0;
245
246             if (xml_type)
247             {
248                 if (!strcmp((const char *) xml_type, "generic"))
249                     type = Metadata_type_generic;
250                 else if (!strcmp((const char *) xml_type, "year"))
251                     type = Metadata_type_year;
252                 else
253                 {
254                     yaz_log(YLOG_FATAL, "Unknown value for metadata/type: %s", xml_type);
255                     return 0;
256                 }
257             }
258             else
259                 type = Metadata_type_generic;
260
261             if (xml_merge)
262             {
263                 if (!strcmp((const char *) xml_merge, "no"))
264                     merge = Metadata_merge_no;
265                 else if (!strcmp((const char *) xml_merge, "unique"))
266                     merge = Metadata_merge_unique;
267                 else if (!strcmp((const char *) xml_merge, "longest"))
268                     merge = Metadata_merge_longest;
269                 else if (!strcmp((const char *) xml_merge, "range"))
270                     merge = Metadata_merge_range;
271                 else if (!strcmp((const char *) xml_merge, "all"))
272                     merge = Metadata_merge_all;
273                 else
274                 {
275                     yaz_log(YLOG_FATAL, 
276                             "Unknown value for metadata/merge: %s", xml_merge);
277                     return 0;
278                 }
279             }
280             else
281                 merge = Metadata_merge_no;
282
283             // add a sortkey if so specified
284             if (xml_sortkey && strcmp((const char *) xml_sortkey, "no"))
285             {
286                 if (merge == Metadata_merge_no)
287                 {
288                     yaz_log(YLOG_FATAL, 
289                             "Can't specify sortkey on a non-merged field");
290                     return 0;
291                 }
292                 if (!strcmp((const char *) xml_sortkey, "numeric"))
293                     sk_type = Metadata_sortkey_numeric;
294                 else if (!strcmp((const char *) xml_sortkey, "skiparticle"))
295                     sk_type = Metadata_sortkey_skiparticle;
296                 else
297                 {
298                     yaz_log(YLOG_FATAL,
299                             "Unknown sortkey in metadata element: %s", 
300                             xml_sortkey);
301                     return 0;
302                 }
303                 sortkey_offset = sk_node;
304
305                 conf_service_add_sortkey(nmem, service, sk_node,
306                                          (const char *) xml_name, sk_type);
307                 
308                 sk_node++;
309             }
310             else
311                 sortkey_offset = -1;
312
313             // metadata known, assign values
314             conf_service_add_metadata(nmem, service, md_node,
315                                       (const char *) xml_name,
316                                       type, merge,
317                                       brief, termlist, rank, sortkey_offset);
318
319             xmlFree(xml_name);
320             xmlFree(xml_brief);
321             xmlFree(xml_sortkey);
322             xmlFree(xml_merge);
323             xmlFree(xml_type);
324             xmlFree(xml_termlist);
325             xmlFree(xml_rank);
326             md_node++;
327         }
328         else
329         {
330             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
331             return 0;
332         }
333     }
334     return service;
335 }
336
337 static char *parse_settings(xmlNode *node)
338 {
339     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
340     char *r;
341
342     if (src)
343         r = nmem_strdup(nmem, (const char *) src);
344     else
345     {
346         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
347         return 0;
348     }
349     xmlFree(src);
350     return r;
351 }
352
353 static struct conf_server *parse_server(xmlNode *node)
354 {
355     xmlNode *n;
356     struct conf_server *r = nmem_malloc(nmem, sizeof(struct conf_server));
357
358     r->host = 0;
359     r->port = 0;
360     r->proxy_host = 0;
361     r->proxy_port = 0;
362     r->myurl = 0;
363     r->zproxy_host = 0;
364     r->zproxy_port = 0;
365     r->service = 0;
366     r->next = 0;
367     r->settings = 0;
368
369     for (n = node->children; n; n = n->next)
370     {
371         if (n->type != XML_ELEMENT_NODE)
372             continue;
373         if (!strcmp((const char *) n->name, "listen"))
374         {
375             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
376             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
377             if (port)
378                 r->port = atoi((const char *) port);
379             if (host)
380                 r->host = nmem_strdup(nmem, (const char *) host);
381             xmlFree(port);
382             xmlFree(host);
383         }
384         else if (!strcmp((const char *) n->name, "proxy"))
385         {
386             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
387             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
388             xmlChar *myurl = xmlGetProp(n, (xmlChar *) "myurl");
389             if (port)
390                 r->proxy_port = atoi((const char *) port);
391             if (host)
392                 r->proxy_host = nmem_strdup(nmem, (const char *) host);
393             if (myurl)
394                 r->myurl = nmem_strdup(nmem, (const char *) myurl);
395 #ifdef GAGA
396             else
397             {
398                 yaz_log(YLOG_FATAL, "Must specify @myurl for proxy");
399                 return 0;
400             }
401 #endif
402             xmlFree(port);
403             xmlFree(host);
404             xmlFree(myurl);
405         }
406         else if (!strcmp((const char *) n->name, "zproxy"))
407         {
408             xmlChar *port = 0;
409             xmlChar *host = 0;
410
411             port = xmlGetProp(n, (xmlChar *) "port");
412             host = xmlGetProp(n, (xmlChar *) "host");
413
414             if (port)
415                 r->zproxy_port = atoi((const char *) port);
416             if (host)
417                 r->zproxy_host = nmem_strdup(nmem, (const char *) host);
418
419             xmlFree(port);
420             xmlFree(host);
421         }
422         else if (!strcmp((const char *) n->name, "settings"))
423         {
424             if (r->settings)
425             {
426                 yaz_log(YLOG_FATAL, "Can't repeat 'settings'");
427                 return 0;
428             }
429             if (!(r->settings = parse_settings(n)))
430                 return 0;
431         }
432         else if (!strcmp((const char *) n->name, "service"))
433         {
434             struct conf_service *s = parse_service(n);
435             if (!s)
436                 return 0;
437             r->service = s;
438         }
439         else
440         {
441             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
442             return 0;
443         }
444     }
445     return r;
446 }
447
448 xsltStylesheet *conf_load_stylesheet(const char *fname)
449 {
450     char path[256];
451     sprintf(path, "%s/%s", confdir, fname);
452     return xsltParseStylesheetFile((xmlChar *) path);
453 }
454
455 static struct conf_targetprofiles *parse_targetprofiles(xmlNode *node)
456 {
457     struct conf_targetprofiles *r = nmem_malloc(nmem, sizeof(*r));
458     xmlChar *type = xmlGetProp(node, (xmlChar *) "type");
459     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
460
461     memset(r, 0, sizeof(*r));
462
463     if (type)
464     {
465         if (!strcmp((const char *) type, "local"))
466             r->type = Targetprofiles_local;
467         else
468         {
469             yaz_log(YLOG_FATAL, "Unknown targetprofile type");
470             return 0;
471         }
472     }
473     else
474     {
475         yaz_log(YLOG_FATAL, "Must specify type for targetprofile");
476         return 0;
477     }
478
479     if (src)
480         r->src = nmem_strdup(nmem, (const char *) src);
481     else
482     {
483         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
484         return 0;
485     }
486     xmlFree(type);
487     xmlFree(src);
488     return r;
489 }
490
491 static struct conf_config *parse_config(xmlNode *root)
492 {
493     xmlNode *n;
494     struct conf_config *r = nmem_malloc(nmem, sizeof(struct conf_config));
495
496     r->servers = 0;
497     r->targetprofiles = 0;
498
499     for (n = root->children; n; n = n->next)
500     {
501         if (n->type != XML_ELEMENT_NODE)
502             continue;
503         if (!strcmp((const char *) n->name, "server"))
504         {
505             struct conf_server *tmp = parse_server(n);
506             if (!tmp)
507                 return 0;
508             tmp->next = r->servers;
509             r->servers = tmp;
510         }
511         else if (!strcmp((const char *) n->name, "targetprofiles"))
512         {
513             // It would be fun to be able to fix this sometime
514             if (r->targetprofiles)
515             {
516                 yaz_log(YLOG_FATAL, "Can't repeat targetprofiles");
517                 return 0;
518             }
519             if (!(r->targetprofiles = parse_targetprofiles(n)))
520                 return 0;
521         }
522         else
523         {
524             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
525             return 0;
526         }
527     }
528     return r;
529 }
530
531 int read_config(const char *fname)
532 {
533     xmlDoc *doc = xmlParseFile(fname);
534     const char *p;
535
536     if (!nmem)  // Initialize
537     {
538         nmem = nmem_create();
539         xmlSubstituteEntitiesDefault(1);
540         xmlLoadExtDtdDefaultValue = 1;
541     }
542     if (!doc)
543     {
544         yaz_log(YLOG_FATAL, "Failed to read %s", fname);
545         exit(1);
546     }
547     if ((p = strrchr(fname, '/')))
548     {
549         int len = p - fname;
550         strncpy(confdir, fname, len);
551         confdir[len] = '\0';
552     }
553     config = parse_config(xmlDocGetRootElement(doc));
554     xmlFreeDoc(doc);
555
556     if (config)
557         return 1;
558     else
559         return 0;
560 }
561
562
563 /*
564  * Local variables:
565  * c-basic-offset: 4
566  * indent-tabs-mode: nil
567  * End:
568  * vim: shiftwidth=4 tabstop=8 expandtab
569  */