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