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