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