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