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;
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     while (1) {
860         switch (cur->type) {
861         case XML_DOCUMENT_NODE:
862         case XML_HTML_DOCUMENT_NODE:
863 	    xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
864 	    break;
865 
866         case XML_DTD_NODE:
867             xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
868             break;
869 
870         case XML_DOCUMENT_FRAG_NODE:
871             if (cur->children != NULL) {
872                 cur = cur->children;
873                 continue;
874             }
875 	    break;
876 
877         case XML_ELEMENT_DECL:
878             xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
879             break;
880 
881         case XML_ATTRIBUTE_DECL:
882             xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
883             break;
884 
885         case XML_ENTITY_DECL:
886             xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
887             break;
888 
889         case XML_ELEMENT_NODE:
890 	    if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
891 		xmlOutputBufferWrite(buf, ctxt->indent_size *
892 				     (ctxt->level > ctxt->indent_nr ?
893 				      ctxt->indent_nr : ctxt->level),
894 				     ctxt->indent);
895 
896             xmlOutputBufferWrite(buf, 1, "<");
897             if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
898                 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
899                 xmlOutputBufferWrite(buf, 1, ":");
900             }
901             xmlOutputBufferWriteString(buf, (const char *)cur->name);
902             if (cur->nsDef)
903                 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
904             for (attr = cur->properties; attr != NULL; attr = attr->next)
905                 xmlAttrDumpOutput(ctxt, attr);
906 
907             if (cur->children == NULL) {
908                 if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
909                     if (ctxt->format == 2)
910                         xmlOutputBufferWriteWSNonSig(ctxt, 0);
911                     xmlOutputBufferWrite(buf, 2, "/>");
912                 } else {
913                     if (ctxt->format == 2)
914                         xmlOutputBufferWriteWSNonSig(ctxt, 1);
915                     xmlOutputBufferWrite(buf, 3, "></");
916                     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
917                         xmlOutputBufferWriteString(buf,
918                                 (const char *)cur->ns->prefix);
919                         xmlOutputBufferWrite(buf, 1, ":");
920                     }
921                     xmlOutputBufferWriteString(buf, (const char *)cur->name);
922                     if (ctxt->format == 2)
923                         xmlOutputBufferWriteWSNonSig(ctxt, 0);
924                     xmlOutputBufferWrite(buf, 1, ">");
925                 }
926             } else {
927                 if (ctxt->format == 1) {
928                     tmp = cur->children;
929                     while (tmp != NULL) {
930                         if ((tmp->type == XML_TEXT_NODE) ||
931                             (tmp->type == XML_CDATA_SECTION_NODE) ||
932                             (tmp->type == XML_ENTITY_REF_NODE)) {
933                             ctxt->format = 0;
934                             unformattedNode = cur;
935                             break;
936                         }
937                         tmp = tmp->next;
938                     }
939                 }
940                 if (ctxt->format == 2)
941                     xmlOutputBufferWriteWSNonSig(ctxt, 1);
942                 xmlOutputBufferWrite(buf, 1, ">");
943                 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
944                 if (ctxt->level >= 0) ctxt->level++;
945                 cur = cur->children;
946                 continue;
947             }
948 
949             break;
950 
951         case XML_TEXT_NODE:
952 	    if (cur->content == NULL)
953                 break;
954 	    if (cur->name != xmlStringTextNoenc) {
955                 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
956 	    } else {
957 		/*
958 		 * Disable escaping, needed for XSLT
959 		 */
960 		xmlOutputBufferWriteString(buf, (const char *) cur->content);
961 	    }
962 	    break;
963 
964         case XML_PI_NODE:
965 	    if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
966 		xmlOutputBufferWrite(buf, ctxt->indent_size *
967 				     (ctxt->level > ctxt->indent_nr ?
968 				      ctxt->indent_nr : ctxt->level),
969 				     ctxt->indent);
970 
971             if (cur->content != NULL) {
972                 xmlOutputBufferWrite(buf, 2, "<?");
973                 xmlOutputBufferWriteString(buf, (const char *)cur->name);
974                 if (cur->content != NULL) {
975                     if (ctxt->format == 2)
976                         xmlOutputBufferWriteWSNonSig(ctxt, 0);
977                     else
978                         xmlOutputBufferWrite(buf, 1, " ");
979                     xmlOutputBufferWriteString(buf,
980                             (const char *)cur->content);
981                 }
982                 xmlOutputBufferWrite(buf, 2, "?>");
983             } else {
984                 xmlOutputBufferWrite(buf, 2, "<?");
985                 xmlOutputBufferWriteString(buf, (const char *)cur->name);
986                 if (ctxt->format == 2)
987                     xmlOutputBufferWriteWSNonSig(ctxt, 0);
988                 xmlOutputBufferWrite(buf, 2, "?>");
989             }
990             break;
991 
992         case XML_COMMENT_NODE:
993 	    if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
994 		xmlOutputBufferWrite(buf, ctxt->indent_size *
995 				     (ctxt->level > ctxt->indent_nr ?
996 				      ctxt->indent_nr : ctxt->level),
997 				     ctxt->indent);
998 
999             if (cur->content != NULL) {
1000                 xmlOutputBufferWrite(buf, 4, "<!--");
1001                 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1002                 xmlOutputBufferWrite(buf, 3, "-->");
1003             }
1004             break;
1005 
1006         case XML_ENTITY_REF_NODE:
1007             xmlOutputBufferWrite(buf, 1, "&");
1008             xmlOutputBufferWriteString(buf, (const char *)cur->name);
1009             xmlOutputBufferWrite(buf, 1, ";");
1010             break;
1011 
1012         case XML_CDATA_SECTION_NODE:
1013             if (cur->content == NULL || *cur->content == '\0') {
1014                 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1015             } else {
1016                 start = end = cur->content;
1017                 while (*end != '\0') {
1018                     if ((*end == ']') && (*(end + 1) == ']') &&
1019                         (*(end + 2) == '>')) {
1020                         end = end + 2;
1021                         xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1022                         xmlOutputBufferWrite(buf, end - start,
1023                                 (const char *)start);
1024                         xmlOutputBufferWrite(buf, 3, "]]>");
1025                         start = end;
1026                     }
1027                     end++;
1028                 }
1029                 if (start != end) {
1030                     xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1031                     xmlOutputBufferWriteString(buf, (const char *)start);
1032                     xmlOutputBufferWrite(buf, 3, "]]>");
1033                 }
1034             }
1035             break;
1036 
1037         case XML_ATTRIBUTE_NODE:
1038             xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1039             break;
1040 
1041         case XML_NAMESPACE_DECL:
1042             xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1043             break;
1044 
1045         default:
1046             break;
1047         }
1048 
1049         while (1) {
1050             if (cur == root)
1051                 return;
1052             if ((ctxt->format == 1) &&
1053                 (cur->type != XML_XINCLUDE_START) &&
1054                 (cur->type != XML_XINCLUDE_END))
1055                 xmlOutputBufferWrite(buf, 1, "\n");
1056             if (cur->next != NULL) {
1057                 cur = cur->next;
1058                 break;
1059             }
1060 
1061             /*
1062              * The parent should never be NULL here but we want to handle
1063              * corrupted documents gracefully.
1064              */
1065             if (cur->parent == NULL)
1066                 return;
1067             cur = cur->parent;
1068 
1069             if (cur->type == XML_ELEMENT_NODE) {
1070                 if (ctxt->level > 0) ctxt->level--;
1071                 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1072                     xmlOutputBufferWrite(buf, ctxt->indent_size *
1073                                          (ctxt->level > ctxt->indent_nr ?
1074                                           ctxt->indent_nr : ctxt->level),
1075                                          ctxt->indent);
1076 
1077                 xmlOutputBufferWrite(buf, 2, "</");
1078                 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1079                     xmlOutputBufferWriteString(buf,
1080                             (const char *)cur->ns->prefix);
1081                     xmlOutputBufferWrite(buf, 1, ":");
1082                 }
1083 
1084                 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1085                 if (ctxt->format == 2)
1086                     xmlOutputBufferWriteWSNonSig(ctxt, 0);
1087                 xmlOutputBufferWrite(buf, 1, ">");
1088 
1089                 if (cur == unformattedNode) {
1090                     ctxt->format = format;
1091                     unformattedNode = NULL;
1092                 }
1093             }
1094         }
1095     }
1096 }
1097 
1098 /**
1099  * xmlDocContentDumpOutput:
1100  * @cur:  the document
1101  *
1102  * Dump an XML document.
1103  */
1104 static int
xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt,xmlDocPtr cur)1105 xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
1106 #ifdef LIBXML_HTML_ENABLED
1107     xmlDtdPtr dtd;
1108     int is_xhtml = 0;
1109 #endif
1110     const xmlChar *oldenc = cur->encoding;
1111     const xmlChar *oldctxtenc = ctxt->encoding;
1112     const xmlChar *encoding = ctxt->encoding;
1113     xmlCharEncodingOutputFunc oldescape = ctxt->escape;
1114     xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
1115     xmlOutputBufferPtr buf = ctxt->buf;
1116     xmlCharEncoding enc;
1117     int switched_encoding = 0;
1118 
1119     xmlInitParser();
1120 
1121     if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
1122         (cur->type != XML_DOCUMENT_NODE))
1123 	 return(-1);
1124 
1125     if (ctxt->encoding != NULL) {
1126         cur->encoding = BAD_CAST ctxt->encoding;
1127     } else if (cur->encoding != NULL) {
1128 	encoding = cur->encoding;
1129     }
1130 
1131     if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
1132          ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
1133          ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
1134         (ctxt->options & XML_SAVE_AS_HTML)) {
1135 #ifdef LIBXML_HTML_ENABLED
1136         if (encoding != NULL)
1137 	    htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1138         if (encoding == NULL)
1139 	    encoding = htmlGetMetaEncoding(cur);
1140         if (encoding == NULL)
1141 	    encoding = BAD_CAST "HTML";
1142 	if ((encoding != NULL) && (oldctxtenc == NULL) &&
1143 	    (buf->encoder == NULL) && (buf->conv == NULL)) {
1144 	    if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1145 		cur->encoding = oldenc;
1146 		return(-1);
1147 	    }
1148 	}
1149         if (ctxt->options & XML_SAVE_FORMAT)
1150 	    htmlDocContentDumpFormatOutput(buf, cur,
1151 	                                   (const char *)encoding, 1);
1152 	else
1153 	    htmlDocContentDumpFormatOutput(buf, cur,
1154 	                                   (const char *)encoding, 0);
1155 	if (ctxt->encoding != NULL)
1156 	    cur->encoding = oldenc;
1157 	return(0);
1158 #else
1159         return(-1);
1160 #endif
1161     } else if ((cur->type == XML_DOCUMENT_NODE) ||
1162                (ctxt->options & XML_SAVE_AS_XML) ||
1163                (ctxt->options & XML_SAVE_XHTML)) {
1164 	enc = xmlParseCharEncoding((const char*) encoding);
1165 	if ((encoding != NULL) && (oldctxtenc == NULL) &&
1166 	    (buf->encoder == NULL) && (buf->conv == NULL) &&
1167 	    ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
1168 	    if ((enc != XML_CHAR_ENCODING_UTF8) &&
1169 		(enc != XML_CHAR_ENCODING_NONE) &&
1170 		(enc != XML_CHAR_ENCODING_ASCII)) {
1171 		/*
1172 		 * we need to switch to this encoding but just for this
1173 		 * document since we output the XMLDecl the conversion
1174 		 * must be done to not generate not well formed documents.
1175 		 */
1176 		if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1177 		    cur->encoding = oldenc;
1178 		    return(-1);
1179 		}
1180 		switched_encoding = 1;
1181 	    }
1182 	    if (ctxt->escape == xmlEscapeEntities)
1183 		ctxt->escape = NULL;
1184 	    if (ctxt->escapeAttr == xmlEscapeEntities)
1185 		ctxt->escapeAttr = NULL;
1186 	}
1187 
1188 
1189 	/*
1190 	 * Save the XML declaration
1191 	 */
1192 	if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1193 	    xmlOutputBufferWrite(buf, 14, "<?xml version=");
1194 	    if (cur->version != NULL)
1195 		xmlBufWriteQuotedString(buf->buffer, cur->version);
1196 	    else
1197 		xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1198 	    if (encoding != NULL) {
1199 		xmlOutputBufferWrite(buf, 10, " encoding=");
1200 		xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
1201 	    }
1202 	    switch (cur->standalone) {
1203 		case 0:
1204 		    xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1205 		    break;
1206 		case 1:
1207 		    xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1208 		    break;
1209 	    }
1210 	    xmlOutputBufferWrite(buf, 3, "?>\n");
1211 	}
1212 
1213 #ifdef LIBXML_HTML_ENABLED
1214         if (ctxt->options & XML_SAVE_XHTML)
1215             is_xhtml = 1;
1216 	if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1217 	    dtd = xmlGetIntSubset(cur);
1218 	    if (dtd != NULL) {
1219 		is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1220 		if (is_xhtml < 0) is_xhtml = 0;
1221 	    }
1222 	}
1223 #endif
1224 	if (cur->children != NULL) {
1225 	    xmlNodePtr child = cur->children;
1226 
1227 	    while (child != NULL) {
1228 		ctxt->level = 0;
1229 #ifdef LIBXML_HTML_ENABLED
1230 		if (is_xhtml)
1231 		    xhtmlNodeDumpOutput(ctxt, child);
1232 		else
1233 #endif
1234 		    xmlNodeDumpOutputInternal(ctxt, child);
1235                 if ((child->type != XML_XINCLUDE_START) &&
1236                     (child->type != XML_XINCLUDE_END))
1237                     xmlOutputBufferWrite(buf, 1, "\n");
1238 		child = child->next;
1239 	    }
1240 	}
1241     }
1242 
1243     /*
1244      * Restore the state of the saving context at the end of the document
1245      */
1246     if ((switched_encoding) && (oldctxtenc == NULL)) {
1247 	xmlSaveClearEncoding(ctxt);
1248 	ctxt->escape = oldescape;
1249 	ctxt->escapeAttr = oldescapeAttr;
1250     }
1251     cur->encoding = oldenc;
1252     return(0);
1253 }
1254 
1255 #ifdef LIBXML_HTML_ENABLED
1256 /************************************************************************
1257  *									*
1258  *		Functions specific to XHTML serialization		*
1259  *									*
1260  ************************************************************************/
1261 
1262 /**
1263  * xhtmlIsEmpty:
1264  * @node:  the node
1265  *
1266  * Check if a node is an empty xhtml node
1267  *
1268  * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1269  */
1270 static int
xhtmlIsEmpty(xmlNodePtr node)1271 xhtmlIsEmpty(xmlNodePtr node) {
1272     if (node == NULL)
1273 	return(-1);
1274     if (node->type != XML_ELEMENT_NODE)
1275 	return(0);
1276     if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1277 	return(0);
1278     if (node->children != NULL)
1279 	return(0);
1280     switch (node->name[0]) {
1281 	case 'a':
1282 	    if (xmlStrEqual(node->name, BAD_CAST "area"))
1283 		return(1);
1284 	    return(0);
1285 	case 'b':
1286 	    if (xmlStrEqual(node->name, BAD_CAST "br"))
1287 		return(1);
1288 	    if (xmlStrEqual(node->name, BAD_CAST "base"))
1289 		return(1);
1290 	    if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1291 		return(1);
1292 	    return(0);
1293 	case 'c':
1294 	    if (xmlStrEqual(node->name, BAD_CAST "col"))
1295 		return(1);
1296 	    return(0);
1297 	case 'f':
1298 	    if (xmlStrEqual(node->name, BAD_CAST "frame"))
1299 		return(1);
1300 	    return(0);
1301 	case 'h':
1302 	    if (xmlStrEqual(node->name, BAD_CAST "hr"))
1303 		return(1);
1304 	    return(0);
1305 	case 'i':
1306 	    if (xmlStrEqual(node->name, BAD_CAST "img"))
1307 		return(1);
1308 	    if (xmlStrEqual(node->name, BAD_CAST "input"))
1309 		return(1);
1310 	    if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1311 		return(1);
1312 	    return(0);
1313 	case 'l':
1314 	    if (xmlStrEqual(node->name, BAD_CAST "link"))
1315 		return(1);
1316 	    return(0);
1317 	case 'm':
1318 	    if (xmlStrEqual(node->name, BAD_CAST "meta"))
1319 		return(1);
1320 	    return(0);
1321 	case 'p':
1322 	    if (xmlStrEqual(node->name, BAD_CAST "param"))
1323 		return(1);
1324 	    return(0);
1325     }
1326     return(0);
1327 }
1328 
1329 /**
1330  * xhtmlAttrListDumpOutput:
1331  * @cur:  the first attribute pointer
1332  *
1333  * Dump a list of XML attributes
1334  */
1335 static void
xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt,xmlAttrPtr cur)1336 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1337     xmlAttrPtr xml_lang = NULL;
1338     xmlAttrPtr lang = NULL;
1339     xmlAttrPtr name = NULL;
1340     xmlAttrPtr id = NULL;
1341     xmlNodePtr parent;
1342     xmlOutputBufferPtr buf;
1343 
1344     if (cur == NULL) return;
1345     buf = ctxt->buf;
1346     parent = cur->parent;
1347     while (cur != NULL) {
1348 	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1349 	    id = cur;
1350 	else
1351 	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1352 	    name = cur;
1353 	else
1354 	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1355 	    lang = cur;
1356 	else
1357 	if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1358 	    (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1359 	    xml_lang = cur;
1360 	else if ((cur->ns == NULL) &&
1361 		 ((cur->children == NULL) ||
1362 		  (cur->children->content == NULL) ||
1363 		  (cur->children->content[0] == 0)) &&
1364 		 (htmlIsBooleanAttr(cur->name))) {
1365 	    if (cur->children != NULL)
1366 		xmlFreeNode(cur->children);
1367 	    cur->children = xmlNewText(cur->name);
1368 	    if (cur->children != NULL)
1369 		cur->children->parent = (xmlNodePtr) cur;
1370 	}
1371         xmlAttrDumpOutput(ctxt, cur);
1372 	cur = cur->next;
1373     }
1374     /*
1375      * C.8
1376      */
1377     if ((name != NULL) && (id == NULL)) {
1378 	if ((parent != NULL) && (parent->name != NULL) &&
1379 	    ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1380 	     (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1381 	     (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1382 	     (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1383 	     (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1384 	     (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1385 	     (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1386 	     (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1387 	     (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
1388 	    xmlOutputBufferWrite(buf, 5, " id=\"");
1389 	    xmlAttrSerializeContent(buf, name);
1390 	    xmlOutputBufferWrite(buf, 1, "\"");
1391 	}
1392     }
1393     /*
1394      * C.7.
1395      */
1396     if ((lang != NULL) && (xml_lang == NULL)) {
1397 	xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1398 	xmlAttrSerializeContent(buf, lang);
1399 	xmlOutputBufferWrite(buf, 1, "\"");
1400     } else
1401     if ((xml_lang != NULL) && (lang == NULL)) {
1402 	xmlOutputBufferWrite(buf, 7, " lang=\"");
1403 	xmlAttrSerializeContent(buf, xml_lang);
1404 	xmlOutputBufferWrite(buf, 1, "\"");
1405     }
1406 }
1407 
1408 /**
1409  * xhtmlNodeDumpOutput:
1410  * @buf:  the XML buffer output
1411  * @doc:  the XHTML document
1412  * @cur:  the current node
1413  * @level: the imbrication level for indenting
1414  * @format: is formatting allowed
1415  * @encoding:  an optional encoding string
1416  *
1417  * Dump an XHTML node, recursive behaviour, children are printed too.
1418  */
1419 static void
xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)1420 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1421     int format = ctxt->format, addmeta;
1422     xmlNodePtr tmp, root, unformattedNode = NULL;
1423     xmlChar *start, *end;
1424     xmlOutputBufferPtr buf = ctxt->buf;
1425 
1426     if (cur == NULL) return;
1427 
1428     root = cur;
1429     while (1) {
1430         switch (cur->type) {
1431         case XML_DOCUMENT_NODE:
1432         case XML_HTML_DOCUMENT_NODE:
1433             xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1434 	    break;
1435 
1436         case XML_NAMESPACE_DECL:
1437 	    xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1438 	    break;
1439 
1440         case XML_DTD_NODE:
1441             xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1442 	    break;
1443 
1444         case XML_DOCUMENT_FRAG_NODE:
1445             if (cur->children) {
1446                 cur = cur->children;
1447                 continue;
1448             }
1449             break;
1450 
1451         case XML_ELEMENT_DECL:
1452             xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1453 	    break;
1454 
1455         case XML_ATTRIBUTE_DECL:
1456             xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1457 	    break;
1458 
1459         case XML_ENTITY_DECL:
1460             xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1461 	    break;
1462 
1463         case XML_ELEMENT_NODE:
1464             addmeta = 0;
1465 
1466 	    if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
1467 		xmlOutputBufferWrite(buf, ctxt->indent_size *
1468 				     (ctxt->level > ctxt->indent_nr ?
1469 				      ctxt->indent_nr : ctxt->level),
1470 				     ctxt->indent);
1471 
1472             xmlOutputBufferWrite(buf, 1, "<");
1473             if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1474                 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1475                 xmlOutputBufferWrite(buf, 1, ":");
1476             }
1477 
1478             xmlOutputBufferWriteString(buf, (const char *)cur->name);
1479             if (cur->nsDef)
1480                 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1481             if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1482                 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1483                 /*
1484                  * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1485                  */
1486                 xmlOutputBufferWriteString(buf,
1487                         " xmlns=\"http://www.w3.org/1999/xhtml\"");
1488             }
1489             if (cur->properties != NULL)
1490                 xhtmlAttrListDumpOutput(ctxt, cur->properties);
1491 
1492             if ((cur->parent != NULL) &&
1493                 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1494                 xmlStrEqual(cur->name, BAD_CAST"head") &&
1495                 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1496 
1497                 tmp = cur->children;
1498                 while (tmp != NULL) {
1499                     if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1500                         xmlChar *httpequiv;
1501 
1502                         httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
1503                         if (httpequiv != NULL) {
1504                             if (xmlStrcasecmp(httpequiv,
1505                                         BAD_CAST"Content-Type") == 0) {
1506                                 xmlFree(httpequiv);
1507                                 break;
1508                             }
1509                             xmlFree(httpequiv);
1510                         }
1511                     }
1512                     tmp = tmp->next;
1513                 }
1514                 if (tmp == NULL)
1515                     addmeta = 1;
1516             }
1517 
1518             if (cur->children == NULL) {
1519                 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1520                     ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1521                     /*
1522                      * C.2. Empty Elements
1523                      */
1524                     xmlOutputBufferWrite(buf, 3, " />");
1525                 } else {
1526                     if (addmeta == 1) {
1527                         xmlOutputBufferWrite(buf, 1, ">");
1528                         if (ctxt->format == 1) {
1529                             xmlOutputBufferWrite(buf, 1, "\n");
1530                             if (xmlIndentTreeOutput)
1531                                 xmlOutputBufferWrite(buf, ctxt->indent_size *
1532                                     (ctxt->level + 1 > ctxt->indent_nr ?
1533                                     ctxt->indent_nr : ctxt->level + 1),
1534                                     ctxt->indent);
1535                         }
1536                         xmlOutputBufferWriteString(buf,
1537                                 "<meta http-equiv=\"Content-Type\" "
1538                                 "content=\"text/html; charset=");
1539                         if (ctxt->encoding) {
1540                             xmlOutputBufferWriteString(buf,
1541                                     (const char *)ctxt->encoding);
1542                         } else {
1543                             xmlOutputBufferWrite(buf, 5, "UTF-8");
1544                         }
1545                         xmlOutputBufferWrite(buf, 4, "\" />");
1546                         if (ctxt->format == 1)
1547                             xmlOutputBufferWrite(buf, 1, "\n");
1548                     } else {
1549                         xmlOutputBufferWrite(buf, 1, ">");
1550                     }
1551                     /*
1552                      * C.3. Element Minimization and Empty Element Content
1553                      */
1554                     xmlOutputBufferWrite(buf, 2, "</");
1555                     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1556                         xmlOutputBufferWriteString(buf,
1557                                 (const char *)cur->ns->prefix);
1558                         xmlOutputBufferWrite(buf, 1, ":");
1559                     }
1560                     xmlOutputBufferWriteString(buf, (const char *)cur->name);
1561                     xmlOutputBufferWrite(buf, 1, ">");
1562                 }
1563             } else {
1564                 xmlOutputBufferWrite(buf, 1, ">");
1565                 if (addmeta == 1) {
1566                     if (ctxt->format == 1) {
1567                         xmlOutputBufferWrite(buf, 1, "\n");
1568                         if (xmlIndentTreeOutput)
1569                             xmlOutputBufferWrite(buf, ctxt->indent_size *
1570                                 (ctxt->level + 1 > ctxt->indent_nr ?
1571                                 ctxt->indent_nr : ctxt->level + 1),
1572                                 ctxt->indent);
1573                     }
1574                     xmlOutputBufferWriteString(buf,
1575                             "<meta http-equiv=\"Content-Type\" "
1576                             "content=\"text/html; charset=");
1577                     if (ctxt->encoding) {
1578                         xmlOutputBufferWriteString(buf,
1579                                 (const char *)ctxt->encoding);
1580                     } else {
1581                         xmlOutputBufferWrite(buf, 5, "UTF-8");
1582                     }
1583                     xmlOutputBufferWrite(buf, 4, "\" />");
1584                 }
1585 
1586                 if (ctxt->format == 1) {
1587                     tmp = cur->children;
1588                     while (tmp != NULL) {
1589                         if ((tmp->type == XML_TEXT_NODE) ||
1590                             (tmp->type == XML_ENTITY_REF_NODE)) {
1591                             unformattedNode = cur;
1592                             ctxt->format = 0;
1593                             break;
1594                         }
1595                         tmp = tmp->next;
1596                     }
1597                 }
1598 
1599                 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1600                 if (ctxt->level >= 0) ctxt->level++;
1601                 cur = cur->children;
1602                 continue;
1603             }
1604 
1605             break;
1606 
1607         case XML_TEXT_NODE:
1608 	    if (cur->content == NULL)
1609                 break;
1610 	    if ((cur->name == xmlStringText) ||
1611 		(cur->name != xmlStringTextNoenc)) {
1612                 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1613 	    } else {
1614 		/*
1615 		 * Disable escaping, needed for XSLT
1616 		 */
1617 		xmlOutputBufferWriteString(buf, (const char *) cur->content);
1618 	    }
1619 	    break;
1620 
1621         case XML_PI_NODE:
1622             if (cur->content != NULL) {
1623                 xmlOutputBufferWrite(buf, 2, "<?");
1624                 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1625                 if (cur->content != NULL) {
1626                     xmlOutputBufferWrite(buf, 1, " ");
1627                     xmlOutputBufferWriteString(buf,
1628                             (const char *)cur->content);
1629                 }
1630                 xmlOutputBufferWrite(buf, 2, "?>");
1631             } else {
1632                 xmlOutputBufferWrite(buf, 2, "<?");
1633                 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1634                 xmlOutputBufferWrite(buf, 2, "?>");
1635             }
1636             break;
1637 
1638         case XML_COMMENT_NODE:
1639             if (cur->content != NULL) {
1640                 xmlOutputBufferWrite(buf, 4, "<!--");
1641                 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1642                 xmlOutputBufferWrite(buf, 3, "-->");
1643             }
1644             break;
1645 
1646         case XML_ENTITY_REF_NODE:
1647             xmlOutputBufferWrite(buf, 1, "&");
1648             xmlOutputBufferWriteString(buf, (const char *)cur->name);
1649             xmlOutputBufferWrite(buf, 1, ";");
1650             break;
1651 
1652         case XML_CDATA_SECTION_NODE:
1653             if (cur->content == NULL || *cur->content == '\0') {
1654                 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1655             } else {
1656                 start = end = cur->content;
1657                 while (*end != '\0') {
1658                     if (*end == ']' && *(end + 1) == ']' &&
1659                         *(end + 2) == '>') {
1660                         end = end + 2;
1661                         xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1662                         xmlOutputBufferWrite(buf, end - start,
1663                                 (const char *)start);
1664                         xmlOutputBufferWrite(buf, 3, "]]>");
1665                         start = end;
1666                     }
1667                     end++;
1668                 }
1669                 if (start != end) {
1670                     xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1671                     xmlOutputBufferWriteString(buf, (const char *)start);
1672                     xmlOutputBufferWrite(buf, 3, "]]>");
1673                 }
1674             }
1675             break;
1676 
1677         case XML_ATTRIBUTE_NODE:
1678             xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1679 	    break;
1680 
1681         default:
1682             break;
1683         }
1684 
1685         while (1) {
1686             if (cur == root)
1687                 return;
1688             if (ctxt->format == 1)
1689                 xmlOutputBufferWrite(buf, 1, "\n");
1690             if (cur->next != NULL) {
1691                 cur = cur->next;
1692                 break;
1693             }
1694 
1695             /*
1696              * The parent should never be NULL here but we want to handle
1697              * corrupted documents gracefully.
1698              */
1699             if (cur->parent == NULL)
1700                 return;
1701             cur = cur->parent;
1702 
1703             if (cur->type == XML_ELEMENT_NODE) {
1704                 if (ctxt->level > 0) ctxt->level--;
1705                 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1706                     xmlOutputBufferWrite(buf, ctxt->indent_size *
1707                                          (ctxt->level > ctxt->indent_nr ?
1708                                           ctxt->indent_nr : ctxt->level),
1709                                          ctxt->indent);
1710 
1711                 xmlOutputBufferWrite(buf, 2, "</");
1712                 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1713                     xmlOutputBufferWriteString(buf,
1714                             (const char *)cur->ns->prefix);
1715                     xmlOutputBufferWrite(buf, 1, ":");
1716                 }
1717 
1718                 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1719                 xmlOutputBufferWrite(buf, 1, ">");
1720 
1721                 if (cur == unformattedNode) {
1722                     ctxt->format = format;
1723                     unformattedNode = NULL;
1724                 }
1725             }
1726         }
1727     }
1728 }
1729 #endif
1730 
1731 /************************************************************************
1732  *									*
1733  *			Public entry points				*
1734  *									*
1735  ************************************************************************/
1736 
1737 /**
1738  * xmlSaveToFd:
1739  * @fd:  a file descriptor number
1740  * @encoding:  the encoding name to use or NULL
1741  * @options:  a set of xmlSaveOptions
1742  *
1743  * Create a document saving context serializing to a file descriptor
1744  * with the encoding and the options given.
1745  *
1746  * Returns a new serialization context or NULL in case of error.
1747  */
1748 xmlSaveCtxtPtr
xmlSaveToFd(int fd,const char * encoding,int options)1749 xmlSaveToFd(int fd, const char *encoding, int options)
1750 {
1751     xmlSaveCtxtPtr ret;
1752 
1753     ret = xmlNewSaveCtxt(encoding, options);
1754     if (ret == NULL) return(NULL);
1755     ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1756     if (ret->buf == NULL) {
1757         xmlCharEncCloseFunc(ret->handler);
1758 	xmlFreeSaveCtxt(ret);
1759 	return(NULL);
1760     }
1761     return(ret);
1762 }
1763 
1764 /**
1765  * xmlSaveToFilename:
1766  * @filename:  a file name or an URL
1767  * @encoding:  the encoding name to use or NULL
1768  * @options:  a set of xmlSaveOptions
1769  *
1770  * Create a document saving context serializing to a filename or possibly
1771  * to an URL (but this is less reliable) with the encoding and the options
1772  * given.
1773  *
1774  * Returns a new serialization context or NULL in case of error.
1775  */
1776 xmlSaveCtxtPtr
xmlSaveToFilename(const char * filename,const char * encoding,int options)1777 xmlSaveToFilename(const char *filename, const char *encoding, int options)
1778 {
1779     xmlSaveCtxtPtr ret;
1780     int compression = 0; /* TODO handle compression option */
1781 
1782     ret = xmlNewSaveCtxt(encoding, options);
1783     if (ret == NULL) return(NULL);
1784     ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
1785                                              compression);
1786     if (ret->buf == NULL) {
1787         xmlCharEncCloseFunc(ret->handler);
1788 	xmlFreeSaveCtxt(ret);
1789 	return(NULL);
1790     }
1791     return(ret);
1792 }
1793 
1794 /**
1795  * xmlSaveToBuffer:
1796  * @buffer:  a buffer
1797  * @encoding:  the encoding name to use or NULL
1798  * @options:  a set of xmlSaveOptions
1799  *
1800  * Create a document saving context serializing to a buffer
1801  * with the encoding and the options given
1802  *
1803  * Returns a new serialization context or NULL in case of error.
1804  */
1805 
1806 xmlSaveCtxtPtr
xmlSaveToBuffer(xmlBufferPtr buffer,const char * encoding,int options)1807 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1808 {
1809     xmlSaveCtxtPtr ret;
1810 
1811     ret = xmlNewSaveCtxt(encoding, options);
1812     if (ret == NULL) return(NULL);
1813     ret->buf = xmlOutputBufferCreateBuffer(buffer, ret->handler);
1814     if (ret->buf == NULL) {
1815         xmlCharEncCloseFunc(ret->handler);
1816 	xmlFreeSaveCtxt(ret);
1817 	return(NULL);
1818     }
1819     return(ret);
1820 }
1821 
1822 /**
1823  * xmlSaveToIO:
1824  * @iowrite:  an I/O write function
1825  * @ioclose:  an I/O close function
1826  * @ioctx:  an I/O handler
1827  * @encoding:  the encoding name to use or NULL
1828  * @options:  a set of xmlSaveOptions
1829  *
1830  * Create a document saving context serializing to a file descriptor
1831  * with the encoding and the options given
1832  *
1833  * Returns a new serialization context or NULL in case of error.
1834  */
1835 xmlSaveCtxtPtr
xmlSaveToIO(xmlOutputWriteCallback iowrite,xmlOutputCloseCallback ioclose,void * ioctx,const char * encoding,int options)1836 xmlSaveToIO(xmlOutputWriteCallback iowrite,
1837             xmlOutputCloseCallback ioclose,
1838             void *ioctx, const char *encoding, int options)
1839 {
1840     xmlSaveCtxtPtr ret;
1841 
1842     ret = xmlNewSaveCtxt(encoding, options);
1843     if (ret == NULL) return(NULL);
1844     ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1845     if (ret->buf == NULL) {
1846         xmlCharEncCloseFunc(ret->handler);
1847 	xmlFreeSaveCtxt(ret);
1848 	return(NULL);
1849     }
1850     return(ret);
1851 }
1852 
1853 /**
1854  * xmlSaveDoc:
1855  * @ctxt:  a document saving context
1856  * @doc:  a document
1857  *
1858  * Save a full document to a saving context
1859  * TODO: The function is not fully implemented yet as it does not return the
1860  * byte count but 0 instead
1861  *
1862  * Returns the number of byte written or -1 in case of error
1863  */
1864 long
xmlSaveDoc(xmlSaveCtxtPtr ctxt,xmlDocPtr doc)1865 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1866 {
1867     long ret = 0;
1868 
1869     if ((ctxt == NULL) || (doc == NULL)) return(-1);
1870     if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1871         return(-1);
1872     return(ret);
1873 }
1874 
1875 /**
1876  * xmlSaveTree:
1877  * @ctxt:  a document saving context
1878  * @node:  the top node of the subtree to save
1879  *
1880  * Save a subtree starting at the node parameter to a saving context
1881  * TODO: The function is not fully implemented yet as it does not return the
1882  * byte count but 0 instead
1883  *
1884  * Returns the number of byte written or -1 in case of error
1885  */
1886 long
xmlSaveTree(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)1887 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr cur)
1888 {
1889     long ret = 0;
1890 
1891     if ((ctxt == NULL) || (cur == NULL)) return(-1);
1892 #ifdef LIBXML_HTML_ENABLED
1893     if (ctxt->options & XML_SAVE_XHTML) {
1894         xhtmlNodeDumpOutput(ctxt, cur);
1895         return(ret);
1896     }
1897     if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
1898          (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
1899          ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
1900         (ctxt->options & XML_SAVE_AS_HTML)) {
1901 	htmlNodeDumpOutputInternal(ctxt, cur);
1902 	return(ret);
1903     }
1904 #endif
1905     xmlNodeDumpOutputInternal(ctxt, cur);
1906     return(ret);
1907 }
1908 
1909 /**
1910  * xmlSaveFlush:
1911  * @ctxt:  a document saving context
1912  *
1913  * Flush a document saving context, i.e. make sure that all bytes have
1914  * been output.
1915  *
1916  * Returns the number of byte written or -1 in case of error.
1917  */
1918 int
xmlSaveFlush(xmlSaveCtxtPtr ctxt)1919 xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1920 {
1921     if (ctxt == NULL) return(-1);
1922     if (ctxt->buf == NULL) return(-1);
1923     return(xmlOutputBufferFlush(ctxt->buf));
1924 }
1925 
1926 /**
1927  * xmlSaveClose:
1928  * @ctxt:  a document saving context
1929  *
1930  * Close a document saving context, i.e. make sure that all bytes have
1931  * been output and free the associated data.
1932  *
1933  * Returns the number of byte written or -1 in case of error.
1934  */
1935 int
xmlSaveClose(xmlSaveCtxtPtr ctxt)1936 xmlSaveClose(xmlSaveCtxtPtr ctxt)
1937 {
1938     int ret;
1939 
1940     if (ctxt == NULL) return(-1);
1941     ret = xmlSaveFlush(ctxt);
1942     xmlFreeSaveCtxt(ctxt);
1943     return(ret);
1944 }
1945 
1946 /**
1947  * xmlSaveSetEscape:
1948  * @ctxt:  a document saving context
1949  * @escape:  the escaping function
1950  *
1951  * Set a custom escaping function to be used for text in element content
1952  *
1953  * Returns 0 if successful or -1 in case of error.
1954  */
1955 int
xmlSaveSetEscape(xmlSaveCtxtPtr ctxt,xmlCharEncodingOutputFunc escape)1956 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1957 {
1958     if (ctxt == NULL) return(-1);
1959     ctxt->escape = escape;
1960     return(0);
1961 }
1962 
1963 /**
1964  * xmlSaveSetAttrEscape:
1965  * @ctxt:  a document saving context
1966  * @escape:  the escaping function
1967  *
1968  * Set a custom escaping function to be used for text in attribute content
1969  *
1970  * Returns 0 if successful or -1 in case of error.
1971  */
1972 int
xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt,xmlCharEncodingOutputFunc escape)1973 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1974 {
1975     if (ctxt == NULL) return(-1);
1976     ctxt->escapeAttr = escape;
1977     return(0);
1978 }
1979 
1980 /************************************************************************
1981  *									*
1982  *		Public entry points based on buffers			*
1983  *									*
1984  ************************************************************************/
1985 
1986 /**
1987  * xmlBufAttrSerializeTxtContent:
1988  * @buf:  and xmlBufPtr output
1989  * @doc:  the document
1990  * @attr: the attribute node
1991  * @string: the text content
1992  *
1993  * Serialize text attribute values to an xmlBufPtr
1994  */
1995 void
xmlBufAttrSerializeTxtContent(xmlBufPtr buf,xmlDocPtr doc,xmlAttrPtr attr,const xmlChar * string)1996 xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
1997                               xmlAttrPtr attr, const xmlChar * string)
1998 {
1999     xmlChar *base, *cur;
2000 
2001     if (string == NULL)
2002         return;
2003     base = cur = (xmlChar *) string;
2004     while (*cur != 0) {
2005         if (*cur == '\n') {
2006             if (base != cur)
2007                 xmlBufAdd(buf, base, cur - base);
2008             xmlBufAdd(buf, BAD_CAST "&#10;", 5);
2009             cur++;
2010             base = cur;
2011         } else if (*cur == '\r') {
2012             if (base != cur)
2013                 xmlBufAdd(buf, base, cur - base);
2014             xmlBufAdd(buf, BAD_CAST "&#13;", 5);
2015             cur++;
2016             base = cur;
2017         } else if (*cur == '\t') {
2018             if (base != cur)
2019                 xmlBufAdd(buf, base, cur - base);
2020             xmlBufAdd(buf, BAD_CAST "&#9;", 4);
2021             cur++;
2022             base = cur;
2023         } else if (*cur == '"') {
2024             if (base != cur)
2025                 xmlBufAdd(buf, base, cur - base);
2026             xmlBufAdd(buf, BAD_CAST "&quot;", 6);
2027             cur++;
2028             base = cur;
2029         } else if (*cur == '<') {
2030             if (base != cur)
2031                 xmlBufAdd(buf, base, cur - base);
2032             xmlBufAdd(buf, BAD_CAST "&lt;", 4);
2033             cur++;
2034             base = cur;
2035         } else if (*cur == '>') {
2036             if (base != cur)
2037                 xmlBufAdd(buf, base, cur - base);
2038             xmlBufAdd(buf, BAD_CAST "&gt;", 4);
2039             cur++;
2040             base = cur;
2041         } else if (*cur == '&') {
2042             if (base != cur)
2043                 xmlBufAdd(buf, base, cur - base);
2044             xmlBufAdd(buf, BAD_CAST "&amp;", 5);
2045             cur++;
2046             base = cur;
2047         } else if ((*cur >= 0x80) && (cur[1] != 0) &&
2048 	           ((doc == NULL) || (doc->encoding == NULL))) {
2049             /*
2050              * We assume we have UTF-8 content.
2051              */
2052             unsigned char tmp[12];
2053             int val = 0, l = 1;
2054 
2055             if (base != cur)
2056                 xmlBufAdd(buf, base, cur - base);
2057             if (*cur < 0xC0) {
2058                 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
2059 		xmlSerializeHexCharRef(tmp, *cur);
2060                 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2061                 cur++;
2062                 base = cur;
2063                 continue;
2064             } else if (*cur < 0xE0) {
2065                 val = (cur[0]) & 0x1F;
2066                 val <<= 6;
2067                 val |= (cur[1]) & 0x3F;
2068                 l = 2;
2069             } else if ((*cur < 0xF0) && (cur [2] != 0)) {
2070                 val = (cur[0]) & 0x0F;
2071                 val <<= 6;
2072                 val |= (cur[1]) & 0x3F;
2073                 val <<= 6;
2074                 val |= (cur[2]) & 0x3F;
2075                 l = 3;
2076             } else if ((*cur < 0xF8) && (cur [2] != 0) && (cur[3] != 0)) {
2077                 val = (cur[0]) & 0x07;
2078                 val <<= 6;
2079                 val |= (cur[1]) & 0x3F;
2080                 val <<= 6;
2081                 val |= (cur[2]) & 0x3F;
2082                 val <<= 6;
2083                 val |= (cur[3]) & 0x3F;
2084                 l = 4;
2085             }
2086             if ((l == 1) || (!IS_CHAR(val))) {
2087                 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
2088 		xmlSerializeHexCharRef(tmp, *cur);
2089                 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2090                 cur++;
2091                 base = cur;
2092                 continue;
2093             }
2094             /*
2095              * We could do multiple things here. Just save
2096              * as a char ref
2097              */
2098 	    xmlSerializeHexCharRef(tmp, val);
2099             xmlBufAdd(buf, (xmlChar *) tmp, -1);
2100             cur += l;
2101             base = cur;
2102         } else {
2103             cur++;
2104         }
2105     }
2106     if (base != cur)
2107         xmlBufAdd(buf, base, cur - base);
2108 }
2109 
2110 /**
2111  * xmlAttrSerializeTxtContent:
2112  * @buf:  the XML buffer output
2113  * @doc:  the document
2114  * @attr: the attribute node
2115  * @string: the text content
2116  *
2117  * Serialize text attribute values to an xml simple buffer
2118  */
2119 void
xmlAttrSerializeTxtContent(xmlBufferPtr buf,xmlDocPtr doc,xmlAttrPtr attr,const xmlChar * string)2120 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
2121                            xmlAttrPtr attr, const xmlChar * string)
2122 {
2123     xmlBufPtr buffer;
2124 
2125     if ((buf == NULL) || (string == NULL))
2126         return;
2127     buffer = xmlBufFromBuffer(buf);
2128     if (buffer == NULL)
2129         return;
2130     xmlBufAttrSerializeTxtContent(buffer, doc, attr, string);
2131     xmlBufBackToBuffer(buffer);
2132 }
2133 
2134 /**
2135  * xmlNodeDump:
2136  * @buf:  the XML buffer output
2137  * @doc:  the document
2138  * @cur:  the current node
2139  * @level: the imbrication level for indenting
2140  * @format: is formatting allowed
2141  *
2142  * Dump an XML node, recursive behaviour,children are printed too.
2143  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2144  * or xmlKeepBlanksDefault(0) was called.
2145  * Since this is using xmlBuffer structures it is limited to 2GB and somehow
2146  * deprecated, use xmlNodeDumpOutput() instead.
2147  *
2148  * Returns the number of bytes written to the buffer or -1 in case of error
2149  */
2150 int
xmlNodeDump(xmlBufferPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format)2151 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2152             int format)
2153 {
2154     xmlBufPtr buffer;
2155     size_t ret;
2156 
2157     if ((buf == NULL) || (cur == NULL))
2158         return(-1);
2159     buffer = xmlBufFromBuffer(buf);
2160     if (buffer == NULL)
2161         return(-1);
2162     ret = xmlBufNodeDump(buffer, doc, cur, level, format);
2163     xmlBufBackToBuffer(buffer);
2164     if (ret > INT_MAX)
2165         return(-1);
2166     return((int) ret);
2167 }
2168 
2169 /**
2170  * xmlBufNodeDump:
2171  * @buf:  the XML buffer output
2172  * @doc:  the document
2173  * @cur:  the current node
2174  * @level: the imbrication level for indenting
2175  * @format: is formatting allowed
2176  *
2177  * Dump an XML node, recursive behaviour,children are printed too.
2178  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2179  * or xmlKeepBlanksDefault(0) was called
2180  *
2181  * Returns the number of bytes written to the buffer, in case of error 0
2182  *     is returned or @buf stores the error
2183  */
2184 
2185 size_t
xmlBufNodeDump(xmlBufPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format)2186 xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2187             int format)
2188 {
2189     size_t use;
2190     int ret;
2191     xmlOutputBufferPtr outbuf;
2192     int oldalloc;
2193 
2194     xmlInitParser();
2195 
2196     if (cur == NULL) {
2197 #ifdef DEBUG_TREE
2198         xmlGenericError(xmlGenericErrorContext,
2199                         "xmlNodeDump : node == NULL\n");
2200 #endif
2201         return (-1);
2202     }
2203     if (buf == NULL) {
2204 #ifdef DEBUG_TREE
2205         xmlGenericError(xmlGenericErrorContext,
2206                         "xmlNodeDump : buf == NULL\n");
2207 #endif
2208         return (-1);
2209     }
2210     outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2211     if (outbuf == NULL) {
2212         xmlSaveErrMemory("creating buffer");
2213         return (-1);
2214     }
2215     memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2216     outbuf->buffer = buf;
2217     outbuf->encoder = NULL;
2218     outbuf->writecallback = NULL;
2219     outbuf->closecallback = NULL;
2220     outbuf->context = NULL;
2221     outbuf->written = 0;
2222 
2223     use = xmlBufUse(buf);
2224     oldalloc = xmlBufGetAllocationScheme(buf);
2225     xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
2226     xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
2227     xmlBufSetAllocationScheme(buf, oldalloc);
2228     xmlFree(outbuf);
2229     ret = xmlBufUse(buf) - use;
2230     return (ret);
2231 }
2232 
2233 /**
2234  * xmlElemDump:
2235  * @f:  the FILE * for the output
2236  * @doc:  the document
2237  * @cur:  the current node
2238  *
2239  * Dump an XML/HTML node, recursive behaviour, children are printed too.
2240  */
2241 void
xmlElemDump(FILE * f,xmlDocPtr doc,xmlNodePtr cur)2242 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2243 {
2244     xmlOutputBufferPtr outbuf;
2245 
2246     xmlInitParser();
2247 
2248     if (cur == NULL) {
2249 #ifdef DEBUG_TREE
2250         xmlGenericError(xmlGenericErrorContext,
2251                         "xmlElemDump : cur == NULL\n");
2252 #endif
2253         return;
2254     }
2255 #ifdef DEBUG_TREE
2256     if (doc == NULL) {
2257         xmlGenericError(xmlGenericErrorContext,
2258                         "xmlElemDump : doc == NULL\n");
2259     }
2260 #endif
2261 
2262     outbuf = xmlOutputBufferCreateFile(f, NULL);
2263     if (outbuf == NULL)
2264         return;
2265     if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
2266 #ifdef LIBXML_HTML_ENABLED
2267         htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2268 #else
2269 	xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
2270 #endif /* LIBXML_HTML_ENABLED */
2271     } else
2272         xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2273     xmlOutputBufferClose(outbuf);
2274 }
2275 
2276 /************************************************************************
2277  *									*
2278  *		Saving functions front-ends				*
2279  *									*
2280  ************************************************************************/
2281 
2282 /**
2283  * xmlNodeDumpOutput:
2284  * @buf:  the XML buffer output
2285  * @doc:  the document
2286  * @cur:  the current node
2287  * @level: the imbrication level for indenting
2288  * @format: is formatting allowed
2289  * @encoding:  an optional encoding string
2290  *
2291  * Dump an XML node, recursive behaviour, children are printed too.
2292  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2293  * or xmlKeepBlanksDefault(0) was called
2294  */
2295 void
xmlNodeDumpOutput(xmlOutputBufferPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format,const char * encoding)2296 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2297                   int level, int format, const char *encoding)
2298 {
2299     xmlSaveCtxt ctxt;
2300 #ifdef LIBXML_HTML_ENABLED
2301     xmlDtdPtr dtd;
2302     int is_xhtml = 0;
2303 #endif
2304 
2305     xmlInitParser();
2306 
2307     if ((buf == NULL) || (cur == NULL)) return;
2308 
2309     if (encoding == NULL)
2310         encoding = "UTF-8";
2311 
2312     memset(&ctxt, 0, sizeof(ctxt));
2313     ctxt.buf = buf;
2314     ctxt.level = level;
2315     ctxt.format = format ? 1 : 0;
2316     ctxt.encoding = (const xmlChar *) encoding;
2317     xmlSaveCtxtInit(&ctxt);
2318     ctxt.options |= XML_SAVE_AS_XML;
2319 
2320 #ifdef LIBXML_HTML_ENABLED
2321     dtd = xmlGetIntSubset(doc);
2322     if (dtd != NULL) {
2323 	is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2324 	if (is_xhtml < 0)
2325 	    is_xhtml = 0;
2326     }
2327 
2328     if (is_xhtml)
2329         xhtmlNodeDumpOutput(&ctxt, cur);
2330     else
2331 #endif
2332         xmlNodeDumpOutputInternal(&ctxt, cur);
2333 }
2334 
2335 /**
2336  * xmlDocDumpFormatMemoryEnc:
2337  * @out_doc:  Document to generate XML text from
2338  * @doc_txt_ptr:  Memory pointer for allocated XML text
2339  * @doc_txt_len:  Length of the generated XML text
2340  * @txt_encoding:  Character encoding to use when generating XML text
2341  * @format:  should formatting spaces been added
2342  *
2343  * Dump the current DOM tree into memory using the character encoding specified
2344  * by the caller.  Note it is up to the caller of this function to free the
2345  * allocated memory with xmlFree().
2346  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2347  * or xmlKeepBlanksDefault(0) was called
2348  */
2349 
2350 void
xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc,xmlChar ** doc_txt_ptr,int * doc_txt_len,const char * txt_encoding,int format)2351 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2352 		int * doc_txt_len, const char * txt_encoding,
2353 		int format) {
2354     xmlSaveCtxt ctxt;
2355     int                         dummy = 0;
2356     xmlOutputBufferPtr          out_buff = NULL;
2357     xmlCharEncodingHandlerPtr   conv_hdlr = NULL;
2358 
2359     if (doc_txt_len == NULL) {
2360         doc_txt_len = &dummy;   /*  Continue, caller just won't get length */
2361     }
2362 
2363     if (doc_txt_ptr == NULL) {
2364         *doc_txt_len = 0;
2365         return;
2366     }
2367 
2368     *doc_txt_ptr = NULL;
2369     *doc_txt_len = 0;
2370 
2371     if (out_doc == NULL) {
2372         /*  No document, no output  */
2373         return;
2374     }
2375 
2376     /*
2377      *  Validate the encoding value, if provided.
2378      *  This logic is copied from xmlSaveFileEnc.
2379      */
2380 
2381     if (txt_encoding == NULL)
2382 	txt_encoding = (const char *) out_doc->encoding;
2383     if (txt_encoding != NULL) {
2384 	conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2385 	if ( conv_hdlr == NULL ) {
2386 	    xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2387 		       txt_encoding);
2388 	    return;
2389 	}
2390     }
2391 
2392     if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2393         xmlSaveErrMemory("creating buffer");
2394         return;
2395     }
2396 
2397     memset(&ctxt, 0, sizeof(ctxt));
2398     ctxt.buf = out_buff;
2399     ctxt.level = 0;
2400     ctxt.format = format ? 1 : 0;
2401     ctxt.encoding = (const xmlChar *) txt_encoding;
2402     xmlSaveCtxtInit(&ctxt);
2403     ctxt.options |= XML_SAVE_AS_XML;
2404     xmlDocContentDumpOutput(&ctxt, out_doc);
2405     xmlOutputBufferFlush(out_buff);
2406     if (out_buff->conv != NULL) {
2407 	*doc_txt_len = xmlBufUse(out_buff->conv);
2408 	*doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len);
2409     } else {
2410 	*doc_txt_len = xmlBufUse(out_buff->buffer);
2411 	*doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len);
2412     }
2413     (void)xmlOutputBufferClose(out_buff);
2414 
2415     if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2416         *doc_txt_len = 0;
2417         xmlSaveErrMemory("creating output");
2418     }
2419 
2420     return;
2421 }
2422 
2423 /**
2424  * xmlDocDumpMemory:
2425  * @cur:  the document
2426  * @mem:  OUT: the memory pointer
2427  * @size:  OUT: the memory length
2428  *
2429  * Dump an XML document in memory and return the #xmlChar * and it's size
2430  * in bytes. It's up to the caller to free the memory with xmlFree().
2431  * The resulting byte array is zero terminated, though the last 0 is not
2432  * included in the returned size.
2433  */
2434 void
xmlDocDumpMemory(xmlDocPtr cur,xmlChar ** mem,int * size)2435 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2436     xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2437 }
2438 
2439 /**
2440  * xmlDocDumpFormatMemory:
2441  * @cur:  the document
2442  * @mem:  OUT: the memory pointer
2443  * @size:  OUT: the memory length
2444  * @format:  should formatting spaces been added
2445  *
2446  *
2447  * Dump an XML document in memory and return the #xmlChar * and it's size.
2448  * It's up to the caller to free the memory with xmlFree().
2449  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2450  * or xmlKeepBlanksDefault(0) was called
2451  */
2452 void
xmlDocDumpFormatMemory(xmlDocPtr cur,xmlChar ** mem,int * size,int format)2453 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2454     xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2455 }
2456 
2457 /**
2458  * xmlDocDumpMemoryEnc:
2459  * @out_doc:  Document to generate XML text from
2460  * @doc_txt_ptr:  Memory pointer for allocated XML text
2461  * @doc_txt_len:  Length of the generated XML text
2462  * @txt_encoding:  Character encoding to use when generating XML text
2463  *
2464  * Dump the current DOM tree into memory using the character encoding specified
2465  * by the caller.  Note it is up to the caller of this function to free the
2466  * allocated memory with xmlFree().
2467  */
2468 
2469 void
xmlDocDumpMemoryEnc(xmlDocPtr out_doc,xmlChar ** doc_txt_ptr,int * doc_txt_len,const char * txt_encoding)2470 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2471 	            int * doc_txt_len, const char * txt_encoding) {
2472     xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2473 	                      txt_encoding, 0);
2474 }
2475 
2476 /**
2477  * xmlDocFormatDump:
2478  * @f:  the FILE*
2479  * @cur:  the document
2480  * @format: should formatting spaces been added
2481  *
2482  * Dump an XML document to an open FILE.
2483  *
2484  * returns: the number of bytes written or -1 in case of failure.
2485  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2486  * or xmlKeepBlanksDefault(0) was called
2487  */
2488 int
xmlDocFormatDump(FILE * f,xmlDocPtr cur,int format)2489 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2490     xmlSaveCtxt ctxt;
2491     xmlOutputBufferPtr buf;
2492     const char * encoding;
2493     xmlCharEncodingHandlerPtr handler = NULL;
2494     int ret;
2495 
2496     if (cur == NULL) {
2497 #ifdef DEBUG_TREE
2498         xmlGenericError(xmlGenericErrorContext,
2499 		"xmlDocDump : document == NULL\n");
2500 #endif
2501 	return(-1);
2502     }
2503     encoding = (const char *) cur->encoding;
2504 
2505     if (encoding != NULL) {
2506 	handler = xmlFindCharEncodingHandler(encoding);
2507 	if (handler == NULL) {
2508 	    xmlFree((char *) cur->encoding);
2509 	    cur->encoding = NULL;
2510 	    encoding = NULL;
2511 	}
2512     }
2513     buf = xmlOutputBufferCreateFile(f, handler);
2514     if (buf == NULL) return(-1);
2515     memset(&ctxt, 0, sizeof(ctxt));
2516     ctxt.buf = buf;
2517     ctxt.level = 0;
2518     ctxt.format = format ? 1 : 0;
2519     ctxt.encoding = (const xmlChar *) encoding;
2520     xmlSaveCtxtInit(&ctxt);
2521     ctxt.options |= XML_SAVE_AS_XML;
2522     xmlDocContentDumpOutput(&ctxt, cur);
2523 
2524     ret = xmlOutputBufferClose(buf);
2525     return(ret);
2526 }
2527 
2528 /**
2529  * xmlDocDump:
2530  * @f:  the FILE*
2531  * @cur:  the document
2532  *
2533  * Dump an XML document to an open FILE.
2534  *
2535  * returns: the number of bytes written or -1 in case of failure.
2536  */
2537 int
xmlDocDump(FILE * f,xmlDocPtr cur)2538 xmlDocDump(FILE *f, xmlDocPtr cur) {
2539     return(xmlDocFormatDump (f, cur, 0));
2540 }
2541 
2542 /**
2543  * xmlSaveFileTo:
2544  * @buf:  an output I/O buffer
2545  * @cur:  the document
2546  * @encoding:  the encoding if any assuming the I/O layer handles the transcoding
2547  *
2548  * Dump an XML document to an I/O buffer.
2549  * Warning ! This call xmlOutputBufferClose() on buf which is not available
2550  * after this call.
2551  *
2552  * returns: the number of bytes written or -1 in case of failure.
2553  */
2554 int
xmlSaveFileTo(xmlOutputBufferPtr buf,xmlDocPtr cur,const char * encoding)2555 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2556     xmlSaveCtxt ctxt;
2557     int ret;
2558 
2559     if (buf == NULL) return(-1);
2560     if (cur == NULL) {
2561         xmlOutputBufferClose(buf);
2562 	return(-1);
2563     }
2564     memset(&ctxt, 0, sizeof(ctxt));
2565     ctxt.buf = buf;
2566     ctxt.level = 0;
2567     ctxt.format = 0;
2568     ctxt.encoding = (const xmlChar *) encoding;
2569     xmlSaveCtxtInit(&ctxt);
2570     ctxt.options |= XML_SAVE_AS_XML;
2571     xmlDocContentDumpOutput(&ctxt, cur);
2572     ret = xmlOutputBufferClose(buf);
2573     return(ret);
2574 }
2575 
2576 /**
2577  * xmlSaveFormatFileTo:
2578  * @buf:  an output I/O buffer
2579  * @cur:  the document
2580  * @encoding:  the encoding if any assuming the I/O layer handles the transcoding
2581  * @format: should formatting spaces been added
2582  *
2583  * Dump an XML document to an I/O buffer.
2584  * Warning ! This call xmlOutputBufferClose() on buf which is not available
2585  * after this call.
2586  *
2587  * returns: the number of bytes written or -1 in case of failure.
2588  */
2589 int
xmlSaveFormatFileTo(xmlOutputBufferPtr buf,xmlDocPtr cur,const char * encoding,int format)2590 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2591                     const char *encoding, int format)
2592 {
2593     xmlSaveCtxt ctxt;
2594     int ret;
2595 
2596     if (buf == NULL) return(-1);
2597     if ((cur == NULL) ||
2598         ((cur->type != XML_DOCUMENT_NODE) &&
2599 	 (cur->type != XML_HTML_DOCUMENT_NODE))) {
2600         xmlOutputBufferClose(buf);
2601 	return(-1);
2602     }
2603     memset(&ctxt, 0, sizeof(ctxt));
2604     ctxt.buf = buf;
2605     ctxt.level = 0;
2606     ctxt.format = format ? 1 : 0;
2607     ctxt.encoding = (const xmlChar *) encoding;
2608     xmlSaveCtxtInit(&ctxt);
2609     ctxt.options |= XML_SAVE_AS_XML;
2610     xmlDocContentDumpOutput(&ctxt, cur);
2611     ret = xmlOutputBufferClose(buf);
2612     return (ret);
2613 }
2614 
2615 /**
2616  * xmlSaveFormatFileEnc:
2617  * @filename:  the filename or URL to output
2618  * @cur:  the document being saved
2619  * @encoding:  the name of the encoding to use or NULL.
2620  * @format:  should formatting spaces be added.
2621  *
2622  * Dump an XML document to a file or an URL.
2623  *
2624  * Returns the number of bytes written or -1 in case of error.
2625  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2626  * or xmlKeepBlanksDefault(0) was called
2627  */
2628 int
xmlSaveFormatFileEnc(const char * filename,xmlDocPtr cur,const char * encoding,int format)2629 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2630 			const char * encoding, int format ) {
2631     xmlSaveCtxt ctxt;
2632     xmlOutputBufferPtr buf;
2633     xmlCharEncodingHandlerPtr handler = NULL;
2634     int ret;
2635 
2636     if (cur == NULL)
2637 	return(-1);
2638 
2639     if (encoding == NULL)
2640 	encoding = (const char *) cur->encoding;
2641 
2642     if (encoding != NULL) {
2643 
2644 	    handler = xmlFindCharEncodingHandler(encoding);
2645 	    if (handler == NULL)
2646 		return(-1);
2647     }
2648 
2649 #ifdef LIBXML_ZLIB_ENABLED
2650     if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2651 #endif
2652     /*
2653      * save the content to a temp buffer.
2654      */
2655     buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2656     if (buf == NULL) return(-1);
2657     memset(&ctxt, 0, sizeof(ctxt));
2658     ctxt.buf = buf;
2659     ctxt.level = 0;
2660     ctxt.format = format ? 1 : 0;
2661     ctxt.encoding = (const xmlChar *) encoding;
2662     xmlSaveCtxtInit(&ctxt);
2663     ctxt.options |= XML_SAVE_AS_XML;
2664 
2665     xmlDocContentDumpOutput(&ctxt, cur);
2666 
2667     ret = xmlOutputBufferClose(buf);
2668     return(ret);
2669 }
2670 
2671 
2672 /**
2673  * xmlSaveFileEnc:
2674  * @filename:  the filename (or URL)
2675  * @cur:  the document
2676  * @encoding:  the name of an encoding (or NULL)
2677  *
2678  * Dump an XML document, converting it to the given encoding
2679  *
2680  * returns: the number of bytes written or -1 in case of failure.
2681  */
2682 int
xmlSaveFileEnc(const char * filename,xmlDocPtr cur,const char * encoding)2683 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2684     return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2685 }
2686 
2687 /**
2688  * xmlSaveFormatFile:
2689  * @filename:  the filename (or URL)
2690  * @cur:  the document
2691  * @format:  should formatting spaces been added
2692  *
2693  * Dump an XML document to a file. Will use compression if
2694  * compiled in and enabled. If @filename is "-" the stdout file is
2695  * used. If @format is set then the document will be indented on output.
2696  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2697  * or xmlKeepBlanksDefault(0) was called
2698  *
2699  * returns: the number of bytes written or -1 in case of failure.
2700  */
2701 int
xmlSaveFormatFile(const char * filename,xmlDocPtr cur,int format)2702 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2703     return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2704 }
2705 
2706 /**
2707  * xmlSaveFile:
2708  * @filename:  the filename (or URL)
2709  * @cur:  the document
2710  *
2711  * Dump an XML document to a file. Will use compression if
2712  * compiled in and enabled. If @filename is "-" the stdout file is
2713  * used.
2714  * returns: the number of bytes written or -1 in case of failure.
2715  */
2716 int
xmlSaveFile(const char * filename,xmlDocPtr cur)2717 xmlSaveFile(const char *filename, xmlDocPtr cur) {
2718     return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2719 }
2720 
2721 #endif /* LIBXML_OUTPUT_ENABLED */
2722 
2723 #define bottom_xmlsave
2724 #include "elfgcchack.h"
2725