root/libswish3/trunk/src/libswish3/header.c

Revision 2129, 31.3 kB (checked in by karpet, 7 months ago)

use our string_to_int instead of strtol() directly

Line 
1 /*
2  * This file is part of libswish3
3  * Copyright (C) 2008 Peter Karman
4  *
5  *  libswish3 is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  libswish3 is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with libswish3; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20 /* read/write the swish.xml header file */
21
22 #include <libxml/xmlreader.h>
23 #include <libxml/xmlwriter.h>
24 #include <libxml/encoding.h>
25 #include "libswish3.h"
26
27 extern int SWISH_DEBUG;
28
29 /* local struct to ease passing around flags/state */
30 typedef struct
31 {
32     boolean isprops;
33     boolean ismetas;
34     boolean isindex;
35     boolean isparser;
36     boolean isalias;
37     boolean ismime;
38     const xmlChar *parent_name;
39     swish_Config *config;
40     boolean is_valid;
41     unsigned int prop_id;
42     unsigned int meta_id;
43 } headmaker;
44
45 typedef struct
46 {
47     void *thing1;
48     void *thing2;
49     void *thing3;
50 } things;
51
52 static void read_metaname_aliases(
53     xmlChar *str,
54     headmaker * h,
55     swish_MetaName *meta
56 );
57 static void read_metaname_attr(
58     const xmlChar *attr,
59     const xmlChar *attr_val,
60     swish_MetaName *meta,
61     headmaker * h
62 );
63 static void read_metaname(
64     xmlTextReaderPtr reader,
65     headmaker * h
66 );
67 static void read_property_aliases(
68     xmlChar *str,
69     headmaker * h,
70     swish_Property *prop
71 );
72 static void read_property_attr(
73     const xmlChar *attr,
74     const xmlChar *attr_val,
75     swish_Property *prop,
76     headmaker * h
77 );
78 static void read_property(
79     xmlTextReaderPtr reader,
80     headmaker * h
81 );
82 static void process_node(
83     xmlTextReaderPtr reader,
84     headmaker * h
85 );
86 static void read_key_values_pair(
87     xmlTextReaderPtr reader,
88     xmlHashTablePtr hash,
89     xmlChar *name
90 );
91 static void read_key_value_pair(
92     xmlTextReaderPtr reader,
93     xmlHashTablePtr hash,
94     xmlChar *name
95 );
96 static void
97 read_key_value_stringlist(
98     xmlTextReaderPtr reader,
99     xmlHashTablePtr hash,
100     xmlChar *name
101 );
102 static void read_header(
103     char *filename,
104     headmaker * h
105 );
106 static void test_meta_alias_for(
107     swish_MetaName *meta,
108     swish_Config *c,
109     xmlChar *name
110 );
111 static void test_prop_alias_for(
112     swish_Property *prop,
113     swish_Config *c,
114     xmlChar *name
115 );
116 static headmaker *init_headmaker(
117 );
118 static void write_open_tag(
119     xmlTextWriterPtr writer,
120     xmlChar *tag
121 );
122 static void write_close_tag(
123     xmlTextWriterPtr writer
124 );
125 static void write_element_with_content(
126     xmlTextWriterPtr writer,
127     xmlChar *tag,
128     xmlChar *content
129 );
130 static void write_metaname(
131     swish_MetaName *meta,
132     xmlTextWriterPtr writer,
133     xmlChar *name
134 );
135 static void write_metanames(
136     xmlTextWriterPtr writer,
137     xmlHashTablePtr metanames
138 );
139 static void write_hash_entry(
140     xmlChar *value,
141     xmlTextWriterPtr writer,
142     xmlChar *key
143 );
144 static void write_property(
145     swish_Property *prop,
146     xmlTextWriterPtr writer,
147     xmlChar *name
148 );
149 static void write_properties(
150     xmlTextWriterPtr writer,
151     xmlHashTablePtr properties
152 );
153 static void write_parser(
154     xmlChar *val,
155     xmlTextWriterPtr writer,
156     xmlChar *key
157 );
158 static void write_parsers(
159     xmlTextWriterPtr writer,
160     xmlHashTablePtr parsers
161 );
162 static void write_mime(
163     xmlChar *type,
164     things * things,
165     xmlChar *ext
166 );
167 static void write_mimes(
168     xmlTextWriterPtr writer,
169     xmlHashTablePtr mimes
170 );
171 static void write_index(
172     xmlTextWriterPtr writer,
173     xmlHashTablePtr index
174 );
175 static void write_tag_aliases(
176     xmlTextWriterPtr writer,
177     xmlHashTablePtr tag_aliases
178 );
179 static void write_misc(
180     xmlTextWriterPtr writer,
181     xmlHashTablePtr hash
182 );
183
184 static void
185 read_metaname_aliases(
186     xmlChar *str,
187     headmaker * h,
188     swish_MetaName *meta
189 )
190 {
191     swish_StringList *strlist;
192     int i;
193
194     strlist = swish_make_stringlist(str);
195
196 /* loop over each alias and create a MetaName for each,
197        setting alias_for to meta->name
198 */
199     for (i = 0; i < strlist->n; i++) {
200
201         if (!swish_hash_exists(h->config->metanames, strlist->word[i])) {
202             swish_MetaName *newmeta;
203             xmlChar *newname;
204             newname = swish_str_tolower(strlist->word[i]);
205
206 /* is this an existing metaname? pull it from hash and update */
207             if (swish_hash_exists(h->config->metanames, newname)) {
208                 newmeta = swish_hash_fetch(h->config->metanames, newname);
209             }
210 /* else new metaname */
211             else {
212                 newmeta = swish_init_metaname(newname);
213                 newmeta->ref_cnt++;
214                 newmeta->id = h->meta_id++;
215                 newmeta->bias = meta->bias;
216                 swish_hash_add(h->config->metanames, newmeta->name, newmeta);
217             }
218
219             newmeta->alias_for = swish_xstrdup(meta->name);
220
221 /* swish_debug_metaname(newmeta); */
222         }
223         else {
224             SWISH_CROAK
225                 ("Cannot alias MetaName %s to %s because %s is already a real MetaName",
226                  strlist->word[i], meta->name, strlist->word[i]);
227         }
228
229     }
230
231     swish_free_stringlist(strlist);
232 }
233
234 static void
235 read_metaname_attr(
236     const xmlChar *attr,
237     const xmlChar *attr_val,
238     swish_MetaName *meta,
239     headmaker * h
240 )
241 {
242     swish_MetaName *dupe;
243     if (xmlStrEqual(attr, (xmlChar *)"bias")) {
244         meta->bias = swish_string_to_int((char*)attr_val);
245     }
246     else if (xmlStrEqual(attr, (xmlChar *)"id")) {
247         // make sure id is not already assigned
248         if (swish_hash_exists(h->config->flags->meta_ids, (xmlChar*)attr_val)) {
249             dupe = swish_hash_fetch(h->config->flags->meta_ids, (xmlChar*)attr_val);
250             SWISH_CROAK("duplicate id %s on MetaName %s (already assigned to %s)",
251                 attr_val, meta->name, dupe->name);
252         }
253         meta->id = swish_string_to_int((char*)attr_val);
254         // cache for id lookup
255         swish_hash_add(h->config->flags->meta_ids, (xmlChar*)attr_val, meta);
256     }
257     else if (xmlStrEqual(attr, (xmlChar *)"alias_for")) {
258         meta->alias_for = swish_str_tolower(BAD_CAST attr_val);
259     }
260     else {
261         SWISH_CROAK("Unknown MetaName attribute: %s", attr);
262     }
263 }
264
265 static void
266 read_metaname(
267     xmlTextReaderPtr reader,
268     headmaker * h
269 )
270 {
271     xmlChar *value;
272     swish_MetaName *meta;
273
274     meta =
275         swish_init_metaname(swish_str_tolower((xmlChar *)xmlTextReaderConstName(reader)));
276     meta->ref_cnt++;
277     value = NULL;
278
279     if (xmlTextReaderHasValue(reader)
280         && xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) {
281         meta->name = swish_str_tolower((xmlChar *)h->parent_name);
282         read_metaname_aliases(xmlTextReaderValue(reader), h, meta);
283         return;
284     }
285
286     if (xmlTextReaderHasAttributes(reader)) {
287
288         xmlTextReaderMoveToFirstAttribute(reader);
289         read_metaname_attr(xmlTextReaderConstName(reader),
290                            xmlTextReaderConstValue(reader), meta, h);
291
292         while (xmlTextReaderMoveToNextAttribute(reader) == 1) {
293             read_metaname_attr(xmlTextReaderConstName(reader),
294                                xmlTextReaderConstValue(reader), meta, h);
295         }
296
297     }
298
299 /*  must have an id */
300     if (meta->id == -1) {
301         meta->id = h->meta_id++;
302     }
303
304     if (!swish_hash_exists(h->config->metanames, meta->name)) {
305         swish_hash_add(h->config->metanames, meta->name, meta);
306     }
307     else {
308         SWISH_WARN("MetaName %s is already defined", meta->name);
309 /*  TODO could be alias. how to check? */
310     }
311
312 /* swish_debug_metaname(meta); */
313
314     if (value != NULL) {
315         xmlFree(value);
316     }
317
318     h->parent_name = xmlTextReaderConstName(reader);
319
320 }
321
322 static void
323 read_property_aliases(
324     xmlChar *str,
325     headmaker * h,
326     swish_Property *prop
327 )
328 {
329     swish_StringList *strlist;
330     int i;
331
332     strlist = swish_make_stringlist(str);
333
334 /* loop over each alias and create a Property for each,
335        setting alias_for to prop->name
336 */
337     for (i = 0; i < strlist->n; i++) {
338
339         if (!swish_hash_exists(h->config->properties, strlist->word[i])) {
340             swish_Property *newprop =
341                 swish_init_property(swish_str_tolower(strlist->word[i]));
342             newprop->ref_cnt++;
343             newprop->alias_for = swish_xstrdup(prop->name);
344             newprop->id = h->prop_id++;
345             newprop->ignore_case = prop->ignore_case;
346             newprop->type = prop->type;
347             newprop->verbatim = prop->verbatim;
348             newprop->max = prop->max;
349             newprop->sort = prop->sort;
350             swish_hash_add(h->config->properties, newprop->name, newprop);
351 /* swish_debug_property(newprop); */
352         }
353         else {
354             SWISH_CROAK
355                 ("Cannot alias Property %s to %s because %s is already a real Property",
356                  strlist->word[i], prop->name, strlist->word[i]);
357         }
358
359     }
360
361     swish_free_stringlist(strlist);
362 }
363
364 static void
365 read_property_attr(
366     const xmlChar *attr,
367     const xmlChar *attr_val,
368     swish_Property *prop,
369     headmaker * h
370 )
371 {
372     swish_Property *dupe;
373    
374     if (xmlStrEqual(attr, (xmlChar *)"ignore_case")) {
375         prop->ignore_case = (boolean)swish_string_to_int((char *)attr_val);
376     }
377     else if (xmlStrEqual(attr, (xmlChar *)"max")) {
378         prop->max = swish_string_to_int((char *)attr_val);
379     }
380     else if (xmlStrEqual(attr, (xmlChar *)"verbatim")) {
381         prop->verbatim = (boolean)swish_string_to_int((char *)attr_val);
382     }
383     else if (xmlStrEqual(attr, (xmlChar *)"sort")) {
384         prop->sort = (boolean)swish_string_to_int((char *)attr_val);
385     }
386     else if (xmlStrEqual(attr, (xmlChar *)"id")) {
387         // make sure id is not already assigned
388         if (swish_hash_exists(h->config->flags->prop_ids, (xmlChar*)attr_val)) {
389             dupe = swish_hash_fetch(h->config->flags->prop_ids, (xmlChar*)attr_val);
390             SWISH_CROAK("duplicate id %s on MetaName %s (already assigned to %s)",
391                 attr_val, prop->name, dupe->name);
392         }
393         prop->id = swish_string_to_int((char*)attr_val);
394         // cache for id lookup
395         swish_hash_add(h->config->flags->prop_ids, (xmlChar*)attr_val, prop);
396     }
397     else if (xmlStrEqual(attr, (xmlChar *)"type")) {
398         if (xmlStrEqual(attr_val, (xmlChar *)"int")) {
399             prop->type = SWISH_PROP_INT;
400         }
401         else if (xmlStrEqual(attr_val, (xmlChar *)"date")) {
402             prop->type = SWISH_PROP_DATE;
403         }
404         else {
405             prop->type = SWISH_PROP_STRING;
406         }
407     }
408     else if (xmlStrEqual(attr, (xmlChar *)"alias_for")) {
409         prop->alias_for = swish_str_tolower(BAD_CAST attr_val);
410     }
411     else {
412         SWISH_CROAK("unknown Property attribute: %s", attr);
413     }
414
415 }
416
417 static void
418 read_property(
419     xmlTextReaderPtr reader,
420     headmaker * h
421 )
422 {
423     xmlChar *value;
424     swish_Property *prop;
425
426     prop =
427         swish_init_property(swish_str_tolower((xmlChar *)xmlTextReaderConstName(reader)));
428     prop->ref_cnt++;
429     value = NULL;
430
431     if (xmlTextReaderHasValue(reader)
432         && xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) {
433         prop->name = swish_str_tolower((xmlChar *)h->parent_name);
434         read_property_aliases(xmlTextReaderValue(reader), h, prop);
435         return;
436     }
437
438     if (xmlTextReaderHasAttributes(reader)) {
439
440         xmlTextReaderMoveToFirstAttribute(reader);
441         read_property_attr(xmlTextReaderConstName(reader),
442                            xmlTextReaderConstValue(reader), prop, h);
443
444         while (xmlTextReaderMoveToNextAttribute(reader) == 1) {
445             read_property_attr(xmlTextReaderConstName(reader),
446                                xmlTextReaderConstValue(reader), prop, h);
447         }
448
449     }
450
451     if (prop->id == -1) {
452         prop->id = h->prop_id++;
453     }
454
455     if (!swish_hash_exists(h->config->properties, prop->name)) {
456         swish_hash_add(h->config->properties, prop->name, prop);
457     }
458     else {
459 /* swish_debug_config( h->config ); */
460         SWISH_CROAK("Property %s is already defined", prop->name);
461     }
462
463 /* swish_debug_property(prop); */
464
465     if (value != NULL) {
466         xmlFree(value);
467     }
468
469     h->parent_name = xmlTextReaderConstName(reader);
470
471 }
472
473 static void
474 process_node(
475     xmlTextReaderPtr reader,
476     headmaker * h
477 )
478 {
479     const xmlChar *name, *value;
480     int type;
481
482     type = xmlTextReaderNodeType(reader);
483     name = xmlTextReaderConstName(reader);
484     value = xmlTextReaderConstValue(reader);
485
486 /* SWISH_DEBUG_MSG("name %s  type %d  value %s", name, type, value); */
487
488     if (name == NULL)
489         name = BAD_CAST "--";
490
491     if (type == XML_READER_TYPE_COMMENT)
492         return;
493
494     if (xmlStrEqual(name, (const xmlChar *)SWISH_HEADER_ROOT)) {
495         h->is_valid = 1;
496         return;
497     }
498     if (!h->is_valid) {
499         SWISH_CROAK("invalid header file");
500     }
501
502     if (swish_str_all_ws((xmlChar *)value)
503         && xmlStrEqual(name, (xmlChar *)"#text")) {
504         return;
505     }           
506
507     if (type == XML_READER_TYPE_END_ELEMENT) {
508         if (xmlStrEqual(name, (const xmlChar *)SWISH_PROP)) {
509             h->isprops = 0;
510             return;
511         }
512         else if (xmlStrEqual(name, (const xmlChar *)SWISH_META)) {
513             h->ismetas = 0;
514             return;
515         }
516         else if (xmlStrEqual(name, (const xmlChar *)SWISH_INDEX)) {
517             h->isindex = 0;
518             return;
519         }
520         else if (xmlStrEqual(name, (const xmlChar *)SWISH_PARSERS)) {
521             h->isparser = 0;
522             return;
523         }
524         else if (xmlStrEqual(name, (const xmlChar *)SWISH_MIME)) {
525             h->ismime = 0;
526             return;
527         }
528         else if (xmlStrEqual(name, (const xmlChar *)SWISH_ALIAS)) {
529             h->isalias = 0;
530             return;
531         }
532
533         //SWISH_DEBUG_MSG("END ELEMENT name %s  type %d  value %s", name, type, value);
534
535         return;
536
537     }
538     else {
539    
540              
541     /* the special include directive means we stop and process
542      * that config file immediately instead of storing the value
543      * in the hash.
544      */
545         if (xmlStrEqual(name, (xmlChar *)SWISH_INCLUDE_FILE)) {
546             swish_merge_config_with_header((char*)value, h->config);
547             return;
548         }
549         else if (xmlStrEqual(name, (const xmlChar *)SWISH_PROP)) {
550             h->isprops = 1;
551             return;
552         }
553         else if (xmlStrEqual(name, (const xmlChar *)SWISH_META)) {
554             h->ismetas = 1;
555             return;
556         }
557         else if (xmlStrEqual(name, (const xmlChar *)SWISH_INDEX)) {
558             h->isindex = 1;
559             return;
560         }
561         else if (xmlStrEqual(name, (const xmlChar *)SWISH_PARSERS)) {
562             h->isparser = 1;
563             return;
564         }
565         else if (xmlStrEqual(name, (const xmlChar *)SWISH_MIME)) {
566             h->ismime = 1;
567             return;
568         }
569         else if (xmlStrEqual(name, (const xmlChar *)SWISH_ALIAS)) {
570             h->isalias = 1;
571             return;
572         }
573
574         //SWISH_DEBUG_MSG("NOT END ELEMENT name %s  type %d  value %s", name, type, value);
575     }
576
577     if (type != XML_READER_TYPE_END_ELEMENT) {
578
579         if (h->isprops) {
580             read_property(reader, h);
581             return;
582         }
583         else if (h->ismetas) {
584             read_metaname(reader, h);
585             return;
586         }
587         else if (h->isindex) {
588             read_key_value_pair(reader, h->config->index, (xmlChar *)name);
589             return;
590         }
591         else if (h->isparser) {
592             read_key_values_pair(reader, h->config->parsers, (xmlChar *)name);
593             return;
594         }
595         else if (h->ismime) {
596             read_key_value_pair(reader, h->config->mimes, (xmlChar *)name);
597             return;
598         }
599         else if (h->isalias) {
600             read_key_values_pair(reader, h->config->tag_aliases, (xmlChar *)name);
601             return;
602         }
603         else if (xmlStrEqual((xmlChar *)SWISH_CLASS_ATTRIBUTES, (xmlChar *)name)) {
604             read_key_value_stringlist(reader, h->config->stringlists, (xmlChar *)name);
605             return;
606         }
607         else if (type == XML_READER_TYPE_ELEMENT) {
608             read_key_value_pair(reader, h->config->misc, (xmlChar *)name);
609             return;
610         }
611
612         /*
613            SWISH_DEBUG_MSG("STILL NOT END ELEMENT name %s  type %d  value %s", name, type, value);
614          */
615
616     }
617
618 }
619
620 static void
621 read_key_value_stringlist(
622     xmlTextReaderPtr reader,
623     xmlHashTablePtr hash,
624     xmlChar *name
625 )
626 {
627     swish_StringList *strlist;
628     xmlChar *str;
629     const xmlChar *value;
630
631 /* element. get text and add to misc */
632     if (xmlTextReaderRead(reader) == 1) {
633         if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) {
634
635             value = xmlTextReaderConstValue(reader);
636             str = swish_str_tolower((xmlChar *)value);
637             strlist = swish_make_stringlist(str);
638             if (swish_hash_exists(hash, name)) {
639                 swish_merge_stringlists(strlist, swish_hash_fetch(hash, name));
640             }
641             else {
642                 swish_hash_add(hash, name, strlist);
643             }
644             swish_xfree(str);
645         }
646         else {
647             SWISH_CROAK("header line missing value: %s", name);
648         }
649     }
650     else {
651         SWISH_CROAK("error reading value for header element %s", name);
652     }
653 }
654
655 static void
656 read_key_values_pair(
657     xmlTextReaderPtr reader,
658     xmlHashTablePtr hash,
659     xmlChar *name
660 )
661 {
662     swish_StringList *strlist;
663     xmlChar *str;
664     const xmlChar *value;
665     int i;
666
667 /* element. get text and add to misc */
668     if (xmlTextReaderRead(reader) == 1) {
669         if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) {
670
671             value = xmlTextReaderConstValue(reader);
672             str = swish_str_tolower((xmlChar *)value);
673             strlist = swish_make_stringlist(str);
674
675             for (i = 0; i < strlist->n; i++) {
676 /*  SWISH_DEBUG_MSG("key_values pair: %s -> %s", strlist->word[i], name);  */
677                 if (swish_hash_exists(hash, strlist->word[i])) {
678                     swish_hash_replace(hash, strlist->word[i], swish_xstrdup(name));
679                 }
680                 else {
681                     swish_hash_add(hash, strlist->word[i], swish_xstrdup(name));
682                 }
683             }
684
685             swish_free_stringlist(strlist);
686             swish_xfree(str);
687
688         }
689         else {
690             SWISH_CROAK("header line missing value: %s", name);
691         }
692     }
693     else {
694         SWISH_CROAK("error reading value for header element %s", name);
695     }
696
697 }
698
699 static void
700 read_key_value_pair(
701     xmlTextReaderPtr reader,
702     xmlHashTablePtr hash,
703     xmlChar *name
704 )
705 {
706     const xmlChar *value;
707
708 /* element. get text and add to misc */
709     if (xmlTextReaderRead(reader) == 1) {
710         if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) {
711             value = xmlTextReaderConstValue(reader);           
712 /*  SWISH_DEBUG_MSG("read key %s for value %s", name, value);  */
713             if (swish_hash_exists(hash, name)) {
714                 swish_hash_replace(hash, name, swish_xstrdup(value));
715             }
716             else {
717                 swish_hash_add(hash, name, swish_xstrdup(value));
718             }
719         }
720         else {
721             SWISH_CROAK("header line missing value: %s", name);
722         }
723     }
724     else {
725         SWISH_CROAK("error reading value for header element %s", name);
726     }
727
728 }
729
730 static void
731 read_header(
732     char *filename,
733     headmaker * h
734 )
735 {
736     xmlTextReaderPtr reader;
737     int ret;
738     struct stat fileinfo;
739
740 /* parse either a filename, or, if we can't stat it,
741        assume conf is a XML string
742 */
743     if (stat((char *)filename, &fileinfo)) {
744         reader =
745             xmlReaderForMemory((const char *)filename, xmlStrlen((xmlChar *)filename),
746                                "[ swish.xml ]", NULL, 0);
747
748         if (SWISH_DEBUG & SWISH_DEBUG_CONFIG) {
749             SWISH_DEBUG_MSG("header parsed in-memory");
750         }
751     }
752     else {
753         reader = xmlReaderForFile(filename, NULL, 0);
754
755         if (SWISH_DEBUG & SWISH_DEBUG_CONFIG) {
756             SWISH_DEBUG_MSG("header parsed from file");
757         }
758     }
759
760     if (reader != NULL) {
761         ret = xmlTextReaderRead(reader);
762         while (ret == 1) {
763             process_node(reader, h);
764             ret = xmlTextReaderRead(reader);
765         }
766         xmlFreeTextReader(reader);
767         if (ret != 0) {
768             SWISH_CROAK("%s : failed to parse\n", filename);
769         }
770     }
771     else {
772         SWISH_CROAK("Unable to open %s\n", filename);
773     }
774
775 /*
776      * Cleanup function for the XML library.
777 */
778     xmlCleanupParser();
779 }
780
781 static void
782 test_meta_alias_for(
783     swish_MetaName *meta,
784     swish_Config *c,
785     xmlChar *name
786 )
787 {
788     if (meta->alias_for != NULL && !swish_hash_exists(c->metanames, meta->alias_for)
789         ) {
790         SWISH_CROAK
791             ("MetaName '%s' has alias_for value of '%s' but no such MetaName defined",
792              name, meta->alias_for);
793     }
794 }
795
796 static void
797 test_prop_alias_for(
798     swish_Property *prop,
799     swish_Config *c,
800     xmlChar *name
801 )
802 {
803     if (prop->alias_for != NULL && !swish_hash_exists(c->properties, prop->alias_for)
804         ) {
805         SWISH_CROAK
806             ("Property '%s' has alias_for value of '%s' but no such Property defined",
807              name, prop->alias_for);
808     }
809 }
810
811 void
812 swish_config_test_alias_fors(
813     swish_Config *c
814 )
815 {
816     xmlHashScan(c->metanames, (xmlHashScanner)test_meta_alias_for, c);
817     xmlHashScan(c->properties, (xmlHashScanner)test_prop_alias_for, c);
818 }
819
820
821 static headmaker *
822 init_headmaker(
823 )
824 {
825     headmaker *h;
826     h = swish_xmalloc(sizeof(headmaker));
827     h->config = swish_init_config();
828 /*  mimes is set to NULL in default config but we need it to be a hash here. */
829     h->config->mimes = swish_init_hash(8);
830     h->isprops = 0;
831     h->ismetas = 0;
832     h->isindex = 0;
833     h->isalias = 0;
834     h->isparser = 0;
835     h->ismime = 0;
836     h->parent_name = NULL;
837     h->prop_id = SWISH_PROP_THIS_MUST_COME_LAST_ID;
838     h->meta_id = SWISH_META_THIS_MUST_COME_LAST_ID;
839     return h;
840 }
841
842 boolean
843 swish_validate_header(
844     char *filename
845 )
846 {
847     headmaker *h;
848     h = init_headmaker();
849     read_header(filename, h);
850
851 /*  test that all the alias_for links resolve ok */
852     swish_config_test_alias_fors(h->config);
853
854     swish_debug_config(h->config);
855     swish_free_config(h->config);
856     swish_xfree(h);
857     return 1;                   /* how to test ? */
858 }
859
860 boolean
861 swish_merge_config_with_header(
862     char *filename,
863     swish_Config *c
864 )
865 {
866     headmaker *h;
867     h = init_headmaker();
868     read_header(filename, h);
869     if (SWISH_DEBUG & SWISH_DEBUG_CONFIG) {
870         SWISH_DEBUG_MSG("read_header complete");
871     }
872     swish_config_merge(c, h->config);
873     if (SWISH_DEBUG & SWISH_DEBUG_CONFIG) {
874         SWISH_DEBUG_MSG("config_merge complete");
875     }
876     swish_free_config(h->config);
877     swish_xfree(h);
878
879 /*  test that all the alias_for links resolve ok */
880     swish_config_test_alias_fors(c);
881
882     return 1;
883 }
884
885 swish_Config *
886 swish_read_header(
887     char *filename
888 )
889 {
890     headmaker *h;
891     swish_Config *c;
892     h = init_headmaker();
893     read_header(filename, h);
894     c = h->config;
895     swish_xfree(h);
896     return c;
897 }
898
899 static void
900 write_open_tag(
901     xmlTextWriterPtr writer,
902     xmlChar *tag
903 )
904 {
905     int rc;
906     rc = xmlTextWriterStartElement(writer, tag);
907
908     if (rc < 0) {
909         SWISH_CROAK("Error writing element %s", tag);
910     }
911 }
912
913 static void
914 write_close_tag(
915     xmlTextWriterPtr writer
916 )
917 {
918     int rc;
919     rc = xmlTextWriterEndElement(writer);
920     if (rc < 0) {
921         SWISH_CROAK("Error at xmlTextWriterEndElement");
922     }
923 }
924
925 static void
926 write_element_with_content(
927     xmlTextWriterPtr writer,
928     xmlChar *tag,
929     xmlChar *content
930 )
931 {
932     int rc;
933     rc = xmlTextWriterWriteElement(writer, tag, content);
934     if (rc < 0) {
935         SWISH_CROAK("Error writing element %s with content %s", tag, content);
936     }
937 }
938
939 static void
940 write_metaname(
941     swish_MetaName *meta,
942     xmlTextWriterPtr writer,
943     xmlChar *name
944 )
945 {
946     int rc;
947     write_open_tag(writer, name);
948     rc = xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "id", "%d", meta->id);
949     if (rc < 0) {
950         SWISH_CROAK("Error writing metaname id attribute for %s", name);
951     }
952
953     if (meta->alias_for != NULL) {
954         rc = xmlTextWriterWriteAttribute(writer, BAD_CAST "alias_for", meta->alias_for);
955         if (rc < 0) {
956             SWISH_CROAK("Error writing metaname alias_for attribute for %s", name);
957         }
958
959     }
960     else {
961         rc = xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "bias", "%d", meta->bias);
962         if (rc < 0) {
963             SWISH_CROAK("Error writing metaname bias attribute for %s", name);
964         }
965     }
966
967     write_close_tag(writer);
968 }
969
970 static void
971 write_metanames(
972     xmlTextWriterPtr writer,
973     xmlHashTablePtr metanames
974 )
975 {
976     xmlHashScan(metanames, (xmlHashScanner)write_metaname, writer);
977 }
978
979 static void
980 write_hash_entry(
981     xmlChar *value,
982     xmlTextWriterPtr writer,
983     xmlChar *key
984 )
985 {
986     write_element_with_content(writer, key, value);
987 }
988
989 static void
990 write_property(
991     swish_Property *prop,
992     xmlTextWriterPtr writer,
993     xmlChar *name
994 )
995 {
996     int rc;
997     write_open_tag(writer, name);
998     rc = xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "id", "%d", prop->id);
999     if (rc < 0) {
1000         SWISH_CROAK("Error writing property id attribute for %s", name);
1001     }
1002
1003     if (prop->alias_for != NULL) {
1004         rc = xmlTextWriterWriteAttribute(writer, BAD_CAST "alias_for", prop->alias_for);
1005         if (rc < 0) {
1006             SWISH_CROAK("Error writing property alias_for attribute for %s", name);
1007         }
1008     }
1009     else {
1010
1011 /* all other attrs are irrelevant if this is an alias */
1012         rc = xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "ignore_case", "%d",
1013                                                prop->ignore_case);
1014         if (rc < 0) {
1015             SWISH_CROAK("Error writing property ignore_case attribute for %s", name);
1016         }
1017         rc = xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "verbatim", "%d",
1018                                                prop->verbatim);
1019         if (rc < 0) {
1020             SWISH_CROAK("Error writing property verbatim attribute for %s", name);
1021         }
1022         rc = xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "type", "%d", prop->type);
1023         if (rc < 0) {
1024             SWISH_CROAK("Error writing property type attribute for %s", name);
1025         }
1026         rc = xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "max", "%d", prop->max);
1027         if (rc < 0) {
1028             SWISH_CROAK("Error writing property max attribute for %s", name);
1029         }
1030         rc = xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "sort", "%d", prop->sort);
1031         if (rc < 0) {
1032             SWISH_CROAK("Error writing property sort attribute for %s", name);
1033         }
1034     }
1035     write_close_tag(writer);
1036 }
1037
1038 static void
1039 write_properties(
1040     xmlTextWriterPtr writer,
1041     xmlHashTablePtr properties
1042 )
1043 {
1044     xmlHashScan(properties, (xmlHashScanner)write_property, writer);
1045 }
1046
1047 static void