• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * xmlsave.c: Implementation of the document serializer
3  *
4  * See Copyright for the status of this software.
5  *
6  * daniel@veillard.com
7  */
8 
9 #define IN_LIBXML
10 #include "libxml.h"
11 
12 #include <string.h>
13 #include <libxml/xmlmemory.h>
14 #include <libxml/parserInternals.h>
15 #include <libxml/tree.h>
16 #include <libxml/xmlsave.h>
17 
18 #define MAX_INDENT 60
19 
20 #include <libxml/HTMLtree.h>
21 
22 #include "buf.h"
23 #include "enc.h"
24 #include "save.h"
25 
26 /************************************************************************
27  *									*
28  *			XHTML detection					*
29  *									*
30  ************************************************************************/
31 #define XHTML_STRICT_PUBLIC_ID BAD_CAST \
32    "-//W3C//DTD XHTML 1.0 Strict//EN"
33 #define XHTML_STRICT_SYSTEM_ID BAD_CAST \
34    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
35 #define XHTML_FRAME_PUBLIC_ID BAD_CAST \
36    "-//W3C//DTD XHTML 1.0 Frameset//EN"
37 #define XHTML_FRAME_SYSTEM_ID BAD_CAST \
38    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
39 #define XHTML_TRANS_PUBLIC_ID BAD_CAST \
40    "-//W3C//DTD XHTML 1.0 Transitional//EN"
41 #define XHTML_TRANS_SYSTEM_ID BAD_CAST \
42    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
43 
44 #define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
45 /**
46  * xmlIsXHTML:
47  * @systemID:  the system identifier
48  * @publicID:  the public identifier
49  *
50  * Try to find if the document correspond to an XHTML DTD
51  *
52  * Returns 1 if true, 0 if not and -1 in case of error
53  */
54 int
xmlIsXHTML(const xmlChar * systemID,const xmlChar * publicID)55 xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
56     if ((systemID == NULL) && (publicID == NULL))
57 	return(-1);
58     if (publicID != NULL) {
59 	if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
60 	if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
61 	if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
62     }
63     if (systemID != NULL) {
64 	if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
65 	if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
66 	if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
67     }
68     return(0);
69 }
70 
71 #ifdef LIBXML_OUTPUT_ENABLED
72 
73 #define TODO								\
74     xmlGenericError(xmlGenericErrorContext,				\
75 	    "Unimplemented block at %s:%d\n",				\
76             __FILE__, __LINE__);
77 
78 struct _xmlSaveCtxt {
79     void *_private;
80     int type;
81     int fd;
82     const xmlChar *filename;
83     const xmlChar *encoding;
84     xmlCharEncodingHandlerPtr handler;
85     xmlOutputBufferPtr buf;
86     int options;
87     int level;
88     int format;
89     char indent[MAX_INDENT + 1];	/* array for indenting output */
90     int indent_nr;
91     int indent_size;
92     xmlCharEncodingOutputFunc escape;	/* used for element content */
93     xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */
94 };
95 
96 /************************************************************************
97  *									*
98  *			Output error handlers				*
99  *									*
100  ************************************************************************/
101 /**
102  * xmlSaveErrMemory:
103  * @extra:  extra information
104  *
105  * Handle an out of memory condition
106  */
107 static void
xmlSaveErrMemory(const char * extra)108 xmlSaveErrMemory(const char *extra)
109 {
110     __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
111 }
112 
113 /**
114  * xmlSaveErr:
115  * @code:  the error number
116  * @node:  the location of the error.
117  * @extra:  extra information
118  *
119  * Handle an out of memory condition
120  */
121 static void
xmlSaveErr(int code,xmlNodePtr node,const char * extra)122 xmlSaveErr(int code, xmlNodePtr node, const char *extra)
123 {
124     const char *msg = NULL;
125 
126     switch(code) {
127         case XML_SAVE_NOT_UTF8:
128 	    msg = "string is not in UTF-8\n";
129 	    break;
130 	case XML_SAVE_CHAR_INVALID:
131 	    msg = "invalid character value\n";
132 	    break;
133 	case XML_SAVE_UNKNOWN_ENCODING:
134 	    msg = "unknown encoding %s\n";
135 	    break;
136 	case XML_SAVE_NO_DOCTYPE:
137 	    msg = "document has no DOCTYPE\n";
138 	    break;
139 	default:
140 	    msg = "unexpected error number\n";
141     }
142     __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
143 }
144 
145 /************************************************************************
146  *									*
147  *			Special escaping routines			*
148  *									*
149  ************************************************************************/
150 static unsigned char *
xmlSerializeHexCharRef(unsigned char * out,int val)151 xmlSerializeHexCharRef(unsigned char *out, int val) {
152     unsigned char *ptr;
153 
154     *out++ = '&';
155     *out++ = '#';
156     *out++ = 'x';
157     if (val < 0x10) ptr = out;
158     else if (val < 0x100) ptr = out + 1;
159     else if (val < 0x1000) ptr = out + 2;
160     else if (val < 0x10000) ptr = out + 3;
161     else if (val < 0x100000) ptr = out + 4;
162     else ptr = out + 5;
163     out = ptr + 1;
164     while (val > 0) {
165 	switch (val & 0xF) {
166 	    case 0: *ptr-- = '0'; break;
167 	    case 1: *ptr-- = '1'; break;
168 	    case 2: *ptr-- = '2'; break;
169 	    case 3: *ptr-- = '3'; break;
170 	    case 4: *ptr-- = '4'; break;
171 	    case 5: *ptr-- = '5'; break;
172 	    case 6: *ptr-- = '6'; break;
173 	    case 7: *ptr-- = '7'; break;
174 	    case 8: *ptr-- = '8'; break;
175 	    case 9: *ptr-- = '9'; break;
176 	    case 0xA: *ptr-- = 'A'; break;
177 	    case 0xB: *ptr-- = 'B'; break;
178 	    case 0xC: *ptr-- = 'C'; break;
179 	    case 0xD: *ptr-- = 'D'; break;
180 	    case 0xE: *ptr-- = 'E'; break;
181 	    case 0xF: *ptr-- = 'F'; break;
182 	    default: *ptr-- = '0'; break;
183 	}
184 	val >>= 4;
185     }
186     *out++ = ';';
187     *out = 0;
188     return(out);
189 }
190 
191 /**
192  * xmlEscapeEntities:
193  * @out:  a pointer to an array of bytes to store the result
194  * @outlen:  the length of @out
195  * @in:  a pointer to an array of unescaped UTF-8 bytes
196  * @inlen:  the length of @in
197  *
198  * Take a block of UTF-8 chars in and escape them. Used when there is no
199  * encoding specified.
200  *
201  * Returns 0 if success, or -1 otherwise
202  * The value of @inlen after return is the number of octets consumed
203  *     if the return value is positive, else unpredictable.
204  * The value of @outlen after return is the number of octets consumed.
205  */
206 static int
xmlEscapeEntities(unsigned char * out,int * outlen,const xmlChar * in,int * inlen)207 xmlEscapeEntities(unsigned char* out, int *outlen,
208                  const xmlChar* in, int *inlen) {
209     unsigned char* outstart = out;
210     const unsigned char* base = in;
211     unsigned char* outend = out + *outlen;
212     const unsigned char* inend;
213     int val;
214 
215     inend = in + (*inlen);
216 
217     while ((in < inend) && (out < outend)) {
218 	if (*in == '<') {
219 	    if (outend - out < 4) break;
220 	    *out++ = '&';
221 	    *out++ = 'l';
222 	    *out++ = 't';
223 	    *out++ = ';';
224 	    in++;
225 	    continue;
226 	} else if (*in == '>') {
227 	    if (outend - out < 4) break;
228 	    *out++ = '&';
229 	    *out++ = 'g';
230 	    *out++ = 't';
231 	    *out++ = ';';
232 	    in++;
233 	    continue;
234 	} else if (*in == '&') {
235 	    if (outend - out < 5) break;
236 	    *out++ = '&';
237 	    *out++ = 'a';
238 	    *out++ = 'm';
239 	    *out++ = 'p';
240 	    *out++ = ';';
241 	    in++;
242 	    continue;
243 	} else if (((*in >= 0x20) && (*in < 0x80)) ||
244 	           (*in == '\n') || (*in == '\t')) {
245 	    /*
246 	     * default case, just copy !
247 	     */
248 	    *out++ = *in++;
249 	    continue;
250 	} else if (*in >= 0x80) {
251 	    /*
252 	     * We assume we have UTF-8 input.
253 	     */
254 	    if (outend - out < 11) break;
255 
256 	    if (*in < 0xC0) {
257 		xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
258 		in++;
259 		goto error;
260 	    } else if (*in < 0xE0) {
261 		if (inend - in < 2) break;
262 		val = (in[0]) & 0x1F;
263 		val <<= 6;
264 		val |= (in[1]) & 0x3F;
265 		in += 2;
266 	    } else if (*in < 0xF0) {
267 		if (inend - in < 3) break;
268 		val = (in[0]) & 0x0F;
269 		val <<= 6;
270 		val |= (in[1]) & 0x3F;
271 		val <<= 6;
272 		val |= (in[2]) & 0x3F;
273 		in += 3;
274 	    } else if (*in < 0xF8) {
275 		if (inend - in < 4) break;
276 		val = (in[0]) & 0x07;
277 		val <<= 6;
278 		val |= (in[1]) & 0x3F;
279 		val <<= 6;
280 		val |= (in[2]) & 0x3F;
281 		val <<= 6;
282 		val |= (in[3]) & 0x3F;
283 		in += 4;
284 	    } else {
285 		xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
286 		in++;
287 		goto error;
288 	    }
289 	    if (!IS_CHAR(val)) {
290 		xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
291 		in++;
292 		goto error;
293 	    }
294 
295 	    /*
296 	     * We could do multiple things here. Just save as a char ref
297 	     */
298 	    out = xmlSerializeHexCharRef(out, val);
299 	} else if (IS_BYTE_CHAR(*in)) {
300 	    if (outend - out < 6) break;
301 	    out = xmlSerializeHexCharRef(out, *in++);
302 	} else {
303 	    xmlGenericError(xmlGenericErrorContext,
304 		"xmlEscapeEntities : char out of range\n");
305 	    in++;
306 	    goto error;
307 	}
308     }
309     *outlen = out - outstart;
310     *inlen = in - base;
311     return(0);
312 error:
313     *outlen = out - outstart;
314     *inlen = in - base;
315     return(-1);
316 }
317 
318 /************************************************************************
319  *									*
320  *			Allocation and deallocation			*
321  *									*
322  ************************************************************************/
323 /**
324  * xmlSaveCtxtInit:
325  * @ctxt: the saving context
326  *
327  * Initialize a saving context
328  */
329 static void
xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)330 xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
331 {
332     int i;
333     int len;
334 
335     if (ctxt == NULL) return;
336     if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
337         ctxt->escape = xmlEscapeEntities;
338     len = xmlStrlen((xmlChar *)xmlTreeIndentString);
339     if ((xmlTreeIndentString == NULL) || (len == 0)) {
340         memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
341     } else {
342 	ctxt->indent_size = len;
343 	ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
344 	for (i = 0;i < ctxt->indent_nr;i++)
345 	    memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
346 		   ctxt->indent_size);
347         ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
348     }
349 
350     if (xmlSaveNoEmptyTags) {
351 	ctxt->options |= XML_SAVE_NO_EMPTY;
352     }
353 }
354 
355 /**
356  * xmlFreeSaveCtxt:
357  *
358  * Free a saving context, destroying the output in any remaining buffer
359  */
360 static void
xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)361 xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
362 {
363     if (ctxt == NULL) return;
364     if (ctxt->encoding != NULL)
365         xmlFree((char *) ctxt->encoding);
366     if (ctxt->buf != NULL)
367         xmlOutputBufferClose(ctxt->buf);
368     xmlFree(ctxt);
369 }
370 
371 /**
372  * xmlNewSaveCtxt:
373  *
374  * Create a new saving context
375  *
376  * Returns the new structure or NULL in case of error
377  */
378 static xmlSaveCtxtPtr
xmlNewSaveCtxt(const char * encoding,int options)379 xmlNewSaveCtxt(const char *encoding, int options)
380 {
381     xmlSaveCtxtPtr ret;
382 
383     ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
384     if (ret == NULL) {
385 	xmlSaveErrMemory("creating saving context");
386 	return ( NULL );
387     }
388     memset(ret, 0, sizeof(xmlSaveCtxt));
389 
390     if (encoding != NULL) {
391         ret->handler = xmlFindCharEncodingHandler(encoding);
392 	if (ret->handler == NULL) {
393 	    xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
394             xmlFreeSaveCtxt(ret);
395 	    return(NULL);
396 	}
397         ret->encoding = xmlStrdup((const xmlChar *)encoding);
398 	ret->escape = NULL;
399     }
400     xmlSaveCtxtInit(ret);
401 
402     /*
403      * Use the options
404      */
405 
406     /* Re-check this option as it may already have been set */
407     if ((ret->options & XML_SAVE_NO_EMPTY) && ! (options & XML_SAVE_NO_EMPTY)) {
408 	options |= XML_SAVE_NO_EMPTY;
409     }
410 
411     ret->options = options;
412     if (options & XML_SAVE_FORMAT)
413         ret->format = 1;
414     else if (options & XML_SAVE_WSNONSIG)
415         ret->format = 2;
416 
417     return(ret);
418 }
419 
420 /************************************************************************
421  *									*
422  *		Dumping XML tree content to a simple buffer		*
423  *									*
424  ************************************************************************/
425 /**
426  * xmlAttrSerializeContent:
427  * @buf:  the XML buffer output
428  * @doc:  the document
429  * @attr:  the attribute pointer
430  *
431  * Serialize the attribute in the buffer
432  */
433 static void
xmlAttrSerializeContent(xmlOutputBufferPtr buf,xmlAttrPtr attr)434 xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
435 {
436     xmlNodePtr children;
437 
438     children = attr->children;
439     while (children != NULL) {
440         switch (children->type) {
441             case XML_TEXT_NODE:
442 	        xmlBufAttrSerializeTxtContent(buf->buffer, attr->doc,
443 		                              attr, children->content);
444 		break;
445             case XML_ENTITY_REF_NODE:
446                 xmlBufAdd(buf->buffer, BAD_CAST "&", 1);
447                 xmlBufAdd(buf->buffer, children->name,
448                              xmlStrlen(children->name));
449                 xmlBufAdd(buf->buffer, BAD_CAST ";", 1);
450                 break;
451             default:
452                 /* should not happen unless we have a badly built tree */
453                 break;
454         }
455         children = children->next;
456     }
457 }
458 
459 /**
460  * xmlBufDumpNotationTable:
461  * @buf:  an xmlBufPtr output
462  * @table:  A notation table
463  *
464  * This will dump the content of the notation table as an XML DTD definition
465  */
466 void
xmlBufDumpNotationTable(xmlBufPtr buf,xmlNotationTablePtr table)467 xmlBufDumpNotationTable(xmlBufPtr buf, xmlNotationTablePtr table) {
468     xmlBufferPtr buffer;
469 
470     buffer = xmlBufferCreate();
471     if (buffer == NULL) {
472         /*
473          * TODO set the error in buf
474          */
475         return;
476     }
477     xmlDumpNotationTable(buffer, table);
478     xmlBufMergeBuffer(buf, buffer);
479 }
480 
481 /**
482  * xmlBufDumpElementDecl:
483  * @buf:  an xmlBufPtr output
484  * @elem:  An element table
485  *
486  * This will dump the content of the element declaration as an XML
487  * DTD definition
488  */
489 void
xmlBufDumpElementDecl(xmlBufPtr buf,xmlElementPtr elem)490 xmlBufDumpElementDecl(xmlBufPtr buf, xmlElementPtr elem) {
491     xmlBufferPtr buffer;
492 
493     buffer = xmlBufferCreate();
494     if (buffer == NULL) {
495         /*
496          * TODO set the error in buf
497          */
498         return;
499     }
500     xmlDumpElementDecl(buffer, elem);
501     xmlBufMergeBuffer(buf, buffer);
502 }
503 
504 /**
505  * xmlBufDumpAttributeDecl:
506  * @buf:  an xmlBufPtr output
507  * @attr:  An attribute declaration
508  *
509  * This will dump the content of the attribute declaration as an XML
510  * DTD definition
511  */
512 void
xmlBufDumpAttributeDecl(xmlBufPtr buf,xmlAttributePtr attr)513 xmlBufDumpAttributeDecl(xmlBufPtr buf, xmlAttributePtr attr) {
514     xmlBufferPtr buffer;
515 
516     buffer = xmlBufferCreate();
517     if (buffer == NULL) {
518         /*
519          * TODO set the error in buf
520          */
521         return;
522     }
523     xmlDumpAttributeDecl(buffer, attr);
524     xmlBufMergeBuffer(buf, buffer);
525 }
526 
527 /**
528  * xmlBufDumpEntityDecl:
529  * @buf:  an xmlBufPtr output
530  * @ent:  An entity table
531  *
532  * This will dump the content of the entity table as an XML DTD definition
533  */
534 void
xmlBufDumpEntityDecl(xmlBufPtr buf,xmlEntityPtr ent)535 xmlBufDumpEntityDecl(xmlBufPtr buf, xmlEntityPtr ent) {
536     xmlBufferPtr buffer;
537 
538     buffer = xmlBufferCreate();
539     if (buffer == NULL) {
540         /*
541          * TODO set the error in buf
542          */
543         return;
544     }
545     xmlDumpEntityDecl(buffer, ent);
546     xmlBufMergeBuffer(buf, buffer);
547 }
548 
549 /************************************************************************
550  *									*
551  *		Dumping XML tree content to an I/O output buffer	*
552  *									*
553  ************************************************************************/
554 
xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt,const char * encoding)555 static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
556     xmlOutputBufferPtr buf = ctxt->buf;
557 
558     if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
559 	buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
560 	if (buf->encoder == NULL) {
561 	    xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
562 		       (const char *)encoding);
563 	    return(-1);
564 	}
565 	buf->conv = xmlBufCreate();
566 	if (buf->conv == NULL) {
567 	    xmlCharEncCloseFunc(buf->encoder);
568 	    xmlSaveErrMemory("creating encoding buffer");
569 	    return(-1);
570 	}
571 	/*
572 	 * initialize the state, e.g. if outputting a BOM
573 	 */
574         xmlCharEncOutput(buf, 1);
575     }
576     return(0);
577 }
578 
xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt)579 static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
580     xmlOutputBufferPtr buf = ctxt->buf;
581     xmlOutputBufferFlush(buf);
582     xmlCharEncCloseFunc(buf->encoder);
583     xmlBufFree(buf->conv);
584     buf->encoder = NULL;
585     buf->conv = NULL;
586     return(0);
587 }
588 
589 #ifdef LIBXML_HTML_ENABLED
590 static void
591 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
592 #endif
593 static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
594 void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
595 static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
596 
597 /**
598  * xmlOutputBufferWriteWSNonSig:
599  * @ctxt:  The save context
600  * @extra: Number of extra indents to apply to ctxt->level
601  *
602  * Write out formatting for non-significant whitespace output.
603  */
604 static void
xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt,int extra)605 xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra)
606 {
607     int i;
608     if ((ctxt == NULL) || (ctxt->buf == NULL))
609         return;
610     xmlOutputBufferWrite(ctxt->buf, 1, "\n");
611     for (i = 0; i < (ctxt->level + extra); i += ctxt->indent_nr) {
612         xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size *
613                 ((ctxt->level + extra - i) > ctxt->indent_nr ?
614                  ctxt->indent_nr : (ctxt->level + extra - i)),
615                 ctxt->indent);
616     }
617 }
618 
619 /**
620  * xmlNsDumpOutput:
621  * @buf:  the XML buffer output
622  * @cur:  a namespace
623  * @ctxt: the output save context. Optional.
624  *
625  * Dump a local Namespace definition.
626  * Should be called in the context of attributes dumps.
627  * If @ctxt is supplied, @buf should be its buffer.
628  */
629 static void
xmlNsDumpOutput(xmlOutputBufferPtr buf,xmlNsPtr cur,xmlSaveCtxtPtr ctxt)630 xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
631     if ((cur == NULL) || (buf == NULL)) return;
632     if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
633 	if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
634 	    return;
635 
636 	if (ctxt != NULL && ctxt->format == 2)
637 	    xmlOutputBufferWriteWSNonSig(ctxt, 2);
638 	else
639 	    xmlOutputBufferWrite(buf, 1, " ");
640 
641         /* Within the context of an element attributes */
642 	if (cur->prefix != NULL) {
643 	    xmlOutputBufferWrite(buf, 6, "xmlns:");
644 	    xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
645 	} else
646 	    xmlOutputBufferWrite(buf, 5, "xmlns");
647 	xmlOutputBufferWrite(buf, 1, "=");
648 	xmlBufWriteQuotedString(buf->buffer, cur->href);
649     }
650 }
651 
652 /**
653  * xmlNsDumpOutputCtxt
654  * @ctxt: the save context
655  * @cur:  a namespace
656  *
657  * Dump a local Namespace definition to a save context.
658  * Should be called in the context of attribute dumps.
659  */
660 static void
xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt,xmlNsPtr cur)661 xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
662     xmlNsDumpOutput(ctxt->buf, cur, ctxt);
663 }
664 
665 /**
666  * xmlNsListDumpOutputCtxt
667  * @ctxt: the save context
668  * @cur:  the first namespace
669  *
670  * Dump a list of local namespace definitions to a save context.
671  * Should be called in the context of attribute dumps.
672  */
673 static void
xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt,xmlNsPtr cur)674 xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
675     while (cur != NULL) {
676         xmlNsDumpOutput(ctxt->buf, cur, ctxt);
677 	cur = cur->next;
678     }
679 }
680 
681 /**
682  * xmlNsListDumpOutput:
683  * @buf:  the XML buffer output
684  * @cur:  the first namespace
685  *
686  * Dump a list of local Namespace definitions.
687  * Should be called in the context of attributes dumps.
688  */
689 void
xmlNsListDumpOutput(xmlOutputBufferPtr buf,xmlNsPtr cur)690 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
691     while (cur != NULL) {
692         xmlNsDumpOutput(buf, cur, NULL);
693 	cur = cur->next;
694     }
695 }
696 
697 /**
698  * xmlDtdDumpOutput:
699  * @buf:  the XML buffer output
700  * @dtd:  the pointer to the DTD
701  *
702  * Dump the XML document DTD, if any.
703  */
704 static void
xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt,xmlDtdPtr dtd)705 xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
706     xmlOutputBufferPtr buf;
707     xmlNodePtr cur;
708     int format, level;
709 
710     if (dtd == NULL) return;
711     if ((ctxt == NULL) || (ctxt->buf == NULL))
712         return;
713     buf = ctxt->buf;
714     xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
715     xmlOutputBufferWriteString(buf, (const char *)dtd->name);
716     if (dtd->ExternalID != NULL) {
717 	xmlOutputBufferWrite(buf, 8, " PUBLIC ");
718 	xmlBufWriteQuotedString(buf->buffer, dtd->ExternalID);
719 	xmlOutputBufferWrite(buf, 1, " ");
720 	xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
721     }  else if (dtd->SystemID != NULL) {
722 	xmlOutputBufferWrite(buf, 8, " SYSTEM ");
723 	xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
724     }
725     if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
726         (dtd->attributes == NULL) && (dtd->notations == NULL) &&
727 	(dtd->pentities == NULL)) {
728 	xmlOutputBufferWrite(buf, 1, ">");
729 	return;
730     }
731     xmlOutputBufferWrite(buf, 3, " [\n");
732     /*
733      * Dump the notations first they are not in the DTD children list
734      * Do this only on a standalone DTD or on the internal subset though.
735      */
736     if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
737         (dtd->doc->intSubset == dtd))) {
738         xmlBufDumpNotationTable(buf->buffer,
739                                 (xmlNotationTablePtr) dtd->notations);
740     }
741     format = ctxt->format;
742     level = ctxt->level;
743     ctxt->format = 0;
744     ctxt->level = -1;
745     for (cur = dtd->children; cur != NULL; cur = cur->next) {
746         xmlNodeDumpOutputInternal(ctxt, cur);
747     }
748     ctxt->format = format;
749     ctxt->level = level;
750     xmlOutputBufferWrite(buf, 2, "]>");
751 }
752 
753 /**
754  * xmlAttrDumpOutput:
755  * @buf:  the XML buffer output
756  * @cur:  the attribute pointer
757  *
758  * Dump an XML attribute
759  */
760 static void
xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt,xmlAttrPtr cur)761 xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
762     xmlOutputBufferPtr buf;
763 
764     if (cur == NULL) return;
765     buf = ctxt->buf;
766     if (buf == NULL) return;
767     if (ctxt->format == 2)
768         xmlOutputBufferWriteWSNonSig(ctxt, 2);
769     else
770         xmlOutputBufferWrite(buf, 1, " ");
771     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
772         xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
773 	xmlOutputBufferWrite(buf, 1, ":");
774     }
775     xmlOutputBufferWriteString(buf, (const char *)cur->name);
776     xmlOutputBufferWrite(buf, 2, "=\"");
777     xmlAttrSerializeContent(buf, cur);
778     xmlOutputBufferWrite(buf, 1, "\"");
779 }
780 
781 #ifdef LIBXML_HTML_ENABLED
782 /**
783  * htmlNodeDumpOutputInternal:
784  * @cur:  the current node
785  *
786  * Dump an HTML node, recursive behaviour, children are printed too.
787  */
788 static int
htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)789 htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
790     const xmlChar *oldenc = NULL;
791     const xmlChar *oldctxtenc = ctxt->encoding;
792     const xmlChar *encoding = ctxt->encoding;
793     xmlOutputBufferPtr buf = ctxt->buf;
794     int switched_encoding = 0;
795     xmlDocPtr doc;
796 
797     xmlInitParser();
798 
799     doc = cur->doc;
800     if (doc != NULL) {
801         oldenc = doc->encoding;
802 	if (ctxt->encoding != NULL) {
803 	    doc->encoding = BAD_CAST ctxt->encoding;
804 	} else if (doc->encoding != NULL) {
805 	    encoding = doc->encoding;
806 	}
807     }
808 
809     if ((encoding != NULL) && (doc != NULL))
810 	htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
811     if ((encoding == NULL) && (doc != NULL))
812 	encoding = htmlGetMetaEncoding(doc);
813     if (encoding == NULL)
814 	encoding = BAD_CAST "HTML";
815     if ((encoding != NULL) && (oldctxtenc == NULL) &&
816 	(buf->encoder == NULL) && (buf->conv == NULL)) {
817 	if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
818 	    doc->encoding = oldenc;
819 	    return(-1);
820 	}
821 	switched_encoding = 1;
822     }
823     if (ctxt->options & XML_SAVE_FORMAT)
824 	htmlNodeDumpFormatOutput(buf, doc, cur,
825 				       (const char *)encoding, 1);
826     else
827 	htmlNodeDumpFormatOutput(buf, doc, cur,
828 				       (const char *)encoding, 0);
829     /*
830      * Restore the state of the saving context at the end of the document
831      */
832     if ((switched_encoding) && (oldctxtenc == NULL)) {
833 	xmlSaveClearEncoding(ctxt);
834     }
835     if (doc != NULL)
836 	doc->encoding = oldenc;
837     return(0);
838 }
839 #endif
840 
841 /**
842  * xmlNodeDumpOutputInternal:
843  * @cur:  the current node
844  *
845  * Dump an XML node, recursive behaviour, children are printed too.
846  */
847 static void
xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)848 xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
849     int format = ctxt->format;
850     xmlNodePtr tmp, root, unformattedNode = NULL, parent;
851     xmlAttrPtr attr;
852     xmlChar *start, *end;
853     xmlOutputBufferPtr buf;
854 
855     if (cur == NULL) return;
856     buf = ctxt->buf;
857 
858     root = cur;
859     parent = cur->parent;
860     while (1) {
861         switch (cur->type) {
862         case XML_DOCUMENT_NODE:
863         case XML_HTML_DOCUMENT_NODE:
864 	    xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
865 	    break;
866 
867         case XML_DTD_NODE:
868             xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
869             break;
870 
871         case XML_DOCUMENT_FRAG_NODE:
872             /* Always validate cur->parent when descending. */
873             if ((cur->parent == parent) && (cur->children != NULL)) {
874                 parent = cur;
875                 cur = cur->children;
876                 continue;
877             }
878 	    break;
879 
880         case XML_ELEMENT_DECL:
881             xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
882             break;
883 
884         case XML_ATTRIBUTE_DECL:
885             xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
886             break;
887 
888         case XML_ENTITY_DECL:
889             xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
890             break;
891 
892         case XML_ELEMENT_NODE:
893 	    if ((cur != root) && (ctxt->format == 1) &&
894                 (xmlIndentTreeOutput))
895 		xmlOutputBufferWrite(buf, ctxt->indent_size *
896 				     (ctxt->level > ctxt->indent_nr ?
897 				      ctxt->indent_nr : ctxt->level),
898 				     ctxt->indent);
899 
900             /*
901              * Some users like lxml are known to pass nodes with a corrupted
902              * tree structure. Fall back to a recursive call to handle this
903              * case.
904              */
905             if ((cur->parent != parent) && (cur->children != NULL)) {
906                 xmlNodeDumpOutputInternal(ctxt, cur);
907                 break;
908             }
909 
910             xmlOutputBufferWrite(buf, 1, "<");
911             if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
912                 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
913                 xmlOutputBufferWrite(buf, 1, ":");
914             }
915             xmlOutputBufferWriteString(buf, (const char *)cur->name);
916             if (cur->nsDef)
917                 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
918             for (attr = cur->properties; attr != NULL; attr = attr->next)
919                 xmlAttrDumpOutput(ctxt, attr);
920 
921             if (cur->children == NULL) {
922                 if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
923                     if (ctxt->format == 2)
924                         xmlOutputBufferWriteWSNonSig(ctxt, 0);
925                     xmlOutputBufferWrite(buf, 2, "/>");
926                 } else {
927                     if (ctxt->format == 2)
928                         xmlOutputBufferWriteWSNonSig(ctxt, 1);
929                     xmlOutputBufferWrite(buf, 3, "></");
930                     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
931                         xmlOutputBufferWriteString(buf,
932                                 (const char *)cur->ns->prefix);
933                         xmlOutputBufferWrite(buf, 1, ":");
934                     }
935                     xmlOutputBufferWriteString(buf, (const char *)cur->name);
936                     if (ctxt->format == 2)
937                         xmlOutputBufferWriteWSNonSig(ctxt, 0);
938                     xmlOutputBufferWrite(buf, 1, ">");
939                 }
940             } else {
941                 if (ctxt->format == 1) {
942                     tmp = cur->children;
943                     while (tmp != NULL) {
944                         if ((tmp->type == XML_TEXT_NODE) ||
945                             (tmp->type == XML_CDATA_SECTION_NODE) ||
946                             (tmp->type == XML_ENTITY_REF_NODE)) {
947                             ctxt->format = 0;
948                             unformattedNode = cur;
949                             break;
950                         }
951                         tmp = tmp->next;
952                     }
953                 }
954                 if (ctxt->format == 2)
955                     xmlOutputBufferWriteWSNonSig(ctxt, 1);
956                 xmlOutputBufferWrite(buf, 1, ">");
957                 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
958                 if (ctxt->level >= 0) ctxt->level++;
959                 parent = cur;
960                 cur = cur->children;
961                 continue;
962             }
963 
964             break;
965 
966         case XML_TEXT_NODE:
967 	    if (cur->content == NULL)
968                 break;
969 	    if (cur->name != xmlStringTextNoenc) {
970                 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
971 	    } else {
972 		/*
973 		 * Disable escaping, needed for XSLT
974 		 */
975 		xmlOutputBufferWriteString(buf, (const char *) cur->content);
976 	    }
977 	    break;
978 
979         case XML_PI_NODE:
980 	    if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
981 		xmlOutputBufferWrite(buf, ctxt->indent_size *
982 				     (ctxt->level > ctxt->indent_nr ?
983 				      ctxt->indent_nr : ctxt->level),
984 				     ctxt->indent);
985 
986             if (cur->content != NULL) {
987                 xmlOutputBufferWrite(buf, 2, "<?");
988                 xmlOutputBufferWriteString(buf, (const char *)cur->name);
989                 if (cur->content != NULL) {
990                     if (ctxt->format == 2)
991                         xmlOutputBufferWriteWSNonSig(ctxt, 0);
992                     else
993                         xmlOutputBufferWrite(buf, 1, " ");
994                     xmlOutputBufferWriteString(buf,
995                             (const char *)cur->content);
996                 }
997                 xmlOutputBufferWrite(buf, 2, "?>");
998             } else {
999                 xmlOutputBufferWrite(buf, 2, "<?");
1000                 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1001                 if (ctxt->format == 2)
1002                     xmlOutputBufferWriteWSNonSig(ctxt, 0);
1003                 xmlOutputBufferWrite(buf, 2, "?>");
1004             }
1005             break;
1006 
1007         case XML_COMMENT_NODE:
1008 	    if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
1009 		xmlOutputBufferWrite(buf, ctxt->indent_size *
1010 				     (ctxt->level > ctxt->indent_nr ?
1011 				      ctxt->indent_nr : ctxt->level),
1012 				     ctxt->indent);
1013 
1014             if (cur->content != NULL) {
1015                 xmlOutputBufferWrite(buf, 4, "<!--");
1016                 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1017                 xmlOutputBufferWrite(buf, 3, "-->");
1018             }
1019             break;
1020 
1021         case XML_ENTITY_REF_NODE:
1022             xmlOutputBufferWrite(buf, 1, "&");
1023             xmlOutputBufferWriteString(buf, (const char *)cur->name);
1024             xmlOutputBufferWrite(buf, 1, ";");
1025             break;
1026 
1027         case XML_CDATA_SECTION_NODE:
1028             if (cur->content == NULL || *cur->content == '\0') {
1029                 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1030             } else {
1031                 start = end = cur->content;
1032                 while (*end != '\0') {
1033                     if ((*end == ']') && (*(end + 1) == ']') &&
1034                         (*(end + 2) == '>')) {
1035                         end = end + 2;
1036                         xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1037                         xmlOutputBufferWrite(buf, end - start,
1038                                 (const char *)start);
1039                         xmlOutputBufferWrite(buf, 3, "]]>");
1040                         start = end;
1041                     }
1042                     end++;
1043                 }
1044                 if (start != end) {
1045                     xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1046                     xmlOutputBufferWriteString(buf, (const char *)start);
1047                     xmlOutputBufferWrite(buf, 3, "]]>");
1048                 }
1049             }
1050             break;
1051 
1052         case XML_ATTRIBUTE_NODE:
1053             xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1054             break;
1055 
1056         case XML_NAMESPACE_DECL:
1057             xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1058             break;
1059 
1060         default:
1061             break;
1062         }
1063 
1064         while (1) {
1065             if (cur == root)
1066                 return;
1067             if ((ctxt->format == 1) &&
1068                 (cur->type != XML_XINCLUDE_START) &&
1069                 (cur->type != XML_XINCLUDE_END))
1070                 xmlOutputBufferWrite(buf, 1, "\n");
1071             if (cur->next != NULL) {
1072                 cur = cur->next;
1073                 break;
1074             }
1075 
1076             cur = parent;
1077             /* cur->parent was validated when descending. */
1078             parent = cur->parent;
1079 
1080             if (cur->type == XML_ELEMENT_NODE) {
1081                 if (ctxt->level > 0) ctxt->level--;
1082                 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1083                     xmlOutputBufferWrite(buf, ctxt->indent_size *
1084                                          (ctxt->level > ctxt->indent_nr ?
1085                                           ctxt->indent_nr : ctxt->level),
1086                                          ctxt->indent);
1087 
1088                 xmlOutputBufferWrite(buf, 2, "</");
1089                 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1090                     xmlOutputBufferWriteString(buf,
1091                             (const char *)cur->ns->prefix);
1092                     xmlOutputBufferWrite(buf, 1, ":");
1093                 }
1094 
1095                 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1096                 if (ctxt->format == 2)
1097                     xmlOutputBufferWriteWSNonSig(ctxt, 0);
1098                 xmlOutputBufferWrite(buf, 1, ">");
1099 
1100                 if (cur == unformattedNode) {
1101                     ctxt->format = format;
1102                     unformattedNode = NULL;
1103                 }
1104             }
1105         }
1106     }
1107 }
1108 
1109 /**
1110  * xmlDocContentDumpOutput:
1111  * @cur:  the document
1112  *
1113  * Dump an XML document.
1114  */
1115 static int
xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt,xmlDocPtr cur)1116 xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
1117 #ifdef LIBXML_HTML_ENABLED
1118     xmlDtdPtr dtd;
1119     int is_xhtml = 0;
1120 #endif
1121     const xmlChar *oldenc = cur->encoding;
1122     const xmlChar *oldctxtenc = ctxt->encoding;
1123     const xmlChar *encoding = ctxt->encoding;
1124     xmlCharEncodingOutputFunc oldescape = ctxt->escape;
1125     xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
1126     xmlOutputBufferPtr buf = ctxt->buf;
1127     xmlCharEncoding enc;
1128     int switched_encoding = 0;
1129 
1130     xmlInitParser();
1131 
1132     if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
1133         (cur->type != XML_DOCUMENT_NODE))
1134 	 return(-1);
1135 
1136     if (ctxt->encoding != NULL) {
1137         cur->encoding = BAD_CAST ctxt->encoding;
1138     } else if (cur->encoding != NULL) {
1139 	encoding = cur->encoding;
1140     }
1141 
1142     if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
1143          ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
1144          ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
1145         (ctxt->options & XML_SAVE_AS_HTML)) {
1146 #ifdef LIBXML_HTML_ENABLED
1147         if (encoding != NULL)
1148 	    htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1149         if (encoding == NULL)
1150 	    encoding = htmlGetMetaEncoding(cur);
1151         if (encoding == NULL)
1152 	    encoding = BAD_CAST "HTML";
1153 	if ((encoding != NULL) && (oldctxtenc == NULL) &&
1154 	    (buf->encoder == NULL) && (buf->conv == NULL)) {
1155 	    if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1156 		cur->encoding = oldenc;
1157 		return(-1);
1158 	    }
1159 	}
1160         if (ctxt->options & XML_SAVE_FORMAT)
1161 	    htmlDocContentDumpFormatOutput(buf, cur,
1162 	                                   (const char *)encoding, 1);
1163 	else
1164 	    htmlDocContentDumpFormatOutput(buf, cur,
1165 	                                   (const char *)encoding, 0);
1166 	if (ctxt->encoding != NULL)
1167 	    cur->encoding = oldenc;
1168 	return(0);
1169 #else
1170         return(-1);
1171 #endif
1172     } else if ((cur->type == XML_DOCUMENT_NODE) ||
1173                (ctxt->options & XML_SAVE_AS_XML) ||
1174                (ctxt->options & XML_SAVE_XHTML)) {
1175 	enc = xmlParseCharEncoding((const char*) encoding);
1176 	if ((encoding != NULL) && (oldctxtenc == NULL) &&
1177 	    (buf->encoder == NULL) && (buf->conv == NULL) &&
1178 	    ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
1179 	    if ((enc != XML_CHAR_ENCODING_UTF8) &&
1180 		(enc != XML_CHAR_ENCODING_NONE) &&
1181 		(enc != XML_CHAR_ENCODING_ASCII)) {
1182 		/*
1183 		 * we need to switch to this encoding but just for this
1184 		 * document since we output the XMLDecl the conversion
1185 		 * must be done to not generate not well formed documents.
1186 		 */
1187 		if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1188 		    cur->encoding = oldenc;
1189 		    return(-1);
1190 		}
1191 		switched_encoding = 1;
1192 	    }
1193 	    if (ctxt->escape == xmlEscapeEntities)
1194 		ctxt->escape = NULL;
1195 	    if (ctxt->escapeAttr == xmlEscapeEntities)
1196 		ctxt->escapeAttr = NULL;
1197 	}
1198 
1199 
1200 	/*
1201 	 * Save the XML declaration
1202 	 */
1203 	if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1204 	    xmlOutputBufferWrite(buf, 14, "<?xml version=");
1205 	    if (cur->version != NULL)
1206 		xmlBufWriteQuotedString(buf->buffer, cur->version);
1207 	    else
1208 		xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1209 	    if (encoding != NULL) {
1210 		xmlOutputBufferWrite(buf, 10, " encoding=");
1211 		xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
1212 	    }
1213 	    switch (cur->standalone) {
1214 		case 0:
1215 		    xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1216 		    break;
1217 		case 1:
1218 		    xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1219 		    break;
1220 	    }
1221 	    xmlOutputBufferWrite(buf, 3, "?>\n");
1222 	}
1223 
1224 #ifdef LIBXML_HTML_ENABLED
1225         if (ctxt->options & XML_SAVE_XHTML)
1226             is_xhtml = 1;
1227 	if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1228 	    dtd = xmlGetIntSubset(cur);
1229 	    if (dtd != NULL) {
1230 		is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1231 		if (is_xhtml < 0) is_xhtml = 0;
1232 	    }
1233 	}
1234 #endif
1235 	if (cur->children != NULL) {
1236 	    xmlNodePtr child = cur->children;
1237 
1238 	    while (child != NULL) {
1239 		ctxt->level = 0;
1240 #ifdef LIBXML_HTML_ENABLED
1241 		if (is_xhtml)
1242 		    xhtmlNodeDumpOutput(ctxt, child);
1243 		else
1244 #endif
1245 		    xmlNodeDumpOutputInternal(ctxt, child);
1246                 if ((child->type != XML_XINCLUDE_START) &&
1247                     (child->type != XML_XINCLUDE_END))
1248                     xmlOutputBufferWrite(buf, 1, "\n");
1249 		child = child->next;
1250 	    }
1251 	}
1252     }
1253 
1254     /*
1255      * Restore the state of the saving context at the end of the document
1256      */
1257     if ((switched_encoding) && (oldctxtenc == NULL)) {
1258 	xmlSaveClearEncoding(ctxt);
1259 	ctxt->escape = oldescape;
1260 	ctxt->escapeAttr = oldescapeAttr;
1261     }
1262     cur->encoding = oldenc;
1263     return(0);
1264 }
1265 
1266 #ifdef LIBXML_HTML_ENABLED
1267 /************************************************************************
1268  *									*
1269  *		Functions specific to XHTML serialization		*
1270  *									*
1271  ************************************************************************/
1272 
1273 /**
1274  * xhtmlIsEmpty:
1275  * @node:  the node
1276  *
1277  * Check if a node is an empty xhtml node
1278  *
1279  * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1280  */
1281 static int
xhtmlIsEmpty(xmlNodePtr node)1282 xhtmlIsEmpty(xmlNodePtr node) {
1283     if (node == NULL)
1284 	return(-1);
1285     if (node->type != XML_ELEMENT_NODE)
1286 	return(0);
1287     if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1288 	return(0);
1289     if (node->children != NULL)
1290 	return(0);
1291     switch (node->name[0]) {
1292 	case 'a':
1293 	    if (xmlStrEqual(node->name, BAD_CAST "area"))
1294 		return(1);
1295 	    return(0);
1296 	case 'b':
1297 	    if (xmlStrEqual(node->name, BAD_CAST "br"))
1298 		return(1);
1299 	    if (xmlStrEqual(node->name, BAD_CAST "base"))
1300 		return(1);
1301 	    if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1302 		return(1);
1303 	    return(0);
1304 	case 'c':
1305 	    if (xmlStrEqual(node->name, BAD_CAST "col"))
1306 		return(1);
1307 	    return(0);
1308 	case 'f':
1309 	    if (xmlStrEqual(node->name, BAD_CAST "frame"))
1310 		return(1);
1311 	    return(0);
1312 	case 'h':
1313 	    if (xmlStrEqual(node->name, BAD_CAST "hr"))
1314 		return(1);
1315 	    return(0);
1316 	case 'i':
1317 	    if (xmlStrEqual(node->name, BAD_CAST "img"))
1318 		return(1);
1319 	    if (xmlStrEqual(node->name, BAD_CAST "input"))
1320 		return(1);
1321 	    if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1322 		return(1);
1323 	    return(0);
1324 	case 'l':
1325 	    if (xmlStrEqual(node->name, BAD_CAST "link"))
1326 		return(1);
1327 	    return(0);
1328 	case 'm':
1329 	    if (xmlStrEqual(node->name, BAD_CAST "meta"))
1330 		return(1);
1331 	    return(0);
1332 	case 'p':
1333 	    if (xmlStrEqual(node->name, BAD_CAST "param"))
1334 		return(1);
1335 	    return(0);
1336     }
1337     return(0);
1338 }
1339 
1340 /**
1341  * xhtmlAttrListDumpOutput:
1342  * @cur:  the first attribute pointer
1343  *
1344  * Dump a list of XML attributes
1345  */
1346 static void
xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt,xmlAttrPtr cur)1347 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1348     xmlAttrPtr xml_lang = NULL;
1349     xmlAttrPtr lang = NULL;
1350     xmlAttrPtr name = NULL;
1351     xmlAttrPtr id = NULL;
1352     xmlNodePtr parent;
1353     xmlOutputBufferPtr buf;
1354 
1355     if (cur == NULL) return;
1356     buf = ctxt->buf;
1357     parent = cur->parent;
1358     while (cur != NULL) {
1359 	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1360 	    id = cur;
1361 	else
1362 	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1363 	    name = cur;
1364 	else
1365 	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1366 	    lang = cur;
1367 	else
1368 	if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1369 	    (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1370 	    xml_lang = cur;
1371 	else if ((cur->ns == NULL) &&
1372 		 ((cur->children == NULL) ||
1373 		  (cur->children->content == NULL) ||
1374 		  (cur->children->content[0] == 0)) &&
1375 		 (htmlIsBooleanAttr(cur->name))) {
1376 	    if (cur->children != NULL)
1377 		xmlFreeNode(cur->children);
1378 	    cur->children = xmlNewText(cur->name);
1379 	    if (cur->children != NULL)
1380 		cur->children->parent = (xmlNodePtr) cur;
1381 	}
1382         xmlAttrDumpOutput(ctxt, cur);
1383 	cur = cur->next;
1384     }
1385     /*
1386      * C.8
1387      */
1388     if ((name != NULL) && (id == NULL)) {
1389 	if ((parent != NULL) && (parent->name != NULL) &&
1390 	    ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1391 	     (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1392 	     (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1393 	     (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1394 	     (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1395 	     (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1396 	     (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1397 	     (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1398 	     (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
1399 	    xmlOutputBufferWrite(buf, 5, " id=\"");
1400 	    xmlAttrSerializeContent(buf, name);
1401 	    xmlOutputBufferWrite(buf, 1, "\"");
1402 	}
1403     }
1404     /*
1405      * C.7.
1406      */
1407     if ((lang != NULL) && (xml_lang == NULL)) {
1408 	xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1409 	xmlAttrSerializeContent(buf, lang);
1410 	xmlOutputBufferWrite(buf, 1, "\"");
1411     } else
1412     if ((xml_lang != NULL) && (lang == NULL)) {
1413 	xmlOutputBufferWrite(buf, 7, " lang=\"");
1414 	xmlAttrSerializeContent(buf, xml_lang);
1415 	xmlOutputBufferWrite(buf, 1, "\"");
1416     }
1417 }
1418 
1419 /**
1420  * xhtmlNodeDumpOutput:
1421  * @buf:  the XML buffer output
1422  * @doc:  the XHTML document
1423  * @cur:  the current node
1424  * @level: the imbrication level for indenting
1425  * @format: is formatting allowed
1426  * @encoding:  an optional encoding string
1427  *
1428  * Dump an XHTML node, recursive behaviour, children are printed too.
1429  */
1430 static void
xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)1431 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1432     int format = ctxt->format, addmeta;
1433     xmlNodePtr tmp, root, unformattedNode = NULL;
1434     xmlChar *start, *end;
1435     xmlOutputBufferPtr buf = ctxt->buf;
1436 
1437     if (cur == NULL) return;
1438 
1439     root = cur;
1440     while (1) {
1441         switch (cur->type) {
1442         case XML_DOCUMENT_NODE:
1443         case XML_HTML_DOCUMENT_NODE:
1444             xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1445 	    break;
1446 
1447         case XML_NAMESPACE_DECL:
1448 	    xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1449 	    break;
1450 
1451         case XML_DTD_NODE:
1452             xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1453 	    break;
1454 
1455         case XML_DOCUMENT_FRAG_NODE:
1456             if (cur->children) {
1457                 cur = cur->children;
1458                 continue;
1459             }
1460             break;
1461 
1462         case XML_ELEMENT_DECL:
1463             xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1464 	    break;
1465 
1466         case XML_ATTRIBUTE_DECL:
1467             xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1468 	    break;
1469 
1470         case XML_ENTITY_DECL:
1471             xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1472 	    break;
1473 
1474         case XML_ELEMENT_NODE:
1475             addmeta = 0;
1476 
1477 	    if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
1478 		xmlOutputBufferWrite(buf, ctxt->indent_size *
1479 				     (ctxt->level > ctxt->indent_nr ?
1480 				      ctxt->indent_nr : ctxt->level),
1481 				     ctxt->indent);
1482 
1483             xmlOutputBufferWrite(buf, 1, "<");
1484             if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1485                 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1486                 xmlOutputBufferWrite(buf, 1, ":");
1487             }
1488 
1489             xmlOutputBufferWriteString(buf, (const char *)cur->name);
1490             if (cur->nsDef)
1491                 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1492             if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1493                 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1494                 /*
1495                  * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1496                  */
1497                 xmlOutputBufferWriteString(buf,
1498                         " xmlns=\"http://www.w3.org/1999/xhtml\"");
1499             }
1500             if (cur->properties != NULL)
1501                 xhtmlAttrListDumpOutput(ctxt, cur->properties);
1502 
1503             if ((cur->parent != NULL) &&
1504                 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1505                 xmlStrEqual(cur->name, BAD_CAST"head") &&
1506                 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1507 
1508                 tmp = cur->children;
1509                 while (tmp != NULL) {
1510                     if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1511                         xmlChar *httpequiv;
1512 
1513                         httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
1514                         if (httpequiv != NULL) {
1515                             if (xmlStrcasecmp(httpequiv,
1516                                         BAD_CAST"Content-Type") == 0) {
1517                                 xmlFree(httpequiv);
1518                                 break;
1519                             }
1520                             xmlFree(httpequiv);
1521                         }
1522                     }
1523                     tmp = tmp->next;
1524                 }
1525                 if (tmp == NULL)
1526                     addmeta = 1;
1527             }
1528 
1529             if (cur->children == NULL) {
1530                 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1531                     ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1532                     /*
1533                      * C.2. Empty Elements
1534                      */
1535                     xmlOutputBufferWrite(buf, 3, " />");
1536                 } else {
1537                     if (addmeta == 1) {
1538                         xmlOutputBufferWrite(buf, 1, ">");
1539                         if (ctxt->format == 1) {
1540                             xmlOutputBufferWrite(buf, 1, "\n");
1541                             if (xmlIndentTreeOutput)
1542                                 xmlOutputBufferWrite(buf, ctxt->indent_size *
1543                                     (ctxt->level + 1 > ctxt->indent_nr ?
1544                                     ctxt->indent_nr : ctxt->level + 1),
1545                                     ctxt->indent);
1546                         }
1547                         xmlOutputBufferWriteString(buf,
1548                                 "<meta http-equiv=\"Content-Type\" "
1549                                 "content=\"text/html; charset=");
1550                         if (ctxt->encoding) {
1551                             xmlOutputBufferWriteString(buf,
1552                                     (const char *)ctxt->encoding);
1553                         } else {
1554                             xmlOutputBufferWrite(buf, 5, "UTF-8");
1555                         }
1556                         xmlOutputBufferWrite(buf, 4, "\" />");
1557                         if (ctxt->format == 1)
1558                             xmlOutputBufferWrite(buf, 1, "\n");
1559                     } else {
1560                         xmlOutputBufferWrite(buf, 1, ">");
1561                     }
1562                     /*
1563                      * C.3. Element Minimization and Empty Element Content
1564                      */
1565                     xmlOutputBufferWrite(buf, 2, "</");
1566                     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1567                         xmlOutputBufferWriteString(buf,
1568                                 (const char *)cur->ns->prefix);
1569                         xmlOutputBufferWrite(buf, 1, ":");
1570                     }
1571                     xmlOutputBufferWriteString(buf, (const char *)cur->name);
1572                     xmlOutputBufferWrite(buf, 1, ">");
1573                 }
1574             } else {
1575                 xmlOutputBufferWrite(buf, 1, ">");
1576                 if (addmeta == 1) {
1577                     if (ctxt->format == 1) {
1578                         xmlOutputBufferWrite(buf, 1, "\n");
1579                         if (xmlIndentTreeOutput)
1580                             xmlOutputBufferWrite(buf, ctxt->indent_size *
1581                                 (ctxt->level + 1 > ctxt->indent_nr ?
1582                                 ctxt->indent_nr : ctxt->level + 1),
1583                                 ctxt->indent);
1584                     }
1585                     xmlOutputBufferWriteString(buf,
1586                             "<meta http-equiv=\"Content-Type\" "
1587                             "content=\"text/html; charset=");
1588                     if (ctxt->encoding) {
1589                         xmlOutputBufferWriteString(buf,
1590                                 (const char *)ctxt->encoding);
1591                     } else {
1592                         xmlOutputBufferWrite(buf, 5, "UTF-8");
1593                     }
1594                     xmlOutputBufferWrite(buf, 4, "\" />");
1595                 }
1596 
1597                 if (ctxt->format == 1) {
1598                     tmp = cur->children;
1599                     while (tmp != NULL) {
1600                         if ((tmp->type == XML_TEXT_NODE) ||
1601                             (tmp->type == XML_ENTITY_REF_NODE)) {
1602                             unformattedNode = cur;
1603                             ctxt->format = 0;
1604                             break;
1605                         }
1606                         tmp = tmp->next;
1607                     }
1608                 }
1609 
1610                 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1611                 if (ctxt->level >= 0) ctxt->level++;
1612                 cur = cur->children;
1613                 continue;
1614             }
1615 
1616             break;
1617 
1618         case XML_TEXT_NODE:
1619 	    if (cur->content == NULL)
1620                 break;
1621 	    if ((cur->name == xmlStringText) ||
1622 		(cur->name != xmlStringTextNoenc)) {
1623                 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1624 	    } else {
1625 		/*
1626 		 * Disable escaping, needed for XSLT
1627 		 */
1628 		xmlOutputBufferWriteString(buf, (const char *) cur->content);
1629 	    }
1630 	    break;
1631 
1632         case XML_PI_NODE:
1633             if (cur->content != NULL) {
1634                 xmlOutputBufferWrite(buf, 2, "<?");
1635                 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1636                 if (cur->content != NULL) {
1637                     xmlOutputBufferWrite(buf, 1, " ");
1638                     xmlOutputBufferWriteString(buf,
1639                             (const char *)cur->content);
1640                 }
1641                 xmlOutputBufferWrite(buf, 2, "?>");
1642             } else {
1643                 xmlOutputBufferWrite(buf, 2, "<?");
1644                 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1645                 xmlOutputBufferWrite(buf, 2, "?>");
1646             }
1647             break;
1648 
1649         case XML_COMMENT_NODE:
1650             if (cur->content != NULL) {
1651                 xmlOutputBufferWrite(buf, 4, "<!--");
1652                 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1653                 xmlOutputBufferWrite(buf, 3, "-->");
1654             }
1655             break;
1656 
1657         case XML_ENTITY_REF_NODE:
1658             xmlOutputBufferWrite(buf, 1, "&");
1659             xmlOutputBufferWriteString(buf, (const char *)cur->name);
1660             xmlOutputBufferWrite(buf, 1, ";");
1661             break;
1662 
1663         case XML_CDATA_SECTION_NODE:
1664             if (cur->content == NULL || *cur->content == '\0') {
1665                 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1666             } else {
1667                 start = end = cur->content;
1668                 while (*end != '\0') {
1669                     if (*end == ']' && *(end + 1) == ']' &&
1670                         *(end + 2) == '>') {
1671                         end = end + 2;
1672                         xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1673                         xmlOutputBufferWrite(buf, end - start,
1674                                 (const char *)start);
1675                         xmlOutputBufferWrite(buf, 3, "]]>");
1676                         start = end;
1677                     }
1678                     end++;
1679                 }
1680                 if (start != end) {
1681                     xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1682                     xmlOutputBufferWriteString(buf, (const char *)start);
1683                     xmlOutputBufferWrite(buf, 3, "]]>");
1684                 }
1685             }
1686             break;
1687 
1688         case XML_ATTRIBUTE_NODE:
1689             xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1690 	    break;
1691 
1692         default:
1693             break;
1694         }
1695 
1696         while (1) {
1697             if (cur == root)
1698                 return;
1699             if (ctxt->format == 1)
1700                 xmlOutputBufferWrite(buf, 1, "\n");
1701             if (cur->next != NULL) {
1702                 cur = cur->next;
1703                 break;
1704             }
1705 
1706             /*
1707              * The parent should never be NULL here but we want to handle
1708              * corrupted documents gracefully.
1709              */
1710             if (cur->parent == NULL)
1711                 return;
1712             cur = cur->parent;
1713 
1714             if (cur->type == XML_ELEMENT_NODE) {
1715                 if (ctxt->level > 0) ctxt->level--;
1716                 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1717                     xmlOutputBufferWrite(buf, ctxt->indent_size *
1718                                          (ctxt->level > ctxt->indent_nr ?
1719                                           ctxt->indent_nr : ctxt->level),
1720                                          ctxt->indent);
1721 
1722                 xmlOutputBufferWrite(buf, 2, "</");
1723                 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1724                     xmlOutputBufferWriteString(buf,
1725                             (const char *)cur->ns->prefix);
1726                     xmlOutputBufferWrite(buf, 1, ":");
1727                 }
1728 
1729                 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1730                 xmlOutputBufferWrite(buf, 1, ">");
1731 
1732                 if (cur == unformattedNode) {
1733                     ctxt->format = format;
1734                     unformattedNode = NULL;
1735                 }
1736             }
1737         }
1738     }
1739 }
1740 #endif
1741 
1742 /************************************************************************
1743  *									*
1744  *			Public entry points				*
1745  *									*
1746  ************************************************************************/
1747 
1748 /**
1749  * xmlSaveToFd:
1750  * @fd:  a file descriptor number
1751  * @encoding:  the encoding name to use or NULL
1752  * @options:  a set of xmlSaveOptions
1753  *
1754  * Create a document saving context serializing to a file descriptor
1755  * with the encoding and the options given.
1756  *
1757  * Returns a new serialization context or NULL in case of error.
1758  */
1759 xmlSaveCtxtPtr
xmlSaveToFd(int fd,const char * encoding,int options)1760 xmlSaveToFd(int fd, const char *encoding, int options)
1761 {
1762     xmlSaveCtxtPtr ret;
1763 
1764     ret = xmlNewSaveCtxt(encoding, options);
1765     if (ret == NULL) return(NULL);
1766     ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1767     if (ret->buf == NULL) {
1768         xmlCharEncCloseFunc(ret->handler);
1769 	xmlFreeSaveCtxt(ret);
1770 	return(NULL);
1771     }
1772     return(ret);
1773 }
1774 
1775 /**
1776  * xmlSaveToFilename:
1777  * @filename:  a file name or an URL
1778  * @encoding:  the encoding name to use or NULL
1779  * @options:  a set of xmlSaveOptions
1780  *
1781  * Create a document saving context serializing to a filename or possibly
1782  * to an URL (but this is less reliable) with the encoding and the options
1783  * given.
1784  *
1785  * Returns a new serialization context or NULL in case of error.
1786  */
1787 xmlSaveCtxtPtr
xmlSaveToFilename(const char * filename,const char * encoding,int options)1788 xmlSaveToFilename(const char *filename, const char *encoding, int options)
1789 {
1790     xmlSaveCtxtPtr ret;
1791     int compression = 0; /* TODO handle compression option */
1792 
1793     ret = xmlNewSaveCtxt(encoding, options);
1794     if (ret == NULL) return(NULL);
1795     ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
1796                                              compression);
1797     if (ret->buf == NULL) {
1798         xmlCharEncCloseFunc(ret->handler);
1799 	xmlFreeSaveCtxt(ret);
1800 	return(NULL);
1801     }
1802     return(ret);
1803 }
1804 
1805 /**
1806  * xmlSaveToBuffer:
1807  * @buffer:  a buffer
1808  * @encoding:  the encoding name to use or NULL
1809  * @options:  a set of xmlSaveOptions
1810  *
1811  * Create a document saving context serializing to a buffer
1812  * with the encoding and the options given
1813  *
1814  * Returns a new serialization context or NULL in case of error.
1815  */
1816 
1817 xmlSaveCtxtPtr
xmlSaveToBuffer(xmlBufferPtr buffer,const char * encoding,int options)1818 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1819 {
1820     xmlSaveCtxtPtr ret;
1821 
1822     ret = xmlNewSaveCtxt(encoding, options);
1823     if (ret == NULL) return(NULL);
1824     ret->buf = xmlOutputBufferCreateBuffer(buffer, ret->handler);
1825     if (ret->buf == NULL) {
1826         xmlCharEncCloseFunc(ret->handler);
1827 	xmlFreeSaveCtxt(ret);
1828 	return(NULL);
1829     }
1830     return(ret);
1831 }
1832 
1833 /**
1834  * xmlSaveToIO:
1835  * @iowrite:  an I/O write function
1836  * @ioclose:  an I/O close function
1837  * @ioctx:  an I/O handler
1838  * @encoding:  the encoding name to use or NULL
1839  * @options:  a set of xmlSaveOptions
1840  *
1841  * Create a document saving context serializing to a file descriptor
1842  * with the encoding and the options given
1843  *
1844  * Returns a new serialization context or NULL in case of error.
1845  */
1846 xmlSaveCtxtPtr
xmlSaveToIO(xmlOutputWriteCallback iowrite,xmlOutputCloseCallback ioclose,void * ioctx,const char * encoding,int options)1847 xmlSaveToIO(xmlOutputWriteCallback iowrite,
1848             xmlOutputCloseCallback ioclose,
1849             void *ioctx, const char *encoding, int options)
1850 {
1851     xmlSaveCtxtPtr ret;
1852 
1853     ret = xmlNewSaveCtxt(encoding, options);
1854     if (ret == NULL) return(NULL);
1855     ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1856     if (ret->buf == NULL) {
1857         xmlCharEncCloseFunc(ret->handler);
1858 	xmlFreeSaveCtxt(ret);
1859 	return(NULL);
1860     }
1861     return(ret);
1862 }
1863 
1864 /**
1865  * xmlSaveDoc:
1866  * @ctxt:  a document saving context
1867  * @doc:  a document
1868  *
1869  * Save a full document to a saving context
1870  * TODO: The function is not fully implemented yet as it does not return the
1871  * byte count but 0 instead
1872  *
1873  * Returns the number of byte written or -1 in case of error
1874  */
1875 long
xmlSaveDoc(xmlSaveCtxtPtr ctxt,xmlDocPtr doc)1876 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1877 {
1878     long ret = 0;
1879 
1880     if ((ctxt == NULL) || (doc == NULL)) return(-1);
1881     if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1882         return(-1);
1883     return(ret);
1884 }
1885 
1886 /**
1887  * xmlSaveTree:
1888  * @ctxt:  a document saving context
1889  * @node:  the top node of the subtree to save
1890  *
1891  * Save a subtree starting at the node parameter to a saving context
1892  * TODO: The function is not fully implemented yet as it does not return the
1893  * byte count but 0 instead
1894  *
1895  * Returns the number of byte written or -1 in case of error
1896  */
1897 long
xmlSaveTree(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)1898 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr cur)
1899 {
1900     long ret = 0;
1901 
1902     if ((ctxt == NULL) || (cur == NULL)) return(-1);
1903 #ifdef LIBXML_HTML_ENABLED
1904     if (ctxt->options & XML_SAVE_XHTML) {
1905         xhtmlNodeDumpOutput(ctxt, cur);
1906         return(ret);
1907     }
1908     if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
1909          (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
1910          ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
1911         (ctxt->options & XML_SAVE_AS_HTML)) {
1912 	htmlNodeDumpOutputInternal(ctxt, cur);
1913 	return(ret);
1914     }
1915 #endif
1916     xmlNodeDumpOutputInternal(ctxt, cur);
1917     return(ret);
1918 }
1919 
1920 /**
1921  * xmlSaveFlush:
1922  * @ctxt:  a document saving context
1923  *
1924  * Flush a document saving context, i.e. make sure that all bytes have
1925  * been output.
1926  *
1927  * Returns the number of byte written or -1 in case of error.
1928  */
1929 int
xmlSaveFlush(xmlSaveCtxtPtr ctxt)1930 xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1931 {
1932     if (ctxt == NULL) return(-1);
1933     if (ctxt->buf == NULL) return(-1);
1934     return(xmlOutputBufferFlush(ctxt->buf));
1935 }
1936 
1937 /**
1938  * xmlSaveClose:
1939  * @ctxt:  a document saving context
1940  *
1941  * Close a document saving context, i.e. make sure that all bytes have
1942  * been output and free the associated data.
1943  *
1944  * Returns the number of byte written or -1 in case of error.
1945  */
1946 int
xmlSaveClose(xmlSaveCtxtPtr ctxt)1947 xmlSaveClose(xmlSaveCtxtPtr ctxt)
1948 {
1949     int ret;
1950 
1951     if (ctxt == NULL) return(-1);
1952     ret = xmlSaveFlush(ctxt);
1953     xmlFreeSaveCtxt(ctxt);
1954     return(ret);
1955 }
1956 
1957 /**
1958  * xmlSaveSetEscape:
1959  * @ctxt:  a document saving context
1960  * @escape:  the escaping function
1961  *
1962  * Set a custom escaping function to be used for text in element content
1963  *
1964  * Returns 0 if successful or -1 in case of error.
1965  */
1966 int
xmlSaveSetEscape(xmlSaveCtxtPtr ctxt,xmlCharEncodingOutputFunc escape)1967 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1968 {
1969     if (ctxt == NULL) return(-1);
1970     ctxt->escape = escape;
1971     return(0);
1972 }
1973 
1974 /**
1975  * xmlSaveSetAttrEscape:
1976  * @ctxt:  a document saving context
1977  * @escape:  the escaping function
1978  *
1979  * Set a custom escaping function to be used for text in attribute content
1980  *
1981  * Returns 0 if successful or -1 in case of error.
1982  */
1983 int
xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt,xmlCharEncodingOutputFunc escape)1984 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1985 {
1986     if (ctxt == NULL) return(-1);
1987     ctxt->escapeAttr = escape;
1988     return(0);
1989 }
1990 
1991 /************************************************************************
1992  *									*
1993  *		Public entry points based on buffers			*
1994  *									*
1995  ************************************************************************/
1996 
1997 /**
1998  * xmlBufAttrSerializeTxtContent:
1999  * @buf:  and xmlBufPtr output
2000  * @doc:  the document
2001  * @attr: the attribute node
2002  * @string: the text content
2003  *
2004  * Serialize text attribute values to an xmlBufPtr
2005  */
2006 void
xmlBufAttrSerializeTxtContent(xmlBufPtr buf,xmlDocPtr doc,xmlAttrPtr attr,const xmlChar * string)2007 xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
2008                               xmlAttrPtr attr, const xmlChar * string)
2009 {
2010     xmlChar *base, *cur;
2011 
2012     if (string == NULL)
2013         return;
2014     base = cur = (xmlChar *) string;
2015     while (*cur != 0) {
2016         if (*cur == '\n') {
2017             if (base != cur)
2018                 xmlBufAdd(buf, base, cur - base);
2019             xmlBufAdd(buf, BAD_CAST "&#10;", 5);
2020             cur++;
2021             base = cur;
2022         } else if (*cur == '\r') {
2023             if (base != cur)
2024                 xmlBufAdd(buf, base, cur - base);
2025             xmlBufAdd(buf, BAD_CAST "&#13;", 5);
2026             cur++;
2027             base = cur;
2028         } else if (*cur == '\t') {
2029             if (base != cur)
2030                 xmlBufAdd(buf, base, cur - base);
2031             xmlBufAdd(buf, BAD_CAST "&#9;", 4);
2032             cur++;
2033             base = cur;
2034         } else if (*cur == '"') {
2035             if (base != cur)
2036                 xmlBufAdd(buf, base, cur - base);
2037             xmlBufAdd(buf, BAD_CAST "&quot;", 6);
2038             cur++;
2039             base = cur;
2040         } else if (*cur == '<') {
2041             if (base != cur)
2042                 xmlBufAdd(buf, base, cur - base);
2043             xmlBufAdd(buf, BAD_CAST "&lt;", 4);
2044             cur++;
2045             base = cur;
2046         } else if (*cur == '>') {
2047             if (base != cur)
2048                 xmlBufAdd(buf, base, cur - base);
2049             xmlBufAdd(buf, BAD_CAST "&gt;", 4);
2050             cur++;
2051             base = cur;
2052         } else if (*cur == '&') {
2053             if (base != cur)
2054                 xmlBufAdd(buf, base, cur - base);
2055             xmlBufAdd(buf, BAD_CAST "&amp;", 5);
2056             cur++;
2057             base = cur;
2058         } else if ((*cur >= 0x80) && (cur[1] != 0) &&
2059 	           ((doc == NULL) || (doc->encoding == NULL))) {
2060             /*
2061              * We assume we have UTF-8 content.
2062              */
2063             unsigned char tmp[12];
2064             int val = 0, l = 1;
2065 
2066             if (base != cur)
2067                 xmlBufAdd(buf, base, cur - base);
2068             if (*cur < 0xC0) {
2069                 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
2070 		xmlSerializeHexCharRef(tmp, *cur);
2071                 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2072                 cur++;
2073                 base = cur;
2074                 continue;
2075             } else if (*cur < 0xE0) {
2076                 val = (cur[0]) & 0x1F;
2077                 val <<= 6;
2078                 val |= (cur[1]) & 0x3F;
2079                 l = 2;
2080             } else if ((*cur < 0xF0) && (cur [2] != 0)) {
2081                 val = (cur[0]) & 0x0F;
2082                 val <<= 6;
2083                 val |= (cur[1]) & 0x3F;
2084                 val <<= 6;
2085                 val |= (cur[2]) & 0x3F;
2086                 l = 3;
2087             } else if ((*cur < 0xF8) && (cur [2] != 0) && (cur[3] != 0)) {
2088                 val = (cur[0]) & 0x07;
2089                 val <<= 6;
2090                 val |= (cur[1]) & 0x3F;
2091                 val <<= 6;
2092                 val |= (cur[2]) & 0x3F;
2093                 val <<= 6;
2094                 val |= (cur[3]) & 0x3F;
2095                 l = 4;
2096             }
2097             if ((l == 1) || (!IS_CHAR(val))) {
2098                 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
2099 		xmlSerializeHexCharRef(tmp, *cur);
2100                 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2101                 cur++;
2102                 base = cur;
2103                 continue;
2104             }
2105             /*
2106              * We could do multiple things here. Just save
2107              * as a char ref
2108              */
2109 	    xmlSerializeHexCharRef(tmp, val);
2110             xmlBufAdd(buf, (xmlChar *) tmp, -1);
2111             cur += l;
2112             base = cur;
2113         } else {
2114             cur++;
2115         }
2116     }
2117     if (base != cur)
2118         xmlBufAdd(buf, base, cur - base);
2119 }
2120 
2121 /**
2122  * xmlAttrSerializeTxtContent:
2123  * @buf:  the XML buffer output
2124  * @doc:  the document
2125  * @attr: the attribute node
2126  * @string: the text content
2127  *
2128  * Serialize text attribute values to an xml simple buffer
2129  */
2130 void
xmlAttrSerializeTxtContent(xmlBufferPtr buf,xmlDocPtr doc,xmlAttrPtr attr,const xmlChar * string)2131 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
2132                            xmlAttrPtr attr, const xmlChar * string)
2133 {
2134     xmlBufPtr buffer;
2135 
2136     if ((buf == NULL) || (string == NULL))
2137         return;
2138     buffer = xmlBufFromBuffer(buf);
2139     if (buffer == NULL)
2140         return;
2141     xmlBufAttrSerializeTxtContent(buffer, doc, attr, string);
2142     xmlBufBackToBuffer(buffer);
2143 }
2144 
2145 /**
2146  * xmlNodeDump:
2147  * @buf:  the XML buffer output
2148  * @doc:  the document
2149  * @cur:  the current node
2150  * @level: the imbrication level for indenting
2151  * @format: is formatting allowed
2152  *
2153  * Dump an XML node, recursive behaviour,children are printed too.
2154  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2155  * or xmlKeepBlanksDefault(0) was called.
2156  * Since this is using xmlBuffer structures it is limited to 2GB and somehow
2157  * deprecated, use xmlNodeDumpOutput() instead.
2158  *
2159  * Returns the number of bytes written to the buffer or -1 in case of error
2160  */
2161 int
xmlNodeDump(xmlBufferPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format)2162 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2163             int format)
2164 {
2165     xmlBufPtr buffer;
2166     size_t ret;
2167 
2168     if ((buf == NULL) || (cur == NULL))
2169         return(-1);
2170     buffer = xmlBufFromBuffer(buf);
2171     if (buffer == NULL)
2172         return(-1);
2173     ret = xmlBufNodeDump(buffer, doc, cur, level, format);
2174     xmlBufBackToBuffer(buffer);
2175     if (ret > INT_MAX)
2176         return(-1);
2177     return((int) ret);
2178 }
2179 
2180 /**
2181  * xmlBufNodeDump:
2182  * @buf:  the XML buffer output
2183  * @doc:  the document
2184  * @cur:  the current node
2185  * @level: the imbrication level for indenting
2186  * @format: is formatting allowed
2187  *
2188  * Dump an XML node, recursive behaviour,children are printed too.
2189  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2190  * or xmlKeepBlanksDefault(0) was called
2191  *
2192  * Returns the number of bytes written to the buffer, in case of error 0
2193  *     is returned or @buf stores the error
2194  */
2195 
2196 size_t
xmlBufNodeDump(xmlBufPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format)2197 xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2198             int format)
2199 {
2200     size_t use;
2201     int ret;
2202     xmlOutputBufferPtr outbuf;
2203     int oldalloc;
2204 
2205     xmlInitParser();
2206 
2207     if (cur == NULL) {
2208 #ifdef DEBUG_TREE
2209         xmlGenericError(xmlGenericErrorContext,
2210                         "xmlNodeDump : node == NULL\n");
2211 #endif
2212         return (-1);
2213     }
2214     if (buf == NULL) {
2215 #ifdef DEBUG_TREE
2216         xmlGenericError(xmlGenericErrorContext,
2217                         "xmlNodeDump : buf == NULL\n");
2218 #endif
2219         return (-1);
2220     }
2221     outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2222     if (outbuf == NULL) {
2223         xmlSaveErrMemory("creating buffer");
2224         return (-1);
2225     }
2226     memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2227     outbuf->buffer = buf;
2228     outbuf->encoder = NULL;
2229     outbuf->writecallback = NULL;
2230     outbuf->closecallback = NULL;
2231     outbuf->context = NULL;
2232     outbuf->written = 0;
2233 
2234     use = xmlBufUse(buf);
2235     oldalloc = xmlBufGetAllocationScheme(buf);
2236     xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
2237     xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
2238     xmlBufSetAllocationScheme(buf, oldalloc);
2239     xmlFree(outbuf);
2240     ret = xmlBufUse(buf) - use;
2241     return (ret);
2242 }
2243 
2244 /**
2245  * xmlElemDump:
2246  * @f:  the FILE * for the output
2247  * @doc:  the document
2248  * @cur:  the current node
2249  *
2250  * Dump an XML/HTML node, recursive behaviour, children are printed too.
2251  */
2252 void
xmlElemDump(FILE * f,xmlDocPtr doc,xmlNodePtr cur)2253 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2254 {
2255     xmlOutputBufferPtr outbuf;
2256 
2257     xmlInitParser();
2258 
2259     if (cur == NULL) {
2260 #ifdef DEBUG_TREE
2261         xmlGenericError(xmlGenericErrorContext,
2262                         "xmlElemDump : cur == NULL\n");
2263 #endif
2264         return;
2265     }
2266 #ifdef DEBUG_TREE
2267     if (doc == NULL) {
2268         xmlGenericError(xmlGenericErrorContext,
2269                         "xmlElemDump : doc == NULL\n");
2270     }
2271 #endif
2272 
2273     outbuf = xmlOutputBufferCreateFile(f, NULL);
2274     if (outbuf == NULL)
2275         return;
2276     if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
2277 #ifdef LIBXML_HTML_ENABLED
2278         htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2279 #else
2280 	xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
2281 #endif /* LIBXML_HTML_ENABLED */
2282     } else
2283         xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2284     xmlOutputBufferClose(outbuf);
2285 }
2286 
2287 /************************************************************************
2288  *									*
2289  *		Saving functions front-ends				*
2290  *									*
2291  ************************************************************************/
2292 
2293 /**
2294  * xmlNodeDumpOutput:
2295  * @buf:  the XML buffer output
2296  * @doc:  the document
2297  * @cur:  the current node
2298  * @level: the imbrication level for indenting
2299  * @format: is formatting allowed
2300  * @encoding:  an optional encoding string
2301  *
2302  * Dump an XML node, recursive behaviour, children are printed too.
2303  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2304  * or xmlKeepBlanksDefault(0) was called
2305  */
2306 void
xmlNodeDumpOutput(xmlOutputBufferPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format,const char * encoding)2307 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2308                   int level, int format, const char *encoding)
2309 {
2310     xmlSaveCtxt ctxt;
2311 #ifdef LIBXML_HTML_ENABLED
2312     xmlDtdPtr dtd;
2313     int is_xhtml = 0;
2314 #endif
2315 
2316     xmlInitParser();
2317 
2318     if ((buf == NULL) || (cur == NULL)) return;
2319 
2320     if (encoding == NULL)
2321         encoding = "UTF-8";
2322 
2323     memset(&ctxt, 0, sizeof(ctxt));
2324     ctxt.buf = buf;
2325     ctxt.level = level;
2326     ctxt.format = format ? 1 : 0;
2327     ctxt.encoding = (const xmlChar *) encoding;
2328     xmlSaveCtxtInit(&ctxt);
2329     ctxt.options |= XML_SAVE_AS_XML;
2330 
2331 #ifdef LIBXML_HTML_ENABLED
2332     dtd = xmlGetIntSubset(doc);
2333     if (dtd != NULL) {
2334 	is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2335 	if (is_xhtml < 0)
2336 	    is_xhtml = 0;
2337     }
2338 
2339     if (is_xhtml)
2340         xhtmlNodeDumpOutput(&ctxt, cur);
2341     else
2342 #endif
2343         xmlNodeDumpOutputInternal(&ctxt, cur);
2344 }
2345 
2346 /**
2347  * xmlDocDumpFormatMemoryEnc:
2348  * @out_doc:  Document to generate XML text from
2349  * @doc_txt_ptr:  Memory pointer for allocated XML text
2350  * @doc_txt_len:  Length of the generated XML text
2351  * @txt_encoding:  Character encoding to use when generating XML text
2352  * @format:  should formatting spaces been added
2353  *
2354  * Dump the current DOM tree into memory using the character encoding specified
2355  * by the caller.  Note it is up to the caller of this function to free the
2356  * allocated memory with xmlFree().
2357  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2358  * or xmlKeepBlanksDefault(0) was called
2359  */
2360 
2361 void
xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc,xmlChar ** doc_txt_ptr,int * doc_txt_len,const char * txt_encoding,int format)2362 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2363 		int * doc_txt_len, const char * txt_encoding,
2364 		int format) {
2365     xmlSaveCtxt ctxt;
2366     int                         dummy = 0;
2367     xmlOutputBufferPtr          out_buff = NULL;
2368     xmlCharEncodingHandlerPtr   conv_hdlr = NULL;
2369 
2370     if (doc_txt_len == NULL) {
2371         doc_txt_len = &dummy;   /*  Continue, caller just won't get length */
2372     }
2373 
2374     if (doc_txt_ptr == NULL) {
2375         *doc_txt_len = 0;
2376         return;
2377     }
2378 
2379     *doc_txt_ptr = NULL;
2380     *doc_txt_len = 0;
2381 
2382     if (out_doc == NULL) {
2383         /*  No document, no output  */
2384         return;
2385     }
2386 
2387     /*
2388      *  Validate the encoding value, if provided.
2389      *  This logic is copied from xmlSaveFileEnc.
2390      */
2391 
2392     if (txt_encoding == NULL)
2393 	txt_encoding = (const char *) out_doc->encoding;
2394     if (txt_encoding != NULL) {
2395 	conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2396 	if ( conv_hdlr == NULL ) {
2397 	    xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2398 		       txt_encoding);
2399 	    return;
2400 	}
2401     }
2402 
2403     if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2404         xmlSaveErrMemory("creating buffer");
2405         return;
2406     }
2407 
2408     memset(&ctxt, 0, sizeof(ctxt));
2409     ctxt.buf = out_buff;
2410     ctxt.level = 0;
2411     ctxt.format = format ? 1 : 0;
2412     ctxt.encoding = (const xmlChar *) txt_encoding;
2413     xmlSaveCtxtInit(&ctxt);
2414     ctxt.options |= XML_SAVE_AS_XML;
2415     xmlDocContentDumpOutput(&ctxt, out_doc);
2416     xmlOutputBufferFlush(out_buff);
2417     if (out_buff->conv != NULL) {
2418 	*doc_txt_len = xmlBufUse(out_buff->conv);
2419 	*doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len);
2420     } else {
2421 	*doc_txt_len = xmlBufUse(out_buff->buffer);
2422 	*doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len);
2423     }
2424     (void)xmlOutputBufferClose(out_buff);
2425 
2426     if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2427         *doc_txt_len = 0;
2428         xmlSaveErrMemory("creating output");
2429     }
2430 
2431     return;
2432 }
2433 
2434 /**
2435  * xmlDocDumpMemory:
2436  * @cur:  the document
2437  * @mem:  OUT: the memory pointer
2438  * @size:  OUT: the memory length
2439  *
2440  * Dump an XML document in memory and return the #xmlChar * and it's size
2441  * in bytes. It's up to the caller to free the memory with xmlFree().
2442  * The resulting byte array is zero terminated, though the last 0 is not
2443  * included in the returned size.
2444  */
2445 void
xmlDocDumpMemory(xmlDocPtr cur,xmlChar ** mem,int * size)2446 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2447     xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2448 }
2449 
2450 /**
2451  * xmlDocDumpFormatMemory:
2452  * @cur:  the document
2453  * @mem:  OUT: the memory pointer
2454  * @size:  OUT: the memory length
2455  * @format:  should formatting spaces been added
2456  *
2457  *
2458  * Dump an XML document in memory and return the #xmlChar * and it's size.
2459  * It's up to the caller to free the memory with xmlFree().
2460  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2461  * or xmlKeepBlanksDefault(0) was called
2462  */
2463 void
xmlDocDumpFormatMemory(xmlDocPtr cur,xmlChar ** mem,int * size,int format)2464 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2465     xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2466 }
2467 
2468 /**
2469  * xmlDocDumpMemoryEnc:
2470  * @out_doc:  Document to generate XML text from
2471  * @doc_txt_ptr:  Memory pointer for allocated XML text
2472  * @doc_txt_len:  Length of the generated XML text
2473  * @txt_encoding:  Character encoding to use when generating XML text
2474  *
2475  * Dump the current DOM tree into memory using the character encoding specified
2476  * by the caller.  Note it is up to the caller of this function to free the
2477  * allocated memory with xmlFree().
2478  */
2479 
2480 void
xmlDocDumpMemoryEnc(xmlDocPtr out_doc,xmlChar ** doc_txt_ptr,int * doc_txt_len,const char * txt_encoding)2481 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2482 	            int * doc_txt_len, const char * txt_encoding) {
2483     xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2484 	                      txt_encoding, 0);
2485 }
2486 
2487 /**
2488  * xmlDocFormatDump:
2489  * @f:  the FILE*
2490  * @cur:  the document
2491  * @format: should formatting spaces been added
2492  *
2493  * Dump an XML document to an open FILE.
2494  *
2495  * returns: the number of bytes written or -1 in case of failure.
2496  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2497  * or xmlKeepBlanksDefault(0) was called
2498  */
2499 int
xmlDocFormatDump(FILE * f,xmlDocPtr cur,int format)2500 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2501     xmlSaveCtxt ctxt;
2502     xmlOutputBufferPtr buf;
2503     const char * encoding;
2504     xmlCharEncodingHandlerPtr handler = NULL;
2505     int ret;
2506 
2507     if (cur == NULL) {
2508 #ifdef DEBUG_TREE
2509         xmlGenericError(xmlGenericErrorContext,
2510 		"xmlDocDump : document == NULL\n");
2511 #endif
2512 	return(-1);
2513     }
2514     encoding = (const char *) cur->encoding;
2515 
2516     if (encoding != NULL) {
2517 	handler = xmlFindCharEncodingHandler(encoding);
2518 	if (handler == NULL) {
2519 	    xmlFree((char *) cur->encoding);
2520 	    cur->encoding = NULL;
2521 	    encoding = NULL;
2522 	}
2523     }
2524     buf = xmlOutputBufferCreateFile(f, handler);
2525     if (buf == NULL) return(-1);
2526     memset(&ctxt, 0, sizeof(ctxt));
2527     ctxt.buf = buf;
2528     ctxt.level = 0;
2529     ctxt.format = format ? 1 : 0;
2530     ctxt.encoding = (const xmlChar *) encoding;
2531     xmlSaveCtxtInit(&ctxt);
2532     ctxt.options |= XML_SAVE_AS_XML;
2533     xmlDocContentDumpOutput(&ctxt, cur);
2534 
2535     ret = xmlOutputBufferClose(buf);
2536     return(ret);
2537 }
2538 
2539 /**
2540  * xmlDocDump:
2541  * @f:  the FILE*
2542  * @cur:  the document
2543  *
2544  * Dump an XML document to an open FILE.
2545  *
2546  * returns: the number of bytes written or -1 in case of failure.
2547  */
2548 int
xmlDocDump(FILE * f,xmlDocPtr cur)2549 xmlDocDump(FILE *f, xmlDocPtr cur) {
2550     return(xmlDocFormatDump (f, cur, 0));
2551 }
2552 
2553 /**
2554  * xmlSaveFileTo:
2555  * @buf:  an output I/O buffer
2556  * @cur:  the document
2557  * @encoding:  the encoding if any assuming the I/O layer handles the transcoding
2558  *
2559  * Dump an XML document to an I/O buffer.
2560  * Warning ! This call xmlOutputBufferClose() on buf which is not available
2561  * after this call.
2562  *
2563  * returns: the number of bytes written or -1 in case of failure.
2564  */
2565 int
xmlSaveFileTo(xmlOutputBufferPtr buf,xmlDocPtr cur,const char * encoding)2566 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2567     xmlSaveCtxt ctxt;
2568     int ret;
2569 
2570     if (buf == NULL) return(-1);
2571     if (cur == NULL) {
2572         xmlOutputBufferClose(buf);
2573 	return(-1);
2574     }
2575     memset(&ctxt, 0, sizeof(ctxt));
2576     ctxt.buf = buf;
2577     ctxt.level = 0;
2578     ctxt.format = 0;
2579     ctxt.encoding = (const xmlChar *) encoding;
2580     xmlSaveCtxtInit(&ctxt);
2581     ctxt.options |= XML_SAVE_AS_XML;
2582     xmlDocContentDumpOutput(&ctxt, cur);
2583     ret = xmlOutputBufferClose(buf);
2584     return(ret);
2585 }
2586 
2587 /**
2588  * xmlSaveFormatFileTo:
2589  * @buf:  an output I/O buffer
2590  * @cur:  the document
2591  * @encoding:  the encoding if any assuming the I/O layer handles the transcoding
2592  * @format: should formatting spaces been added
2593  *
2594  * Dump an XML document to an I/O buffer.
2595  * Warning ! This call xmlOutputBufferClose() on buf which is not available
2596  * after this call.
2597  *
2598  * returns: the number of bytes written or -1 in case of failure.
2599  */
2600 int
xmlSaveFormatFileTo(xmlOutputBufferPtr buf,xmlDocPtr cur,const char * encoding,int format)2601 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2602                     const char *encoding, int format)
2603 {
2604     xmlSaveCtxt ctxt;
2605     int ret;
2606 
2607     if (buf == NULL) return(-1);
2608     if ((cur == NULL) ||
2609         ((cur->type != XML_DOCUMENT_NODE) &&
2610 	 (cur->type != XML_HTML_DOCUMENT_NODE))) {
2611         xmlOutputBufferClose(buf);
2612 	return(-1);
2613     }
2614     memset(&ctxt, 0, sizeof(ctxt));
2615     ctxt.buf = buf;
2616     ctxt.level = 0;
2617     ctxt.format = format ? 1 : 0;
2618     ctxt.encoding = (const xmlChar *) encoding;
2619     xmlSaveCtxtInit(&ctxt);
2620     ctxt.options |= XML_SAVE_AS_XML;
2621     xmlDocContentDumpOutput(&ctxt, cur);
2622     ret = xmlOutputBufferClose(buf);
2623     return (ret);
2624 }
2625 
2626 /**
2627  * xmlSaveFormatFileEnc:
2628  * @filename:  the filename or URL to output
2629  * @cur:  the document being saved
2630  * @encoding:  the name of the encoding to use or NULL.
2631  * @format:  should formatting spaces be added.
2632  *
2633  * Dump an XML document to a file or an URL.
2634  *
2635  * Returns the number of bytes written or -1 in case of error.
2636  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2637  * or xmlKeepBlanksDefault(0) was called
2638  */
2639 int
xmlSaveFormatFileEnc(const char * filename,xmlDocPtr cur,const char * encoding,int format)2640 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2641 			const char * encoding, int format ) {
2642     xmlSaveCtxt ctxt;
2643     xmlOutputBufferPtr buf;
2644     xmlCharEncodingHandlerPtr handler = NULL;
2645     int ret;
2646 
2647     if (cur == NULL)
2648 	return(-1);
2649 
2650     if (encoding == NULL)
2651 	encoding = (const char *) cur->encoding;
2652 
2653     if (encoding != NULL) {
2654 
2655 	    handler = xmlFindCharEncodingHandler(encoding);
2656 	    if (handler == NULL)
2657 		return(-1);
2658     }
2659 
2660 #ifdef LIBXML_ZLIB_ENABLED
2661     if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2662 #endif
2663     /*
2664      * save the content to a temp buffer.
2665      */
2666     buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2667     if (buf == NULL) return(-1);
2668     memset(&ctxt, 0, sizeof(ctxt));
2669     ctxt.buf = buf;
2670     ctxt.level = 0;
2671     ctxt.format = format ? 1 : 0;
2672     ctxt.encoding = (const xmlChar *) encoding;
2673     xmlSaveCtxtInit(&ctxt);
2674     ctxt.options |= XML_SAVE_AS_XML;
2675 
2676     xmlDocContentDumpOutput(&ctxt, cur);
2677 
2678     ret = xmlOutputBufferClose(buf);
2679     return(ret);
2680 }
2681 
2682 
2683 /**
2684  * xmlSaveFileEnc:
2685  * @filename:  the filename (or URL)
2686  * @cur:  the document
2687  * @encoding:  the name of an encoding (or NULL)
2688  *
2689  * Dump an XML document, converting it to the given encoding
2690  *
2691  * returns: the number of bytes written or -1 in case of failure.
2692  */
2693 int
xmlSaveFileEnc(const char * filename,xmlDocPtr cur,const char * encoding)2694 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2695     return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2696 }
2697 
2698 /**
2699  * xmlSaveFormatFile:
2700  * @filename:  the filename (or URL)
2701  * @cur:  the document
2702  * @format:  should formatting spaces been added
2703  *
2704  * Dump an XML document to a file. Will use compression if
2705  * compiled in and enabled. If @filename is "-" the stdout file is
2706  * used. If @format is set then the document will be indented on output.
2707  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2708  * or xmlKeepBlanksDefault(0) was called
2709  *
2710  * returns: the number of bytes written or -1 in case of failure.
2711  */
2712 int
xmlSaveFormatFile(const char * filename,xmlDocPtr cur,int format)2713 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2714     return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2715 }
2716 
2717 /**
2718  * xmlSaveFile:
2719  * @filename:  the filename (or URL)
2720  * @cur:  the document
2721  *
2722  * Dump an XML document to a file. Will use compression if
2723  * compiled in and enabled. If @filename is "-" the stdout file is
2724  * used.
2725  * returns: the number of bytes written or -1 in case of failure.
2726  */
2727 int
xmlSaveFile(const char * filename,xmlDocPtr cur)2728 xmlSaveFile(const char *filename, xmlDocPtr cur) {
2729     return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2730 }
2731 
2732 #endif /* LIBXML_OUTPUT_ENABLED */
2733 
2734 #define bottom_xmlsave
2735 #include "elfgcchack.h"
2736