/services/fconf/fconf_xml.c
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