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