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