PageRenderTime 68ms CodeModel.GetById 19ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 0ms

/services/fconf/fconf_xml.c

http://ftk.googlecode.com/
C | 829 lines | 669 code | 131 blank | 29 comment | 208 complexity | 835abd0a21ce44098a8cf7ea2a2078a6 MD5 | raw file
  1/*
  2 * File: fconf_xml.c
  3 * Author:  Li XianJing <xianjimli@hotmail.com>
  4 * Brief:   xml implementation for interface FConf.
  5 *
  6 * Copyright (c) 2009 - 2010  Li XianJing <xianjimli@hotmail.com>
  7 *
  8 * Licensed under the Academic Free License version 2.1
  9 *
 10 * This program is free software; you can redistribute it and/or modify
 11 * it under the terms of the GNU General Public License as published by
 12 * the Free Software Foundation; either version 2 of the License, or
 13 * (at your option) any later version.
 14 *
 15 * This program is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 18 * GNU General Public License for more details.
 19 *
 20 * You should have received a copy of the GNU General Public License
 21 * along with this program; if not, write to the Free Software
 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 23 */
 24
 25/*
 26 * History:
 27 * ================================================================
 28 * 2010-08-01 Li XianJing <xianjimli@hotmail.com> created
 29 *
 30 */
 31
 32#include "ftk_path.h"
 33#include "fconf_xml.h"
 34#include "ftk_mmap.h"
 35#include "ftk_util.h"
 36#include "ftk_xml_parser.h"
 37#include "ftk_allocator.h"
 38
 39typedef enum _XmlNodeAttr
 40{
 41	NODE_ATTR_READONLY = 1,
 42	NODE_ATTR_MODIFIED = 2
 43}XmlNodeAttr;
 44
 45typedef struct _XmlNode
 46{
 47	char* name;
 48	char* value;
 49	unsigned int attr;
 50	struct _XmlNode* next;
 51	struct _XmlNode* prev;
 52	struct _XmlNode* parent;
 53	struct _XmlNode* children;
 54}XmlNode;
 55
 56static XmlNode* xml_node_create(const char* name, const char* value)
 57{
 58	XmlNode* node = NULL;
 59	return_val_if_fail(name != NULL, NULL);
 60
 61	if((node = FTK_ZALLOC(sizeof(XmlNode))) != NULL)
 62	{
 63		node->name = ftk_strdup(name);
 64		if(node->name != NULL)
 65		{
 66			if(value != NULL)
 67			{
 68				node->value = ftk_strdup(value);
 69			}
 70		}
 71		else
 72		{	
 73			FTK_FREE(node);
 74		}
 75	}
 76
 77	return node;
 78}
 79
 80static Ret xml_node_set_value(XmlNode* node, const char* value)
 81{
 82	return_val_if_fail(node != NULL, RET_FAIL);
 83	return_val_if_fail((node->attr & NODE_ATTR_READONLY) == 0, RET_FAIL);
 84
 85	FTK_FREE(node->value);
 86	if(value != NULL)
 87	{
 88		node->value = ftk_strdup(value);
 89	}
 90
 91	return RET_OK;
 92}
 93
 94static Ret xml_node_set_readonly(XmlNode* node, int readonly)
 95{
 96	return_val_if_fail(node != NULL, RET_FAIL);
 97
 98	if(readonly)
 99	{
100		node->attr |= NODE_ATTR_READONLY;
101	}
102	else
103	{
104		node->attr &= ~NODE_ATTR_READONLY;
105	}
106
107	return RET_OK;
108}
109
110static Ret xml_node_set_modified(XmlNode* node, int modified)
111{
112	return_val_if_fail(node != NULL, RET_FAIL);
113
114	if(modified)
115	{
116		node->attr |= NODE_ATTR_MODIFIED;
117	}
118	else
119	{
120		node->attr &= ~NODE_ATTR_MODIFIED;
121	}
122
123	return RET_OK;
124}
125
126static int xml_node_get_child_count(XmlNode* node)
127{
128	int nr = 0;
129	XmlNode* iter = NULL;
130	return_val_if_fail(node != NULL, 0);
131
132	for(iter = node->children; iter != NULL; iter = iter->next)
133	{
134		nr++;
135	}
136
137	return nr;
138}
139
140static XmlNode* xml_node_get_child(XmlNode* node, size_t index)
141{
142	XmlNode* iter = NULL;
143	return_val_if_fail(node != NULL, 0);
144
145	for(iter = node->children; iter != NULL; iter = iter->next)
146	{
147		if(index == 0)
148		{
149			return iter;
150		}
151		else
152		{
153			index--;
154		}
155	}	
156
157	return NULL;
158}
159
160static XmlNode* xml_node_find(XmlNode* node, FtkPath* path)
161{
162	return_val_if_fail(node != NULL && node->name != NULL && path != NULL, NULL);
163
164	for(; node != NULL; node = node->next)
165	{
166		if(strcmp(node->name, ftk_path_current(path)) == 0)
167		{
168			if(ftk_path_is_leaf(path))
169			{
170				return node;
171			}
172			else if(node->children != NULL)
173			{
174				ftk_path_down(path);
175				return xml_node_find(node->children, path);
176			}
177			else
178			{
179				return NULL;
180			}
181		}
182	}
183
184	return NULL;
185}
186
187static XmlNode* xml_node_append_sibling(XmlNode* node, const char* name, const char* value)
188{
189	XmlNode* iter = NULL;
190	XmlNode* sibling = NULL;
191	return_val_if_fail(name != NULL, NULL);
192
193	if((sibling = xml_node_create(name, value)) != NULL)
194	{
195		if(node != NULL)
196		{
197			for(iter = node; iter->next != NULL; iter = iter->next)
198			{
199			}
200			
201			iter->next = sibling;
202			sibling->prev = iter;
203			sibling->parent = node->parent;
204		}
205	}
206
207	return sibling;
208}
209
210static Ret xml_node_add(XmlNode* node, FtkPath* path, const char* value)
211{
212	XmlNode* iter = node;
213	return_val_if_fail(node != NULL && node->name != NULL && path != NULL, RET_FAIL);
214
215	for(; iter != NULL; iter = iter->next)
216	{
217		if(strcmp(iter->name, ftk_path_current(path)) == 0)
218		{
219			if(ftk_path_is_leaf(path))
220			{
221				xml_node_set_value(iter, value);
222			}
223			else
224			{
225				ftk_path_down(path);
226				if(iter->children == NULL)
227				{
228					iter->children = xml_node_create(ftk_path_current(path), NULL);
229					iter->children->parent = iter;
230				}
231				xml_node_add(iter->children, path, value);
232			}
233			
234			return RET_OK;
235		}
236	}
237
238	iter = xml_node_append_sibling(node, ftk_path_current(path), value);
239	if(!ftk_path_is_leaf(path))
240	{
241		return xml_node_add(iter, path, value);
242	}
243
244	return RET_OK;
245}
246
247static void xml_node_destroy(XmlNode* node)
248{
249	XmlNode* iter = NULL;
250	XmlNode* temp = NULL;
251	
252	if(node != NULL)
253	{
254		iter = node->children;
255		while(iter != NULL)
256		{
257			temp = iter->next;
258			xml_node_destroy(iter);
259			iter = temp;
260		}
261
262		if(node->parent != NULL)
263		{
264			if(node->parent->children == node)
265			{
266				node->parent->children = node->next;
267			}
268		}
269
270		if(node->prev != NULL)
271		{
272			node->prev->next = node->next;
273		}
274
275		if(node->next != NULL)
276		{
277			node->next->prev = node->prev;
278		}
279
280		FTK_ZFREE(node, sizeof(XmlNode));
281	}
282
283	return;
284}
285
286static Ret xml_node_save(XmlNode* node, FILE* fp)
287{
288	return_val_if_fail(node != NULL && fp != NULL, RET_FAIL);
289
290	if(node->children == NULL)
291	{
292		fprintf(fp, "<%s value=\"%s\" readonly=\"%d\"/>\n", 
293			node->name, node->value, node->attr & NODE_ATTR_READONLY ? 1 : 0);
294	}
295	else
296	{
297		XmlNode* iter = NULL;
298		fprintf(fp, "<%s>\n", node->name);
299		for(iter = node->children; iter != NULL; iter = iter->next)
300		{
301			xml_node_save(iter, fp);
302		}
303		fprintf(fp, "</%s>\n", node->name);
304	}
305
306	return RET_OK;
307}
308
309typedef struct _PrivInfo
310{
311	int modified;
312	XmlNode* root;
313	char* root_path;
314	FtkPath* path;
315	FConfOnChanged on_changed;
316	void* on_changed_ctx;
317}PrivInfo;
318
319Ret fconf_xml_save(FConf* thiz)
320{
321	FILE* fp = NULL;
322	XmlNode* iter = NULL;
323	DECL_PRIV(thiz, priv);
324	char filename[FTK_MAX_PATH+1] = {0};
325	return_val_if_fail(thiz != NULL && priv->root != NULL, RET_FAIL);	
326
327	for(iter = priv->root; iter != NULL; iter = iter->next)
328	{
329		if(iter->attr & NODE_ATTR_MODIFIED)
330		{
331			ftk_snprintf(filename, FTK_MAX_PATH, "%s%c%s.cnf", priv->root_path, FTK_PATH_DELIM, iter->name);
332			if((fp = fopen(filename, "w+")) != NULL)
333			{
334				xml_node_save(iter, fp);
335				xml_node_set_modified(iter, 0);
336				fclose(fp);
337			}
338		}
339	}
340
341	priv->modified = 0;
342	ftk_logd("%s: done\n", __func__);
343
344	return RET_OK;
345}
346
347typedef struct _BuilderInfo
348{
349	XmlNode* root;
350	XmlNode* current;
351}BuilderInfo;
352
353static void fconf_xml_builder_on_start(FtkXmlBuilder* thiz, const char* tag, const char** attrs)
354{
355	int i = 0;
356	int readonly = 0;
357	XmlNode* node = NULL;
358	const char* data = NULL;
359	BuilderInfo* info = (BuilderInfo*)thiz->priv;
360	return_if_fail(thiz != NULL && tag != NULL && attrs != NULL);
361	
362	for(i = 0; attrs[i] != NULL; i+=2)
363	{
364		const char* name = attrs[i];
365		const char* value = attrs[i+1];
366		if(strcmp(name, "value") == 0)
367		{
368			data = value;
369		}
370		else if(strcmp(name, "readonly") == 0)
371		{
372			readonly = ftk_str2bool(value);
373		}
374	}
375
376	node = xml_node_append_sibling(info->current->children, tag, data);
377	return_if_fail(node != NULL);
378	if(readonly)
379	{
380		xml_node_set_readonly(node, readonly);
381	}
382	
383	if(info->current->children == NULL)
384	{
385		info->current->children = node;
386		node->parent = info->current;
387	}
388	info->current = node;
389
390	return;
391}
392
393static void fconf_xml_builder_on_end(FtkXmlBuilder* thiz, const char* tag)
394{
395	BuilderInfo* info = (BuilderInfo*)thiz->priv;
396	return_if_fail(info->current != NULL);
397
398	if(info->current->parent != NULL)
399	{
400		info->current = info->current->parent;
401	}
402
403	return;
404}
405
406static void fconf_xml_builder_on_text(FtkXmlBuilder* thiz, const char* text, size_t length)
407{
408	return;
409}
410
411static void fconf_xml_builder_destroy(FtkXmlBuilder* thiz)
412{
413	if(thiz != NULL)
414	{
415		FTK_ZFREE(thiz, sizeof(FtkXmlBuilder) + sizeof(BuilderInfo));
416	}
417
418	return;
419}
420
421static FtkXmlBuilder* fconf_xml_builder_create(void)
422{
423	FtkXmlBuilder* thiz = FTK_ZALLOC(sizeof(FtkXmlBuilder) + sizeof(BuilderInfo));
424
425	if(thiz != NULL)
426	{
427		thiz->on_start_element = fconf_xml_builder_on_start;
428		thiz->on_end_element   = fconf_xml_builder_on_end;
429		thiz->on_text		   = fconf_xml_builder_on_text;
430		thiz->destroy		   = fconf_xml_builder_destroy;
431	}
432
433	return thiz;
434}
435
436static XmlNode*  fconf_xml_parse(const char* name, const char* xml, size_t length)
437{
438	XmlNode* node = NULL;
439    FtkXmlParser* parser = NULL;
440    FtkXmlBuilder* builder = NULL;
441    return_val_if_fail(xml != NULL, NULL);
442
443	node = xml_node_create(name, NULL);
444	return_val_if_fail(node != NULL, NULL);
445
446    if((parser  = ftk_xml_parser_create()) == NULL)
447    {
448    	xml_node_destroy(node);
449    	return_val_if_fail(parser != NULL, NULL);
450    }
451
452    builder = fconf_xml_builder_create();
453    if(builder != NULL)
454    {
455		BuilderInfo* priv      = (BuilderInfo* )builder->priv;
456		priv->root = priv->current = node;
457        ftk_xml_parser_set_builder(parser, builder);
458        ftk_xml_parser_parse(parser, xml, length);
459    }
460    ftk_xml_builder_destroy(builder);
461    ftk_xml_parser_destroy(parser);
462
463    return node;
464}
465
466Ret  fconf_xml_load_buffer(FConf* thiz, const char* name, const char* xml, size_t length)
467{
468    DECL_PRIV(thiz, priv);
469    XmlNode* node = NULL;
470    XmlNode* iter = NULL;
471    node = fconf_xml_parse(name, xml, length);
472	return_val_if_fail(node != NULL, RET_FAIL);
473	
474	if(priv->root == NULL)
475	{
476		priv->root = node;
477	}
478	else
479	{
480		for(iter = priv->root; iter->next != NULL; iter = iter->next);
481
482		iter->next = node;
483		node->prev = iter;
484	}
485
486	return RET_OK;
487}
488
489Ret  fconf_xml_load_file(FConf* thiz, const char* filename)
490{
491    FtkMmap* m = NULL;
492    char* p = NULL;
493    char root_node_name[FTK_MAX_PATH + 1] = {0};
494    return_val_if_fail(thiz != NULL && filename != NULL, RET_FAIL);
495
496	p = strrchr(filename, FTK_PATH_DELIM);
497	if(p != NULL)
498	{
499		strncpy(root_node_name, p, FTK_MAX_PATH);
500	}
501	else
502	{
503		strncpy(root_node_name, filename, FTK_MAX_PATH);
504	}
505
506    if((p = strrchr(root_node_name, '.')) != NULL)
507    {
508    	*p = '\0';
509    }
510
511    m = ftk_mmap_create(filename, 0, -1);
512    return_val_if_fail(m != NULL, RET_FAIL);
513	fconf_xml_load_buffer(thiz, root_node_name, ftk_mmap_data(m), ftk_mmap_length(m));
514    ftk_mmap_destroy(m);
515
516    return RET_OK;
517}
518
519Ret  fconf_xml_load_dir(FConf* thiz, const char* path)
520{
521    DIR* dir = NULL;
522    struct dirent* iter = NULL;
523    char filename[FTK_MAX_PATH+1] = {0};
524    return_val_if_fail(thiz != NULL && path != NULL, RET_FAIL);
525    
526	dir = opendir(path);
527    return_val_if_fail(dir != NULL, RET_FAIL);
528
529    while((iter = readdir(dir)) != NULL)
530    {
531        if(iter->d_name[0] == '.') continue;
532        if(strstr(iter->d_name, ".cnf") == NULL) continue;
533
534        ftk_snprintf(filename, sizeof(filename)-1, "%s%c%s", path, FTK_PATH_DELIM, iter->d_name);
535        fconf_xml_load_file(thiz, filename);
536    }
537    closedir(dir);
538
539    return RET_OK;
540}
541
542Ret fconf_xml_load(FConf* thiz, const char* dir)
543{
544	return fconf_xml_load_dir(thiz, dir);
545}
546
547static Ret fconf_xml_reg_changed_notify(FConf* thiz, FConfOnChanged on_changed, void* ctx)
548{
549	DECL_PRIV(thiz, priv);
550
551	priv->on_changed = on_changed;
552	priv->on_changed_ctx = ctx;
553
554	return RET_OK;
555}
556
557static Ret fconf_xml_on_changed(FConf* thiz, FConfChangeType change_type, const char* value)
558{
559	XmlNode* iter = NULL;
560	DECL_PRIV(thiz, priv);
561
562	ftk_path_root(priv->path);
563	for(iter = priv->root; iter != NULL; iter = iter->next)
564	{
565		if(strcmp(iter->name, ftk_path_current(priv->path)) == 0)
566		{
567			xml_node_set_modified(iter, 1);
568			break;
569		}
570	}
571
572	if(priv->on_changed != NULL)
573	{
574		priv->on_changed(priv->on_changed_ctx, 1, change_type, ftk_path_full(priv->path), value);
575	}
576
577	priv->modified = 1;
578
579	return RET_OK;
580}
581
582
583Ret fconf_xml_remove(FConf* thiz, const char* path)
584{
585	Ret ret = RET_FAIL;
586	XmlNode* node = NULL;
587	DECL_PRIV(thiz, priv);
588	return_val_if_fail(thiz != NULL && priv->root != NULL && path != NULL, RET_FAIL);
589
590	ftk_path_set_path(priv->path, path);
591	if((node = xml_node_find(priv->root, priv->path)) != NULL)
592	{
593		if(priv->root == node)
594		{
595			priv->root = node->next;
596		}
597
598		ret = RET_OK;
599		xml_node_destroy(node);
600		fconf_xml_on_changed(thiz, FCONF_CHANGED_BY_REMOVE, NULL);
601	}
602
603	return ret;
604}
605
606Ret fconf_xml_get(FConf* thiz, const char* path, char** value)
607{
608	XmlNode* node = NULL;
609	DECL_PRIV(thiz, priv);
610	return_val_if_fail(thiz != NULL && priv->root != NULL && path != NULL && value != NULL, RET_FAIL);
611
612	ftk_path_set_path(priv->path, path);
613	if((node = xml_node_find(priv->root, priv->path)) != NULL)
614	{
615		*value = node->value;
616	}
617	else
618	{
619		*value = NULL;
620	}
621
622	return *value != NULL ? RET_OK : RET_FAIL;
623}
624
625Ret fconf_xml_set(FConf* thiz, const char* path, const char* value)
626{
627	Ret ret = RET_FAIL;
628	XmlNode* node = NULL;
629	DECL_PRIV(thiz, priv);
630	FConfChangeType type = FCONF_CHANGED_BY_SET;
631	return_val_if_fail(thiz != NULL && path != NULL && value != NULL, RET_FAIL);
632
633	ftk_path_set_path(priv->path, path);
634	if(priv->root == NULL)
635	{
636		type = FCONF_CHANGED_BY_ADD;
637		if(ftk_path_is_leaf(priv->path))
638		{
639			priv->root = xml_node_create(ftk_path_current(priv->path), value);
640		}
641		else
642		{
643			priv->root = xml_node_create(ftk_path_current(priv->path), NULL);
644			ret = xml_node_add(priv->root, priv->path, value);
645		}
646	}
647	else
648	{
649		if((node = xml_node_find(priv->root, priv->path)) != NULL)
650		{
651			ret = xml_node_set_value(node, value);
652		}
653		else
654		{
655			ftk_path_root(priv->path);
656			type = FCONF_CHANGED_BY_ADD;
657			ret = xml_node_add(priv->root, priv->path, value);
658		}
659	}
660
661	if(ret == RET_OK)
662	{
663		fconf_xml_on_changed(thiz, type, value);
664	}
665
666	return ret;
667}
668
669Ret fconf_xml_get_child_count(FConf* thiz, const char* path, int* nr)
670{
671	Ret ret = RET_FAIL;
672	XmlNode* node = NULL;
673	DECL_PRIV(thiz, priv);
674	return_val_if_fail(thiz != NULL && priv->root != NULL && path != NULL && nr != NULL, RET_FAIL);
675
676	*nr = 0;
677	ftk_path_set_path(priv->path, path);
678	if((node = xml_node_find(priv->root, priv->path)) != NULL)
679	{
680		ret = RET_OK;
681		*nr = xml_node_get_child_count(node);
682	}
683
684	return ret;
685}
686
687Ret fconf_xml_get_child(FConf* thiz, const char* path, int index, char** child)
688{
689	Ret ret = RET_FAIL;
690	XmlNode* node = NULL;
691	DECL_PRIV(thiz, priv);
692	return_val_if_fail(thiz != NULL && priv->root != NULL && path != NULL && child != NULL, RET_FAIL);
693
694	*child = NULL;
695	ftk_path_set_path(priv->path, path);
696	if((node = xml_node_find(priv->root, priv->path)) != NULL)
697	{
698		if((node = xml_node_get_child(node, index)) != NULL)
699		{
700			ret = RET_OK;
701			*child = node->name;
702		}
703	}
704
705	return ret;
706}
707
708static Ret fconf_xml_lock(FConf* thiz)
709{
710	return RET_FAIL;
711}
712
713static Ret fconf_xml_unlock(FConf* thiz)
714{
715	return RET_FAIL;
716}
717
718void fconf_xml_destroy(FConf* thiz)
719{
720	if(thiz != NULL)
721	{
722		DECL_PRIV(thiz, priv);
723		XmlNode* temp = NULL;
724		XmlNode* iter = priv->root;
725		
726		fconf_xml_save(thiz);
727		while(iter != NULL)
728		{
729			temp = iter->next;
730			xml_node_destroy(iter);
731			iter = temp;
732		}
733		ftk_path_destroy(priv->path);
734		FTK_FREE(priv->root_path);
735		FTK_FREE(thiz);
736	}
737
738	return;
739}
740
741int fconf_xml_is_dirty(FConf* thiz)
742{
743	DECL_PRIV(thiz, priv);
744	return_val_if_fail(thiz != NULL, 0);
745
746	return priv->modified;
747}
748
749FConf* fconf_xml_create(const char* dir)
750{
751	FConf* thiz = NULL;
752	
753	return_val_if_fail(dir != NULL, NULL);
754	thiz = FTK_ZALLOC(sizeof(FConf) + sizeof(PrivInfo));
755
756	if(thiz != NULL)
757	{
758		DECL_PRIV(thiz, priv);
759
760		thiz->lock = fconf_xml_lock;
761		thiz->unlock = fconf_xml_unlock;
762		thiz->remove = fconf_xml_remove;
763		thiz->set = fconf_xml_set;
764		thiz->get = fconf_xml_get;
765		thiz->get_child_count = fconf_xml_get_child_count;
766		thiz->get_child = fconf_xml_get_child;
767		thiz->reg_changed_notify = fconf_xml_reg_changed_notify;
768		thiz->destroy = fconf_xml_destroy;
769
770		priv->path = ftk_path_create(NULL);
771		if(dir != NULL)
772		{
773			priv->root_path = ftk_strdup(dir);
774			fconf_xml_load(thiz, dir);
775		}
776	}
777
778	return thiz;
779}
780
781#ifdef FCONF_XML_TEST
782#include <assert.h>
783#include "fconf.c"
784#include "ftk_allocator_default.h"
785
786void test_default(void)
787{
788	FConf* thiz = NULL;
789	thiz = fconf_xml_create("./config");
790	fconf_test(thiz);
791	fconf_xml_save(thiz);
792	fconf_destroy(thiz);
793
794	return;
795}
796
797const char* testcase = "<a1><b1><c1 value=\"data1\"/> <c2 value=\"data2\"/></b1>\
798<b2><c1 value=\"data3\"/> <c2 value=\"data4\"/></b2></a1>";
799
800void test_load(void)
801{
802	FConf* thiz = NULL;
803	char* data = NULL;
804	thiz = fconf_xml_create("./config");
805	fconf_xml_load_buffer(thiz, "test", testcase, strlen(testcase)); 
806	assert(fconf_get(thiz, "/test/a1/b1/c1", &data) == RET_OK);
807	assert(strcmp(data, "data1") == 0);
808	assert(fconf_get(thiz, "/test/a1/b1/c2", &data) == RET_OK);
809	assert(strcmp(data, "data2") == 0);
810	
811	assert(fconf_get(thiz, "/test/a1/b2/c1", &data) == RET_OK);
812	assert(strcmp(data, "data3") == 0);
813	assert(fconf_get(thiz, "/test/a1/b2/c2", &data) == RET_OK);
814	assert(strcmp(data, "data4") == 0);
815	fconf_destroy(thiz);
816
817	return;
818}
819int main(int argc, char* argv[])
820{
821#ifndef USE_STD_MALLOC
822	ftk_set_allocator((ftk_allocator_default_create()));
823#endif
824	test_default();
825	test_load();
826
827	return 0;
828}
829#endif