More strict metadata assignment code
[pazpar2-moved-to-github.git] / src / pazpar2_config.c
1 /* This file is part of Pazpar2.
2    Copyright (C) 2006-2009 Index Data
3
4 Pazpar2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <string.h>
25 #include <assert.h>
26
27 #include <libxml/parser.h>
28 #include <libxml/tree.h>
29 #include <libxslt/xslt.h>
30 #include <libxslt/transform.h>
31 #include <libxslt/xsltutils.h>
32
33 #include <yaz/yaz-util.h>
34 #include <yaz/nmem.h>
35 #include <yaz/snprintf.h>
36 #include <yaz/tpath.h>
37
38 #if HAVE_GLOB_H
39 #define USE_POSIX_GLOB 1
40 #else
41 #define USE_POSIX_GLOB 0
42 #endif
43
44
45 #if USE_POSIX_GLOB
46 #include <glob.h>
47 #endif
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #if HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53 #include "pazpar2_config.h"
54 #include "settings.h"
55 #include "eventl.h"
56 #include "http.h"
57
58 struct conf_config
59 {
60     NMEM nmem; /* for conf_config and servers memory */
61     struct conf_server *servers;
62     WRBUF confdir;
63 };
64
65
66 static char *parse_settings(struct conf_config *config,
67                             NMEM nmem, xmlNode *node);
68
69 static struct conf_targetprofiles *parse_targetprofiles(NMEM nmem,
70                                                         xmlNode *node);
71
72 static void conf_metadata_assign(NMEM nmem, 
73                                  struct conf_metadata * metadata,
74                                  const char *name,
75                                  enum conf_metadata_type type,
76                                  enum conf_metadata_merge merge,
77                                  enum conf_setting_type setting,
78                                  int brief,
79                                  int termlist,
80                                  int rank,
81                                  int sortkey_offset,
82                                  enum conf_metadata_mergekey mt)
83 {
84     assert(nmem && metadata && name);
85     
86     metadata->name = nmem_strdup(nmem, name);
87
88     metadata->type = type;
89
90     // enforcing that type_year is always range_merge
91     if (metadata->type == Metadata_type_year)
92         metadata->merge = Metadata_merge_range;
93     else
94         metadata->merge = merge;    
95
96     metadata->setting = setting;
97     metadata->brief = brief;   
98     metadata->termlist = termlist;
99     metadata->rank = rank;    
100     metadata->sortkey_offset = sortkey_offset;
101     metadata->mergekey = mt;
102 }
103
104
105 static void conf_sortkey_assign(NMEM nmem, 
106                                 struct conf_sortkey * sortkey,
107                                 const char *name,
108                                 enum conf_sortkey_type type)
109 {
110     assert(nmem && sortkey && name);
111     
112     sortkey->name = nmem_strdup(nmem, name);
113     sortkey->type = type;
114 }
115
116
117 struct conf_service *conf_service_create(struct conf_config *config,
118                                          int num_metadata, int num_sortkeys,
119                                           const char *service_id)
120 {
121     struct conf_service * service = 0;
122     NMEM nmem = nmem_create();
123
124     service = nmem_malloc(nmem, sizeof(struct conf_service));
125     service->nmem = nmem;
126     service->next = 0;
127     service->settings = 0;
128     service->databases = 0;
129     service->targetprofiles = 0;
130     service->config = config;
131
132     service->id = service_id ? nmem_strdup(nmem, service_id) : 0;
133     service->num_metadata = num_metadata;
134     service->metadata = 0;
135     if (service->num_metadata)
136       service->metadata 
137           = nmem_malloc(nmem, 
138                         sizeof(struct conf_metadata) * service->num_metadata);
139     service->num_sortkeys = num_sortkeys;
140     service->sortkeys = 0;
141     if (service->num_sortkeys)
142         service->sortkeys 
143             = nmem_malloc(nmem, 
144                           sizeof(struct conf_sortkey) * service->num_sortkeys);
145     service->dictionary = 0;
146     return service; 
147 }
148
149 struct conf_metadata* conf_service_add_metadata(struct conf_service *service,
150                                                 int field_id,
151                                                 const char *name,
152                                                 enum conf_metadata_type type,
153                                                 enum conf_metadata_merge merge,
154                                                 enum conf_setting_type setting,
155                                                 int brief,
156                                                 int termlist,
157                                                 int rank,
158                                                 int sortkey_offset,
159                                                 enum conf_metadata_mergekey mt)
160 {
161     struct conf_metadata * md = 0;
162
163     if (!service || !service->metadata || !service->num_metadata
164         || field_id < 0  || !(field_id < service->num_metadata))
165         return 0;
166
167     md = service->metadata + field_id;
168     conf_metadata_assign(service->nmem, md, name, type, merge, setting,
169                          brief, termlist, rank, sortkey_offset,
170                          mt);
171     return md;
172 }
173
174
175 struct conf_sortkey * conf_service_add_sortkey(struct conf_service *service,
176                                                int field_id,
177                                                const char *name,
178                                                enum conf_sortkey_type type)
179 {
180     struct conf_sortkey * sk = 0;
181
182     if (!service || !service->sortkeys || !service->num_sortkeys
183         || field_id < 0  || !(field_id < service->num_sortkeys))
184         return 0;
185
186     //sk = &((service->sortkeys)[field_id]);
187     sk = service->sortkeys + field_id;
188     conf_sortkey_assign(service->nmem, sk, name, type);
189
190     return sk;
191 }
192
193
194 int conf_service_metadata_field_id(struct conf_service *service,
195                                    const char * name)
196 {
197     int i = 0;
198
199     if (!service || !service->metadata || !service->num_metadata)
200         return -1;
201
202     for(i = 0; i < service->num_metadata; i++) {
203         if (!strcmp(name, (service->metadata[i]).name))
204             return i;
205     }
206    
207     return -1;
208 }
209
210
211 int conf_service_sortkey_field_id(struct conf_service *service,
212                                   const char * name)
213 {
214     int i = 0;
215
216     if (!service || !service->sortkeys || !service->num_sortkeys)
217         return -1;
218
219     for(i = 0; i < service->num_sortkeys; i++) {
220         if (!strcmp(name, (service->sortkeys[i]).name))
221             return i;
222     }
223    
224     return -1;
225 }
226
227 static void conf_dir_path(struct conf_config *config, WRBUF w, const char *src)
228 {
229     if (config->confdir && wrbuf_len(config->confdir) > 0 &&
230         !yaz_is_abspath(src))
231     {
232         wrbuf_printf(w, "%s/%s", wrbuf_cstr(config->confdir), src);
233     }
234     else
235         wrbuf_puts(w, src);
236 }
237
238 static void service_destroy(struct conf_service *service)
239 {
240     if (service)
241     {
242         pp2_charset_destroy(service->relevance_pct);
243         pp2_charset_destroy(service->sort_pct);
244         pp2_charset_destroy(service->mergekey_pct);
245         nmem_destroy(service->nmem);
246     }
247 }
248
249 static struct conf_service *service_create(struct conf_config *config,
250                                            xmlNode *node, const char *service_id)
251 {
252     xmlNode *n;
253     int md_node = 0;
254     int sk_node = 0;
255
256     struct conf_service *service = 0;
257     int num_metadata = 0;
258     int num_sortkeys = 0;
259     
260     // count num_metadata and num_sortkeys
261     for (n = node->children; n; n = n->next)
262         if (n->type == XML_ELEMENT_NODE && !strcmp((const char *)
263                                                    n->name, "metadata"))
264         {
265             xmlChar *sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
266             num_metadata++;
267             if (sortkey && strcmp((const char *) sortkey, "no"))
268                 num_sortkeys++;
269             xmlFree(sortkey);
270         }
271
272     service = conf_service_create(config,
273                                   num_metadata, num_sortkeys, service_id);
274
275     for (n = node->children; n; n = n->next)
276     {
277         if (n->type != XML_ELEMENT_NODE)
278             continue;
279         if (!strcmp((const char *) n->name, "settings"))
280         {
281             if (service->settings)
282             {
283                 yaz_log(YLOG_FATAL, "Can't repeat 'settings'");
284                 return 0;
285             }
286             service->settings = parse_settings(config, service->nmem, n);
287             if (!service->settings)
288                 return 0;
289         }
290         else if (!strcmp((const char *) n->name, (const char *) "targetprofiles"))
291         {
292             if (service->targetprofiles)
293             {
294                 yaz_log(YLOG_FATAL, "Can't repeat targetprofiles");
295                 return 0;
296             }
297             if (!(service->targetprofiles = 
298                   parse_targetprofiles(service->nmem, n)))
299                 return 0;
300         }
301         else if (!strcmp((const char *) n->name, (const char *) "metadata"))
302         {
303             xmlChar *xml_name = xmlGetProp(n, (xmlChar *) "name");
304             xmlChar *xml_brief = xmlGetProp(n, (xmlChar *) "brief");
305             xmlChar *xml_sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
306             xmlChar *xml_merge = xmlGetProp(n, (xmlChar *) "merge");
307             xmlChar *xml_type = xmlGetProp(n, (xmlChar *) "type");
308             xmlChar *xml_termlist = xmlGetProp(n, (xmlChar *) "termlist");
309             xmlChar *xml_rank = xmlGetProp(n, (xmlChar *) "rank");
310             xmlChar *xml_setting = xmlGetProp(n, (xmlChar *) "setting");
311             xmlChar *xml_mergekey = xmlGetProp(n, (xmlChar *) "mergekey");
312
313             enum conf_metadata_type type = Metadata_type_generic;
314             enum conf_metadata_merge merge = Metadata_merge_no;
315             enum conf_setting_type setting = Metadata_setting_no;
316             enum conf_sortkey_type sk_type = Metadata_sortkey_relevance;
317             enum conf_metadata_mergekey mergekey_type = Metadata_mergekey_no;
318             int brief = 0;
319             int termlist = 0;
320             int rank = 0;
321             int sortkey_offset = 0;
322             
323             // now do the parsing logic
324             if (!xml_name)
325             {
326                 yaz_log(YLOG_FATAL, "Must specify name in metadata element");
327                 return 0;
328             }
329             if (xml_brief)
330             {
331                 if (!strcmp((const char *) xml_brief, "yes"))
332                     brief = 1;
333                  else if (strcmp((const char *) xml_brief, "no"))
334                 {
335                     yaz_log(YLOG_FATAL, "metadata/brief must be yes or no");
336                     return 0;
337                 }
338             }
339             else
340                 brief = 0;
341
342             if (xml_termlist)
343             {
344                 if (!strcmp((const char *) xml_termlist, "yes"))
345                     termlist = 1;
346                 else if (strcmp((const char *) xml_termlist, "no"))
347                 {
348                     yaz_log(YLOG_FATAL, "metadata/termlist must be yes or no");
349                     return 0;
350                 }
351             }
352             else
353                 termlist = 0;
354
355             if (xml_rank)
356                 rank = atoi((const char *) xml_rank);
357             else
358                 rank = 0;
359
360             if (xml_type)
361             {
362                 if (!strcmp((const char *) xml_type, "generic"))
363                     type = Metadata_type_generic;
364                 else if (!strcmp((const char *) xml_type, "year"))
365                     type = Metadata_type_year;
366                 else if (!strcmp((const char *) xml_type, "date"))
367                     type = Metadata_type_date;
368                 else
369                 {
370                     yaz_log(YLOG_FATAL, 
371                             "Unknown value for metadata/type: %s", xml_type);
372                     return 0;
373                 }
374             }
375             else
376                 type = Metadata_type_generic;
377
378             if (xml_merge)
379             {
380                 if (!strcmp((const char *) xml_merge, "no"))
381                     merge = Metadata_merge_no;
382                 else if (!strcmp((const char *) xml_merge, "unique"))
383                     merge = Metadata_merge_unique;
384                 else if (!strcmp((const char *) xml_merge, "longest"))
385                     merge = Metadata_merge_longest;
386                 else if (!strcmp((const char *) xml_merge, "range"))
387                     merge = Metadata_merge_range;
388                 else if (!strcmp((const char *) xml_merge, "all"))
389                     merge = Metadata_merge_all;
390                 else
391                 {
392                     yaz_log(YLOG_FATAL, 
393                             "Unknown value for metadata/merge: %s", xml_merge);
394                     return 0;
395                 }
396             }
397             else
398                 merge = Metadata_merge_no;
399
400             if (xml_setting)
401             {
402                 if (!strcmp((const char *) xml_setting, "no"))
403                     setting = Metadata_setting_no;
404                 else if (!strcmp((const char *) xml_setting, "postproc"))
405                     setting = Metadata_setting_postproc;
406                 else if (!strcmp((const char *) xml_setting, "parameter"))
407                     setting = Metadata_setting_parameter;
408                 else
409                 {
410                     yaz_log(YLOG_FATAL,
411                         "Unknown value for medadata/setting: %s", xml_setting);
412                     return 0;
413                 }
414             }
415
416             // add a sortkey if so specified
417             if (xml_sortkey && strcmp((const char *) xml_sortkey, "no"))
418             {
419                 if (merge == Metadata_merge_no)
420                 {
421                     yaz_log(YLOG_FATAL, 
422                             "Can't specify sortkey on a non-merged field");
423                     return 0;
424                 }
425                 if (!strcmp((const char *) xml_sortkey, "numeric"))
426                     sk_type = Metadata_sortkey_numeric;
427                 else if (!strcmp((const char *) xml_sortkey, "skiparticle"))
428                     sk_type = Metadata_sortkey_skiparticle;
429                 else
430                 {
431                     yaz_log(YLOG_FATAL,
432                             "Unknown sortkey in metadata element: %s", 
433                             xml_sortkey);
434                     return 0;
435                 }
436                 sortkey_offset = sk_node;
437
438                 conf_service_add_sortkey(service, sk_node,
439                                          (const char *) xml_name, sk_type);
440                 
441                 sk_node++;
442             }
443             else
444                 sortkey_offset = -1;
445
446             if (xml_mergekey && strcmp((const char *) xml_mergekey, "no"))
447             {
448                 mergekey_type = Metadata_mergekey_yes;
449             }
450
451
452             // metadata known, assign values
453             conf_service_add_metadata(service, md_node,
454                                       (const char *) xml_name,
455                                       type, merge, setting,
456                                       brief, termlist, rank, sortkey_offset,
457                                       mergekey_type);
458
459             xmlFree(xml_name);
460             xmlFree(xml_brief);
461             xmlFree(xml_sortkey);
462             xmlFree(xml_merge);
463             xmlFree(xml_type);
464             xmlFree(xml_termlist);
465             xmlFree(xml_rank);
466             xmlFree(xml_setting);
467             md_node++;
468         }
469         else
470         {
471             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
472             return 0;
473         }
474     }
475     return service;
476 }
477
478 static char *parse_settings(struct conf_config *config,
479                             NMEM nmem, xmlNode *node)
480 {
481     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
482     char *r;
483
484     if (src)
485     {
486         WRBUF w = wrbuf_alloc();
487         conf_dir_path(config, w, (const char *) src);
488         r = nmem_strdup(nmem, wrbuf_cstr(w));
489         wrbuf_destroy(w);
490     }
491     else
492     {
493         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
494         return 0;
495     }
496     xmlFree(src);
497     return r;
498 }
499
500 static struct conf_server *parse_server(struct conf_config *config,
501                                         NMEM nmem, xmlNode *node)
502 {
503     xmlNode *n;
504     struct conf_server *server = nmem_malloc(nmem, sizeof(struct conf_server));
505
506     server->host = 0;
507     server->port = 0;
508     server->proxy_host = 0;
509     server->proxy_port = 0;
510     server->myurl = 0;
511     server->proxy_addr = 0;
512     server->service = 0;
513     server->next = 0;
514     server->server_settings = 0;
515     server->relevance_pct = 0;
516     server->sort_pct = 0;
517     server->mergekey_pct = 0;
518
519     for (n = node->children; n; n = n->next)
520     {
521         if (n->type != XML_ELEMENT_NODE)
522             continue;
523         if (!strcmp((const char *) n->name, "listen"))
524         {
525             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
526             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
527             if (port)
528                 server->port = atoi((const char *) port);
529             if (host)
530                 server->host = nmem_strdup(nmem, (const char *) host);
531             xmlFree(port);
532             xmlFree(host);
533         }
534         else if (!strcmp((const char *) n->name, "proxy"))
535         {
536             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
537             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
538             xmlChar *myurl = xmlGetProp(n, (xmlChar *) "myurl");
539             if (port)
540                 server->proxy_port = atoi((const char *) port);
541             if (host)
542                 server->proxy_host = nmem_strdup(nmem, (const char *) host);
543             if (myurl)
544                 server->myurl = nmem_strdup(nmem, (const char *) myurl);
545             xmlFree(port);
546             xmlFree(host);
547             xmlFree(myurl);
548         }
549         else if (!strcmp((const char *) n->name, "settings"))
550         {
551             if (server->server_settings)
552             {
553                 yaz_log(YLOG_FATAL, "Can't repeat 'settings'");
554                 return 0;
555             }
556             if (!(server->server_settings = parse_settings(config, nmem, n)))
557                 return 0;
558         }
559         else if (!strcmp((const char *) n->name, "relevance"))
560         {
561             server->relevance_pct = pp2_charset_create_xml(n);
562             if (!server->relevance_pct)
563                 return 0;
564         }
565         else if (!strcmp((const char *) n->name, "sort"))
566         {
567             server->sort_pct = pp2_charset_create_xml(n);
568             if (!server->sort_pct)
569                 return 0;
570         }
571         else if (!strcmp((const char *) n->name, "mergekey"))
572         {
573             server->mergekey_pct = pp2_charset_create_xml(n);
574             if (!server->mergekey_pct)
575                 return 0;
576         }
577         else if (!strcmp((const char *) n->name, "service"))
578         {
579             char *service_id = (char *)
580                 xmlGetProp(n, (xmlChar *) "id");
581
582             struct conf_service **sp = &server->service;
583             for (; *sp; sp = &(*sp)->next)
584                 if ((*sp)->id && service_id &&
585                     0 == strcmp((*sp)->id, service_id))
586                 {
587                     yaz_log(YLOG_FATAL, "Duplicate service: %s", service_id);
588                     break;
589                 }
590                 else if (!(*sp)->id && !service_id)
591                 {
592                     yaz_log(YLOG_FATAL, "Duplicate unnamed service");
593                     break;
594                 }
595
596             if (*sp)  /* service already exist */
597                 return 0;
598             else
599             {
600                 struct conf_service *s = service_create(config, n, service_id);
601                 if (s)
602                 {
603                     if (server->relevance_pct)
604                     {
605                         s->relevance_pct = server->relevance_pct;
606                         pp2_charset_incref(s->relevance_pct);
607                     }
608                     else
609                         s->relevance_pct = pp2_charset_create(0);
610
611                     if (server->sort_pct)
612                     {
613                         
614                         s->sort_pct = server->sort_pct;
615                         pp2_charset_incref(s->sort_pct);
616                     }
617                     else
618                         s->sort_pct = pp2_charset_create(0);
619
620                     if (server->mergekey_pct)
621                     {
622                         s->mergekey_pct = server->mergekey_pct;
623                         pp2_charset_incref(s->mergekey_pct);
624                     }
625                     else
626                         s->mergekey_pct = pp2_charset_create(0);
627                     *sp = s;
628                 }
629             }
630             xmlFree(service_id);
631         }
632         else
633         {
634             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
635             return 0;
636         }
637     }
638     return server;
639 }
640
641 xsltStylesheet *conf_load_stylesheet(struct conf_config *config,
642                                      const char *fname)
643 {
644     WRBUF w = wrbuf_alloc();
645     xsltStylesheet *s;
646
647     conf_dir_path(config, w, fname);
648     s = xsltParseStylesheetFile((xmlChar *) wrbuf_cstr(w));
649     wrbuf_destroy(w);
650     return s;
651 }
652
653 static struct conf_targetprofiles *parse_targetprofiles(NMEM nmem,
654                                                         xmlNode *node)
655 {
656     struct conf_targetprofiles *r = nmem_malloc(nmem, sizeof(*r));
657     xmlChar *type = xmlGetProp(node, (xmlChar *) "type");
658     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
659
660     memset(r, 0, sizeof(*r));
661
662     if (type)
663     {
664         if (!strcmp((const char *) type, "local"))
665             r->type = Targetprofiles_local;
666         else
667         {
668             yaz_log(YLOG_FATAL, "Unknown targetprofile type");
669             return 0;
670         }
671     }
672     else
673     {
674         yaz_log(YLOG_FATAL, "Must specify type for targetprofile");
675         return 0;
676     }
677
678     if (src)
679         r->src = nmem_strdup(nmem, (const char *) src);
680     else
681     {
682         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
683         return 0;
684     }
685     xmlFree(type);
686     xmlFree(src);
687     return r;
688 }
689
690 struct conf_service *locate_service(struct conf_server *server,
691                                     const char *service_id)
692 {
693     struct conf_service *s = server->service;
694     for (; s; s = s->next)
695         if (s->id && service_id && 0 == strcmp(s->id, service_id))
696             return s;
697         else if (!s->id && !service_id)
698             return s;
699     return 0;
700 }
701
702
703 static int parse_config(struct conf_config *config, xmlNode *root)
704 {
705     xmlNode *n;
706
707     for (n = root->children; n; n = n->next)
708     {
709         if (n->type != XML_ELEMENT_NODE)
710             continue;
711         if (!strcmp((const char *) n->name, "server"))
712         {
713             struct conf_server *tmp = parse_server(config, config->nmem, n);
714             if (!tmp)
715                 return -1;
716             tmp->next = config->servers;
717             config->servers = tmp;
718         }
719         else if (!strcmp((const char *) n->name, "targetprofiles"))
720         {
721             yaz_log(YLOG_FATAL, "targetprofiles unsupported here. Must be part of service");
722             return -1;
723
724         }
725         else
726         {
727             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
728             return -1;
729         }
730     }
731     return 0;
732 }
733
734 static int process_config_includes(struct conf_config *config, xmlNode *n);
735
736 static int config_include_one(struct conf_config *config, xmlNode **sib,
737     const char *path)
738 {
739     struct stat st;
740     if (stat(path, &st) < 0)
741     {
742         yaz_log(YLOG_FATAL|YLOG_ERRNO, "stat %s", path);
743         return -1;
744     }
745     else
746     {
747         if ((st.st_mode & S_IFMT) == S_IFREG)
748         {
749             xmlDoc *doc = xmlParseFile(path);
750             yaz_log(YLOG_LOG, "processing include path=%s", path);
751             if (doc)
752             {
753                 xmlNodePtr t = xmlDocGetRootElement(doc);
754                 int ret = process_config_includes(config, t);
755                 *sib = xmlAddNextSibling(*sib, xmlCopyNode(t, 1));
756                 xmlFreeDoc(doc);
757                 if (ret)
758                     return -1;
759             }
760             else
761             {
762                 yaz_log(YLOG_FATAL, "Could not parse %s", path);
763                 return -1;
764             }
765         }
766     }
767     return 0;
768 }
769
770 static int config_include_src(struct conf_config *config, xmlNode **np,
771                               const char *src)
772 {
773     int ret = 0; /* return code. OK so far */
774     WRBUF w = wrbuf_alloc();
775     xmlNodePtr sib; /* our sibling that we append */
776     xmlNodePtr c; /* tmp node */
777
778     wrbuf_printf(w, " begin include src=\"%s\" ", src);
779
780     /* replace include element with a 'begin' comment */
781     sib = xmlNewComment((const xmlChar *) wrbuf_cstr(w));
782     xmlReplaceNode(*np, sib);
783
784     xmlFreeNode(*np);
785
786     wrbuf_rewind(w);
787     conf_dir_path(config, w, src);
788 #if USE_POSIX_GLOB
789     {
790         size_t i;
791         glob_t glob_res;
792         glob(wrbuf_cstr(w), 0 /* flags */, 0 /* errfunc */, &glob_res);
793         
794         for (i = 0; ret == 0 && i < glob_res.gl_pathc; i++)
795         {
796             const char *path = glob_res.gl_pathv[i];
797             ret = config_include_one(config, &sib, path);
798         }
799         globfree(&glob_res);
800     }
801 #else
802     ret = config_include_one(config, &sib, wrbuf_cstr(w));
803 #endif
804     wrbuf_rewind(w);
805     wrbuf_printf(w, " end include src=\"%s\" ", src);
806     c = xmlNewComment((const xmlChar *) wrbuf_cstr(w));
807     sib = xmlAddNextSibling(sib, c);
808     
809     *np = sib;
810     wrbuf_destroy(w);
811     return ret;
812 }
813
814 static int process_config_includes(struct conf_config *config, xmlNode *n)
815 {
816     for (; n; n = n->next)
817     {
818         if (n->type == XML_ELEMENT_NODE)
819         {
820             if (!strcmp((const char *) n->name, "include"))
821             {
822                 xmlChar *src = xmlGetProp(n, (xmlChar *) "src");
823                 if (src)
824                 {
825                     int ret = config_include_src(config, &n,
826                                                  (const char *) src);
827                     xmlFree(src);
828                     if (ret)
829                         return ret;
830                         
831                 }
832             }
833             else
834             {
835                 if (process_config_includes(config, n->children))
836                     return -1;
837             }
838         }
839     }
840     return 0;
841 }
842
843 struct conf_config *config_create(const char *fname, int verbose)
844 {
845     xmlDoc *doc = xmlParseFile(fname);
846     xmlNode *n;
847     const char *p;
848     int r;
849     NMEM nmem = nmem_create();
850     struct conf_config *config = nmem_malloc(nmem, sizeof(struct conf_config));
851
852     xmlSubstituteEntitiesDefault(1);
853     xmlLoadExtDtdDefaultValue = 1;
854     if (!doc)
855     {
856         yaz_log(YLOG_FATAL, "Failed to read %s", fname);
857         return 0;
858     }
859
860     config->nmem = nmem;
861     config->servers = 0;
862
863     config->confdir = wrbuf_alloc();
864     if ((p = strrchr(fname, 
865 #ifdef WIN32
866                      '\\'
867 #else
868                      '/'
869 #endif
870              )))
871     {
872         int len = p - fname;
873         wrbuf_write(config->confdir, fname, len);
874     }
875     wrbuf_puts(config->confdir, "");
876     
877     n = xmlDocGetRootElement(doc);
878     r = process_config_includes(config, n);
879     if (r == 0) /* OK */
880     {
881         if (verbose)
882         {
883             yaz_log(YLOG_LOG, "Configuration %s after include processing",
884                     fname);
885             xmlDocFormatDump(yaz_log_file(), doc, 0);
886         }
887         r = parse_config(config, n);
888     }
889     xmlFreeDoc(doc);
890
891     if (r)
892     {
893         config_destroy(config);
894         return 0;
895     }
896     return config;
897 }
898
899 void server_destroy(struct conf_server *server)
900 {
901     struct conf_service *s = server->service;
902     while (s)
903     {
904         struct conf_service *s_next = s->next;
905         service_destroy(s);
906         s = s_next;
907     }
908     pp2_charset_destroy(server->relevance_pct);
909     pp2_charset_destroy(server->sort_pct);
910     pp2_charset_destroy(server->mergekey_pct);
911 }
912
913 void config_destroy(struct conf_config *config)
914 {
915     if (config)
916     {
917         struct conf_server *server = config->servers;
918         while (server)
919         {
920             struct conf_server *s_next = server->next;
921             server_destroy(server);
922             server = s_next;
923         }
924         wrbuf_destroy(config->confdir);
925         nmem_destroy(config->nmem);
926     }
927 }
928
929 void config_read_settings(struct conf_config *config)
930 {
931     struct conf_service *s = config->servers->service;
932     for (;s ; s = s->next)
933     {
934         init_settings(s);
935         if (s->settings)
936             settings_read(s, s->settings);
937         else if (config->servers->server_settings)
938             settings_read(s, config->servers->server_settings);
939         else
940             yaz_log(YLOG_WARN, "No settings for service");
941     }
942 }
943
944 void config_stop_listeners(struct conf_config *conf)
945 {
946     struct conf_server *ser;
947     for (ser = conf->servers; ser; ser = ser->next)
948         http_close_server(ser);
949 }
950
951 int config_start_listeners(struct conf_config *conf,
952                            const char *listener_override)
953 {
954     struct conf_server *ser;
955     for (ser = conf->servers; ser; ser = ser->next)
956     {
957         WRBUF w = wrbuf_alloc();
958         int r;
959         if (listener_override)
960         {
961             wrbuf_puts(w, listener_override);
962             listener_override = 0; /* only first server is overriden */
963         }
964         else
965         {
966             if (ser->host)
967                 wrbuf_puts(w, ser->host);
968             if (ser->port)
969             {
970                 if (wrbuf_len(w))
971                     wrbuf_puts(w, ":");
972                 wrbuf_printf(w, "%d", ser->port);
973             }
974         }
975         r = http_init(wrbuf_cstr(w), ser);
976         wrbuf_destroy(w);
977         if (r)
978             return -1;
979
980         w = wrbuf_alloc();
981         if (ser->proxy_host || ser->proxy_port)
982         {
983             if (ser->proxy_host)
984                 wrbuf_puts(w, ser->proxy_host);
985             if (ser->proxy_port)
986             {
987                 if (wrbuf_len(w))
988                     wrbuf_puts(w, ":");
989                 wrbuf_printf(w, "%d", ser->proxy_port);
990             }
991         }
992         if (wrbuf_len(w))
993             http_set_proxyaddr(wrbuf_cstr(w), ser);
994         wrbuf_destroy(w);
995     }
996     return 0;
997 }
998
999 /*
1000  * Local variables:
1001  * c-basic-offset: 4
1002  * c-file-style: "Stroustrup"
1003  * indent-tabs-mode: nil
1004  * End:
1005  * vim: shiftwidth=4 tabstop=8 expandtab
1006  */
1007