Annotation of gpl/axl/src/axl_dtd.c, revision 1.1
1.1 ! misho 1: /*
! 2: * LibAxl: Another XML library
! 3: * Copyright (C) 2006 Advanced Software Production Line, S.L.
! 4: *
! 5: * This program is free software; you can redistribute it and/or
! 6: * modify it under the terms of the GNU Lesser General Public License
! 7: * as published by the Free Software Foundation; either version 2.1 of
! 8: * the License, or (at your option) any later version.
! 9: *
! 10: * This program 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 Lesser General Public License for more details.
! 14: *
! 15: * You should have received a copy of the GNU Lesser General Public
! 16: * License along with this program; if not, write to the Free
! 17: * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
! 18: * 02111-1307 USA
! 19: *
! 20: * You may find a copy of the license under this software is released
! 21: * at COPYING file. This is LGPL software: you are welcome to
! 22: * develop proprietary applications using this library without any
! 23: * royalty or fee but returning back any change, improvement or
! 24: * addition in the form of source code, project image, documentation
! 25: * patches, etc.
! 26: *
! 27: * For commercial support on build XML enabled solutions contact us:
! 28: *
! 29: * Postal address:
! 30: * Advanced Software Production Line, S.L.
! 31: * Edificio Alius A, Oficina 102,
! 32: * C/ Antonio Suarez Nº 10,
! 33: * Alcalá de Henares 28802 Madrid
! 34: * Spain
! 35: *
! 36: * Email address:
! 37: * info@aspl.es - http://www.aspl.es/xml
! 38: */
! 39: #include <axl_decl.h>
! 40: #include <axl.h>
! 41:
! 42: #define LOG_DOMAIN "axl-dtd"
! 43:
! 44: struct _axlDtdElementListNode {
! 45: NodeType type;
! 46: AxlDtdTimes times;
! 47: axlPointer data;
! 48: };
! 49:
! 50: struct _axlDtdElementList {
! 51: /**
! 52: * @brief Allows to configure how is given top level
! 53: * configuration for nodes to be defined inside the xml
! 54: * document being configured. As defined in the XML 1.0
! 55: * Recomendation, available top level choices are: choice or
! 56: * sequence.
! 57: *
! 58: * They allow to configure allowed nodes to be selected as
! 59: * childs, from a set of node names, called choice or to
! 60: * configure which are the set of nodes to be used, in a
! 61: * particular order, called sequence.
! 62: *
! 63: * This variable allows to configure which is the top level
! 64: * section configuration: either a choice or a sequence.
! 65: *
! 66: * Keep in mind that, having only one element inside the
! 67: * itemList, there is no difference between the sequence and
! 68: * the choice.
! 69: */
! 70: AxlDtdNestedType type;
! 71:
! 72: /**
! 73: * @brief Allows to configure how many times is repeated a
! 74: * selection provided (by this element).
! 75: */
! 76: AxlDtdTimes times;
! 77:
! 78: /**
! 79: * @brief Item list, which contains more axlDtdElementList
! 80: * nodes, configuring elements allowed.
! 81: */
! 82: axlList * itemList;
! 83: };
! 84:
! 85: struct _axlDtdElement {
! 86: /**
! 87: * @brief The document type element declaration name. This is
! 88: * the name of the xml node being constrained.
! 89: */
! 90: char * name;
! 91:
! 92: /**
! 93: * @brief This is the type of the xml node being constrained.
! 94: */
! 95: AxlDtdElementType type;
! 96: /**
! 97: * @brief List of available items.
! 98: *
! 99: * This variable holds current top level list selection. See
! 100: * axlDtdElementList.type variable.
! 101: */
! 102: axlDtdElementList * list;
! 103:
! 104: /**
! 105: * @brief Minimum item list count to be matched while using
! 106: * this DTD element rule.
! 107: */
! 108: int minimum_match;
! 109: };
! 110:
! 111: struct _axlDtdAttributeDecl {
! 112: /**
! 113: * @brief Attribute name. This is the attribute value defined
! 114: * for the node.
! 115: */
! 116: char * name;
! 117:
! 118: /**
! 119: * @brief This is the attribute declaration type. It shows if
! 120: */
! 121: AxlDtdAttributeType type;
! 122:
! 123: /**
! 124: * @brief Allows to model whoe is
! 125: */
! 126: AxlDtdAttributeDefaults defaults;
! 127:
! 128: /**
! 129: * @brief This is a default value for the <!ATTLIST
! 130: * declaration received, in the case a FIXED value is required
! 131: * or a default value is declarted.
! 132: */
! 133: char * default_value;
! 134:
! 135: /**
! 136: * @brief Internal declaration for enum values defined for
! 137: * this rule. This list is only initialized in the case enum
! 138: * values are defined.
! 139: */
! 140: axlList * enumvalues;
! 141:
! 142: };
! 143:
! 144: struct _axlDtdAttribute {
! 145: /**
! 146: * @brief The document attribute list declaration name. This
! 147: * is the name of the node that will receive the constrain
! 148: * defined.
! 149: */
! 150: char * name;
! 151:
! 152: /**
! 153: * @brief This is the list of constrains defined for the
! 154: * node. It as list of \ref axlDtdAttributeDecl which defines
! 155: * the attribute that is declarted, if it is required, and the
! 156: * type of its content.
! 157: */
! 158: axlList * list;
! 159: };
! 160:
! 161: struct _axlDtdEntityExternalData {
! 162: /**
! 163: * @brief Contains the system literal reference. This is a URI
! 164: * reference to the resource pointed by the \ref axlDtdEntity
! 165: * definition.
! 166: */
! 167: char * system_literal;
! 168: /**
! 169: * @brief Contains the public literal information associated
! 170: * to the entity definition.
! 171: */
! 172: char * public_literal;
! 173: /**
! 174: * @brief Contains the NDATA information (a notation name
! 175: * reference).
! 176: */
! 177: char * ndata;
! 178: };
! 179:
! 180: struct _axlDtdEntity {
! 181: /**
! 182: * @brief Contains the entity name.
! 183: */
! 184: char * name;
! 185:
! 186: /**
! 187: * @brief Contains the entity type.
! 188: */
! 189: axlDtdEntityType type;
! 190:
! 191: /**
! 192: * @brief Content of the entity definition ([9] EntityValue).
! 193: */
! 194: char * content;
! 195:
! 196: /**
! 197: * @brief An entity definition can have a reference to a
! 198: * external resource. The following pointer contains
! 199: * information for the external resource pointed.
! 200: */
! 201: axlDtdEntityExternalData * data;
! 202: };
! 203:
! 204: struct _axlDtd {
! 205: /**
! 206: * @brief Holds all entity definitions inside the DTD
! 207: * declaration (<!ENTITY..>).
! 208: */
! 209: axlList * entities;
! 210:
! 211: /**
! 212: * @brief All elements inside the DTD declaration
! 213: * (<!ELEMENT..> ).
! 214: */
! 215: axlList * elements;
! 216:
! 217: /**
! 218: * @brief All attribute type declerations inside the DTD
! 219: * (<!ATTLIST..>)
! 220: */
! 221: axlList * attributes;
! 222:
! 223: /**
! 224: * @brief The element root, for the given DTD declaration.
! 225: */
! 226: axlDtdElement * root;
! 227:
! 228: /**
! 229: * @brief Internal flag that allows to notify that the DTD
! 230: * contains ID attribute declaration, making the DTD this
! 231: * references.
! 232: */
! 233: axl_bool haveIdDecl;
! 234:
! 235: /**
! 236: * @brief Flag that the dtd declaration have attributes which
! 237: * are flaged as IDREF.
! 238: */
! 239: axl_bool haveIdRefDecl;
! 240: };
! 241:
! 242: /**
! 243: * \defgroup axl_dtd_module Axl DTD: Document type declaration interface (functions, validation, and DTD parsing)
! 244: */
! 245:
! 246:
! 247: /**
! 248: * \addtogroup axl_dtd_module
! 249: * @{
! 250: */
! 251:
! 252:
! 253: /**
! 254: * @internal
! 255: *
! 256: * Allows to create a new dtd element list item, which represents a
! 257: * content particule inside an item list or a item list. This allows
! 258: * the recursion defined on the XML 1.0 standard.
! 259: *
! 260: * The function receives the node name and a reference list. According
! 261: * to the values the function creates a node which contains a leaf
! 262: * value or a node which contains a reference to the a new list which
! 263: * is nested.
! 264: */
! 265: axlDtdElementListNode * __create_axl_dtd_element_list (char * node_name,
! 266: axlDtdElementList * list)
! 267: {
! 268: axlDtdElementListNode * node;
! 269:
! 270: node = axl_new (axlDtdElementListNode, 1);
! 271:
! 272: /* create a node element reference */
! 273: if (node_name != NULL) {
! 274: node->data = node_name;
! 275: node->type = AXL_ELEMENT_NODE;
! 276: return node;
! 277: }
! 278:
! 279: /* create an element list reference */
! 280: if (list != NULL) {
! 281: node->data = list;
! 282: node->type = AXL_ELEMENT_LIST;
! 283: return node;
! 284: }
! 285:
! 286: /* if another type is requested, return NULL */
! 287: return NULL;
! 288: }
! 289:
! 290: /**
! 291: * @internal
! 292: *
! 293: * Support function used to destroy all items stored on a item list.
! 294: *
! 295: * @param node
! 296: */
! 297: void __destroy_axl_dtd_element_list (axlDtdElementListNode * node)
! 298: {
! 299: if (node == NULL)
! 300: return;
! 301: /* free the reference to the leaf node if defined */
! 302: if (node->type == AXL_ELEMENT_NODE)
! 303: axl_free (node->data);
! 304:
! 305: /* do not do nothing if the reference is not element list */
! 306: if (node->type == AXL_ELEMENT_LIST)
! 307: axl_dtd_item_list_free (node->data);
! 308:
! 309: /* free de node itself */
! 310: axl_free (node);
! 311: return;
! 312: }
! 313:
! 314: /**
! 315: * @internal
! 316: *
! 317: * @brief Support function to \ref axl_dtd_parse which creates a new
! 318: * empty DTD reference.
! 319: *
! 320: *
! 321: * @return A newly allocated \ref axlDtd reference.
! 322: */
! 323: axlDtd * __axl_dtd_new (void)
! 324: {
! 325: axlDtd * dtd;
! 326:
! 327: /* create the DTD element and nothing else. The rest of items
! 328: * created on demand */
! 329: dtd = axl_new (axlDtd, 1);
! 330:
! 331: return dtd;
! 332: }
! 333:
! 334: axl_bool __queue_items (axlPointer data, axlPointer _stack)
! 335: {
! 336: axlStack * stack = _stack;
! 337:
! 338: /* queue the data */
! 339: axl_stack_push (stack, data);
! 340:
! 341: /* return axl_false to make the function to not stop */
! 342: return axl_false;
! 343: }
! 344:
! 345: void __axl_dtd_queue_childs (axlStack * stack, axlNode * parent)
! 346: {
! 347: axlNode * child;
! 348:
! 349: /* get the first child */
! 350: child = axl_node_get_first_child (parent);
! 351: while (child != NULL) {
! 352:
! 353: /* queue the child */
! 354: axl_stack_push (stack, child);
! 355:
! 356: /* get the next child */
! 357: child = axl_node_get_next (child);
! 358: } /* end while */
! 359:
! 360: return;
! 361: }
! 362:
! 363: /**
! 364: * @internal
! 365: *
! 366: * Support internal function which allows to queue all items inside an
! 367: * axlDtdElementList to be checked.
! 368: *
! 369: * @param stack The stack where all data will be placed.
! 370: *
! 371: * @param dtd_element_list The dtd element list where the data will be
! 372: * extracted.
! 373: */
! 374: void __axl_dtd_queue_items (axlStack * stack, axlList * list)
! 375: {
! 376: /* call to queue items */
! 377: axl_list_lookup (list, __queue_items, stack);
! 378:
! 379: /* nothing more */
! 380: return;
! 381: }
! 382:
! 383: /**
! 384: * @internal
! 385: *
! 386: * Support function which allows to check if the provided two dtd
! 387: * elements are in fact, parent and child.
! 388: *
! 389: * DTD element have a parent-child relation based in the fact that the
! 390: * first define top level xml nodes that are followed, in the form of
! 391: * childs nodes, by other DTD elements that define more childs, etc...
! 392: *
! 393: * This function allows to check if the provided parent dtd element
! 394: * have references inside its content specification that proves that
! 395: * it is indeed a parent definition.
! 396: *
! 397: * @param dtd_element_parent The supposed DTD parent element.
! 398: * @param dtd_element_child The supposedd DTD child element.
! 399: *
! 400: * @return \ref axl_true if the function can confirm that the parent-child
! 401: * relation exists, \ref axl_false if not or it could be proved.
! 402: */
! 403: axl_bool __axl_dtd_get_is_parent (axlDtdElement * dtd_element_parent,
! 404: axlDtdElement * dtd_element_child)
! 405: {
! 406: axlStack * stack;
! 407: axlDtdElementListNode * node;
! 408: axlDtdElementList * list;
! 409:
! 410: /* check for leaf nodes, that, by definition, could be a
! 411: * parent of nothing. */
! 412: if (dtd_element_parent->list == NULL || dtd_element_parent->list->itemList == NULL) {
! 413: return axl_false;
! 414: }
! 415:
! 416: /* prepare all elements inside the stack to be checked */
! 417: stack = axl_stack_new (NULL);
! 418: __axl_dtd_queue_items (stack, dtd_element_parent->list->itemList);
! 419:
! 420:
! 421: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "stack size to operate: %d, list: %d",
! 422: axl_stack_size (stack),
! 423: axl_list_length (dtd_element_parent->list->itemList));
! 424:
! 425: /* now search for a content particule that makes are reference
! 426: * to the child DTD element */
! 427: do {
! 428: node = axl_stack_pop (stack);
! 429: switch (node->type) {
! 430: case AXL_ELEMENT_NODE:
! 431: /* leaf node case */
! 432: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a leaf node, checking it");
! 433:
! 434: /* seems this is a final node */
! 435: if (axl_cmp (node->data, dtd_element_child->name)) {
! 436: /* seems that the content
! 437: * specification makes a reference to
! 438: * the child node. */
! 439: axl_stack_free (stack);
! 440: return axl_true;
! 441: }
! 442: break;
! 443: case AXL_ELEMENT_LIST:
! 444: /* a nested list case */
! 445: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a complex node queuing its internal elements, while checking parent=%s for child=%s",
! 446: dtd_element_parent->name, dtd_element_child->name);
! 447: /* the item list read, is a complex value,
! 448: * queue all items inside to be inspected */
! 449: list = node->data;
! 450: __axl_dtd_queue_items (stack, list->itemList);
! 451: break;
! 452: case AXL_ELEMENT_NOT_DEFINED:
! 453: /* do nothing */
! 454: break;
! 455: }
! 456:
! 457: /* iterate until all elements are evaluated */
! 458: }while (! axl_stack_is_empty (stack));
! 459:
! 460: /* deallocates no longer used stack */
! 461: axl_stack_free (stack);
! 462:
! 463: /* either it isn't the parent or it can't be proved. */
! 464: return axl_false;
! 465: }
! 466:
! 467:
! 468: /**
! 469: * @internal
! 470: *
! 471: * Support function which allows to get which is the most top root
! 472: * node for the provided set of DTD elements.
! 473: */
! 474: axlDtdElement * __axl_dtd_get_new_root (axlDtd * dtd)
! 475: {
! 476: int iterator;
! 477: axl_bool change_detected;
! 478:
! 479: axlDtdElement * dtd_element_aux;
! 480: axlDtdElement * dtd_element_the_root_is_on_fire;
! 481:
! 482: /* set the very first root node */
! 483: dtd_element_the_root_is_on_fire = axl_list_get_nth (dtd->elements, 0);
! 484:
! 485: do {
! 486: /* check which is the top */
! 487: iterator = 0;
! 488: change_detected = axl_false;
! 489: while (iterator < axl_list_length (dtd->elements)) {
! 490:
! 491: /* get the next reference */
! 492: dtd_element_aux = axl_list_get_nth (dtd->elements, iterator);
! 493:
! 494: /* check which is the top */
! 495: if (__axl_dtd_get_is_parent (dtd_element_aux,
! 496: dtd_element_the_root_is_on_fire)) {
! 497: /* it seems that the new element is the root
! 498: * one, update the reference */
! 499: dtd_element_the_root_is_on_fire = dtd_element_aux;
! 500: change_detected = axl_true;
! 501: }
! 502:
! 503: /* update inner loop iterator */
! 504: iterator ++;
! 505: } /* while end */
! 506: }while (change_detected);
! 507:
! 508: /* return the root found */
! 509: return dtd_element_the_root_is_on_fire;
! 510: }
! 511:
! 512: /**
! 513: * @internal
! 514: *
! 515: * @brief Adds the axlDtdElement into the given axlDtd definition,
! 516: * checking that everything is properly configured, and ensuring that
! 517: * the root element gets properly configured.
! 518: *
! 519: * @param dtd The \ref axlDtd object that will receive the
! 520: * axlDtdElement.
! 521: *
! 522: * @param stream The \ref axlStream object that will be destroyed if
! 523: * something wrong is found.
! 524: *
! 525: * @param element The axlDtdElement to be added to the give axlDtd
! 526: * object.
! 527: *
! 528: * @return axl_true if the given axlDtdElement is compatible inside
! 529: * the axlDtd declaration or axl_false if a error is found.
! 530: */
! 531: axl_bool __axl_dtd_add_element (axlDtd * dtd, axlDtdElement * element,
! 532: axlStream * stream, axlError ** error)
! 533: {
! 534: int iterator = 0;
! 535: axlDtdElement * dtd_element_aux = NULL;
! 536:
! 537: /* check that there is no element already named like the
! 538: * element received. If it is the case drop an error */
! 539: while (iterator < axl_list_length (dtd->elements)) {
! 540: dtd_element_aux = axl_list_get_nth (dtd->elements, iterator);
! 541: if (axl_cmp (dtd_element_aux->name, element->name)) {
! 542: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD element for <%s> == <%s> was defined twice",
! 543: dtd_element_aux->name, element->name);
! 544:
! 545: axl_error_new (-1, "Find that an DTD element was defined twice (no more than one time is allowed)",
! 546: stream, error);
! 547: axl_stream_free (stream);
! 548: return axl_false;
! 549: }
! 550:
! 551: /* update current iterator */
! 552: iterator++;
! 553: }
! 554:
! 555: /* add the new DTD element to the list */
! 556: axl_list_add (dtd->elements, element);
! 557: return axl_true;
! 558: }
! 559:
! 560: /**
! 561: * @internal
! 562: *
! 563: * Internal support function which adds the provided content particule
! 564: * to the dtd item list received. It also perform all operations
! 565: * required for the chunk_matched option received.
! 566: *
! 567: * In the case the function fails to do its work, it will deallocate
! 568: * the stream, filling the error received.
! 569: *
! 570: * According to the chunk matched value, the function will react
! 571: * adding the element and configuring current element list.
! 572: *
! 573: */
! 574: axl_bool __axl_dtd_element_content_particule_add (axlDtdElementList * dtd_item_list,
! 575: char * string_aux,
! 576: int chunk_matched,
! 577: axlStream * stream,
! 578: axlError **error)
! 579: {
! 580: axlDtdElementListNode * node;
! 581:
! 582: /* check if the item list was creted or not */
! 583: if (dtd_item_list->itemList == NULL) {
! 584: dtd_item_list->itemList = axl_list_new (axl_list_always_return_1,
! 585: (axlDestroyFunc) __destroy_axl_dtd_element_list);
! 586: }
! 587:
! 588: /* create the node to be added */
! 589: node = __create_axl_dtd_element_list (string_aux, NULL);
! 590:
! 591: /* know add the element found */
! 592: axl_list_add (dtd_item_list->itemList, node);
! 593:
! 594: /* set configuration for item repetition */
! 595: switch (chunk_matched) {
! 596: case 4:
! 597: /* one or many times */
! 598: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting one to many repeat pattern: (+)");
! 599: node->times = ONE_OR_MANY;
! 600: break;
! 601: case 5:
! 602: /* zero or many times */
! 603: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting zero to many repeat pattern: (*)");
! 604: node->times = ZERO_OR_MANY;
! 605: break;
! 606: case 6:
! 607: /* zero or one time */
! 608: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting one to one repeat pattern: (?)");
! 609: node->times = ZERO_OR_ONE;
! 610: break;
! 611: default:
! 612: /* one and only one time */
! 613: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting one and only one repeat pattern: ()");
! 614: node->times = ONE_AND_ONLY_ONE;
! 615: }
! 616:
! 617: /* return that all is ok */
! 618: return axl_true;
! 619: }
! 620:
! 621:
! 622: /**
! 623: * @internal
! 624: *
! 625: * @brief Support function which allows to get current repetition
! 626: * configuration.
! 627: *
! 628: * @param stream The stream where the operation will be performed.
! 629: *
! 630: * @return Current configuration read, the function will properly work
! 631: * if it is called when it is espected to find a content specification
! 632: * repetition. If not found, the \ref ONE_AND_ONLY_ONE is returned.
! 633: */
! 634: AxlDtdTimes __axl_dtd_get_repetition_conf (axlStream * stream)
! 635: {
! 636: axl_return_val_if_fail (stream, ONE_AND_ONLY_ONE);
! 637:
! 638: if (axl_stream_inspect (stream, "?", 1) > 0) {
! 639:
! 640: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found '?' repetition conf");
! 641: /* seems the content specification could appear zero
! 642: * or one time */
! 643: return ZERO_OR_ONE;
! 644:
! 645: } else if (axl_stream_inspect (stream, "+", 1) > 0) {
! 646:
! 647: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found '+' repetition conf");
! 648: /* seems the content specification must appear one up
! 649: * to many */
! 650: return ONE_OR_MANY;
! 651:
! 652: } else if (axl_stream_inspect (stream, "*", 1) > 0) {
! 653:
! 654: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found '*' repetition conf");
! 655: /* seems the content specification could appear zero
! 656: * up to many */
! 657: return ZERO_OR_MANY;
! 658: }
! 659:
! 660: /* the content specification must appear */
! 661: return ONE_AND_ONLY_ONE;
! 662: }
! 663:
! 664: /**
! 665: * @internal
! 666: *
! 667: * Support function which creates a child item list, insert it to the
! 668: * parent item list received.
! 669: *
! 670: * @param parent
! 671: *
! 672: * @return
! 673: */
! 674: axlDtdElementList * __axl_dtd_create_and_queue (axlDtdElementList * parent)
! 675: {
! 676: axlDtdElementList * child;
! 677: axlDtdElementListNode * node;
! 678:
! 679: /* create the DTD item list */
! 680: child = axl_new (axlDtdElementList, 1);
! 681:
! 682: /* make by default the item list to be defined as "not
! 683: * defined" until the first separator is found */
! 684: child->type = STILL_UNDEF;
! 685:
! 686: /* create a node that */
! 687: node = __create_axl_dtd_element_list (NULL, child);
! 688:
! 689: /* create the parent list reference if weren't */
! 690: if (parent->itemList == NULL) {
! 691: parent->itemList = axl_list_new (axl_list_always_return_1,
! 692: (axlDestroyFunc) __destroy_axl_dtd_element_list);
! 693: }
! 694:
! 695: /* add the node */
! 696: axl_list_add (parent->itemList, node);
! 697:
! 698: /* return the new child created */
! 699: return child;
! 700: }
! 701:
! 702: /**
! 703: * @internal
! 704: *
! 705: * Updates current chunk readed information to allow perform a better
! 706: * code after calling this function.
! 707: *
! 708: */
! 709: void __axl_dtd_element_spec_update_chunk_matched (axlStream * stream,
! 710: int * chunk_matched)
! 711: {
! 712: /* check for for sequence or choice characters */
! 713: if (axl_stream_inspect (stream, ",", 1) > 0) {
! 714: /* flag that we have found a , (choice)
! 715: * separator */
! 716: (*chunk_matched) = 1;
! 717:
! 718: } else if (axl_stream_inspect (stream, "|", 1) > 0) {
! 719: /* flag that we have found a | (sequence)
! 720: * separator */
! 721: (*chunk_matched) = 2;
! 722:
! 723: } else if (axl_stream_inspect (stream, ")", 1) > 0) {
! 724: /* flag that we have found a | (sequence)
! 725: * separator */
! 726: (*chunk_matched) = 3;
! 727:
! 728: } else if (axl_stream_inspect (stream, "+", 1) > 0) {
! 729: /* flag that we have found a | (sequence)
! 730: * separator */
! 731: (*chunk_matched) = 4;
! 732:
! 733: } else if (axl_stream_inspect (stream, "*", 1) > 0) {
! 734: /* flag that we have found a | (sequence)
! 735: * separator */
! 736: (*chunk_matched) = 5;
! 737:
! 738: } else if (axl_stream_inspect (stream, "?", 1) > 0) {
! 739: /* flag that we have found a | (sequence)
! 740: * separator */
! 741: (*chunk_matched) = 6;
! 742: }
! 743:
! 744: return;
! 745: }
! 746:
! 747: /**
! 748: * @internal
! 749: *
! 750: * Support function to read the content particule separator once the
! 751: * repeat pattern was found
! 752: *
! 753: */
! 754: axl_bool __axl_dtd_element_spec_update_chunk_matched_for_cp_separator (axlStream * stream,
! 755: int * chunk_matched)
! 756: {
! 757: /* consume previous white spaces */
! 758: AXL_CONSUME_SPACES (stream);
! 759:
! 760: /* check for for sequence or choice characters */
! 761: if (axl_stream_inspect (stream, ",", 1) > 0) {
! 762: /* flag that we have found a , (choice)
! 763: * separator */
! 764: (*chunk_matched) = 1;
! 765: return axl_true;
! 766:
! 767: } else if (axl_stream_inspect (stream, "|", 1) > 0) {
! 768: /* flag that we have found a | (sequence)
! 769: * separator */
! 770: (*chunk_matched) = 2;
! 771: return axl_true;
! 772:
! 773: } else if (axl_stream_inspect (stream, ")", 1) > 0) {
! 774: /* flag that we have found a | (sequence)
! 775: * separator */
! 776: (*chunk_matched) = 3;
! 777: return axl_true;
! 778: }
! 779:
! 780: return axl_false;
! 781: }
! 782:
! 783: /**
! 784: * @internal
! 785: *
! 786: * Support function which allows to read the next content particule.
! 787: */
! 788: char * __axl_dtd_read_content_particule (axlStream * stream,
! 789: int * chunk_matched,
! 790: axlStack * dtd_item_stack,
! 791: axlError ** error)
! 792: {
! 793: char * string_aux;
! 794:
! 795: /* read the spec particule stopping when a white space
! 796: * or other character is found */
! 797: string_aux = axl_stream_get_until (stream, NULL, chunk_matched, axl_true, 8,
! 798: /* basic, default delimiters: 0, 1, 2, 3 */
! 799: " ", ",", "|", ")",
! 800: /* repetition configuration: 4, 5, 6 */
! 801: "+", "*", "?",
! 802: /* new dtd item list being opened: 8 */
! 803: "(");
! 804: if (string_aux == NULL) {
! 805: axl_error_new (-1, "Expected to find a element content specification particule, but it wasn't found",
! 806: stream, error);
! 807: axl_stack_free (dtd_item_stack);
! 808: axl_stream_free (stream);
! 809: return NULL;
! 810: }
! 811:
! 812: /* check the user doesn't nest item list in a not
! 813: * proper way */
! 814: if (*chunk_matched == 8) {
! 815: axl_error_new (-1, "Found a not proper nesting item list for a DTD element, before using ( a separator must be used (CHOICE: |, SEQUENCE: ,)",
! 816: stream, error);
! 817: axl_stack_free (dtd_item_stack);
! 818: axl_stream_free (stream);
! 819: return NULL;
! 820: }
! 821:
! 822: /* nullify stream internal reference */
! 823: axl_stream_nullify (stream, LAST_CHUNK);
! 824:
! 825: /* return the content particule found */
! 826: return string_aux;
! 827: }
! 828:
! 829: /**
! 830: * @internal
! 831: *
! 832: * Support function which reads current <!ELEMENT specification,
! 833: * configuring it to the received axlDtdElement.
! 834: *
! 835: * @param stream The stream where the axlDtdElement spec will be read.
! 836: *
! 837: * @param dtd_element The axlDtdElement that will receive the content
! 838: * spec.
! 839: *
! 840: * @param error An optional \ref axlError, where errors will be
! 841: * reported.
! 842: *
! 843: * @return \ref axl_true if the content spec was properly read or \ref
! 844: * axl_false if not.
! 845: */
! 846: axl_bool __axl_dtd_read_element_spec (axlStream * stream, axlDtdElement * dtd_element, axlError ** error)
! 847: {
! 848: char * string_aux;
! 849: axl_bool is_pcdata;
! 850: int chunk_matched = -1;
! 851: axlStack * dtd_item_stack;
! 852: axlDtdElementList * dtd_item_list;
! 853: axl_bool is_empty;
! 854:
! 855:
! 856: /* create the stack used to control which is
! 857: * the current context for the items read for
! 858: * the xml DTD especification (pd, pd2, (pr|po), ..) */
! 859: dtd_item_stack = axl_stack_new (NULL);
! 860:
! 861: /* create the DTD item list */
! 862: dtd_item_list = axl_new (axlDtdElementList, 1);
! 863:
! 864: /* by default, set still undef to change it once a separator
! 865: * is detected or the function ends. This will help to detect
! 866: * problems produced by people mixing content element
! 867: * separators. */
! 868: dtd_item_list->type = STILL_UNDEF;
! 869:
! 870: /* set the content spec list to the dtd element read */
! 871: dtd_element->list = dtd_item_list;
! 872:
! 873: /* push the item created */
! 874: /* axl_stack_push (dtd_item_stack, dtd_item_list); */
! 875:
! 876: /* consume previous white spaces */
! 877: AXL_CONSUME_SPACES (stream);
! 878:
! 879: /* check that the content specification have an ( */
! 880: if (! (axl_stream_inspect (stream, "(", 1))) {
! 881: axl_error_new (-1, "Expected to find a element content specification opener \"(\", but it wasn't found",
! 882: stream, error);
! 883: axl_stack_free (dtd_item_stack);
! 884: axl_stream_free (stream);
! 885: return axl_false;
! 886: }
! 887:
! 888: do {
! 889: /* consume previous white spaces */
! 890: AXL_CONSUME_SPACES (stream);
! 891:
! 892: /* a new item list have been opened */
! 893: if (axl_stream_inspect (stream, "(", 1) > 0) {
! 894:
! 895: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a DTD item list openining: %d",
! 896: axl_stack_size (dtd_item_stack));
! 897:
! 898: /* a new item list is being defined, we have
! 899: * to queue current dtd_item_list and create a
! 900: * new item list */
! 901: axl_stack_push (dtd_item_stack, dtd_item_list);
! 902:
! 903: /* create the DTD item list */
! 904: dtd_item_list = __axl_dtd_create_and_queue (dtd_item_list);
! 905:
! 906: /* let's continue at the begining */
! 907: continue;
! 908: }
! 909:
! 910:
! 911: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "iterating again to get a new content particule (item list size: %d)",
! 912: axl_dtd_item_list_count (dtd_item_list));
! 913:
! 914: /* read the next content particule: here is the chunk
! 915: * matched codes found:
! 916: * basic, default delimiters:
! 917: * 0, 1, 2, 3 -> " ", ",", "|", ")"
! 918: * repetition configuration:
! 919: * 4, 5, 6 -> "+", "*", "?",
! 920: * new dtd item list being opened:
! 921: * 8 -> "(" */
! 922: string_aux = __axl_dtd_read_content_particule (stream, &chunk_matched, dtd_item_stack, error);
! 923: if (string_aux == NULL)
! 924: return axl_false;
! 925:
! 926: /* check, and record, that the string read is
! 927: * PCDATA */
! 928: is_pcdata = axl_cmp (string_aux, "#PCDATA");
! 929:
! 930: /* add the item read if have something defined */
! 931:
! 932: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found content spec particule: (size: %d) '%s'",
! 933: strlen (string_aux),
! 934: string_aux);
! 935:
! 936: /* check if the have matched a white space: next check is
! 937: * based on the call to axl_stream_get_until at the caller
! 938: * function: " " */
! 939: if (chunk_matched == 0) {
! 940:
! 941: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
! 942: "found white spaces as delimiter, consuming them (current chunk matched: %d)",
! 943: chunk_matched);
! 944:
! 945: /* consume previous white spaces */
! 946: AXL_CONSUME_SPACES (stream);
! 947:
! 948: /* update current chunk_matched to conform to
! 949: * an stream that have all elements really
! 950: * close: the following function tries to read
! 951: * and update chunk_matched variable to point
! 952: * to the value read for ",", "|", "+", "*",
! 953: * "?" and ")" because white spaces were found */
! 954: __axl_dtd_element_spec_update_chunk_matched (stream, &chunk_matched);
! 955:
! 956:
! 957: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
! 958: "current chunk matched before update (%d)",
! 959: chunk_matched);
! 960: }
! 961:
! 962: /* add the content particule found, this function
! 963: * already detect that a white space was found and
! 964: * consumes all white spaces found */
! 965: if (!__axl_dtd_element_content_particule_add (dtd_item_list, string_aux, chunk_matched, stream, error))
! 966: return axl_false;
! 967:
! 968: if (chunk_matched == 4 || chunk_matched == 5 || chunk_matched == 6) {
! 969: /* found a repetition pattern */
! 970: if (! __axl_dtd_element_spec_update_chunk_matched_for_cp_separator (stream, &chunk_matched)) {
! 971: axl_error_new (-1, "Before a repetition pattern (*,+,?) expected to find a content particule separator",
! 972: stream, error);
! 973: axl_stack_free (dtd_item_stack);
! 974: axl_stream_free (stream);
! 975: return axl_false;
! 976: }
! 977: }
! 978:
! 979: /* set current sequence type accoring to separators
! 980: * used */
! 981: switch (chunk_matched) {
! 982: case 1:
! 983: if (dtd_item_list->type == CHOICE) {
! 984: axl_error_new (-1, "Detected that the DTD definition is mixing content particules separators at the same level ('|' and ','). First detected a sequence spec (,) but then detected a choice element (|)",
! 985: stream, error);
! 986: axl_stack_free (dtd_item_stack);
! 987: axl_stream_free (stream);
! 988: return axl_false;
! 989: }
! 990: dtd_item_list->type = SEQUENCE;
! 991: break;
! 992: case 2:
! 993: if (dtd_item_list->type == SEQUENCE) {
! 994: axl_error_new (-1, "Detected that the DTD definition is mixing content particules separators at the same level ('|' and ','). First detected a choice spec (|) but then detected a sequence element (,)",
! 995: stream, error);
! 996: axl_stack_free (dtd_item_stack);
! 997: axl_stream_free (stream);
! 998: return axl_false;
! 999: }
! 1000: dtd_item_list->type = CHOICE;
! 1001: break;
! 1002: }
! 1003:
! 1004: /* set element type if a element list terminator was
! 1005: * found ( 3 = ')' = chunk_matched) */
! 1006: if ((chunk_matched == 3) && is_pcdata) {
! 1007: if (axl_list_length (dtd_item_list->itemList) == 1)
! 1008: dtd_element->type = ELEMENT_TYPE_PCDATA;
! 1009: else if (axl_list_length (dtd_item_list->itemList) > 1)
! 1010: dtd_element->type = ELEMENT_TYPE_MIXED;
! 1011: }
! 1012:
! 1013: /* pop current element list header */
! 1014: if (chunk_matched == 3) {
! 1015: do {
! 1016: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a DTD item list termination: stack status: %d",
! 1017: axl_stack_size (dtd_item_stack));
! 1018: /* consume previous white spaces */
! 1019: AXL_CONSUME_SPACES (stream);
! 1020: dtd_item_list->times = __axl_dtd_get_repetition_conf (stream);
! 1021:
! 1022: /* consume previous white spaces */
! 1023: AXL_CONSUME_SPACES (stream);
! 1024:
! 1025: if (axl_stream_inspect (stream, ",", 1) > 0) {
! 1026: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a sequence (,) separator while reading terminator list");
! 1027:
! 1028: chunk_matched = 1;
! 1029: }
! 1030: else if (axl_stream_inspect (stream, "|", 1) > 0) {
! 1031: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a choice (|) separator while reading terminator list");
! 1032: chunk_matched = 2;
! 1033: }
! 1034:
! 1035: /* this means that a ) was found, we have to
! 1036: * pop current queue */
! 1037: is_empty = axl_stack_is_empty (dtd_item_stack);
! 1038: if (! is_empty) {
! 1039: dtd_item_list = axl_stack_pop (dtd_item_stack);
! 1040: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting the next item list in the stack, stack status: %d",
! 1041: axl_stack_size (dtd_item_stack));
! 1042: }
! 1043:
! 1044: /* special case: check if the next element to
! 1045: * be read is a new ) */
! 1046: /* consume previous white spaces */
! 1047: AXL_CONSUME_SPACES (stream);
! 1048:
! 1049: }while ((axl_stream_inspect (stream, ")", 1) > 0) && !is_empty);
! 1050:
! 1051: /* drop a log */
! 1052: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "terminator sequence status: chunk matched=%d ans stack status: %d",
! 1053: chunk_matched, axl_stack_size (dtd_item_stack));
! 1054:
! 1055: }
! 1056:
! 1057: /* check if we have finished */
! 1058: } while (chunk_matched != 3 || (! axl_stack_is_empty (dtd_item_stack)));
! 1059:
! 1060:
! 1061: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "content spec terminated, now lookup for the termination");
! 1062:
! 1063: /* consume previous white spaces */
! 1064: /* AXL_CONSUME_SPACES (stream);*/
! 1065:
! 1066: /* read here repetition specification */
! 1067: /* dtd_item_list->times = __axl_dtd_get_repetition_conf (stream); */
! 1068:
! 1069: /* set default content element separator */
! 1070: if (dtd_item_list->type == STILL_UNDEF)
! 1071: dtd_item_list->type = SEQUENCE;
! 1072:
! 1073: /* free the stack used */
! 1074: axl_stack_free (dtd_item_stack);
! 1075:
! 1076:
! 1077: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD content element specification found and parsed ok");
! 1078:
! 1079:
! 1080: /* content spec readed properly */
! 1081: return axl_true;
! 1082: }
! 1083:
! 1084: /**
! 1085: * @internal
! 1086: *
! 1087: * Calculates the number of nodes to be matched at minimum for the
! 1088: * provided DTD element.
! 1089: *
! 1090: * @param element The DTD element to configure with its minimum item
! 1091: * count to be matched.
! 1092: */
! 1093: int __axl_dtd_parse_element_get_compulsory_num (axlDtdElementList * list)
! 1094: {
! 1095: axlDtdElementListNode * itemNode;
! 1096: int count = 0;
! 1097: int iterator = 0;
! 1098:
! 1099: /* check for null parameters */
! 1100: if (list == NULL)
! 1101: return 0;
! 1102:
! 1103: /* only count for repetitiong patterns that makes obligatory
! 1104: * to have childs */
! 1105: if (list->times == ONE_AND_ONLY_ONE ||
! 1106: list->times == ONE_OR_MANY) {
! 1107:
! 1108: while (iterator < axl_list_length (list->itemList)) {
! 1109: /* get the reference for the item node */
! 1110: itemNode = axl_list_get_nth (list->itemList, iterator);
! 1111:
! 1112: /* check if the repetitiong patter is
! 1113: * compulsory */
! 1114: if (itemNode->times == ONE_OR_MANY ||
! 1115: itemNode->times == ONE_AND_ONLY_ONE) {
! 1116: /* check if we have an itemNode that has an
! 1117: * Node or a list */
! 1118: if (itemNode->type == AXL_ELEMENT_NODE) {
! 1119: /* we have an item node */
! 1120: count++;
! 1121: if (list->type == CHOICE) {
! 1122: /* because we have a
! 1123: * choice list, once
! 1124: * validated one item,
! 1125: * it is the minimum
! 1126: * requirement. */
! 1127: return count;
! 1128: }
! 1129: } else {
! 1130: /* we have a list */
! 1131: count += __axl_dtd_parse_element_get_compulsory_num (itemNode->data);
! 1132: }
! 1133: }
! 1134:
! 1135: /* update the index */
! 1136: iterator++;
! 1137: }
! 1138: }
! 1139:
! 1140: /* return current count */
! 1141: return count;
! 1142: }
! 1143:
! 1144:
! 1145: /**
! 1146: * @internal
! 1147: *
! 1148: * Parses a document type element that it is expected to be found at
! 1149: * the given stream.
! 1150: *
! 1151: * @param dtd The axlDtd where the element type readed must be added.
! 1152: *
! 1153: * @param stream The stream where the element type if expected to be found.
! 1154: *
! 1155: * @param error An axlError, optional, reference where error will be
! 1156: * reported.
! 1157: *
! 1158: * @return axl_true if the element was parsed properly, axl_false if
! 1159: * not. The stream associated will be unrefered and the axlError
! 1160: * provided will be filled if an error is found.
! 1161: */
! 1162: axl_bool __axl_dtd_parse_element (axlDtd * dtd, axlStream * stream, axlError ** error)
! 1163: {
! 1164: char * string_aux;
! 1165: int matched_chunk = -1;
! 1166: axlDtdElement * element;
! 1167:
! 1168: /* init the dtd element list */
! 1169: if (dtd->elements == NULL)
! 1170: dtd->elements = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_dtd_element_free);
! 1171:
! 1172: /* consume previous white spaces */
! 1173: AXL_CONSUME_SPACES (stream);
! 1174:
! 1175: /* get for the first element declaration */
! 1176: if (! (axl_stream_inspect (stream, "<!ELEMENT", 9) > 0)) {
! 1177: axl_error_new (-1, "Expected to receive a <!ELEMENT, but it wasn't found", stream, error);
! 1178: axl_stream_free (stream);
! 1179: return axl_false;
! 1180: }
! 1181:
! 1182: /* consume previous white spaces */
! 1183: AXL_CONSUME_SPACES (stream);
! 1184:
! 1185: /* get the element name */
! 1186: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_false, 3, ">", "(", " ", "<!ELEMENT");
! 1187: if (string_aux == NULL) {
! 1188: axl_error_new (-1, "Expected to receive a DTD element name for <!ELEMENT declaration, but not found", stream, error);
! 1189: axl_stream_free (stream);
! 1190: return axl_false;
! 1191: }
! 1192:
! 1193: /* check that the DTD have an element name and an element type */
! 1194: if ((matched_chunk == 0) || (matched_chunk == 3)) {
! 1195: axl_error_new (-1, "Found a DTD <!ELEMENT declaration, without content specification. Missing value, examples: EMPTY, ANY, (..)", stream, error);
! 1196: axl_stream_free (stream);
! 1197: return axl_false;
! 1198: }
! 1199:
! 1200: /* nullify internal stream content */
! 1201: axl_stream_nullify (stream, LAST_CHUNK);
! 1202:
! 1203: /* create the DTD element */
! 1204: element = axl_new (axlDtdElement, 1);
! 1205: element->name = string_aux;
! 1206:
! 1207: /* consume previous white spaces */
! 1208: AXL_CONSUME_SPACES (stream);
! 1209:
! 1210: /* now, check for the basic cases: ANY and EMPTY */
! 1211: if (axl_stream_peek (stream, "EMPTY", 5) > 0) {
! 1212: /* accept previous peek */
! 1213: axl_stream_accept (stream);
! 1214:
! 1215: /* found empty declaration */
! 1216: element->type = ELEMENT_TYPE_EMPTY;
! 1217:
! 1218: } else if (axl_stream_peek (stream, "ANY", 3) > 0) {
! 1219: /* accept previous peek */
! 1220: axl_stream_accept (stream);
! 1221:
! 1222: /* found any declaration */
! 1223: element->type = ELEMENT_TYPE_ANY;
! 1224: } else {
! 1225: /* complex element type declaration, let's roll now
! 1226: * get the element content type read current dtd
! 1227: * element spec.
! 1228: *
! 1229: * By default, any comple element type definition,
! 1230: * have childrens, until PC data definition is found,
! 1231: * which leads to the two possible values: Mixed and
! 1232: * PcData */
! 1233: element->type = ELEMENT_TYPE_CHILDREN;
! 1234: if (!__axl_dtd_read_element_spec (stream, element, error))
! 1235: return axl_false;
! 1236: }
! 1237:
! 1238: /* add element found */
! 1239: if (! __axl_dtd_add_element (dtd, element, stream, error))
! 1240: return axl_false;
! 1241:
! 1242: /* consume previous white spaces */
! 1243: AXL_CONSUME_SPACES (stream);
! 1244:
! 1245: /* check for the last DTD declaration */
! 1246: if (! (axl_stream_inspect (stream, ">", 1))) {
! 1247: axl_error_new (-1, "Unable to find last, > terminator for the DTD <!ELEMENT declaration", stream, error);
! 1248: axl_stream_free (stream);
! 1249: return axl_false;
! 1250: }
! 1251:
! 1252: /* now, count the number of obligatory elements, required for
! 1253: * the validation process */
! 1254: element->minimum_match = __axl_dtd_parse_element_get_compulsory_num (element->list);
! 1255:
! 1256: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD element declaration read complete: minimum matching elements: %d",
! 1257: element->minimum_match);
! 1258:
! 1259: /* element type declaration completely read */
! 1260: return axl_true;
! 1261: }
! 1262:
! 1263: /**
! 1264: * @internal
! 1265: *
! 1266: * Destroy the provided reference and its associated data.
! 1267: *
! 1268: * @param decl The reference declaration.
! 1269: */
! 1270: void axl_dtd_attribute_decl_free (axlDtdAttributeDecl * decl)
! 1271: {
! 1272: /* free the rule name */
! 1273: if (decl->name != NULL)
! 1274: axl_free (decl->name);
! 1275:
! 1276: /* free the default value */
! 1277: if (decl->default_value != NULL)
! 1278: axl_free (decl->default_value);
! 1279:
! 1280: /* free enum declaration list if defined */
! 1281: if (decl->enumvalues != NULL)
! 1282: axl_list_free (decl->enumvalues);
! 1283:
! 1284: /* free the node itself */
! 1285: axl_free (decl);
! 1286:
! 1287: /* nothing more to do */
! 1288: return;
! 1289: }
! 1290:
! 1291: /**
! 1292: * @internal function to dealloc an single attribute set decleration.
! 1293: *
! 1294: * @param attribute The reference to dealloc.
! 1295: */
! 1296: void axl_dtd_attribute_free (axlDtdAttribute * attribute)
! 1297: {
! 1298: /* free the attribute list, name and the node itself */
! 1299: axl_free (attribute->name);
! 1300: axl_list_free (attribute->list);
! 1301: axl_free (attribute);
! 1302:
! 1303: return;
! 1304: }
! 1305:
! 1306: axl_bool __find_attr_decl (axlPointer _element, axlPointer data)
! 1307: {
! 1308: axlDtdAttributeDecl * decl = _element;
! 1309: char * name = data;
! 1310:
! 1311: /* check the name */
! 1312: if (axl_cmp (decl->name, name))
! 1313: return axl_true;
! 1314:
! 1315: /* it is not the element */
! 1316: return axl_false;
! 1317: }
! 1318:
! 1319: /**
! 1320: * @brief Allows to check if the stream contains a reference to a
! 1321: * entity, calling the resolver to get the replacement text to be
! 1322: * placed.
! 1323: *
! 1324: * @param resolver The function to be called with the replacement
! 1325: * text. This function must return the replacement text or NULL if it
! 1326: * fails. Failing to return a reference resolution will make the
! 1327: * entity reference to appear as is.
! 1328: *
! 1329: * @param resolver The entity reference resolver function to be called
! 1330: * to solve references found.
! 1331: *
! 1332: * @param data User defined data provided to the function, passed
! 1333: * directly to the resolver function once executed.
! 1334: *
! 1335: * @param stream The stream where the entity reference could appear.
! 1336: *
! 1337: * @param prefix The reference prefix to recognize. Values allowed
! 1338: * are: % (DTD references) and & (general entity references).
! 1339: *
! 1340: * @param error Optional reference to the axlError to report textual
! 1341: * diagnostic errors.
! 1342: *
! 1343: * @return The function return \ref axl_false if some error while
! 1344: * resolving entity references was found. Otherwise the function
! 1345: * return axl_true.
! 1346: */
! 1347: axl_bool axl_dtd_check_entity_ref_and_expand (axlDtdEntityResolver resolver,
! 1348: axlPointer data,
! 1349: axlStream * stream,
! 1350: const char * prefix,
! 1351: axlError ** error)
! 1352:
! 1353: {
! 1354: char * string_aux;
! 1355: char * new_value;
! 1356: int index;
! 1357:
! 1358: /* check if we have an entity reference using the provided prefix */
! 1359: index = axl_stream_get_index (stream);
! 1360: if (! (axl_stream_inspect (stream, prefix, 1) > 0))
! 1361: return axl_true;
! 1362:
! 1363: /* get the entity reference until the end */
! 1364: string_aux = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, ";");
! 1365: if (string_aux == NULL) {
! 1366: axl_error_new (-1, "null value received while expecting to find the entity reference to resolve.", stream, error);
! 1367: axl_stream_free (stream);
! 1368: return axl_false;
! 1369: } /* end if */
! 1370:
! 1371: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found entity reference: %s%s;...resolving", prefix, string_aux);
! 1372:
! 1373: /* resolve the reference */
! 1374: new_value = (char *) resolver (string_aux, data);
! 1375: if (new_value == NULL) {
! 1376: axl_stream_move (stream, index);
! 1377: return axl_true;
! 1378: } /* end if */
! 1379:
! 1380: /* accept content consumed */
! 1381: axl_stream_accept (stream);
! 1382:
! 1383: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "entity resolved to: %s", new_value);
! 1384:
! 1385: /* place the replacement data at the start of the stream */
! 1386: new_value = axl_strdup_printf ("%s ", new_value);
! 1387: axl_stream_push (stream, new_value, strlen (new_value));
! 1388: axl_free (new_value);
! 1389:
! 1390: return axl_true;
! 1391: }
! 1392:
! 1393: /**
! 1394: * @internal Entity resolver used by __axl_dtd_parse_attlist.
! 1395: */
! 1396: const char * __axl_dtd_entity_resolver (const char * entityName, axlPointer data)
! 1397: {
! 1398: /* return the entity resolution */
! 1399: return axl_dtd_entity_value ((axlDtd *) data, entityName, PARAMETER_ENTITY);
! 1400: } /* end if */
! 1401:
! 1402: axlList * __axl_dtd_parse_enumvalues (const char * _enum_values)
! 1403: {
! 1404: char ** result;
! 1405: int iterator;
! 1406: axlList * list;
! 1407:
! 1408: result = axl_stream_split (_enum_values, 1, "|");
! 1409: iterator = 0;
! 1410: list = axl_list_new (axl_list_always_return_1, axl_free);
! 1411:
! 1412:
! 1413: while (result[iterator]) {
! 1414: /* clean the enum value */
! 1415: axl_stream_trim (result[iterator]);
! 1416:
! 1417: /* add to the list */
! 1418: axl_list_add (list, axl_strdup (result[iterator]));
! 1419:
! 1420: /* update the iterator value */
! 1421: iterator++;
! 1422:
! 1423: } /* end while */
! 1424:
! 1425: /* free tokens */
! 1426: axl_stream_freev (result);
! 1427:
! 1428: /* return the list */
! 1429: return list;
! 1430: }
! 1431:
! 1432: /**
! 1433: * @internal function used by \ref axl_dtd_attr_validation function to
! 1434: * lookup ATTLIST contraints flaged as unique ID.
! 1435: */
! 1436: axl_bool __find_id_decl (axlPointer _element, axlPointer data)
! 1437: {
! 1438: /* return the comparision */
! 1439: return (((axlDtdAttributeDecl *) _element)->type == TOKENIZED_TYPE_ID);
! 1440:
! 1441: } /* end __find_id_decl */
! 1442:
! 1443:
! 1444: /**
! 1445: * @internal
! 1446: *
! 1447: * Parse the <!ATTLIST decleration, registering it into the provided
! 1448: * dtd element.
! 1449: */
! 1450: axl_bool __axl_dtd_parse_attlist (axlDtd * dtd, axlStream * stream, axlError ** error)
! 1451: {
! 1452: char * string_aux = NULL;
! 1453: int matched_chunk = -1;
! 1454: axlDtdAttribute * attribute = NULL;
! 1455: axlDtdAttributeDecl * decl = NULL;
! 1456: axlDtdAttributeDecl * declAux = NULL;
! 1457: char * err_msg;
! 1458:
! 1459: /* init the dtd attr list */
! 1460: if (dtd->attributes == NULL)
! 1461: dtd->attributes = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_dtd_attribute_free);
! 1462:
! 1463: /* consume previous white spaces */
! 1464: AXL_CONSUME_SPACES (stream);
! 1465:
! 1466: /* get the element name */
! 1467: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_false, 1, " ");
! 1468: if (string_aux == NULL) {
! 1469: axl_error_new (-1, "Expected to receive a DTD attribute name for <!ATTLIST declaration, but not found", stream, error);
! 1470: axl_stream_free (stream);
! 1471: return axl_false;
! 1472: }
! 1473:
! 1474: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found dtd attr declaration for node: <%s>", string_aux);
! 1475:
! 1476: /* find the node that holds all attr declarations for the node found */
! 1477: attribute = axl_dtd_get_attr (dtd, string_aux);
! 1478:
! 1479: /* check if found */
! 1480: if (attribute == NULL) {
! 1481: /* create the axlDtdAttribute holder */
! 1482: attribute = axl_new (axlDtdAttribute, 1);
! 1483:
! 1484: /* record the node to which the list of rules applies */
! 1485: axl_stream_nullify (stream, LAST_CHUNK);
! 1486: attribute->name = string_aux;
! 1487:
! 1488: /* init the attribute rule list */
! 1489: attribute->list = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_dtd_attribute_decl_free);
! 1490:
! 1491: /* now configure this new attribute inside the dtd */
! 1492: axl_list_add (dtd->attributes, attribute);
! 1493: } /* end if */
! 1494:
! 1495: /* now get the list of attributes */
! 1496: while (1) {
! 1497: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "finding next att declaration");
! 1498:
! 1499: /* consume previous white spaces */
! 1500: AXL_CONSUME_SPACES (stream);
! 1501:
! 1502: /* check if we have finished */
! 1503: if (axl_stream_inspect (stream, ">", 1) > 0)
! 1504: break;
! 1505:
! 1506: /* get the attribute name the rules applies */
! 1507: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_false, 1, " ");
! 1508: if (string_aux == NULL) {
! 1509: axl_error_new (-1, "Expected to receive an attribute name for <!ATTLIST declaration, but not found", stream, error);
! 1510: axl_stream_free (stream);
! 1511: return axl_false;
! 1512: }
! 1513:
! 1514: /* nully the string and store it new rule created */
! 1515: axl_stream_nullify (stream, LAST_CHUNK);
! 1516:
! 1517: /* create a new attribute single constraint */
! 1518: decl = axl_new (axlDtdAttributeDecl, 1);
! 1519: decl->name = string_aux;
! 1520:
! 1521: /* add the attribute constraint to the list */
! 1522: axl_list_add (attribute->list, decl);
! 1523:
! 1524: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "find constraint for attribute name=%s", decl->name);
! 1525:
! 1526: /* consume previous white spaces */
! 1527: AXL_CONSUME_SPACES (stream);
! 1528:
! 1529: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking constraint type..");
! 1530:
! 1531: /* check for an entity reference and expand the stream
! 1532: * content with its resolution */
! 1533: if (! axl_dtd_check_entity_ref_and_expand (__axl_dtd_entity_resolver, dtd, stream, "%", error))
! 1534: return axl_false;
! 1535:
! 1536: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "about to check attr constraint type, stream status: '%s'",
! 1537: axl_stream_get_following (stream, 30));
! 1538:
! 1539: /* now check the contraint type */
! 1540: if (axl_stream_inspect (stream, "NOTATION", 8) > 0) {
! 1541: /* parse notation declaration */
! 1542: }else if (axl_stream_inspect (stream, "(", 1) > 0) {
! 1543: /* parse enum declaration */
! 1544: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_true, 1, ")");
! 1545: if (string_aux == NULL) {
! 1546: axl_error_new (-1, "expected to find enum declaration but termination caracter ')' was not found", stream, error);
! 1547: axl_stream_free (stream);
! 1548: return axl_false;
! 1549: } /* end if */
! 1550: decl->type = ENUMERATION_TYPE;
! 1551: decl->enumvalues = __axl_dtd_parse_enumvalues (string_aux);
! 1552: }else {
! 1553: /* set the attribute type */
! 1554: if (axl_stream_inspect (stream, "CDATA", 5) > 0) {
! 1555: decl->type = CDATA_ATTRIBUTE;
! 1556: } else if (axl_stream_inspect (stream, "IDREFS", 6) > 0) {
! 1557:
! 1558: /* flag the type */
! 1559: decl->type = TOKENIZED_TYPE_IDREFS;
! 1560:
! 1561: /* flag the dtd to have a IDREF declaration */
! 1562: dtd->haveIdRefDecl = axl_true;
! 1563: } else if (axl_stream_inspect (stream, "IDREF", 5) > 0) {
! 1564: /* notify type found */
! 1565: decl->type = TOKENIZED_TYPE_IDREF;
! 1566:
! 1567: /* flag the dtd to have a IDREF declaration */
! 1568: dtd->haveIdRefDecl = axl_true;
! 1569:
! 1570: } else if (axl_stream_inspect (stream, "ID", 2) > 0) {
! 1571:
! 1572: /* notify the type found */
! 1573: decl->type = TOKENIZED_TYPE_ID;
! 1574:
! 1575: /* flag the dtd to have a ID declaration */
! 1576: dtd->haveIdDecl = axl_true;
! 1577:
! 1578: } else if (axl_stream_inspect (stream, "ENTITY", 6) > 0)
! 1579: decl->type = TOKENIZED_TYPE_ENTITY;
! 1580: else if (axl_stream_inspect (stream, "ENTITIES", 8) > 0)
! 1581: decl->type = TOKENIZED_TYPE_ENTITIES;
! 1582: else if (axl_stream_inspect (stream, "NMTOKENS", 8) > 0)
! 1583: decl->type = TOKENIZED_TYPE_NMTOKENS;
! 1584: else if (axl_stream_inspect (stream, "NMTOKEN", 7) > 0)
! 1585: decl->type = TOKENIZED_TYPE_NMTOKEN;
! 1586: else {
! 1587: axl_error_new (-1, "Unrecognied attr type declaration found, check your <!ATTLIST declaration", stream, error);
! 1588: axl_stream_free (stream);
! 1589: return axl_false;
! 1590: } /* end if */
! 1591: } /* end if */
! 1592:
! 1593: /* consume previous white spaces */
! 1594: AXL_CONSUME_SPACES (stream);
! 1595:
! 1596: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking default value declaration, stream status: '%s'",
! 1597: axl_stream_get_following (stream, 30));
! 1598:
! 1599: /* get default declaration value */
! 1600: if (axl_stream_inspect (stream, "#REQUIRED", 9) > 0) {
! 1601: decl->defaults = ATT_REQUIRED;
! 1602: } else if (axl_stream_inspect (stream, "#IMPLIED", 8) > 0) {
! 1603: decl->defaults = ATT_IMPLIED;
! 1604: } else {
! 1605: decl->defaults = ATT_IMPLIED;
! 1606: if (axl_stream_inspect (stream, "#FIXED", 6) > 0) {
! 1607: decl->defaults = ATT_FIXED;
! 1608:
! 1609: /* consume previous white spaces */
! 1610: AXL_CONSUME_SPACES (stream);
! 1611: }
! 1612:
! 1613: /* check default value for this case */
! 1614: if (! (axl_stream_peek (stream, "\"", 1) > 0 ||
! 1615: axl_stream_peek (stream, "'", 1) > 0)) {
! 1616: err_msg = axl_strdup_printf ("Unable to find default attribute declaration (#REQUIRED, #IMPLIED, #FIXED) for attribute %s, node <%s>",
! 1617: decl->name, attribute->name);
! 1618: axl_error_new (-1, err_msg, stream, error);
! 1619: axl_stream_free (stream);
! 1620: axl_free (err_msg);
! 1621: return axl_false;
! 1622: } /* end if */
! 1623: } /* end if */
! 1624:
! 1625: /* check constraint for ID types */
! 1626: if (decl->type == TOKENIZED_TYPE_ID) {
! 1627: /* check that the node doesn't have any unique
! 1628: * id declared */
! 1629:
! 1630: /* check if the node have TOKENIZED_TYPE_ID */
! 1631: declAux = axl_list_lookup (attribute->list, __find_id_decl, NULL);
! 1632: if (declAux != NULL && !axl_cmp (declAux->name, decl->name)) {
! 1633: err_msg = axl_strdup_printf ("Found ATTLIST declaration, with several ID declarations <ATTLIST %s %s..",
! 1634: attribute->name, decl->name);
! 1635: axl_error_new (-1, err_msg, stream, error);
! 1636: axl_stream_free (stream);
! 1637: axl_free (err_msg);
! 1638: return axl_false;
! 1639: } /* end if */
! 1640:
! 1641: /* check required and implied */
! 1642: if (decl->defaults != ATT_REQUIRED && decl->defaults != ATT_IMPLIED) {
! 1643: err_msg = axl_strdup_printf ("Found ATTLIST declaration, with ID, that don't have configured either #IMPLICIT or #REQUIRED for attribute %s, node <%s>",
! 1644: decl->name, attribute->name);
! 1645: axl_error_new (-1, err_msg, stream, error);
! 1646: axl_stream_free (stream);
! 1647: axl_free (err_msg);
! 1648: return axl_false;
! 1649: } /* end if */
! 1650: } /* end if */
! 1651:
! 1652: /* consume previous white spaces */
! 1653: AXL_CONSUME_SPACES (stream);
! 1654:
! 1655: /* nullify to check this value later */
! 1656: string_aux = NULL;
! 1657: if (axl_stream_inspect (stream, "\"", 1) > 0) {
! 1658: /* get until */
! 1659: string_aux = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, "\"");
! 1660: } else if (axl_stream_inspect (stream, "'", 1) > 0) {
! 1661: /* get until */
! 1662: string_aux = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, "\'");
! 1663: } /* end if */
! 1664:
! 1665: /* check if default value was found */
! 1666: if (string_aux != NULL) {
! 1667:
! 1668: /* found default value, check if we have an
! 1669: * enumeration type, enforcing that the value
! 1670: * defined to be inside the enumeration */
! 1671: if (decl->type == ENUMERATION_TYPE) {
! 1672: if (axl_list_lookup (decl->enumvalues, axl_list_find_string, string_aux) == NULL) {
! 1673: axl_error_new (-1,
! 1674: "Configured a default value for an attribute list which only accepts a set of enum values that do not containt it.",
! 1675: stream, error);
! 1676: axl_stream_free (stream);
! 1677: return axl_false;
! 1678: } /* end if */
! 1679: } /* end if */
! 1680:
! 1681: /* nullify value and make string_aux to be
! 1682: * owned by the axlDtdAttributeDecl
! 1683: * reference */
! 1684: axl_stream_nullify (stream, LAST_CHUNK);
! 1685: decl->default_value = string_aux;
! 1686: } /* end if */
! 1687:
! 1688: } /* end while */
! 1689:
! 1690: /* properly parsed */
! 1691: return axl_true;
! 1692: }
! 1693:
! 1694: /**
! 1695: * @internal
! 1696: *
! 1697: * Destroy the provided entity reference and all allocated memory.
! 1698: *
! 1699: * @param entity The entity the deallocate.
! 1700: */
! 1701: void axl_dtd_entity_free (axlDtdEntity * entity)
! 1702: {
! 1703: /* the entity reference */
! 1704: axl_return_if_fail (entity);
! 1705:
! 1706: /* free the entity name */
! 1707: if (entity->name)
! 1708: axl_free (entity->name);
! 1709:
! 1710: /* free the content */
! 1711: if (entity->content)
! 1712: axl_free (entity->content);
! 1713:
! 1714: /* free external data if defined */
! 1715: if (entity->data) {
! 1716: /* free system literal */
! 1717: if (entity->data->system_literal)
! 1718: axl_free (entity->data->system_literal);
! 1719:
! 1720: /* free public literal */
! 1721: if (entity->data->public_literal)
! 1722: axl_free (entity->data->public_literal);
! 1723:
! 1724: /* free ndata literal */
! 1725: if (entity->data->ndata)
! 1726: axl_free (entity->data->ndata);
! 1727:
! 1728: /* free the node itself */
! 1729: axl_free (entity->data);
! 1730: }
! 1731:
! 1732: /* free the node */
! 1733: axl_free (entity);
! 1734:
! 1735: return;
! 1736: }
! 1737:
! 1738: /**
! 1739: * @internal
! 1740: *
! 1741: * Parses an entity definition from the current status of the stream
! 1742: * provided.
! 1743: */
! 1744: axl_bool __axl_dtd_parse_entity (axlDtd * dtd, axlStream * stream, axlError ** error)
! 1745: {
! 1746: char * string_aux;
! 1747: int matched_chunk;
! 1748: axlDtdEntity * entity;
! 1749:
! 1750: /* init the dtd element list */
! 1751: if (dtd->entities == NULL)
! 1752: dtd->entities = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_dtd_entity_free);
! 1753:
! 1754: /* consume previous white spaces */
! 1755: AXL_CONSUME_SPACES (stream);
! 1756:
! 1757: /* get for the first element declaration */
! 1758: if (! (axl_stream_inspect (stream, "<!ENTITY", 8) > 0)) {
! 1759: axl_error_new (-1, "Expected to receive a <!ENTITY, but it wasn't found", stream, error);
! 1760: axl_stream_free (stream);
! 1761: return axl_false;
! 1762: }
! 1763:
! 1764: /* consume previous white spaces */
! 1765: AXL_CONSUME_SPACES (stream);
! 1766:
! 1767: /* create a new entity */
! 1768: entity = axl_new (axlDtdEntity, 1);
! 1769:
! 1770: /* set the entity and return axl_true */
! 1771: axl_list_add (dtd->entities, entity);
! 1772:
! 1773: /* check for parameter entity definition */
! 1774: if (axl_stream_inspect (stream, "%", 1) > 0) {
! 1775: /* set the entity type */
! 1776: entity->type = PARAMETER_ENTITY;
! 1777:
! 1778: /* consume previous white spaces */
! 1779: AXL_CONSUME_SPACES (stream);
! 1780:
! 1781: } else
! 1782: entity->type = GENERAL_ENTITY;
! 1783:
! 1784: /* get the element name */
! 1785: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_false, 1, " ");
! 1786: if (string_aux == NULL) {
! 1787: axl_error_new (-1, "Expected to receive a DTD entity name for <!ENTITY declaration, but not found", stream, error);
! 1788: axl_stream_free (stream);
! 1789: return axl_false;
! 1790: }
! 1791:
! 1792: /* set the name */
! 1793: axl_stream_nullify (stream, LAST_CHUNK);
! 1794: entity->name = string_aux;
! 1795:
! 1796: /* consume previous white spaces */
! 1797: AXL_CONSUME_SPACES (stream);
! 1798:
! 1799: /* now check if we have an external reference */
! 1800: if (axl_stream_inspect (stream, "PUBLIC", 6) > 0) {
! 1801: /* we have a public external resource definition */
! 1802:
! 1803: }else if (axl_stream_inspect (stream, "SYSTEM", 6) > 0) {
! 1804: /* we have a system definition */
! 1805:
! 1806: }else {
! 1807: /* we have a plain value get the content remove next "
! 1808: and ' if defined */
! 1809: if (! ((axl_stream_inspect (stream, "\"", 1) > 0))) {
! 1810: if (! (axl_stream_inspect (stream, "\'", 1) > 0)) {
! 1811: axl_error_new (-2, "Expected to find entity value initiator (\") or ('), every entity value must start with them",
! 1812: stream, error);
! 1813: axl_stream_free (stream);
! 1814: return axl_false;
! 1815: }
! 1816: /* knowing that ' was matched, now get the attribute value */
! 1817: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_true, 1, "'");
! 1818: }else {
! 1819: /* knowhing that " was matched, now get the attribute value */
! 1820: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_true, 1, "\"");
! 1821: }
! 1822:
! 1823: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "entity value found: [%s]", string_aux);
! 1824:
! 1825: /* nullify internal reference so we have the
! 1826: * only one reference to entity content value
! 1827: * inside string_aux */
! 1828: axl_stream_nullify (stream, LAST_CHUNK);
! 1829:
! 1830: /* set the value */
! 1831: entity->content = string_aux;
! 1832: }
! 1833:
! 1834: /* consume previous white spaces */
! 1835: AXL_CONSUME_SPACES (stream);
! 1836:
! 1837: /* check last item to parse */
! 1838: if (! (axl_stream_inspect (stream, ">", 1) > 0)) {
! 1839: axl_error_new (-2, "Expected to find entity definition terminator (>), but it wasn't found",
! 1840: stream, error);
! 1841: axl_stream_free (stream);
! 1842: return axl_false;
! 1843: }
! 1844:
! 1845: return axl_true;
! 1846: }
! 1847:
! 1848:
! 1849: /**
! 1850: * @internal
! 1851: *
! 1852: * Implements DTD parsing, reading it from a direct buffer, or a file
! 1853: * path or a file handle.
! 1854: */
! 1855: axlDtd * __axl_dtd_parse_common (const char * entity, int entity_size,
! 1856: const char * file_path, int fd_handle,
! 1857: axlError ** error)
! 1858: {
! 1859: axlStream * stream;
! 1860: axlDtd * dtd;
! 1861: int iterator;
! 1862:
! 1863: /* create the stream associated */
! 1864: stream = axl_stream_new (entity, entity_size, file_path, fd_handle, error);
! 1865: axl_return_val_if_fail (stream, NULL);
! 1866:
! 1867: dtd = __axl_dtd_new ();
! 1868: axl_stream_link (stream, dtd, (axlDestroyFunc) axl_dtd_free);
! 1869:
! 1870: iterator = 0;
! 1871: while (axl_stream_remains (stream)) {
! 1872: /* get rid from comments found */
! 1873: if (! axl_doc_consume_comments (NULL, stream, error))
! 1874: return NULL;
! 1875:
! 1876: /* check for element declaration */
! 1877: if (axl_stream_peek (stream, "<!ELEMENT", 9) > 0) {
! 1878: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD element declaration");
! 1879: /* found element declaration */
! 1880: if (! __axl_dtd_parse_element (dtd, stream, error))
! 1881: return NULL;
! 1882:
! 1883: continue;
! 1884:
! 1885: }
! 1886:
! 1887: /* check for attribute list declarations */
! 1888: if (axl_stream_inspect (stream, "<!ATTLIST", 9) > 0) {
! 1889: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD attribute list declaration");
! 1890:
! 1891: /* parse it */
! 1892: if (! __axl_dtd_parse_attlist (dtd, stream, error))
! 1893: return NULL;
! 1894:
! 1895: continue;
! 1896: }
! 1897:
! 1898: /* check for the entity declaration */
! 1899: if (axl_stream_peek (stream, "<!ENTITY", 8) > 0) {
! 1900: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD entity declaration");
! 1901:
! 1902: /* parse the entity definition */
! 1903: if (! __axl_dtd_parse_entity (dtd, stream, error))
! 1904: return NULL;
! 1905:
! 1906: continue;
! 1907: }
! 1908:
! 1909: /* stop the loop */
! 1910: if (iterator == 3) {
! 1911: axl_error_new (-1, "unable to process DTD content, unable to find expected information (no <!ELEMENT, <!ATTLIST or <!ENTITY declaration)", stream, error);
! 1912: axl_stream_free (stream);
! 1913: return NULL;
! 1914: }
! 1915: iterator++;
! 1916: }
! 1917:
! 1918:
! 1919: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD elements totally loaded, building references..");
! 1920:
! 1921: /* update current root reference, the DTD root for the DTD
! 1922: * document already parsed */
! 1923: if (dtd->elements != NULL)
! 1924: dtd->root = __axl_dtd_get_new_root (dtd);
! 1925:
! 1926: /* check if the DTD has ID declarations if found IDREF
! 1927: * declarations */
! 1928: if (! dtd->haveIdDecl && dtd->haveIdRefDecl) {
! 1929: axl_error_new (-1, "DTD semantic error, found IDREF attribute declaration but no attribute ID declaration was found.", stream, error);
! 1930: axl_stream_free (stream);
! 1931: return NULL;
! 1932: }
! 1933:
! 1934: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD load COMPLETE");
! 1935:
! 1936: axl_stream_unlink (stream);
! 1937: axl_stream_free (stream);
! 1938: return dtd;
! 1939: }
! 1940:
! 1941: /**
! 1942: * @brief Allows to parse the provided entity, which is expected to
! 1943: * contain a DTD (Document Type Definition).
! 1944: *
! 1945: * @param entity The document type definition to parse.
! 1946: *
! 1947: * @param entity_size The document size, or -1 to make the function to
! 1948: * figure out current size.
! 1949: *
! 1950: * @param error An optional \ref axlError where errors will be reported.
! 1951: *
! 1952: * @return A newly allocated \ref axlDtd that must be deallocated when
! 1953: * no longer need with \ref axl_dtd_free. The function could return
! 1954: * NULL on failure detected. On that case, it is requred to check \ref
! 1955: * axlError variable, if defined.
! 1956: */
! 1957: axlDtd * axl_dtd_parse (const char * entity,
! 1958: int entity_size,
! 1959: axlError ** error)
! 1960: {
! 1961:
! 1962: return __axl_dtd_parse_common (entity, entity_size, NULL, -1, error);
! 1963: }
! 1964:
! 1965: /**
! 1966: * @brief Allows to parse the provided DTD definition, which is found
! 1967: * on the provided file path.
! 1968: *
! 1969: * @param file_path The file path where it is expected to receive a
! 1970: * DTD file.
! 1971: *
! 1972: * @param error An optional \ref axlError reference where all errors found will be reported.
! 1973: *
! 1974: * @return A newly allocated \ref axlDtd instance or NULL if it fails.
! 1975: *
! 1976: * <b>Making a DTD to be inline loaded: </b><br>
! 1977: *
! 1978: * It may be helpful to make the DTD definition available at your
! 1979: * binary, inline compiled, to avoid distributing DTD files along with
! 1980: * libraries, etc. This also solves installation problems like
! 1981: * provisioning a default location to make your application to find
! 1982: * such files.
! 1983: *
! 1984: * With the following command you can create an inline representation
! 1985: * from your DTD file:
! 1986: * \code
! 1987: * >> axl-knife --input your-file.dtd --dtd-to-c --output your-file.dtd.h --ifnewer
! 1988: * \endcode
! 1989: *
! 1990: * This will create a header with an C-macro style definition of your
! 1991: * DTD. Now, you can include it using:
! 1992: *
! 1993: * \code
! 1994: * #include <your-file.dtd.h>
! 1995: * \endcode
! 1996: *
! 1997: * In the case you are developing a library, it is recommended to do
! 1998: * such include at the body implementation (usually .c or .cpp files,
! 1999: * to avoid requiring your API consumers to also include your DTD
! 2000: * inline definition).
! 2001: *
! 2002: * Now, to load your DTD file, use the following:
! 2003: *
! 2004: * \code
! 2005: * axlError * err = NULL;
! 2006: * axlDtd * dtd = axl_dtd_parse (YOUR_FILE_DTD, -1, &err);
! 2007: * if (dtd == NULL) {
! 2008: * // This won't happen unless axl-runtime error found, since axl-knife
! 2009: * // checks your dtd file before producing the in-line definition.
! 2010: * // However, bug happens! check this.
! 2011: * }
! 2012: * \endcode
! 2013: */
! 2014: axlDtd * axl_dtd_parse_from_file (const char * file_path,
! 2015: axlError ** error)
! 2016: {
! 2017: return __axl_dtd_parse_common (NULL, -1, file_path, -1, error);
! 2018: }
! 2019:
! 2020:
! 2021: /**
! 2022: * @internal
! 2023: *
! 2024: * Support function for axl_dtd_validate which checks if the provided
! 2025: * parent have its childs configuration according to the values
! 2026: * expresed on the sequenced represented by the itemList.
! 2027: *
! 2028: * The function return axl_true if the validation was ok, or axl_false
! 2029: * if something have failed. It also creates an error, using the
! 2030: * optional axlError reference received.
! 2031: */
! 2032: axl_bool __axl_dtd_validate_sequence (axlNode * parent,
! 2033: int * child_position,
! 2034: axlDtdElementList * itemList,
! 2035: axlError ** error,
! 2036: axl_bool try_match,
! 2037: axl_bool top_level)
! 2038: {
! 2039: int iterator = 0;
! 2040: int child_pos = *child_position;
! 2041: axlNode * node;
! 2042: axlDtdElementListNode * itemNode;
! 2043: axl_bool status = axl_false;
! 2044: axl_bool one_matched;
! 2045: AxlDtdTimes times;
! 2046:
! 2047:
! 2048: axl_return_val_if_fail (parent, axl_false);
! 2049: axl_return_val_if_fail (itemList, axl_false);
! 2050:
! 2051:
! 2052: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validating a sequence list: iterator=%d, item list count=%d, at child position=%d",
! 2053: iterator, axl_dtd_item_list_count (itemList), child_pos);
! 2054:
! 2055: /* iterate over the sequence, checking its order */
! 2056: while (iterator < axl_dtd_item_list_count (itemList)) {
! 2057:
! 2058: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting next item node from the DTD item list at: %d",
! 2059: iterator);
! 2060:
! 2061: /* get the item node specification */
! 2062: itemNode = axl_dtd_item_list_get_node (itemList, iterator);
! 2063: one_matched = axl_false;
! 2064: times = axl_dtd_item_node_get_repeat (itemNode);
! 2065:
! 2066: do {
! 2067:
! 2068: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting node at child position: %d",
! 2069: child_pos);
! 2070:
! 2071: /* get the node that is located at the same position
! 2072: * than the sequence */
! 2073: if (child_pos < axl_node_get_child_num (parent)) {
! 2074: node = axl_node_get_child_nth (parent, child_pos);
! 2075: } else
! 2076: node = NULL;
! 2077:
! 2078: /* the node child list have ended, check if
! 2079: * this situation was expected */
! 2080: if (node == NULL) {
! 2081: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "no more child nodes to validate at %d, for parent: %s, times: %d, iterator: %d, item count: %d",
! 2082: child_pos, axl_node_get_name (parent), times,
! 2083: iterator, axl_dtd_item_list_count (itemList));
! 2084: /* check if we were working with a
! 2085: * list, which have matched at least
! 2086: * one item */
! 2087: if (times == ONE_OR_MANY && one_matched && status) {
! 2088: if ((iterator + 1) == axl_dtd_item_list_count (itemList)) {
! 2089: *child_position = child_pos;
! 2090: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence validated with child position (III): %d", child_pos);
! 2091: return axl_true;
! 2092: }
! 2093:
! 2094: /* reached this point we have
! 2095: matched a one to many with
! 2096: at least one match */
! 2097: break;
! 2098: }
! 2099:
! 2100: /* check that the rest of the
! 2101: * specification item is optional,
! 2102: * including the one used */
! 2103: status = axl_true;
! 2104: do {
! 2105: if (times != ZERO_OR_MANY &&
! 2106: times != ZERO_OR_ONE) {
! 2107:
! 2108: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found item, inside the DTD item list, that is not optional: %d (repeat value: %d)",
! 2109: iterator, times);
! 2110: status = axl_false;
! 2111: break;
! 2112: }
! 2113:
! 2114: /* update index and get the next item */
! 2115: iterator++;
! 2116: if (iterator < axl_dtd_item_list_count (itemList))
! 2117: itemNode = axl_dtd_item_list_get_node (itemList, iterator);
! 2118: }while (status && (iterator < axl_dtd_item_list_count (itemList)));
! 2119:
! 2120: /* check status before checking the rest of the item spec */
! 2121: if (status) {
! 2122: *child_position = child_pos;
! 2123:
! 2124: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence validated with child position (II): %d", child_pos);
! 2125:
! 2126: return axl_true;
! 2127: }
! 2128:
! 2129: /* check if a try match is being runned */
! 2130: if (! try_match) {
! 2131: axl_error_report (error, -1 ,
! 2132: "Found that DTD specifies more nodes to be hold by the parent (<%s>), but no more childs were found",
! 2133: axl_node_get_name (parent));
! 2134: }
! 2135:
! 2136: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found that no nodes left to satisfy DTD validation operation");
! 2137: *child_position = child_pos;
! 2138: return axl_false;
! 2139: }
! 2140:
! 2141: /* check node type */
! 2142: if (axl_dtd_item_node_get_type (itemNode) == AXL_ELEMENT_LIST) {
! 2143:
! 2144: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "the item node is an item list, dtd item list position: %d, child position: %d=<%s>",
! 2145: iterator, child_pos, axl_node_get_name (node));
! 2146:
! 2147: /* element list found, validate its content */
! 2148: if (! __axl_dtd_validate_item_list (axl_dtd_item_node_get_list (itemNode),
! 2149: parent, &child_pos, error, axl_false)) {
! 2150: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sub item list validation have failed (not critical)");
! 2151: /* check if we are the top
! 2152: * level list and the itemNode
! 2153: * checked is the last one
! 2154: * item on the item list */
! 2155: if (top_level && ((iterator + 1) == axl_node_get_child_num (parent))) {
! 2156: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "found that the last item list wasn't matched");
! 2157:
! 2158: }
! 2159:
! 2160: *child_position = child_pos;
! 2161: return axl_false;
! 2162: }
! 2163:
! 2164: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validated item list, child position after: %d",
! 2165: child_pos);
! 2166: /* because child position updating and
! 2167: * repeat matching is already handled
! 2168: * by dtd_validate_item_list function
! 2169: * we just continue with the next
! 2170: * iteration */
! 2171: break;
! 2172:
! 2173: } else if (axl_dtd_item_node_get_type (itemNode) == AXL_ELEMENT_NODE) {
! 2174: /* check the name against the spec */
! 2175:
! 2176: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
! 2177: "the item node is a final content particule definition: %s",
! 2178: axl_dtd_item_node_get_value (itemNode));
! 2179:
! 2180: status = NODE_CMP_NAME (node, axl_dtd_item_node_get_value (itemNode));
! 2181: }
! 2182:
! 2183: /* check previous status */
! 2184: if ((times == ONE_AND_ONLY_ONE) ||
! 2185: (times == ONE_OR_MANY && one_matched == axl_false)) {
! 2186: if (! status) {
! 2187: /* only report an upper level
! 2188: * error if we are not running
! 2189: * a try match */
! 2190: if (! try_match) {
! 2191:
! 2192: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL,
! 2193: "Found different node (<%s>) for a sequence expected (<%s>), at child position: %d, item list pos: %d",
! 2194: axl_node_get_name (node),
! 2195: axl_dtd_item_node_get_value (itemNode),
! 2196: child_pos, iterator);
! 2197: axl_error_report (error, -1,
! 2198: "Found different node (<%s>) for a sequence expected (<%s>), at child position: %d, item list pos: %d",
! 2199: axl_node_get_name (node),
! 2200: axl_dtd_item_node_get_value (itemNode),
! 2201: child_pos, iterator);
! 2202: }
! 2203: /* return that a match wasn't possible */
! 2204: *child_position = child_pos;
! 2205: return axl_false;
! 2206: }
! 2207: }
! 2208:
! 2209: /* according to the repetition pattern, update loop indexes */
! 2210: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "updating child nodes references: %d, repeat type: %d, status=%d",
! 2211: child_pos, times, status);
! 2212:
! 2213:
! 2214: /* one only item to match and exactly one */
! 2215: if (times == ONE_AND_ONLY_ONE) {
! 2216: child_pos++;
! 2217: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "updated child position to: %d, repeat type: %d, status=%d",
! 2218: child_pos, times, status);
! 2219: break;
! 2220: }
! 2221:
! 2222: /* one or many items to match */
! 2223: if (times == ONE_OR_MANY) {
! 2224:
! 2225: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "matched one to many item node: status=%d one_matched=%d",
! 2226: status, one_matched);
! 2227:
! 2228: /* if the match have failed and
! 2229: * previous matches was ok, it seems
! 2230: * we have reached the next
! 2231: * items. Just break the loop */
! 2232: if (status == axl_false && one_matched == axl_true)
! 2233: break;
! 2234:
! 2235: child_pos++;
! 2236: one_matched = axl_true;
! 2237: continue; /* don't break the loop */
! 2238: }
! 2239:
! 2240: /* zero or optionally one item to match */
! 2241: if (times == ZERO_OR_ONE) {
! 2242: /* if previous status was ok, it seems
! 2243: * that we have matched the optional
! 2244: * character. In that case, move the
! 2245: * index to the following value. If
! 2246: * not, just break the loop. */
! 2247: if (status == axl_true)
! 2248: child_pos++;
! 2249: break;
! 2250: }
! 2251:
! 2252: /* zero or many items to match */
! 2253: if (times == ZERO_OR_MANY) {
! 2254: if (status == axl_true) {
! 2255: one_matched = axl_true;
! 2256: child_pos++;
! 2257: continue;
! 2258: }
! 2259: break;
! 2260: }
! 2261:
! 2262:
! 2263: /* until break the loop */
! 2264: }while (axl_true);
! 2265:
! 2266: /* update iterator index */
! 2267: iterator++;
! 2268: }
! 2269:
! 2270: /* check if more nodes where specified than the DTD spec */
! 2271: times = axl_dtd_item_list_repeat (itemList);
! 2272: if ((times == ONE_OR_MANY || times == ONE_AND_ONLY_ONE) &&
! 2273: top_level && (child_pos < axl_node_get_child_num (parent))) {
! 2274:
! 2275: /* drop a log */
! 2276: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "parent node <%s> have more childs=%d than the childs iterated=%d, top_level=%d",
! 2277: axl_node_get_name (parent),
! 2278: axl_node_get_child_num (parent),
! 2279: child_pos, top_level);
! 2280:
! 2281: /* do not report an error found if a try match is
! 2282: * being run */
! 2283: if (! try_match) {
! 2284: axl_error_new (-1, "More childs, than the ones especified in the DTD, were found",
! 2285: NULL, error);
! 2286: }
! 2287: /* return that the match wasn't possible */
! 2288: *child_position = child_pos;
! 2289: return axl_false;
! 2290: }
! 2291:
! 2292: /* return that the sequence has been validated */
! 2293: *child_position = child_pos;
! 2294:
! 2295: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence validated with child position (I): %d", child_pos);
! 2296:
! 2297: return axl_true;
! 2298: }
! 2299:
! 2300: /**
! 2301: * @internal
! 2302: *
! 2303: * Internal support function to validate the choice list.
! 2304: */
! 2305: axl_bool __axl_dtd_validate_choice (axlNode * parent,
! 2306: int * child_position,
! 2307: axlDtdElementList * itemList,
! 2308: axlError ** error,
! 2309: axl_bool try_match,
! 2310: axl_bool top_level)
! 2311: {
! 2312: axlNode * node;
! 2313: axlDtdElementListNode * itemNode;
! 2314: int iterator;
! 2315: axl_bool status;
! 2316: AxlDtdTimes times;
! 2317: axl_bool one_match;
! 2318:
! 2319:
! 2320: if (*child_position < axl_node_get_child_num (parent)) {
! 2321: /* get a reference to be matched by the choice list */
! 2322: node = axl_node_get_child_nth (parent, *child_position);
! 2323: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validated choice list at position: %d=<%s>", *child_position, axl_node_get_name (node));
! 2324: } else {
! 2325: /* tried to match a choice list with a child index
! 2326: * outside the maximum number of childs */
! 2327: if (! try_match) {
! 2328: axl_error_new (-1, "Unable to match choice list, it seems that the are not enough childs to validate the choice list",
! 2329: NULL, error);
! 2330: }
! 2331: return axl_false;
! 2332: }
! 2333:
! 2334: iterator = 0;
! 2335: while (iterator < axl_dtd_item_list_count (itemList)) {
! 2336: /* get the DTD item list to match */
! 2337: itemNode = axl_dtd_item_list_get_node (itemList, iterator);
! 2338: times = axl_dtd_item_node_get_repeat (itemNode);
! 2339:
! 2340: if (axl_dtd_item_node_get_type (itemNode) == AXL_ELEMENT_NODE) {
! 2341: /* reset match configuration */
! 2342: one_match = axl_false;
! 2343: repeat_for_node:
! 2344: /* a node was found */
! 2345: status = NODE_CMP_NAME (node, axl_dtd_item_node_get_value (itemNode));
! 2346:
! 2347: /* know, if the node was matched check it
! 2348: * repetition configuration */
! 2349: if (status) {
! 2350: /* update child position */
! 2351: (*child_position)++;
! 2352:
! 2353: if (times == ONE_AND_ONLY_ONE || times == ZERO_OR_ONE) {
! 2354: /* the node was matched and
! 2355: * the itemNode has a one and
! 2356: * only one configuration,
! 2357: * just return that the choice
! 2358: * list was matched */
! 2359: return axl_true;
! 2360: }
! 2361: if (times == ONE_OR_MANY || times == ZERO_OR_MANY) {
! 2362: /* because the node was matched, but the repetition
! 2363: * pattern allows to match more nodes we have to
! 2364: * iterate a bit more */
! 2365: node = axl_node_get_child_nth (parent, *child_position);
! 2366: if (node == NULL) {
! 2367: /* because we already matched at least one item,
! 2368: * we can assume that the itemNode was successfully
! 2369: * matched for both cases (*) and (+). */
! 2370: return axl_true;
! 2371: }
! 2372: /* flag the one match */
! 2373: one_match = axl_true;
! 2374:
! 2375: /* if the node reference is
! 2376: * not NULL, try to match the
! 2377: * next item */
! 2378: goto repeat_for_node;
! 2379: }
! 2380: } /* end if */
! 2381:
! 2382: /* before returning, that that we have matched
! 2383: * previously, at least, one node for
! 2384: * one-to-many and zero-to-many pattern */
! 2385: if ((times == ONE_OR_MANY || times == ZERO_OR_MANY) && one_match) {
! 2386: return axl_true;
! 2387: }
! 2388:
! 2389: } else if (axl_dtd_item_node_get_type (itemNode) == AXL_ELEMENT_LIST) {
! 2390: /* an element list was found, call to validate it */
! 2391: /* element list found, validate its content */
! 2392: if (__axl_dtd_validate_item_list (axl_dtd_item_node_get_list (itemNode),
! 2393: parent, child_position, error, axl_false)) {
! 2394: /* item list matched */
! 2395: return axl_true;
! 2396: }
! 2397: }
! 2398:
! 2399: /* no item was matched, update iterator indexes */
! 2400: iterator++;
! 2401: }
! 2402:
! 2403: /* seems that the choice list wasn't matched */
! 2404: if (! try_match) {
! 2405: axl_error_new (-1, "Unable to match choice list, after checking all posibilities, choice list wasn't validated",
! 2406: NULL, error);
! 2407: }
! 2408: return axl_false;
! 2409: }
! 2410:
! 2411: /**
! 2412: * @internal
! 2413: *
! 2414: * Tries to perform a validation, based on the item list received and
! 2415: * the repetition configuration.
! 2416: *
! 2417: * @param itemList The item list containing DTD content spec
! 2418: * information used to validate.
! 2419: *
! 2420: * @param parent The parent node where the validation process is being
! 2421: * applied. The content spec refers to the childs the parent has.
! 2422: *
! 2423: * @param stack An stack used by the overall process to store the
! 2424: * subsequent parents to be validated. This stack must be released if
! 2425: * a error is found.
! 2426: *
! 2427: * @param error An optional axlError reference containing the error
! 2428: * textual diagnostic if found.
! 2429: *
! 2430: * @return axl_true if the validation was ok, otherwise axl_false is
! 2431: * returned.
! 2432: */
! 2433: axl_bool __axl_dtd_validate_item_list (axlDtdElementList * itemList,
! 2434: axlNode * parent,
! 2435: int * child_position,
! 2436: axlError ** error,
! 2437: axl_bool top_level)
! 2438: {
! 2439: int temp_child_pos;
! 2440: int caller_child_pos;
! 2441: axl_bool status;
! 2442: axl_bool already_matched;
! 2443:
! 2444:
! 2445: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validating an item list with repeat pattern: %d, at %d, top level=%d",
! 2446: axl_dtd_item_list_repeat (itemList), *child_position, top_level);
! 2447:
! 2448: /* store current caller child position value to check it before */
! 2449: caller_child_pos = *child_position;
! 2450:
! 2451: /* now check repetition type */
! 2452: switch (axl_dtd_item_list_repeat (itemList)) {
! 2453: case ONE_AND_ONLY_ONE:
! 2454: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a (one and only one) spec..");
! 2455: if (axl_dtd_item_list_type (itemList) == SEQUENCE) {
! 2456:
! 2457: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "using a SEQUENCE form");
! 2458: /* it is a choice, so the item list specifies
! 2459: * the nodes that could appear */
! 2460: if (!__axl_dtd_validate_sequence (parent, child_position, itemList, error,
! 2461: axl_false, top_level)) {
! 2462: return axl_false;
! 2463: }
! 2464: }else {
! 2465: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "using a CHOICE form");
! 2466: /* it is a sequence, so, item list
! 2467: * specification represents the nodes, in the
! 2468: * order they must appear */
! 2469: if (!__axl_dtd_validate_choice (parent, child_position, itemList, error,
! 2470: axl_false, top_level)) {
! 2471: return axl_false;
! 2472: }
! 2473: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "choice list was properly validated");
! 2474: }
! 2475: break;
! 2476: case ZERO_OR_ONE:
! 2477:
! 2478: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a (zero or one) spec..");
! 2479: if (axl_dtd_item_list_type (itemList) == SEQUENCE) {
! 2480:
! 2481: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "using a SEQUENCE form, parent: <%s>",
! 2482: axl_node_get_name (parent));
! 2483: /* because we are running a zero or one item
! 2484: * list matching, we don't care if it doesn't
! 2485: * match. In the case it match, the child
! 2486: * position is updated and next calls will be
! 2487: * properly aligned. In the match doesn't
! 2488: * happens, it also don't matter because the
! 2489: * pattern allow to not match */
! 2490: temp_child_pos = *child_position;
! 2491: if (!__axl_dtd_validate_sequence (parent, child_position, itemList, error,
! 2492: axl_true, top_level)) {
! 2493: /* check that the match wasn't
! 2494: * produced, at any level */
! 2495: if (temp_child_pos != *child_position) {
! 2496: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item list mismatch (%d != %d)",
! 2497: temp_child_pos, *child_position);
! 2498: axl_error_new (-1, "Found an DTD item list definition, that should be matched entirely or not, zero or one time, but it was matched partially",
! 2499: NULL, error);
! 2500: return axl_false;
! 2501: }
! 2502:
! 2503: return axl_false;
! 2504: }
! 2505:
! 2506: }else {
! 2507: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a CHOICE form");
! 2508:
! 2509: /* it is a sequence, so, item list
! 2510: * specification represents the nodes, in the
! 2511: * order they must appear */
! 2512: __axl_dtd_validate_choice (parent, child_position, itemList, error,
! 2513: axl_true, top_level);
! 2514: }
! 2515: break;
! 2516: case ZERO_OR_MANY:
! 2517:
! 2518: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a (zero or many) spec..");
! 2519: if (axl_dtd_item_list_type (itemList) == SEQUENCE) {
! 2520:
! 2521: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a SEQUENCE (size: %d) form ",
! 2522: axl_dtd_item_list_count (itemList));
! 2523: /* one this case, several matches must be
! 2524: * tried, until the validation fails */
! 2525: do {
! 2526: temp_child_pos = *child_position;
! 2527: status = __axl_dtd_validate_sequence (parent, child_position, itemList, error,
! 2528: axl_true, top_level);
! 2529:
! 2530: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence match status=%d", status);
! 2531: if (! status) {
! 2532: /* check that the match wasn't
! 2533: * produced, at any level */
! 2534: if ((temp_child_pos != *child_position)) {
! 2535:
! 2536: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item list mismatch (%d != %d)",
! 2537: temp_child_pos, *child_position);
! 2538: axl_error_new (-1, "Found an DTD item list definition, that should be matched entirely or not, zero or many times, but it was matched partially",
! 2539: NULL, error);
! 2540: return axl_false;
! 2541: }
! 2542: }
! 2543: }while (status);
! 2544: }else {
! 2545:
! 2546: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a CHOICE form");
! 2547: /* it is a sequence, so, item list
! 2548: * specification represents the nodes, in the
! 2549: * order they must appear */
! 2550: do {
! 2551: status = __axl_dtd_validate_choice (parent, child_position, itemList, error,
! 2552: axl_true, top_level);
! 2553: }while (status);
! 2554: }
! 2555: break;
! 2556: case ONE_OR_MANY:
! 2557: /* one or many sequence spec (+) */
! 2558: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a (zero or many) spec..");
! 2559: if (axl_dtd_item_list_type (itemList) == SEQUENCE) {
! 2560:
! 2561: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a SEQUENCE (size: %d) form ",
! 2562: axl_dtd_item_list_count (itemList));
! 2563:
! 2564: /* one this case, several matches must be
! 2565: * tried, until the validation fails */
! 2566: already_matched = axl_false;
! 2567: do {
! 2568: temp_child_pos = *child_position;
! 2569: /* try to match the one or many
! 2570: sequence according to the value
! 2571: stored inside already matched */
! 2572: status = __axl_dtd_validate_sequence (parent, child_position, itemList, error,
! 2573: already_matched, top_level);
! 2574:
! 2575: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence match status=%d", status);
! 2576: if (! status) {
! 2577: /* check that the match wasn't
! 2578: * produced, at any level */
! 2579: if ((temp_child_pos != *child_position)) {
! 2580:
! 2581: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item list mismatch, matched partially (%d != %d)",
! 2582: temp_child_pos, *child_position);
! 2583: axl_error_new (-1,
! 2584: "Found an DTD item list definition, that should be matched entirely or not, one or many times, but it was matched partially",
! 2585: NULL, error);
! 2586: return axl_false;
! 2587: }
! 2588: }else {
! 2589: /* set that we have matched, at least, one item */
! 2590: already_matched = axl_true;
! 2591: }
! 2592:
! 2593:
! 2594:
! 2595: }while (status);
! 2596: }else {
! 2597:
! 2598: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a CHOICE form");
! 2599: /* it is a sequence, so, item list
! 2600: * specification represents the nodes, in the
! 2601: * order they must appear */
! 2602: already_matched = axl_false;
! 2603: do {
! 2604: /* the next choice matching is done
! 2605: * according to the value stored in
! 2606: * already matched */
! 2607: status = __axl_dtd_validate_choice (parent, child_position, itemList, error,
! 2608: already_matched, top_level);
! 2609: /* if the validation successed, set
! 2610: * that next matched are not required
! 2611: * to be successful ones */
! 2612: if (status)
! 2613: already_matched = axl_true;
! 2614: }while (status);
! 2615: }
! 2616: break;
! 2617: default:
! 2618: /* this case will never be reached */
! 2619: #define INTERNAL_ERROR_01 "critical error reached a place that shows the dtd parser is not properly defining the repetition pattern for the current itemList."
! 2620: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, INTERNAL_ERROR_01);
! 2621: axl_error_new (-1, INTERNAL_ERROR_01, NULL, error);
! 2622: return axl_false;
! 2623: }
! 2624:
! 2625:
! 2626: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validate item list terminated, now check post-conditions, top_level=%d, item list type=%d, child pos=%d, childs num=%d",
! 2627: top_level, axl_dtd_item_list_type (itemList), *child_position, axl_node_get_child_num (parent));
! 2628:
! 2629: /* check that, in the case that the choice item list is being
! 2630: * validated, ensure it has validated all nodes, especially if
! 2631: * we are the top level definition */
! 2632: if (top_level && (axl_dtd_item_list_type (itemList) == CHOICE)) {
! 2633: if (((*child_position) + 1) < axl_node_get_child_num (parent)) {
! 2634:
! 2635:
! 2636: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "found that the choice list didn't cover all childs (%d), while parent=<%s> has: (%d)",
! 2637: (*child_position), axl_node_get_name (parent), axl_node_get_child_num (parent));
! 2638: axl_error_new (-1, "Found that the validation process didn't cover all nodes, while using a choice list. This means that the xml document have more content than the DTD spec",
! 2639: NULL, error);
! 2640: return axl_false;
! 2641: }
! 2642: }
! 2643:
! 2644: /* element type children validated */
! 2645: return axl_true;
! 2646: }
! 2647:
! 2648: /**
! 2649: * @internal
! 2650: *
! 2651: * Support function validate parent nodes which are element type
! 2652: * children ones.
! 2653: */
! 2654: axl_bool __axl_dtd_validate_element_type_children (axlDtdElement * element,
! 2655: axlNode * parent,
! 2656: axl_bool top_level,
! 2657: axlError ** error)
! 2658: {
! 2659: axlDtdElementList * itemList;
! 2660: int child_pos = 0;
! 2661: char * err_msg;
! 2662:
! 2663: /* get a reference to the item list */
! 2664: itemList = axl_dtd_get_item_list (element);
! 2665:
! 2666: /* check for xml nodes with fewer content than the initially
! 2667: * expected. */
! 2668: if (axl_node_get_child_num (parent) < element->minimum_match) {
! 2669: err_msg = axl_strdup_printf ("Found that the parent node (<%s>) received doesn't contains enough xml nodes inside to get a proper validation (childs found (%d) != childs that should be found (%d)). This means that the xml document have fewer content than the DTD spec.",
! 2670: axl_node_get_name (parent),
! 2671: axl_node_get_child_num (parent),
! 2672: element->minimum_match);
! 2673: axl_error_new (-1, err_msg, NULL, error);
! 2674: axl_free (err_msg);
! 2675: return axl_false;
! 2676: }
! 2677:
! 2678: /* validate the item list, starting from the child 0 */
! 2679: if (__axl_dtd_validate_item_list (itemList, parent, &child_pos, error, top_level)) {
! 2680: /* check if, at least, all minimum elements was
! 2681: * matched */
! 2682: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking minimum node match: (%d < min: %d) (%d < childs: %d) node=<%s>",
! 2683: child_pos, element->minimum_match,
! 2684: child_pos, axl_node_get_child_num (parent),
! 2685: axl_node_get_name (parent));
! 2686:
! 2687: if (child_pos < axl_node_get_child_num (parent)) {
! 2688: axl_error_report (error, -1,
! 2689: "Found that the validation process didn't cover all nodes (%d < min:%d) (%d < childs:%d). All xml child nodes inside the parent=<%s> wasn't covered. This means that the xml document have more content than the DTD spec defines.",
! 2690: child_pos, element->minimum_match, child_pos, axl_node_get_child_num (parent),
! 2691: axl_node_get_name (parent));
! 2692: return axl_false;
! 2693: }
! 2694: /* seems that the minimum match */
! 2695: return axl_true;
! 2696: }
! 2697:
! 2698: return axl_false;
! 2699: }
! 2700:
! 2701: /**
! 2702: * @internal
! 2703: * Internal support function to validate #PCDATA nodes.
! 2704: */
! 2705: axl_bool __axl_dtd_validate_element_type_pcdata (axlDtdElement * element,
! 2706: axlNode * parent,
! 2707: axlStack * stack,
! 2708: axlError ** error)
! 2709: {
! 2710: /* check for childs */
! 2711: if (axl_node_have_childs (parent)) {
! 2712: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "node <%s> should be #PCDATA and it contains childs",
! 2713: axl_node_get_name (parent));
! 2714: axl_error_new (-1,
! 2715: "Found a node for which its espeficiation makes it to be a node with only data and no childs, and it currently contains childs",
! 2716: NULL, error);
! 2717: return axl_false;
! 2718: }
! 2719:
! 2720: /* return that the validation was ok */
! 2721: return axl_true;
! 2722: }
! 2723:
! 2724: /**
! 2725: * @internal
! 2726: *
! 2727: * Support function to validate empty nodes.
! 2728: */
! 2729: axl_bool __axl_dtd_validate_element_type_empty (axlDtdElement * element,
! 2730: axlNode * parent,
! 2731: axlStack * stack,
! 2732: axlError ** error)
! 2733: {
! 2734: char * err_msg;
! 2735:
! 2736: /* check the node is indeed, empty */
! 2737: if (! axl_node_is_empty (parent)) {
! 2738: err_msg = axl_strdup_printf (
! 2739: "Found a node <%s> that it is especified that must be empty, but it isn't",
! 2740: axl_node_get_name (parent));
! 2741: axl_error_new (-1, err_msg, NULL, error);
! 2742: axl_free (err_msg);
! 2743: return axl_false;
! 2744: }
! 2745:
! 2746: /* check the node doesn't have childs */
! 2747: if (axl_node_have_childs (parent)) {
! 2748: err_msg = axl_strdup_printf (
! 2749: "Found a node <%s> that it is especified that must be empty, but it has childs",
! 2750: axl_node_get_name (parent));
! 2751: axl_error_new (-1, err_msg, NULL, error);
! 2752: axl_free (err_msg);
! 2753: return axl_false;
! 2754: }
! 2755:
! 2756: /* return that the validation was ok */
! 2757: return axl_true;
! 2758: }
! 2759:
! 2760: axl_bool __axl_dtd_attr_validate_foreach (const char * key, const char * value, axlPointer data, axlPointer data2)
! 2761: {
! 2762: axlDtdAttribute * attribute = data;
! 2763: axlError ** error = data2;
! 2764: axlDtdAttributeDecl * decl;
! 2765: char * err_msg;
! 2766:
! 2767: /* get declaration associated */
! 2768: decl = axl_list_lookup (attribute->list, __find_attr_decl, (axlPointer) key);
! 2769:
! 2770: if (decl == NULL) {
! 2771: /* found an error */
! 2772: err_msg = axl_strdup_printf ("Found an attribute (%s) which is not specified by the attribute declaration for <%s>",
! 2773: key, attribute->name);
! 2774: axl_error_new (-1, err_msg, NULL, error);
! 2775:
! 2776: /* free the cursor and the error message */
! 2777: axl_free (err_msg);
! 2778:
! 2779: /* return axl_true here because we want to stop the process */
! 2780: return axl_true;
! 2781:
! 2782: } /* end if */
! 2783:
! 2784: /* if the declaration is found, now check its
! 2785: * contraints */
! 2786: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking contraint for attribute: %s=%s", decl->name, value);
! 2787: if (decl->type == CDATA_ATTRIBUTE) {
! 2788: /* found free attribute declaration, go ahed */
! 2789: } else if (decl->type == ENUMERATION_TYPE) {
! 2790: /* found an enumeration type, check and return */
! 2791: if (axl_list_lookup (decl->enumvalues, axl_list_find_string, (char *) value) == NULL) {
! 2792: /* found an error */
! 2793: err_msg = axl_strdup_printf ("Found an attribute (%s) with a value not allowed by the enum declaration (%s) for the node <%s>",
! 2794: key, value, attribute->name);
! 2795: axl_error_new (-1, err_msg, NULL, error);
! 2796:
! 2797: /* free the cursor and the error message */
! 2798: axl_free (err_msg);
! 2799: return axl_true;
! 2800: }
! 2801: } else {
! 2802: /* not supported yet */
! 2803: }
! 2804:
! 2805: /* return axl_false to continue with the process */
! 2806: return axl_false;
! 2807: }
! 2808:
! 2809: axl_bool __axl_dtd_attr_validate_required (axlPointer element, axlPointer data)
! 2810: {
! 2811: axlNode * node = data;
! 2812: axlDtdAttributeDecl * decl = element;
! 2813:
! 2814: switch (decl->defaults) {
! 2815: case ATT_REQUIRED:
! 2816: /* attribute required */
! 2817: return !HAS_ATTR (node, decl->name);
! 2818: case ATT_FIXED:
! 2819: return !HAS_ATTR_VALUE (node, decl->name, decl->default_value);
! 2820: default:
! 2821: break;
! 2822: } /* end switch */
! 2823:
! 2824: /* return axl_false for this because it is not obligatory
! 2825: * to have the attribute defined. */
! 2826: return axl_false;
! 2827: }
! 2828:
! 2829: /**
! 2830: * @internal Functions which validates the attribute declaration for
! 2831: * the node provided, using attribute declarations found.
! 2832: *
! 2833: * @param node The node to check for its attributes.
! 2834: *
! 2835: * @param dtd The dtd used to validate the node provided.
! 2836: *
! 2837: * @param error A reference to the axlError where the textual
! 2838: * diagnostic error will be reported.
! 2839: *
! 2840: * @return axl_true if the node is validated, axl_false if not.
! 2841: */
! 2842: axl_bool axl_dtd_attr_validate (axlNode * node, axlDtd * dtd, axlError ** error, axlHash * id_validation, axlList * idref_validation)
! 2843: {
! 2844: axlDtdAttribute * attribute;
! 2845: axlDtdAttributeDecl * decl;
! 2846: char * err_msg;
! 2847: int iterator;
! 2848: axlError * _error = NULL;
! 2849:
! 2850: /* find attribute contraints for the node */
! 2851: attribute = axl_dtd_get_attr (dtd, axl_node_get_name (node));
! 2852: if (attribute == NULL)
! 2853: return axl_true;
! 2854:
! 2855: /* we have an especification, run it */
! 2856:
! 2857: /* for each attribute found, check against the spec */
! 2858: axl_node_attr_foreach (node, __axl_dtd_attr_validate_foreach, attribute, &_error);
! 2859:
! 2860: /* check the error */
! 2861: if (! axl_error_was_ok (_error)) {
! 2862: /* reconfigure error returned */
! 2863: if (error != NULL)
! 2864: *error = _error;
! 2865: return axl_false;
! 2866: } /* end if */
! 2867:
! 2868:
! 2869: /* now, for each contraint, check that all required nodes
! 2870: * exists */
! 2871: decl = axl_list_lookup (attribute->list, __axl_dtd_attr_validate_required, node);
! 2872: if (decl != NULL) {
! 2873: if (decl->defaults == ATT_FIXED)
! 2874: err_msg = axl_strdup_printf ("attribute required '%s' (or its value), due to #FIXED declaration, not found for node <%s>",
! 2875: decl->name, attribute->name);
! 2876: else
! 2877: err_msg = axl_strdup_printf ("attribute required '%s', due to #REQUIRED declaration, not found for node <%s>",
! 2878: decl->name, attribute->name);
! 2879: axl_error_new (-1, err_msg, NULL, error);
! 2880: axl_free (err_msg);
! 2881: return axl_false;
! 2882: } /* end if */
! 2883:
! 2884: /* check declarations */
! 2885: if (dtd->haveIdDecl) {
! 2886:
! 2887: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD has ID unique attribute declaration..");
! 2888:
! 2889: /* check if the node have TOKENIZED_TYPE_ID */
! 2890: decl = axl_list_lookup (attribute->list, __find_id_decl, NULL);
! 2891:
! 2892: /* if we have a tokenized */
! 2893: if (decl != NULL) {
! 2894:
! 2895: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found ID unique attribute declaration %s=\"%s\"..",
! 2896: decl->name, ATTR_VALUE (node, decl->name));
! 2897:
! 2898: /* check if the attribute value for the decl that is
! 2899: * flagged as ID is already found at the
! 2900: * id_validation */
! 2901: if (axl_hash_exists (id_validation, (axlPointer) ATTR_VALUE (node, decl->name))) {
! 2902: err_msg = axl_strdup_printf ("DTD declared the attribute '%s' as unique (ID) for the node %s, but was found used several times",
! 2903: decl->name, attribute->name);
! 2904: axl_error_new (-1, err_msg, NULL, error);
! 2905: axl_free (err_msg);
! 2906: return axl_false;
! 2907: } /* end if */
! 2908:
! 2909: /* seems the attribute was not used, nice!, store it */
! 2910: axl_hash_insert (id_validation, (axlPointer) ATTR_VALUE (node, decl->name), (axlPointer) ATTR_VALUE (node, decl->name));
! 2911: } /* end if */
! 2912: } /* end if */
! 2913:
! 2914: if (dtd->haveIdRefDecl) {
! 2915: /* find the id ref declaration */
! 2916:
! 2917: iterator = 0;
! 2918: while (iterator < axl_list_length (attribute->list)) {
! 2919:
! 2920: /* get the attribute declaration at the
! 2921: * particular position */
! 2922: decl = axl_list_get_nth (attribute->list, iterator);
! 2923: if (decl->type == TOKENIZED_TYPE_IDREF) {
! 2924: /* found a reference, but do not check
! 2925: * it at this place becase the
! 2926: * reference could be placed at any
! 2927: * part in the document event after
! 2928: * the reference pointed is
! 2929: * defined. store and check later */
! 2930: if (ATTR_VALUE (node, decl->name)) {
! 2931: /* store the id ref reference
! 2932: * if defined */
! 2933: axl_list_add (idref_validation, (axlPointer) ATTR_VALUE (node, decl->name));
! 2934: }
! 2935: } /* end if */
! 2936:
! 2937: /* get the next */
! 2938: iterator++;
! 2939:
! 2940: } /* end if */
! 2941:
! 2942: } /* end if */
! 2943:
! 2944: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "attributes validated for node=<%s>", attribute->name);
! 2945:
! 2946: return axl_true;
! 2947: }
! 2948:
! 2949: /**
! 2950: * @internal Function used by axl_dtd_validate_references to ensure
! 2951: * that all references found point to a valid reference defined.
! 2952: */
! 2953: axl_bool __axl_dtd_reference_check (axlPointer _element, axlPointer data)
! 2954: {
! 2955: #if defined(SHOW_DEBUG_LOG)
! 2956: const char * value = _element;
! 2957:
! 2958: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking id ref: %s", value);
! 2959: #endif
! 2960:
! 2961: return ! axl_hash_exists ((axlHash *) data, _element);
! 2962: }
! 2963:
! 2964: /**
! 2965: * @internal Function that validates all references found (from IDREF
! 2966: * attribute) to unique references (defined by ID attributes).
! 2967: *
! 2968: */
! 2969: axl_bool axl_dtd_validate_references (axlHash * id_validation, axlList * idref_validation, axlError ** error)
! 2970: {
! 2971: char * reference;
! 2972: char * err_msg;
! 2973:
! 2974: /* if no empty at the valiadtion reference list, means not
! 2975: * reference was done, so there is no room for errors */
! 2976: if (idref_validation == NULL)
! 2977: return axl_true;
! 2978:
! 2979: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "id_validation reference: 0x%x", id_validation);
! 2980:
! 2981: /* find first reference not found */
! 2982: reference = axl_list_lookup (idref_validation, __axl_dtd_reference_check, id_validation);
! 2983:
! 2984: if (reference != NULL) {
! 2985: /* found a reference not defined, report it to the
! 2986: * application level */
! 2987: err_msg = axl_strdup_printf ("Found a reference defined ('%s') which is not found in any ID attribute in the document",
! 2988: reference);
! 2989: axl_error_new (-1, err_msg, NULL, error);
! 2990: axl_free (err_msg);
! 2991:
! 2992: return axl_false;
! 2993: } /* end if */
! 2994:
! 2995: /* validation ok */
! 2996: return axl_true;
! 2997: }
! 2998:
! 2999: /**
! 3000: * @brief Allows to validate the given XML document (\ref axlDoc)
! 3001: * against the given document type definition (DTD, \ref axlDtd).
! 3002: *
! 3003: * This function allows to validate your XML documents providing the
! 3004: * document type definition, that was read using \ref axl_dtd_parse or
! 3005: * \ref axl_dtd_parse_from_file.
! 3006: *
! 3007: * Keep in mind that a document could be well-formed and valid. The
! 3008: * only difference is that valid XML document are those that, meet all
! 3009: * XML rules, but also are clasified and recognized as XML documents
! 3010: * with some particular structure, that is represented (or
! 3011: * constrained) with providing a DTD definition.
! 3012: *
! 3013: * @param doc The \ref axlDoc containing the XML document to be
! 3014: * validated.
! 3015: *
! 3016: * @param dtd The \ref axlDtd containing the DTD definition used to
! 3017: * validate the document.
! 3018: *
! 3019: * @param error An optional reference to a \ref axlError object where
! 3020: * validation errors are reported.
! 3021: *
! 3022: * @return axl_true if the document is valid, axl_false if not.
! 3023: */
! 3024: axl_bool axl_dtd_validate (axlDoc * doc, axlDtd * dtd,
! 3025: axlError ** error)
! 3026: {
! 3027: axlNode * parent;
! 3028: axlStack * stack;
! 3029: axlHash * id_validation = NULL;
! 3030: axlList * idref_validation = NULL;
! 3031:
! 3032: axlDtdElement * element;
! 3033: axl_bool top_level;
! 3034: char * err_msg;
! 3035: axl_bool result;
! 3036:
! 3037: /* perform some checkings */
! 3038: axl_return_val_if_fail (doc, axl_false);
! 3039: axl_return_val_if_fail (dtd, axl_false);
! 3040:
! 3041: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "starting DTD validation");
! 3042:
! 3043: /* validate the very first root node */
! 3044: parent = axl_doc_get_root (doc);
! 3045: element = axl_dtd_get_root (dtd);
! 3046: if ((element != NULL) && ! NODE_CMP_NAME (parent, axl_dtd_get_element_name (element))) {
! 3047:
! 3048: /* because a DTD document could have several top level
! 3049: * elements, ensure this is not the case */
! 3050: element = axl_dtd_get_element (dtd, axl_node_get_name (parent));
! 3051: if (element == NULL) { /* || ! axl_dtd_element_is_toplevel (dtd, element)) { */
! 3052: /* root node doesn't match */
! 3053: err_msg = axl_strdup_printf ("Found that root node doesn't match (%s != %s!",
! 3054: axl_node_get_name (parent),
! 3055: axl_dtd_get_element_name (element));
! 3056: axl_error_new (-1, err_msg, NULL, error);
! 3057: axl_free (err_msg);
! 3058: return axl_false;
! 3059:
! 3060: } /* end if */
! 3061: } /* end if */
! 3062:
! 3063: /* check if the node has DTD element declaration */
! 3064: if (element == NULL) {
! 3065: err_msg = axl_strdup_printf ("There is not DTD element declaration to validate the node <%s>",
! 3066: axl_node_get_name (parent));
! 3067: axl_error_new (-1, err_msg, NULL, error);
! 3068: axl_free (err_msg);
! 3069: return axl_false;
! 3070: } /* end if */
! 3071:
! 3072: /* check if the dtd contains a Id declaration */
! 3073: if (dtd->haveIdDecl) {
! 3074: /* seems the user have declarted ID attributes init the hash */
! 3075: id_validation = axl_hash_new (axl_hash_string, axl_hash_equal_string);
! 3076: } /* end if */
! 3077:
! 3078: /* check if the dtd contains Id ref declarations */
! 3079: if (dtd->haveIdRefDecl) {
! 3080: /* create a list that could contain all references done */
! 3081: idref_validation = axl_list_new (axl_list_always_return_1, NULL);
! 3082: } /* end if */
! 3083:
! 3084: /* check empty content spec */
! 3085: if (axl_dtd_get_element_type (element) == ELEMENT_TYPE_EMPTY) {
! 3086: /* check if the document provided have only one node */
! 3087: result = axl_node_is_empty (parent) && !axl_node_have_childs (parent) && axl_dtd_attr_validate (parent, dtd, error, id_validation, idref_validation);
! 3088:
! 3089: /* check references */
! 3090: if (result)
! 3091: result = axl_dtd_validate_references (id_validation, idref_validation, error);
! 3092:
! 3093: /* free and return */
! 3094: axl_hash_free (id_validation);
! 3095:
! 3096: /* free the list */
! 3097: axl_list_free (idref_validation);
! 3098: return result;
! 3099: } /* end if */
! 3100:
! 3101: /* queue initial nodes to validate */
! 3102: stack = axl_stack_new (NULL);
! 3103:
! 3104:
! 3105: /* set that the only top level node is the first one */
! 3106: top_level = axl_true;
! 3107:
! 3108: do {
! 3109: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "doing a DTD iteration: <%s>...",
! 3110: axl_node_get_name (parent));
! 3111:
! 3112: /* validate attributes */
! 3113: if (! axl_dtd_attr_validate (parent, dtd, error, id_validation, idref_validation)) {
! 3114: /* free the stack */
! 3115: axl_stack_free (stack);
! 3116:
! 3117: /* free id_validation */
! 3118: axl_hash_free (id_validation);
! 3119:
! 3120: /* free the list */
! 3121: axl_list_free (idref_validation);
! 3122: return axl_false;
! 3123: }
! 3124:
! 3125: /* reach this position, the <parent> reference contains
! 3126: * a reference to the parent node, which will be used
! 3127: * to validate current child content against current
! 3128: * configuration for dtd element constraining it.
! 3129: *
! 3130: * equally, the <element> reference contains a dtd
! 3131: * reference to the already checked DTD element which
! 3132: * configure this parent node. */
! 3133: switch (axl_dtd_get_element_type (element)) {
! 3134: case ELEMENT_TYPE_PCDATA:
! 3135: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find PCDATA dtd element=%s: parent=<%s>, ",
! 3136: axl_dtd_get_element_name (element),
! 3137: axl_node_get_name (parent));
! 3138:
! 3139: /* ok, a leaf node was found, know it is
! 3140: * required to check that the node doesn't
! 3141: * have more childs and only have content,
! 3142: * that is, it is not empty */
! 3143: if (!__axl_dtd_validate_element_type_pcdata (element, parent, stack, error)) {
! 3144: /* free id_validation */
! 3145: axl_hash_free (id_validation);
! 3146:
! 3147: /* free the stack */
! 3148: axl_stack_free (stack);
! 3149:
! 3150: /* free the list */
! 3151: axl_list_free (idref_validation);
! 3152: return axl_false;
! 3153: }
! 3154: break;
! 3155: case ELEMENT_TYPE_CHILDREN:
! 3156:
! 3157: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find CHILDREN dtd element");
! 3158: /* ok, a parent node that have childs */
! 3159: if (!__axl_dtd_validate_element_type_children (element, parent, top_level, error)) {
! 3160: /* free id_validation */
! 3161: axl_hash_free (id_validation);
! 3162:
! 3163: /* free the stack */
! 3164: axl_stack_free (stack);
! 3165:
! 3166: /* free the list */
! 3167: axl_list_free (idref_validation);
! 3168:
! 3169: return axl_false;
! 3170: }
! 3171: break;
! 3172: case ELEMENT_TYPE_EMPTY:
! 3173: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find EMPTY dtd element");
! 3174: /* the element especification is empty, the
! 3175: * node being validated must also be the
! 3176: * same */
! 3177: if (!__axl_dtd_validate_element_type_empty (element, parent, stack, error)) {
! 3178: /* free id_validation */
! 3179: axl_hash_free (id_validation);
! 3180:
! 3181: /* free the stack */
! 3182: axl_stack_free (stack);
! 3183:
! 3184: /* free the list */
! 3185: axl_list_free (idref_validation);
! 3186:
! 3187: return axl_false;
! 3188: }
! 3189: break;
! 3190: case ELEMENT_TYPE_ANY:
! 3191: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find ANY dtd element");
! 3192: /* the anything is allowed cased from this
! 3193: * parent node. */
! 3194: goto continue_with_validation;
! 3195: case ELEMENT_TYPE_MIXED:
! 3196: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find MIXED dtd element");
! 3197:
! 3198: /* the mixed case, where nodes and PC data
! 3199: * could be mixed */
! 3200: break;
! 3201: default:
! 3202: /* do not do any thing on this case */
! 3203: break;
! 3204: }
! 3205:
! 3206: /* queue more childs, as future parents to be
! 3207: * validated on the provided queue, only in the case
! 3208: * the parent node have childs */
! 3209: if (axl_node_have_childs (parent)) {
! 3210:
! 3211: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "parent node <%s> have childs, adding its childs (stack size: %d)",
! 3212: axl_node_get_name (parent),
! 3213: axl_stack_size (stack));
! 3214:
! 3215: /* queue childs to be processed */
! 3216: __axl_dtd_queue_childs (stack, parent);
! 3217:
! 3218: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "parent node <%s> childs: %d, (stack size: %d)",
! 3219: axl_node_get_name (parent), axl_node_get_child_num (parent),
! 3220: axl_stack_size (stack));
! 3221: }
! 3222:
! 3223: /* set the parent reference to NULL */
! 3224: parent = NULL;
! 3225:
! 3226: /* update the reference to the new parent node, only
! 3227: * if there are new parents on the stack */
! 3228: continue_with_validation:
! 3229: if (! axl_stack_is_empty (stack)) {
! 3230:
! 3231:
! 3232: /* get a new reference */
! 3233: parent = axl_stack_pop (stack);
! 3234:
! 3235: /* get a reference to the DTD element to used */
! 3236: element = axl_dtd_get_element (dtd, axl_node_get_name (parent));
! 3237: if (element == NULL) {
! 3238: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "found that the node <%s> doesn't have DTD especification",
! 3239: axl_node_get_name (parent));
! 3240: /* prepare the error message */
! 3241: err_msg = axl_strdup_printf ("Found a node <%s> that doesn't have a DTD element espefication to validate it, DTD validation failed",
! 3242: axl_node_get_name (parent));
! 3243: axl_error_new (-1, err_msg, NULL, error);
! 3244: axl_free (err_msg);
! 3245:
! 3246: /* free id_validation */
! 3247: axl_hash_free (id_validation);
! 3248:
! 3249: /* free the list */
! 3250: axl_list_free (idref_validation);
! 3251:
! 3252: /* free the stack */
! 3253: axl_stack_free (stack);
! 3254: return axl_false;
! 3255: } /* end if */
! 3256: } /* end if */
! 3257:
! 3258: /* set the top level status */
! 3259: top_level = axl_false;
! 3260:
! 3261: /* until the stack is empty */
! 3262: }while (parent != NULL);
! 3263:
! 3264: /* check references */
! 3265: result = axl_dtd_validate_references (id_validation, idref_validation, error);
! 3266:
! 3267: /* deallocate stack used */
! 3268: axl_stack_free (stack);
! 3269:
! 3270: /* free id_validation */
! 3271: axl_hash_free (id_validation);
! 3272:
! 3273: /* free the list */
! 3274: axl_list_free (idref_validation);
! 3275:
! 3276: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD validation, %s", result ? "ok" : "failed");
! 3277:
! 3278: /* the document is valid */
! 3279: return result;
! 3280: }
! 3281:
! 3282: /**
! 3283: * @brief Allows to check if the provided two references represents
! 3284: * DTD documents with the same rules.
! 3285: *
! 3286: * @param dtd First reference to compare.
! 3287: * @param dtd2 Second reference to compare.
! 3288: *
! 3289: * @return axl_true if both references represent the same document. If
! 3290: * some of the references received are NULL the function returns
! 3291: * axl_false.
! 3292: *
! 3293: * NOTE: The function does not have the ability to perform a smart
! 3294: * equal operation like detecting DTD that are semantically
! 3295: * equivalent. It only checks internal structure.
! 3296: */
! 3297: axl_bool axl_dtd_are_equal (axlDtd * dtd,
! 3298: axlDtd * dtd2)
! 3299: {
! 3300: int iterator;
! 3301: int iterator2;
! 3302: int iterator3;
! 3303: axlDtdEntity * entity, * entity2;
! 3304: axlDtdElement * element, * element2;
! 3305: axlDtdElementListNode * node, * node2;
! 3306: axlDtdAttribute * attribute, * attribute2;
! 3307: axlDtdAttributeDecl * attr_decl, * attr_decl2;
! 3308:
! 3309: /* check references received */
! 3310: if (dtd == NULL)
! 3311: return axl_false;
! 3312: if (dtd2 == NULL)
! 3313: return axl_false;
! 3314:
! 3315: /* check each rule inside both documents */
! 3316: if (axl_list_length (dtd->entities) != axl_list_length (dtd2->entities))
! 3317: return axl_false;
! 3318: if (axl_list_length (dtd->elements) != axl_list_length (dtd2->elements))
! 3319: return axl_false;
! 3320: if (axl_list_length (dtd->attributes) != axl_list_length (dtd2->attributes))
! 3321: return axl_false;
! 3322: if (dtd->haveIdRefDecl != dtd2->haveIdRefDecl)
! 3323: return axl_false;
! 3324: if (dtd->haveIdDecl != dtd2->haveIdDecl)
! 3325: return axl_false;
! 3326:
! 3327: /* now check inner elements (ENTITIES) */
! 3328: iterator = 0;
! 3329: while (iterator < axl_list_length (dtd->entities)) {
! 3330: /* get referneces */
! 3331: entity = axl_list_get_nth (dtd->entities, iterator);
! 3332: entity2 = axl_list_get_nth (dtd2->entities, iterator);
! 3333:
! 3334: /* check types */
! 3335: if (entity->type != entity2->type)
! 3336: return axl_false;
! 3337:
! 3338: /* check names */
! 3339: if (! axl_cmp (entity->name, entity2->name))
! 3340: return axl_false;
! 3341:
! 3342: /* check content */
! 3343: if (! axl_cmp (entity->content, entity2->content))
! 3344: return axl_false;
! 3345:
! 3346: /* check external data */
! 3347: if (entity->data == NULL && entity2->data != NULL)
! 3348: return axl_false;
! 3349: if (entity->data != NULL && entity2->data == NULL)
! 3350: return axl_false;
! 3351: if (entity->data != NULL && entity2->data != NULL) {
! 3352: if (! axl_cmp (entity->data->system_literal, entity2->data->system_literal))
! 3353: return axl_false;
! 3354: if (! axl_cmp (entity->data->public_literal, entity2->data->public_literal))
! 3355: return axl_false;
! 3356: if (! axl_cmp (entity->data->ndata, entity2->data->ndata))
! 3357: return axl_false;
! 3358: } /* end if */
! 3359:
! 3360: /* next iterator */
! 3361: iterator++;
! 3362: } /* end while */
! 3363:
! 3364: /* now check inner elements (ELEMENTS) */
! 3365: iterator = 0;
! 3366: while (iterator < axl_list_length (dtd->elements)) {
! 3367: /* get referneces */
! 3368: element = axl_list_get_nth (dtd->elements, iterator);
! 3369: element2 = axl_list_get_nth (dtd2->elements, iterator);
! 3370:
! 3371: /* check types */
! 3372: if (element->type != element2->type)
! 3373: return axl_false;
! 3374:
! 3375: /* minimum match */
! 3376: if (element->minimum_match != element2->minimum_match)
! 3377: return axl_false;
! 3378:
! 3379: /* check names */
! 3380: if (! axl_cmp (element->name, element2->name))
! 3381: return axl_false;
! 3382:
! 3383: /* check element list */
! 3384: if (element->list == NULL && element2->list != NULL)
! 3385: return axl_false;
! 3386: if (element->list != NULL && element2->list == NULL)
! 3387: return axl_false;
! 3388: if (element->list != NULL && element2->list != NULL) {
! 3389:
! 3390: /* check internal values */
! 3391: if (element->list->type != element2->list->type)
! 3392: return axl_false;
! 3393: if (element->list->times != element2->list->times)
! 3394: return axl_false;
! 3395:
! 3396: iterator2 = 0;
! 3397: while (iterator2 < axl_list_length (element->list->itemList)) {
! 3398:
! 3399: /* get references */
! 3400: node = axl_list_get_nth (element->list->itemList, iterator2);
! 3401: node2 = axl_list_get_nth (element2->list->itemList, iterator2);
! 3402:
! 3403: if (node->type != node->type)
! 3404: return axl_false;
! 3405: if (node->times != node2->times)
! 3406: return axl_false;
! 3407:
! 3408: /* next value */
! 3409: iterator2++;
! 3410:
! 3411: } /* end while */
! 3412:
! 3413: } /* end if */
! 3414:
! 3415: /* next iterator */
! 3416: iterator++;
! 3417: } /* end while */
! 3418:
! 3419: /* now check inner elements (ATTRIBUTES) */
! 3420: iterator = 0;
! 3421: while (iterator < axl_list_length (dtd->attributes)) {
! 3422: /* get referneces */
! 3423: attribute = axl_list_get_nth (dtd->attributes, iterator);
! 3424: attribute2 = axl_list_get_nth (dtd2->attributes, iterator);
! 3425:
! 3426: /* check names */
! 3427: if (! axl_cmp (attribute->name, attribute2->name))
! 3428: return axl_false;
! 3429:
! 3430: /* check values */
! 3431: if (attribute->list == NULL && attribute2->list != NULL)
! 3432: return axl_false;
! 3433: if (attribute->list != NULL && attribute2->list == NULL)
! 3434: return axl_false;
! 3435: if (attribute->list != NULL && attribute2->list != NULL) {
! 3436:
! 3437: /* check list length */
! 3438: if (axl_list_length (attribute->list) != axl_list_length (attribute2->list))
! 3439: return axl_false;
! 3440:
! 3441: /* check internal values */
! 3442: iterator2 = 0;
! 3443: while (iterator2 < axl_list_length (attribute->list)) {
! 3444:
! 3445: /* get references */
! 3446: attr_decl = axl_list_get_nth (attribute->list, iterator2);
! 3447: attr_decl2 = axl_list_get_nth (attribute2->list, iterator2);
! 3448:
! 3449: if (attr_decl->type != attr_decl2->type)
! 3450: return axl_false;
! 3451: if (attr_decl->defaults != attr_decl2->defaults)
! 3452: return axl_false;
! 3453: if (! axl_cmp (attr_decl->name, attr_decl2->name))
! 3454: return axl_false;
! 3455:
! 3456: if (attr_decl->enumvalues == NULL && attr_decl2->enumvalues != NULL)
! 3457: return axl_false;
! 3458: if (attr_decl->enumvalues != NULL && attr_decl2->enumvalues == NULL)
! 3459: return axl_false;
! 3460: if (attr_decl->enumvalues != NULL && attr_decl2->enumvalues != NULL) {
! 3461: if (axl_list_length (attr_decl->enumvalues) != axl_list_length (attr_decl2->enumvalues))
! 3462: return axl_false;
! 3463: iterator3 = 0;
! 3464: while (iterator3 < axl_list_length (attr_decl->enumvalues)) {
! 3465: /* check values */
! 3466: if (! axl_cmp (axl_list_get_nth (attr_decl->enumvalues, iterator3),
! 3467: axl_list_get_nth (attr_decl2->enumvalues, iterator3)))
! 3468: return axl_false;
! 3469:
! 3470: /* next value */
! 3471: iterator3++;
! 3472: } /* end while */
! 3473: } /* end if */
! 3474:
! 3475: /* next value */
! 3476: iterator2++;
! 3477:
! 3478: } /* end while */
! 3479:
! 3480: } /* end if */
! 3481:
! 3482: /* next iterator */
! 3483: iterator++;
! 3484: } /* end while */
! 3485:
! 3486: return axl_true;
! 3487:
! 3488: }
! 3489:
! 3490: /**
! 3491: * @brief Allows to get the root node for the provided DTD.
! 3492: *
! 3493: * Every DTD have a root node defined, which is the root node accepted
! 3494: * for the set of XML document considered to be valid under the
! 3495: * definition of the DTD provided.
! 3496: *
! 3497: * The value returned is the name of the root node that must have the
! 3498: * XML document being validated.
! 3499: *
! 3500: * @param dtd The \ref axlDtd where the root node name will be
! 3501: * returned.
! 3502: *
! 3503: * @return A reference to the internal representation of the root node
! 3504: * Value must not be deallocated.
! 3505: */
! 3506: axlDtdElement * axl_dtd_get_root (axlDtd * dtd)
! 3507: {
! 3508: axl_return_val_if_fail (dtd, NULL);
! 3509:
! 3510: /* return current status for the root node */
! 3511: if (dtd->root == NULL) {
! 3512: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "dtd root element not defined");
! 3513: return NULL;
! 3514: }
! 3515: return dtd->root;
! 3516: }
! 3517:
! 3518: /**
! 3519: * @internal function used by \ref axl_dtd_get_element to perform node
! 3520: * lookups.
! 3521: */
! 3522: axl_bool __find_dtd_element (axlPointer _element, axlPointer data)
! 3523: {
! 3524: axlDtdElement * element = _element;
! 3525: char * name = data;
! 3526:
! 3527: /* check the name */
! 3528: if (axl_cmp (element->name, name))
! 3529: return axl_true;
! 3530:
! 3531: /* it is not the element */
! 3532: return axl_false;
! 3533: }
! 3534:
! 3535: /**
! 3536: * @brief Allows to get the DTD element (\ref axlDtdElement), inside
! 3537: * the provided DTD (\ref axlDtd), that represent the spefication for
! 3538: * the node called by the provided name.
! 3539: *
! 3540: * @param dtd The DTD (\ref axlDtd) where the lookup will be
! 3541: * performed.
! 3542: *
! 3543: * @param name The element name to lookup.
! 3544: *
! 3545: * @return A reference to the \ref axlDtdElement searched or NULL if
! 3546: * fails. The function also returns NULL if values received are NULL.
! 3547: */
! 3548: axlDtdElement * axl_dtd_get_element (axlDtd * dtd, const char * name)
! 3549: {
! 3550:
! 3551: axl_return_val_if_fail (dtd, NULL);
! 3552: axl_return_val_if_fail (name, NULL);
! 3553:
! 3554: /* perform the lookup */
! 3555: return axl_list_lookup (dtd->elements, __find_dtd_element, (axlPointer) name);
! 3556: }
! 3557:
! 3558: /**
! 3559: * @internal function used by \ref axl_dtd_get_attr to perform node
! 3560: * lookups.
! 3561: */
! 3562: axl_bool __find_dtd_attr (axlPointer _element, axlPointer data)
! 3563: {
! 3564: axlDtdAttribute * attr = _element;
! 3565: char * name = data;
! 3566:
! 3567: /* check the name */
! 3568: if (axl_cmp (attr->name, name))
! 3569: return axl_true;
! 3570:
! 3571: /* it is not the element */
! 3572: return axl_false;
! 3573: }
! 3574:
! 3575: /**
! 3576: * @brief Allows to get the set of attribute declerations for a
! 3577: * particular node.
! 3578: *
! 3579: * The \ref axlDtdAttribute declaration contains all constraints
! 3580: * configured for attributes found for the particular xml node
! 3581: * (identified by <b>name</b>).
! 3582: *
! 3583: * @param dtd A reference to the DTD document.
! 3584: *
! 3585: * @param nodeName The xml node that is requested to return all attribute
! 3586: * declarations.
! 3587: *
! 3588: * @return A reference to the \ref axlDtdAttribute or NULL if it
! 3589: * fails.
! 3590: */
! 3591: axlDtdAttribute * axl_dtd_get_attr (axlDtd * dtd,
! 3592: const char * nodeName)
! 3593: {
! 3594: axl_return_val_if_fail (dtd, NULL);
! 3595: axl_return_val_if_fail (nodeName, NULL);
! 3596:
! 3597: /* perform the lookup */
! 3598: return axl_list_lookup (dtd->attributes, __find_dtd_attr, (axlPointer) nodeName);
! 3599: }
! 3600:
! 3601: /**
! 3602: * @brief Allows to get the number of constraints that have been
! 3603: * configured for the particular node.
! 3604: *
! 3605: * @param dtd The reference to the DTD document.
! 3606: *
! 3607: * @param nodeName The name of the node that is being asked for its
! 3608: * constraints.
! 3609: *
! 3610: * @return 0 or the number of contraints. The function return -1 if
! 3611: * any of the parameter received is null.
! 3612: */
! 3613: int axl_dtd_get_attr_contraints (axlDtd * dtd,
! 3614: const char * nodeName)
! 3615: {
! 3616: axlDtdAttribute * attr;
! 3617:
! 3618: axl_return_val_if_fail (dtd, -1);
! 3619: axl_return_val_if_fail (nodeName, -1);
! 3620:
! 3621: /* get the attribute specification for the node */
! 3622: attr = axl_dtd_get_attr (dtd, nodeName);
! 3623:
! 3624: /* return the number of items */
! 3625: return axl_list_length (attr->list);
! 3626: }
! 3627:
! 3628: /**
! 3629: * @brief Returns the name of the provided \ref axlDtdElement.
! 3630: *
! 3631: * @param element A reference to a \ref axlDtdElement where the name
! 3632: * will be returned.
! 3633: *
! 3634: * @return A reference to the internal DTD element name. Returned
! 3635: * value mustn't be deallocated.
! 3636: */
! 3637: char * axl_dtd_get_element_name (axlDtdElement * element)
! 3638: {
! 3639: axl_return_val_if_fail (element, NULL);
! 3640:
! 3641: return element->name;
! 3642: }
! 3643:
! 3644: /**
! 3645: * @brief Returns current element type for the provided \ref axlDtdElement.
! 3646: *
! 3647: * @param element The axlDtdElement where its type will be returned.
! 3648: *
! 3649: * @return Current element type for the provided node.
! 3650: */
! 3651: AxlDtdElementType axl_dtd_get_element_type (axlDtdElement * element)
! 3652: {
! 3653: axl_return_val_if_fail (element, ELEMENT_TYPE_UNKNOWN);
! 3654:
! 3655: return element->type;
! 3656: }
! 3657:
! 3658: /**
! 3659: * @brief Returns current DTD content specification, represented by the Item list.
! 3660: *
! 3661: * @param element The DTD element (\ref axlDtdElement) which is being
! 3662: * requested to return its \ref axlDtdElementList.
! 3663: *
! 3664: * @return The \ref axlDtdElementList reference. The value returned
! 3665: * must not be deallocated. The function returns NULL if the reference received is NULL.
! 3666: */
! 3667: axlDtdElementList * axl_dtd_get_item_list (axlDtdElement * element)
! 3668: {
! 3669: axl_return_val_if_fail (element, NULL);
! 3670:
! 3671: return element->list;
! 3672: }
! 3673:
! 3674: /**
! 3675: * @brief Allows to check if the provided DTD ELEMENT representation
! 3676: * is a top level definition.
! 3677: *
! 3678: * @param dtd The DTD document where the operation will be performed.
! 3679: * @param element The \ref axlDtdElement to check.
! 3680: *
! 3681: * @return \ref axl_true if the dtd element is a top level element or
! 3682: * \ref axl_false if not. The function returns \ref axl_false if the
! 3683: * provided reference is NULL.
! 3684: */
! 3685: axl_bool axl_dtd_element_is_toplevel (axlDtd * dtd, axlDtdElement * element)
! 3686: {
! 3687: /* support several top level definitions */
! 3688: int iterator;
! 3689: axlDtdElement * dtd_element_aux;
! 3690:
! 3691: axl_return_val_if_fail (dtd, axl_false);
! 3692: axl_return_val_if_fail (element, axl_false);
! 3693:
! 3694: /* check which is the top */
! 3695: iterator = 0;
! 3696: while (iterator < axl_list_length (dtd->elements)) {
! 3697:
! 3698: /* get the next reference */
! 3699: dtd_element_aux = axl_list_get_nth (dtd->elements, iterator);
! 3700:
! 3701: /* check which is the top */
! 3702: if (__axl_dtd_get_is_parent (dtd_element_aux, element)) {
! 3703: /* the element provided have a parent */
! 3704: return axl_false;
! 3705: }
! 3706:
! 3707: /* update inner loop iterator */
! 3708: iterator ++;
! 3709: } /* while end */
! 3710:
! 3711: /* return that the provided node doesn't have a parent node */
! 3712: return axl_true;
! 3713: }
! 3714:
! 3715: /**
! 3716: * @brief Returns the number of item nodes (\ref
! 3717: * axlDtdElementListNode) inside the item list received (\ref axlDtdElementList).
! 3718: *
! 3719: * @param itemList The \ref axlDtdElementList where the count
! 3720: * operation is being requested.
! 3721: *
! 3722: * @return The number of item list the provided \ref axlDtdElementList
! 3723: * reference has. The function return -1 if the provided reference is
! 3724: * NULL.
! 3725: */
! 3726: int axl_dtd_item_list_count (axlDtdElementList * itemList)
! 3727: {
! 3728: axl_return_val_if_fail (itemList, -1);
! 3729:
! 3730: if (itemList->itemList == NULL)
! 3731: return 0;
! 3732:
! 3733: return axl_list_length (itemList->itemList);
! 3734: }
! 3735:
! 3736: /**
! 3737: * @brief Allows to get current configuration for the provided item
! 3738: * list, which is the content specification for a DTD element.
! 3739: *
! 3740: * @param itemList The item list where the operation will be
! 3741: * performed.
! 3742: *
! 3743: * @return Current configuration (\ref SEQUENCE or a \ref CHOICE).
! 3744: */
! 3745: AxlDtdNestedType axl_dtd_item_list_type (axlDtdElementList * itemList)
! 3746: {
! 3747: axl_return_val_if_fail (itemList, -1);
! 3748:
! 3749: return itemList->type;
! 3750: }
! 3751:
! 3752: /**
! 3753: * @brief Allows to get current configuration for DTD content spec
! 3754: * repetition.
! 3755: *
! 3756: * @param itemList The content spec where the query will be performed.
! 3757: *
! 3758: * @return Current configuration for times to be repeated DTD element
! 3759: * content specification.
! 3760: */
! 3761: AxlDtdTimes axl_dtd_item_list_repeat (axlDtdElementList * itemList)
! 3762: {
! 3763: axl_return_val_if_fail (itemList, DTD_TIMES_UNKNOWN);
! 3764:
! 3765: /* returns current times configuration */
! 3766: return itemList->times;
! 3767: }
! 3768:
! 3769: /**
! 3770: * @brief Allows to get the provided item node reference (\ref
! 3771: * axlDtdElementListNode) from the provided item list (\ref
! 3772: * axlDtdElementList).
! 3773: *
! 3774: * Provided position ranges from 0 up to \ref axl_dtd_item_list_count.
! 3775: *
! 3776: * @param itemList The itemList where the operation will be performed.
! 3777: * @param position The position where the item node will be looked up.
! 3778: *
! 3779: * @return A reference to the \ref axlDtdElementListNode, or NULL if
! 3780: * there is no item node at the selected index. The function return
! 3781: * NULL if the provided position is a non positive value or it is
! 3782: * greater than the current item list count (\ref
! 3783: * axl_dtd_item_list_count) or the provided item list reference is
! 3784: * NULL.
! 3785: */
! 3786: axlDtdElementListNode * axl_dtd_item_list_get_node (axlDtdElementList * itemList,
! 3787: int position)
! 3788: {
! 3789: axl_return_val_if_fail (itemList, NULL);
! 3790: axl_return_val_if_fail (position >= 0, NULL);
! 3791: axl_return_val_if_fail (position < axl_dtd_item_list_count (itemList), NULL);
! 3792:
! 3793: return axl_list_get_nth (itemList->itemList, position);
! 3794: }
! 3795:
! 3796: /**
! 3797: * @brief Allows to get current node type for the provided DTD element
! 3798: * type content particule or item node (\ref axlDtdElementListNode).
! 3799: *
! 3800: * @param node The node where the type is being requested.
! 3801: *
! 3802: * @return It returns if the item node contains a final leaf node,
! 3803: * making a reference to an explicit node naming that is allowed to be
! 3804: * used in the context where is found the provided \ref
! 3805: * axlDtdElementListNode or a \ref axlDtdElementList containing more
! 3806: * nodes or lists.
! 3807: */
! 3808: NodeType axl_dtd_item_node_get_type (axlDtdElementListNode * node)
! 3809: {
! 3810: axl_return_val_if_fail (node, AXL_ELEMENT_NOT_DEFINED);
! 3811: return node->type;
! 3812: }
! 3813:
! 3814: /**
! 3815: * @brief Returns the item list inside the provided node.
! 3816: *
! 3817: * The node is supported to contain an item list reference or NULL
! 3818: * will be returned. Check \ref axl_dtd_item_node_get_type.
! 3819: *
! 3820: * @param node The node where the operation will be performed.
! 3821: *
! 3822: * @return The item list inside the node or NULL if fails.
! 3823: */
! 3824: axlDtdElementList * axl_dtd_item_node_get_list (axlDtdElementListNode * node)
! 3825: {
! 3826: axl_return_val_if_fail (node, NULL);
! 3827: axl_return_val_if_fail (node->type == AXL_ELEMENT_LIST, NULL);
! 3828:
! 3829: return node->data;
! 3830: }
! 3831:
! 3832: /**
! 3833: * @brief Allows to get the dtd item list value, which represents the
! 3834: * node name that is being constrained/represented.
! 3835: *
! 3836: * @param node The item node where the value is being requested.
! 3837: *
! 3838: * @return The value inside the item node, supposing it contains an
! 3839: * leaf item node or NULL if fails. The value returned must not be
! 3840: * deallocated.
! 3841: */
! 3842: char * axl_dtd_item_node_get_value (axlDtdElementListNode * node)
! 3843: {
! 3844: axl_return_val_if_fail (node, NULL);
! 3845: if (node->type != AXL_ELEMENT_NODE)
! 3846: return "requested-value-on-a-list";
! 3847:
! 3848: return node->data;
! 3849: }
! 3850:
! 3851: /**
! 3852: * @brief Allows to get current configuration for the provided content
! 3853: * particule for the times to be repeated.
! 3854: *
! 3855: * @param node The content particule where the query will be
! 3856: * performed.
! 3857: *
! 3858: * @return Return current repetition configuration.
! 3859: */
! 3860: AxlDtdTimes axl_dtd_item_node_get_repeat (axlDtdElementListNode * node)
! 3861: {
! 3862: axlDtdElementList * list;
! 3863:
! 3864: axl_return_val_if_fail (node, DTD_TIMES_UNKNOWN);
! 3865:
! 3866:
! 3867: if (node->type == AXL_ELEMENT_NODE) {
! 3868: /* return value requested */
! 3869: return node->times;
! 3870: }
! 3871:
! 3872: if (node->type == AXL_ELEMENT_LIST) {
! 3873: /* return the requested value for an item list */
! 3874: list = node->data;
! 3875: return list->times;
! 3876: }
! 3877:
! 3878: /* return that we don't know man */
! 3879: return DTD_TIMES_UNKNOWN;
! 3880: }
! 3881:
! 3882:
! 3883: /**
! 3884: * @internal
! 3885: *
! 3886: * Internal function which allows to lookup the DTD entity reference
! 3887: * provided the name and the type.
! 3888: */
! 3889: axlDtdEntity * __axl_dtd_entity_lookup (axlDtd * dtd,
! 3890: const char * name,
! 3891: axlDtdEntityType type)
! 3892: {
! 3893: axlDtdEntity * entity;
! 3894: int iterator;
! 3895: int length;
! 3896:
! 3897: /* check values received */
! 3898: axl_return_val_if_fail (dtd, NULL);
! 3899: axl_return_val_if_fail (name, NULL);
! 3900:
! 3901: /* lookup for the item */
! 3902: iterator = 0;
! 3903: length = axl_list_length (dtd->entities);
! 3904: while (iterator < length) {
! 3905:
! 3906: /* get the entity at the provided position */
! 3907: entity = axl_list_get_nth (dtd->entities, iterator);
! 3908:
! 3909: /* check the type and the name */
! 3910: if ((entity->type == type) && axl_cmp (entity->name, name))
! 3911: return entity;
! 3912:
! 3913: /* update iterator */
! 3914: iterator++;
! 3915: } /* end while */
! 3916:
! 3917: return NULL;
! 3918: }
! 3919:
! 3920: /**
! 3921: * @brief Allows to check if the provided entity name, with the
! 3922: * provided type is defined on the given DTD object.
! 3923: *
! 3924: * @param dtd The \ref axlDtd instance where the entity lookup will be
! 3925: * performed.
! 3926: *
! 3927: * @param name The entity name to lookup.
! 3928: *
! 3929: * @param type The entity type to lookup.
! 3930: *
! 3931: * @return axl_true if an entity is found named as provided with the type
! 3932: * provided. Othewise, axl_false is returned.
! 3933: */
! 3934: axl_bool axl_dtd_entity_exists (axlDtd * dtd,
! 3935: const char * name,
! 3936: axlDtdEntityType type)
! 3937: {
! 3938: /* return if the entity exists */
! 3939: return (__axl_dtd_entity_lookup (dtd, name, type) != NULL);
! 3940: }
! 3941:
! 3942: /**
! 3943: * @brief Allows to get the content configured inside the entity that
! 3944: * is identified by the provided name and the provided type.
! 3945: *
! 3946: * @param dtd The DTD where the lookup will be performed.
! 3947: *
! 3948: * @param name The entity name to lookup for its content.
! 3949: *
! 3950: * @param type The entity type to match.
! 3951: *
! 3952: * @return An internal reference to the content associated to the
! 3953: * entity found or NULL. In case the content is defined (as return
! 3954: * value) it must not be deallocated.
! 3955: */
! 3956: char * axl_dtd_entity_value (axlDtd * dtd,
! 3957: const char * name,
! 3958: axlDtdEntityType type)
! 3959: {
! 3960: axlDtdEntity * entity;
! 3961:
! 3962: /* get the entity reference */
! 3963: entity = __axl_dtd_entity_lookup (dtd, name, type);
! 3964:
! 3965: /* check the entity reference */
! 3966: axl_return_val_if_fail (entity, NULL);
! 3967:
! 3968: /* return the content */
! 3969: return entity->content;
! 3970: }
! 3971:
! 3972: /**
! 3973: * @brief Allows to destroy the provided \ref axlDtd document.
! 3974: *
! 3975: * @param dtd The \ref axlDtd document to destroy.
! 3976: */
! 3977: void axl_dtd_free (axlDtd * dtd)
! 3978: {
! 3979: if (dtd == NULL) {
! 3980: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "received a null DTD reference, doing nothing");
! 3981: return;
! 3982: }
! 3983:
! 3984: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "releasing the DTD reference");
! 3985: /* free dtd elements */
! 3986: if (dtd->elements)
! 3987: axl_list_free (dtd->elements);
! 3988:
! 3989: /* free entities */
! 3990: if (dtd->entities)
! 3991: axl_list_free (dtd->entities);
! 3992:
! 3993: /* free attributes */
! 3994: if (dtd->attributes)
! 3995: axl_list_free (dtd->attributes);
! 3996:
! 3997: /* free the node itself */
! 3998: axl_free (dtd);
! 3999:
! 4000: return;
! 4001: }
! 4002:
! 4003:
! 4004: /**
! 4005: * @internal
! 4006: *
! 4007: * @brief Allows to release the memory hold by the given
! 4008: * axlDtdElement.
! 4009: *
! 4010: * @param element The axlDtdElement to release.
! 4011: */
! 4012: void axl_dtd_element_free (axlDtdElement * element)
! 4013: {
! 4014: if (element == NULL)
! 4015: return;
! 4016:
! 4017: /* free element name */
! 4018: if (element->name != NULL)
! 4019: axl_free (element->name);
! 4020:
! 4021: /* free element list definitions */
! 4022: axl_dtd_item_list_free (element->list);
! 4023:
! 4024: /* free element itself */
! 4025: axl_free (element);
! 4026:
! 4027: return;
! 4028: }
! 4029:
! 4030: /**
! 4031: * @internal
! 4032: *
! 4033: * @brief Deallocates memory used by the \ref axlDtdElementList
! 4034: * reference.
! 4035: *
! 4036: * @param list The reference to deallocate.
! 4037: */
! 4038: void axl_dtd_item_list_free (axlDtdElementList * list)
! 4039: {
! 4040: if (list == NULL)
! 4041: return;
! 4042:
! 4043: /* check and deallocate the list provided */
! 4044: if (list->itemList != NULL)
! 4045: axl_list_free (list->itemList);
! 4046:
! 4047: /* deallocates the node itself */
! 4048: axl_free (list);
! 4049: return;
! 4050: }
! 4051:
! 4052: /* @} */
! 4053:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>