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