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