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