1/*
2 * "Canonical XML" implementation
3 * http://www.w3.org/TR/xml-c14n
4 *
5 * "Exclusive XML Canonicalization" implementation
6 * http://www.w3.org/TR/xml-exc-c14n
7 *
8 * See Copyright for the status of this software.
9 *
10 * Author: Aleksey Sanin <aleksey@aleksey.com>
11 */
12#define IN_LIBXML
13#include "libxml.h"
14#ifdef LIBXML_C14N_ENABLED
15#ifdef LIBXML_OUTPUT_ENABLED
16
17#ifdef HAVE_STDLIB_H
18#include <stdlib.h>
19#endif
20#include <string.h>
21
22#include <libxml/tree.h>
23#include <libxml/parser.h>
24#include <libxml/uri.h>
25#include <libxml/xmlerror.h>
26#include <libxml/globals.h>
27#include <libxml/xpathInternals.h>
28#include <libxml/c14n.h>
29
30/************************************************************************
31 *									*
32 *		Some declaration better left private ATM		*
33 *									*
34 ************************************************************************/
35
36typedef enum {
37    XMLC14N_BEFORE_DOCUMENT_ELEMENT = 0,
38    XMLC14N_INSIDE_DOCUMENT_ELEMENT = 1,
39    XMLC14N_AFTER_DOCUMENT_ELEMENT = 2
40} xmlC14NPosition;
41
42typedef struct _xmlC14NVisibleNsStack {
43    int nsCurEnd;           /* number of nodes in the set */
44    int nsPrevStart;        /* the begginning of the stack for previous visible node */
45    int nsPrevEnd;          /* the end of the stack for previous visible node */
46    int nsMax;              /* size of the array as allocated */
47    xmlNsPtr 	*nsTab;	    /* array of ns in no particular order */
48    xmlNodePtr	*nodeTab;   /* array of nodes in no particular order */
49} xmlC14NVisibleNsStack, *xmlC14NVisibleNsStackPtr;
50
51typedef struct _xmlC14NCtx {
52    /* input parameters */
53    xmlDocPtr doc;
54    xmlC14NIsVisibleCallback is_visible_callback;
55    void* user_data;
56    int with_comments;
57    xmlOutputBufferPtr buf;
58
59    /* position in the XML document */
60    xmlC14NPosition pos;
61    int parent_is_doc;
62    xmlC14NVisibleNsStackPtr ns_rendered;
63
64    /* exclusive canonicalization */
65    int exclusive;
66    xmlChar **inclusive_ns_prefixes;
67
68    /* error number */
69    int error;
70} xmlC14NCtx, *xmlC14NCtxPtr;
71
72static xmlC14NVisibleNsStackPtr	xmlC14NVisibleNsStackCreate	(void);
73static void     xmlC14NVisibleNsStackDestroy	(xmlC14NVisibleNsStackPtr cur);
74static void     xmlC14NVisibleNsStackAdd	    (xmlC14NVisibleNsStackPtr cur,
75                                                 xmlNsPtr ns,
76                                                 xmlNodePtr node);
77static void 			xmlC14NVisibleNsStackSave	(xmlC14NVisibleNsStackPtr cur,
78								 xmlC14NVisibleNsStackPtr state);
79static void 			xmlC14NVisibleNsStackRestore	(xmlC14NVisibleNsStackPtr cur,
80								 xmlC14NVisibleNsStackPtr state);
81static void 			xmlC14NVisibleNsStackShift	(xmlC14NVisibleNsStackPtr cur);
82static int			xmlC14NVisibleNsStackFind	(xmlC14NVisibleNsStackPtr cur,
83								 xmlNsPtr ns);
84static int			xmlExcC14NVisibleNsStackFind	(xmlC14NVisibleNsStackPtr cur,
85								 xmlNsPtr ns,
86								 xmlC14NCtxPtr ctx);
87
88static int			xmlC14NIsNodeInNodeset		(xmlNodeSetPtr nodes,
89								 xmlNodePtr node,
90								 xmlNodePtr parent);
91
92
93
94static int xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur);
95static int xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur);
96typedef enum {
97    XMLC14N_NORMALIZE_ATTR = 0,
98    XMLC14N_NORMALIZE_COMMENT = 1,
99    XMLC14N_NORMALIZE_PI = 2,
100    XMLC14N_NORMALIZE_TEXT = 3
101} xmlC14NNormalizationMode;
102
103static xmlChar *xmlC11NNormalizeString(const xmlChar * input,
104                                       xmlC14NNormalizationMode mode);
105
106#define 	xmlC11NNormalizeAttr( a ) \
107    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
108#define 	xmlC11NNormalizeComment( a ) \
109    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
110#define 	xmlC11NNormalizePI( a )	\
111    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
112#define 	xmlC11NNormalizeText( a ) \
113    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
114
115#define 	xmlC14NIsVisible( ctx, node, parent ) \
116     (((ctx)->is_visible_callback != NULL) ? \
117	(ctx)->is_visible_callback((ctx)->user_data, \
118		(xmlNodePtr)(node), (xmlNodePtr)(parent)) : 1)
119
120/************************************************************************
121 *									*
122 * 		Some factorized error routines				*
123 *									*
124 ************************************************************************/
125
126/**
127 * xmlC14NErrMemory:
128 * @extra:  extra informations
129 *
130 * Handle a redefinition of memory error
131 */
132static void
133xmlC14NErrMemory(const char *extra)
134{
135    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
136		    XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0, extra,
137		    NULL, NULL, 0, 0,
138		    "Memory allocation failed : %s\n", extra);
139}
140
141/**
142 * xmlC14NErrParam:
143 * @extra:  extra informations
144 *
145 * Handle a redefinition of param error
146 */
147static void
148xmlC14NErrParam(const char *extra)
149{
150    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
151		    XML_ERR_INTERNAL_ERROR, XML_ERR_ERROR, NULL, 0, extra,
152		    NULL, NULL, 0, 0,
153		    "Invalid parameter : %s\n", extra);
154}
155
156/**
157 * xmlC14NErrInternal:
158 * @extra:  extra informations
159 *
160 * Handle a redefinition of internal error
161 */
162static void
163xmlC14NErrInternal(const char *extra)
164{
165    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
166		    XML_ERR_INTERNAL_ERROR, XML_ERR_ERROR, NULL, 0, extra,
167		    NULL, NULL, 0, 0,
168		    "Internal error : %s\n", extra);
169}
170
171/**
172 * xmlC14NErrInvalidNode:
173 * @extra:  extra informations
174 *
175 * Handle a redefinition of invalid node error
176 */
177static void
178xmlC14NErrInvalidNode(const char *node_type, const char *extra)
179{
180    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
181		    XML_C14N_INVALID_NODE, XML_ERR_ERROR, NULL, 0, extra,
182		    NULL, NULL, 0, 0,
183		    "Node %s is invalid here : %s\n", node_type, extra);
184}
185
186/**
187 * xmlC14NErrUnknownNode:
188 * @extra:  extra informations
189 *
190 * Handle a redefinition of unknown node error
191 */
192static void
193xmlC14NErrUnknownNode(int node_type, const char *extra)
194{
195    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
196		    XML_C14N_UNKNOW_NODE, XML_ERR_ERROR, NULL, 0, extra,
197		    NULL, NULL, 0, 0,
198		    "Unknown node type %d found : %s\n", node_type, extra);
199}
200
201/**
202 * xmlC14NErrRelativeNamespace:
203 * @extra:  extra informations
204 *
205 * Handle a redefinition of relative namespace error
206 */
207static void
208xmlC14NErrRelativeNamespace(const char *ns_uri)
209{
210    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
211		    XML_C14N_RELATIVE_NAMESPACE, XML_ERR_ERROR, NULL, 0, NULL,
212		    NULL, NULL, 0, 0,
213		    "Relative namespace UR is invalid here : %s\n", ns_uri);
214}
215
216
217
218/**
219 * xmlC14NErr:
220 * @ctxt:  a C14N evaluation context
221 * @node:  the context node
222 * @error:  the erorr code
223 * @msg:  the message
224 * @extra:  extra informations
225 *
226 * Handle a redefinition of attribute error
227 */
228static void
229xmlC14NErr(xmlC14NCtxPtr ctxt, xmlNodePtr node, int error,
230           const char * msg)
231{
232    if (ctxt != NULL)
233        ctxt->error = error;
234    __xmlRaiseError(NULL, NULL, NULL,
235		    ctxt, node, XML_FROM_C14N, error,
236		    XML_ERR_ERROR, NULL, 0,
237		    NULL, NULL, NULL, 0, 0, msg);
238}
239
240/************************************************************************
241 *									*
242 *		The implementation internals				*
243 *									*
244 ************************************************************************/
245#define XML_NAMESPACES_DEFAULT		16
246
247static int
248xmlC14NIsNodeInNodeset(xmlNodeSetPtr nodes, xmlNodePtr node, xmlNodePtr parent) {
249    if((nodes != NULL) && (node != NULL)) {
250	if(node->type != XML_NAMESPACE_DECL) {
251	    return(xmlXPathNodeSetContains(nodes, node));
252	} else {
253	    xmlNs ns;
254
255	    memcpy(&ns, node, sizeof(ns));
256
257	    /* this is a libxml hack! check xpath.c for details */
258	    if((parent != NULL) && (parent->type == XML_ATTRIBUTE_NODE)) {
259		ns.next = (xmlNsPtr)parent->parent;
260	    } else {
261		ns.next = (xmlNsPtr)parent;
262	    }
263
264	    /*
265	     * If the input is an XPath node-set, then the node-set must explicitly
266	     * contain every node to be rendered to the canonical form.
267	     */
268	    return(xmlXPathNodeSetContains(nodes, (xmlNodePtr)&ns));
269	}
270    }
271    return(1);
272}
273
274static xmlC14NVisibleNsStackPtr
275xmlC14NVisibleNsStackCreate(void) {
276    xmlC14NVisibleNsStackPtr ret;
277
278    ret = (xmlC14NVisibleNsStackPtr) xmlMalloc(sizeof(xmlC14NVisibleNsStack));
279    if (ret == NULL) {
280        xmlC14NErrMemory("creating namespaces stack");
281	return(NULL);
282    }
283    memset(ret, 0 , (size_t) sizeof(xmlC14NVisibleNsStack));
284    return(ret);
285}
286
287static void
288xmlC14NVisibleNsStackDestroy(xmlC14NVisibleNsStackPtr cur) {
289    if(cur == NULL) {
290        xmlC14NErrParam("destroying namespaces stack");
291        return;
292    }
293    if(cur->nsTab != NULL) {
294	memset(cur->nsTab, 0, cur->nsMax * sizeof(xmlNsPtr));
295	xmlFree(cur->nsTab);
296    }
297    if(cur->nodeTab != NULL) {
298	memset(cur->nodeTab, 0, cur->nsMax * sizeof(xmlNodePtr));
299	xmlFree(cur->nodeTab);
300    }
301    memset(cur, 0, sizeof(xmlC14NVisibleNsStack));
302    xmlFree(cur);
303
304}
305
306static void
307xmlC14NVisibleNsStackAdd(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlNodePtr node) {
308    if((cur == NULL) ||
309       ((cur->nsTab == NULL) && (cur->nodeTab != NULL)) ||
310       ((cur->nsTab != NULL) && (cur->nodeTab == NULL))) {
311        xmlC14NErrParam("adding namespace to stack");
312	return;
313    }
314
315    if ((cur->nsTab == NULL) && (cur->nodeTab == NULL)) {
316        cur->nsTab = (xmlNsPtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
317        cur->nodeTab = (xmlNodePtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
318	if ((cur->nsTab == NULL) || (cur->nodeTab == NULL)) {
319	    xmlC14NErrMemory("adding node to stack");
320	    return;
321	}
322	memset(cur->nsTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
323	memset(cur->nodeTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
324        cur->nsMax = XML_NAMESPACES_DEFAULT;
325    } else if(cur->nsMax == cur->nsCurEnd) {
326	void *tmp;
327	int tmpSize;
328
329	tmpSize = 2 * cur->nsMax;
330	tmp = xmlRealloc(cur->nsTab, tmpSize * sizeof(xmlNsPtr));
331	if (tmp == NULL) {
332	    xmlC14NErrMemory("adding node to stack");
333	    return;
334	}
335	cur->nsTab = (xmlNsPtr*)tmp;
336
337	tmp = xmlRealloc(cur->nodeTab, tmpSize * sizeof(xmlNodePtr));
338	if (tmp == NULL) {
339	    xmlC14NErrMemory("adding node to stack");
340	    return;
341	}
342	cur->nodeTab = (xmlNodePtr*)tmp;
343
344	cur->nsMax = tmpSize;
345    }
346    cur->nsTab[cur->nsCurEnd] = ns;
347    cur->nodeTab[cur->nsCurEnd] = node;
348
349    ++cur->nsCurEnd;
350}
351
352static void
353xmlC14NVisibleNsStackSave(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
354    if((cur == NULL) || (state == NULL)) {
355        xmlC14NErrParam("saving namespaces stack");
356	return;
357    }
358
359    state->nsCurEnd = cur->nsCurEnd;
360    state->nsPrevStart = cur->nsPrevStart;
361    state->nsPrevEnd = cur->nsPrevEnd;
362}
363
364static void
365xmlC14NVisibleNsStackRestore(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
366    if((cur == NULL) || (state == NULL)) {
367        xmlC14NErrParam("restoring namespaces stack");
368	return;
369    }
370    cur->nsCurEnd = state->nsCurEnd;
371    cur->nsPrevStart = state->nsPrevStart;
372    cur->nsPrevEnd = state->nsPrevEnd;
373}
374
375static void
376xmlC14NVisibleNsStackShift(xmlC14NVisibleNsStackPtr cur) {
377    if(cur == NULL) {
378        xmlC14NErrParam("shifting namespaces stack");
379	return;
380    }
381    cur->nsPrevStart = cur->nsPrevEnd;
382    cur->nsPrevEnd = cur->nsCurEnd;
383}
384
385static int
386xmlC14NStrEqual(const xmlChar *str1, const xmlChar *str2) {
387    if (str1 == str2) return(1);
388    if (str1 == NULL) return((*str2) == '\0');
389    if (str2 == NULL) return((*str1) == '\0');
390    do {
391	if (*str1++ != *str2) return(0);
392    } while (*str2++);
393    return(1);
394}
395
396/**
397 * xmlC14NVisibleNsStackFind:
398 * @ctx:		the C14N context
399 * @ns:			the namespace to check
400 *
401 * Checks whether the given namespace was already rendered or not
402 *
403 * Returns 1 if we already wrote this namespace or 0 otherwise
404 */
405static int
406xmlC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns)
407{
408    int i;
409    const xmlChar *prefix;
410    const xmlChar *href;
411    int has_empty_ns;
412
413    if(cur == NULL) {
414        xmlC14NErrParam("searching namespaces stack (c14n)");
415        return (0);
416    }
417
418    /*
419     * if the default namespace xmlns="" is not defined yet then
420     * we do not want to print it out
421     */
422    prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
423    href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
424    has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
425
426    if (cur->nsTab != NULL) {
427	int start = (has_empty_ns) ? 0 : cur->nsPrevStart;
428        for (i = cur->nsCurEnd - 1; i >= start; --i) {
429            xmlNsPtr ns1 = cur->nsTab[i];
430
431	    if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
432		return(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL));
433	    }
434        }
435    }
436    return(has_empty_ns);
437}
438
439static int
440xmlExcC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlC14NCtxPtr ctx) {
441    int i;
442    const xmlChar *prefix;
443    const xmlChar *href;
444    int has_empty_ns;
445
446    if(cur == NULL) {
447        xmlC14NErrParam("searching namespaces stack (exc c14n)");
448        return (0);
449    }
450
451    /*
452     * if the default namespace xmlns="" is not defined yet then
453     * we do not want to print it out
454     */
455    prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
456    href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
457    has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
458
459    if (cur->nsTab != NULL) {
460	int start = 0;
461        for (i = cur->nsCurEnd - 1; i >= start; --i) {
462            xmlNsPtr ns1 = cur->nsTab[i];
463
464	    if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
465		if(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL)) {
466	    	    return(xmlC14NIsVisible(ctx, ns1, cur->nodeTab[i]));
467		} else {
468		    return(0);
469		}
470	    }
471        }
472    }
473    return(has_empty_ns);
474}
475
476
477
478
479/**
480 * xmlC14NIsXmlNs:
481 * @ns: 		the namespace to check
482 *
483 * Checks whether the given namespace is a default "xml:" namespace
484 * with href="http://www.w3.org/XML/1998/namespace"
485 *
486 * Returns 1 if the node is default or 0 otherwise
487 */
488
489/* todo: make it a define? */
490static int
491xmlC14NIsXmlNs(xmlNsPtr ns)
492{
493    return ((ns != NULL) &&
494            (xmlStrEqual(ns->prefix, BAD_CAST "xml")) &&
495            (xmlStrEqual(ns->href,
496                         BAD_CAST
497                         "http://www.w3.org/XML/1998/namespace")));
498}
499
500
501/**
502 * xmlC14NNsCompare:
503 * @ns1:		the pointer to first namespace
504 * @ns2: 		the pointer to second namespace
505 *
506 * Compares the namespaces by names (prefixes).
507 *
508 * Returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2.
509 */
510static int
511xmlC14NNsCompare(xmlNsPtr ns1, xmlNsPtr ns2)
512{
513    if (ns1 == ns2)
514        return (0);
515    if (ns1 == NULL)
516        return (-1);
517    if (ns2 == NULL)
518        return (1);
519
520    return (xmlStrcmp(ns1->prefix, ns2->prefix));
521}
522
523
524/**
525 * xmlC14NPrintNamespaces:
526 * @ns:			the pointer to namespace
527 * @ctx: 		the C14N context
528 *
529 * Prints the given namespace to the output buffer from C14N context.
530 *
531 * Returns 1 on success or 0 on fail.
532 */
533static int
534xmlC14NPrintNamespaces(const xmlNsPtr ns, xmlC14NCtxPtr ctx)
535{
536
537    if ((ns == NULL) || (ctx == NULL)) {
538        xmlC14NErrParam("writing namespaces");
539        return 0;
540    }
541
542    if (ns->prefix != NULL) {
543        xmlOutputBufferWriteString(ctx->buf, " xmlns:");
544        xmlOutputBufferWriteString(ctx->buf, (const char *) ns->prefix);
545        xmlOutputBufferWriteString(ctx->buf, "=\"");
546    } else {
547        xmlOutputBufferWriteString(ctx->buf, " xmlns=\"");
548    }
549    if(ns->href != NULL) {
550	xmlOutputBufferWriteString(ctx->buf, (const char *) ns->href);
551    }
552    xmlOutputBufferWriteString(ctx->buf, "\"");
553    return (1);
554}
555
556/**
557 * xmlC14NProcessNamespacesAxis:
558 * @ctx: 		the C14N context
559 * @node:		the current node
560 *
561 * Prints out canonical namespace axis of the current node to the
562 * buffer from C14N context as follows
563 *
564 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
565 *
566 * Namespace Axis
567 * Consider a list L containing only namespace nodes in the
568 * axis and in the node-set in lexicographic order (ascending). To begin
569 * processing L, if the first node is not the default namespace node (a node
570 * with no namespace URI and no local name), then generate a space followed
571 * by xmlns="" if and only if the following conditions are met:
572 *    - the element E that owns the axis is in the node-set
573 *    - The nearest ancestor element of E in the node-set has a default
574 *	    namespace node in the node-set (default namespace nodes always
575 *      have non-empty values in XPath)
576 * The latter condition eliminates unnecessary occurrences of xmlns="" in
577 * the canonical form since an element only receives an xmlns="" if its
578 * default namespace is empty and if it has an immediate parent in the
579 * canonical form that has a non-empty default namespace. To finish
580 * processing  L, simply process every namespace node in L, except omit
581 * namespace node with local name xml, which defines the xml prefix,
582 * if its string value is http://www.w3.org/XML/1998/namespace.
583 *
584 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
585 * Canonical XML applied to a document subset requires the search of the
586 * ancestor nodes of each orphan element node for attributes in the xml
587 * namespace, such as xml:lang and xml:space. These are copied into the
588 * element node except if a declaration of the same attribute is already
589 * in the attribute axis of the element (whether or not it is included in
590 * the document subset). This search and copying are omitted from the
591 * Exclusive XML Canonicalization method.
592 *
593 * Returns 0 on success or -1 on fail.
594 */
595static int
596xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
597{
598    xmlNodePtr n;
599    xmlNsPtr ns, tmp;
600    xmlListPtr list;
601    int already_rendered;
602    int has_empty_ns = 0;
603
604    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
605        xmlC14NErrParam("processing namespaces axis (c14n)");
606        return (-1);
607    }
608
609    /*
610     * Create a sorted list to store element namespaces
611     */
612    list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNsCompare);
613    if (list == NULL) {
614        xmlC14NErrInternal("creating namespaces list (c14n)");
615        return (-1);
616    }
617
618    /* check all namespaces */
619    for(n = cur; n != NULL; n = n->parent) {
620	for(ns = n->nsDef; ns != NULL; ns = ns->next) {
621	    tmp = xmlSearchNs(cur->doc, cur, ns->prefix);
622
623	    if((tmp == ns) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
624		already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
625		if(visible) {
626        	    xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
627		}
628		if(!already_rendered) {
629		    xmlListInsert(list, ns);
630		}
631    		if(xmlStrlen(ns->prefix) == 0) {
632		    has_empty_ns = 1;
633		}
634	    }
635	}
636    }
637
638    /**
639     * if the first node is not the default namespace node (a node with no
640     * namespace URI and no local name), then generate a space followed by
641     * xmlns="" if and only if the following conditions are met:
642     *  - the element E that owns the axis is in the node-set
643     *  - the nearest ancestor element of E in the node-set has a default
644     *     namespace node in the node-set (default namespace nodes always
645     *     have non-empty values in XPath)
646     */
647    if(visible && !has_empty_ns) {
648        static xmlNs ns_default;
649
650        memset(&ns_default, 0, sizeof(ns_default));
651        if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
652    	    xmlC14NPrintNamespaces(&ns_default, ctx);
653	}
654    }
655
656
657    /*
658     * print out all elements from list
659     */
660    xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces, (const void *) ctx);
661
662    /*
663     * Cleanup
664     */
665    xmlListDelete(list);
666    return (0);
667}
668
669
670/**
671 * xmlExcC14NProcessNamespacesAxis:
672 * @ctx: 		the C14N context
673 * @node:		the current node
674 *
675 * Prints out exclusive canonical namespace axis of the current node to the
676 * buffer from C14N context as follows
677 *
678 * Exclusive XML Canonicalization
679 * http://www.w3.org/TR/xml-exc-c14n
680 *
681 * If the element node is in the XPath subset then output the node in
682 * accordance with Canonical XML except for namespace nodes which are
683 * rendered as follows:
684 *
685 * 1. Render each namespace node iff:
686 *    * it is visibly utilized by the immediate parent element or one of
687 *      its attributes, or is present in InclusiveNamespaces PrefixList, and
688 *    * its prefix and value do not appear in ns_rendered. ns_rendered is
689 *      obtained by popping the state stack in order to obtain a list of
690 *      prefixes and their values which have already been rendered by
691 *      an output ancestor of the namespace node's parent element.
692 * 2. Append the rendered namespace node to the list ns_rendered of namespace
693 * nodes rendered by output ancestors. Push ns_rendered on state stack and
694 * recurse.
695 * 3. After the recursion returns, pop thestate stack.
696 *
697 *
698 * Returns 0 on success or -1 on fail.
699 */
700static int
701xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
702{
703    xmlNsPtr ns;
704    xmlListPtr list;
705    xmlAttrPtr attr;
706    int already_rendered;
707    int has_empty_ns = 0;
708    int has_visibly_utilized_empty_ns = 0;
709    int has_empty_ns_in_inclusive_list = 0;
710
711    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
712        xmlC14NErrParam("processing namespaces axis (exc c14n)");
713        return (-1);
714    }
715
716    if(!ctx->exclusive) {
717        xmlC14NErrParam("processing namespaces axis (exc c14n)");
718        return (-1);
719
720    }
721
722    /*
723     * Create a sorted list to store element namespaces
724     */
725    list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNsCompare);
726    if (list == NULL) {
727        xmlC14NErrInternal("creating namespaces list (exc c14n)");
728        return (-1);
729    }
730
731    /*
732     * process inclusive namespaces:
733     * All namespace nodes appearing on inclusive ns list are
734     * handled as provided in Canonical XML
735     */
736    if(ctx->inclusive_ns_prefixes != NULL) {
737	xmlChar *prefix;
738	int i;
739
740	for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
741	    prefix = ctx->inclusive_ns_prefixes[i];
742	    /*
743	     * Special values for namespace with empty prefix
744	     */
745            if (xmlStrEqual(prefix, BAD_CAST "#default")
746                || xmlStrEqual(prefix, BAD_CAST "")) {
747                prefix = NULL;
748		has_empty_ns_in_inclusive_list = 1;
749            }
750
751	    ns = xmlSearchNs(cur->doc, cur, prefix);
752	    if((ns != NULL) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
753		already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
754		if(visible) {
755    	    	    xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
756		}
757		if(!already_rendered) {
758	    	    xmlListInsert(list, ns);
759		}
760    		if(xmlStrlen(ns->prefix) == 0) {
761		    has_empty_ns = 1;
762		}
763	    }
764	}
765    }
766
767    /* add node namespace */
768    if(cur->ns != NULL) {
769	ns = cur->ns;
770    } else {
771        ns = xmlSearchNs(cur->doc, cur, NULL);
772	has_visibly_utilized_empty_ns = 1;
773    }
774    if((ns != NULL) && !xmlC14NIsXmlNs(ns)) {
775	if(visible && xmlC14NIsVisible(ctx, ns, cur)) {
776	    if(!xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, ns, ctx)) {
777		xmlListInsert(list, ns);
778	    }
779	}
780	if(visible) {
781    	    xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
782	}
783	if(xmlStrlen(ns->prefix) == 0) {
784	    has_empty_ns = 1;
785	}
786    }
787
788
789    /* add attributes */
790    for(attr = cur->properties; attr != NULL; attr = attr->next) {
791        /*
792         * we need to check that attribute is visible and has non
793         * default namespace (XML Namespaces: "default namespaces
794    	 * do not apply directly to attributes")
795         */
796	if((attr->ns != NULL) && !xmlC14NIsXmlNs(attr->ns) && xmlC14NIsVisible(ctx, attr, cur)) {
797	    already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, attr->ns, ctx);
798	    xmlC14NVisibleNsStackAdd(ctx->ns_rendered, attr->ns, cur);
799	    if(!already_rendered && visible) {
800		xmlListInsert(list, attr->ns);
801	    }
802	    if(xmlStrlen(attr->ns->prefix) == 0) {
803		has_empty_ns = 1;
804	    }
805	} else if((attr->ns != NULL) && (xmlStrlen(attr->ns->prefix) == 0) && (xmlStrlen(attr->ns->href) == 0)) {
806	    has_visibly_utilized_empty_ns = 1;
807	}
808    }
809
810    /*
811     * Process xmlns=""
812     */
813    if(visible && has_visibly_utilized_empty_ns &&
814	    !has_empty_ns && !has_empty_ns_in_inclusive_list) {
815        static xmlNs ns_default;
816
817        memset(&ns_default, 0, sizeof(ns_default));
818
819        already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default, ctx);
820	if(!already_rendered) {
821    	    xmlC14NPrintNamespaces(&ns_default, ctx);
822	}
823    } else if(visible && !has_empty_ns && has_empty_ns_in_inclusive_list) {
824        static xmlNs ns_default;
825
826        memset(&ns_default, 0, sizeof(ns_default));
827        if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
828    	    xmlC14NPrintNamespaces(&ns_default, ctx);
829	}
830    }
831
832
833
834    /*
835     * print out all elements from list
836     */
837    xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces, (const void *) ctx);
838
839    /*
840     * Cleanup
841     */
842    xmlListDelete(list);
843    return (0);
844}
845
846
847/**
848 * xmlC14NAttrsCompare:
849 * @attr1:		the pointer tls o first attr
850 * @attr2: 		the pointer to second attr
851 *
852 * Prints the given attribute to the output buffer from C14N context.
853 *
854 * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
855 */
856static int
857xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2)
858{
859    int ret = 0;
860
861    /*
862     * Simple cases
863     */
864    if (attr1 == attr2)
865        return (0);
866    if (attr1 == NULL)
867        return (-1);
868    if (attr2 == NULL)
869        return (1);
870    if (attr1->ns == attr2->ns) {
871        return (xmlStrcmp(attr1->name, attr2->name));
872    }
873
874    /*
875     * Attributes in the default namespace are first
876     * because the default namespace is not applied to
877     * unqualified attributes
878     */
879    if (attr1->ns == NULL)
880        return (-1);
881    if (attr2->ns == NULL)
882        return (1);
883    if (attr1->ns->prefix == NULL)
884        return (-1);
885    if (attr2->ns->prefix == NULL)
886        return (1);
887
888    ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
889    if (ret == 0) {
890        ret = xmlStrcmp(attr1->name, attr2->name);
891    }
892    return (ret);
893}
894
895
896/**
897 * xmlC14NPrintAttrs:
898 * @attr:		the pointer to attr
899 * @ctx: 		the C14N context
900 *
901 * Prints out canonical attribute urrent node to the
902 * buffer from C14N context as follows
903 *
904 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
905 *
906 * Returns 1 on success or 0 on fail.
907 */
908static int
909xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx)
910{
911    xmlChar *value;
912    xmlChar *buffer;
913
914    if ((attr == NULL) || (ctx == NULL)) {
915        xmlC14NErrParam("writing attributes");
916        return (0);
917    }
918
919    xmlOutputBufferWriteString(ctx->buf, " ");
920    if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
921        xmlOutputBufferWriteString(ctx->buf,
922                                   (const char *) attr->ns->prefix);
923        xmlOutputBufferWriteString(ctx->buf, ":");
924    }
925    xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
926    xmlOutputBufferWriteString(ctx->buf, "=\"");
927
928    value = xmlNodeListGetString(attr->doc, attr->children, 1);
929    /* todo: should we log an error if value==NULL ? */
930    if (value != NULL) {
931        buffer = xmlC11NNormalizeAttr(value);
932        xmlFree(value);
933        if (buffer != NULL) {
934            xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
935            xmlFree(buffer);
936        } else {
937            xmlC14NErrInternal("normalizing attributes axis");
938            return (0);
939        }
940    }
941    xmlOutputBufferWriteString(ctx->buf, "\"");
942    return (1);
943}
944
945/**
946 * xmlC14NProcessAttrsAxis:
947 * @ctx: 		the C14N context
948 * @cur:		the current node
949 * @parent_visible:	the visibility of parent node
950 *
951 * Prints out canonical attribute axis of the current node to the
952 * buffer from C14N context as follows
953 *
954 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
955 *
956 * Attribute Axis
957 * In lexicographic order (ascending), process each node that
958 * is in the element's attribute axis and in the node-set.
959 *
960 * The processing of an element node E MUST be modified slightly
961 * when an XPath node-set is given as input and the element's
962 * parent is omitted from the node-set.
963 *
964 *
965 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
966 *
967 * Canonical XML applied to a document subset requires the search of the
968 * ancestor nodes of each orphan element node for attributes in the xml
969 * namespace, such as xml:lang and xml:space. These are copied into the
970 * element node except if a declaration of the same attribute is already
971 * in the attribute axis of the element (whether or not it is included in
972 * the document subset). This search and copying are omitted from the
973 * Exclusive XML Canonicalization method.
974 *
975 * Returns 0 on success or -1 on fail.
976 */
977static int
978xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int parent_visible)
979{
980    xmlAttrPtr attr;
981    xmlListPtr list;
982
983    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
984        xmlC14NErrParam("processing attributes axis");
985        return (-1);
986    }
987
988    /*
989     * Create a sorted list to store element attributes
990     */
991    list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NAttrsCompare);
992    if (list == NULL) {
993        xmlC14NErrInternal("creating attributes list");
994        return (-1);
995    }
996
997    /*
998     * Add all visible attributes from current node.
999     */
1000    attr = cur->properties;
1001    while (attr != NULL) {
1002        /* check that attribute is visible */
1003        if (xmlC14NIsVisible(ctx, attr, cur)) {
1004            xmlListInsert(list, attr);
1005        }
1006        attr = attr->next;
1007    }
1008
1009    /*
1010     * include attributes in "xml" namespace defined in ancestors
1011     * (only for non-exclusive XML Canonicalization)
1012     */
1013    if (parent_visible && (!ctx->exclusive) && (cur->parent != NULL)
1014        && (!xmlC14NIsVisible(ctx, cur->parent, cur->parent->parent))) {
1015        /*
1016         * If XPath node-set is not specified then the parent is always
1017         * visible!
1018         */
1019        cur = cur->parent;
1020        while (cur != NULL) {
1021            attr = cur->properties;
1022            while (attr != NULL) {
1023                if ((attr->ns != NULL)
1024                    && (xmlStrEqual(attr->ns->prefix, BAD_CAST "xml"))) {
1025                    if (xmlListSearch(list, attr) == NULL) {
1026                        xmlListInsert(list, attr);
1027                    }
1028                }
1029                attr = attr->next;
1030            }
1031            cur = cur->parent;
1032        }
1033    }
1034
1035    /*
1036     * print out all elements from list
1037     */
1038    xmlListWalk(list, (xmlListWalker) xmlC14NPrintAttrs, (const void *) ctx);
1039
1040    /*
1041     * Cleanup
1042     */
1043    xmlListDelete(list);
1044    return (0);
1045}
1046
1047/**
1048 * xmlC14NCheckForRelativeNamespaces:
1049 * @ctx:		the C14N context
1050 * @cur:		the current element node
1051 *
1052 * Checks that current element node has no relative namespaces defined
1053 *
1054 * Returns 0 if the node has no relative namespaces or -1 otherwise.
1055 */
1056static int
1057xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1058{
1059    xmlNsPtr ns;
1060
1061    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
1062        xmlC14NErrParam("checking for relative namespaces");
1063        return (-1);
1064    }
1065
1066    ns = cur->nsDef;
1067    while (ns != NULL) {
1068        if (xmlStrlen(ns->href) > 0) {
1069            xmlURIPtr uri;
1070
1071            uri = xmlParseURI((const char *) ns->href);
1072            if (uri == NULL) {
1073                xmlC14NErrInternal("parsing namespace uri");
1074                return (-1);
1075            }
1076            if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
1077                xmlC14NErrRelativeNamespace(uri->scheme);
1078                xmlFreeURI(uri);
1079                return (-1);
1080            }
1081            if ((xmlStrcasecmp((const xmlChar *) uri->scheme, BAD_CAST "urn") != 0)
1082                && (xmlStrcasecmp((const xmlChar *) uri->scheme, BAD_CAST "dav") !=0)
1083                && (xmlStrlen((const xmlChar *) uri->server) == 0)) {
1084                xmlC14NErrRelativeNamespace(uri->scheme);
1085                xmlFreeURI(uri);
1086                return (-1);
1087            }
1088            xmlFreeURI(uri);
1089        }
1090        ns = ns->next;
1091    }
1092    return (0);
1093}
1094
1095/**
1096 * xmlC14NProcessElementNode:
1097 * @ctx: 		the pointer to C14N context object
1098 * @cur:		the node to process
1099 *
1100 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
1101 *
1102 * Element Nodes
1103 * If the element is not in the node-set, then the result is obtained
1104 * by processing the namespace axis, then the attribute axis, then
1105 * processing the child nodes of the element that are in the node-set
1106 * (in document order). If the element is in the node-set, then the result
1107 * is an open angle bracket (<), the element QName, the result of
1108 * processing the namespace axis, the result of processing the attribute
1109 * axis, a close angle bracket (>), the result of processing the child
1110 * nodes of the element that are in the node-set (in document order), an
1111 * open angle bracket, a forward slash (/), the element QName, and a close
1112 * angle bracket.
1113 *
1114 * Returns non-negative value on success or negative value on fail
1115 */
1116static int
1117xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
1118{
1119    int ret;
1120    xmlC14NVisibleNsStack state;
1121    int parent_is_doc = 0;
1122
1123    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
1124        xmlC14NErrParam("processing element node");
1125        return (-1);
1126    }
1127
1128    /*
1129     * Check relative relative namespaces:
1130     * implementations of XML canonicalization MUST report an operation
1131     * failure on documents containing relative namespace URIs.
1132     */
1133    if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
1134        xmlC14NErrInternal("checking for relative namespaces");
1135        return (-1);
1136    }
1137
1138
1139    /*
1140     * Save ns_rendered stack position
1141     */
1142    memset(&state, 0, sizeof(state));
1143    xmlC14NVisibleNsStackSave(ctx->ns_rendered, &state);
1144
1145    if (visible) {
1146        if (ctx->parent_is_doc) {
1147	    /* save this flag into the stack */
1148	    parent_is_doc = ctx->parent_is_doc;
1149	    ctx->parent_is_doc = 0;
1150            ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
1151        }
1152        xmlOutputBufferWriteString(ctx->buf, "<");
1153
1154        if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1155            xmlOutputBufferWriteString(ctx->buf,
1156                                       (const char *) cur->ns->prefix);
1157            xmlOutputBufferWriteString(ctx->buf, ":");
1158        }
1159        xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
1160    }
1161
1162    if (!ctx->exclusive) {
1163        ret = xmlC14NProcessNamespacesAxis(ctx, cur, visible);
1164    } else {
1165        ret = xmlExcC14NProcessNamespacesAxis(ctx, cur, visible);
1166    }
1167    if (ret < 0) {
1168        xmlC14NErrInternal("processing namespaces axis");
1169        return (-1);
1170    }
1171    /* todo: shouldn't this go to "visible only"? */
1172    if(visible) {
1173	xmlC14NVisibleNsStackShift(ctx->ns_rendered);
1174    }
1175
1176    ret = xmlC14NProcessAttrsAxis(ctx, cur, visible);
1177    if (ret < 0) {
1178	xmlC14NErrInternal("processing attributes axis");
1179    	return (-1);
1180    }
1181
1182    if (visible) {
1183        xmlOutputBufferWriteString(ctx->buf, ">");
1184    }
1185    if (cur->children != NULL) {
1186        ret = xmlC14NProcessNodeList(ctx, cur->children);
1187        if (ret < 0) {
1188            xmlC14NErrInternal("processing childrens list");
1189            return (-1);
1190        }
1191    }
1192    if (visible) {
1193        xmlOutputBufferWriteString(ctx->buf, "</");
1194        if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1195            xmlOutputBufferWriteString(ctx->buf,
1196                                       (const char *) cur->ns->prefix);
1197            xmlOutputBufferWriteString(ctx->buf, ":");
1198        }
1199        xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
1200        xmlOutputBufferWriteString(ctx->buf, ">");
1201        if (parent_is_doc) {
1202	    /* restore this flag from the stack for next node */
1203            ctx->parent_is_doc = parent_is_doc;
1204	    ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
1205        }
1206    }
1207
1208    /*
1209     * Restore ns_rendered stack position
1210     */
1211    xmlC14NVisibleNsStackRestore(ctx->ns_rendered, &state);
1212    return (0);
1213}
1214
1215/**
1216 * xmlC14NProcessNode:
1217 * @ctx: 		the pointer to C14N context object
1218 * @cur:		the node to process
1219 *
1220 * Processes the given node
1221 *
1222 * Returns non-negative value on success or negative value on fail
1223 */
1224static int
1225xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1226{
1227    int ret = 0;
1228    int visible;
1229
1230    if ((ctx == NULL) || (cur == NULL)) {
1231        xmlC14NErrParam("processing node");
1232        return (-1);
1233    }
1234
1235    visible = xmlC14NIsVisible(ctx, cur, cur->parent);
1236    switch (cur->type) {
1237        case XML_ELEMENT_NODE:
1238            ret = xmlC14NProcessElementNode(ctx, cur, visible);
1239            break;
1240        case XML_CDATA_SECTION_NODE:
1241        case XML_TEXT_NODE:
1242            /*
1243             * Text Nodes
1244             * the string value, except all ampersands are replaced
1245             * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
1246             * angle brackets (>) are replaced by &gt;, and all #xD characters are
1247             * replaced by &#xD;.
1248             */
1249            /* cdata sections are processed as text nodes */
1250            /* todo: verify that cdata sections are included in XPath nodes set */
1251            if ((visible) && (cur->content != NULL)) {
1252                xmlChar *buffer;
1253
1254                buffer = xmlC11NNormalizeText(cur->content);
1255                if (buffer != NULL) {
1256                    xmlOutputBufferWriteString(ctx->buf,
1257                                               (const char *) buffer);
1258                    xmlFree(buffer);
1259                } else {
1260                    xmlC14NErrInternal("normalizing text node");
1261                    return (-1);
1262                }
1263            }
1264            break;
1265        case XML_PI_NODE:
1266            /*
1267             * Processing Instruction (PI) Nodes-
1268             * The opening PI symbol (<?), the PI target name of the node,
1269             * a leading space and the string value if it is not empty, and
1270             * the closing PI symbol (?>). If the string value is empty,
1271             * then the leading space is not added. Also, a trailing #xA is
1272             * rendered after the closing PI symbol for PI children of the
1273             * root node with a lesser document order than the document
1274             * element, and a leading #xA is rendered before the opening PI
1275             * symbol of PI children of the root node with a greater document
1276             * order than the document element.
1277             */
1278            if (visible) {
1279                if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1280                    xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
1281                } else {
1282                    xmlOutputBufferWriteString(ctx->buf, "<?");
1283                }
1284
1285                xmlOutputBufferWriteString(ctx->buf,
1286                                           (const char *) cur->name);
1287                if ((cur->content != NULL) && (*(cur->content) != '\0')) {
1288                    xmlChar *buffer;
1289
1290                    xmlOutputBufferWriteString(ctx->buf, " ");
1291
1292                    /* todo: do we need to normalize pi? */
1293                    buffer = xmlC11NNormalizePI(cur->content);
1294                    if (buffer != NULL) {
1295                        xmlOutputBufferWriteString(ctx->buf,
1296                                                   (const char *) buffer);
1297                        xmlFree(buffer);
1298                    } else {
1299                        xmlC14NErrInternal("normalizing pi node");
1300                        return (-1);
1301                    }
1302                }
1303
1304                if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1305                    xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
1306                } else {
1307                    xmlOutputBufferWriteString(ctx->buf, "?>");
1308                }
1309            }
1310            break;
1311        case XML_COMMENT_NODE:
1312            /*
1313             * Comment Nodes
1314             * Nothing if generating canonical XML without  comments. For
1315             * canonical XML with comments, generate the opening comment
1316             * symbol (<!--), the string value of the node, and the
1317             * closing comment symbol (-->). Also, a trailing #xA is rendered
1318             * after the closing comment symbol for comment children of the
1319             * root node with a lesser document order than the document
1320             * element, and a leading #xA is rendered before the opening
1321             * comment symbol of comment children of the root node with a
1322             * greater document order than the document element. (Comment
1323             * children of the root node represent comments outside of the
1324             * top-level document element and outside of the document type
1325             * declaration).
1326             */
1327            if (visible && ctx->with_comments) {
1328                if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1329                    xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1330                } else {
1331                    xmlOutputBufferWriteString(ctx->buf, "<!--");
1332                }
1333
1334                if (cur->content != NULL) {
1335                    xmlChar *buffer;
1336
1337                    /* todo: do we need to normalize comment? */
1338                    buffer = xmlC11NNormalizeComment(cur->content);
1339                    if (buffer != NULL) {
1340                        xmlOutputBufferWriteString(ctx->buf,
1341                                                   (const char *) buffer);
1342                        xmlFree(buffer);
1343                    } else {
1344                        xmlC14NErrInternal("normalizing comment node");
1345                        return (-1);
1346                    }
1347                }
1348
1349                if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1350                    xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1351                } else {
1352                    xmlOutputBufferWriteString(ctx->buf, "-->");
1353                }
1354            }
1355            break;
1356        case XML_DOCUMENT_NODE:
1357        case XML_DOCUMENT_FRAG_NODE:   /* should be processed as document? */
1358#ifdef LIBXML_DOCB_ENABLED
1359        case XML_DOCB_DOCUMENT_NODE:   /* should be processed as document? */
1360#endif
1361#ifdef LIBXML_HTML_ENABLED
1362        case XML_HTML_DOCUMENT_NODE:   /* should be processed as document? */
1363#endif
1364            if (cur->children != NULL) {
1365                ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1366                ctx->parent_is_doc = 1;
1367                ret = xmlC14NProcessNodeList(ctx, cur->children);
1368            }
1369            break;
1370
1371        case XML_ATTRIBUTE_NODE:
1372            xmlC14NErrInvalidNode("XML_ATTRIBUTE_NODE", "processing node");
1373            return (-1);
1374        case XML_NAMESPACE_DECL:
1375            xmlC14NErrInvalidNode("XML_NAMESPACE_DECL", "processing node");
1376            return (-1);
1377        case XML_ENTITY_REF_NODE:
1378            xmlC14NErrInvalidNode("XML_ENTITY_REF_NODE", "processing node");
1379            return (-1);
1380        case XML_ENTITY_NODE:
1381            xmlC14NErrInvalidNode("XML_ENTITY_NODE", "processing node");
1382            return (-1);
1383
1384        case XML_DOCUMENT_TYPE_NODE:
1385        case XML_NOTATION_NODE:
1386        case XML_DTD_NODE:
1387        case XML_ELEMENT_DECL:
1388        case XML_ATTRIBUTE_DECL:
1389        case XML_ENTITY_DECL:
1390#ifdef LIBXML_XINCLUDE_ENABLED
1391        case XML_XINCLUDE_START:
1392        case XML_XINCLUDE_END:
1393#endif
1394            /*
1395             * should be ignored according to "W3C Canonical XML"
1396             */
1397            break;
1398        default:
1399            xmlC14NErrUnknownNode(cur->type, "processing node");
1400            return (-1);
1401    }
1402
1403    return (ret);
1404}
1405
1406/**
1407 * xmlC14NProcessNodeList:
1408 * @ctx: 		the pointer to C14N context object
1409 * @cur:		the node to start from
1410 *
1411 * Processes all nodes in the row starting from cur.
1412 *
1413 * Returns non-negative value on success or negative value on fail
1414 */
1415static int
1416xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1417{
1418    int ret;
1419
1420    if (ctx == NULL) {
1421        xmlC14NErrParam("processing node list");
1422        return (-1);
1423    }
1424
1425    for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1426        ret = xmlC14NProcessNode(ctx, cur);
1427    }
1428    return (ret);
1429}
1430
1431
1432/**
1433 * xmlC14NFreeCtx:
1434 * @ctx: the pointer to C14N context object
1435 *
1436 * Cleanups the C14N context object.
1437 */
1438
1439static void
1440xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1441{
1442    if (ctx == NULL) {
1443        xmlC14NErrParam("freeing context");
1444        return;
1445    }
1446
1447    if (ctx->ns_rendered != NULL) {
1448        xmlC14NVisibleNsStackDestroy(ctx->ns_rendered);
1449    }
1450    xmlFree(ctx);
1451}
1452
1453/**
1454 * xmlC14NNewCtx:
1455 * @doc: 		the XML document for canonization
1456 * @is_visible_callback:the function to use to determine is node visible
1457 *			or not
1458 * @user_data: 		the first parameter for @is_visible_callback function
1459 *			(in most cases, it is nodes set)
1460 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1461 *			ended with a NULL or NULL if there is no
1462 *			inclusive namespaces (only for exclusive
1463 *			canonicalization)
1464 * @with_comments: 	include comments in the result (!=0) or not (==0)
1465 * @buf: 		the output buffer to store canonical XML; this
1466 *			buffer MUST have encoder==NULL because C14N requires
1467 *			UTF-8 output
1468 *
1469 * Creates new C14N context object to store C14N parameters.
1470 *
1471 * Returns pointer to newly created object (success) or NULL (fail)
1472 */
1473static xmlC14NCtxPtr
1474xmlC14NNewCtx(xmlDocPtr doc,
1475	      xmlC14NIsVisibleCallback is_visible_callback, void* user_data,
1476              int exclusive, xmlChar ** inclusive_ns_prefixes,
1477              int with_comments, xmlOutputBufferPtr buf)
1478{
1479    xmlC14NCtxPtr ctx = NULL;
1480
1481    if ((doc == NULL) || (buf == NULL)) {
1482        xmlC14NErrParam("creating new context");
1483        return (NULL);
1484    }
1485
1486    /*
1487     *  Validate the encoding output buffer encoding
1488     */
1489    if (buf->encoder != NULL) {
1490        xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
1491"xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1492        return (NULL);
1493    }
1494
1495    /*
1496     *  Validate the XML document encoding value, if provided.
1497     */
1498    if (doc->charset != XML_CHAR_ENCODING_UTF8) {
1499        xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
1500		   "xmlC14NNewCtx: source document not in UTF8\n");
1501        return (NULL);
1502    }
1503
1504    /*
1505     * Allocate a new xmlC14NCtxPtr and fill the fields.
1506     */
1507    ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1508    if (ctx == NULL) {
1509	xmlC14NErrMemory("creating context");
1510        return (NULL);
1511    }
1512    memset(ctx, 0, sizeof(xmlC14NCtx));
1513
1514    /*
1515     * initialize C14N context
1516     */
1517    ctx->doc = doc;
1518    ctx->with_comments = with_comments;
1519    ctx->is_visible_callback = is_visible_callback;
1520    ctx->user_data = user_data;
1521    ctx->buf = buf;
1522    ctx->parent_is_doc = 1;
1523    ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1524    ctx->ns_rendered = xmlC14NVisibleNsStackCreate();
1525
1526    if(ctx->ns_rendered == NULL) {
1527        xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_CREATE_STACK,
1528		   "xmlC14NNewCtx: xmlC14NVisibleNsStackCreate failed\n");
1529	xmlC14NFreeCtx(ctx);
1530        return (NULL);
1531    }
1532
1533    /*
1534     * Set "exclusive" flag, create a nodes set for namespaces
1535     * stack and remember list of incluseve prefixes
1536     */
1537    if (exclusive) {
1538        ctx->exclusive = 1;
1539        ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1540    }
1541    return (ctx);
1542}
1543
1544/**
1545 * xmlC14NExecute:
1546 * @doc: 		the XML document for canonization
1547 * @is_visible_callback:the function to use to determine is node visible
1548 *			or not
1549 * @user_data: 		the first parameter for @is_visible_callback function
1550 *			(in most cases, it is nodes set)
1551 * @exclusive:		the exclusive flag (0 - non-exclusive canonicalization;
1552 *			otherwise - exclusive canonicalization)
1553 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
1554 *			ended with a NULL or NULL if there is no
1555 *			inclusive namespaces (only for exclusive
1556 *			canonicalization, ignored otherwise)
1557 * @with_comments: 	include comments in the result (!=0) or not (==0)
1558 * @buf: 		the output buffer to store canonical XML; this
1559 *			buffer MUST have encoder==NULL because C14N requires
1560 *			UTF-8 output
1561 *
1562 * Dumps the canonized image of given XML document into the provided buffer.
1563 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1564 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1565 *
1566 * Returns non-negative value on success or a negative value on fail
1567 */
1568int
1569xmlC14NExecute(xmlDocPtr doc, xmlC14NIsVisibleCallback is_visible_callback,
1570	 void* user_data, int exclusive, xmlChar **inclusive_ns_prefixes,
1571	 int with_comments, xmlOutputBufferPtr buf) {
1572
1573    xmlC14NCtxPtr ctx;
1574    int ret;
1575
1576    if ((buf == NULL) || (doc == NULL)) {
1577        xmlC14NErrParam("executing c14n");
1578        return (-1);
1579    }
1580
1581    /*
1582     *  Validate the encoding output buffer encoding
1583     */
1584    if (buf->encoder != NULL) {
1585        xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
1586"xmlC14NExecute: output buffer encoder != NULL but C14N requires UTF8 output\n");
1587        return (-1);
1588    }
1589
1590    ctx = xmlC14NNewCtx(doc, is_visible_callback, user_data,
1591			exclusive, inclusive_ns_prefixes,
1592                        with_comments, buf);
1593    if (ctx == NULL) {
1594        xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_CREATE_CTXT,
1595		   "xmlC14NExecute: unable to create C14N context\n");
1596        return (-1);
1597    }
1598
1599
1600
1601    /*
1602     * Root Node
1603     * The root node is the parent of the top-level document element. The
1604     * result of processing each of its child nodes that is in the node-set
1605     * in document order. The root node does not generate a byte order mark,
1606     * XML declaration, nor anything from within the document type
1607     * declaration.
1608     */
1609    if (doc->children != NULL) {
1610        ret = xmlC14NProcessNodeList(ctx, doc->children);
1611        if (ret < 0) {
1612            xmlC14NErrInternal("processing docs children list");
1613            xmlC14NFreeCtx(ctx);
1614            return (-1);
1615        }
1616    }
1617
1618    /*
1619     * Flush buffer to get number of bytes written
1620     */
1621    ret = xmlOutputBufferFlush(buf);
1622    if (ret < 0) {
1623        xmlC14NErrInternal("flushing output buffer");
1624        xmlC14NFreeCtx(ctx);
1625        return (-1);
1626    }
1627
1628    /*
1629     * Cleanup
1630     */
1631    xmlC14NFreeCtx(ctx);
1632    return (ret);
1633}
1634
1635/**
1636 * xmlC14NDocSaveTo:
1637 * @doc: 		the XML document for canonization
1638 * @nodes: 		the nodes set to be included in the canonized image
1639 *      		or NULL if all document nodes should be included
1640 * @exclusive:		the exclusive flag (0 - non-exclusive canonicalization;
1641 *			otherwise - exclusive canonicalization)
1642 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
1643 *			ended with a NULL or NULL if there is no
1644 *			inclusive namespaces (only for exclusive
1645 *			canonicalization, ignored otherwise)
1646 * @with_comments: 	include comments in the result (!=0) or not (==0)
1647 * @buf: 		the output buffer to store canonical XML; this
1648 *			buffer MUST have encoder==NULL because C14N requires
1649 *			UTF-8 output
1650 *
1651 * Dumps the canonized image of given XML document into the provided buffer.
1652 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1653 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1654 *
1655 * Returns non-negative value on success or a negative value on fail
1656 */
1657int
1658xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
1659                 int exclusive, xmlChar ** inclusive_ns_prefixes,
1660                 int with_comments, xmlOutputBufferPtr buf) {
1661    return(xmlC14NExecute(doc,
1662			(xmlC14NIsVisibleCallback)xmlC14NIsNodeInNodeset,
1663			nodes,
1664			exclusive,
1665			inclusive_ns_prefixes,
1666			with_comments,
1667			buf));
1668}
1669
1670
1671/**
1672 * xmlC14NDocDumpMemory:
1673 * @doc: 		the XML document for canonization
1674 * @nodes: 		the nodes set to be included in the canonized image
1675 *      		or NULL if all document nodes should be included
1676 * @exclusive:		the exclusive flag (0 - non-exclusive canonicalization;
1677 *			otherwise - exclusive canonicalization)
1678 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
1679 *			ended with a NULL or NULL if there is no
1680 *			inclusive namespaces (only for exclusive
1681 *			canonicalization, ignored otherwise)
1682 * @with_comments: 	include comments in the result (!=0) or not (==0)
1683 * @doc_txt_ptr: 	the memory pointer for allocated canonical XML text;
1684 *			the caller of this functions is responsible for calling
1685 *			xmlFree() to free allocated memory
1686 *
1687 * Dumps the canonized image of given XML document into memory.
1688 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1689 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1690 *
1691 * Returns the number of bytes written on success or a negative value on fail
1692 */
1693int
1694xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
1695                     int exclusive, xmlChar ** inclusive_ns_prefixes,
1696                     int with_comments, xmlChar ** doc_txt_ptr)
1697{
1698    int ret;
1699    xmlOutputBufferPtr buf;
1700
1701    if (doc_txt_ptr == NULL) {
1702        xmlC14NErrParam("dumping doc to memory");
1703        return (-1);
1704    }
1705
1706    *doc_txt_ptr = NULL;
1707
1708    /*
1709     * create memory buffer with UTF8 (default) encoding
1710     */
1711    buf = xmlAllocOutputBuffer(NULL);
1712    if (buf == NULL) {
1713        xmlC14NErrMemory("creating output buffer");
1714        return (-1);
1715    }
1716
1717    /*
1718     * canonize document and write to buffer
1719     */
1720    ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1721                           with_comments, buf);
1722    if (ret < 0) {
1723        xmlC14NErrInternal("saving doc to output buffer");
1724        (void) xmlOutputBufferClose(buf);
1725        return (-1);
1726    }
1727
1728    ret = buf->buffer->use;
1729    if (ret > 0) {
1730        *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret);
1731    }
1732    (void) xmlOutputBufferClose(buf);
1733
1734    if ((*doc_txt_ptr == NULL) && (ret > 0)) {
1735        xmlC14NErrMemory("coping canonicanized document");
1736        return (-1);
1737    }
1738    return (ret);
1739}
1740
1741/**
1742 * xmlC14NDocSave:
1743 * @doc: 		the XML document for canonization
1744 * @nodes: 		the nodes set to be included in the canonized image
1745 *      		or NULL if all document nodes should be included
1746 * @exclusive:		the exclusive flag (0 - non-exclusive canonicalization;
1747 *			otherwise - exclusive canonicalization)
1748 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
1749 *			ended with a NULL or NULL if there is no
1750 *			inclusive namespaces (only for exclusive
1751 *			canonicalization, ignored otherwise)
1752 * @with_comments: 	include comments in the result (!=0) or not (==0)
1753 * @filename: 		the filename to store canonical XML image
1754 * @compression:	the compression level (zlib requred):
1755 *				-1 - libxml default,
1756 *				 0 - uncompressed,
1757 *				>0 - compression level
1758 *
1759 * Dumps the canonized image of given XML document into the file.
1760 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1761 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1762 *
1763 * Returns the number of bytes written success or a negative value on fail
1764 */
1765int
1766xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
1767               int exclusive, xmlChar ** inclusive_ns_prefixes,
1768               int with_comments, const char *filename, int compression)
1769{
1770    xmlOutputBufferPtr buf;
1771    int ret;
1772
1773    if (filename == NULL) {
1774        xmlC14NErrParam("saving doc");
1775        return (-1);
1776    }
1777#ifdef HAVE_ZLIB_H
1778    if (compression < 0)
1779        compression = xmlGetCompressMode();
1780#endif
1781
1782    /*
1783     * save the content to a temp buffer, use default UTF8 encoding.
1784     */
1785    buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
1786    if (buf == NULL) {
1787        xmlC14NErrInternal("creating temporary filename");
1788        return (-1);
1789    }
1790
1791    /*
1792     * canonize document and write to buffer
1793     */
1794    ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1795                           with_comments, buf);
1796    if (ret < 0) {
1797        xmlC14NErrInternal("cannicanize document to buffer");
1798        (void) xmlOutputBufferClose(buf);
1799        return (-1);
1800    }
1801
1802    /*
1803     * get the numbers of bytes written
1804     */
1805    ret = xmlOutputBufferClose(buf);
1806    return (ret);
1807}
1808
1809
1810
1811/*
1812 * Macro used to grow the current buffer.
1813 */
1814#define growBufferReentrant() {						\
1815    buffer_size *= 2;							\
1816    buffer = (xmlChar *)						\
1817    		xmlRealloc(buffer, buffer_size * sizeof(xmlChar));	\
1818    if (buffer == NULL) {						\
1819	xmlC14NErrMemory("growing buffer");				\
1820	return(NULL);							\
1821    }									\
1822}
1823
1824/**
1825 * xmlC11NNormalizeString:
1826 * @input:		the input string
1827 * @mode:		the normalization mode (attribute, comment, PI or text)
1828 *
1829 * Converts a string to a canonical (normalized) format. The code is stolen
1830 * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
1831 * and the @mode parameter
1832 *
1833 * Returns a normalized string (caller is responsible for calling xmlFree())
1834 * or NULL if an error occurs
1835 */
1836static xmlChar *
1837xmlC11NNormalizeString(const xmlChar * input,
1838                       xmlC14NNormalizationMode mode)
1839{
1840    const xmlChar *cur = input;
1841    xmlChar *buffer = NULL;
1842    xmlChar *out = NULL;
1843    int buffer_size = 0;
1844
1845    if (input == NULL)
1846        return (NULL);
1847
1848    /*
1849     * allocate an translation buffer.
1850     */
1851    buffer_size = 1000;
1852    buffer = (xmlChar *) xmlMallocAtomic(buffer_size * sizeof(xmlChar));
1853    if (buffer == NULL) {
1854	xmlC14NErrMemory("allocating buffer");
1855        return (NULL);
1856    }
1857    out = buffer;
1858
1859    while (*cur != '\0') {
1860        if ((out - buffer) > (buffer_size - 10)) {
1861            int indx = out - buffer;
1862
1863            growBufferReentrant();
1864            out = &buffer[indx];
1865        }
1866
1867        if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1868                              (mode == XMLC14N_NORMALIZE_TEXT))) {
1869            *out++ = '&';
1870            *out++ = 'l';
1871            *out++ = 't';
1872            *out++ = ';';
1873        } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
1874            *out++ = '&';
1875            *out++ = 'g';
1876            *out++ = 't';
1877            *out++ = ';';
1878        } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1879                                     (mode == XMLC14N_NORMALIZE_TEXT))) {
1880            *out++ = '&';
1881            *out++ = 'a';
1882            *out++ = 'm';
1883            *out++ = 'p';
1884            *out++ = ';';
1885        } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1886            *out++ = '&';
1887            *out++ = 'q';
1888            *out++ = 'u';
1889            *out++ = 'o';
1890            *out++ = 't';
1891            *out++ = ';';
1892        } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1893            *out++ = '&';
1894            *out++ = '#';
1895            *out++ = 'x';
1896            *out++ = '9';
1897            *out++ = ';';
1898        } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1899            *out++ = '&';
1900            *out++ = '#';
1901            *out++ = 'x';
1902            *out++ = 'A';
1903            *out++ = ';';
1904        } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1905                                        (mode == XMLC14N_NORMALIZE_TEXT) ||
1906                                        (mode == XMLC14N_NORMALIZE_COMMENT) ||
1907					(mode == XMLC14N_NORMALIZE_PI))) {
1908            *out++ = '&';
1909            *out++ = '#';
1910            *out++ = 'x';
1911            *out++ = 'D';
1912            *out++ = ';';
1913        } else {
1914            /*
1915             * Works because on UTF-8, all extended sequences cannot
1916             * result in bytes in the ASCII range.
1917             */
1918            *out++ = *cur;
1919        }
1920        cur++;
1921    }
1922    *out++ = 0;
1923    return (buffer);
1924}
1925#endif /* LIBXML_OUTPUT_ENABLED */
1926#define bottom_c14n
1927#include "elfgcchack.h"
1928#endif /* LIBXML_C14N_ENABLED */
1929