blob: 15630dcbf688f5f5bb67aa3be4942f143a06e915 [file] [log] [blame]
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001/*
2 * xmlsave.c: Implemetation 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#ifdef LIBXML_HTML_ENABLED
18#include <libxml/HTMLtree.h>
19
20/************************************************************************
21 * *
22 * XHTML detection *
23 * *
24 ************************************************************************/
25#define XHTML_STRICT_PUBLIC_ID BAD_CAST \
26 "-//W3C//DTD XHTML 1.0 Strict//EN"
27#define XHTML_STRICT_SYSTEM_ID BAD_CAST \
28 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
29#define XHTML_FRAME_PUBLIC_ID BAD_CAST \
30 "-//W3C//DTD XHTML 1.0 Frameset//EN"
31#define XHTML_FRAME_SYSTEM_ID BAD_CAST \
32 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
33#define XHTML_TRANS_PUBLIC_ID BAD_CAST \
34 "-//W3C//DTD XHTML 1.0 Transitional//EN"
35#define XHTML_TRANS_SYSTEM_ID BAD_CAST \
36 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
37
38#define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
39/**
40 * xmlIsXHTML:
41 * @systemID: the system identifier
42 * @publicID: the public identifier
43 *
44 * Try to find if the document correspond to an XHTML DTD
45 *
46 * Returns 1 if true, 0 if not and -1 in case of error
47 */
48int
49xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
50 if ((systemID == NULL) && (publicID == NULL))
51 return(-1);
52 if (publicID != NULL) {
53 if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
54 if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
55 if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
56 }
57 if (systemID != NULL) {
58 if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
59 if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
60 if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
61 }
62 return(0);
63}
64#endif /* LIBXML_HTML_ENABLED */
65
66
67#ifdef LIBXML_OUTPUT_ENABLED
68
69#define TODO \
70 xmlGenericError(xmlGenericErrorContext, \
71 "Unimplemented block at %s:%d\n", \
72 __FILE__, __LINE__);
73
74struct _xmlSaveCtxt {
75 void *_private;
76 int type;
77 int fd;
78 const xmlChar *filename;
79 const xmlChar *encoding;
80 xmlCharEncodingHandlerPtr handler;
81 xmlOutputBufferPtr out;
82 int options;
83 int level;
84};
85
86/************************************************************************
87 * *
88 * Output error handlers *
89 * *
90 ************************************************************************/
91/**
92 * xmlSaveErrMemory:
93 * @extra: extra informations
94 *
95 * Handle an out of memory condition
96 */
97static void
98xmlSaveErrMemory(const char *extra)
99{
100 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
101}
102
103/**
104 * xmlSaveErr:
105 * @code: the error number
106 * @node: the location of the error.
107 * @extra: extra informations
108 *
109 * Handle an out of memory condition
110 */
111static void
112xmlSaveErr(int code, xmlNodePtr node, const char *extra)
113{
114 const char *msg = NULL;
115
116 switch(code) {
117 case XML_SAVE_NOT_UTF8:
118 msg = "string is not in UTF-8";
119 break;
120 case XML_SAVE_CHAR_INVALID:
121 msg = "invalid character value";
122 break;
123 case XML_SAVE_UNKNOWN_ENCODING:
124 msg = "unknown encoding %s";
125 break;
126 case XML_SAVE_NO_DOCTYPE:
127 msg = "document has no DOCTYPE";
128 break;
129 default:
130 msg = "unexpected error number";
131 }
132 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
133}
134
135/************************************************************************
136 * *
137 * Allocation and deallocation *
138 * *
139 ************************************************************************/
140
141/**
142 * xmlFreeSaveCtxt:
143 *
144 * Free a saving context, destroying the ouptut in any remaining buffer
145 */
146static void
147xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
148{
149 if (ctxt == NULL) return;
150 if (ctxt->encoding != NULL)
151 xmlFree((char *) ctxt->encoding);
152 xmlFree(ctxt);
153}
154
155/**
156 * xmlNewSaveCtxt:
157 *
158 * Create a new saving context
159 *
160 * Returns the new structure or NULL in case of error
161 */
162static xmlSaveCtxtPtr
163xmlNewSaveCtxt(const char *encoding, int options)
164{
165 xmlSaveCtxtPtr ret;
166
167 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
168 if (ret == NULL) {
169 xmlSaveErrMemory("creating saving context");
170 return ( NULL );
171 }
172 memset(ret, 0, sizeof(xmlSaveCtxt));
173 ret->options = options;
174 if (encoding != NULL) {
175 ret->handler = xmlFindCharEncodingHandler(encoding);
176 if (ret->handler == NULL) {
177 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
178 xmlFreeSaveCtxt(ret);
179 return(NULL);
180 }
181 ret->encoding = xmlStrdup((const xmlChar *)encoding);
182 }
183 return(ret);
184}
185
186/************************************************************************
187 * *
188 * Dumping XML tree content to a simple buffer *
189 * *
190 ************************************************************************/
191/**
192 * xmlAttrSerializeContent:
193 * @buf: the XML buffer output
194 * @doc: the document
195 * @attr: the attribute pointer
196 *
197 * Serialize the attribute in the buffer
198 */
199static void
200xmlAttrSerializeContent(xmlBufferPtr buf, xmlDocPtr doc, xmlAttrPtr attr)
201{
202 xmlNodePtr children;
203
204 children = attr->children;
205 while (children != NULL) {
206 switch (children->type) {
207 case XML_TEXT_NODE:
208 xmlAttrSerializeTxtContent(buf, doc, attr, children->content);
209 break;
210 case XML_ENTITY_REF_NODE:
211 xmlBufferAdd(buf, BAD_CAST "&", 1);
212 xmlBufferAdd(buf, children->name,
213 xmlStrlen(children->name));
214 xmlBufferAdd(buf, BAD_CAST ";", 1);
215 break;
216 default:
217 /* should not happen unless we have a badly built tree */
218 break;
219 }
220 children = children->next;
221 }
222}
223
224/************************************************************************
225 * *
226 * Dumping XML tree content to an I/O output buffer *
227 * *
228 ************************************************************************/
229
230#ifdef LIBXML_HTML_ENABLED
231static void
232xhtmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
233 int level, int format, const char *encoding);
234#endif
235static void
236xmlNodeListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
237 int level, int format, const char *encoding);
238static void
239xmlNodeDumpOutputInternal(xmlOutputBufferPtr buf, xmlDocPtr doc,
240 xmlNodePtr cur, int level, int format, const char *encoding);
241
242void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
243
244/**
245 * xmlNsDumpOutput:
246 * @buf: the XML buffer output
247 * @cur: a namespace
248 *
249 * Dump a local Namespace definition.
250 * Should be called in the context of attributes dumps.
251 */
252static void
253xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
254 if (cur == NULL) {
255#ifdef DEBUG_TREE
256 xmlGenericError(xmlGenericErrorContext,
257 "xmlNsDumpOutput : Ns == NULL\n");
258#endif
259 return;
260 }
261 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
262 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
263 return;
264
265 /* Within the context of an element attributes */
266 if (cur->prefix != NULL) {
267 xmlOutputBufferWriteString(buf, " xmlns:");
268 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
269 } else
270 xmlOutputBufferWriteString(buf, " xmlns");
271 xmlOutputBufferWriteString(buf, "=");
272 xmlBufferWriteQuotedString(buf->buffer, cur->href);
273 }
274}
275
276/**
277 * xmlNsListDumpOutput:
278 * @buf: the XML buffer output
279 * @cur: the first namespace
280 *
281 * Dump a list of local Namespace definitions.
282 * Should be called in the context of attributes dumps.
283 */
284void
285xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
286 while (cur != NULL) {
287 xmlNsDumpOutput(buf, cur);
288 cur = cur->next;
289 }
290}
291
292/**
293 * xmlDtdDumpOutput:
294 * @buf: the XML buffer output
295 * @doc: the document
296 * @encoding: an optional encoding string
297 *
298 * Dump the XML document DTD, if any.
299 */
300static void
301xmlDtdDumpOutput(xmlOutputBufferPtr buf, xmlDtdPtr dtd, const char *encoding) {
302 if (dtd == NULL) {
303#ifdef DEBUG_TREE
304 xmlGenericError(xmlGenericErrorContext,
305 "xmlDtdDumpOutput : no internal subset\n");
306#endif
307 return;
308 }
309 xmlOutputBufferWriteString(buf, "<!DOCTYPE ");
310 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
311 if (dtd->ExternalID != NULL) {
312 xmlOutputBufferWriteString(buf, " PUBLIC ");
313 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
314 xmlOutputBufferWriteString(buf, " ");
315 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
316 } else if (dtd->SystemID != NULL) {
317 xmlOutputBufferWriteString(buf, " SYSTEM ");
318 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
319 }
320 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
321 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
322 (dtd->pentities == NULL)) {
323 xmlOutputBufferWriteString(buf, ">");
324 return;
325 }
326 xmlOutputBufferWriteString(buf, " [\n");
327 xmlNodeListDumpOutput(buf, dtd->doc, dtd->children, -1, 0, encoding);
328 xmlOutputBufferWriteString(buf, "]>");
329}
330
331/**
332 * xmlAttrDumpOutput:
333 * @buf: the XML buffer output
334 * @doc: the document
335 * @cur: the attribute pointer
336 * @encoding: an optional encoding string
337 *
338 * Dump an XML attribute
339 */
340static void
341xmlAttrDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlAttrPtr cur,
342 const char *encoding ATTRIBUTE_UNUSED) {
343 if (cur == NULL) {
344#ifdef DEBUG_TREE
345 xmlGenericError(xmlGenericErrorContext,
346 "xmlAttrDumpOutput : property == NULL\n");
347#endif
348 return;
349 }
350 xmlOutputBufferWriteString(buf, " ");
351 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
352 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
353 xmlOutputBufferWriteString(buf, ":");
354 }
355 xmlOutputBufferWriteString(buf, (const char *)cur->name);
356 xmlOutputBufferWriteString(buf, "=\"");
357 xmlAttrSerializeContent(buf->buffer, doc, cur);
358 xmlOutputBufferWriteString(buf, "\"");
359}
360
361/**
362 * xmlAttrListDumpOutput:
363 * @buf: the XML buffer output
364 * @doc: the document
365 * @cur: the first attribute pointer
366 * @encoding: an optional encoding string
367 *
368 * Dump a list of XML attributes
369 */
370static void
371xmlAttrListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
372 xmlAttrPtr cur, const char *encoding) {
373 if (cur == NULL) {
374#ifdef DEBUG_TREE
375 xmlGenericError(xmlGenericErrorContext,
376 "xmlAttrListDumpOutput : property == NULL\n");
377#endif
378 return;
379 }
380 while (cur != NULL) {
381 xmlAttrDumpOutput(buf, doc, cur, encoding);
382 cur = cur->next;
383 }
384}
385
386
387
388/**
389 * xmlNodeListDumpOutput:
390 * @buf: the XML buffer output
391 * @doc: the document
392 * @cur: the first node
393 * @level: the imbrication level for indenting
394 * @format: is formatting allowed
395 * @encoding: an optional encoding string
396 *
397 * Dump an XML node list, recursive behaviour, children are printed too.
398 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
399 * or xmlKeepBlanksDefault(0) was called
400 */
401static void
402xmlNodeListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
403 xmlNodePtr cur, int level, int format, const char *encoding) {
404 int i;
405
406 if (cur == NULL) {
407#ifdef DEBUG_TREE
408 xmlGenericError(xmlGenericErrorContext,
409 "xmlNodeListDumpOutput : node == NULL\n");
410#endif
411 return;
412 }
413 while (cur != NULL) {
414 if ((format) && (xmlIndentTreeOutput) &&
415 (cur->type == XML_ELEMENT_NODE))
416 for (i = 0;i < level;i++)
417 xmlOutputBufferWriteString(buf, xmlTreeIndentString);
418 xmlNodeDumpOutputInternal(buf, doc, cur, level, format, encoding);
419 if (format) {
420 xmlOutputBufferWriteString(buf, "\n");
421 }
422 cur = cur->next;
423 }
424}
425
426/**
427 * xmlNodeDumpOutputInternal:
428 * @buf: the XML buffer output
429 * @doc: the document
430 * @cur: the current node
431 * @level: the imbrication level for indenting
432 * @format: is formatting allowed
433 * @encoding: an optional encoding string
434 *
435 * Dump an XML node, recursive behaviour, children are printed too.
436 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
437 * or xmlKeepBlanksDefault(0) was called
438 */
439static void
440xmlNodeDumpOutputInternal(xmlOutputBufferPtr buf, xmlDocPtr doc,
441 xmlNodePtr cur, int level, int format, const char *encoding) {
442 int i;
443 xmlNodePtr tmp;
444 xmlChar *start, *end;
445
446 if (cur == NULL) {
447#ifdef DEBUG_TREE
448 xmlGenericError(xmlGenericErrorContext,
449 "xmlNodeDumpOutput : node == NULL\n");
450#endif
451 return;
452 }
453 if (cur->type == XML_XINCLUDE_START)
454 return;
455 if (cur->type == XML_XINCLUDE_END)
456 return;
457 if (cur->type == XML_DTD_NODE) {
458 xmlDtdDumpOutput(buf, (xmlDtdPtr) cur, encoding);
459 return;
460 }
461 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
462 xmlNodeListDumpOutput(buf, doc, cur->children, level, format, encoding);
463 return;
464 }
465 if (cur->type == XML_ELEMENT_DECL) {
466 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
467 return;
468 }
469 if (cur->type == XML_ATTRIBUTE_DECL) {
470 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
471 return;
472 }
473 if (cur->type == XML_ENTITY_DECL) {
474 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
475 return;
476 }
477 if (cur->type == XML_TEXT_NODE) {
478 if (cur->content != NULL) {
479 if ((cur->name == xmlStringText) ||
480 (cur->name != xmlStringTextNoenc)) {
481 xmlChar *buffer;
482
483 if (encoding == NULL)
484 buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
485 else
486 buffer = xmlEncodeSpecialChars(doc, cur->content);
487 if (buffer != NULL) {
488 xmlOutputBufferWriteString(buf, (const char *)buffer);
489 xmlFree(buffer);
490 }
491 } else {
492 /*
493 * Disable escaping, needed for XSLT
494 */
495 xmlOutputBufferWriteString(buf, (const char *) cur->content);
496 }
497 }
498
499 return;
500 }
501 if (cur->type == XML_PI_NODE) {
502 if (cur->content != NULL) {
503 xmlOutputBufferWriteString(buf, "<?");
504 xmlOutputBufferWriteString(buf, (const char *)cur->name);
505 if (cur->content != NULL) {
506 xmlOutputBufferWriteString(buf, " ");
507 xmlOutputBufferWriteString(buf, (const char *)cur->content);
508 }
509 xmlOutputBufferWriteString(buf, "?>");
510 } else {
511 xmlOutputBufferWriteString(buf, "<?");
512 xmlOutputBufferWriteString(buf, (const char *)cur->name);
513 xmlOutputBufferWriteString(buf, "?>");
514 }
515 return;
516 }
517 if (cur->type == XML_COMMENT_NODE) {
518 if (cur->content != NULL) {
519 xmlOutputBufferWriteString(buf, "<!--");
520 xmlOutputBufferWriteString(buf, (const char *)cur->content);
521 xmlOutputBufferWriteString(buf, "-->");
522 }
523 return;
524 }
525 if (cur->type == XML_ENTITY_REF_NODE) {
526 xmlOutputBufferWriteString(buf, "&");
527 xmlOutputBufferWriteString(buf, (const char *)cur->name);
528 xmlOutputBufferWriteString(buf, ";");
529 return;
530 }
531 if (cur->type == XML_CDATA_SECTION_NODE) {
532 start = end = cur->content;
533 while (*end != '\0') {
534 if ((*end == ']') && (*(end + 1) == ']') && (*(end + 2) == '>')) {
535 end = end + 2;
536 xmlOutputBufferWriteString(buf, "<![CDATA[");
537 xmlOutputBufferWrite(buf, end - start, (const char *)start);
538 xmlOutputBufferWriteString(buf, "]]>");
539 start = end;
540 }
541 end++;
542 }
543 if (start != end) {
544 xmlOutputBufferWriteString(buf, "<![CDATA[");
545 xmlOutputBufferWriteString(buf, (const char *)start);
546 xmlOutputBufferWriteString(buf, "]]>");
547 }
548 return;
549 }
550 if (cur->type == XML_ATTRIBUTE_NODE) {
551 xmlAttrDumpOutput(buf,doc, (xmlAttrPtr) cur,encoding);
552 return;
553 }
554 if (cur->type == XML_NAMESPACE_DECL) {
555 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
556 return;
557 }
558
559 if (format == 1) {
560 tmp = cur->children;
561 while (tmp != NULL) {
562 if ((tmp->type == XML_TEXT_NODE) ||
563 (tmp->type == XML_CDATA_SECTION_NODE) ||
564 (tmp->type == XML_ENTITY_REF_NODE)) {
565 format = 0;
566 break;
567 }
568 tmp = tmp->next;
569 }
570 }
571 xmlOutputBufferWriteString(buf, "<");
572 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
573 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
574 xmlOutputBufferWriteString(buf, ":");
575 }
576
577 xmlOutputBufferWriteString(buf, (const char *)cur->name);
578 if (cur->nsDef)
579 xmlNsListDumpOutput(buf, cur->nsDef);
580 if (cur->properties != NULL)
581 xmlAttrListDumpOutput(buf, doc, cur->properties, encoding);
582
583 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
584 (cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
585 xmlOutputBufferWriteString(buf, "/>");
586 return;
587 }
588 xmlOutputBufferWriteString(buf, ">");
589 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
590 xmlChar *buffer;
591
592 if (encoding == NULL)
593 buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
594 else
595 buffer = xmlEncodeSpecialChars(doc, cur->content);
596 if (buffer != NULL) {
597 xmlOutputBufferWriteString(buf, (const char *)buffer);
598 xmlFree(buffer);
599 }
600 }
601 if (cur->children != NULL) {
602 if (format) xmlOutputBufferWriteString(buf, "\n");
603 xmlNodeListDumpOutput(buf, doc, cur->children,
604 (level >= 0?level+1:-1), format, encoding);
605 if ((xmlIndentTreeOutput) && (format))
606 for (i = 0;i < level;i++)
607 xmlOutputBufferWriteString(buf, xmlTreeIndentString);
608 }
609 xmlOutputBufferWriteString(buf, "</");
610 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
611 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
612 xmlOutputBufferWriteString(buf, ":");
613 }
614
615 xmlOutputBufferWriteString(buf, (const char *)cur->name);
616 xmlOutputBufferWriteString(buf, ">");
617}
618
619/**
620 * xmlDocContentDumpOutput:
621 * @buf: the XML buffer output
622 * @cur: the document
623 * @encoding: an optional encoding string
624 * @format: should formatting spaces been added
625 *
626 * Dump an XML document.
627 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
628 * or xmlKeepBlanksDefault(0) was called
629 */
630static void
631xmlDocContentDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr cur,
632 const char *encoding, int format) {
633#ifdef LIBXML_HTML_ENABLED
634 xmlDtdPtr dtd;
635 int is_xhtml = 0;
636#endif
637 const xmlChar *oldenc = cur->encoding;
638
639 xmlInitParser();
640
641 if (encoding != NULL)
642 cur->encoding = BAD_CAST encoding;
643
644 xmlOutputBufferWriteString(buf, "<?xml version=");
645 if (cur->version != NULL)
646 xmlBufferWriteQuotedString(buf->buffer, cur->version);
647 else
648 xmlOutputBufferWriteString(buf, "\"1.0\"");
649 if (encoding == NULL) {
650 if (cur->encoding != NULL)
651 encoding = (const char *) cur->encoding;
652 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
653 encoding = xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
654 }
655 if (encoding != NULL) {
656 xmlOutputBufferWriteString(buf, " encoding=");
657 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
658 }
659 switch (cur->standalone) {
660 case 0:
661 xmlOutputBufferWriteString(buf, " standalone=\"no\"");
662 break;
663 case 1:
664 xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
665 break;
666 }
667 xmlOutputBufferWriteString(buf, "?>\n");
668
669#ifdef LIBXML_HTML_ENABLED
670 dtd = xmlGetIntSubset(cur);
671 if (dtd != NULL) {
672 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
673 if (is_xhtml < 0) is_xhtml = 0;
674 }
675 if (is_xhtml) {
676 if (encoding != NULL)
677 htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
678 else
679 htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
680 }
681#endif
682 if (cur->children != NULL) {
683 xmlNodePtr child = cur->children;
684
685 while (child != NULL) {
686#ifdef LIBXML_HTML_ENABLED
687 if (is_xhtml)
688 xhtmlNodeDumpOutput(buf, cur, child, 0, format, encoding);
689 else
690#endif
691 xmlNodeDumpOutputInternal(buf, cur, child, 0, format, encoding);
692 xmlOutputBufferWriteString(buf, "\n");
693 child = child->next;
694 }
695 }
696 if (encoding != NULL)
697 cur->encoding = oldenc;
698}
699
700#ifdef LIBXML_HTML_ENABLED
701/************************************************************************
702 * *
703 * Functions specific to XHTML serialization *
704 * *
705 ************************************************************************/
706
707/**
708 * xhtmlIsEmpty:
709 * @node: the node
710 *
711 * Check if a node is an empty xhtml node
712 *
713 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
714 */
715static int
716xhtmlIsEmpty(xmlNodePtr node) {
717 if (node == NULL)
718 return(-1);
719 if (node->type != XML_ELEMENT_NODE)
720 return(0);
721 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
722 return(0);
723 if (node->children != NULL)
724 return(0);
725 switch (node->name[0]) {
726 case 'a':
727 if (xmlStrEqual(node->name, BAD_CAST "area"))
728 return(1);
729 return(0);
730 case 'b':
731 if (xmlStrEqual(node->name, BAD_CAST "br"))
732 return(1);
733 if (xmlStrEqual(node->name, BAD_CAST "base"))
734 return(1);
735 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
736 return(1);
737 return(0);
738 case 'c':
739 if (xmlStrEqual(node->name, BAD_CAST "col"))
740 return(1);
741 return(0);
742 case 'f':
743 if (xmlStrEqual(node->name, BAD_CAST "frame"))
744 return(1);
745 return(0);
746 case 'h':
747 if (xmlStrEqual(node->name, BAD_CAST "hr"))
748 return(1);
749 return(0);
750 case 'i':
751 if (xmlStrEqual(node->name, BAD_CAST "img"))
752 return(1);
753 if (xmlStrEqual(node->name, BAD_CAST "input"))
754 return(1);
755 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
756 return(1);
757 return(0);
758 case 'l':
759 if (xmlStrEqual(node->name, BAD_CAST "link"))
760 return(1);
761 return(0);
762 case 'm':
763 if (xmlStrEqual(node->name, BAD_CAST "meta"))
764 return(1);
765 return(0);
766 case 'p':
767 if (xmlStrEqual(node->name, BAD_CAST "param"))
768 return(1);
769 return(0);
770 }
771 return(0);
772}
773
774/**
775 * xhtmlAttrListDumpOutput:
776 * @buf: the XML buffer output
777 * @doc: the document
778 * @cur: the first attribute pointer
779 * @encoding: an optional encoding string
780 *
781 * Dump a list of XML attributes
782 */
783static void
784xhtmlAttrListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
785 xmlAttrPtr cur, const char *encoding) {
786 xmlAttrPtr xml_lang = NULL;
787 xmlAttrPtr lang = NULL;
788 xmlAttrPtr name = NULL;
789 xmlAttrPtr id = NULL;
790 xmlNodePtr parent;
791
792 if (cur == NULL) {
793#ifdef DEBUG_TREE
794 xmlGenericError(xmlGenericErrorContext,
795 "xmlAttrListDumpOutput : property == NULL\n");
796#endif
797 return;
798 }
799 parent = cur->parent;
800 while (cur != NULL) {
801 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
802 id = cur;
803 else
804 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
805 name = cur;
806 else
807 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
808 lang = cur;
809 else
810 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
811 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
812 xml_lang = cur;
813 else if ((cur->ns == NULL) &&
814 ((cur->children == NULL) ||
815 (cur->children->content == NULL) ||
816 (cur->children->content[0] == 0)) &&
817 (htmlIsBooleanAttr(cur->name))) {
818 if (cur->children != NULL)
819 xmlFreeNode(cur->children);
820 cur->children = xmlNewText(cur->name);
821 if (cur->children != NULL)
822 cur->children->parent = (xmlNodePtr) cur;
823 }
824 xmlAttrDumpOutput(buf, doc, cur, encoding);
825 cur = cur->next;
826 }
827 /*
828 * C.8
829 */
830 if ((name != NULL) && (id == NULL)) {
831 if ((parent != NULL) && (parent->name != NULL) &&
832 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
833 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
834 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
835 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
836 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
837 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
838 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
839 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
840 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
841 xmlOutputBufferWriteString(buf, " id=\"");
842 xmlAttrSerializeContent(buf->buffer, doc, name);
843 xmlOutputBufferWriteString(buf, "\"");
844 }
845 }
846 /*
847 * C.7.
848 */
849 if ((lang != NULL) && (xml_lang == NULL)) {
850 xmlOutputBufferWriteString(buf, " xml:lang=\"");
851 xmlAttrSerializeContent(buf->buffer, doc, lang);
852 xmlOutputBufferWriteString(buf, "\"");
853 } else
854 if ((xml_lang != NULL) && (lang == NULL)) {
855 xmlOutputBufferWriteString(buf, " lang=\"");
856 xmlAttrSerializeContent(buf->buffer, doc, xml_lang);
857 xmlOutputBufferWriteString(buf, "\"");
858 }
859}
860
861/**
862 * xhtmlNodeListDumpOutput:
863 * @buf: the XML buffer output
864 * @doc: the XHTML document
865 * @cur: the first node
866 * @level: the imbrication level for indenting
867 * @format: is formatting allowed
868 * @encoding: an optional encoding string
869 *
870 * Dump an XML node list, recursive behaviour, children are printed too.
871 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
872 * or xmlKeepBlanksDefault(0) was called
873 */
874static void
875xhtmlNodeListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
876 xmlNodePtr cur, int level, int format, const char *encoding) {
877 int i;
878
879 if (cur == NULL) {
880#ifdef DEBUG_TREE
881 xmlGenericError(xmlGenericErrorContext,
882 "xhtmlNodeListDumpOutput : node == NULL\n");
883#endif
884 return;
885 }
886 while (cur != NULL) {
887 if ((format) && (xmlIndentTreeOutput) &&
888 (cur->type == XML_ELEMENT_NODE))
889 for (i = 0;i < level;i++)
890 xmlOutputBufferWriteString(buf, xmlTreeIndentString);
891 xhtmlNodeDumpOutput(buf, doc, cur, level, format, encoding);
892 if (format) {
893 xmlOutputBufferWriteString(buf, "\n");
894 }
895 cur = cur->next;
896 }
897}
898
899/**
900 * xhtmlNodeDumpOutput:
901 * @buf: the XML buffer output
902 * @doc: the XHTML document
903 * @cur: the current node
904 * @level: the imbrication level for indenting
905 * @format: is formatting allowed
906 * @encoding: an optional encoding string
907 *
908 * Dump an XHTML node, recursive behaviour, children are printed too.
909 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
910 * or xmlKeepBlanksDefault(0) was called
911 */
912static void
913xhtmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
914 int level, int format, const char *encoding) {
915 int i;
916 xmlNodePtr tmp;
917 xmlChar *start, *end;
918
919 if (cur == NULL) {
920#ifdef DEBUG_TREE
921 xmlGenericError(xmlGenericErrorContext,
922 "xmlNodeDumpOutput : node == NULL\n");
923#endif
924 return;
925 }
926 if (cur->type == XML_XINCLUDE_START)
927 return;
928 if (cur->type == XML_XINCLUDE_END)
929 return;
930 if (cur->type == XML_DTD_NODE) {
931 xmlDtdDumpOutput(buf, (xmlDtdPtr) cur, encoding);
932 return;
933 }
934 if (cur->type == XML_ELEMENT_DECL) {
935 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
936 return;
937 }
938 if (cur->type == XML_ATTRIBUTE_DECL) {
939 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
940 return;
941 }
942 if (cur->type == XML_ENTITY_DECL) {
943 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
944 return;
945 }
946 if (cur->type == XML_TEXT_NODE) {
947 if (cur->content != NULL) {
948 if ((cur->name == xmlStringText) ||
949 (cur->name != xmlStringTextNoenc)) {
950 xmlChar *buffer;
951
952 if (encoding == NULL)
953 buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
954 else
955 buffer = xmlEncodeSpecialChars(doc, cur->content);
956 if (buffer != NULL) {
957 xmlOutputBufferWriteString(buf, (const char *)buffer);
958 xmlFree(buffer);
959 }
960 } else {
961 /*
962 * Disable escaping, needed for XSLT
963 */
964 xmlOutputBufferWriteString(buf, (const char *) cur->content);
965 }
966 }
967
968 return;
969 }
970 if (cur->type == XML_PI_NODE) {
971 if (cur->content != NULL) {
972 xmlOutputBufferWriteString(buf, "<?");
973 xmlOutputBufferWriteString(buf, (const char *)cur->name);
974 if (cur->content != NULL) {
975 xmlOutputBufferWriteString(buf, " ");
976 xmlOutputBufferWriteString(buf, (const char *)cur->content);
977 }
978 xmlOutputBufferWriteString(buf, "?>");
979 } else {
980 xmlOutputBufferWriteString(buf, "<?");
981 xmlOutputBufferWriteString(buf, (const char *)cur->name);
982 xmlOutputBufferWriteString(buf, "?>");
983 }
984 return;
985 }
986 if (cur->type == XML_COMMENT_NODE) {
987 if (cur->content != NULL) {
988 xmlOutputBufferWriteString(buf, "<!--");
989 xmlOutputBufferWriteString(buf, (const char *)cur->content);
990 xmlOutputBufferWriteString(buf, "-->");
991 }
992 return;
993 }
994 if (cur->type == XML_ENTITY_REF_NODE) {
995 xmlOutputBufferWriteString(buf, "&");
996 xmlOutputBufferWriteString(buf, (const char *)cur->name);
997 xmlOutputBufferWriteString(buf, ";");
998 return;
999 }
1000 if (cur->type == XML_CDATA_SECTION_NODE) {
1001 start = end = cur->content;
1002 while (*end != '\0') {
1003 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1004 end = end + 2;
1005 xmlOutputBufferWriteString(buf, "<![CDATA[");
1006 xmlOutputBufferWrite(buf, end - start, (const char *)start);
1007 xmlOutputBufferWriteString(buf, "]]>");
1008 start = end;
1009 }
1010 end++;
1011 }
1012 if (start != end) {
1013 xmlOutputBufferWriteString(buf, "<![CDATA[");
1014 xmlOutputBufferWriteString(buf, (const char *)start);
1015 xmlOutputBufferWriteString(buf, "]]>");
1016 }
1017 return;
1018 }
1019
1020 if (format == 1) {
1021 tmp = cur->children;
1022 while (tmp != NULL) {
1023 if ((tmp->type == XML_TEXT_NODE) ||
1024 (tmp->type == XML_ENTITY_REF_NODE)) {
1025 format = 0;
1026 break;
1027 }
1028 tmp = tmp->next;
1029 }
1030 }
1031 xmlOutputBufferWriteString(buf, "<");
1032 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1033 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1034 xmlOutputBufferWriteString(buf, ":");
1035 }
1036
1037 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1038 if (cur->nsDef)
1039 xmlNsListDumpOutput(buf, cur->nsDef);
1040 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1041 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1042 /*
1043 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1044 */
1045 xmlOutputBufferWriteString(buf,
1046 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1047 }
1048 if (cur->properties != NULL)
1049 xhtmlAttrListDumpOutput(buf, doc, cur->properties, encoding);
1050
1051 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1052 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1053 (xhtmlIsEmpty(cur) == 1)) {
1054 /*
1055 * C.2. Empty Elements
1056 */
1057 xmlOutputBufferWriteString(buf, " />");
1058 } else {
1059 /*
1060 * C.3. Element Minimization and Empty Element Content
1061 */
1062 xmlOutputBufferWriteString(buf, "></");
1063 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1064 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1065 xmlOutputBufferWriteString(buf, ":");
1066 }
1067 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1068 xmlOutputBufferWriteString(buf, ">");
1069 }
1070 return;
1071 }
1072 xmlOutputBufferWriteString(buf, ">");
1073 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
1074 xmlChar *buffer;
1075
1076 if (encoding == NULL)
1077 buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
1078 else
1079 buffer = xmlEncodeSpecialChars(doc, cur->content);
1080 if (buffer != NULL) {
1081 xmlOutputBufferWriteString(buf, (const char *)buffer);
1082 xmlFree(buffer);
1083 }
1084 }
1085
1086 /*
1087 * 4.8. Script and Style elements
1088 */
1089 if ((cur->type == XML_ELEMENT_NODE) &&
1090 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1091 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1092 ((cur->ns == NULL) ||
1093 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1094 xmlNodePtr child = cur->children;
1095
1096 while (child != NULL) {
1097 if ((child->type == XML_TEXT_NODE) ||
1098 (child->type == XML_CDATA_SECTION_NODE)) {
1099 /*
1100 * Apparently CDATA escaping for style just break on IE,
1101 * mozilla and galeon, so ...
1102 */
1103 if (xmlStrEqual(cur->name, BAD_CAST "style") &&
1104 (xmlStrchr(child->content, '<') == NULL) &&
1105 (xmlStrchr(child->content, '>') == NULL) &&
1106 (xmlStrchr(child->content, '&') == NULL)) {
1107 xhtmlNodeDumpOutput(buf, doc, child, 0, 0, encoding);
1108 } else {
1109 start = end = child->content;
1110 while (*end != '\0') {
1111 if (*end == ']' &&
1112 *(end + 1) == ']' &&
1113 *(end + 2) == '>') {
1114 end = end + 2;
1115 xmlOutputBufferWriteString(buf, "<![CDATA[");
1116 xmlOutputBufferWrite(buf, end - start,
1117 (const char *)start);
1118 xmlOutputBufferWriteString(buf, "]]>");
1119 start = end;
1120 }
1121 end++;
1122 }
1123 if (start != end) {
1124 xmlOutputBufferWriteString(buf, "<![CDATA[");
1125 xmlOutputBufferWriteString(buf, (const char *)start);
1126 xmlOutputBufferWriteString(buf, "]]>");
1127 }
1128 }
1129 } else {
1130 xhtmlNodeDumpOutput(buf, doc, child, 0, 0, encoding);
1131 }
1132 child = child->next;
1133 }
1134 } else if (cur->children != NULL) {
1135 if (format) xmlOutputBufferWriteString(buf, "\n");
1136 xhtmlNodeListDumpOutput(buf, doc, cur->children,
1137 (level >= 0?level+1:-1), format, encoding);
1138 if ((xmlIndentTreeOutput) && (format))
1139 for (i = 0;i < level;i++)
1140 xmlOutputBufferWriteString(buf, xmlTreeIndentString);
1141 }
1142 xmlOutputBufferWriteString(buf, "</");
1143 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1144 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1145 xmlOutputBufferWriteString(buf, ":");
1146 }
1147
1148 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1149 xmlOutputBufferWriteString(buf, ">");
1150}
1151#endif
1152
1153/************************************************************************
1154 * *
1155 * Public entry points *
1156 * *
1157 ************************************************************************/
1158
1159/**
1160 * xmlSaveToFd:
1161 * @fd: a file descriptor number
1162 * @encoding: the encoding name to use or NULL
1163 * @options: a set of xmlSaveOptions
1164 *
1165 * Create a document saving context serializing to a file descriptor
1166 * with the encoding and the options given.
1167 *
1168 * Returns a new serialization context or NULL in case of error.
1169 */
1170xmlSaveCtxtPtr
1171xmlSaveToFd(int fd, const char *encoding, int options)
1172{
1173 xmlSaveCtxtPtr ret;
1174
1175 ret = xmlNewSaveCtxt(encoding, options);
1176 if (ret == NULL) return(NULL);
1177 ret->out = xmlOutputBufferCreateFd(fd, ret->handler);
1178 if (ret->out == NULL) {
1179 xmlFreeSaveCtxt(ret);
1180 return(NULL);
1181 }
1182 return(ret);
1183}
1184
1185/**
1186 * xmlSaveToFilename:
1187 * @filename: a file name or an URL
1188 * @encoding: the encoding name to use or NULL
1189 * @options: a set of xmlSaveOptions
1190 *
1191 * Create a document saving context serializing to a filename or possibly
1192 * to an URL (but this is less reliable) with the encoding and the options
1193 * given.
1194 *
1195 * Returns a new serialization context or NULL in case of error.
1196 */
1197xmlSaveCtxtPtr
1198xmlSaveToFilename(const char *filename, const char *encoding, int options)
1199{
1200 xmlSaveCtxtPtr ret;
1201 int compression = 0; /* TODO handle compression option */
1202
1203 ret = xmlNewSaveCtxt(encoding, options);
1204 if (ret == NULL) return(NULL);
1205 ret->out = xmlOutputBufferCreateFilename(filename, ret->handler,
1206 compression);
1207 if (ret->out == NULL) {
1208 xmlFreeSaveCtxt(ret);
1209 return(NULL);
1210 }
1211 return(ret);
1212}
1213
1214#if 0
1215/**
1216 * xmlSaveToBuffer:
1217 * @buffer: a buffer
1218 * @encoding: the encoding name to use or NULL
1219 * @options: a set of xmlSaveOptions
1220 *
1221 * Create a document saving context serializing to a buffer
1222 * with the encoding and the options given
1223 *
1224 * Returns a new serialization context or NULL in case of error.
1225 */
1226xmlSaveCtxtPtr
1227xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1228{
1229 TODO
1230 return(NULL);
1231}
1232#endif
1233
1234/**
1235 * xmlSaveToIO:
1236 * @iowrite: an I/O write function
1237 * @ioclose: an I/O close function
1238 * @ioctx: an I/O handler
1239 * @encoding: the encoding name to use or NULL
1240 * @options: a set of xmlSaveOptions
1241 *
1242 * Create a document saving context serializing to a file descriptor
1243 * with the encoding and the options given
1244 *
1245 * Returns a new serialization context or NULL in case of error.
1246 */
1247xmlSaveCtxtPtr
1248xmlSaveToIO(xmlOutputWriteCallback iowrite,
1249 xmlOutputCloseCallback ioclose,
1250 void *ioctx, const char *encoding, int options)
1251{
1252 xmlSaveCtxtPtr ret;
1253
1254 ret = xmlNewSaveCtxt(encoding, options);
1255 if (ret == NULL) return(NULL);
1256 ret->out = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1257 if (ret->out == NULL) {
1258 xmlFreeSaveCtxt(ret);
1259 return(NULL);
1260 }
1261 return(ret);
1262}
1263
1264/**
1265 * xmlSaveDoc:
1266 * @ctxt: a document saving context
1267 * @doc: a document
1268 *
1269 * Save a full document to a saving context
1270 *
1271 * Returns the number of byte written or -1 in case of error
1272 */
1273long
1274xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1275{
1276}
1277
1278/**
1279 * xmlSaveTree:
1280 * @ctxt: a document saving context
1281 * @node: a document
1282 *
1283 * Save a subtree starting at the node parameter to a saving context
1284 *
1285 * Returns the number of byte written or -1 in case of error
1286 */
1287long
1288xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1289{
1290}
1291
1292/**
1293 * xmlSaveFlush:
1294 * @ctxt: a document saving context
1295 *
1296 * Flush a document saving context, i.e. make sure that all bytes have
1297 * been output.
1298 *
1299 * Returns the number of byte written or -1 in case of error.
1300 */
1301int
1302xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1303{
1304 if (ctxt == NULL) return(-1);
1305 if (ctxt->out == NULL) return(-1);
1306 return(xmlOutputBufferFlush(ctxt->out));
1307}
1308
1309/**
1310 * xmlSaveClose:
1311 * @ctxt: a document saving context
1312 *
1313 * Close a document saving context, i.e. make sure that all bytes have
1314 * been output and free the associated data.
1315 *
1316 * Returns the number of byte written or -1 in case of error.
1317 */
1318int
1319xmlSaveClose(xmlSaveCtxtPtr ctxt)
1320{
1321 int ret;
1322
1323 if (ctxt == NULL) return(-1);
1324 ret = xmlSaveFlush(ctxt);
1325 xmlFreeSaveCtxt(ctxt);
1326 return(ret);
1327}
1328
1329/************************************************************************
1330 * *
1331 * Public entry points based on buffers *
1332 * *
1333 ************************************************************************/
1334/**
1335 * xmlAttrSerializeTxtContent:
1336 * @buf: the XML buffer output
1337 * @doc: the document
1338 * @attr: the attribute node
1339 * @string: the text content
1340 *
1341 * Serialize text attribute values to an xml simple buffer
1342 */
1343void
1344xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
1345 xmlAttrPtr attr, const xmlChar *string) {
1346 xmlChar *base, *cur;
1347
1348 if (string == NULL) return;
1349 base = cur = (xmlChar *)string;
1350 while (*cur != 0) {
1351 if (*cur == '\n') {
1352 if (base != cur)
1353 xmlBufferAdd(buf, base, cur - base);
1354 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1355 cur++;
1356 base = cur;
1357 } else if (*cur == '\r') {
1358 if (base != cur)
1359 xmlBufferAdd(buf, base, cur - base);
1360 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1361 cur++;
1362 base = cur;
1363 } else if (*cur == '\t') {
1364 if (base != cur)
1365 xmlBufferAdd(buf, base, cur - base);
1366 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1367 cur++;
1368 base = cur;
1369 } else if (*cur == '"') {
1370 if (base != cur)
1371 xmlBufferAdd(buf, base, cur - base);
1372 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1373 cur++;
1374 base = cur;
1375 } else if (*cur == '<') {
1376 if (base != cur)
1377 xmlBufferAdd(buf, base, cur - base);
1378 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1379 cur++;
1380 base = cur;
1381 } else if (*cur == '>') {
1382 if (base != cur)
1383 xmlBufferAdd(buf, base, cur - base);
1384 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1385 cur++;
1386 base = cur;
1387 } else if (*cur == '&') {
1388 if (base != cur)
1389 xmlBufferAdd(buf, base, cur - base);
1390 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1391 cur++;
1392 base = cur;
1393 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1394 (doc->encoding == NULL))) {
1395 /*
1396 * We assume we have UTF-8 content.
1397 */
1398 char tmp[10];
1399 int val = 0, l = 1;
1400
1401 if (base != cur)
1402 xmlBufferAdd(buf, base, cur - base);
1403 if (*cur < 0xC0) {
1404 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1405 if (doc != NULL)
1406 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1407 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1408 tmp[sizeof(tmp) - 1] = 0;
1409 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1410 cur++;
1411 base = cur;
1412 continue;
1413 } else if (*cur < 0xE0) {
1414 val = (cur[0]) & 0x1F;
1415 val <<= 6;
1416 val |= (cur[1]) & 0x3F;
1417 l = 2;
1418 } else if (*cur < 0xF0) {
1419 val = (cur[0]) & 0x0F;
1420 val <<= 6;
1421 val |= (cur[1]) & 0x3F;
1422 val <<= 6;
1423 val |= (cur[2]) & 0x3F;
1424 l = 3;
1425 } else if (*cur < 0xF8) {
1426 val = (cur[0]) & 0x07;
1427 val <<= 6;
1428 val |= (cur[1]) & 0x3F;
1429 val <<= 6;
1430 val |= (cur[2]) & 0x3F;
1431 val <<= 6;
1432 val |= (cur[3]) & 0x3F;
1433 l = 4;
1434 }
1435 if ((l == 1) || (!IS_CHAR(val))) {
1436 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1437 if (doc != NULL)
1438 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1439 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1440 tmp[sizeof(tmp) - 1] = 0;
1441 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1442 cur++;
1443 base = cur;
1444 continue;
1445 }
1446 /*
1447 * We could do multiple things here. Just save
1448 * as a char ref
1449 */
1450 snprintf(tmp, sizeof(tmp), "&#x%X;", val);
1451 tmp[sizeof(tmp) - 1] = 0;
1452 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1453 cur += l;
1454 base = cur;
1455 } else {
1456 cur++;
1457 }
1458 }
1459 if (base != cur)
1460 xmlBufferAdd(buf, base, cur - base);
1461}
1462
1463/**
1464 * xmlNodeDump:
1465 * @buf: the XML buffer output
1466 * @doc: the document
1467 * @cur: the current node
1468 * @level: the imbrication level for indenting
1469 * @format: is formatting allowed
1470 *
1471 * Dump an XML node, recursive behaviour,children are printed too.
1472 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1473 * or xmlKeepBlanksDefault(0) was called
1474 *
1475 * Returns the number of bytes written to the buffer or -1 in case of error
1476 */
1477int
1478xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1479 int format)
1480{
1481 unsigned int use;
1482 int ret;
1483 xmlOutputBufferPtr outbuf;
1484
1485 xmlInitParser();
1486
1487 if (cur == NULL) {
1488#ifdef DEBUG_TREE
1489 xmlGenericError(xmlGenericErrorContext,
1490 "xmlNodeDump : node == NULL\n");
1491#endif
1492 return (-1);
1493 }
1494 if (buf == NULL) {
1495#ifdef DEBUG_TREE
1496 xmlGenericError(xmlGenericErrorContext,
1497 "xmlNodeDump : buf == NULL\n");
1498#endif
1499 return (-1);
1500 }
1501 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1502 if (outbuf == NULL) {
1503 xmlSaveErrMemory("creating buffer");
1504 return (-1);
1505 }
1506 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1507 outbuf->buffer = buf;
1508 outbuf->encoder = NULL;
1509 outbuf->writecallback = NULL;
1510 outbuf->closecallback = NULL;
1511 outbuf->context = NULL;
1512 outbuf->written = 0;
1513
1514 use = buf->use;
1515 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1516 xmlFree(outbuf);
1517 ret = buf->use - use;
1518 return (ret);
1519}
1520
1521/**
1522 * xmlElemDump:
1523 * @f: the FILE * for the output
1524 * @doc: the document
1525 * @cur: the current node
1526 *
1527 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1528 */
1529void
1530xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1531{
1532 xmlOutputBufferPtr outbuf;
1533
1534 xmlInitParser();
1535
1536 if (cur == NULL) {
1537#ifdef DEBUG_TREE
1538 xmlGenericError(xmlGenericErrorContext,
1539 "xmlElemDump : cur == NULL\n");
1540#endif
1541 return;
1542 }
1543#ifdef DEBUG_TREE
1544 if (doc == NULL) {
1545 xmlGenericError(xmlGenericErrorContext,
1546 "xmlElemDump : doc == NULL\n");
1547 }
1548#endif
1549
1550 outbuf = xmlOutputBufferCreateFile(f, NULL);
1551 if (outbuf == NULL)
1552 return;
1553 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1554#ifdef LIBXML_HTML_ENABLED
1555 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1556#else
1557 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1558#endif /* LIBXML_HTML_ENABLED */
1559 } else
1560 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1561 xmlOutputBufferClose(outbuf);
1562}
1563
1564/************************************************************************
1565 * *
1566 * Saving functions front-ends *
1567 * *
1568 ************************************************************************/
1569
1570/**
1571 * xmlNodeDumpOutput:
1572 * @buf: the XML buffer output
1573 * @doc: the document
1574 * @cur: the current node
1575 * @level: the imbrication level for indenting
1576 * @format: is formatting allowed
1577 * @encoding: an optional encoding string
1578 *
1579 * Dump an XML node, recursive behaviour, children are printed too.
1580 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1581 * or xmlKeepBlanksDefault(0) was called
1582 */
1583void
1584xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1585 int level, int format, const char *encoding)
1586{
1587#ifdef LIBXML_HTML_ENABLED
1588 xmlDtdPtr dtd;
1589 int is_xhtml = 0;
1590#endif
1591
1592 xmlInitParser();
1593
1594#ifdef LIBXML_HTML_ENABLED
1595 dtd = xmlGetIntSubset(doc);
1596 if (dtd != NULL) {
1597 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1598 if (is_xhtml < 0)
1599 is_xhtml = 0;
1600 if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
1601 (cur->type == XML_ELEMENT_NODE) &&
1602 (xmlStrEqual(cur->name, BAD_CAST "html"))) {
1603 if (encoding != NULL)
1604 htmlSetMetaEncoding((htmlDocPtr) doc,
1605 (const xmlChar *) encoding);
1606 else
1607 htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
1608 }
1609 }
1610
1611 if (is_xhtml)
1612 xhtmlNodeDumpOutput(buf, doc, cur, level, format, encoding);
1613 else
1614#endif
1615 xmlNodeDumpOutputInternal(buf, doc, cur, level, format, encoding);
1616}
1617
1618/**
1619 * xmlDocDumpFormatMemoryEnc:
1620 * @out_doc: Document to generate XML text from
1621 * @doc_txt_ptr: Memory pointer for allocated XML text
1622 * @doc_txt_len: Length of the generated XML text
1623 * @txt_encoding: Character encoding to use when generating XML text
1624 * @format: should formatting spaces been added
1625 *
1626 * Dump the current DOM tree into memory using the character encoding specified
1627 * by the caller. Note it is up to the caller of this function to free the
1628 * allocated memory with xmlFree().
1629 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1630 * or xmlKeepBlanksDefault(0) was called
1631 */
1632
1633void
1634xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1635 int * doc_txt_len, const char * txt_encoding,
1636 int format) {
1637 int dummy = 0;
1638 xmlOutputBufferPtr out_buff = NULL;
1639 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1640
1641 if (doc_txt_len == NULL) {
1642 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1643 }
1644
1645 if (doc_txt_ptr == NULL) {
1646 *doc_txt_len = 0;
1647 return;
1648 }
1649
1650 *doc_txt_ptr = NULL;
1651 *doc_txt_len = 0;
1652
1653 if (out_doc == NULL) {
1654 /* No document, no output */
1655 return;
1656 }
1657
1658 /*
1659 * Validate the encoding value, if provided.
1660 * This logic is copied from xmlSaveFileEnc.
1661 */
1662
1663 if (txt_encoding == NULL)
1664 txt_encoding = (const char *) out_doc->encoding;
1665 if (txt_encoding != NULL) {
1666 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1667 if ( conv_hdlr == NULL ) {
1668 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1669 txt_encoding);
1670 return;
1671 }
1672 }
1673
1674 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1675 xmlSaveErrMemory("creating buffer");
1676 return;
1677 }
1678
1679 xmlDocContentDumpOutput(out_buff, out_doc, txt_encoding, format);
1680 xmlOutputBufferFlush(out_buff);
1681 if (out_buff->conv != NULL) {
1682 *doc_txt_len = out_buff->conv->use;
1683 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1684 } else {
1685 *doc_txt_len = out_buff->buffer->use;
1686 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1687 }
1688 (void)xmlOutputBufferClose(out_buff);
1689
1690 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1691 *doc_txt_len = 0;
1692 xmlSaveErrMemory("creating output");
1693 }
1694
1695 return;
1696}
1697
1698/**
1699 * xmlDocDumpMemory:
1700 * @cur: the document
1701 * @mem: OUT: the memory pointer
1702 * @size: OUT: the memory length
1703 *
1704 * Dump an XML document in memory and return the #xmlChar * and it's size
1705 * in bytes. It's up to the caller to free the memory with xmlFree().
1706 * The resulting byte array is zero terminated, though the last 0 is not
1707 * included in the returned size.
1708 */
1709void
1710xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1711 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1712}
1713
1714/**
1715 * xmlDocDumpFormatMemory:
1716 * @cur: the document
1717 * @mem: OUT: the memory pointer
1718 * @size: OUT: the memory length
1719 * @format: should formatting spaces been added
1720 *
1721 *
1722 * Dump an XML document in memory and return the #xmlChar * and it's size.
1723 * It's up to the caller to free the memory with xmlFree().
1724 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1725 * or xmlKeepBlanksDefault(0) was called
1726 */
1727void
1728xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
1729 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
1730}
1731
1732/**
1733 * xmlDocDumpMemoryEnc:
1734 * @out_doc: Document to generate XML text from
1735 * @doc_txt_ptr: Memory pointer for allocated XML text
1736 * @doc_txt_len: Length of the generated XML text
1737 * @txt_encoding: Character encoding to use when generating XML text
1738 *
1739 * Dump the current DOM tree into memory using the character encoding specified
1740 * by the caller. Note it is up to the caller of this function to free the
1741 * allocated memory with xmlFree().
1742 */
1743
1744void
1745xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1746 int * doc_txt_len, const char * txt_encoding) {
1747 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
1748 txt_encoding, 0);
1749}
1750
1751/**
1752 * xmlDocFormatDump:
1753 * @f: the FILE*
1754 * @cur: the document
1755 * @format: should formatting spaces been added
1756 *
1757 * Dump an XML document to an open FILE.
1758 *
1759 * returns: the number of bytes written or -1 in case of failure.
1760 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1761 * or xmlKeepBlanksDefault(0) was called
1762 */
1763int
1764xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
1765 xmlOutputBufferPtr buf;
1766 const char * encoding;
1767 xmlCharEncodingHandlerPtr handler = NULL;
1768 int ret;
1769
1770 if (cur == NULL) {
1771#ifdef DEBUG_TREE
1772 xmlGenericError(xmlGenericErrorContext,
1773 "xmlDocDump : document == NULL\n");
1774#endif
1775 return(-1);
1776 }
1777 encoding = (const char *) cur->encoding;
1778
1779 if (encoding != NULL) {
1780 handler = xmlFindCharEncodingHandler(encoding);
1781 if (handler == NULL) {
1782 xmlFree((char *) cur->encoding);
1783 cur->encoding = NULL;
1784 }
1785 }
1786 buf = xmlOutputBufferCreateFile(f, handler);
1787 if (buf == NULL) return(-1);
1788 xmlDocContentDumpOutput(buf, cur, NULL, format);
1789
1790 ret = xmlOutputBufferClose(buf);
1791 return(ret);
1792}
1793
1794/**
1795 * xmlDocDump:
1796 * @f: the FILE*
1797 * @cur: the document
1798 *
1799 * Dump an XML document to an open FILE.
1800 *
1801 * returns: the number of bytes written or -1 in case of failure.
1802 */
1803int
1804xmlDocDump(FILE *f, xmlDocPtr cur) {
1805 return(xmlDocFormatDump (f, cur, 0));
1806}
1807
1808/**
1809 * xmlSaveFileTo:
1810 * @buf: an output I/O buffer
1811 * @cur: the document
1812 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
1813 *
1814 * Dump an XML document to an I/O buffer.
1815 *
1816 * returns: the number of bytes written or -1 in case of failure.
1817 */
1818int
1819xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
1820 int ret;
1821
1822 if (buf == NULL) return(0);
1823 xmlDocContentDumpOutput(buf, cur, encoding, 0);
1824 ret = xmlOutputBufferClose(buf);
1825 return(ret);
1826}
1827
1828/**
1829 * xmlSaveFormatFileTo:
1830 * @buf: an output I/O buffer
1831 * @cur: the document
1832 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
1833 * @format: should formatting spaces been added
1834 *
1835 * Dump an XML document to an I/O buffer.
1836 *
1837 * returns: the number of bytes written or -1 in case of failure.
1838 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1839 * or xmlKeepBlanksDefault(0) was called
1840 */
1841int
1842xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding, int format) {
1843 int ret;
1844
1845 if (buf == NULL) return(0);
1846 xmlDocContentDumpOutput(buf, cur, encoding, format);
1847 ret = xmlOutputBufferClose(buf);
1848 return(ret);
1849}
1850
1851/**
1852 * xmlSaveFormatFileEnc:
1853 * @filename: the filename or URL to output
1854 * @cur: the document being saved
1855 * @encoding: the name of the encoding to use or NULL.
1856 * @format: should formatting spaces be added.
1857 *
1858 * Dump an XML document to a file or an URL.
1859 *
1860 * Returns the number of bytes written or -1 in case of error.
1861 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1862 * or xmlKeepBlanksDefault(0) was called
1863 */
1864int
1865xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
1866 const char * encoding, int format ) {
1867 xmlOutputBufferPtr buf;
1868 xmlCharEncodingHandlerPtr handler = NULL;
1869 int ret;
1870
1871 if (cur == NULL)
1872 return(-1);
1873
1874 if (encoding == NULL)
1875 encoding = (const char *) cur->encoding;
1876
1877 if (encoding != NULL) {
1878
1879 handler = xmlFindCharEncodingHandler(encoding);
1880 if (handler == NULL)
1881 return(-1);
1882 }
1883
1884#ifdef HAVE_ZLIB_H
1885 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
1886#endif
1887 /*
1888 * save the content to a temp buffer.
1889 */
1890 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
1891 if (buf == NULL) return(-1);
1892
1893 xmlDocContentDumpOutput(buf, cur, encoding, format);
1894
1895 ret = xmlOutputBufferClose(buf);
1896 return(ret);
1897}
1898
1899
1900/**
1901 * xmlSaveFileEnc:
1902 * @filename: the filename (or URL)
1903 * @cur: the document
1904 * @encoding: the name of an encoding (or NULL)
1905 *
1906 * Dump an XML document, converting it to the given encoding
1907 *
1908 * returns: the number of bytes written or -1 in case of failure.
1909 */
1910int
1911xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
1912 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
1913}
1914
1915/**
1916 * xmlSaveFormatFile:
1917 * @filename: the filename (or URL)
1918 * @cur: the document
1919 * @format: should formatting spaces been added
1920 *
1921 * Dump an XML document to a file. Will use compression if
1922 * compiled in and enabled. If @filename is "-" the stdout file is
1923 * used. If @format is set then the document will be indented on output.
1924 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1925 * or xmlKeepBlanksDefault(0) was called
1926 *
1927 * returns: the number of bytes written or -1 in case of failure.
1928 */
1929int
1930xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
1931 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
1932}
1933
1934/**
1935 * xmlSaveFile:
1936 * @filename: the filename (or URL)
1937 * @cur: the document
1938 *
1939 * Dump an XML document to a file. Will use compression if
1940 * compiled in and enabled. If @filename is "-" the stdout file is
1941 * used.
1942 * returns: the number of bytes written or -1 in case of failure.
1943 */
1944int
1945xmlSaveFile(const char *filename, xmlDocPtr cur) {
1946 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
1947}
1948
1949#endif /* LIBXML_OUTPUT_ENABLED */
1950