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