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