Add missing files
[gmpc-magnatune.git] / src / axl / axl_doc.c
blobc7e74284ef04589dc68a50746616a2f519ef1a5e
1 /*
2 * LibAxl: Another XML library
3 * Copyright (C) 2006 Advanced Software Production Line, S.L.
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.
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.
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
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.
27 * For commercial support on build XML enabled solutions contact us:
29 * Postal address:
30 * Advanced Software Production Line, S.L.
31 * C/ Dr. Michavila NÂș 14
32 * Coslada 28820 Madrid
33 * Spain
35 * Email address:
36 * info@aspl.es - http://fact.aspl.es
39 /**
40 * @internal
41 * @brief XML 1.0 Third edition grammar
43 * [1] document ::= prolog element Misc*
44 * [1] status: partially
46 * [2] Char ::= \x9 | \xA | \xD | \x20-\xD7FF | \xE000-\xFFFD | \x10000-\10FFFF
47 * [2] status: not implemented
49 * [3] S ::= ( \x20 | \x9 | \xD | \xA)
50 * [3] status: ok
52 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender
53 * [4] status: not implemented
55 * [5] Name ::= ( Letter | '_' | ':' |) ( NameChar )*
56 * [5] status: not implemented
58 * [6] Names ::= Name ( \x20 Name )*
59 * [6] status: not implemented
61 * [7] Nmtoken ::= ( NameChar ) +
62 * [7] status: not implemented
64 * [8] Nmtokens ::= Nmtoken (\x20 Nmtoken)*
65 * [8] status: not implemented
67 * [9] EntityValue ::= '"' ( [^%&"] | PEReference | Reference )* '"' | "'" ( [^%&'] ! PEReference | Reference )* "'"
68 * [9] status: not implemented
70 * [10] AttValue ::= '"' ( [^<&"] | Reference)* '"' | "'" ( [^<&'] | Reference )* "'"
71 * [10] status: not implemented
73 * [11] SystemLiteral ::= ( '"' [^"]* '"') | ("'" [^']* "'")
74 * [11] status: not implemented
76 * [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'") * "'"
77 * [12] status: not implemented
79 * [13] PubidChar ::= \x20 | \xD | \xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
80 * [13] status: not implemented
82 * [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
83 * [14] status: not implemented
85 * [15] Comments ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
86 * [15] status: not implemented
88 * [16] PI ::= '<?' PITarget (S (Char* - (Char* '?<' Char*)))? '?>'
89 * [16] status: not implemented
91 * [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') | ('L' | 'l'))
92 * [17] status: not implemented
94 * [18] CDsect ::= CDStart CData CDend
95 * [18] status: not implemented
97 * [19] CDStart ::= '<![CDATA['
98 * [19] status: not implemented
100 * [20] CData ::= (Char* - (Char* ']]>' Char*))
101 * [20] status: not implemented
103 * [21] CDEnd ::= ']]>'
104 * [21] status: not implemented
106 * [22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)?
107 * [22] status: partially
109 * [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
110 * [23] status: ok
112 * [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')
113 * [24] status: ok
115 * [25] Eq ::= S? '=' S?
116 * [25] status: ok
118 * [26] VersionNum ::= '1.0'
119 * [26] status: ok
121 * [27] Misc ::= Comment | PI | S
122 * [27] status: not implemented
124 * [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubsect ']' S?)? '>'
125 * [28] status: not implemented
127 * [28a] DeclSep ::= PEReference | S
128 * [28a] status: not implemented
130 * [28b] intSubset ::= (markupdecl | DeclSep)*
131 * [28b] status: not implemented
133 * [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment
134 * [29] status: not implemented
136 * [30] extSubset ::= TextDecl? extSubsetDecl
137 * [30] status: not implemented
139 * [31] extSubsetDecl ::= ( markupdecl | conditionalSect | DeclSep) *
140 * [31] status: not implemented
142 * [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"'" ('yes' | 'no') '"'))
143 * [32] status: ok
146 * ** productions 33 through 39 have been removed. It seems that this
147 * ** productions were supporting xml:lang stuff that is easily
148 * ** supported by using directily the xml standard rather than
149 * ** mention it as an special production inside the language.
151 * [39] element ::= EmptyElemTag | Stag content ETag
152 * [39] status: not implemented
154 * [40] Stag ::= '<' Name (S Attribute)* S? '>'
155 * [40] status: not implemented
157 * [41] Attribute ::= Name Eq AttValue
158 * [41] status: not implemented
160 * [42] ETag ::= '</' Name S? '>'
161 * [42] status: not implemented
163 * [43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)*
164 * [43] status: not implemented
166 * [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
167 * [44] status: not implemented
169 * [45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>'
170 * [45] status: not implemented
172 * [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children
173 * [46] status: not implemented
175 * [47] children ::= (choice | seq) ('?' | '*' | '+')?
176 * [47] status: not implemented
178 * [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')?
179 * [48] status: not implemented
181 * [49] choice ::= '(' S? cp ( S? '|' S? cp)+ S? ')'
182 * [49] status: not implemented
184 * [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')'
185 * [50] status: not implemented
187 * [51] Mixed ::= '(' '#PCDATA' (S? '|' S? Name)* S? ')*' | '(' S? '#PCDATA' S? ')'
188 * [51] status: not implemented
190 * [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
191 * [52] status: not implemented
193 * [53] AttDef ::= S Name S AttType S DefaultDecl
194 * [53] status: not implemented
196 * [54] AttType ::= Stringtype | TokenizedType | Enumeratedtype
197 * [54] status: not implemented
199 * [55] StringType ::= 'CDATA'
200 * [55] status: not implemented
202 * [56] tokenized ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' | 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS'
203 * [56] status: not implemented
205 * [57] EnumeratedType ::= NotationType | Enumeration
206 * [57] status: not implemented
208 * [58] NotationType ::= 'NOTATION' S '(' S? Name (S? Name (S? '|' S? Name)* S? ')'
209 * [58] status: not implemented
211 * [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
212 * [59] status: not implemented
214 * [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
215 * [60] status: not implemented
217 * [61] conditionalSect ::= includeSect | ignoreSect
218 * [61] status: not implemented
220 * [62] includeSect ::= '<![' S? 'INCLUDE S? '[' extSubsetDecl ']]>'
221 * [62] status: not implemented
223 * [63] ignoreSect ::= <![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>'
224 * [63] status: not implemented
226 * [64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore) *
227 * [64] status: not implemented
229 * [65] Ignore ::= Char * - (Char * ('<!' | ']]>') Char *)
230 * [65] status: not implemented
232 * [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-FA-F]+ ';'
233 * [66] status: not implemented
235 * [67] Reference ::= EntityRef | CharRef
236 * [67] status: not implemented
238 * [68] EntityRef ::= '&' Name ';'
239 * [68] status: not implemented
241 * [69] PEReference ::= '%' Name ';'
242 * [69] status: not implemented
244 * [70] EntityDecl ::= GEDecl | PEDecl
245 * [70] status: not implemented
247 * [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
248 * [71] status: not implemented
250 * [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
251 * [72] status: not implemented
253 * [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
254 * [73] status: not implemented
256 * [74] PEDef ::= EntityValue | ExternalID
257 * [74] status: not implemented
259 * [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral
260 * [75] status: not implemented
262 * [76] NDataDecl ::= S 'NData' S Name
263 * [76] status: not implemented
265 * [77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>'
266 * [77] status: not implemented
268 * [78] extParseEnt ::= TextDecl? content
269 * [78] status: not implemented
271 * [80] EncodingDecl ::= S 'encoding' Eq ( '"' EncName '"' | "'" EncName "'" )
272 * [80] status: ok
274 * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
275 * [81] status: ok
277 * [82] NotationalDecl ::= '<!NOTATION' S Name S (ExternalID | PublicID) S? '>'
278 * [82] status: not implemented
280 * [83] PublicID ::= 'PUBLIC' S PubidLiteral
281 * [83] status: not implemented
289 * \defgroup axl_doc_module Axl Doc: XML Documents related functions, loading XML documents and using them.
292 /**
293 * \addtogroup axl_doc_module
294 * @{
297 #include <axl.h>
298 #include <sys/stat.h>
299 #include <sys/types.h>
300 #include <fcntl.h>
303 #define LOG_DOMAIN "axl-doc"
305 struct _axlDoc {
306 /**
307 * @internal
308 * @brief A reference to the very first axlNode this axlDoc
309 * has.
311 axlNode * rootNode;
313 /**
314 * @internal
315 * The document version.
317 char * version;
319 /**
320 * @internal
321 * @brief Current xml encoding document.
323 char * encoding;
325 /**
326 * @internal
327 * @brief Current standalone configuration of the given \ref
328 * axlDoc object.
330 bool standalone;
332 /**
333 * @internal
335 * @brief Parent node stack. This stack is used to control how
336 * are nested nodes while creating/parsing xml files. This
337 * nesting allows to not only properly contruct the xml but
338 * also to check if it is well balanced.
340 axlStack * parentNode;
342 /**
343 * @internal
345 * @brief Internal list to hold all PI targets readed.
347 axlList * piTargets;
349 /**
350 * @internal
352 * @brief Instruct the \ref axlDoc instance to notify that the
353 * xml header have been defined. This helps to allow define PI
354 * instruction that are only found inside the root document,
355 * or after the xml header definition.
357 bool headerProcess;
359 /**
360 * @internal Factory to create items in a memory efficient
361 * manner.
363 axlFactory * item_factory;
365 /**
366 * @internal Factory to create nodes in a memory efficient
367 * manner.
369 axlFactory * node_factory;
371 /**
372 * @internal Factory to create nodes to hold content elements.
374 axlFactory * content_factory;
376 /**
377 * @internal Factory to create nodes to hold attribute
378 * elements.
380 axlFactory * attr_factory;
382 /**
383 * @internal Factory to alloc strings.
385 axlStrFactory * str_factory;
388 struct _axlPI {
389 /**
390 * @internal
392 * @brief PI Target name.
394 char * name;
395 /**
396 * @internal
398 * @brief PI target content.
400 char * content;
404 /**
405 * @internal
407 * @brief Creates a new empty \ref axlDoc reference.
409 * Creates the parent stack used for parsing functions.
411 * @return A newly allocated \ref axlDoc reference.
413 axlDoc * __axl_doc_new (bool create_parent_stack)
415 axlDoc * result = axl_new (axlDoc, 1);
417 /* default container lists */
418 result->parentNode = axl_stack_new (NULL);
419 result->piTargets = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_pi_free);
421 /* create factories */
422 result->item_factory = axl_item_factory_create ();
423 result->node_factory = axl_node_factory_create ();
424 result->content_factory = axl_item_content_factory_create ();
425 result->attr_factory = axl_item_attr_factory_create ();
426 result->str_factory = axl_string_factory_create ();
427 return result;
430 /**
431 * @internal
433 * Clears internal axlDoc variables used mainly to parse documents.
435 * @param doc The \ref axlDoc to clear
437 void __axl_doc_clean (axlDoc * doc)
439 /* release memory used by the parser */
440 if (doc->parentNode != NULL) {
441 axl_stack_free (doc->parentNode);
442 doc->parentNode = NULL;
445 return;
448 /**
449 * @internal Function used by the axl doc module to allocate memory to
450 * be used by the axl stream. Currently this is used to alloc xml node
451 * names and xml attribute key and its value. The rest of items are
452 * allocated by the system memory allocation.
454 * @param size The size that is required by the axl stream to be allocated.
456 * @param doc The axlDoc reference, which contains a reference to the
457 * string factory used to allocate memory.
459 * @return A reference to the allocated memory.
461 char * __axl_doc_alloc (int size, axlDoc * doc)
463 /* just return a piece of memory */
464 return axl_string_factory_alloc (doc->str_factory, size);
467 /**
468 * @internal
470 * @brief Support for parsing the xml entity header
472 * @param stream The axlStream where is expected to receive the xml
473 * header
475 * @param doc The axlDoc where the header configuration will be
476 * placed.
478 * @param error An optional error that will be filled in the case an
479 * error is found.
481 * @return It is supposed that the function return \ref true, an
482 * not deallocation is performed, and all elements were parsed
483 * properly. In the case \ref false is returned, memory associated
484 * with the given stream will be released. If the document is
485 * associated, it will also be released.
487 bool __axl_doc_parse_xml_header (axlStream * stream, axlDoc * doc, axlError ** error)
489 char * string_aux;
490 int size;
492 /* check if the user is defining the header many times */
493 if (doc->headerProcess) {
494 axl_error_new (-1, "Found a new xml header expecification. Only one header is allowed for each xml document.", stream, error);
495 axl_stream_free (stream);
496 return false;
499 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "looking for an xml header declaration");
501 /* check for initial XMLDec (production 23) */
502 if (axl_stream_inspect (stream, "<?", 2)) {
504 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found xml declaration");
506 /* check initial <?xml xml header */
507 if (! (axl_stream_inspect (stream, "xml", 3) > 0)) {
508 axl_error_new (-2, "expected initial <?xml declaration, not found.", stream, error);
509 axl_stream_free (stream);
510 return false;
513 /* consume spaces */
514 AXL_CONSUME_SPACES (stream);
516 if (! axl_stream_inspect (stream, "version=", 8)) {
517 axl_error_new (-2, "expected to find 'version=' declaration, not found.", stream, error);
518 axl_stream_free (stream);
519 return false;
522 /* consume spaces */
523 AXL_CONSUME_SPACES (stream);
525 /* check for " or ' */
526 if (! axl_stream_inspect_several (stream, 2, "\"1.0\"", 5, "'1.0'", 5)) {
527 axl_error_new (-2, "expected to find either \" or ' while procesing version number, not found.", stream, error);
528 axl_stream_free (stream);
529 return false;
532 /* check for an space */
533 AXL_CONSUME_SPACES(stream);
535 /* now check for encoding */
536 if (axl_stream_inspect_several (stream, 2, "encoding=\"", 10, "encoding='", 10) > 0) {
538 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found encoding declaration");
540 /* found encoding instruction */
541 string_aux = axl_stream_get_until (stream, NULL, NULL, true, 2, "'", "\"");
542 if (string_aux == NULL) {
543 axl_error_new (-2, "expected encoding value, not found.", stream, error);
544 axl_stream_free (stream);
545 return false;
548 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "encoding found=%s", string_aux);
550 /* set document encoding: do not allocate
551 * twice the string returned, just nullify
552 * stream internal reference and use the same
553 * reference */
554 axl_stream_nullify (stream, LAST_CHUNK);
555 doc->encoding = string_aux;
559 /* check for an space */
560 AXL_CONSUME_SPACES(stream);
562 /* get standalone configuration */
563 if ((axl_stream_inspect_several (stream, 2, "standalone=\"", 12, "standalone='", 12) > 0)) {
565 /* found standalone instruction */
566 string_aux = axl_stream_get_until (stream, NULL, NULL, true, 2, "'", "\"");
567 if (string_aux == NULL) {
568 axl_error_new (-2, "expected to receive standalone value, not found.", stream, error);
569 axl_stream_free (stream);
570 return false;
573 /* set standalone configuration */
574 if (memcmp ("yes", string_aux, 3))
575 doc->standalone = false;
576 else
577 doc->standalone = true;
580 /* check for an space */
581 AXL_CONSUME_SPACES(stream);
583 /* get the trailing header */
584 if (! (axl_stream_inspect (stream, "?>", 2) > 0)) {
585 axl_error_new (-2, "expected to receive the xml trailing header ?>, not found.", stream, error);
586 axl_stream_free (stream);
587 return false;
590 /* consume a possible comment */
591 if (! axl_doc_consume_comments (doc, stream, error))
592 return false;
595 /* now process the document type declaration */
596 if (axl_stream_inspect (stream, "<!DOCTYPE", 9) > 0) {
597 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found doc type declaration..");
598 /* found document type declaration, just skip it for
599 * now */
600 axl_stream_get_until_ref (stream, NULL, NULL, true, &size, 1, ">");
602 /* consume a possible comment */
603 if (! axl_doc_consume_comments (doc, stream, error))
604 return false;
607 /* return TRUE value */
608 return true;
611 /**
612 * @internal
614 * @brief Tries to parse the first (and compulsory) node that the xml
615 * document must have.
617 * The very minimal expresion of an xml document is the one defined by
618 * only one node, with no content and no attributes. This minimal xml
619 * could be defined as:
621 * \code
622 * <hello/>
623 * \endcode
625 * Or the other form accepted:
627 * \code
628 * <hello />
629 * \endcode
634 * @param stream The \ref axlStream object where is expected to find
635 * the xml node content.
637 * @param doc The \ref axlDoc object where node read will be placed
638 * inside.
640 * @param node The node that has been added due to calling to this
641 * function.
643 * @param error An optional error reporting variable to used to report
644 * upper level the error found.
646 * @return true if the first node was successfully parsed or
647 * false if not. If the function find something wrong the document
648 * is unrefered.
650 bool __axl_doc_parse_node (axlStream * stream, axlDoc * doc, axlNode ** calling_node, bool * is_empty, axlError ** error)
652 char * string_aux;
653 char * string_aux2;
654 axlNode * node;
655 int matched_chunk;
656 int length;
657 bool delim;
659 /* consume a possible comment */
660 if (! axl_doc_consume_comments (doc, stream, error))
661 return false;
663 /* check for initial < definition */
664 if (! (axl_stream_inspect (stream, "<", 1) > 0) && ! axl_stream_remains (stream)) {
665 /* check if we are reading the first node node */
666 if (doc->rootNode == NULL)
667 axl_error_new (-2, "expected initial < for a root node definition, not found. An xml document must have, at least, one node definition.",
668 stream, error);
669 else
670 axl_error_new (-2, "expected initial < for a node definition, not found.", stream, error);
671 axl_stream_free (stream);
672 return false;
675 /* get node name, keeping in mind the following:
676 * chunk_matched
677 * > : 0
678 * /> : 1
679 * " ": 2
681 * We also reconfigure the alloc method used by the axl stream
682 * to ensure that the module name is allocated through the
683 * string factory.
685 axl_stream_set_buffer_alloc (stream, (axlStreamAlloc)__axl_doc_alloc, doc);
686 string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, true, 2, ">", " ");
688 /* nullify */
689 axl_stream_nullify (stream, LAST_CHUNK);
691 if (AXL_IS_STR_EMPTY (string_aux)) {
692 /* use alloc though string factory */
693 axl_stream_set_buffer_alloc (stream, NULL, NULL);
695 axl_error_new (-2, "expected an non empty content for the node name not found.", stream, error);
696 axl_stream_free (stream);
697 return false;
700 /* if found a '/', it is matched as 1 */
701 if (matched_chunk == 1)
702 matched_chunk = 2;
703 else {
704 /* get the string length */
705 length = strlen (string_aux);
707 /* if matched / it means that it was readed />, remove
708 * it and all white spaces */
709 if (string_aux[length - 1] == '/') {
710 /* flag as matched /> */
711 matched_chunk = 1;
712 string_aux[length - 1] = 0;
713 } /* end if */
714 } /* end if */
716 /* create the node and associate it the node name found */
717 node = axl_node_factory_get (doc->node_factory);
718 axl_node_set_name_from_factory (node, string_aux);
720 if (doc->rootNode == NULL) {
721 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting as first node found, the root node: <%s>", string_aux);
723 doc->rootNode = node;
725 /* set the node read, the root one, to be the parent */
726 axl_stack_push (doc->parentNode, node);
728 /* configure the node */
729 axl_node_set_doc (node, doc);
731 } else {
732 /* or set the node as a child of the current parent */
733 axl_doc_set_child_current_parent (doc, node);
736 /* set the node created to the calling node, so the caller
737 * could get a reference */
738 if (calling_node != NULL)
739 *calling_node = node;
741 /* only consume white spaces if matched_chunk is 2 */
742 if (matched_chunk == 2) {
743 /* get rid from spaces */
744 AXL_CONSUME_SPACES (stream);
747 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "node found: [%s]", string_aux);
749 /* now, until the node ends, we have to find the node
750 * attributes or the node defintion end */
751 while (1) {
752 /* check if we have an attribute for the node, or the node
753 * definition have ended or the node definition is an empty
754 * one
756 * the following code that relies on matched_chunk is
757 * done due to previous call to get_until function. If
758 * the value 0 or 1 was matched, this means that we
759 * are on "/>" case */
760 if ((matched_chunk == 1) ||
761 axl_stream_inspect (stream, "/>", 2) > 0) {
762 /* use alloc though string factory */
763 axl_stream_set_buffer_alloc (stream, NULL, NULL);
765 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found end xml node definition '/>'");
767 /* empty node configuration found */
768 *is_empty = true;
769 /* axl_node_set_is_empty (node, true); */
771 /* make this node to be completed and no child
772 * could be set. */
773 axl_stack_pop (doc->parentNode);
775 /* set the parent node to receive all content
776 * found in the next parsing elements because
777 * the element found is totally empty */
778 *calling_node = axl_stack_peek (doc->parentNode);
779 return true;
782 /* check if we have an attribute for the node, or the node
783 * definition have ended or the node definition is an empty
784 * one
786 * the following code that relies on matched_chunk is
787 * done due to previous call to get_until function. If
788 * the value 2 or 3 was matched, this means that we
789 * are on ">" case */
790 if ((matched_chunk == 0) ||
791 (axl_stream_inspect (stream, ">", 1) > 0)) {
792 /* use alloc though string factory */
793 axl_stream_set_buffer_alloc (stream, NULL, NULL);
795 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found [end] xml node definition '>', for node: [%s]",
796 axl_node_get_name (node));
798 /* flag that the node is an empty definition */
799 *is_empty = false;
801 /* this node is ended */
802 return true;
805 /* get rid from spaces */
806 AXL_CONSUME_SPACES (stream);
808 /* found attribute declaration, try to read it.
810 * We also reconfigure the alloc method used by the
811 * axl stream to ensure that xml node attributes are
812 * allocated through the string factory.
814 string_aux = axl_stream_get_until (stream, NULL, NULL, true, 1, "=");
815 if (string_aux != NULL) {
817 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "attribute found: [%s]", string_aux);
819 /* nullify internal reference to the stream:
820 * now we have inside string_aux the attribute
821 * name */
822 axl_stream_nullify (stream, LAST_CHUNK);
824 /* remove next " and ' if defined */
825 /* flag the we are looking for a " */
826 delim = true;
827 if (! ((axl_stream_inspect (stream, "\"", 1) > 0))) {
828 /* seems it is not found, flag we are
829 * looking for ' */
830 delim = false;
831 if (! (axl_stream_inspect (stream, "\'", 1) > 0)) {
832 /* use alloc though string factory */
833 axl_stream_set_buffer_alloc (stream, NULL, NULL);
835 axl_error_new (-2, "Expected to find an attribute value initiator (\") or ('), every attribute value must start with them",
836 stream, error);
837 axl_stream_free (stream);
838 return false;
843 /* now get the attribute value */
844 if (delim)
845 string_aux2 = axl_stream_get_until (stream, NULL, NULL, true, 1, "\"");
846 else
847 string_aux2 = axl_stream_get_until (stream, NULL, NULL, true, 1, "'");
849 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "value found: [%s]", string_aux2);
851 /* nullify internal reference so we have the
852 * only one reference to attribute value
853 * inside string_aux2 */
854 axl_stream_nullify (stream, LAST_CHUNK);
856 /* set a new attribute for the given node */
857 axl_node_set_attribute_from_factory (doc->attr_factory, node, string_aux, string_aux2);
859 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "attribute installed..");
861 /* get rid from spaces */
862 AXL_CONSUME_SPACES (stream);
863 continue;
866 /* if reached this point, error found */
867 axl_error_new (-2, "Parse error while reading a node being opened", stream, error);
868 axl_stream_free (stream);
869 return false;
871 } /* end while */
873 /* node properly parsed */
874 return true;
877 /**
878 * @internal
879 * @brief Perform the close node operation.
882 bool __axl_doc_parse_close_node (axlStream * stream, axlDoc * doc, axlNode ** _node, axlError ** error)
884 char * string;
885 int result_size = -1;
886 axlNode * node;
888 /* get the node being closed to check to the current parent */
889 string = axl_stream_get_until_ref (stream, NULL, NULL, true, &result_size, 1, ">");
890 if (string == NULL) {
891 axl_error_new (-1, "An error was found while closing the xml node", stream, error);
892 axl_stream_free (stream);
893 return false;
896 /* check for optional white space inside the trailing result */
897 if (axl_stream_is_white_space (string + result_size - 1)) {
898 /* nullify to remove the optional white spaces */
899 string [result_size - 1] = 0;
900 } /* end if */
902 /* get current parent node */
903 node = axl_stack_peek (doc->parentNode);
904 if (node == NULL) {
905 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",
906 stream, error);
907 axl_stream_free (stream);
908 return false;
911 /* check current axl node name against closed string */
912 if (axl_cmp (axl_node_get_name (node), string)) {
914 /* ok, axl node to be closed is the one expected */
915 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "closing xml node, that matched with parent opened");
917 return true;
920 /* seems that the node being closed doesn't match */
921 __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "xml node names to be closed doesn't matched (%s != %s), current node stack status:",
922 axl_node_get_name (node), string);
924 node = axl_stack_pop (doc->parentNode);
925 while (node != NULL) {
926 __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "<%s>", axl_node_get_name (node));
927 node = axl_stack_pop (doc->parentNode);
930 axl_error_new (-1, "An error was found while closing the opened xml node, parent opened and xml node being closed doesn't match",
931 stream, error);
932 axl_stream_free (stream);
934 return false;
937 /**
938 * @internal
940 * Internal function which works as a common base for all functions
941 * that parse XML documents from different inputs.
943 axlDoc * __axl_doc_parse_common (const char * entity, int entity_size,
944 const char * file_path, int fd_handle,
945 axlError ** error)
947 axlStream * stream = NULL;
948 axlDoc * doc = NULL;
949 axlNode * node = NULL;
950 char * string = NULL;
951 int index;
952 bool is_empty = false;
954 /* create the xml stream using provided data */
955 stream = axl_stream_new (entity, entity_size, file_path, fd_handle, error);
956 axl_return_val_if_fail (stream, NULL);
958 /* create a document reference */
959 doc = __axl_doc_new (true);
960 axl_stream_link (stream, doc, (axlDestroyFunc) axl_doc_free);
962 /* parse initial xml header */
963 if (!__axl_doc_parse_xml_header (stream, doc, error))
964 return NULL;
966 /* signal that this document have processed its header */
967 doc->headerProcess = true;
969 /* parse the rest of the document, setting as parent NULL
970 * because still no parent is found. */
971 if (!__axl_doc_parse_node (stream, doc, &node, &is_empty, error))
972 return NULL;
974 /* if the node returned is not empty */
975 if (! is_empty) {
977 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "the first node ready, have content, reading it");
979 /* while the stream have data */
980 while (axl_stream_remains (stream)) {
982 /* get current index */
983 index = axl_stream_get_index (stream);
985 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "current index: %d (global: %d)", index,
986 axl_stream_get_global_index (stream));
988 /* get rid from spaces */
989 AXL_CONSUME_SPACES(stream);
991 /* consume a possible comment and process instructions */
992 if (axl_stream_peek (stream, "<?", 2) > 0 || axl_stream_peek (stream, "<!--", 4) > 0) {
993 if (! axl_doc_consume_comments (doc, stream, error))
994 return false;
996 /* continue on the next index */
997 continue;
998 } /* end if */
1000 if ((axl_stream_peek (stream, "</", 2) > 0)) {
1001 /* accept previous peek */
1002 axl_stream_accept (stream);
1004 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a node termination signal");
1006 /* seems that a node is being closed */
1007 if (! __axl_doc_parse_close_node (stream, doc, &node, error))
1008 return NULL;
1010 /* because the xml node have been
1011 * closed, make the parent to be the
1012 * previous one */
1013 axl_stack_pop (doc->parentNode);
1015 /* get the new parent */
1016 node = axl_stack_peek (doc->parentNode);
1018 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "node properly closed, current parent node stack size: %d, parent=<%s>",
1019 axl_stack_size (doc->parentNode), (node != NULL) ? axl_node_get_name (node) : "--no parent--");
1021 if (axl_stack_size (doc->parentNode) > 0)
1022 continue;
1023 break;
1024 } /* end if */
1026 /* check here for CDATA section. This is done
1027 * here because the following checking could
1028 * be mixed because they starts with the same:
1029 * < */
1030 if ((axl_stream_peek (stream, "<![CDATA[", 9) > 0)) {
1031 /* accet previous peek */
1032 axl_stream_accept (stream);
1034 /* found CDATA section, get current content */
1035 axl_stream_set_buffer_alloc (stream, (axlStreamAlloc)__axl_doc_alloc, doc);
1036 string = axl_stream_get_until (stream, NULL, NULL, true, 1, "]]>");
1037 axl_stream_set_buffer_alloc (stream, NULL, NULL);
1038 if (string == NULL) {
1039 axl_error_new (-1, "Unable to get CDATA content. There was an error.", stream, error);
1040 axl_stream_free (stream);
1041 return NULL;
1044 /* nullify internal reference to the
1045 * string_aux so we can use that
1046 * memory allocated as our
1047 * reference */
1048 axl_stream_nullify (stream, LAST_CHUNK);
1050 /* set current data */
1051 /* axl_node_set_content_ref (node, string, -1); */
1052 axl_node_set_cdata_content_from_factory (doc->content_factory, node, string, -1);
1053 continue;
1054 } /* end if */
1057 if ((axl_stream_peek (stream, "<", 1) > 0)) {
1058 /* accept previous peek */
1059 axl_stream_accept (stream);
1061 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a new node being opened");
1063 /* seems that another node is being opened */
1064 if (!__axl_doc_parse_node (stream, doc, &node, &is_empty, error))
1065 return NULL;
1067 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "finished parsing opened node, current parent=<%s>",
1068 axl_node_get_name (node));
1070 continue;
1073 /* restore index position previous to the axl
1074 * space consuming */
1075 if (axl_stream_get_index (stream) > index) {
1076 axl_stream_move (stream, index);
1079 /* found node content */
1080 axl_stream_set_buffer_alloc (stream, (axlStreamAlloc)__axl_doc_alloc, doc);
1081 string = axl_stream_get_until (stream, NULL, NULL, false, 1, "<");
1082 axl_stream_set_buffer_alloc (stream, NULL, NULL);
1084 /* check for a null content found */
1085 if (string == NULL) {
1086 axl_error_new (-1, "an error was found while reading the xml node content", stream, error);
1087 axl_stream_free (stream);
1088 return NULL;
1091 /* nullify internal stream reference to have
1092 * the unique reference */
1093 axl_stream_nullify (stream, LAST_CHUNK);
1095 /* set current data */
1096 /* axl_node_set_content_ref (node, string, -1); */
1097 axl_node_set_content_from_factory (doc->content_factory, node, string, -1);
1099 /* keep on looping */
1103 /* pop axl parent */
1104 if (! axl_stack_is_empty (doc->parentNode)) {
1106 __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL,
1107 "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)",
1108 axl_stack_size (doc->parentNode));
1110 axl_error_new (-1, "XML document is not balanced, still remains xml nodes", stream, error);
1111 axl_stream_free (stream);
1112 return NULL;
1115 /* parse complete */
1116 axl_stream_unlink (stream);
1117 axl_stream_free (stream);
1119 /* clean document internal variables */
1120 __axl_doc_clean (doc);
1122 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "xml document parse COMPLETED");
1124 return doc;
1127 /**
1128 * @brief Creates a new empty xml document, especifying options to be
1129 * used in the header.
1131 * This function allows to create the xml document representation the
1132 * must be used to add childs to it.
1134 * The following is a simple example that creates a xml document (\ref
1135 * axlDoc) with a single root node (\ref axlNode):
1136 * \code
1137 * // some variables
1138 * axlDoc * doc;
1139 * axlNode * node;
1141 * // dump content variables
1142 * char * content;
1143 * int content_size;
1145 * // create the document
1146 * doc = axl_doc_create (NULL, NULL, true);
1148 * // create the root document node
1149 * node = axl_node_create ("root-node");
1151 * // configure it as the root
1152 * axl_doc_set_root (doc, node);
1154 * // dump pretty
1155 * axl_doc_dump_pretty (doc, &content, &content_size, 4);
1157 * // print the content and free
1158 * printf ("document size=%d, content: %s\n", content_size, content);
1159 * axl_free (content);
1160 * \endcode
1162 * @param version The xml document version. This value is optional. If
1163 * NULL is used, the library will use "1.0" as version value.
1165 * @param encoding The document encoding to be used. This value is
1166 * optional, if NULL is provided, no encoding specification will be
1167 * used.
1169 * @param standalone Standalone configuration flag. By default, use
1170 * false.
1172 * @return Returns a newly allocated \ref axlDoc instance that must be
1173 * deallocated by using \ref axl_doc_free.
1175 axlDoc * axl_doc_create (const char * version,
1176 const char * encoding,
1177 bool standalone)
1179 axlDoc * doc;
1181 /* create a new reference, without creating */
1182 doc = __axl_doc_new (false);
1184 /* save the version */
1185 if (version != NULL)
1186 doc->version = axl_strdup (version);
1188 /* save encoding value */
1189 if (encoding != NULL)
1190 doc->encoding = axl_strdup (encoding);
1192 /* save standalone configuration */
1193 doc->standalone = standalone;
1195 /* return the reference created */
1196 return doc;
1199 /**
1200 * @internal Returns how many bytes will hold the document provided.
1202 * @param doc The document to measure.
1204 * @param pretty_print If pretty print is activated.
1206 * @return The number of bytes or -1 if it fails.
1208 int __axl_doc_get_flat_size_common (axlDoc * doc, bool pretty_print, int tabular)
1211 int result;
1212 axl_return_val_if_fail (doc, -1);
1214 /* count the xml header:
1216 * "<?xml version='1.0'" = 19 characters
1217 * " standalone='yes'" = 17 characters
1218 * " encoding='enc'" = 12 characters + strlen (enc)
1219 * " ?>" = 3 characters
1221 * if pretty print add: "\r\n" +2 on windows
1222 * and \n on unix.
1224 result = 22;
1226 if (pretty_print)
1227 #ifdef __AXL_OS_WIN32__
1228 result += 2;
1229 #else
1230 result += 1;
1231 #endif
1233 if (doc->standalone)
1234 result += 17;
1236 if (doc->encoding != NULL) {
1237 result += 12 + strlen (doc->encoding);
1240 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "xml document header size=%d",
1241 result);
1243 /* now, count every node that the document have */
1244 result += axl_node_get_flat_size (doc->rootNode, pretty_print, 0, tabular);
1246 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "xml document body size=%d",
1247 result);
1249 /* return current result */
1250 return result;
1253 /**
1254 * @internal
1255 * Common implementation for the dumping functions.
1257 bool __axl_doc_dump_common (axlDoc * doc, char ** content, int * size, bool pretty_print, int tabular)
1260 char * result;
1261 int index;
1263 /* perform some envrironmental checks */
1264 axl_return_val_if_fail (doc, false);
1265 axl_return_val_if_fail (content, false);
1266 axl_return_val_if_fail (size, false);
1268 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting document size..");
1270 /* get the about of memory to allocate so the whole xml
1271 * document fit in only one memory block */
1272 (* size) = __axl_doc_get_flat_size_common (doc, pretty_print, tabular);
1273 (* content) = NULL;
1275 /* check returned size */
1276 if ((* size) == -1)
1277 return false;
1279 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "document dump size: %d", *size);
1281 /* allocate the memory block required */
1282 result = axl_new (char, (*size) + 1);
1284 /* xml document header */
1285 index = 0;
1286 memcpy (result, "<?xml version='1.0' ", 20);
1287 index = 20;
1289 /* encoding declaration */
1290 if (doc->encoding) {
1291 /* initial encoding declaration */
1292 memcpy (result + index, "encoding='", 10);
1293 index += 10;
1295 /* copy encoding content */
1296 memcpy (result + index, doc->encoding, strlen (doc->encoding));
1297 index += strlen (doc->encoding);
1299 /* encoding trailing */
1300 memcpy (result + index, "' ", 2);
1301 index += 2;
1304 /* standalone attribute */
1305 if (doc->standalone) {
1306 memcpy (result + index, "standalone='yes' ", 17);
1307 index += 17;
1310 /* header trailing */
1311 memcpy (result + index, "?>", 2);
1312 index += 2;
1314 if (pretty_print) {
1315 #ifdef __AXL_OS_WIN32__
1316 memcpy (result + index, "\r\n", 2);
1317 index += 2;
1318 #else
1319 memcpy (result + index, "\n", 1);
1320 index += 1;
1321 #endif
1324 /* dump node information */
1325 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "starting dump at: %d", index);
1326 index = axl_node_dump_at (doc->rootNode, result, index, pretty_print, 0, tabular);
1328 /* check dump size */
1329 if (*size != index) {
1330 __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "internal dump error, inconsitent size: calculated=%d != returned=%d",
1331 *size, index);
1333 /* free allocated result */
1334 axl_free (result);
1336 *size = -1;
1337 *content = NULL;
1339 return false;
1342 /* set results */
1343 *content = result;
1344 *size = index;
1346 return true;
1348 /**
1349 * @brief Allows to get the xml representation for the provided \ref
1350 * axlDoc reference.
1352 * Given the \ref axlDoc reference, which represents a XML document,
1353 * this function allows to get its stringify representation.
1355 * @param doc The \ref axlDoc to stringify
1357 * @param content The reference where the result will be returned.
1359 * @param size The reference where the document content size will be
1360 * returned.
1362 * @return The function returns \ref true if the dump operation was
1363 * performed. Otherwise \ref false is returned.
1365 bool axl_doc_dump (axlDoc * doc,
1366 char ** content,
1367 int * size)
1369 /* use common implementation */
1370 return __axl_doc_dump_common (doc, content, size, false, 0);
1374 /**
1375 * @brief Allows to perform a dump operation like \ref axl_doc_dump,
1376 * but making the output to be pretty printed.
1378 * @param doc The \ref axlDoc reference to be dumped.
1380 * @param content The reference that will hold the dumped information.
1382 * @param size Result size for the dumped information.
1384 * @param tabular The tabular size basic unit used for level
1385 * tabulation. An appropiate value could be 4.
1387 * @return \ref true if the document was dumped, \ref false if
1388 * something has failed.
1390 bool axl_doc_dump_pretty (axlDoc * doc,
1391 char ** content,
1392 int * size,
1393 int tabular)
1395 /* use common implementation */
1396 return __axl_doc_dump_common (doc, content, size, true, tabular);
1399 /**
1400 * @brief Allows to dump a xml document directly to the file located
1401 * at the file path.
1403 * This function saves you the round trip to declare variables to hold
1404 * the memory, open a file, dump the content and properly close the
1405 * output file. The function works the same as \ref axl_doc_dump but
1406 * doing the extra job to transfer the xml document into a file.
1408 * See also \ref axl_doc_dump_pretty_to_file to get a version dumps
1409 * the content doing some pretty printing operations.
1411 * @param doc The document to be dumped into a file.
1413 * @param file_path The file path where the output will be placed. The
1414 * function will require to have access rights to the file (or to
1415 * create a new file if it doesnt exists). The default behaviour is to
1416 * overwrite the file found if exists. So, if you don't want to get
1417 * content overwrited, you must provide the enough code to avoid such
1418 * situations prior calling to this function.
1420 * @return \ref true if the dump operation was ok, otherwisde \ref
1421 * false is returned.
1423 bool axl_doc_dump_to_file (axlDoc * doc,
1424 char * file_path)
1426 char * content = NULL;
1427 int size = -1;
1428 int written = -1;
1429 FILE * fd = NULL;
1431 /* dump content and check result */
1432 if (! __axl_doc_dump_common (doc, &content, &size, false, 0)) {
1433 /* no dump operation done */
1434 return false;
1437 /* open the file and check */
1438 #if defined(AXL_OS_WIN32) && ! defined(__GNUC__)
1439 if (fopen_s (&fd, file_path, "w") != 0) {
1440 #else
1441 if ((fd = fopen (file_path, "w")) == NULL) {
1442 #endif
1443 /* failed to open the file to dump the content */
1444 axl_free (content);
1446 return false;
1449 /* dump the content */
1450 written = fwrite (content, 1, size, fd);
1452 /* free the content */
1453 axl_free (content);
1455 /* close file */
1456 fclose (fd);
1458 /* return if we have failed to dump all the content to the
1459 * file or not. */
1460 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "returning that the dump was: %s (written:%d == size:%d)",
1461 (written == size) ? "OK" : "FAILED",
1462 written, size);
1464 return (written == size);
1467 /**
1468 * @brief Allows to dump a xml document directly to the file located
1469 * at the file path, doing pretty printing operations.
1471 * This function saves you the round trip to declare variables to hold
1472 * the memory, open a file, dump the content and properly close the
1473 * output file. The function works the same as \ref axl_doc_dump but
1474 * doing the extra job to transfer the xml document into a file.
1476 * See also \ref axl_doc_dump_to_file to get a version dumps the
1477 * content without doing pretty printing operations.
1479 * @param doc The document to be dumped into a file.
1481 * @param file_path The file path where the output will be placed. The
1482 * function will require to have access rights to the file (or to
1483 * create a new file if it doesnt exists). The default behaviour is to
1484 * overwrite the file found if exists. So, if you don't want to get
1485 * content overwrited, you must provide the enough code to avoid such
1486 * situations prior calling to this function.
1488 * @param tabular The amount of white spaces to introduce as tabular
1489 * for each level found inside the xml.
1491 * @return \ref true if the dump operation was ok, otherwisde \ref
1492 * false is returned.
1494 bool axl_doc_dump_pretty_to_file (axlDoc * doc,
1495 char * file_path,
1496 int tabular)
1498 char * content = NULL;
1499 int size = -1;
1500 int written = -1;
1501 FILE * fd = NULL;
1503 /* dump content and check result */
1504 if (! __axl_doc_dump_common (doc, &content, &size, true, tabular)) {
1505 /* no dump operation done */
1506 return false;
1509 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "document dumped, now transfer that content to a file");
1511 /* open the file and check */
1512 #if defined(AXL_OS_WIN32) && ! defined(__GNUC__)
1513 if (fopen_s (&fd, file_path, "w") != 0) {
1514 #else
1515 if ((fd = fopen (file_path, "w")) == NULL) {
1516 #endif
1517 /* failed to open the file to dump the content */
1518 axl_free (content);
1520 return false;
1523 /* dump the content */
1524 written = fwrite (content, 1, size, fd);
1526 /* free the content */
1527 axl_free (content);
1529 /* close file */
1530 fclose (fd);
1532 /* return if we have failed to dump all the content to the
1533 * file or not. */
1534 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "returning that the dump was: %s (written:%d == size:%d)",
1535 (written == size) ? "OK" : "FAILED",
1536 written, size);
1537 return (written == size);
1540 /**
1541 * @brief Allows to get how much will take the \ref axlDoc instance
1542 * represented as an XML document in an storage device (like memory).
1544 * @param doc The \ref axlDoc reference that is being requested to return its size.
1546 * @return The size the \ref axlDoc will represent, in a
1547 * octect-counting, or -1 if fails. The function will only fail if the
1548 * provided reference is NULL.
1550 int axl_doc_get_flat_size (axlDoc * doc)
1552 /* use common implementation */
1553 return __axl_doc_get_flat_size_common (doc, false, 0);
1557 /**
1558 * @brief Parse an XML entity that is hold inside the memory pointed
1559 * by <b>entity</b> and limited by <b>entity_size</b>.
1561 * The function parses the XML document inside the memory hold inside
1562 * the given reference. The function returns an XML document,
1563 * represented by \ref axlDoc.
1565 * The function, optionall, could report error found inside the given
1566 * \ref axlError variable. In the case the function returns a NULL
1567 * value, this variable is filled containing the a textual diagnostic
1568 * error to be showed to the user interface and an error code.
1570 * Here is an example:
1571 * \code
1572 * // axl document representation
1573 * axlDoc * doc;
1574 * axlError * error;
1577 * // parse the given string
1578 * doc = axl_doc_parse ("<?xml version='1.0' ?><axldoc />", 32, &error);
1579 * if (doc == NULL) {
1580 * printf ("Error found: %s\n", axl_error_get (error));
1581 * axl_error_free (error);
1582 * return false;
1585 * // release document parsed
1586 * axl_doc_free (doc);
1587 * \endcode
1589 * @param entity The XML document to load.
1591 * @param entity_size The XML document size to load. If a <b>-1</b> is
1592 * provided, strlen function is used to figure out current document
1593 * size. This is not recomended while using xml documents that include
1594 * binary data, that maybe comes inside the CDATA section or because
1595 * an utf caracter used that includes the \\0 inside its value.
1597 * @param error Optional \ref axlError reference that will be used to
1598 * report errors found while processing xml into the \ref axlDoc
1599 * instance.
1601 * @return A newly allocated Axl Document, that must be deallocated
1602 * using \ref axl_doc_free, when no longer needed. The function could
1603 * return NULL if the document is not loaded properly.
1605 * In the case an error is found while procesing the document, error
1606 * variable will be filled, if defined. -1 will be returned is
1607 * received parameter are wrong. -2 will be returned if there some
1608 * error is found while processing the document.
1610 axlDoc * axl_doc_parse (const char * entity, int entity_size, axlError ** error)
1612 return __axl_doc_parse_common (entity, entity_size, NULL, -1, error);
1615 /**
1616 * @internal
1618 * Allows to get current file size, in bytes, of the provided file
1619 * located at the given file path.
1621 int __axl_doc_get_file_size (char * file_path)
1623 struct stat buf;
1625 axl_return_val_if_fail (file_path, -1);
1627 /* clear the memory hold */
1628 memset (&buf, 0, sizeof (struct stat));
1630 /* return current file size */
1631 if (stat ((const char *) file_path, &buf) < 0)
1632 return -1;
1634 /* return the file size */
1635 return buf.st_size;
1639 /**
1640 * @brief Allows to parse an xml document from the given file path
1641 * location.
1643 * This function works the same way like \ref axl_doc_parse and \ref
1644 * axl_doc_parse_strings, but using as an input, the selected file
1645 * provided by the path. In fact, all this function, use the same xml
1646 * parse engine. The advantage of this function is that it is more
1647 * efficient while reading huge xml files.
1649 * Here is an example:
1650 * \code
1651 * axlDoc * doc = NULL;
1652 * axlError * error = NULL;
1654 * // parse the provide file
1655 * doc = axl_doc_parse_from_file ("test.xml", &error);
1656 * if (doc == NULL) {
1657 * // check error found
1658 * printf ("ERROR: (code: %d) %s\n",
1659 * axl_error_get_code (error),
1660 * axl_error_get (error));
1661 * axl_error_free (error);
1662 * return -1;
1665 * // do some stuff with the readed document
1667 * // release it once no longer needed
1668 * axl_doc_free (doc);
1669 * \endcode
1671 * @param file_path The file path to report.
1673 * @param error The \ref axlError where errors found will be reported.
1675 * @return
1677 axlDoc * axl_doc_parse_from_file (const char * file_path,
1678 axlError ** error)
1680 return __axl_doc_parse_common (NULL, -1, file_path, -1, error);
1684 /**
1685 * @brief Allows to parse an xml document that is provided as a set of
1686 * strings ended by a NULL reference.
1688 * This function works the same way like \ref axl_doc_parse function,
1689 * but allowing to provide a set of strings. Here is an example:
1691 * \code
1692 * // a document reference
1693 * axlDoc * doc;
1695 * // note that the error is optional, and, if provided, it is not
1696 * // required to initialize it.
1697 * axlError * error;
1699 * // parse the following set of strings
1700 * doc = axl_doc_parse_strings (&error,
1701 * "<?xml version='1.0' standalone='yes' ?>",
1702 * "<complex>",
1703 * " <data>",
1704 * " <row>",
1705 * " <td>",
1706 * " <value attr='10'/>
1707 * " </td>",
1708 * " </row>",
1709 * " </data>",
1710 * "</complex>",
1711 * NULL); // last null reference
1712 * // check for an error
1713 * if (doc == NULL) {
1714 * printf ("There was an error while parsing the document: (code: %d) %s\n",
1715 * axl_error_get_code (error), axl_error_get (error));
1716 * axl_error_free (error);
1718 * \endcode
1720 * @param error An optional \ref axlError reference where a textual
1721 * diagnostic will be provided.
1723 * @return A newly created \ref axlDoc reference that must be
1724 * deallocated by using \ref axl_doc_free when no longer needed.
1726 axlDoc * axl_doc_parse_strings (axlError ** error,
1727 ...)
1729 axlDoc * doc;
1730 va_list args;
1731 char * string = NULL;
1732 char * stream = NULL;
1733 char * stream_aux = NULL;
1735 /* check incoming data */
1736 axl_return_val_if_fail (error, NULL);
1738 /* open the stdargs */
1739 va_start (args, error);
1741 while ((string = va_arg (args, char *)) != NULL) {
1742 stream_aux = stream;
1743 stream = axl_stream_concat (stream, string);
1744 if (stream_aux != NULL) {
1745 axl_free (stream_aux);
1746 stream_aux = NULL;
1750 /* close the stdargs */
1751 va_end (args);
1753 /* check that we have received, at least, an string
1754 * parseable */
1755 if (stream == NULL)
1756 return NULL;
1758 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "string to parse: %s", stream);
1760 /* parse the string */
1761 doc = axl_doc_parse (stream, -1, error);
1763 /* free the stream */
1764 axl_free (stream);
1766 return doc;
1769 /**
1770 * @internal
1772 * Internal support function which checks the provided child and its
1773 * childs are equal.
1775 bool __axl_doc_are_equal (axlNode * node, axlNode * node2, bool trimmed)
1777 int iterator;
1778 int length;
1779 int length2;
1781 axlItem * child;
1782 axlItem * child2;
1784 /* check if parent nodes are equal */
1785 if (! axl_node_are_equal (node, node2))
1786 return false;
1788 /* iterate over all childs inside the node */
1789 iterator = 0;
1790 length = axl_node_get_child_num (node);
1791 length2 = axl_node_get_child_num (node2);
1793 if (length != length2) {
1794 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "child number differs, documents aren't equal");
1795 return false;
1798 /* get the first item inside the node */
1799 child = axl_item_get_first_child (node);
1800 child2 = axl_item_get_first_child (node2);
1802 /* for each item child found in both nodes */
1803 while (child != NULL && child2 != NULL) {
1805 if (child == NULL)
1806 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "child from the first document is null..");
1808 if (child2 == NULL)
1809 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "child from the second document is null..");
1811 /* check if these nodes are also equal */
1812 if (! axl_item_are_equal (child, child2, trimmed)) {
1813 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "items aren't equal, document is not equal");
1814 return false;
1817 /* check its childs in the case the axl item is
1818 * representing an item node */
1819 if (axl_item_get_type (child) == ITEM_NODE) {
1820 /* get a reference */
1821 node = axl_item_get_data (child);
1822 node2 = axl_item_get_data (child2);
1824 if (! __axl_doc_are_equal (node, node2, trimmed)) {
1825 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "nodes <%s> and <%s> aren't equal, document is not equal",
1826 axl_node_get_name (node), axl_node_get_name (node2));
1827 return false;
1828 } /* end if */
1831 /* get a referece to the next childs to check */
1832 child = axl_item_get_next (child);
1833 child2 = axl_item_get_next (child2);
1835 } /* end while */
1837 /* the nodes recieved are equal */
1838 return (child == NULL && child2 == NULL);
1841 /**
1842 * @internal Common implementation for equal documents.
1844 bool axl_doc_are_equal_common (axlDoc * doc,
1845 axlDoc * doc2,
1846 bool trimmed)
1848 axlNode * node;
1849 axlNode * node2;
1851 axl_return_val_if_fail (doc, false);
1852 axl_return_val_if_fail (doc, false);
1854 /* first, check the document root */
1855 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking that both documents are equal");
1857 node = axl_doc_get_root (doc);
1858 if (node == NULL) {
1859 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "document(a) doesn't have document root ..");
1861 node2 = axl_doc_get_root (doc2);
1862 if (node2 == NULL) {
1863 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "document(b) doesn't have document root ..");
1866 /* call to common implemenation, activating triming */
1867 return __axl_doc_are_equal (node, node2, trimmed);
1870 /**
1871 * @brief Allows to perform a document equal check against order,
1872 * relaxing the checking done to contet found inside nodes.
1874 * This function works the same as \ref axl_doc_are_equal but
1875 * considering that two content are equal no matter which is the
1876 * number of white spaces (in the W3C, ' ', \\t, \\r and \\n) are
1877 * found starting and ending the content.
1879 * Under this approach the both document aren't exactly the same, but
1880 * usually, white spaces found starting and ending content have no
1881 * meaning for the application processing xml. In the case you want a
1882 * fully equal document checking you must \ref axl_doc_are_equal
1884 * @param doc The document to check.
1885 * @param doc2 The second document to check
1887 * @return \ref true if both documents are equal in the sense
1888 * described, otherwise \ref false is returned.
1890 bool axl_doc_are_equal_trimmed (axlDoc * doc,
1891 axlDoc * doc2)
1893 /* call to common implemenation, activating triming */
1894 return axl_doc_are_equal_common (doc, doc2, true);
1897 /**
1898 * @brief Allows to check if the provided two references represents
1899 * equivalent xml documents.
1901 * There is an alternative document checking function (\ref
1902 * axl_doc_are_equal_trimmed) which considered that content found
1903 * inside a xml node is equal if they share the same information
1904 * without considering white spaces found starting and ending both
1905 * elements being checked.
1907 * This function considers that two documents are equal only and only
1908 * if all nodes, attributes and content found is exactly, byte by
1909 * byte, as found in the other document.
1911 * @param doc The first XML document to check.
1912 * @param doc2 The second XML document to check.
1914 * @return true if both documents represents the same document,
1915 * false if not.
1917 bool axl_doc_are_equal (axlDoc * doc,
1918 axlDoc * doc2)
1920 /* call to common implemenation, activating triming */
1921 return axl_doc_are_equal_common (doc, doc2, true);
1927 /**
1928 * @brief Allows to get current root node for the given xml document,
1929 * represented by the \ref axlDoc instance.
1931 * Every XML document has a very first node, which enclose all childs
1932 * found inside the document, that is called the root node. This xml
1933 * node,
1935 * This function couldn't return NULL because every well opened xml
1936 * document, always have a root node. However, the function could
1937 * return a NULL value if the document received is a null reference.
1939 * @param doc The xml document (\ref axlDoc) where the root node will
1940 * be returned.
1942 * @return The root node (\ref axlNode) or NULL if fails.
1944 axlNode * axl_doc_get_root (axlDoc * doc)
1946 axl_return_val_if_fail (doc, NULL);
1948 /* return current root node */
1949 return doc->rootNode;
1952 /**
1953 * @internal
1955 * @brief An always return 1 to make the list to store elements append
1956 * at the end.
1958 int __axl_doc_get_are_equal (axlPointer a, axlPointer b)
1960 return 1;
1963 /**
1964 * @brief Allows to get a particular node (or list of nodes) that are
1965 * located at a selected path.
1967 * Providing a path, the function lookups for nodes stored on the
1968 * selected location inside the provided document. The path provided
1969 * doesn't follow the XPath extension.
1971 * Taking as a reference for the xml to be explained on the following
1972 * rules:
1973 * \code
1974 * <complex>
1975 * <data>
1976 * <node>
1977 * <row>10</row>
1978 * </node>
1979 * </data>
1980 * </complex>
1981 * \endcode
1984 * Here is how the path works:
1986 * - If provided a "/", the root node is returned. This is same than
1987 * provided the root node name, like "/complex", when it is expected
1988 * to find as root node &lt;complex>. However, providing a particular
1989 * node to search allows to get ensure that the root node is the one
1990 * looked up.
1992 * - To select nodes inside the first root node, in a generic way,
1993 * without providing details about the root node name, you could use
1994 * "//\*". This will provide all nodes that are found inside the root
1995 * node, whatever it is called. If it is required to get all nodes,
1996 * inside the root node, ensuring that the root one is called
1997 * "complex" you should use: "/complex/\*"
1999 * - If it is required to get a selected node inside the root node,
2000 * that is called in a particular way you can use: //data. Again, if
2001 * it is required to ensure that a particular node exists, from the
2002 * top level down the leaf node, it will required to write something
2003 * like: "/complex/data".
2005 * - Remember that is totally different to query for "/complex/data"
2006 * than "/complex/data/\*". The first one, returns the node, or nodes,
2007 * called <b>data</b> that are inside the root node called
2008 * <b>complex</b>, while the second one says: return all nodes inside
2009 * the node <b>data</b> that is inside the root node called
2010 * <b>complex</b>
2012 * Finally, keep in mind that this function only returns nodes. To get
2013 * node content, attributes or anything else, you'll have to get the
2014 * node first and then operate with it.
2018 * @param doc The \ref axlDoc reference where the lookup will be
2019 * performed.
2021 * @param path_to A path to the node (nodes) that are inside the path
2022 * especifyied.
2024 * @return A list of nodes (\ref axlNode) if the case something is
2025 * found or NULL if fails to find something at the given path. If the
2026 * path is right but no node match with it or there is no node, the
2027 * function will return NULL reference rather a list with no
2028 * nodes. Returned value must be deallocated by using \ref
2029 * axl_list_free.
2031 axlList * axl_doc_get_list (axlDoc * doc, const char * path_to)
2033 axlList * nodes;
2034 axlNode * node = NULL;
2035 int iterator = 0;
2036 char ** paths = 0;
2039 axl_return_val_if_fail (doc, NULL);
2040 axl_return_val_if_fail (path_to, NULL);
2041 axl_return_val_if_fail (path_to[0] == '/', NULL);
2043 /* create the axl list */
2044 nodes = axl_list_new (__axl_doc_get_are_equal, NULL);
2046 /* split paths */
2047 paths = axl_stream_split (path_to, 1, "/");
2048 axl_return_val_if_fail (paths, nodes);
2050 /* get a reference to the root node */
2051 node = doc->rootNode;
2053 /* basic case, check for the root node */
2054 if (strlen (paths[1]) != 0) {
2055 /* check the node is the one requested */
2056 if (! NODE_CMP_NAME (node, paths[1])) {
2057 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "requested root node = %s wasn't found, current root %s", paths[1],
2058 axl_node_get_name (doc->rootNode));
2059 axl_list_free (nodes);
2060 axl_stream_freev (paths);
2061 return NULL;
2065 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found node: %s for path=%s", paths[1], path_to);
2067 /* now the general case */
2068 iterator = 2;
2069 while ((paths[iterator] != NULL) && (strlen (paths[iterator]) > 0)) {
2070 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking path item %s", paths[iterator]);
2072 /* check that the last path is used */
2073 if (axl_cmp (paths[iterator], "*") &&
2074 (axl_stream_strv_num (paths) != iterator + 1)) {
2075 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "using the '*' at that path different from the last one.", paths[iterator]);
2076 axl_list_free (nodes);
2077 axl_stream_freev (paths);
2078 return NULL;
2081 /* get a reference to the node searched */
2082 node = axl_node_get_child_called (node, paths[iterator]);
2083 if (node == NULL) {
2084 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "the node located at %s wasn't found.", path_to);
2086 axl_list_free (nodes);
2087 axl_stream_freev (paths);
2088 return NULL;
2090 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found node: %s", paths[iterator]);
2092 /* update iterator value */
2093 iterator++;
2096 /* add the node found */
2097 axl_list_add (nodes, node);
2099 /* free paths */
2100 axl_stream_freev (paths);
2102 /* return the node found */
2103 return nodes;
2106 /**
2107 * @brief Allows to return only one node for the selected path.
2109 * This function works the same way like \ref axl_doc_get_list but
2110 * extracting the first node reference found inside the list returned
2111 * by the previous function, and returning it.
2113 * Many times, a path is selected because it is know that under that
2114 * location couldn't be more than one element. However, using \ref
2115 * axl_doc_get_list function makes this task really anoying because
2116 * you have to get the list, extract the node, from the list, and
2117 * releasing the list reference to actually get access to the node
2118 * looked up.
2120 * This function allows you to get access to the node stored on the
2121 * selected location, and, if a path that provides several nodes is
2122 * returned, only the first node found on that list is returned.
2124 * @param doc The \ref axlDoc document where the node will be returned.
2125 * @param path_to A path to the node to get.
2127 * @return A reference to a \ref axlNode instace, or NULL if
2128 * fails. Returned reference must not be deallocated.
2130 axlNode * axl_doc_get (axlDoc * doc, const char * path_to)
2132 axlList * list = NULL;
2133 axlNode * node = NULL;
2135 axl_return_val_if_fail (doc, NULL);
2136 axl_return_val_if_fail (path_to, NULL);
2138 /* get the list of nodes */
2139 list = axl_doc_get_list (doc, path_to);
2140 if (list == NULL)
2141 return NULL;
2143 /* get the node requested */
2144 if (axl_list_length (list) > 0)
2145 node = axl_list_get_nth (list, 0);
2147 axl_list_free (list);
2148 return node;
2152 /**
2153 * @brief Allows to get the node content for the final node provided
2154 * by the path.
2156 * @param doc The (\ref axlDoc) xml document where the content will be
2157 * looked up.
2159 * @param path_to Path to the node where the content will be returned.
2161 * @param content_size An optional reference to a variable to store
2162 * the size of the content returned. If the function receives NULL,
2163 * the content size will not be returned.
2165 * @return A reference to the content that the node have or NULL if
2166 * fails. The function could fail either because the node doesn't have
2167 * content or because the node identified by the path doesn't
2168 * exist. The result returned must not be deallocated.
2170 const char * axl_doc_get_content_at (axlDoc * doc,
2171 const char * path_to,
2172 int * content_size)
2175 axlNode * node;
2177 /* get the node reference */
2178 node = axl_doc_get (doc, path_to);
2179 axl_return_val_if_fail (node, NULL);
2181 /* return the content requested */
2182 return axl_node_get_content (node, content_size);
2186 /**
2187 * @brief Gets current axl Document encoding.
2189 * @param doc The document where the encoding will be retrieved.
2191 * @return A valid \ref axlDoc reference. NULL is returned in the case
2192 * a NULL \ref axlDoc reference is received. The value returned by
2193 * this function must not be deallocated.
2195 const char * axl_doc_get_encoding (axlDoc * doc)
2197 /* check parameter received */
2198 axl_return_val_if_fail (doc, NULL);
2200 return (doc->encoding != NULL) ? doc->encoding : "";
2203 /**
2204 * @brief Allows to get current standalone configuration for the given
2205 * axlDoc document.
2207 * @param doc The \ref axlDoc document where the standalone value will
2208 * be retreived.
2210 * @return \ref true if the standalone configuration, found inside
2211 * the xml header is set to TRUE. Otherwise \ref false is
2212 * returned. Keep in mind that the function will return an \ref
2213 * false value if a null reference is received.
2215 bool axl_doc_get_standalone (axlDoc * doc)
2217 axl_return_val_if_fail (doc, false);
2219 /* return current configuration */
2220 return doc->standalone;
2223 /**
2224 * @brief Allows to configure the document root for the given \ref
2225 * axlDoc instance.
2227 * Every xml document has a xml node root. This is the first node,
2228 * that holds all childs. This function allows to configure that xml
2229 * document root. See also \ref axl_doc_get_root.
2231 * Remember that previous document root will not be deallocated so,
2232 * the user space must take care about previous reference.
2234 * @param doc The \ref axlDoc where the document root will be
2235 * configured.
2237 * @param root The \ref axlNode used to configure the new document
2238 * root. The reference received can be null. In this case, it is
2239 * considered that the root node is being unset.
2241 void axl_doc_set_root (axlDoc * doc, axlNode * root)
2243 axl_return_if_fail (doc);
2245 /* set the new root */
2246 doc->rootNode = root;
2248 /* if the reference received is null, just return */
2249 if (root == NULL)
2250 return;
2252 /* set a refeference to the document root */
2253 axl_node_set_doc (root, doc);
2255 return;
2258 /**
2259 * @internal
2261 * @brief Allows to set the given axlNode to be child of the current
2262 * parent.
2264 * @param doc The \ref axlDoc reference where the \ref axlNode will be
2265 * configured.
2267 * @param node The \ref axlNode reference to set as a child for the
2268 * parent node.
2270 void axl_doc_set_child_current_parent (axlDoc * doc, axlNode * node)
2272 axlNode * parent;
2274 /* perform some environment checks */
2275 axl_return_if_fail (doc);
2276 axl_return_if_fail (node);
2278 parent = axl_stack_peek (doc->parentNode);
2279 axl_return_if_fail (parent);
2281 /* set the child for the current parent */
2282 axl_node_set_child (parent, node);
2284 /* set the new parent */
2285 axl_stack_push (doc->parentNode, node);
2287 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "pushed a new parent into the stack <%s>, current status after operatoin: %d",
2288 axl_node_get_name (node), axl_stack_size (doc->parentNode));
2290 return;
2293 /**
2294 * @internal Allows to make current \ref axlDoc to pop current parent
2295 * node, making the new parent node the previously opened.
2297 * This API is deprecated (internal function used in the past).
2299 * @param doc The \ref axlDoc where the pop operation will be
2300 * performed.
2302 void axl_doc_pop_current_parent (axlDoc * doc)
2304 return;
2307 /**
2308 * @brief Allows to configure a PI target, with its content, on the given \ref axlDoc.
2310 * A PI is a process instruction that is passed to the
2311 * application. This process instruction has a target name, which
2312 * acording to the standard is the application which should receive
2313 * the target content and an optional target content associated.
2315 * This function allows to configure (add) a new PI item inside the
2316 * given xml document (\ref axlDoc). The PI content is optional. If
2317 * provided NULL, the PI will only contain as information the PI
2318 * target.
2320 * Here is how a process instruction is used inside a xml document:
2321 * \code
2322 * <?xml version='1.0'>
2323 * <?launch "some command" ?>
2324 * <complex>
2325 * <?data "some data" ?>
2326 * <?data "more data" ?>
2327 * <data>
2328 * <row attr="20" />
2329 * </data>
2330 * </complex>
2331 * \endcode
2333 * Previous example shows how to use PI (process instructions) from
2334 * outside the xml root node (<b>complex</b>, and also how it is used
2335 * from inside a xml node definition <b>complex</b>.
2337 * As you can see, PI elements could be used as many times as you want
2338 * and places allowed to do so are just right before begining with the
2339 * root node and inside xml node definitions.
2341 * @param doc The axlDocument where the PI will be added.
2342 * @param target The PI target name.
2343 * @param content The PI content (optional value).
2345 void axl_doc_add_pi_target (axlDoc * doc,
2346 char * target,
2347 char * content)
2349 axlPI * pi;
2351 /* perform some environmental checks */
2352 axl_return_if_fail (doc);
2353 axl_return_if_fail (target);
2355 /* create the PI element */
2356 pi = axl_pi_create (target, content);
2358 /* add the PI */
2359 axl_list_add (doc->piTargets, pi);
2361 return;
2364 /**
2365 * @brief Allows to check if the provided Processing instruction
2366 * target is defined on the given xml document (\ref axlDoc).
2368 * Processing instruction are a way to configure the xml document with
2369 * processing information to instruct the application level that is
2370 * going to consume the XML information.
2372 * @param doc The document where the processing instruction will be read.
2374 * @param pi_target The process instruction name.
2376 * @return true is the processing instruction is defined,
2377 * otherwise false is returned.
2379 bool axl_doc_has_pi_target (axlDoc * doc, char * pi_target)
2381 axlPI * pi;
2382 int iterator = 0;
2383 int length = 0;
2386 axl_return_val_if_fail (doc, false);
2387 axl_return_val_if_fail (pi_target, false);
2389 /* get the length for the items inserted */
2390 length = axl_list_length (doc->piTargets);
2391 while (iterator < length) {
2392 /* for each item inserted */
2393 pi = axl_list_get_nth (doc->piTargets, iterator);
2394 /* only check the first ocurrency */
2395 if (axl_cmp (pi->name, pi_target))
2396 return true;
2398 iterator++;
2401 return false;
2404 /**
2405 * @brief Allows to get current processing instruction content.
2407 * @param doc The document where the processing instruction is placed.
2409 * @param pi_target The processing instruction target to get current
2410 * content.
2412 * @return An internal reference to the process instruction target
2413 * content. Value returned mustn't be deallocated
2415 char * axl_doc_get_pi_target_content (axlDoc * doc, char * pi_target)
2417 axlPI * pi;
2418 int iterator = 0;
2419 int length = 0;
2421 axl_return_val_if_fail (doc, NULL);
2422 axl_return_val_if_fail (pi_target, NULL);
2424 /* get the length for the items inserted */
2425 length = axl_list_length (doc->piTargets);
2426 while (iterator < length) {
2427 /* for each item inserted */
2428 pi = axl_list_get_nth (doc->piTargets, iterator);
2429 /* only check the first ocurrency */
2430 if (axl_cmp (pi->name, pi_target))
2431 return pi->content;
2433 iterator++;
2436 return NULL;
2439 /**
2440 * @brief Allows to get a list which contains \ref axlPI nodes,
2441 * representing all process instruction that the document has.
2443 * While using PI, you can use the following functions to get PI
2444 * information:
2446 * - \ref axl_doc_has_pi_target
2447 * - \ref axl_doc_get_pi_target_content
2449 * However, this function will return first ocurrency for PI found
2450 * inside the xml document. If you don't use repeated PI elements, you
2451 * won't find problems, but, if you need to iterate ever all PI found
2452 * or you are using repeated PI, you can use this function as follows
2453 * to get current pi elements:
2454 * \code
2455 * void show_all_pi (axlDoc * doc)
2457 * int iterator;
2458 * axlPI * pi;
2459 * axlList * PIs;
2461 * // get all PI target that the document has
2462 * PIs = axl_doc_get_pi_target_list (doc);
2463 * iterator = 0;
2465 * while (iterator < axl_list_length (PIs)) {
2466 * // get next pi stored
2467 * pi = axl_list_get_nth (PIs, iterator);
2469 * // do some stuff
2470 * printf ("PI found target name=%s, content=%s\n",
2471 * axl_pi_get_name (pi),
2472 * axl_pi_get_content (pi));
2474 * // update the iterator
2475 * iterator++;
2477 * return;
2479 * \endcode
2481 * @param doc The xml document (\ref axlDoc) where the process
2482 * instruction will be returned.
2484 * @return A reference to the list of processing instruction that the
2485 * xml document (\ref axlDoc) has.
2487 axlList * axl_doc_get_pi_target_list (axlDoc * doc)
2489 axl_return_val_if_fail (doc, NULL);
2491 return doc->piTargets;
2494 /**
2496 * @brief Allows to create a new \ref axlPI element.
2498 * @param name The PI target name.
2499 * @param content The PI content.
2501 * @return A newly allocated \ref axlPI element.
2503 axlPI * axl_pi_create (char * name, char * content)
2505 axlPI * pi;
2507 /* create the PI */
2508 pi = axl_new (axlPI, 1);
2509 pi->name = axl_strdup (name);
2511 /* copy the content if defined */
2512 if (content != NULL)
2513 pi->content = axl_strdup (content);
2515 return pi;
2518 /**
2519 * @brief Returns a newly allocated copy representing the same value
2520 * as the provided \ref axlPI reference.
2522 * @param pi The pi reference received.
2524 * @return A reference to the \ref axlPI element or null if it fails.
2526 axlPI * axl_pi_copy (axlPI * pi)
2528 axlPI * _pi;
2530 axl_return_val_if_fail (pi, NULL);
2532 /* create the PI */
2533 _pi = axl_new (axlPI, 1);
2534 _pi->name = axl_strdup (pi->name);
2536 /* copy the content if defined */
2537 if (pi->content != NULL)
2538 _pi->content = axl_strdup (pi->content);
2540 return _pi;
2543 /**
2544 * @brief Allows to check if both provided process instructions are
2545 * equal.
2547 * @param pi First process instruction to check.
2548 * @param pi2 Second process instructions to check.
2550 * @return \ref true if both process instructions are equal. If some
2551 * of parameters received are NULL, the function will always return
2552 * \ref false.
2554 bool axl_pi_are_equal (axlPI * pi,
2555 axlPI * pi2)
2557 /* basic null reference check */
2558 axl_return_val_if_fail (pi, false);
2559 axl_return_val_if_fail (pi2, false);
2561 /* check internal data */
2562 if (! axl_cmp (pi->name, pi2->name))
2563 return false;
2565 /* final check, both content must be equal */
2566 return axl_cmp (pi->content, pi2->content);
2569 /**
2570 * @brief Allows to get current pi name from the given \ref axlPI
2571 * reference.
2573 * @param pi The PI reference where the name will returned.
2575 * @return A string representing the PI name. Returned value shouldn't
2576 * be deallocated.
2578 char * axl_pi_get_name (axlPI * pi)
2580 axl_return_val_if_fail (pi, NULL);
2582 /* return current PI name */
2583 return pi->name;
2586 /**
2587 * @brief Allows to get current optinal PI content.
2589 * @param pi The PI where the content will be returned.
2591 * @return A string representing the PI content. This value could be
2592 * NULL because it is optional to be defined. Returned value must not
2593 * be deallocated.
2595 char * axl_pi_get_content (axlPI * pi)
2597 axl_return_val_if_fail (pi, NULL);
2599 /* return current PI content */
2600 return pi->content;
2603 /**
2604 * @brief Deallocates memory used by the \ref axlPI target.
2606 * @param pi The target to destroy.
2608 void axl_pi_free (axlPI * pi)
2610 if (pi == NULL)
2611 return;
2613 /* free PI target */
2614 axl_free (pi->name);
2615 axl_free (pi->content);
2616 axl_free (pi);
2617 return;
2620 /**
2621 * @internal Allows to get the number of bytes that the process
2622 * instruction will take.
2624 * @param pi The process instruction.
2626 * @return A size or -1 if it fails.
2628 int axl_pi_get_size (axlPI * pi)
2630 axl_return_val_if_fail (pi, -1);
2632 /* <?name content?> */
2633 return strlen (pi->name) + strlen (pi->content) + 5;
2636 /**
2637 * @internal
2639 * Common implementation for \ref axl_doc_iterate and \ref axl_doc_iterate2.
2641 bool __axl_doc_iterate_common (axlDoc * doc,
2642 axlNode * root,
2643 AxlIterationMode mode,
2644 axlIterationFunc func,
2645 axlIterationFunc2 func2,
2646 axlPointer ptr,
2647 axlPointer ptr2)
2649 int iterator;
2650 bool was_removed = false;
2652 axlNode * node;
2653 axlNode * nodeAux;
2655 axlList * pending;
2657 /* check first node */
2658 axl_return_val_if_fail (root, false);
2660 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "notifying first node inside the iteration");
2662 /* notify first node found we pass in a null value because it
2663 doesn't have a * parent. */
2664 if (func && ! func (root, NULL, doc, &was_removed, ptr))
2665 return false;
2666 if (func2 && ! func2 (root, NULL, doc, &was_removed, ptr, ptr2))
2667 return false;
2669 /* if the root node was removed, don't continue */
2670 if (was_removed)
2671 return false;
2673 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "continuing with next nodes");
2675 /* get childs */
2676 pending = axl_node_get_childs (root);
2678 /* for each pending node */
2679 while (axl_list_length (pending) > 0) {
2681 /* get the first node inside the pending list */
2682 node = axl_list_get_first (pending);
2684 /* remove the node node from the pending list and add
2685 * all childs */
2686 axl_list_remove_first (pending);
2688 /* notify node found */
2689 was_removed = false;
2690 if (func && ! func (node, axl_node_get_parent (node), doc, &was_removed, ptr)) {
2691 axl_list_free (pending);
2692 return false;
2695 /* notify node found */
2696 if (func2 && ! func2 (node, axl_node_get_parent (node), doc, &was_removed, ptr, ptr2)) {
2697 axl_list_free (pending);
2698 return false;
2701 /* add all its childs */
2702 if (!was_removed && axl_node_have_childs (node)) {
2704 /* get first child */
2705 nodeAux = axl_node_get_first_child (node);
2707 /* get all items of the next level and add
2708 * them properly */
2709 iterator = 0;
2710 while (nodeAux != NULL) {
2712 /* add to the pending list */
2713 switch (mode) {
2714 case DEEP_ITERATION:
2715 /* add the element */
2716 axl_list_add_at (pending, nodeAux, iterator);
2718 /* update the iterator */
2719 iterator++;
2720 break;
2722 case WIDE_ITERATION:
2723 /* add to the pending list */
2724 axl_list_add (pending, nodeAux);
2725 break;
2726 } /* end switch */
2729 /* update to the next */
2730 nodeAux = axl_node_get_next (nodeAux);
2732 } /* end while */
2733 } /* end if */
2736 } /* end while */
2738 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "terminated iteration process, deallocating list: %d",
2739 axl_list_length (pending));
2741 axl_list_free (pending);
2743 /* iteration performed completely */
2744 return true;
2747 /**
2748 * @brief Allows to perform an iteration over the documented provided,
2749 * visiting all nodes inside it.
2751 * The function allows to configure the iteration module using \ref
2752 * AxlIterationMode (mode variable) and providing a callback function
2753 * that will be called for each node found (\ref axlIterationFunc).
2755 * The function, optionall, allows to provide a user pointer that will
2756 * be passed to the callback function. See documentation for the
2757 * callback and the iteration module for more details.
2759 * Here is an example:
2760 * \code
2761 * void perform_iteration (axlDoc * doc)
2763 * // call to iterate
2764 * axl_doc_iterate (doc,
2765 * // visit childs before brothers
2766 * DEEP_ITERATION,
2767 * // the func to execute: see below
2768 * show_node_found,
2769 * // optional user pointer
2770 * NULL);
2773 * bool show_node_found (axlNode * node, axlNode * parent,
2774 * axlDoc * doc, bool * was_removed,
2775 * axlPointer ptr)
2777 * // Show node found
2778 * printf ("Node found: %s\n", axl_node_get_name (node));
2780 * // If the node is removed inside the iteration
2781 * // using axl_node_remove or axl_node_replace, you
2782 * // must notify the iteration system using was_removed
2783 * // as follow: (* was_removed) = true;
2784 * //
2785 * // If you don't remove anything, you don't need to do
2786 * // anything especial with was_removed.
2788 * // don't stop iteration
2789 * return true;
2791 * \endcode
2793 * See also alternative APIs:
2795 * - \ref axl_doc_iterate_full
2796 * - \ref axl_doc_iterate_full_from
2798 * @param doc The xml document that will be iterated.
2800 * @param mode The iterarion type to be performed.
2802 * @param func The function to be called for each node found.
2804 * @param ptr An user defined pointer that will be passed to the
2805 * callback function.
2807 * @return The function returns \ref true if the iteration was
2808 * performed over all nodes or \ref false it it was stoped by the
2809 * iteration function (by returning \ref false to stop the
2810 * iteration). The function also false if the parameters provided doc
2811 * or func are not defined.
2813 bool axl_doc_iterate (axlDoc * doc,
2814 AxlIterationMode mode,
2815 axlIterationFunc func,
2816 axlPointer ptr)
2818 axlNode * root;
2820 /* check basic data */
2821 axl_return_val_if_fail (doc, false);
2822 axl_return_val_if_fail (func, false);
2824 /* get the root node where the iteration will start */
2825 root = axl_doc_get_root (doc);
2827 /* call to common implementation */
2828 return __axl_doc_iterate_common (doc, root, mode, func, NULL, ptr, NULL);
2833 /**
2834 * @brief Allows to perform an iteration over the documented provided,
2835 * visiting all nodes inside it (with two user defined pointers support).
2837 * The function allows to configure the iteration module using \ref
2838 * AxlIterationMode (mode variable) and providing a callback function
2839 * that will be called for each node found (\ref axlIterationFunc).
2841 * The function, optionall, allows to provide two user pointer that will
2842 * be passed to the callback function. See documentation for the
2843 * callback and the iteration module for more details. See also \ref axl_doc_iterate.
2846 * @param doc The xml document that will be iterated.
2848 * @param mode The iterarion type to be performed.
2850 * @param func The function to be called for each node found.
2852 * @param ptr An user defined pointer that will be passed to the
2853 * callback function.
2855 * @param ptr2 Second user defined pointer that will be passed to the
2856 * callback function.
2859 * @return The function returns \ref true if the iteration was
2860 * performed over all nodes or \ref false it it was stoped by the
2861 * iteration function (by returning \ref false to stop the
2862 * iteration). The function also false if the parameters provided doc
2863 * or func are not defined.
2865 bool axl_doc_iterate_full (axlDoc * doc,
2866 AxlIterationMode mode,
2867 axlIterationFunc2 func,
2868 axlPointer ptr,
2869 axlPointer ptr2)
2872 axlNode * root;
2874 /* check basic data */
2875 axl_return_val_if_fail (doc, false);
2876 axl_return_val_if_fail (func, false);
2878 /* get the root node where the iteration will start */
2879 root = axl_doc_get_root (doc);
2881 /* call to common implementation */
2882 return __axl_doc_iterate_common (doc, root, mode, NULL, func, ptr, ptr2);
2885 /**
2886 * @brief Allows to perform a iteration operation but configuring
2887 * where to start, discarding the rest content.
2889 * See \ref axl_doc_iterate and \ref axl_doc_iterate_full for more
2890 * details. This function works the same like previous but, unlike
2891 * previous, this function doesn't use the default starting point: the
2892 * root node (\ref axl_doc_get_root). The function allows to configure
2893 * the node where to start the iteration operation.
2895 * This function is equivalent to \ref axl_doc_iterate_full calling if
2896 * it use the root node document as value for <b>starting_from</b>.
2898 * @param doc The xml document that will be iterated.
2900 * @param starting_from The \ref axlNode where the operation will
2901 * start, discarding all content from ascending nodes, previous
2902 * siblings and following sibligins. From a iteration perspective, the
2903 * iteration opeeration.
2905 * @param mode The iterarion type to be performed.
2907 * @param func The function to be called for each node found.
2909 * @param ptr An user defined pointer that will be passed to the
2910 * callback function.
2912 * @param ptr2 Second user defined pointer that will be passed to the
2913 * callback function.
2916 * @return The function returns \ref true if the iteration was
2917 * performed over all nodes or \ref false it it was stoped by the
2918 * iteration function (by returning \ref false to stop the
2919 * iteration). The function also false if the parameters provided doc
2920 * or func are not defined.
2922 bool axl_doc_iterate_full_from (axlDoc * doc,
2923 axlNode * starting_from,
2924 AxlIterationMode mode,
2925 axlIterationFunc2 func,
2926 axlPointer ptr,
2927 axlPointer ptr2)
2929 /* check basic data */
2930 axl_return_val_if_fail (doc, false);
2931 axl_return_val_if_fail (func, false);
2933 /* call to common implementation */
2934 return __axl_doc_iterate_common (doc, starting_from, mode, NULL, func, ptr, ptr2);
2938 /**
2939 * @brief Releases memory allocated by the \ref axlDoc object.
2941 * @param doc The \ref axlDoc object to unref.
2943 void axl_doc_free (axlDoc * doc)
2945 /* do not complain if an axlDoc reference is received */
2946 if (doc == NULL)
2947 return;
2949 /* free first root node */
2950 if (doc->rootNode != NULL)
2951 axl_node_free (doc->rootNode);
2953 /* free node hierarchy */
2954 if (doc->parentNode != NULL)
2955 axl_stack_free (doc->parentNode);
2957 /* free item factory */
2958 if (doc->item_factory != NULL)
2959 axl_factory_free (doc->item_factory);
2961 /* free content holding nodes factory */
2962 if (doc->content_factory != NULL)
2963 axl_factory_free (doc->content_factory);
2965 /* free attribute holding factory */
2966 if (doc->attr_factory != NULL)
2967 axl_factory_free (doc->attr_factory);
2969 /* free node factory */
2970 if (doc->node_factory != NULL)
2971 axl_factory_free (doc->node_factory);
2973 if (doc->str_factory != NULL)
2974 axl_string_factory_free (doc->str_factory);
2976 /* free pi targets read */
2977 if (doc->piTargets != NULL)
2978 axl_list_free (doc->piTargets);
2980 /* free enconding allocated */
2981 axl_free (doc->encoding);
2983 /* free allocated version value */
2984 axl_free (doc->version);
2986 /* free document allocated */
2987 axl_free (doc);
2989 return;
2992 /**
2993 * @internal
2995 * @brief Allows to consume comments found while reading xml files.
2997 * @param stream The axlStream where the comment is spected to be read.
2999 * @param error An optional axlError where problem will be reported.
3001 bool axl_doc_consume_comments (axlDoc * doc, axlStream * stream, axlError ** error)
3004 bool found_item;
3005 char * content;
3006 int size;
3008 /* get current parent node */
3009 axlNode * parent = (doc != NULL) ? axl_stack_peek (doc->parentNode) : NULL;
3011 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking for comemnts");
3013 /* know, try to read comments a process instructions. Do this
3014 * until both fails. Do this until one of them find
3015 * something. */
3016 do {
3017 /* flag the loop to end, and only end if both,
3018 * comments matching and PI matching fails. */
3019 found_item = false;
3021 /* get rid from spaces */
3022 AXL_CONSUME_SPACES(stream);
3024 /* check for comments */
3025 if (axl_stream_inspect (stream, "<!--", 4) > 0) {
3027 /* get comment content */
3028 content = axl_stream_get_until_ref (stream, NULL, NULL, true, &size, 1, "-->");
3029 if (content == NULL) {
3030 axl_error_new (-1, "detected an opened comment but not found the comment ending",
3031 stream, error);
3032 axl_stream_free (stream);
3033 return false;
3036 /* store it */
3037 if (parent != NULL)
3038 axl_node_set_comment (parent, content, size);
3040 /* flag that we have found a comment */
3041 found_item = true;
3043 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "now see for process instructions");
3045 /* get rid from spaces */
3046 AXL_CONSUME_SPACES(stream);
3048 /* check for PI, only once the xml header have been processed */
3049 if ((doc != NULL) && doc->headerProcess && (axl_stream_peek (stream, "<?", 2) > 0)) {
3051 if (! axl_doc_consume_pi (doc, axl_stack_peek (doc->parentNode), stream, error))
3052 return false;
3053 found_item = true;
3056 /* do not consume spaces if an item was found because
3057 * it is done again at the begin of the loop */
3058 if (! found_item) {
3059 /* get rid from spaces */
3060 AXL_CONSUME_SPACES(stream);
3063 /* check to break-the-loop */
3064 }while (found_item);
3066 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "comments and pi parsed");
3068 /* true value */
3069 return true;
3072 /**
3073 * @internal
3075 * @brie Consumes Processing intructions that are directed to the
3076 * application ans configuration or processing instructions.
3078 * @param doc The document there the information will be placed.
3080 * @param stream The stream where the data is being read.
3082 * @param error An optional axlError where the information will be
3083 * reported.
3085 * @return true if not error was found, otherwise AXL_FASLSE is
3086 * returned.
3088 bool axl_doc_consume_pi (axlDoc * doc, axlNode * node,
3089 axlStream * stream, axlError ** error)
3091 char * string_aux;
3092 char * string_aux2;
3093 int matched_chunk;
3096 /* check if a PI target was found */
3098 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "calling to consume PI..");
3101 if (axl_stream_peek (stream, "<?", 2) > 0) {
3103 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a process instruction initialization");
3105 /* found a pi target initialization */
3106 axl_stream_accept (stream);
3108 string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, true, 3,
3109 " ?>", "?>", " ");
3110 /* check error reported */
3111 if (string_aux == NULL) {
3112 axl_error_new (-1, "Found a error while reading the PI target name", stream, error);
3113 axl_stream_free (stream);
3114 return false;
3117 /* check that the reserved xml word is not used for the PI target */
3118 string_aux2 = axl_strdup (string_aux);
3119 if (axl_cmp (axl_stream_to_lower (string_aux2), "xml")) {
3120 axl_free (string_aux2);
3121 axl_error_new (-1, "Using a reserved PI target name (xml), not allowed", stream, error);
3122 axl_stream_free (stream);
3123 return false;
3125 axl_free (string_aux2);
3128 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found PI target name: %s (terminator matched: %d)",
3129 string_aux, matched_chunk);
3131 /* check which was the matched string */
3132 if (matched_chunk == 0 || matched_chunk == 1) {
3133 /* seems that the PI target doesn't have more data associated, craete and return */
3134 if (node != NULL) {
3135 axl_node_add_pi_target (node, string_aux, NULL);
3136 return true;
3139 if (doc != NULL)
3140 axl_doc_add_pi_target (doc, string_aux, NULL);
3141 return true;
3144 /* seems that we have additional content to be read */
3145 if (matched_chunk == 2) {
3146 /* make a local copy for the PI target name
3147 * read previously */
3148 string_aux = axl_strdup (string_aux);
3150 /* get the PI content */
3151 string_aux2 = axl_stream_get_until (stream, NULL, NULL, true, 2, " ?>", "?>");
3153 /* check error reported */
3154 if (string_aux2 == NULL) {
3155 axl_free (string_aux);
3156 axl_error_new (-1, "Found a error while reading the PI content", stream, error);
3157 axl_stream_free (stream);
3158 return false;
3161 /* check the destination for the pi */
3162 if (node != NULL) {
3163 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "PI processing finished, adding PI (node) and its content");
3165 axl_node_add_pi_target (node, string_aux, string_aux2);
3166 axl_free (string_aux);
3167 return true;
3171 if (doc != NULL) {
3172 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "PI processing finished, adding PI (doc) and its content");
3173 axl_doc_add_pi_target (doc, string_aux, string_aux2);
3174 axl_free (string_aux);
3175 return true;
3180 /* check error reported */
3181 axl_error_new (-1, "Found a error while reading the PI target name, unable to find PI terminator ?>", stream, error);
3182 axl_stream_free (stream);
3183 return false;
3187 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "PI processing finished");
3190 return true;
3193 /**
3194 * @internal Function that allows to get axlFactory associated to
3195 * the provided document.
3197 * @param doc The axl document that is requested to return its item
3198 * factory.
3200 * @return An internal reference to the item factory. Do not dealloc.
3202 axlFactory * axl_doc_get_item_factory (axlDoc * doc)
3204 return doc->item_factory;
3207 /* @} */