VirtualBox

source: vbox/trunk/src/libs/libxml2-2.13.2/xmlsave.c@ 105681

最後變更 在這個檔案從105681是 105420,由 vboxsync 提交於 4 月 前

libxml2-2.12.6: Applied and adjusted our libxml2 changes to 2.12.6. bugref:10730

  • 屬性 svn:eol-style 設為 native
檔案大小: 86.5 KB
 
1/*
2 * xmlsave.c: Implementation of the document serializer
3 *
4 * See Copyright for the status of this software.
5 *
6 * [email protected]
7 */
8
9#define IN_LIBXML
10#include "libxml.h"
11
12#include <limits.h>
13#include <stdlib.h>
14#include <string.h>
15#include <libxml/xmlmemory.h>
16#include <libxml/parserInternals.h>
17#include <libxml/tree.h>
18#include <libxml/xmlsave.h>
19
20#define MAX_INDENT 60
21
22#include <libxml/HTMLtree.h>
23
24#include "private/buf.h"
25#include "private/enc.h"
26#include "private/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
34struct _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 */
63static void
64xmlSaveErrMemory(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 */
79static void
80xmlSaveErr(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 ************************************************************************/
128static unsigned char *
129xmlSerializeHexCharRef(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 */
184static int
185xmlEscapeEntities(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 */
280static void
281xmlSaveCtxtInit(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 */
311static void
312xmlFreeSaveCtxt(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 */
329static xmlSaveCtxtPtr
330xmlNewSaveCtxt(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 (res != XML_ERR_OK) {
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 */
387static void
388xmlAttrSerializeContent(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 */
419static void
420xmlBufDumpNotationDecl(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 */
446static void
447xmlBufDumpNotationDeclScan(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 */
459static void
460xmlBufDumpNotationTable(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 */
471static void
472xmlBufDumpElementOccur(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 */
495static void
496xmlBufDumpElementContent(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 */
569static void
570xmlBufDumpElementDecl(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 */
605static void
606xmlBufDumpEnumeration(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 */
625static void
626xmlBufDumpAttributeDecl(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 */
707static void
708xmlBufDumpEntityContent(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 */
746static void
747xmlBufDumpEntityDecl(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
796static 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 (res != XML_ERR_OK) {
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
823static 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
834static void
835xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
836#endif
837static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
838static 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 */
847static void
848xmlOutputBufferWriteWSNonSig(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 */
872static void
873xmlNsDumpOutput(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 */
905static void
906xmlNsListDumpOutputCtxt(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 */
921void
922xmlNsListDumpOutput(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 */
936static void
937xmlDtdDumpOutput(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 */
991static void
992xmlAttrDumpOutput(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 */
1031static int
1032htmlNodeDumpOutputInternal(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 */
1090static void
1091xmlNodeDumpOutputInternal(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 */
1358static int
1359xmlDocContentDumpOutput(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 */
1524static int
1525xhtmlIsEmpty(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 */
1589static void
1590xhtmlAttrListDumpOutput(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 */
1662static void
1663xhtmlNodeDumpOutput(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 */
2006xmlSaveCtxtPtr
2007xmlSaveToFd(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 */
2034xmlSaveCtxtPtr
2035xmlSaveToFilename(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
2064xmlSaveCtxtPtr
2065xmlSaveToBuffer(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 */
2093xmlSaveCtxtPtr
2094xmlSaveToIO(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 */
2122long
2123xmlSaveDoc(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 */
2144long
2145xmlSaveTree(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 */
2176int
2177xmlSaveNotationDecl(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 */
2193int
2194xmlSaveNotationTable(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 */
2210int
2211xmlSaveFlush(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 */
2227int
2228xmlSaveClose(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 */
2249int
2250xmlSaveFinish(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 */
2271int
2272xmlSaveSetEscape(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 */
2288int
2289xmlSaveSetAttrEscape(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 */
2310void
2311xmlBufAttrSerializeTxtContent(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 */
2407void
2408xmlAttrSerializeTxtContent(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 */
2440int
2441xmlNodeDump(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
2479size_t
2480xmlBufNodeDump(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 */
2530void
2531xmlElemDump(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 */
2572void
2573xmlNodeDumpOutput(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
2634void
2635xmlDocDumpFormatMemoryEnc(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 (res != XML_ERR_OK) {
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 */
2718void
2719xmlDocDumpMemory(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 */
2736void
2737xmlDocDumpFormatMemory(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
2753void
2754xmlDocDumpMemoryEnc(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 */
2772int
2773xmlDocFormatDump(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 int res;
2787
2788 res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler);
2789 if (res != XML_ERR_OK) {
2790 xmlFree((char *) cur->encoding);
2791 cur->encoding = NULL;
2792 encoding = NULL;
2793 }
2794 }
2795 buf = xmlOutputBufferCreateFile(f, handler);
2796 if (buf == NULL) return(-1);
2797 memset(&ctxt, 0, sizeof(ctxt));
2798 ctxt.buf = buf;
2799 ctxt.level = 0;
2800 ctxt.format = format ? 1 : 0;
2801 ctxt.encoding = (const xmlChar *) encoding;
2802 xmlSaveCtxtInit(&ctxt);
2803 ctxt.options |= XML_SAVE_AS_XML;
2804 xmlDocContentDumpOutput(&ctxt, cur);
2805
2806 ret = xmlOutputBufferClose(buf);
2807 return(ret);
2808}
2809
2810/**
2811 * xmlDocDump:
2812 * @f: the FILE*
2813 * @cur: the document
2814 *
2815 * Dump an XML document to an open FILE.
2816 *
2817 * returns: the number of bytes written or -1 in case of failure.
2818 */
2819int
2820xmlDocDump(FILE *f, xmlDocPtr cur) {
2821 return(xmlDocFormatDump (f, cur, 0));
2822}
2823
2824/**
2825 * xmlSaveFileTo:
2826 * @buf: an output I/O buffer
2827 * @cur: the document
2828 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2829 *
2830 * Dump an XML document to an I/O buffer.
2831 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2832 * after this call.
2833 *
2834 * returns: the number of bytes written or -1 in case of failure.
2835 */
2836int
2837xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2838 xmlSaveCtxt ctxt;
2839 int ret;
2840
2841 if (buf == NULL) return(-1);
2842 if (cur == NULL) {
2843 xmlOutputBufferClose(buf);
2844 return(-1);
2845 }
2846 memset(&ctxt, 0, sizeof(ctxt));
2847 ctxt.buf = buf;
2848 ctxt.level = 0;
2849 ctxt.format = 0;
2850 ctxt.encoding = (const xmlChar *) encoding;
2851 xmlSaveCtxtInit(&ctxt);
2852 ctxt.options |= XML_SAVE_AS_XML;
2853 xmlDocContentDumpOutput(&ctxt, cur);
2854 ret = xmlOutputBufferClose(buf);
2855 return(ret);
2856}
2857
2858/**
2859 * xmlSaveFormatFileTo:
2860 * @buf: an output I/O buffer
2861 * @cur: the document
2862 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2863 * @format: should formatting spaces been added
2864 *
2865 * Dump an XML document to an I/O buffer.
2866 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2867 * after this call.
2868 *
2869 * returns: the number of bytes written or -1 in case of failure.
2870 */
2871int
2872xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2873 const char *encoding, int format)
2874{
2875 xmlSaveCtxt ctxt;
2876 int ret;
2877
2878 if (buf == NULL) return(-1);
2879 if ((cur == NULL) ||
2880 ((cur->type != XML_DOCUMENT_NODE) &&
2881 (cur->type != XML_HTML_DOCUMENT_NODE))) {
2882 xmlOutputBufferClose(buf);
2883 return(-1);
2884 }
2885 memset(&ctxt, 0, sizeof(ctxt));
2886 ctxt.buf = buf;
2887 ctxt.level = 0;
2888 ctxt.format = format ? 1 : 0;
2889 ctxt.encoding = (const xmlChar *) encoding;
2890 xmlSaveCtxtInit(&ctxt);
2891 ctxt.options |= XML_SAVE_AS_XML;
2892 xmlDocContentDumpOutput(&ctxt, cur);
2893 ret = xmlOutputBufferClose(buf);
2894 return (ret);
2895}
2896
2897/**
2898 * xmlSaveFormatFileEnc:
2899 * @filename: the filename or URL to output
2900 * @cur: the document being saved
2901 * @encoding: the name of the encoding to use or NULL.
2902 * @format: should formatting spaces be added.
2903 *
2904 * Dump an XML document to a file or an URL.
2905 *
2906 * Returns the number of bytes written or -1 in case of error.
2907 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2908 * or xmlKeepBlanksDefault(0) was called
2909 */
2910int
2911xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2912 const char * encoding, int format ) {
2913 xmlSaveCtxt ctxt;
2914 xmlOutputBufferPtr buf;
2915 xmlCharEncodingHandlerPtr handler = NULL;
2916 int ret;
2917
2918 if (cur == NULL)
2919 return(-1);
2920
2921 if (encoding == NULL)
2922 encoding = (const char *) cur->encoding;
2923
2924 if (encoding != NULL) {
2925 int res;
2926
2927 res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler);
2928 if (res != XML_ERR_OK)
2929 return(-1);
2930 }
2931
2932#ifdef LIBXML_ZLIB_ENABLED
2933 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2934#endif
2935 /*
2936 * save the content to a temp buffer.
2937 */
2938 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2939 if (buf == NULL) return(-1);
2940 memset(&ctxt, 0, sizeof(ctxt));
2941 ctxt.buf = buf;
2942 ctxt.level = 0;
2943 ctxt.format = format ? 1 : 0;
2944 ctxt.encoding = (const xmlChar *) encoding;
2945 xmlSaveCtxtInit(&ctxt);
2946 ctxt.options |= XML_SAVE_AS_XML;
2947
2948 xmlDocContentDumpOutput(&ctxt, cur);
2949
2950 ret = xmlOutputBufferClose(buf);
2951 return(ret);
2952}
2953
2954
2955/**
2956 * xmlSaveFileEnc:
2957 * @filename: the filename (or URL)
2958 * @cur: the document
2959 * @encoding: the name of an encoding (or NULL)
2960 *
2961 * Dump an XML document, converting it to the given encoding
2962 *
2963 * returns: the number of bytes written or -1 in case of failure.
2964 */
2965int
2966xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2967 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2968}
2969
2970/**
2971 * xmlSaveFormatFile:
2972 * @filename: the filename (or URL)
2973 * @cur: the document
2974 * @format: should formatting spaces been added
2975 *
2976 * Dump an XML document to a file. Will use compression if
2977 * compiled in and enabled. If @filename is "-" the stdout file is
2978 * used. If @format is set then the document will be indented on output.
2979 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2980 * or xmlKeepBlanksDefault(0) was called
2981 *
2982 * returns: the number of bytes written or -1 in case of failure.
2983 */
2984int
2985xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2986 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2987}
2988
2989/**
2990 * xmlSaveFile:
2991 * @filename: the filename (or URL)
2992 * @cur: the document
2993 *
2994 * Dump an XML document to a file. Will use compression if
2995 * compiled in and enabled. If @filename is "-" the stdout file is
2996 * used.
2997 * returns: the number of bytes written or -1 in case of failure.
2998 */
2999int
3000xmlSaveFile(const char *filename, xmlDocPtr cur) {
3001 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
3002}
3003
3004#endif /* LIBXML_OUTPUT_ENABLED */
3005
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette