Annotation of gpl/axl/src/axl_doc.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:
! 40: /**
! 41: * @internal
! 42: * @brief XML 1.0 Third edition grammar
! 43: *
! 44: * [1] document ::= prolog element Misc*
! 45: * [1] status: partially
! 46: *
! 47: * [2] Char ::= \x9 | \xA | \xD | \x20-\xD7FF | \xE000-\xFFFD | \x10000-\10FFFF
! 48: * [2] status: not implemented
! 49: *
! 50: * [3] S ::= ( \x20 | \x9 | \xD | \xA)
! 51: * [3] status: ok
! 52: *
! 53: * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender
! 54: * [4] status: not implemented
! 55: *
! 56: * [5] Name ::= ( Letter | '_' | ':' |) ( NameChar )*
! 57: * [5] status: not implemented
! 58: *
! 59: * [6] Names ::= Name ( \x20 Name )*
! 60: * [6] status: not implemented
! 61: *
! 62: * [7] Nmtoken ::= ( NameChar ) +
! 63: * [7] status: not implemented
! 64: *
! 65: * [8] Nmtokens ::= Nmtoken (\x20 Nmtoken)*
! 66: * [8] status: not implemented
! 67: *
! 68: * [9] EntityValue ::= '"' ( [^%&"] | PEReference | Reference )* '"' | "'" ( [^%&'] ! PEReference | Reference )* "'"
! 69: * [9] status: not implemented
! 70: *
! 71: * [10] AttValue ::= '"' ( [^<&"] | Reference)* '"' | "'" ( [^<&'] | Reference )* "'"
! 72: * [10] status: not implemented
! 73: *
! 74: * [11] SystemLiteral ::= ( '"' [^"]* '"') | ("'" [^']* "'")
! 75: * [11] status: not implemented
! 76: *
! 77: * [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'") * "'"
! 78: * [12] status: not implemented
! 79: *
! 80: * [13] PubidChar ::= \x20 | \xD | \xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
! 81: * [13] status: not implemented
! 82: *
! 83: * [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
! 84: * [14] status: not implemented
! 85: *
! 86: * [15] Comments ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
! 87: * [15] status: not implemented
! 88: *
! 89: * [16] PI ::= '<?' PITarget (S (Char* - (Char* '?<' Char*)))? '?>'
! 90: * [16] status: not implemented
! 91: *
! 92: * [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') | ('L' | 'l'))
! 93: * [17] status: not implemented
! 94: *
! 95: * [18] CDsect ::= CDStart CData CDend
! 96: * [18] status: not implemented
! 97: *
! 98: * [19] CDStart ::= '<![CDATA['
! 99: * [19] status: not implemented
! 100: *
! 101: * [20] CData ::= (Char* - (Char* ']]>' Char*))
! 102: * [20] status: not implemented
! 103: *
! 104: * [21] CDEnd ::= ']]>'
! 105: * [21] status: not implemented
! 106: *
! 107: * [22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)?
! 108: * [22] status: partially
! 109: *
! 110: * [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
! 111: * [23] status: ok
! 112: *
! 113: * [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')
! 114: * [24] status: ok
! 115: *
! 116: * [25] Eq ::= S? '=' S?
! 117: * [25] status: ok
! 118: *
! 119: * [26] VersionNum ::= '1.0'
! 120: * [26] status: ok
! 121: *
! 122: * [27] Misc ::= Comment | PI | S
! 123: * [27] status: not implemented
! 124: *
! 125: * [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubsect ']' S?)? '>'
! 126: * [28] status: not implemented
! 127: *
! 128: * [28a] DeclSep ::= PEReference | S
! 129: * [28a] status: not implemented
! 130: *
! 131: * [28b] intSubset ::= (markupdecl | DeclSep)*
! 132: * [28b] status: not implemented
! 133: *
! 134: * [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment
! 135: * [29] status: not implemented
! 136: *
! 137: * [30] extSubset ::= TextDecl? extSubsetDecl
! 138: * [30] status: not implemented
! 139: *
! 140: * [31] extSubsetDecl ::= ( markupdecl | conditionalSect | DeclSep) *
! 141: * [31] status: not implemented
! 142: *
! 143: * [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"'" ('yes' | 'no') '"'))
! 144: * [32] status: ok
! 145: *
! 146: *
! 147: * ** productions 33 through 39 have been removed. It seems that this
! 148: * ** productions were supporting xml:lang stuff that is easily
! 149: * ** supported by using directily the xml standard rather than
! 150: * ** mention it as an special production inside the language.
! 151: *
! 152: * [39] element ::= EmptyElemTag | Stag content ETag
! 153: * [39] status: not implemented
! 154: *
! 155: * [40] Stag ::= '<' Name (S Attribute)* S? '>'
! 156: * [40] status: not implemented
! 157: *
! 158: * [41] Attribute ::= Name Eq AttValue
! 159: * [41] status: not implemented
! 160: *
! 161: * [42] ETag ::= '</' Name S? '>'
! 162: * [42] status: not implemented
! 163: *
! 164: * [43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)*
! 165: * [43] status: not implemented
! 166: *
! 167: * [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
! 168: * [44] status: not implemented
! 169: *
! 170: * [45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>'
! 171: * [45] status: not implemented
! 172: *
! 173: * [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children
! 174: * [46] status: not implemented
! 175: *
! 176: * [47] children ::= (choice | seq) ('?' | '*' | '+')?
! 177: * [47] status: not implemented
! 178: *
! 179: * [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')?
! 180: * [48] status: not implemented
! 181: *
! 182: * [49] choice ::= '(' S? cp ( S? '|' S? cp)+ S? ')'
! 183: * [49] status: not implemented
! 184: *
! 185: * [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')'
! 186: * [50] status: not implemented
! 187: *
! 188: * [51] Mixed ::= '(' '#PCDATA' (S? '|' S? Name)* S? ')*' | '(' S? '#PCDATA' S? ')'
! 189: * [51] status: not implemented
! 190: *
! 191: * [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
! 192: * [52] status: not implemented
! 193: *
! 194: * [53] AttDef ::= S Name S AttType S DefaultDecl
! 195: * [53] status: not implemented
! 196: *
! 197: * [54] AttType ::= Stringtype | TokenizedType | Enumeratedtype
! 198: * [54] status: not implemented
! 199: *
! 200: * [55] StringType ::= 'CDATA'
! 201: * [55] status: not implemented
! 202: *
! 203: * [56] tokenized ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' | 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS'
! 204: * [56] status: not implemented
! 205: *
! 206: * [57] EnumeratedType ::= NotationType | Enumeration
! 207: * [57] status: not implemented
! 208: *
! 209: * [58] NotationType ::= 'NOTATION' S '(' S? Name (S? Name (S? '|' S? Name)* S? ')'
! 210: * [58] status: not implemented
! 211: *
! 212: * [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
! 213: * [59] status: not implemented
! 214: *
! 215: * [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
! 216: * [60] status: not implemented
! 217: *
! 218: * [61] conditionalSect ::= includeSect | ignoreSect
! 219: * [61] status: not implemented
! 220: *
! 221: * [62] includeSect ::= '<![' S? 'INCLUDE S? '[' extSubsetDecl ']]>'
! 222: * [62] status: not implemented
! 223: *
! 224: * [63] ignoreSect ::= <![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>'
! 225: * [63] status: not implemented
! 226: *
! 227: * [64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore) *
! 228: * [64] status: not implemented
! 229: *
! 230: * [65] Ignore ::= Char * - (Char * ('<!' | ']]>') Char *)
! 231: * [65] status: not implemented
! 232: *
! 233: * [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-FA-F]+ ';'
! 234: * [66] status: not implemented
! 235: *
! 236: * [67] Reference ::= EntityRef | CharRef
! 237: * [67] status: not implemented
! 238: *
! 239: * [68] EntityRef ::= '&' Name ';'
! 240: * [68] status: not implemented
! 241: *
! 242: * [69] PEReference ::= '%' Name ';'
! 243: * [69] status: not implemented
! 244: *
! 245: * [70] EntityDecl ::= GEDecl | PEDecl
! 246: * [70] status: not implemented
! 247: *
! 248: * [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
! 249: * [71] status: not implemented
! 250: *
! 251: * [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
! 252: * [72] status: not implemented
! 253: *
! 254: * [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
! 255: * [73] status: not implemented
! 256: *
! 257: * [74] PEDef ::= EntityValue | ExternalID
! 258: * [74] status: not implemented
! 259: *
! 260: * [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral
! 261: * [75] status: not implemented
! 262: *
! 263: * [76] NDataDecl ::= S 'NData' S Name
! 264: * [76] status: not implemented
! 265: *
! 266: * [77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>'
! 267: * [77] status: not implemented
! 268: *
! 269: * [78] extParseEnt ::= TextDecl? content
! 270: * [78] status: not implemented
! 271: *
! 272: * [80] EncodingDecl ::= S 'encoding' Eq ( '"' EncName '"' | "'" EncName "'" )
! 273: * [80] status: ok
! 274: *
! 275: * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
! 276: * [81] status: ok
! 277: *
! 278: * [82] NotationalDecl ::= '<!NOTATION' S Name S (ExternalID | PublicID) S? '>'
! 279: * [82] status: not implemented
! 280: *
! 281: * [83] PublicID ::= 'PUBLIC' S PubidLiteral
! 282: * [83] status: not implemented
! 283: *
! 284: *
! 285: *
! 286: *
! 287: */
! 288:
! 289: /**
! 290: * \defgroup axl_doc_module Axl Doc: XML Documents related functions, loading XML documents and using them.
! 291: */
! 292:
! 293: /**
! 294: * \addtogroup axl_doc_module
! 295: * @{
! 296: */
! 297:
! 298: #include <axl.h>
! 299: #include <sys/stat.h>
! 300: #include <sys/types.h>
! 301: #include <fcntl.h>
! 302:
! 303:
! 304: #define LOG_DOMAIN "axl-doc"
! 305:
! 306: struct _axlDoc {
! 307: /**
! 308: * @internal
! 309: * @brief A reference to the very first axlNode this axlDoc
! 310: * has.
! 311: */
! 312: axlNode * rootNode;
! 313:
! 314: /**
! 315: * @internal
! 316: * The document version.
! 317: */
! 318: char * version;
! 319:
! 320: /**
! 321: * @internal
! 322: * @brief Current xml encoding document.
! 323: */
! 324: char * encoding;
! 325: /**
! 326: * @internal
! 327: * @brief Current entity encoding detected.
! 328: */
! 329: const char * detected_encoding;
! 330:
! 331: /**
! 332: * @internal If the document was found in a different encoding
! 333: * than utf-8, this variable will hold its associated value to
! 334: * allow returning to the original encoding.
! 335: */
! 336: char * encoding_found;
! 337:
! 338: /**
! 339: * @internal
! 340: * @brief Current standalone configuration of the given \ref
! 341: * axlDoc object.
! 342: */
! 343: axl_bool standalone;
! 344:
! 345: /**
! 346: * @internal
! 347: *
! 348: * @brief Parent node stack. This stack is used to control how
! 349: * are nested nodes while creating/parsing xml files. This
! 350: * nesting allows to not only properly contruct the xml but
! 351: * also to check if it is well balanced.
! 352: */
! 353: axlStack * parentNode;
! 354:
! 355: /**
! 356: * @internal Binary stack to hold the xml:space preserve
! 357: * status on each level (associated to the current node).
! 358: */
! 359: axlBinaryStack * xmlPreserve;
! 360:
! 361: /**
! 362: * @internal
! 363: *
! 364: * @brief Internal list to hold all PI targets readed.
! 365: */
! 366: axlList * piTargets;
! 367:
! 368: /**
! 369: * @internal
! 370: *
! 371: * @brief Instruct the \ref axlDoc instance to notify that the
! 372: * xml header have been defined. This helps to allow define PI
! 373: * instruction that are only found inside the root document,
! 374: * or after the xml header definition.
! 375: */
! 376: axl_bool headerProcess;
! 377:
! 378: /**
! 379: * @internal Factory to create items in a memory efficient
! 380: * manner.
! 381: */
! 382: axlFactory * item_factory;
! 383:
! 384: /**
! 385: * @internal Factory to create nodes in a memory efficient
! 386: * manner.
! 387: */
! 388: axlFactory * node_factory;
! 389:
! 390: /**
! 391: * @internal Factory to create nodes to hold content elements.
! 392: */
! 393: axlFactory * content_factory;
! 394:
! 395: /**
! 396: * @internal Factory to create nodes to hold attribute
! 397: * elements.
! 398: */
! 399: axlFactory * attr_factory;
! 400:
! 401: /**
! 402: * @internal Factory to alloc strings.
! 403: */
! 404: axlStrFactory * str_factory;
! 405: };
! 406:
! 407: struct _axlPI {
! 408: /**
! 409: * @internal
! 410: *
! 411: * @brief PI Target name.
! 412: */
! 413: char * name;
! 414: /**
! 415: * @internal
! 416: *
! 417: * @brief PI target content.
! 418: */
! 419: char * content;
! 420: };
! 421:
! 422: /* global references to handlers and user defined configuration */
! 423: axlDocDetectCodification detect_codification_func;
! 424: axlPointer detect_codification_data;
! 425:
! 426: axlDocConfigureCodification configure_codification_func;
! 427: axlPointer configure_codification_data;
! 428:
! 429: /**
! 430: * @internal
! 431: *
! 432: * @brief Creates a new empty \ref axlDoc reference.
! 433: *
! 434: * Creates the parent stack used for parsing functions.
! 435: *
! 436: * @return A newly allocated \ref axlDoc reference.
! 437: */
! 438: axlDoc * __axl_doc_new (axl_bool create_parent_stack)
! 439: {
! 440: axlDoc * result = axl_new (axlDoc, 1);
! 441:
! 442: /* default container lists */
! 443: result->parentNode = axl_stack_new (NULL);
! 444: result->piTargets = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_pi_free);
! 445: result->xmlPreserve = axl_binary_stack_new ();
! 446:
! 447: /* create factories */
! 448: result->item_factory = axl_item_factory_create ();
! 449: result->node_factory = axl_node_factory_create ();
! 450: result->content_factory = axl_item_content_factory_create ();
! 451: result->attr_factory = axl_item_attr_factory_create ();
! 452: result->str_factory = axl_string_factory_create ();
! 453: return result;
! 454: }
! 455:
! 456: /**
! 457: * @internal
! 458: *
! 459: * Clears internal axlDoc variables used mainly to parse documents.
! 460: *
! 461: * @param doc The \ref axlDoc to clear
! 462: */
! 463: void __axl_doc_clean (axlDoc * doc)
! 464: {
! 465: /* release memory used by the parser */
! 466: if (doc->parentNode != NULL) {
! 467: axl_stack_free (doc->parentNode);
! 468: doc->parentNode = NULL;
! 469: }
! 470:
! 471: return;
! 472: }
! 473:
! 474: /**
! 475: * @internal Function used by the axl doc module to allocate memory to
! 476: * be used by the axl stream. Currently this is used to alloc xml node
! 477: * names and xml attribute key and its value. The rest of items are
! 478: * allocated by the system memory allocation.
! 479: *
! 480: * @param size The size that is required by the axl stream to be allocated.
! 481: *
! 482: * @param doc The axlDoc reference, which contains a reference to the
! 483: * string factory used to allocate memory.
! 484: *
! 485: * @return A reference to the allocated memory.
! 486: */
! 487: char * __axl_doc_alloc (int size, axlDoc * doc)
! 488: {
! 489: /* just return a piece of memory */
! 490: return axl_string_factory_alloc (doc->str_factory, size);
! 491: }
! 492:
! 493: /**
! 494: * @internal Internal function that tries to check encoding found to
! 495: * configure the proper set of functions to translate from and to
! 496: * utf-8.
! 497: *
! 498: * @param doc The document being configured.
! 499: *
! 500: * @param error An optional error that will be filled in the case an
! 501: * error is found.
! 502: *
! 503: * @return axl_true if the operation was completed, otherwise axl_false is
! 504: * returned.
! 505: */
! 506: axl_bool axl_doc_configure_encoding (axlDoc * doc, axlStream * stream, axlError ** error)
! 507: {
! 508: char * encoding = NULL;
! 509: axl_bool result;
! 510:
! 511: /* normalize encoding found */
! 512: if (doc->encoding) {
! 513: /* copy encoding */
! 514: encoding = axl_strdup (doc->encoding);
! 515:
! 516: /* trim encoding */
! 517: axl_stream_trim (encoding);
! 518:
! 519: /* remove characters not required */
! 520: axl_stream_remove (encoding, "-", axl_false);
! 521: axl_stream_remove (encoding, "_", axl_false);
! 522:
! 523: /* make it lower case */
! 524: axl_stream_to_lower (encoding);
! 525:
! 526: } /* end if */
! 527:
! 528: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "configuring final document enconding, previously detected=%s, declared=%s",
! 529: doc->detected_encoding ? doc->detected_encoding : "none",
! 530: encoding ? encoding : "none");
! 531:
! 532: /* do not perform any configuration if nothing is defined */
! 533: if (! configure_codification_func) {
! 534: axl_free (encoding);
! 535: return axl_true;
! 536: }
! 537:
! 538: /* call to configure encoding */
! 539: result = configure_codification_func (stream, encoding, doc->detected_encoding, configure_codification_data, error);
! 540: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "result from configure encoding function=%d", result);
! 541:
! 542: if (result) {
! 543: /* encoding was fine, that means we are working in
! 544: * utf-8, udate document internals to move encoding to
! 545: * utf-8 */
! 546: doc->encoding_found = encoding;
! 547: encoding = NULL;
! 548:
! 549: /* reset encoding found to the new value */
! 550: if (doc->encoding)
! 551: axl_free (doc->encoding);
! 552: doc->encoding = axl_strdup ("utf-8");
! 553: }
! 554: axl_free (encoding);
! 555:
! 556: return result;
! 557: }
! 558:
! 559: /**
! 560: * @internal
! 561: *
! 562: * @brief Support for parsing the xml entity header
! 563: *
! 564: * @param stream The axlStream where is expected to receive the xml
! 565: * header
! 566: *
! 567: * @param doc The axlDoc where the header configuration will be
! 568: * placed.
! 569: *
! 570: * @param error An optional error that will be filled in the case an
! 571: * error is found.
! 572: *
! 573: * @return It is supposed that the function return \ref axl_true, an
! 574: * not deallocation is performed, and all elements were parsed
! 575: * properly. In the case \ref axl_false is returned, memory associated
! 576: * with the given stream will be released. If the document is
! 577: * associated, it will also be released.
! 578: */
! 579: axl_bool __axl_doc_parse_xml_header (axlStream * stream, axlDoc * doc, axlError ** error)
! 580: {
! 581: char * string_aux;
! 582: int size;
! 583:
! 584: /* check if the user is defining the header many times */
! 585: if (doc->headerProcess) {
! 586: axl_error_new (-1, "Found a new xml header expecification. Only one header is allowed for each xml document.", stream, error);
! 587: axl_stream_free (stream);
! 588: return axl_false;
! 589: }
! 590:
! 591: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "looking for an xml header declaration");
! 592:
! 593: /* check for initial XMLDec (production 23) */
! 594: if (axl_stream_inspect (stream, "<?", 2)) {
! 595:
! 596: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found xml declaration");
! 597:
! 598: /* check initial <?xml xml header */
! 599: if (! (axl_stream_inspect (stream, "xml", 3) > 0)) {
! 600: axl_error_new (-2, "expected initial <?xml declaration, not found.", stream, error);
! 601: axl_stream_free (stream);
! 602: return axl_false;
! 603: }
! 604:
! 605: /* consume spaces */
! 606: AXL_CONSUME_SPACES (stream);
! 607:
! 608: if (! axl_stream_inspect (stream, "version=", 8)) {
! 609: axl_error_new (-2, "expected to find 'version=' declaration, not found.", stream, error);
! 610: axl_stream_free (stream);
! 611: return axl_false;
! 612: }
! 613:
! 614: /* consume spaces */
! 615: AXL_CONSUME_SPACES (stream);
! 616:
! 617: /* check for " or ' */
! 618: if (! axl_stream_inspect_several (stream, 2, "\"1.0\"", 5, "'1.0'", 5)) {
! 619: axl_error_new (-2, "expected to find either \" or ' while procesing version number, not found.", stream, error);
! 620: axl_stream_free (stream);
! 621: return axl_false;
! 622: }
! 623:
! 624: /* check for an space */
! 625: AXL_CONSUME_SPACES(stream);
! 626:
! 627: /* now check for encoding */
! 628: if (axl_stream_inspect_several (stream, 2, "encoding=\"", 10, "encoding='", 10) > 0) {
! 629:
! 630: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found encoding declaration");
! 631:
! 632: /* found encoding instruction */
! 633: string_aux = axl_stream_get_until (stream, NULL, NULL, axl_true, 2, "'", "\"");
! 634: if (string_aux == NULL) {
! 635: axl_error_new (-2, "expected encoding value, not found.", stream, error);
! 636: axl_stream_free (stream);
! 637: return axl_false;
! 638: }
! 639:
! 640: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "encoding found=%s", string_aux);
! 641:
! 642: /* set document encoding: do not allocate
! 643: * twice the string returned, just nullify
! 644: * stream internal reference and use the same
! 645: * reference */
! 646: axl_stream_nullify (stream, LAST_CHUNK);
! 647: doc->encoding = string_aux;
! 648:
! 649: }
! 650:
! 651: /* check for an space */
! 652: AXL_CONSUME_SPACES(stream);
! 653:
! 654: /* get standalone configuration */
! 655: if ((axl_stream_inspect_several (stream, 2, "standalone=\"", 12, "standalone='", 12) > 0)) {
! 656:
! 657: /* found standalone instruction */
! 658: string_aux = axl_stream_get_until (stream, NULL, NULL, axl_true, 2, "'", "\"");
! 659: if (string_aux == NULL) {
! 660: axl_error_new (-2, "expected to receive standalone value, not found.", stream, error);
! 661: axl_stream_free (stream);
! 662: return axl_false;
! 663: }
! 664:
! 665: /* set standalone configuration */
! 666: if (memcmp ("yes", string_aux, 3))
! 667: doc->standalone = axl_false;
! 668: else
! 669: doc->standalone = axl_true;
! 670: }
! 671:
! 672: /* check for an space */
! 673: AXL_CONSUME_SPACES(stream);
! 674:
! 675: /* get the trailing header */
! 676: if (! (axl_stream_inspect (stream, "?>", 2) > 0)) {
! 677: axl_error_new (-2, "expected to receive the xml trailing header ?>, not found.", stream, error);
! 678: axl_stream_free (stream);
! 679: return axl_false;
! 680: }
! 681:
! 682: /* consume a possible comment */
! 683: if (! axl_doc_consume_comments (doc, stream, error))
! 684: return axl_false;
! 685: }
! 686:
! 687: /* configure encoding again, now we could have more data */
! 688: if (! axl_doc_configure_encoding (doc, stream, error)) {
! 689: axl_stream_free (stream);
! 690: return axl_false;
! 691: }
! 692:
! 693: /* now process the document type declaration */
! 694: if (axl_stream_inspect (stream, "<!DOCTYPE", 9) > 0) {
! 695: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found doc type declaration..");
! 696: /* found document type declaration, just skip it for
! 697: * now */
! 698: axl_stream_get_until_ref (stream, NULL, NULL, axl_true, &size, 1, ">");
! 699:
! 700: /* consume a possible comment */
! 701: if (! axl_doc_consume_comments (doc, stream, error))
! 702: return axl_false;
! 703: }
! 704:
! 705: /* return AXL_TRUE value */
! 706: return axl_true;
! 707: }
! 708:
! 709: /**
! 710: * @internal
! 711: *
! 712: * @brief Tries to parse the first (and compulsory) node that the xml
! 713: * document must have.
! 714: *
! 715: * The very minimal expresion of an xml document is the one defined by
! 716: * only one node, with no content and no attributes. This minimal xml
! 717: * could be defined as:
! 718: *
! 719: * \code
! 720: * <hello/>
! 721: * \endcode
! 722: *
! 723: * Or the other form accepted:
! 724: *
! 725: * \code
! 726: * <hello />
! 727: * \endcode
! 728: *
! 729: *
! 730: *
! 731: *
! 732: * @param stream The \ref axlStream object where is expected to find
! 733: * the xml node content.
! 734: *
! 735: * @param doc The \ref axlDoc object where node read will be placed
! 736: * inside.
! 737: *
! 738: * @param node The node that has been added due to calling to this
! 739: * function.
! 740: *
! 741: * @param error An optional error reporting variable to used to report
! 742: * upper level the error found.
! 743: *
! 744: * @return axl_true if the first node was successfully parsed or
! 745: * axl_false if not. If the function find something wrong the document
! 746: * is unrefered.
! 747: */
! 748: axl_bool __axl_doc_parse_node (axlStream * stream,
! 749: axlDoc * doc,
! 750: axlNode ** calling_node,
! 751: axl_bool * is_empty,
! 752: axlError ** error)
! 753: {
! 754: char * string_aux;
! 755: char * string_aux2;
! 756: axlNode * node;
! 757: int matched_chunk;
! 758: int length;
! 759: axl_bool delim;
! 760:
! 761: /* consume a possible comment */
! 762: if (! axl_doc_consume_comments (doc, stream, error))
! 763: return axl_false;
! 764:
! 765: /* check for initial < definition */
! 766: if (! (axl_stream_inspect (stream, "<", 1) > 0) && ! axl_stream_remains (stream)) {
! 767: /* check if we are reading the first node node */
! 768: if (doc->rootNode == NULL)
! 769: axl_error_new (-2, "expected initial < for a root node definition, not found. An xml document must have, at least, one node definition.",
! 770: stream, error);
! 771: else
! 772: axl_error_new (-2, "expected initial < for a node definition, not found.", stream, error);
! 773: axl_stream_free (stream);
! 774: return axl_false;
! 775: }
! 776:
! 777: /* get node name, keeping in mind the following:
! 778: * chunk_matched
! 779: * > : 0
! 780: * /> : 1
! 781: * " ": 2
! 782: *
! 783: * We also reconfigure the alloc method used by the axl stream
! 784: * to ensure that the module name is allocated through the
! 785: * string factory.
! 786: */
! 787: axl_stream_set_buffer_alloc (stream, (axlStreamAlloc)__axl_doc_alloc, doc);
! 788: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_true, 2, ">", " ");
! 789:
! 790: /* nullify */
! 791: axl_stream_nullify (stream, LAST_CHUNK);
! 792:
! 793: if (AXL_IS_STR_EMPTY (string_aux)) {
! 794: /* use alloc though string factory */
! 795: axl_stream_set_buffer_alloc (stream, NULL, NULL);
! 796:
! 797: axl_error_new (-2, "expected an non empty content for the node name not found.", stream, error);
! 798: axl_stream_free (stream);
! 799: return axl_false;
! 800: }
! 801:
! 802: /* if found a '/', it is matched as 1 */
! 803: if (matched_chunk == 1)
! 804: matched_chunk = 2;
! 805: else {
! 806: /* get the string length */
! 807: length = strlen (string_aux);
! 808:
! 809: /* if matched / it means that it was readed />, remove
! 810: * it and all white spaces */
! 811: if (string_aux[length - 1] == '/') {
! 812: /* flag as matched /> */
! 813: matched_chunk = 1;
! 814: string_aux[length - 1] = 0;
! 815: } /* end if */
! 816: } /* end if */
! 817:
! 818: /* create the node and associate it the node name found */
! 819: node = axl_node_factory_get (doc->node_factory);
! 820: axl_node_set_name_from_factory (node, string_aux);
! 821:
! 822: if (doc->rootNode == NULL) {
! 823: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting as first node found, the root node: <%s>", string_aux);
! 824:
! 825: doc->rootNode = node;
! 826:
! 827: /* set the node read, the root one, to be the parent */
! 828: axl_stack_push (doc->parentNode, node);
! 829:
! 830: /* configure the node */
! 831: axl_node_set_doc (node, doc);
! 832:
! 833: } else {
! 834: /* or set the node as a child of the current parent */
! 835: axl_doc_set_child_current_parent (doc, node);
! 836: }
! 837:
! 838: /* set the node created to the calling node, so the caller
! 839: * could get a reference */
! 840: if (calling_node != NULL)
! 841: *calling_node = node;
! 842:
! 843: /* only consume white spaces if matched_chunk is 2 */
! 844: if (matched_chunk == 2) {
! 845: /* get rid from spaces */
! 846: AXL_CONSUME_SPACES (stream);
! 847: }
! 848:
! 849: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "node found: [%s]", string_aux);
! 850:
! 851:
! 852: /* now, until the node ends, we have to find the node
! 853: * attributes or the node defintion end */
! 854: while (1) {
! 855: /* check if we have an attribute for the node, or the node
! 856: * definition have ended or the node definition is an empty
! 857: * one
! 858: *
! 859: * the following code that relies on matched_chunk is
! 860: * done due to previous call to get_until function. If
! 861: * the value 0 or 1 was matched, this means that we
! 862: * are on "/>" case */
! 863: if ((matched_chunk == 1) ||
! 864: axl_stream_inspect (stream, "/>", 2) > 0) {
! 865: /* use alloc though string factory */
! 866: axl_stream_set_buffer_alloc (stream, NULL, NULL);
! 867:
! 868: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found end xml node definition '/>'");
! 869:
! 870: /* empty node configuration found */
! 871: *is_empty = axl_true;
! 872: /* axl_node_set_is_empty (node, axl_true); */
! 873:
! 874: /* make this node to be completed and no child
! 875: * could be set. */
! 876: axl_stack_pop (doc->parentNode);
! 877:
! 878: /* set the parent node to receive all content
! 879: * found in the next parsing elements because
! 880: * the element found is totally empty */
! 881: *calling_node = axl_stack_peek (doc->parentNode);
! 882: return axl_true;
! 883: }
! 884:
! 885: /* check if we have an attribute for the node, or the node
! 886: * definition have ended or the node definition is an empty
! 887: * one
! 888: *
! 889: * the following code that relies on matched_chunk is
! 890: * done due to previous call to get_until function. If
! 891: * the value 2 or 3 was matched, this means that we
! 892: * are on ">" case */
! 893: if ((matched_chunk == 0) ||
! 894: (axl_stream_inspect (stream, ">", 1) > 0)) {
! 895: /* use alloc though string factory */
! 896: axl_stream_set_buffer_alloc (stream, NULL, NULL);
! 897:
! 898: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found [end] xml node definition '>', for node: [%s]",
! 899: axl_node_get_name (node));
! 900:
! 901: /* flag that the node is an empty definition */
! 902: *is_empty = axl_false;
! 903:
! 904: /* this node is ended */
! 905: return axl_true;
! 906: }
! 907:
! 908: /* get rid from spaces */
! 909: AXL_CONSUME_SPACES (stream);
! 910:
! 911: /* found attribute declaration, try to read it.
! 912: *
! 913: * We also reconfigure the alloc method used by the
! 914: * axl stream to ensure that xml node attributes are
! 915: * allocated through the string factory.
! 916: */
! 917: string_aux = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, "=");
! 918: if (string_aux != NULL) {
! 919: /* nullify internal reference to the stream:
! 920: * now we have inside string_aux the attribute
! 921: * name */
! 922: axl_stream_nullify (stream, LAST_CHUNK);
! 923:
! 924: /* check for empty values at the attribute definition */
! 925: if (string_aux [0] == 0) {
! 926: axl_error_new (-5, "Expected to find an attribute name (but found an empty value)", stream, error);
! 927: axl_stream_free (stream);
! 928: return axl_false;
! 929: } /* end if */
! 930:
! 931: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "attribute found: [%s]", string_aux);
! 932:
! 933: /* remove next " and ' if defined */
! 934: /* flag the we are looking for a " */
! 935: delim = axl_true;
! 936: if (! ((axl_stream_inspect (stream, "\"", 1) > 0))) {
! 937: /* seems it is not found, flag we are
! 938: * looking for ' */
! 939: delim = axl_false;
! 940: if (! (axl_stream_inspect (stream, "\'", 1) > 0)) {
! 941: /* use alloc though string factory */
! 942: axl_stream_set_buffer_alloc (stream, NULL, NULL);
! 943:
! 944: axl_error_new (-2, "Expected to find an attribute value initiator (\") or ('), every attribute value must start with them",
! 945: stream, error);
! 946: axl_stream_free (stream);
! 947: return axl_false;
! 948: }
! 949: }
! 950:
! 951:
! 952: /* now get the attribute value */
! 953: if (delim)
! 954: string_aux2 = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, "\"");
! 955: else
! 956: string_aux2 = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, "'");
! 957:
! 958: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "value found: [%s]", string_aux2);
! 959:
! 960: /* nullify internal reference so we have the
! 961: * only one reference to attribute value
! 962: * inside string_aux2 */
! 963: axl_stream_nullify (stream, LAST_CHUNK);
! 964:
! 965: if (axl_node_has_attribute (node, string_aux)) {
! 966: /* parse error */
! 967: axl_error_new (-3, "Unable to add attribute to node which already has this attribute. Duplicate attribute error.", stream, error);
! 968: axl_stream_free (stream);
! 969: return axl_false;
! 970: } /* end if */
! 971:
! 972: /* set a new attribute for the given node */
! 973: axl_node_set_attribute_from_factory (doc->attr_factory, node, string_aux, string_aux2);
! 974:
! 975: /* check xml:space configuration and update binary stack */
! 976: if (axl_cmp (string_aux, "xml:space")) {
! 977: if (axl_cmp (string_aux2, "preserve")) {
! 978: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found xml:space=preseve, notifying..");
! 979:
! 980: /* 1: xml:space=preserve (found)
! 981: * make current node and all its childs to preserve (by
! 982: * default) all white spaces found */
! 983: axl_binary_stack_push (doc->xmlPreserve, axl_true);
! 984:
! 985: } else if (axl_cmp (string_aux2, "default")) {
! 986: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found xml:space=default, notifying..");
! 987:
! 988: /* 2: xml:space=default (found)
! 989: * make current node and all its childs to not
! 990: * preserve white spaces (by default) */
! 991: axl_binary_stack_push (doc->xmlPreserve, axl_false);
! 992: } else {
! 993: /* parse error */
! 994: axl_error_new (-2, "xml:space attribute found with other value than 'preserve' or 'default', this is not allowed.", stream, error);
! 995: axl_stream_free (stream);
! 996: return axl_false;
! 997:
! 998: } /* end if */
! 999: } else {
! 1000: /* 3: xml:space (not found)
! 1001: * make the current node to inherint
! 1002: * default from parent */
! 1003: if (axl_binary_stack_is_empty (doc->xmlPreserve))
! 1004: axl_binary_stack_push (doc->xmlPreserve, axl_false);
! 1005: else
! 1006: axl_binary_stack_push_the_same (doc->xmlPreserve);
! 1007: } /* end if */
! 1008:
! 1009: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "attribute installed..");
! 1010:
! 1011: /* get rid from spaces */
! 1012: AXL_CONSUME_SPACES (stream);
! 1013: continue;
! 1014: }
! 1015:
! 1016: /* if reached this point, error found */
! 1017: axl_error_new (-2, "Parse error while reading a node being opened", stream, error);
! 1018: axl_stream_free (stream);
! 1019: return axl_false;
! 1020:
! 1021: } /* end while */
! 1022:
! 1023: /* node properly parsed */
! 1024: return axl_true;
! 1025: }
! 1026:
! 1027: /**
! 1028: * @internal
! 1029: * @brief Perform the close node operation.
! 1030: *
! 1031: */
! 1032: axl_bool __axl_doc_parse_close_node (axlStream * stream, axlDoc * doc, axlNode ** _node, axlError ** error)
! 1033: {
! 1034: char * string;
! 1035: int result_size = -1;
! 1036: axlNode * node;
! 1037:
! 1038: /* get the node being closed to check to the current parent */
! 1039: string = axl_stream_get_until_ref (stream, NULL, NULL, axl_true, &result_size, 1, ">");
! 1040: if (string == NULL) {
! 1041: axl_error_new (-1, "An error was found while closing the xml node", stream, error);
! 1042: axl_stream_free (stream);
! 1043: return axl_false;
! 1044: }
! 1045:
! 1046: /* check for optional white space inside the trailing result */
! 1047: if (axl_stream_is_white_space (string + result_size - 1)) {
! 1048: /* nullify to remove the optional white spaces */
! 1049: string [result_size - 1] = 0;
! 1050: } /* end if */
! 1051:
! 1052: /* get current parent node */
! 1053: node = axl_stack_peek (doc->parentNode);
! 1054: if (node == NULL) {
! 1055: axl_error_new (-1, "Found that the stack doesn't have any node opened, this means either an libaxl error or the xml being read is closing a node not opened",
! 1056: stream, error);
! 1057: axl_stream_free (stream);
! 1058: return axl_false;
! 1059: }
! 1060:
! 1061: /* check current axl node name against closed string */
! 1062: if (axl_cmp (axl_node_get_name (node), string)) {
! 1063:
! 1064: /* ok, axl node to be closed is the one expected */
! 1065: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "closing xml node, that matched with parent opened");
! 1066:
! 1067: return axl_true;
! 1068: }
! 1069:
! 1070: /* seems that the node being closed doesn't match */
! 1071: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "xml node names to be closed doesn't matched (%s != %s), current node stack status:",
! 1072: axl_node_get_name (node), string);
! 1073:
! 1074: node = axl_stack_pop (doc->parentNode);
! 1075: while (node != NULL) {
! 1076: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "<%s>", axl_node_get_name (node));
! 1077: node = axl_stack_pop (doc->parentNode);
! 1078: }
! 1079:
! 1080: axl_error_new (-1, "An error was found while closing the opened xml node, parent opened and xml node being closed doesn't match",
! 1081: stream, error);
! 1082: axl_stream_free (stream);
! 1083:
! 1084: return axl_false;
! 1085: }
! 1086:
! 1087: /**
! 1088: * @internal
! 1089: *
! 1090: * Internal function which works as a common base for all functions
! 1091: * that parse XML documents from different inputs.
! 1092: */
! 1093: axlDoc * __axl_doc_parse_common (const char * entity, int entity_size,
! 1094: const char * file_path, int fd_handle,
! 1095: axlError ** error)
! 1096: {
! 1097: axlStream * stream = NULL;
! 1098: axlDoc * doc = NULL;
! 1099: axlNode * node = NULL;
! 1100: char * string = NULL;
! 1101: int index;
! 1102: axl_bool is_empty = axl_false;
! 1103:
! 1104: /* create the xml stream using provided data */
! 1105: stream = axl_stream_new (entity, entity_size, file_path, fd_handle, error);
! 1106: axl_return_val_if_fail (stream, NULL);
! 1107:
! 1108: /* create a document reference */
! 1109: doc = __axl_doc_new (axl_true);
! 1110: axl_stream_link (stream, doc, (axlDestroyFunc) axl_doc_free);
! 1111:
! 1112: /* detect transitional entity codification to configure built
! 1113: * decoder (only if defined handler found) */
! 1114: if (detect_codification_func) {
! 1115: if (! detect_codification_func (stream, &doc->detected_encoding, detect_codification_data, error)) {
! 1116: axl_stream_free (stream);
! 1117: return NULL;
! 1118: }
! 1119: } /* end if */
! 1120:
! 1121: /* parse initial xml header */
! 1122: if (!__axl_doc_parse_xml_header (stream, doc, error))
! 1123: return NULL;
! 1124:
! 1125: /* signal that this document have processed its header */
! 1126: doc->headerProcess = axl_true;
! 1127:
! 1128: /* parse the rest of the document, setting as parent NULL
! 1129: * because still no parent is found. */
! 1130: if (!__axl_doc_parse_node (stream, doc, &node, &is_empty, error))
! 1131: return NULL;
! 1132:
! 1133: /* if the node returned is not empty */
! 1134: if (! is_empty) {
! 1135:
! 1136: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "the first node ready, have content, reading it");
! 1137:
! 1138: /* while the stream have data */
! 1139: while (axl_stream_remains (stream)) {
! 1140:
! 1141: /* get current index */
! 1142: index = axl_stream_get_index (stream);
! 1143:
! 1144: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "current index: %d (global: %d)", index,
! 1145: axl_stream_get_global_index (stream));
! 1146:
! 1147: /* get rid from spaces according to the
! 1148: * xml:space configuration */
! 1149: if (! axl_binary_stack_peek (doc->xmlPreserve)) {
! 1150: AXL_CONSUME_SPACES(stream);
! 1151: } /* end if */
! 1152:
! 1153: /* consume a possible comment and process instructions */
! 1154: if (axl_stream_peek (stream, "<?", 2) > 0 || axl_stream_peek (stream, "<!--", 4) > 0) {
! 1155: if (! axl_doc_consume_comments (doc, stream, error))
! 1156: return NULL;
! 1157:
! 1158: /* continue on the next index */
! 1159: continue;
! 1160: } /* end if */
! 1161:
! 1162: if ((axl_stream_peek (stream, "</", 2) > 0)) {
! 1163: /* accept previous peek */
! 1164: axl_stream_accept (stream);
! 1165:
! 1166: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a node termination signal");
! 1167:
! 1168: /* seems that a node is being closed */
! 1169: if (! __axl_doc_parse_close_node (stream, doc, &node, error))
! 1170: return NULL;
! 1171:
! 1172: /* because the xml node have been
! 1173: * closed, make the parent to be the
! 1174: * previous one */
! 1175: axl_stack_pop (doc->parentNode);
! 1176:
! 1177: /* get the new parent */
! 1178: node = axl_stack_peek (doc->parentNode);
! 1179:
! 1180: /* restore previous xml:space value */
! 1181: axl_binary_stack_pop (doc->xmlPreserve);
! 1182:
! 1183: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "node properly closed, current parent node stack size: %d, parent=<%s>",
! 1184: axl_stack_size (doc->parentNode), (node != NULL) ? axl_node_get_name (node) : "--no parent--");
! 1185:
! 1186: if (axl_stack_size (doc->parentNode) > 0)
! 1187: continue;
! 1188: break;
! 1189: } /* end if */
! 1190:
! 1191: /* check here for CDATA section. This is done
! 1192: * here because the following checking could
! 1193: * be mixed because they starts with the same:
! 1194: * < */
! 1195: if ((axl_stream_peek (stream, "<![CDATA[", 9) > 0)) {
! 1196: /* accet previous peek */
! 1197: axl_stream_accept (stream);
! 1198:
! 1199: /* found CDATA section, get current content */
! 1200: axl_stream_set_buffer_alloc (stream, (axlStreamAlloc)__axl_doc_alloc, doc);
! 1201: string = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, "]]>");
! 1202: axl_stream_set_buffer_alloc (stream, NULL, NULL);
! 1203: if (string == NULL) {
! 1204: axl_error_new (-1, "Unable to get CDATA content. There was an error.", stream, error);
! 1205: axl_stream_free (stream);
! 1206: return NULL;
! 1207: }
! 1208:
! 1209: /* nullify internal reference to the
! 1210: * string_aux so we can use that
! 1211: * memory allocated as our
! 1212: * reference */
! 1213: axl_stream_nullify (stream, LAST_CHUNK);
! 1214:
! 1215: /* set current data */
! 1216: /* axl_node_set_content_ref (node, string, -1); */
! 1217: axl_node_set_cdata_content_from_factory (doc->content_factory, node, string, -1);
! 1218: continue;
! 1219: } /* end if */
! 1220:
! 1221:
! 1222: if ((axl_stream_peek (stream, "<", 1) > 0)) {
! 1223: /* accept previous peek */
! 1224: axl_stream_accept (stream);
! 1225:
! 1226: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a new node being opened");
! 1227:
! 1228: /* seems that another node is being opened */
! 1229: if (!__axl_doc_parse_node (stream, doc, &node, &is_empty, error))
! 1230: return NULL;
! 1231:
! 1232: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "finished parsing opened node, current parent=<%s>",
! 1233: axl_node_get_name (node));
! 1234:
! 1235: continue;
! 1236: }
! 1237:
! 1238: /* restore index position previous to the axl
! 1239: * space consuming */
! 1240: if (axl_stream_get_index (stream) > index) {
! 1241: axl_stream_move (stream, index);
! 1242: }
! 1243:
! 1244: /* found node content */
! 1245: axl_stream_set_buffer_alloc (stream, (axlStreamAlloc)__axl_doc_alloc, doc);
! 1246: string = axl_stream_get_until (stream, NULL, NULL, axl_false, 1, "<");
! 1247: axl_stream_set_buffer_alloc (stream, NULL, NULL);
! 1248:
! 1249: /* check for a null content found */
! 1250: if (string == NULL) {
! 1251: axl_error_new (-1, "an error was found while reading the xml node content", stream, error);
! 1252: axl_stream_free (stream);
! 1253: return NULL;
! 1254: }
! 1255:
! 1256: /* nullify internal stream reference to have
! 1257: * the unique reference */
! 1258: axl_stream_nullify (stream, LAST_CHUNK);
! 1259:
! 1260: /* set current data */
! 1261: /* axl_node_set_content_ref (node, string, -1); */
! 1262: axl_node_set_content_from_factory (doc->content_factory, node, string, -1);
! 1263:
! 1264: /* keep on looping */
! 1265: }
! 1266: }
! 1267:
! 1268: /* pop axl parent */
! 1269: if (! axl_stack_is_empty (doc->parentNode)) {
! 1270:
! 1271: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL,
! 1272: "current parent stack size shows that not all opened nodes were closed. This means that the XML document is not properly balanced (stack size: %d)",
! 1273: axl_stack_size (doc->parentNode));
! 1274:
! 1275: /* notify error */
! 1276: axl_error_new (-1, "XML document is not balanced, still remains xml nodes", stream, error);
! 1277: axl_stream_free (stream);
! 1278:
! 1279: return NULL;
! 1280: }
! 1281:
! 1282: /* parse complete */
! 1283: axl_stream_unlink (stream);
! 1284: axl_stream_free (stream);
! 1285:
! 1286: /* clean document internal variables */
! 1287: __axl_doc_clean (doc);
! 1288:
! 1289: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "xml document parse COMPLETED");
! 1290:
! 1291: return doc;
! 1292: }
! 1293:
! 1294: /**
! 1295: * @brief Creates a new empty xml document, especifying options to be
! 1296: * used in the header.
! 1297: *
! 1298: * This function allows to create the xml document representation the
! 1299: * must be used to add childs to it.
! 1300: *
! 1301: * The following is a simple example that creates a xml document (\ref
! 1302: * axlDoc) with a single root node (\ref axlNode):
! 1303: * \code
! 1304: * // some variables
! 1305: * axlDoc * doc;
! 1306: * axlNode * node;
! 1307: *
! 1308: * // dump content variables
! 1309: * char * content;
! 1310: * int content_size;
! 1311: *
! 1312: * // create the document
! 1313: * doc = axl_doc_create (NULL, NULL, axl_true);
! 1314: *
! 1315: * // create the root document node
! 1316: * node = axl_node_create ("root-node");
! 1317: *
! 1318: * // configure it as the root
! 1319: * axl_doc_set_root (doc, node);
! 1320: *
! 1321: * // dump pretty
! 1322: * axl_doc_dump_pretty (doc, &content, &content_size, 4);
! 1323: *
! 1324: * // print the content and free
! 1325: * printf ("document size=%d, content: %s\n", content_size, content);
! 1326: * axl_free (content);
! 1327: * \endcode
! 1328: *
! 1329: * @param version The xml document version. This value is optional. If
! 1330: * NULL is used, the library will use "1.0" as version value.
! 1331: *
! 1332: * @param encoding The document encoding to be used. This value is
! 1333: * optional, if NULL is provided, no encoding specification will be
! 1334: * used.
! 1335: *
! 1336: * @param standalone Standalone configuration flag. By default, use
! 1337: * axl_false.
! 1338: *
! 1339: * @return Returns a newly allocated \ref axlDoc instance that must be
! 1340: * deallocated by using \ref axl_doc_free.
! 1341: */
! 1342: axlDoc * axl_doc_create (const char * version,
! 1343: const char * encoding,
! 1344: axl_bool standalone)
! 1345: {
! 1346: axlDoc * doc;
! 1347:
! 1348: /* create a new reference, without creating */
! 1349: doc = __axl_doc_new (axl_false);
! 1350:
! 1351: /* save the version */
! 1352: if (version != NULL)
! 1353: doc->version = axl_strdup (version);
! 1354:
! 1355: /* save encoding value */
! 1356: if (encoding != NULL)
! 1357: doc->encoding = axl_strdup (encoding);
! 1358:
! 1359: /* save standalone configuration */
! 1360: doc->standalone = standalone;
! 1361:
! 1362: /* return the reference created */
! 1363: return doc;
! 1364: }
! 1365:
! 1366: /**
! 1367: * @internal Returns how many bytes will hold the document provided.
! 1368: *
! 1369: * @param doc The document to measure.
! 1370: *
! 1371: * @param pretty_print If pretty print is activated.
! 1372: *
! 1373: * @return The number of bytes or -1 if it fails.
! 1374: */
! 1375: int __axl_doc_get_flat_size_common (axlDoc * doc, axl_bool pretty_print, int tabular)
! 1376: {
! 1377:
! 1378: int result;
! 1379: axl_return_val_if_fail (doc, -1);
! 1380:
! 1381: /* count the xml header:
! 1382: *
! 1383: * "<?xml version='1.0'" = 19 characters
! 1384: * " standalone='yes'" = 17 characters
! 1385: * " encoding='enc'" = 12 characters + strlen (enc)
! 1386: * " ?>" = 3 characters
! 1387: *
! 1388: * if pretty print add: "\r\n" +2 on windows
! 1389: * and \n on unix.
! 1390: */
! 1391: result = 22;
! 1392:
! 1393: if (pretty_print)
! 1394: #ifdef __AXL_OS_WIN32__
! 1395: result += 2;
! 1396: #else
! 1397: result += 1;
! 1398: #endif
! 1399:
! 1400: if (doc->standalone)
! 1401: result += 17;
! 1402:
! 1403: if (doc->encoding != NULL) {
! 1404: result += 12 + strlen (doc->encoding);
! 1405: }
! 1406:
! 1407: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "xml document header size=%d",
! 1408: result);
! 1409:
! 1410: /* now, count every node that the document have */
! 1411: result += axl_node_get_flat_size (doc->rootNode, pretty_print, 0, tabular);
! 1412:
! 1413: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "xml document body size=%d",
! 1414: result);
! 1415:
! 1416: /* return current result */
! 1417: return result;
! 1418: }
! 1419:
! 1420: /**
! 1421: * @internal
! 1422: * Common implementation for the dumping functions.
! 1423: */
! 1424: axl_bool __axl_doc_dump_common (axlDoc * doc, char ** content, int * size, axl_bool pretty_print, int tabular, axlError ** err)
! 1425: {
! 1426:
! 1427: char * result;
! 1428: int index;
! 1429:
! 1430: /* nullify before returning */
! 1431: if (content)
! 1432: *content = NULL;
! 1433: if (size)
! 1434: *size = 0;
! 1435:
! 1436: /* perform some envrironmental checks */
! 1437: if (doc == NULL) {
! 1438: axl_error_report (err, -1, "Received null doc reference to perform dump operation.");
! 1439: return axl_false;
! 1440: } else if (content == NULL) {
! 1441: axl_error_report (err, -2, "Received null content reference to perform dump operation. To dump the content it is required a valid memory reference to place the content.");
! 1442: return axl_false;
! 1443: } else if (size == NULL) {
! 1444: axl_error_report (err, -3, "Received null size reference to perform dump operation. To dump the content it is required a valid memory reference to report size");
! 1445: return axl_false;
! 1446: }
! 1447:
! 1448: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting document size..");
! 1449:
! 1450: /* get the about of memory to allocate so the whole xml
! 1451: * document fit in only one memory block */
! 1452: (* size) = __axl_doc_get_flat_size_common (doc, pretty_print, tabular);
! 1453: (* content) = NULL;
! 1454:
! 1455: /* check returned size */
! 1456: if ((* size) == -1) {
! 1457: axl_error_report (err, -4, "Failed to perform dump operation, unable to calculate document size to perform dump.");
! 1458: return axl_false;
! 1459: }
! 1460:
! 1461: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "document dump size: %d", *size);
! 1462:
! 1463: /* allocate the memory block required */
! 1464: result = axl_new (char, (*size) + 1);
! 1465:
! 1466: /* xml document header */
! 1467: index = 0;
! 1468: memcpy (result, "<?xml version='1.0' ", 20);
! 1469: index = 20;
! 1470:
! 1471: /* encoding declaration */
! 1472: if (doc->encoding) {
! 1473: /* initial encoding declaration */
! 1474: memcpy (result + index, "encoding='", 10);
! 1475: index += 10;
! 1476:
! 1477: /* copy encoding content */
! 1478: memcpy (result + index, doc->encoding, strlen (doc->encoding));
! 1479: index += strlen (doc->encoding);
! 1480:
! 1481: /* encoding trailing */
! 1482: memcpy (result + index, "' ", 2);
! 1483: index += 2;
! 1484: }
! 1485:
! 1486: /* standalone attribute */
! 1487: if (doc->standalone) {
! 1488: memcpy (result + index, "standalone='yes' ", 17);
! 1489: index += 17;
! 1490: }
! 1491:
! 1492: /* header trailing */
! 1493: memcpy (result + index, "?>", 2);
! 1494: index += 2;
! 1495:
! 1496: if (pretty_print) {
! 1497: #ifdef __AXL_OS_WIN32__
! 1498: memcpy (result + index, "\r\n", 2);
! 1499: index += 2;
! 1500: #else
! 1501: memcpy (result + index, "\n", 1);
! 1502: index += 1;
! 1503: #endif
! 1504: }
! 1505:
! 1506: /* dump node information */
! 1507: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "starting dump at: %d", index);
! 1508: index = axl_node_dump_at (doc->rootNode, result, index, pretty_print, 0, tabular);
! 1509:
! 1510: /* check dump size */
! 1511: if (*size != index) {
! 1512: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "internal dump error, inconsitent size: calculated=%d != returned=%d",
! 1513: *size, index);
! 1514:
! 1515: axl_error_report (err, -5, "Internal dump error, inconsistent size: calculated=%d != returned=%d",
! 1516: *size, index);
! 1517:
! 1518: /* free allocated result */
! 1519: axl_free (result);
! 1520:
! 1521: *size = -1;
! 1522: *content = NULL;
! 1523:
! 1524: return axl_false;
! 1525: }
! 1526:
! 1527: /* set results */
! 1528: *content = result;
! 1529: *size = index;
! 1530:
! 1531: return axl_true;
! 1532: }
! 1533: /**
! 1534: * @brief Allows to get the xml representation for the provided \ref
! 1535: * axlDoc reference.
! 1536: *
! 1537: * Given the \ref axlDoc reference, which represents a XML document,
! 1538: * this function allows to get its stringify representation.
! 1539: *
! 1540: * @param doc The \ref axlDoc to stringify
! 1541: *
! 1542: * @param content The reference where the result will be returned.
! 1543: *
! 1544: * @param size The reference where the document content size will be
! 1545: * returned.
! 1546: *
! 1547: * @return The function returns \ref axl_true if the dump operation was
! 1548: * performed. Otherwise \ref axl_false is returned.
! 1549: */
! 1550: axl_bool axl_doc_dump (axlDoc * doc,
! 1551: char ** content,
! 1552: int * size)
! 1553: {
! 1554: /* use common implementation */
! 1555: return __axl_doc_dump_common (doc, content, size, axl_false, 0, NULL);
! 1556: }
! 1557:
! 1558:
! 1559: /**
! 1560: * @brief Allows to perform a dump operation like \ref axl_doc_dump,
! 1561: * but making the output to be pretty printed.
! 1562: *
! 1563: * @param doc The \ref axlDoc reference to be dumped.
! 1564: *
! 1565: * @param content The reference that will hold the dumped information.
! 1566: *
! 1567: * @param size Result size for the dumped information.
! 1568: *
! 1569: * @param tabular The tabular size basic unit used for level
! 1570: * tabulation. An appropiate value could be 4.
! 1571: *
! 1572: * @return \ref axl_true if the document was dumped, \ref axl_false if
! 1573: * something has failed.
! 1574: */
! 1575: axl_bool axl_doc_dump_pretty (axlDoc * doc,
! 1576: char ** content,
! 1577: int * size,
! 1578: int tabular)
! 1579: {
! 1580: /* use common implementation */
! 1581: return __axl_doc_dump_common (doc, content, size, axl_true, tabular, NULL);
! 1582: }
! 1583:
! 1584: /**
! 1585: * @brief Allows to dump a xml document directly to the file located
! 1586: * at the file path.
! 1587: *
! 1588: * This function saves you the round trip to declare variables to hold
! 1589: * the memory, open a file, dump the content and properly close the
! 1590: * output file. The function works the same as \ref axl_doc_dump but
! 1591: * doing the extra job to transfer the xml document into a file.
! 1592: *
! 1593: * See also \ref axl_doc_dump_pretty_to_file to get a version dumps
! 1594: * the content doing some pretty printing operations.
! 1595: *
! 1596: * @param doc The document to be dumped into a file.
! 1597: *
! 1598: * @param file_path The file path where the output will be placed. The
! 1599: * function will require to have access rights to the file (or to
! 1600: * create a new file if it doesnt exists). The default behaviour is to
! 1601: * overwrite the file found if exists. So, if you don't want to get
! 1602: * content overwrited, you must provide the enough code to avoid such
! 1603: * situations prior calling to this function.
! 1604: *
! 1605: * @return \ref axl_true if the dump operation was ok, otherwisde \ref
! 1606: * axl_false is returned.
! 1607: */
! 1608: axl_bool axl_doc_dump_to_file (axlDoc * doc,
! 1609: const char * file_path)
! 1610: {
! 1611: char * content = NULL;
! 1612: int size = -1;
! 1613: int written = -1;
! 1614: FILE * fd = NULL;
! 1615:
! 1616: /* dump content and check result */
! 1617: if (! __axl_doc_dump_common (doc, &content, &size, axl_false, 0, NULL)) {
! 1618: /* no dump operation done */
! 1619: return axl_false;
! 1620: }
! 1621:
! 1622: /* open the file and check */
! 1623: #if defined(AXL_OS_WIN32) && ! defined(__GNUC__)
! 1624: if (fopen_s (&fd, file_path, "w") != 0) {
! 1625: #else
! 1626: if ((fd = fopen (file_path, "w")) == NULL) {
! 1627: #endif
! 1628: /* failed to open the file to dump the content */
! 1629: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "failed to dump document due to an error on fopen() call.");
! 1630: axl_free (content);
! 1631:
! 1632: return axl_false;
! 1633: }
! 1634:
! 1635: /* dump the content */
! 1636: written = fwrite (content, 1, size, fd);
! 1637:
! 1638: /* free the content */
! 1639: axl_free (content);
! 1640:
! 1641: /* close file */
! 1642: fclose (fd);
! 1643:
! 1644: /* return if we have failed to dump all the content to the
! 1645: * file or not. */
! 1646: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "returning that the dump was: %s (written:%d == size:%d)",
! 1647: (written == size) ? "OK" : "FAILED",
! 1648: written, size);
! 1649:
! 1650: return (written == size);
! 1651: }
! 1652:
! 1653: /**
! 1654: * @brief Allows to dump a xml document directly to the file located
! 1655: * at the file path, doing pretty printing operations.
! 1656: *
! 1657: * This function saves you the round trip to declare variables to hold
! 1658: * the memory, open a file, dump the content and properly close the
! 1659: * output file. The function works the same as \ref axl_doc_dump but
! 1660: * doing the extra job to transfer the xml document into a file.
! 1661: *
! 1662: * See also \ref axl_doc_dump_to_file to get a version dumps the
! 1663: * content without doing pretty printing operations.
! 1664: *
! 1665: * @param doc The document to be dumped into a file.
! 1666: *
! 1667: * @param file_path The file path where the output will be placed. The
! 1668: * function will require to have access rights to the file (or to
! 1669: * create a new file if it doesnt exists). The default behaviour is to
! 1670: * overwrite the file found if exists. So, if you don't want to get
! 1671: * content overwrited, you must provide the enough code to avoid such
! 1672: * situations prior calling to this function.
! 1673: *
! 1674: * @param tabular The amount of white spaces to introduce as tabular
! 1675: * for each level found inside the xml.
! 1676: *
! 1677: * @return \ref axl_true if the dump operation was ok, otherwisde \ref
! 1678: * axl_false is returned.
! 1679: */
! 1680: axl_bool axl_doc_dump_pretty_to_file (axlDoc * doc,
! 1681: const char * file_path,
! 1682: int tabular)
! 1683: {
! 1684: char * content = NULL;
! 1685: int size = -1;
! 1686: int written = -1;
! 1687: FILE * fd = NULL;
! 1688: axlError * err = NULL;
! 1689:
! 1690: /* dump content and check result */
! 1691: if (! __axl_doc_dump_common (doc, &content, &size, axl_true, tabular, &err)) {
! 1692: /* no dump operation done */
! 1693: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "failed to perform dump operation. Internal error found: %s",
! 1694: axl_error_get (err));
! 1695: axl_error_free (err);
! 1696: return axl_false;
! 1697: } /* end if */
! 1698:
! 1699: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "document dumped, now transfer that content to a file");
! 1700:
! 1701: /* open the file and check */
! 1702: #if defined(AXL_OS_WIN32) && ! defined(__GNUC__)
! 1703: if (fopen_s (&fd, file_path, "w") != 0) {
! 1704: #else
! 1705: if ((fd = fopen (file_path, "w")) == NULL) {
! 1706: #endif
! 1707: /* failed to open the file to dump the content */
! 1708: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "failed to dump document due to an error on fopen() call.");
! 1709: axl_free (content);
! 1710:
! 1711: return axl_false;
! 1712: }
! 1713:
! 1714: /* dump the content */
! 1715: written = fwrite (content, 1, size, fd);
! 1716:
! 1717: /* free the content */
! 1718: axl_free (content);
! 1719:
! 1720: /* close file */
! 1721: fclose (fd);
! 1722:
! 1723: /* return if we have failed to dump all the content to the
! 1724: * file or not. */
! 1725: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "returning that the dump was: %s (written:%d == size:%d)",
! 1726: (written == size) ? "OK" : "FAILED",
! 1727: written, size);
! 1728: return (written == size);
! 1729: }
! 1730:
! 1731: /**
! 1732: * @brief Allows to get how much will take the \ref axlDoc instance
! 1733: * represented as an XML document in an storage device (like memory).
! 1734: *
! 1735: * @param doc The \ref axlDoc reference that is being requested to return its size.
! 1736: *
! 1737: * @return The size the \ref axlDoc will represent, in a
! 1738: * octect-counting, or -1 if fails. The function will only fail if the
! 1739: * provided reference is NULL.
! 1740: */
! 1741: int axl_doc_get_flat_size (axlDoc * doc)
! 1742: {
! 1743: /* use common implementation */
! 1744: return __axl_doc_get_flat_size_common (doc, axl_false, 0);
! 1745: }
! 1746:
! 1747:
! 1748: /**
! 1749: * @brief Parse an XML entity that is hold inside the memory pointed
! 1750: * by <b>entity</b> and limited by <b>entity_size</b>.
! 1751: *
! 1752: * The function parses the XML document inside the memory hold inside
! 1753: * the given reference. The function returns an XML document,
! 1754: * represented by \ref axlDoc.
! 1755: *
! 1756: * The function, optionall, could report error found inside the given
! 1757: * \ref axlError variable. In the case the function returns a NULL
! 1758: * value, this variable is filled containing the a textual diagnostic
! 1759: * error to be showed to the user interface and an error code.
! 1760: *
! 1761: * Here is an example:
! 1762: * \code
! 1763: * // axl document representation
! 1764: * axlDoc * doc;
! 1765: * axlError * error;
! 1766: *
! 1767: *
! 1768: * // parse the given string
! 1769: * doc = axl_doc_parse ("<?xml version='1.0' ?><axldoc />", 32, &error);
! 1770: * if (doc == NULL) {
! 1771: * printf ("Error found: %s\n", axl_error_get (error));
! 1772: * axl_error_free (error);
! 1773: * return axl_false;
! 1774: * }
! 1775: *
! 1776: * // release document parsed
! 1777: * axl_doc_free (doc);
! 1778: * \endcode
! 1779: *
! 1780: * @param entity The XML document to load.
! 1781: *
! 1782: * @param entity_size The XML document size to load. If a <b>-1</b> is
! 1783: * provided, strlen function is used to figure out current document
! 1784: * size. This is not recomended while using xml documents that include
! 1785: * binary data, that maybe comes inside the CDATA section or because
! 1786: * an utf caracter used that includes the \\0 inside its value.
! 1787: *
! 1788: * @param error Optional \ref axlError reference that will be used to
! 1789: * report errors found while processing xml into the \ref axlDoc
! 1790: * instance.
! 1791: *
! 1792: * @return A newly allocated Axl Document, that must be deallocated
! 1793: * using \ref axl_doc_free, when no longer needed. The function could
! 1794: * return NULL if the document is not loaded properly.
! 1795: *
! 1796: * In the case an error is found while procesing the document, error
! 1797: * variable will be filled, if defined. -1 will be returned is
! 1798: * received parameter are wrong. -2 will be returned if there some
! 1799: * error is found while processing the document.
! 1800: */
! 1801: axlDoc * axl_doc_parse (const char * entity, int entity_size, axlError ** error)
! 1802: {
! 1803: return __axl_doc_parse_common (entity, entity_size, NULL, -1, error);
! 1804: }
! 1805:
! 1806: /**
! 1807: * @internal
! 1808: *
! 1809: * Allows to get current file size, in bytes, of the provided file
! 1810: * located at the given file path.
! 1811: */
! 1812: int __axl_doc_get_file_size (char * file_path)
! 1813: {
! 1814: struct stat buf;
! 1815:
! 1816: axl_return_val_if_fail (file_path, -1);
! 1817:
! 1818: /* clear the memory hold */
! 1819: memset (&buf, 0, sizeof (struct stat));
! 1820:
! 1821: /* return current file size */
! 1822: if (stat ((const char *) file_path, &buf) < 0)
! 1823: return -1;
! 1824:
! 1825: /* return the file size */
! 1826: return buf.st_size;
! 1827:
! 1828: }
! 1829:
! 1830: /**
! 1831: * @brief Allows to parse an xml document from the given file path
! 1832: * location.
! 1833: *
! 1834: * This function works the same way like \ref axl_doc_parse and \ref
! 1835: * axl_doc_parse_strings, but using as an input, the selected file
! 1836: * provided by the path. In fact, all this function, use the same xml
! 1837: * parse engine. The advantage of this function is that it is more
! 1838: * efficient while reading huge xml files.
! 1839: *
! 1840: * Here is an example:
! 1841: * \code
! 1842: * axlDoc * doc = NULL;
! 1843: * axlError * error = NULL;
! 1844: *
! 1845: * // parse the provide file
! 1846: * doc = axl_doc_parse_from_file ("test.xml", &error);
! 1847: * if (doc == NULL) {
! 1848: * // check error found
! 1849: * printf ("ERROR: (code: %d) %s\n",
! 1850: * axl_error_get_code (error),
! 1851: * axl_error_get (error));
! 1852: * axl_error_free (error);
! 1853: * return -1;
! 1854: * }
! 1855: *
! 1856: * // do some stuff with the readed document
! 1857: *
! 1858: * // release it once no longer needed
! 1859: * axl_doc_free (doc);
! 1860: * \endcode
! 1861: *
! 1862: * @param file_path The file path to report.
! 1863: *
! 1864: * @param error The \ref axlError where errors found will be reported.
! 1865: *
! 1866: * @return
! 1867: */
! 1868: axlDoc * axl_doc_parse_from_file (const char * file_path,
! 1869: axlError ** error)
! 1870: {
! 1871: return __axl_doc_parse_common (NULL, -1, file_path, -1, error);
! 1872: }
! 1873:
! 1874:
! 1875: /**
! 1876: * @brief Allows to parse an xml document that is provided as a set of
! 1877: * strings ended by a NULL reference.
! 1878: *
! 1879: * This function works the same way like \ref axl_doc_parse function,
! 1880: * but allowing to provide a set of strings. Here is an example:
! 1881: *
! 1882: * \code
! 1883: * // a document reference
! 1884: * axlDoc * doc;
! 1885: *
! 1886: * // note that the error is optional, and, if provided, it is not
! 1887: * // required to initialize it.
! 1888: * axlError * error;
! 1889: *
! 1890: * // parse the following set of strings
! 1891: * doc = axl_doc_parse_strings (&error,
! 1892: * "<?xml version='1.0' standalone='yes' ?>",
! 1893: * "<complex>",
! 1894: * " <data>",
! 1895: * " <row>",
! 1896: * " <td>",
! 1897: * " <value attr='10'/>
! 1898: * " </td>",
! 1899: * " </row>",
! 1900: * " </data>",
! 1901: * "</complex>",
! 1902: * NULL); // last null reference
! 1903: * // check for an error
! 1904: * if (doc == NULL) {
! 1905: * printf ("There was an error while parsing the document: (code: %d) %s\n",
! 1906: * axl_error_get_code (error), axl_error_get (error));
! 1907: * axl_error_free (error);
! 1908: * }
! 1909: * \endcode
! 1910: *
! 1911: * @param error An optional \ref axlError reference where a textual
! 1912: * diagnostic will be provided.
! 1913: *
! 1914: * @return A newly created \ref axlDoc reference that must be
! 1915: * deallocated by using \ref axl_doc_free when no longer needed.
! 1916: */
! 1917: axlDoc * axl_doc_parse_strings (axlError ** error,
! 1918: ...)
! 1919: {
! 1920: axlDoc * doc;
! 1921: va_list args;
! 1922: char * string = NULL;
! 1923: char * stream = NULL;
! 1924: char * stream_aux = NULL;
! 1925:
! 1926: /* check incoming data */
! 1927: axl_return_val_if_fail (error, NULL);
! 1928:
! 1929: /* open the stdargs */
! 1930: va_start (args, error);
! 1931:
! 1932: while ((string = va_arg (args, char *)) != NULL) {
! 1933: stream_aux = stream;
! 1934: stream = axl_stream_concat (stream, string);
! 1935: if (stream_aux != NULL) {
! 1936: axl_free (stream_aux);
! 1937: stream_aux = NULL;
! 1938: }
! 1939: }
! 1940:
! 1941: /* close the stdargs */
! 1942: va_end (args);
! 1943:
! 1944: /* check that we have received, at least, an string
! 1945: * parseable */
! 1946: if (stream == NULL)
! 1947: return NULL;
! 1948:
! 1949: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "string to parse: %s", stream);
! 1950:
! 1951: /* parse the string */
! 1952: doc = axl_doc_parse (stream, -1, error);
! 1953:
! 1954: /* free the stream */
! 1955: axl_free (stream);
! 1956:
! 1957: return doc;
! 1958: }
! 1959:
! 1960: /**
! 1961: * @internal
! 1962: *
! 1963: * Internal support function which checks the provided child and its
! 1964: * childs are equal.
! 1965: */
! 1966: axl_bool __axl_doc_are_equal (axlNode * node, axlNode * node2, axl_bool trimmed, axlError ** error)
! 1967: {
! 1968: int iterator;
! 1969: int length;
! 1970: int length2;
! 1971:
! 1972: axlItem * child;
! 1973: axlItem * child2;
! 1974:
! 1975: /* check if parent nodes are equal */
! 1976: if (! axl_node_are_equal (node, node2))
! 1977: return axl_false;
! 1978:
! 1979: /* iterate over all childs inside the node */
! 1980: iterator = 0;
! 1981: length = axl_node_get_child_num (node);
! 1982: length2 = axl_node_get_child_num (node2);
! 1983:
! 1984: if (length != length2) {
! 1985: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "child number differs, documents aren't equal");
! 1986: axl_error_report (error, -1, "child number differs, documents aren't equal");
! 1987: return axl_false;
! 1988: }
! 1989:
! 1990: /* get the first item inside the node */
! 1991: child = axl_item_get_first_child (node);
! 1992: child2 = axl_item_get_first_child (node2);
! 1993:
! 1994: /* for each item child found in both nodes */
! 1995: while (child != NULL && child2 != NULL) {
! 1996:
! 1997: if (child == NULL) {
! 1998: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "child from the first document is null..");
! 1999: axl_error_report (error, -1, "child from the first document is null..");
! 2000: }
! 2001:
! 2002: if (child2 == NULL) {
! 2003: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "child from the second document is null..");
! 2004: axl_error_report (error, -1, "child from the second document is null..");
! 2005: }
! 2006:
! 2007: /* check if these nodes are also equal */
! 2008: if (! axl_item_are_equal_full (child, child2, trimmed, error)) {
! 2009: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "items aren't equal, document is not equal");
! 2010: return axl_false;
! 2011: }
! 2012:
! 2013: /* check its childs in the case the axl item is
! 2014: * representing an item node */
! 2015: if (axl_item_get_type (child) == ITEM_NODE) {
! 2016: /* get a reference */
! 2017: node = axl_item_get_data (child);
! 2018: node2 = axl_item_get_data (child2);
! 2019:
! 2020: if (! __axl_doc_are_equal (node, node2, trimmed, error)) {
! 2021: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "nodes <%s> and <%s> aren't equal, document is not equal",
! 2022: axl_node_get_name (node), axl_node_get_name (node2));
! 2023: return axl_false;
! 2024: } /* end if */
! 2025: }
! 2026:
! 2027: /* get a referece to the next childs to check */
! 2028: child = axl_item_get_next (child);
! 2029: child2 = axl_item_get_next (child2);
! 2030:
! 2031: } /* end while */
! 2032:
! 2033: /* the nodes recieved are equal */
! 2034: return (child == NULL && child2 == NULL);
! 2035: }
! 2036:
! 2037: /**
! 2038: * @internal Common implementation for equal documents.
! 2039: */
! 2040: axl_bool axl_doc_are_equal_common (axlDoc * doc,
! 2041: axlDoc * doc2,
! 2042: axl_bool trimmed,
! 2043: axlError ** error)
! 2044: {
! 2045: axlNode * node;
! 2046: axlNode * node2;
! 2047:
! 2048: /* check first reference */
! 2049: if (doc == NULL) {
! 2050: axl_error_report (error, -1, "Documents differs because first document reference is null");
! 2051: return axl_false;
! 2052: }
! 2053: /* check second reference */
! 2054: if (doc2 == NULL) {
! 2055: axl_error_report (error, -1, "Documents differs because second document reference is null");
! 2056: return axl_false;
! 2057: }
! 2058:
! 2059: /* first, check the document root */
! 2060: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking that both documents are equal");
! 2061:
! 2062: node = axl_doc_get_root (doc);
! 2063: if (node == NULL) {
! 2064: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "document(a) doesn't have document root ..");
! 2065: }
! 2066: node2 = axl_doc_get_root (doc2);
! 2067: if (node2 == NULL) {
! 2068: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "document(b) doesn't have document root ..");
! 2069: }
! 2070:
! 2071: /* call to common implemenation, activating triming */
! 2072: return __axl_doc_are_equal (node, node2, trimmed, error);
! 2073: }
! 2074:
! 2075: /**
! 2076: * @brief Allows to perform a document equal check against order,
! 2077: * relaxing the checking done to contet found inside nodes.
! 2078: *
! 2079: * This function works the same as \ref axl_doc_are_equal but
! 2080: * considering that two content are equal no matter which is the
! 2081: * number of white spaces (in the W3C, ' ', \\t, \\r and \\n) are
! 2082: * found starting and ending the content.
! 2083: *
! 2084: * Under this approach the both document aren't exactly the same, but
! 2085: * usually, white spaces found starting and ending content have no
! 2086: * meaning for the application processing xml. In the case you want a
! 2087: * fully equal document checking you must \ref axl_doc_are_equal
! 2088: *
! 2089: * @param doc The document to check.
! 2090: * @param doc2 The second document to check
! 2091: *
! 2092: * @return \ref axl_true if both documents are equal in the sense
! 2093: * described, otherwise \ref axl_false is returned.
! 2094: */
! 2095: axl_bool axl_doc_are_equal_trimmed (axlDoc * doc,
! 2096: axlDoc * doc2)
! 2097: {
! 2098: /* call to common implemenation, activating triming */
! 2099: return axl_doc_are_equal_common (doc, doc2, axl_true, NULL);
! 2100: }
! 2101:
! 2102: /**
! 2103: * @brief Allows to check if the provided two references represents
! 2104: * equivalent xml documents.
! 2105: *
! 2106: * There is an alternative document checking function (\ref
! 2107: * axl_doc_are_equal_trimmed) which considered that content found
! 2108: * inside a xml node is equal if they share the same information
! 2109: * without considering white spaces found starting and ending both
! 2110: * elements being checked.
! 2111: *
! 2112: * This function considers that two documents are equal only and only
! 2113: * if all nodes, attributes and content found is exactly, byte by
! 2114: * byte, as found in the other document.
! 2115: *
! 2116: * @param doc The first XML document to check.
! 2117: * @param doc2 The second XML document to check.
! 2118: *
! 2119: * @return axl_true if both documents represents the same document,
! 2120: * axl_false if not.
! 2121: */
! 2122: axl_bool axl_doc_are_equal (axlDoc * doc,
! 2123: axlDoc * doc2)
! 2124: {
! 2125: /* call to common implemenation, activating triming */
! 2126: return axl_doc_are_equal_common (doc, doc2, axl_true, NULL);
! 2127: }
! 2128:
! 2129: /**
! 2130: * @brief Allows to check if the provided two references represents
! 2131: * equivalent xml documents.
! 2132: *
! 2133: * There is an alternative document checking function (\ref
! 2134: * axl_doc_are_equal_trimmed) which considered that content found
! 2135: * inside a xml node is equal if they share the same information
! 2136: * without considering white spaces found starting and ending both
! 2137: * elements being checked.
! 2138: *
! 2139: * This function considers that two documents are equal only and only
! 2140: * if all nodes, attributes and content found is exactly, byte by
! 2141: * byte, as found in the other document.
! 2142: *
! 2143: * @param doc The first XML document to check.
! 2144: *
! 2145: * @param doc2 The second XML document to check.
! 2146: *
! 2147: * @param trimmed Allows to configure if node content must be trimmed
! 2148: * before checking them (\ref axl_doc_are_equal_trimmed).
! 2149: *
! 2150: * @param error An optional reference to an \ref axlError node where
! 2151: * difference information will be reported.
! 2152: *
! 2153: * @return axl_true if both documents represents the same document,
! 2154: * axl_false if not.
! 2155: */
! 2156: axl_bool axl_doc_are_equal_full (axlDoc * doc,
! 2157: axlDoc * doc2,
! 2158: axl_bool trimmed,
! 2159: axlError ** error)
! 2160: {
! 2161: /* call to common implemenation, activating triming */
! 2162: return axl_doc_are_equal_common (doc, doc2, trimmed, error);
! 2163: }
! 2164:
! 2165:
! 2166:
! 2167:
! 2168: /**
! 2169: * @brief Allows to get current root node for the given xml document,
! 2170: * represented by the \ref axlDoc instance.
! 2171: *
! 2172: * Every XML document has a very first node, which enclose all childs
! 2173: * found inside the document, that is called the root node. This xml
! 2174: * node,
! 2175: *
! 2176: * This function couldn't return NULL because every well opened xml
! 2177: * document, always have a root node. However, the function could
! 2178: * return a NULL value if the document received is a null reference.
! 2179: *
! 2180: * @param doc The xml document (\ref axlDoc) where the root node will
! 2181: * be returned.
! 2182: *
! 2183: * @return The root node (\ref axlNode) or NULL if fails.
! 2184: */
! 2185: axlNode * axl_doc_get_root (axlDoc * doc)
! 2186: {
! 2187: axl_return_val_if_fail (doc, NULL);
! 2188:
! 2189: /* return current root node */
! 2190: return doc->rootNode;
! 2191: }
! 2192:
! 2193: /**
! 2194: * @internal
! 2195: *
! 2196: * @brief An always return 1 to make the list to store elements append
! 2197: * at the end.
! 2198: */
! 2199: int __axl_doc_get_are_equal (axlPointer a, axlPointer b)
! 2200: {
! 2201: return 1;
! 2202: }
! 2203:
! 2204: /**
! 2205: * @brief Allows to get a particular node (or list of nodes) that are
! 2206: * located at a selected path.
! 2207: *
! 2208: * Providing a path, the function lookups for nodes stored on the
! 2209: * selected location inside the provided document. The path provided
! 2210: * doesn't follow the XPath extension.
! 2211: *
! 2212: * Taking as a reference for the xml to be explained on the following
! 2213: * rules:
! 2214: * \code
! 2215: * <complex>
! 2216: * <data>
! 2217: * <node>
! 2218: * <row>10</row>
! 2219: * </node>
! 2220: * </data>
! 2221: * </complex>
! 2222: * \endcode
! 2223: *
! 2224: *
! 2225: * Here is how the path works:
! 2226: *
! 2227: * - If provided a "/", the root node is returned. This is same than
! 2228: * provided the root node name, like "/complex", when it is expected
! 2229: * to find as root node <complex>. However, providing a particular
! 2230: * node to search allows to get ensure that the root node is the one
! 2231: * looked up.
! 2232: *
! 2233: * - To select nodes inside the first root node, in a generic way,
! 2234: * without providing details about the root node name, you could use
! 2235: * "//\*". This will provide all nodes that are found inside the root
! 2236: * node, whatever it is called. If it is required to get all nodes,
! 2237: * inside the root node, ensuring that the root one is called
! 2238: * "complex" you should use: "/complex/\*"
! 2239: *
! 2240: * - If it is required to get a selected node inside the root node,
! 2241: * that is called in a particular way you can use: //data. Again, if
! 2242: * it is required to ensure that a particular node exists, from the
! 2243: * top level down the leaf node, it will required to write something
! 2244: * like: "/complex/data".
! 2245: *
! 2246: * - Remember that is totally different to query for "/complex/data"
! 2247: * than "/complex/data/\*". The first one, returns the node, or nodes,
! 2248: * called <b>data</b> that are inside the root node called
! 2249: * <b>complex</b>, while the second one says: return all nodes inside
! 2250: * the node <b>data</b> that is inside the root node called
! 2251: * <b>complex</b>
! 2252: *
! 2253: * Finally, keep in mind that this function only returns nodes. To get
! 2254: * node content, attributes or anything else, you'll have to get the
! 2255: * node first and then operate with it.
! 2256: *
! 2257: *
! 2258: *
! 2259: * @param doc The \ref axlDoc reference where the lookup will be
! 2260: * performed.
! 2261: *
! 2262: * @param path_to A path to the node (nodes) that are inside the path
! 2263: * especifyied.
! 2264: *
! 2265: * @return A list of nodes (\ref axlNode) if the case something is
! 2266: * found or NULL if fails to find something at the given path. If the
! 2267: * path is right but no node match with it or there is no node, the
! 2268: * function will return NULL reference rather a list with no
! 2269: * nodes. Returned value must be deallocated by using \ref
! 2270: * axl_list_free.
! 2271: */
! 2272: axlList * axl_doc_get_list (axlDoc * doc, const char * path_to)
! 2273: {
! 2274: axlList * nodes;
! 2275: axlNode * node = NULL;
! 2276: int iterator = 0;
! 2277: char ** paths = 0;
! 2278:
! 2279:
! 2280: axl_return_val_if_fail (doc, NULL);
! 2281: axl_return_val_if_fail (path_to, NULL);
! 2282: axl_return_val_if_fail (path_to[0] == '/', NULL);
! 2283:
! 2284: /* create the axl list */
! 2285: nodes = axl_list_new (__axl_doc_get_are_equal, NULL);
! 2286:
! 2287: /* split paths */
! 2288: paths = axl_stream_split (path_to, 1, "/");
! 2289: axl_return_val_if_fail (paths, nodes);
! 2290:
! 2291: /* get a reference to the root node */
! 2292: node = doc->rootNode;
! 2293:
! 2294: /* basic case, check for the root node */
! 2295: if (strlen (paths[1]) != 0) {
! 2296: /* check the node is the one requested */
! 2297: if (! NODE_CMP_NAME (node, paths[1])) {
! 2298: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "requested root node = %s wasn't found, current root %s", paths[1],
! 2299: axl_node_get_name (doc->rootNode));
! 2300: axl_list_free (nodes);
! 2301: axl_stream_freev (paths);
! 2302: return NULL;
! 2303: }
! 2304: }
! 2305:
! 2306: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found node: %s for path=%s", paths[1], path_to);
! 2307:
! 2308: /* now the general case */
! 2309: iterator = 2;
! 2310: while ((paths[iterator] != NULL) && (strlen (paths[iterator]) > 0)) {
! 2311: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking path item %s", paths[iterator]);
! 2312:
! 2313: /* check that the last path is used */
! 2314: if (axl_cmp (paths[iterator], "*") &&
! 2315: (axl_stream_strv_num (paths) != iterator + 1)) {
! 2316: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "using the '*' at that path different from the last one.", paths[iterator]);
! 2317: axl_list_free (nodes);
! 2318: axl_stream_freev (paths);
! 2319: return NULL;
! 2320: }
! 2321:
! 2322: /* get a reference to the node searched */
! 2323: node = axl_node_get_child_called (node, paths[iterator]);
! 2324: if (node == NULL) {
! 2325: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "the node located at %s wasn't found.", path_to);
! 2326:
! 2327: axl_list_free (nodes);
! 2328: axl_stream_freev (paths);
! 2329: return NULL;
! 2330: }
! 2331: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found node: %s", paths[iterator]);
! 2332:
! 2333: /* update iterator value */
! 2334: iterator++;
! 2335: }
! 2336:
! 2337: /* add the node found */
! 2338: axl_list_add (nodes, node);
! 2339:
! 2340: /* free paths */
! 2341: axl_stream_freev (paths);
! 2342:
! 2343: /* return the node found */
! 2344: return nodes;
! 2345: }
! 2346:
! 2347: /**
! 2348: * @brief Allows to return only one node for the selected path.
! 2349: *
! 2350: * This function works the same way like \ref axl_doc_get_list but
! 2351: * extracting the first node reference found inside the list returned
! 2352: * by the previous function, and returning it.
! 2353: *
! 2354: * Many times, a path is selected because it is know that under that
! 2355: * location couldn't be more than one element. However, using \ref
! 2356: * axl_doc_get_list function makes this task really anoying because
! 2357: * you have to get the list, extract the node, from the list, and
! 2358: * releasing the list reference to actually get access to the node
! 2359: * looked up.
! 2360: *
! 2361: * This function allows you to get access to the node stored on the
! 2362: * selected location, and, if a path that provides several nodes is
! 2363: * returned, only the first node found on that list is returned.
! 2364: *
! 2365: * @param doc The \ref axlDoc document where the node will be returned.
! 2366: * @param path_to A path to the node to get.
! 2367: *
! 2368: * @return A reference to a \ref axlNode instace, or NULL if
! 2369: * fails. Returned reference must not be deallocated.
! 2370: */
! 2371: axlNode * axl_doc_get (axlDoc * doc, const char * path_to)
! 2372: {
! 2373: axlList * list = NULL;
! 2374: axlNode * node = NULL;
! 2375:
! 2376: axl_return_val_if_fail (doc, NULL);
! 2377: axl_return_val_if_fail (path_to, NULL);
! 2378:
! 2379: /* get the list of nodes */
! 2380: list = axl_doc_get_list (doc, path_to);
! 2381: if (list == NULL)
! 2382: return NULL;
! 2383:
! 2384: /* get the node requested */
! 2385: if (axl_list_length (list) > 0)
! 2386: node = axl_list_get_nth (list, 0);
! 2387:
! 2388: axl_list_free (list);
! 2389: return node;
! 2390:
! 2391: }
! 2392:
! 2393: /**
! 2394: * @brief Allows to get the node content for the final node provided
! 2395: * by the path.
! 2396: *
! 2397: * @param doc The (\ref axlDoc) xml document where the content will be
! 2398: * looked up.
! 2399: *
! 2400: * @param path_to Path to the node where the content will be returned.
! 2401: *
! 2402: * @param content_size An optional reference to a variable to store
! 2403: * the size of the content returned. If the function receives NULL,
! 2404: * the content size will not be returned.
! 2405: *
! 2406: * @return A reference to the content that the node have or NULL if
! 2407: * fails. The function could fail either because the node doesn't have
! 2408: * content or because the node identified by the path doesn't
! 2409: * exist. The result returned must not be deallocated.
! 2410: */
! 2411: const char * axl_doc_get_content_at (axlDoc * doc,
! 2412: const char * path_to,
! 2413: int * content_size)
! 2414: {
! 2415:
! 2416: axlNode * node;
! 2417:
! 2418: /* get the node reference */
! 2419: node = axl_doc_get (doc, path_to);
! 2420: axl_return_val_if_fail (node, NULL);
! 2421:
! 2422: /* return the content requested */
! 2423: return axl_node_get_content (node, content_size);
! 2424:
! 2425: }
! 2426:
! 2427: /**
! 2428: * @brief Gets current axl Document encoding.
! 2429: *
! 2430: * @param doc The document where the encoding will be retrieved.
! 2431: *
! 2432: * @return A valid \ref axlDoc reference. NULL is returned in the case
! 2433: * a NULL \ref axlDoc reference is received. The value returned by
! 2434: * this function must not be deallocated.
! 2435: */
! 2436: const char * axl_doc_get_encoding (axlDoc * doc)
! 2437: {
! 2438: /* check parameter received */
! 2439: axl_return_val_if_fail (doc, NULL);
! 2440:
! 2441: return (doc->encoding != NULL) ? doc->encoding : "";
! 2442: }
! 2443:
! 2444: /**
! 2445: * @brief Allows to get current standalone configuration for the given
! 2446: * axlDoc document.
! 2447: *
! 2448: * @param doc The \ref axlDoc document where the standalone value will
! 2449: * be retreived.
! 2450: *
! 2451: * @return \ref axl_true if the standalone configuration, found inside
! 2452: * the xml header is set to AXL_TRUE. Otherwise \ref axl_false is
! 2453: * returned. Keep in mind that the function will return an \ref
! 2454: * axl_false value if a null reference is received.
! 2455: */
! 2456: axl_bool axl_doc_get_standalone (axlDoc * doc)
! 2457: {
! 2458: axl_return_val_if_fail (doc, axl_false);
! 2459:
! 2460: /* return current configuration */
! 2461: return doc->standalone;
! 2462: }
! 2463:
! 2464: /**
! 2465: * @brief Allows to configure the document root for the given \ref
! 2466: * axlDoc instance.
! 2467: *
! 2468: * Every xml document has a xml node root. This is the first node,
! 2469: * that holds all childs. This function allows to configure that xml
! 2470: * document root. See also \ref axl_doc_get_root.
! 2471: *
! 2472: * Remember that previous document root will not be deallocated so,
! 2473: * the user space must take care about previous reference.
! 2474: *
! 2475: * @param doc The \ref axlDoc where the document root will be
! 2476: * configured.
! 2477: *
! 2478: * @param root The \ref axlNode used to configure the new document
! 2479: * root. The reference received can be null. In this case, it is
! 2480: * considered that the root node is being unset.
! 2481: */
! 2482: void axl_doc_set_root (axlDoc * doc, axlNode * root)
! 2483: {
! 2484: axl_return_if_fail (doc);
! 2485:
! 2486: /* set the new root */
! 2487: doc->rootNode = root;
! 2488:
! 2489: /* if the reference received is null, just return */
! 2490: if (root == NULL)
! 2491: return;
! 2492:
! 2493: /* set a refeference to the document root */
! 2494: axl_node_set_doc (root, doc);
! 2495:
! 2496: return;
! 2497: }
! 2498:
! 2499: /**
! 2500: * @internal
! 2501: *
! 2502: * @brief Allows to set the given axlNode to be child of the current
! 2503: * parent.
! 2504: *
! 2505: * @param doc The \ref axlDoc reference where the \ref axlNode will be
! 2506: * configured.
! 2507: *
! 2508: * @param node The \ref axlNode reference to set as a child for the
! 2509: * parent node.
! 2510: */
! 2511: void axl_doc_set_child_current_parent (axlDoc * doc, axlNode * node)
! 2512: {
! 2513: axlNode * parent;
! 2514:
! 2515: /* perform some environment checks */
! 2516: axl_return_if_fail (doc);
! 2517: axl_return_if_fail (node);
! 2518:
! 2519: parent = axl_stack_peek (doc->parentNode);
! 2520: axl_return_if_fail (parent);
! 2521:
! 2522: /* set the child for the current parent */
! 2523: axl_node_set_child (parent, node);
! 2524:
! 2525: /* set the new parent */
! 2526: axl_stack_push (doc->parentNode, node);
! 2527:
! 2528: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "pushed a new parent into the stack <%s>, current status after operation: %d",
! 2529: axl_node_get_name (node), axl_stack_size (doc->parentNode));
! 2530:
! 2531: return;
! 2532: }
! 2533:
! 2534: /**
! 2535: * @internal Allows to make current \ref axlDoc to pop current parent
! 2536: * node, making the new parent node the previously opened.
! 2537: *
! 2538: * This API is deprecated (internal function used in the past).
! 2539: *
! 2540: * @param doc The \ref axlDoc where the pop operation will be
! 2541: * performed.
! 2542: */
! 2543: void axl_doc_pop_current_parent (axlDoc * doc)
! 2544: {
! 2545: return;
! 2546: }
! 2547:
! 2548: /**
! 2549: * @brief Allows to configure a PI target, with its content, on the given \ref axlDoc.
! 2550: *
! 2551: * A PI is a process instruction that is passed to the
! 2552: * application. This process instruction has a target name, which
! 2553: * acording to the standard is the application which should receive
! 2554: * the target content and an optional target content associated.
! 2555: *
! 2556: * This function allows to configure (add) a new PI item inside the
! 2557: * given xml document (\ref axlDoc). The PI content is optional. If
! 2558: * provided NULL, the PI will only contain as information the PI
! 2559: * target.
! 2560: *
! 2561: * Here is how a process instruction is used inside a xml document:
! 2562: * \code
! 2563: * <?xml version='1.0'>
! 2564: * <?launch "some command" ?>
! 2565: * <complex>
! 2566: * <?data "some data" ?>
! 2567: * <?data "more data" ?>
! 2568: * <data>
! 2569: * <row attr="20" />
! 2570: * </data>
! 2571: * </complex>
! 2572: * \endcode
! 2573: *
! 2574: * Previous example shows how to use PI (process instructions) from
! 2575: * outside the xml root node (<b>complex</b>, and also how it is used
! 2576: * from inside a xml node definition <b>complex</b>.
! 2577: *
! 2578: * As you can see, PI elements could be used as many times as you want
! 2579: * and places allowed to do so are just right before begining with the
! 2580: * root node and inside xml node definitions.
! 2581: *
! 2582: * @param doc The axlDocument where the PI will be added.
! 2583: * @param target The PI target name.
! 2584: * @param content The PI content (optional value).
! 2585: */
! 2586: void axl_doc_add_pi_target (axlDoc * doc,
! 2587: char * target,
! 2588: char * content)
! 2589: {
! 2590: axlPI * pi;
! 2591:
! 2592: /* perform some environmental checks */
! 2593: axl_return_if_fail (doc);
! 2594: axl_return_if_fail (target);
! 2595:
! 2596: /* create the PI element */
! 2597: pi = axl_pi_create (target, content);
! 2598:
! 2599: /* add the PI */
! 2600: axl_list_add (doc->piTargets, pi);
! 2601:
! 2602: return;
! 2603: }
! 2604:
! 2605: /**
! 2606: * @brief Allows to check if the provided Processing instruction
! 2607: * target is defined on the given xml document (\ref axlDoc).
! 2608: *
! 2609: * Processing instruction are a way to configure the xml document with
! 2610: * processing information to instruct the application level that is
! 2611: * going to consume the XML information.
! 2612: *
! 2613: * @param doc The document where the processing instruction will be read.
! 2614: *
! 2615: * @param pi_target The process instruction name.
! 2616: *
! 2617: * @return axl_true is the processing instruction is defined,
! 2618: * otherwise axl_false is returned.
! 2619: */
! 2620: axl_bool axl_doc_has_pi_target (axlDoc * doc, char * pi_target)
! 2621: {
! 2622: axlPI * pi;
! 2623: int iterator = 0;
! 2624: int length = 0;
! 2625:
! 2626:
! 2627: axl_return_val_if_fail (doc, axl_false);
! 2628: axl_return_val_if_fail (pi_target, axl_false);
! 2629:
! 2630: /* get the length for the items inserted */
! 2631: length = axl_list_length (doc->piTargets);
! 2632: while (iterator < length) {
! 2633: /* for each item inserted */
! 2634: pi = axl_list_get_nth (doc->piTargets, iterator);
! 2635: /* only check the first ocurrency */
! 2636: if (axl_cmp (pi->name, pi_target))
! 2637: return axl_true;
! 2638:
! 2639: iterator++;
! 2640: }
! 2641:
! 2642: return axl_false;
! 2643: }
! 2644:
! 2645: /**
! 2646: * @brief Allows to get current processing instruction content.
! 2647: *
! 2648: * @param doc The document where the processing instruction is placed.
! 2649: *
! 2650: * @param pi_target The processing instruction target to get current
! 2651: * content.
! 2652: *
! 2653: * @return An internal reference to the process instruction target
! 2654: * content. Value returned mustn't be deallocated
! 2655: */
! 2656: char * axl_doc_get_pi_target_content (axlDoc * doc, char * pi_target)
! 2657: {
! 2658: axlPI * pi;
! 2659: int iterator = 0;
! 2660: int length = 0;
! 2661:
! 2662: axl_return_val_if_fail (doc, NULL);
! 2663: axl_return_val_if_fail (pi_target, NULL);
! 2664:
! 2665: /* get the length for the items inserted */
! 2666: length = axl_list_length (doc->piTargets);
! 2667: while (iterator < length) {
! 2668: /* for each item inserted */
! 2669: pi = axl_list_get_nth (doc->piTargets, iterator);
! 2670: /* only check the first ocurrency */
! 2671: if (axl_cmp (pi->name, pi_target))
! 2672: return pi->content;
! 2673:
! 2674: iterator++;
! 2675: }
! 2676:
! 2677: return NULL;
! 2678: }
! 2679:
! 2680: /**
! 2681: * @brief Allows to get a list which contains \ref axlPI nodes,
! 2682: * representing all process instruction that the document has.
! 2683: *
! 2684: * While using PI, you can use the following functions to get PI
! 2685: * information:
! 2686: *
! 2687: * - \ref axl_doc_has_pi_target
! 2688: * - \ref axl_doc_get_pi_target_content
! 2689: *
! 2690: * However, this function will return first ocurrency for PI found
! 2691: * inside the xml document. If you don't use repeated PI elements, you
! 2692: * won't find problems, but, if you need to iterate ever all PI found
! 2693: * or you are using repeated PI, you can use this function as follows
! 2694: * to get current pi elements:
! 2695: * \code
! 2696: * void show_all_pi (axlDoc * doc)
! 2697: * {
! 2698: * int iterator;
! 2699: * axlPI * pi;
! 2700: * axlList * PIs;
! 2701: *
! 2702: * // get all PI target that the document has
! 2703: * PIs = axl_doc_get_pi_target_list (doc);
! 2704: * iterator = 0;
! 2705: *
! 2706: * while (iterator < axl_list_length (PIs)) {
! 2707: * // get next pi stored
! 2708: * pi = axl_list_get_nth (PIs, iterator);
! 2709: *
! 2710: * // do some stuff
! 2711: * printf ("PI found target name=%s, content=%s\n",
! 2712: * axl_pi_get_name (pi),
! 2713: * axl_pi_get_content (pi));
! 2714: *
! 2715: * // update the iterator
! 2716: * iterator++;
! 2717: * }
! 2718: * return;
! 2719: * }
! 2720: * \endcode
! 2721: *
! 2722: * @param doc The xml document (\ref axlDoc) where the process
! 2723: * instruction will be returned.
! 2724: *
! 2725: * @return A reference to the list of processing instruction that the
! 2726: * xml document (\ref axlDoc) has.
! 2727: */
! 2728: axlList * axl_doc_get_pi_target_list (axlDoc * doc)
! 2729: {
! 2730: axl_return_val_if_fail (doc, NULL);
! 2731:
! 2732: return doc->piTargets;
! 2733: }
! 2734:
! 2735: /**
! 2736: *
! 2737: * @brief Allows to create a new \ref axlPI element.
! 2738: *
! 2739: * @param name The PI target name.
! 2740: * @param content The PI content.
! 2741: *
! 2742: * @return A newly allocated \ref axlPI element.
! 2743: */
! 2744: axlPI * axl_pi_create (char * name, char * content)
! 2745: {
! 2746: axlPI * pi;
! 2747:
! 2748: /* create the PI */
! 2749: pi = axl_new (axlPI, 1);
! 2750: pi->name = axl_strdup (name);
! 2751:
! 2752: /* copy the content if defined */
! 2753: if (content != NULL)
! 2754: pi->content = axl_strdup (content);
! 2755:
! 2756: return pi;
! 2757: }
! 2758:
! 2759: /**
! 2760: * @brief Returns a newly allocated copy representing the same value
! 2761: * as the provided \ref axlPI reference.
! 2762: *
! 2763: * @param pi The pi reference received.
! 2764: *
! 2765: * @return A reference to the \ref axlPI element or null if it fails.
! 2766: */
! 2767: axlPI * axl_pi_copy (axlPI * pi)
! 2768: {
! 2769: axlPI * _pi;
! 2770:
! 2771: axl_return_val_if_fail (pi, NULL);
! 2772:
! 2773: /* create the PI */
! 2774: _pi = axl_new (axlPI, 1);
! 2775: _pi->name = axl_strdup (pi->name);
! 2776:
! 2777: /* copy the content if defined */
! 2778: if (pi->content != NULL)
! 2779: _pi->content = axl_strdup (pi->content);
! 2780:
! 2781: return _pi;
! 2782: }
! 2783:
! 2784: /**
! 2785: * @brief Allows to check if both provided process instructions are
! 2786: * equal.
! 2787: *
! 2788: * @param pi First process instruction to check.
! 2789: * @param pi2 Second process instructions to check.
! 2790: *
! 2791: * @return \ref axl_true if both process instructions are equal. If some
! 2792: * of parameters received are NULL, the function will always return
! 2793: * \ref axl_false.
! 2794: */
! 2795: axl_bool axl_pi_are_equal (axlPI * pi,
! 2796: axlPI * pi2)
! 2797: {
! 2798: /* basic null reference check */
! 2799: axl_return_val_if_fail (pi, axl_false);
! 2800: axl_return_val_if_fail (pi2, axl_false);
! 2801:
! 2802: /* check internal data */
! 2803: if (! axl_cmp (pi->name, pi2->name))
! 2804: return axl_false;
! 2805:
! 2806: /* final check, both content must be equal */
! 2807: return axl_cmp (pi->content, pi2->content);
! 2808: }
! 2809:
! 2810: /**
! 2811: * @brief Allows to get current pi name from the given \ref axlPI
! 2812: * reference.
! 2813: *
! 2814: * @param pi The PI reference where the name will returned.
! 2815: *
! 2816: * @return A string representing the PI name. Returned value shouldn't
! 2817: * be deallocated.
! 2818: */
! 2819: char * axl_pi_get_name (axlPI * pi)
! 2820: {
! 2821: axl_return_val_if_fail (pi, NULL);
! 2822:
! 2823: /* return current PI name */
! 2824: return pi->name;
! 2825: }
! 2826:
! 2827: /**
! 2828: * @brief Allows to get current optinal PI content.
! 2829: *
! 2830: * @param pi The PI where the content will be returned.
! 2831: *
! 2832: * @return A string representing the PI content. This value could be
! 2833: * NULL because it is optional to be defined. Returned value must not
! 2834: * be deallocated.
! 2835: */
! 2836: char * axl_pi_get_content (axlPI * pi)
! 2837: {
! 2838: axl_return_val_if_fail (pi, NULL);
! 2839:
! 2840: /* return current PI content */
! 2841: return pi->content;
! 2842: }
! 2843:
! 2844: /**
! 2845: * @brief Deallocates memory used by the \ref axlPI target.
! 2846: *
! 2847: * @param pi The target to destroy.
! 2848: */
! 2849: void axl_pi_free (axlPI * pi)
! 2850: {
! 2851: if (pi == NULL)
! 2852: return;
! 2853:
! 2854: /* free PI target */
! 2855: axl_free (pi->name);
! 2856: axl_free (pi->content);
! 2857: axl_free (pi);
! 2858: return;
! 2859: }
! 2860:
! 2861: /**
! 2862: * @internal Allows to get the number of bytes that the process
! 2863: * instruction will take.
! 2864: *
! 2865: * @param pi The process instruction.
! 2866: *
! 2867: * @return A size or -1 if it fails.
! 2868: */
! 2869: int axl_pi_get_size (axlPI * pi)
! 2870: {
! 2871: axl_return_val_if_fail (pi, -1);
! 2872:
! 2873: /* <?name content?> */
! 2874: return strlen (pi->name) + strlen (pi->content) + 5;
! 2875: }
! 2876:
! 2877: /**
! 2878: * @internal
! 2879: *
! 2880: * Common implementation for \ref axl_doc_iterate and \ref axl_doc_iterate2.
! 2881: */
! 2882: axl_bool __axl_doc_iterate_common (axlDoc * doc,
! 2883: axlNode * root,
! 2884: AxlIterationMode mode,
! 2885: axlIterationFunc func,
! 2886: axlIterationFunc2 func2,
! 2887: axlPointer ptr,
! 2888: axlPointer ptr2)
! 2889: {
! 2890: int iterator;
! 2891: axl_bool was_removed = axl_false;
! 2892:
! 2893: axlNode * node;
! 2894: axlNode * nodeAux;
! 2895:
! 2896: axlList * pending;
! 2897:
! 2898: /* check first node */
! 2899: axl_return_val_if_fail (root, axl_false);
! 2900:
! 2901: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "notifying first node inside the iteration");
! 2902:
! 2903: /* notify first node found we pass in a null value because it
! 2904: doesn't have a * parent. */
! 2905: if (func && ! func (root, NULL, doc, &was_removed, ptr))
! 2906: return axl_false;
! 2907: if (func2 && ! func2 (root, NULL, doc, &was_removed, ptr, ptr2))
! 2908: return axl_false;
! 2909:
! 2910: /* if the root node was removed, don't continue */
! 2911: if (was_removed)
! 2912: return axl_false;
! 2913:
! 2914: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "continuing with next nodes");
! 2915:
! 2916: /* get childs */
! 2917: pending = axl_node_get_childs (root);
! 2918:
! 2919: /* for each pending node */
! 2920: while (axl_list_length (pending) > 0) {
! 2921:
! 2922: /* get the first node inside the pending list */
! 2923: node = axl_list_get_first (pending);
! 2924:
! 2925: /* remove the node node from the pending list and add
! 2926: * all childs */
! 2927: axl_list_remove_first (pending);
! 2928:
! 2929: /* notify node found */
! 2930: was_removed = axl_false;
! 2931: if (func && ! func (node, axl_node_get_parent (node), doc, &was_removed, ptr)) {
! 2932: axl_list_free (pending);
! 2933: return axl_false;
! 2934: }
! 2935:
! 2936: /* notify node found */
! 2937: if (func2 && ! func2 (node, axl_node_get_parent (node), doc, &was_removed, ptr, ptr2)) {
! 2938: axl_list_free (pending);
! 2939: return axl_false;
! 2940: }
! 2941:
! 2942: /* add all its childs */
! 2943: if (!was_removed && axl_node_have_childs (node)) {
! 2944:
! 2945: /* get first child */
! 2946: nodeAux = axl_node_get_first_child (node);
! 2947:
! 2948: /* get all items of the next level and add
! 2949: * them properly */
! 2950: iterator = 0;
! 2951: while (nodeAux != NULL) {
! 2952:
! 2953: /* add to the pending list */
! 2954: switch (mode) {
! 2955: case DEEP_ITERATION:
! 2956: /* add the element */
! 2957: axl_list_add_at (pending, nodeAux, iterator);
! 2958:
! 2959: /* update the iterator */
! 2960: iterator++;
! 2961: break;
! 2962:
! 2963: case WIDE_ITERATION:
! 2964: /* add to the pending list */
! 2965: axl_list_add (pending, nodeAux);
! 2966: break;
! 2967: } /* end switch */
! 2968:
! 2969:
! 2970: /* update to the next */
! 2971: nodeAux = axl_node_get_next (nodeAux);
! 2972:
! 2973: } /* end while */
! 2974: } /* end if */
! 2975:
! 2976:
! 2977: } /* end while */
! 2978:
! 2979: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "terminated iteration process, deallocating list: %d",
! 2980: axl_list_length (pending));
! 2981:
! 2982: axl_list_free (pending);
! 2983:
! 2984: /* iteration performed completely */
! 2985: return axl_true;
! 2986: }
! 2987:
! 2988: /**
! 2989: * @brief Allows to perform an iteration over the documented provided,
! 2990: * visiting all nodes inside it.
! 2991: *
! 2992: * The function allows to configure the iteration module using \ref
! 2993: * AxlIterationMode (mode variable) and providing a callback function
! 2994: * that will be called for each node found (\ref axlIterationFunc).
! 2995: *
! 2996: * The function, optionall, allows to provide a user pointer that will
! 2997: * be passed to the callback function. See documentation for the
! 2998: * callback and the iteration module for more details.
! 2999: *
! 3000: * Here is an example:
! 3001: * \code
! 3002: * void perform_iteration (axlDoc * doc)
! 3003: * {
! 3004: * // call to iterate
! 3005: * axl_doc_iterate (doc,
! 3006: * // visit childs before brothers
! 3007: * DEEP_ITERATION,
! 3008: * // the func to execute: see below
! 3009: * show_node_found,
! 3010: * // optional user pointer
! 3011: * NULL);
! 3012: * }
! 3013: *
! 3014: * axl_bool show_node_found (axlNode * node, axlNode * parent,
! 3015: * axlDoc * doc, axl_bool * was_removed,
! 3016: * axlPointer ptr)
! 3017: * {
! 3018: * // Show node found
! 3019: * printf ("Node found: %s\n", axl_node_get_name (node));
! 3020: *
! 3021: * // If the node is removed inside the iteration
! 3022: * // using axl_node_remove or axl_node_replace, you
! 3023: * // must notify the iteration system using was_removed
! 3024: * // as follow: (* was_removed) = axl_true;
! 3025: * //
! 3026: * // If you don't remove anything, you don't need to do
! 3027: * // anything especial with was_removed.
! 3028: *
! 3029: * // don't stop iteration
! 3030: * return axl_true;
! 3031: * }
! 3032: * \endcode
! 3033: *
! 3034: * See also alternative APIs:
! 3035: *
! 3036: * - \ref axl_doc_iterate_full
! 3037: * - \ref axl_doc_iterate_full_from
! 3038: *
! 3039: * @param doc The xml document that will be iterated.
! 3040: *
! 3041: * @param mode The iterarion type to be performed.
! 3042: *
! 3043: * @param func The function to be called for each node found.
! 3044: *
! 3045: * @param ptr An user defined pointer that will be passed to the
! 3046: * callback function.
! 3047: *
! 3048: * @return The function returns \ref axl_true if the iteration was
! 3049: * performed over all nodes or \ref axl_false it it was stoped by the
! 3050: * iteration function (by returning \ref axl_false to stop the
! 3051: * iteration). The function also axl_false if the parameters provided doc
! 3052: * or func are not defined.
! 3053: */
! 3054: axl_bool axl_doc_iterate (axlDoc * doc,
! 3055: AxlIterationMode mode,
! 3056: axlIterationFunc func,
! 3057: axlPointer ptr)
! 3058: {
! 3059: axlNode * root;
! 3060:
! 3061: /* check basic data */
! 3062: axl_return_val_if_fail (doc, axl_false);
! 3063: axl_return_val_if_fail (func, axl_false);
! 3064:
! 3065: /* get the root node where the iteration will start */
! 3066: root = axl_doc_get_root (doc);
! 3067:
! 3068: /* call to common implementation */
! 3069: return __axl_doc_iterate_common (doc, root, mode, func, NULL, ptr, NULL);
! 3070:
! 3071: }
! 3072:
! 3073:
! 3074: /**
! 3075: * @brief Allows to perform an iteration over the documented provided,
! 3076: * visiting all nodes inside it (with two user defined pointers support).
! 3077: *
! 3078: * The function allows to configure the iteration module using \ref
! 3079: * AxlIterationMode (mode variable) and providing a callback function
! 3080: * that will be called for each node found (\ref axlIterationFunc).
! 3081: *
! 3082: * The function, optionall, allows to provide two user pointer that will
! 3083: * be passed to the callback function. See documentation for the
! 3084: * callback and the iteration module for more details. See also \ref axl_doc_iterate.
! 3085: *
! 3086: *
! 3087: * @param doc The xml document that will be iterated.
! 3088: *
! 3089: * @param mode The iterarion type to be performed.
! 3090: *
! 3091: * @param func The function to be called for each node found.
! 3092: *
! 3093: * @param ptr An user defined pointer that will be passed to the
! 3094: * callback function.
! 3095: *
! 3096: * @param ptr2 Second user defined pointer that will be passed to the
! 3097: * callback function.
! 3098: *
! 3099: *
! 3100: * @return The function returns \ref axl_true if the iteration was
! 3101: * performed over all nodes or \ref axl_false it it was stoped by the
! 3102: * iteration function (by returning \ref axl_false to stop the
! 3103: * iteration). The function also axl_false if the parameters provided doc
! 3104: * or func are not defined.
! 3105: */
! 3106: axl_bool axl_doc_iterate_full (axlDoc * doc,
! 3107: AxlIterationMode mode,
! 3108: axlIterationFunc2 func,
! 3109: axlPointer ptr,
! 3110: axlPointer ptr2)
! 3111:
! 3112: {
! 3113: axlNode * root;
! 3114:
! 3115: /* check basic data */
! 3116: axl_return_val_if_fail (doc, axl_false);
! 3117: axl_return_val_if_fail (func, axl_false);
! 3118:
! 3119: /* get the root node where the iteration will start */
! 3120: root = axl_doc_get_root (doc);
! 3121:
! 3122: /* call to common implementation */
! 3123: return __axl_doc_iterate_common (doc, root, mode, NULL, func, ptr, ptr2);
! 3124: }
! 3125:
! 3126: /**
! 3127: * @brief Allows to perform a iteration operation but configuring
! 3128: * where to start, discarding the rest content.
! 3129: *
! 3130: * See \ref axl_doc_iterate and \ref axl_doc_iterate_full for more
! 3131: * details. This function works the same like previous but, unlike
! 3132: * previous, this function doesn't use the default starting point: the
! 3133: * root node (\ref axl_doc_get_root). The function allows to configure
! 3134: * the node where to start the iteration operation.
! 3135: *
! 3136: * This function is equivalent to \ref axl_doc_iterate_full calling if
! 3137: * it use the root node document as value for <b>starting_from</b>.
! 3138: *
! 3139: * @param doc The xml document that will be iterated.
! 3140: *
! 3141: * @param starting_from The \ref axlNode where the operation will
! 3142: * start, discarding all content from ascending nodes, previous
! 3143: * siblings and following sibligins. From a iteration perspective, the
! 3144: * iteration opeeration.
! 3145: *
! 3146: * @param mode The iterarion type to be performed.
! 3147: *
! 3148: * @param func The function to be called for each node found.
! 3149: *
! 3150: * @param ptr An user defined pointer that will be passed to the
! 3151: * callback function.
! 3152: *
! 3153: * @param ptr2 Second user defined pointer that will be passed to the
! 3154: * callback function.
! 3155: *
! 3156: *
! 3157: * @return The function returns \ref axl_true if the iteration was
! 3158: * performed over all nodes or \ref axl_false it it was stoped by the
! 3159: * iteration function (by returning \ref axl_false to stop the
! 3160: * iteration). The function also axl_false if the parameters provided doc
! 3161: * or func are not defined.
! 3162: */
! 3163: axl_bool axl_doc_iterate_full_from (axlDoc * doc,
! 3164: axlNode * starting_from,
! 3165: AxlIterationMode mode,
! 3166: axlIterationFunc2 func,
! 3167: axlPointer ptr,
! 3168: axlPointer ptr2)
! 3169: {
! 3170: /* check basic data */
! 3171: axl_return_val_if_fail (doc, axl_false);
! 3172: axl_return_val_if_fail (func, axl_false);
! 3173:
! 3174: /* call to common implementation */
! 3175: return __axl_doc_iterate_common (doc, starting_from, mode, NULL, func, ptr, ptr2);
! 3176: }
! 3177:
! 3178:
! 3179: /**
! 3180: * @brief Releases memory allocated by the \ref axlDoc object.
! 3181: *
! 3182: * @param doc The \ref axlDoc object to unref.
! 3183: */
! 3184: void axl_doc_free (axlDoc * doc)
! 3185: {
! 3186: /* do not complain if an axlDoc reference is received */
! 3187: if (doc == NULL)
! 3188: return;
! 3189:
! 3190: /* free first root node */
! 3191: if (doc->rootNode != NULL)
! 3192: axl_node_free (doc->rootNode);
! 3193:
! 3194: /* free node hierarchy */
! 3195: if (doc->parentNode != NULL)
! 3196: axl_stack_free (doc->parentNode);
! 3197:
! 3198: /* free xml:space hierarchy */
! 3199: if (doc->xmlPreserve != NULL)
! 3200: axl_binary_stack_free (doc->xmlPreserve);
! 3201:
! 3202: /* free item factory */
! 3203: if (doc->item_factory != NULL)
! 3204: axl_factory_free (doc->item_factory);
! 3205:
! 3206: /* free content holding nodes factory */
! 3207: if (doc->content_factory != NULL)
! 3208: axl_factory_free (doc->content_factory);
! 3209:
! 3210: /* free attribute holding factory */
! 3211: if (doc->attr_factory != NULL)
! 3212: axl_factory_free (doc->attr_factory);
! 3213:
! 3214: /* free node factory */
! 3215: if (doc->node_factory != NULL)
! 3216: axl_factory_free (doc->node_factory);
! 3217:
! 3218: if (doc->str_factory != NULL)
! 3219: axl_string_factory_free (doc->str_factory);
! 3220:
! 3221: /* free pi targets read */
! 3222: if (doc->piTargets != NULL)
! 3223: axl_list_free (doc->piTargets);
! 3224:
! 3225: /* free enconding allocated */
! 3226: axl_free (doc->encoding);
! 3227: axl_free (doc->encoding_found);
! 3228:
! 3229: /* free allocated version value */
! 3230: axl_free (doc->version);
! 3231:
! 3232: /* free document allocated */
! 3233: axl_free (doc);
! 3234:
! 3235: return;
! 3236: }
! 3237:
! 3238: /**
! 3239: * @internal
! 3240: *
! 3241: * @brief Allows to consume comments found while reading xml files.
! 3242: *
! 3243: * @param stream The axlStream where the comment is spected to be read.
! 3244: *
! 3245: * @param error An optional axlError where problem will be reported.
! 3246: */
! 3247: axl_bool axl_doc_consume_comments (axlDoc * doc, axlStream * stream, axlError ** error)
! 3248: {
! 3249:
! 3250: axl_bool found_item;
! 3251: char * content;
! 3252: int size;
! 3253:
! 3254: /* get current parent node */
! 3255: axlNode * parent = (doc != NULL) ? axl_stack_peek (doc->parentNode) : NULL;
! 3256:
! 3257: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking for comemnts");
! 3258:
! 3259: /* know, try to read comments a process instructions. Do this
! 3260: * until both fails. Do this until one of them find
! 3261: * something. */
! 3262: do {
! 3263: /* flag the loop to end, and only end if both,
! 3264: * comments matching and PI matching fails. */
! 3265: found_item = axl_false;
! 3266:
! 3267: /* get rid from spaces */
! 3268: AXL_CONSUME_SPACES(stream);
! 3269:
! 3270: /* check for comments */
! 3271: if (axl_stream_inspect (stream, "<!--", 4) > 0) {
! 3272:
! 3273: /* get comment content */
! 3274: content = axl_stream_get_until_ref (stream, NULL, NULL, axl_true, &size, 1, "-->");
! 3275: if (content == NULL) {
! 3276: axl_error_new (-1, "detected an opened comment but not found the comment ending",
! 3277: stream, error);
! 3278: axl_stream_free (stream);
! 3279: return axl_false;
! 3280: }
! 3281:
! 3282: /* store it */
! 3283: if (parent != NULL)
! 3284: axl_node_set_comment (parent, content, size);
! 3285:
! 3286: /* flag that we have found a comment */
! 3287: found_item = axl_true;
! 3288: }
! 3289: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "now see for process instructions");
! 3290:
! 3291: /* get rid from spaces */
! 3292: AXL_CONSUME_SPACES(stream);
! 3293:
! 3294: /* check for PI, only once the xml header have been processed */
! 3295: if ((doc != NULL) && doc->headerProcess && (axl_stream_peek (stream, "<?", 2) > 0)) {
! 3296:
! 3297: if (! axl_doc_consume_pi (doc, axl_stack_peek (doc->parentNode), stream, error))
! 3298: return axl_false;
! 3299: found_item = axl_true;
! 3300: }
! 3301:
! 3302: /* do not consume spaces if an item was found because
! 3303: * it is done again at the begin of the loop */
! 3304: if (! found_item) {
! 3305: /* get rid from spaces */
! 3306: AXL_CONSUME_SPACES(stream);
! 3307: }
! 3308:
! 3309: /* check to break-the-loop */
! 3310: }while (found_item);
! 3311:
! 3312: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "comments and pi parsed");
! 3313:
! 3314: /* axl_true value */
! 3315: return axl_true;
! 3316: }
! 3317:
! 3318: /**
! 3319: * @internal
! 3320: *
! 3321: * @brie Consumes Processing intructions that are directed to the
! 3322: * application ans configuration or processing instructions.
! 3323: *
! 3324: * @param doc The document there the information will be placed.
! 3325: *
! 3326: * @param stream The stream where the data is being read.
! 3327: *
! 3328: * @param error An optional axlError where the information will be
! 3329: * reported.
! 3330: *
! 3331: * @return axl_true if not error was found, otherwise AXL_FASLSE is
! 3332: * returned.
! 3333: */
! 3334: axl_bool axl_doc_consume_pi (axlDoc * doc, axlNode * node,
! 3335: axlStream * stream, axlError ** error)
! 3336: {
! 3337: char * string_aux;
! 3338: char * string_aux2;
! 3339: int matched_chunk;
! 3340:
! 3341:
! 3342: /* check if a PI target was found */
! 3343:
! 3344: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "calling to consume PI..");
! 3345:
! 3346:
! 3347: if (axl_stream_peek (stream, "<?", 2) > 0) {
! 3348:
! 3349: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a process instruction initialization");
! 3350:
! 3351: /* found a pi target initialization */
! 3352: axl_stream_accept (stream);
! 3353:
! 3354: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_true, 3,
! 3355: " ?>", "?>", " ");
! 3356: /* check error reported */
! 3357: if (string_aux == NULL) {
! 3358: axl_error_new (-1, "Found a error while reading the PI target name", stream, error);
! 3359: axl_stream_free (stream);
! 3360: return axl_false;
! 3361: }
! 3362:
! 3363: /* check that the reserved xml word is not used for the PI target */
! 3364: string_aux2 = axl_strdup (string_aux);
! 3365: if (axl_cmp (axl_stream_to_lower (string_aux2), "xml")) {
! 3366: axl_free (string_aux2);
! 3367: axl_error_new (-1, "Using a reserved PI target name (xml), not allowed", stream, error);
! 3368: axl_stream_free (stream);
! 3369: return axl_false;
! 3370: }
! 3371: axl_free (string_aux2);
! 3372:
! 3373:
! 3374: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found PI target name: %s (terminator matched: %d)",
! 3375: string_aux, matched_chunk);
! 3376:
! 3377: /* check which was the matched string */
! 3378: if (matched_chunk == 0 || matched_chunk == 1) {
! 3379: /* seems that the PI target doesn't have more data associated, craete and return */
! 3380: if (node != NULL) {
! 3381: axl_node_add_pi_target (node, string_aux, NULL);
! 3382: return axl_true;
! 3383: }
! 3384:
! 3385: if (doc != NULL)
! 3386: axl_doc_add_pi_target (doc, string_aux, NULL);
! 3387: return axl_true;
! 3388: }
! 3389:
! 3390: /* seems that we have additional content to be read */
! 3391: if (matched_chunk == 2) {
! 3392: /* make a local copy for the PI target name
! 3393: * read previously */
! 3394: string_aux = axl_strdup (string_aux);
! 3395:
! 3396: /* get the PI content */
! 3397: string_aux2 = axl_stream_get_until (stream, NULL, NULL, axl_true, 2, " ?>", "?>");
! 3398:
! 3399: /* check error reported */
! 3400: if (string_aux2 == NULL) {
! 3401: axl_free (string_aux);
! 3402: axl_error_new (-1, "Found a error while reading the PI content", stream, error);
! 3403: axl_stream_free (stream);
! 3404: return axl_false;
! 3405: }
! 3406:
! 3407: /* check the destination for the pi */
! 3408: if (node != NULL) {
! 3409: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "PI processing finished, adding PI (node) and its content");
! 3410:
! 3411: axl_node_add_pi_target (node, string_aux, string_aux2);
! 3412: axl_free (string_aux);
! 3413: return axl_true;
! 3414: }
! 3415:
! 3416:
! 3417: if (doc != NULL) {
! 3418: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "PI processing finished, adding PI (doc) and its content");
! 3419: axl_doc_add_pi_target (doc, string_aux, string_aux2);
! 3420: axl_free (string_aux);
! 3421: return axl_true;
! 3422: }
! 3423:
! 3424: }
! 3425:
! 3426: /* check error reported */
! 3427: axl_error_new (-1, "Found a error while reading the PI target name, unable to find PI terminator ?>", stream, error);
! 3428: axl_stream_free (stream);
! 3429: return axl_false;
! 3430: }
! 3431:
! 3432:
! 3433: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "PI processing finished");
! 3434:
! 3435:
! 3436: return axl_true;
! 3437: }
! 3438:
! 3439: /**
! 3440: * @internal Function that allows to get axlFactory associated to
! 3441: * the provided document.
! 3442: *
! 3443: * @param doc The axl document that is requested to return its item
! 3444: * factory.
! 3445: *
! 3446: * @return An internal reference to the item factory. Do not dealloc.
! 3447: */
! 3448: axlFactory * axl_doc_get_item_factory (axlDoc * doc)
! 3449: {
! 3450: return doc->item_factory;
! 3451: }
! 3452:
! 3453: /**
! 3454: * @brief Allows to configure a handler that implements document
! 3455: * detection and in such cases reconfigure \ref axlStream to act an a
! 3456: * proper manner.
! 3457: *
! 3458: * @param func The function to be configured.
! 3459: *
! 3460: * @param user_data User defined data to be provide to the function.
! 3461: *
! 3462: * @return A reference to the previously configured function.
! 3463: */
! 3464: axlDocDetectCodification axl_doc_set_detect_codification_func (axlDocDetectCodification func,
! 3465: axlPointer user_data)
! 3466: {
! 3467: axlDocDetectCodification previous;
! 3468:
! 3469: /* configure handler and user defined pointer */
! 3470: previous = detect_codification_func;
! 3471: detect_codification_func = func;
! 3472: detect_codification_data = user_data;
! 3473:
! 3474: return previous;
! 3475: }
! 3476:
! 3477: /**
! 3478: * @brief Allows to configure the handler used to finally configure
! 3479: * codification to be used for a particular \ref axlStream.
! 3480: *
! 3481: * @param func The function to be called to configure codification.
! 3482: *
! 3483: * @param user_data A reference to user defined data to be passed to
! 3484: * the function.
! 3485: *
! 3486: * @return A refernece to the previous handler configured.
! 3487: */
! 3488: axlDocConfigureCodification axl_doc_set_configure_codification_func (axlDocConfigureCodification func,
! 3489: axlPointer user_data)
! 3490: {
! 3491: axlDocConfigureCodification previous;
! 3492:
! 3493: /* configure handler and user defined pointer */
! 3494: previous = configure_codification_func;
! 3495: configure_codification_func = func;
! 3496: configure_codification_data = user_data;
! 3497:
! 3498: return previous;
! 3499: }
! 3500:
! 3501: /* @} */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>