blob: bfaf07b73b7737d3fd84c5a28a1f8381fd863028 [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>
Daniel Veillard1a8741c2004-03-04 13:40:59 +000017
Daniel Veillard753086a2004-03-28 16:12:44 +000018#define MAX_INDENT 60
19
Daniel Veillard656ce942004-04-30 23:11:45 +000020#include <libxml/HTMLtree.h>
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}
Daniel Veillard1a8741c2004-03-04 13:40:59 +000066
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;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000081 xmlOutputBufferPtr buf;
82 xmlDocPtr doc;
Daniel Veillard1a8741c2004-03-04 13:40:59 +000083 int options;
84 int level;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000085 int format;
Daniel Veillard753086a2004-03-28 16:12:44 +000086 char indent[MAX_INDENT + 1];
87 int indent_nr;
88 int indent_size;
Daniel Veillard1a8741c2004-03-04 13:40:59 +000089};
90
91/************************************************************************
92 * *
93 * Output error handlers *
94 * *
95 ************************************************************************/
96/**
97 * xmlSaveErrMemory:
98 * @extra: extra informations
99 *
100 * Handle an out of memory condition
101 */
102static void
103xmlSaveErrMemory(const char *extra)
104{
105 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
106}
107
108/**
109 * xmlSaveErr:
110 * @code: the error number
111 * @node: the location of the error.
112 * @extra: extra informations
113 *
114 * Handle an out of memory condition
115 */
116static void
117xmlSaveErr(int code, xmlNodePtr node, const char *extra)
118{
119 const char *msg = NULL;
120
121 switch(code) {
122 case XML_SAVE_NOT_UTF8:
123 msg = "string is not in UTF-8";
124 break;
125 case XML_SAVE_CHAR_INVALID:
126 msg = "invalid character value";
127 break;
128 case XML_SAVE_UNKNOWN_ENCODING:
129 msg = "unknown encoding %s";
130 break;
131 case XML_SAVE_NO_DOCTYPE:
132 msg = "document has no DOCTYPE";
133 break;
134 default:
135 msg = "unexpected error number";
136 }
137 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
138}
139
140/************************************************************************
141 * *
142 * Allocation and deallocation *
143 * *
144 ************************************************************************/
Daniel Veillard753086a2004-03-28 16:12:44 +0000145/**
146 * xmlSaveCtxtInit:
147 * @ctxt: the saving context
148 *
149 * Initialize a saving context
150 */
151static void
152xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
153{
154 int i;
155
156 if (ctxt == NULL) return;
157 if (xmlTreeIndentString == NULL) {
158 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
159 } else {
160 ctxt->indent_size = xmlStrlen((const xmlChar *) xmlTreeIndentString);
161 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
162 for (i = 0;i < ctxt->indent_nr;i++)
163 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
164 ctxt->indent_size);
165 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
166 }
167}
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000168
169/**
170 * xmlFreeSaveCtxt:
171 *
172 * Free a saving context, destroying the ouptut in any remaining buffer
173 */
174static void
175xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
176{
177 if (ctxt == NULL) return;
178 if (ctxt->encoding != NULL)
179 xmlFree((char *) ctxt->encoding);
Daniel Veillarde2161a62004-04-29 17:14:25 +0000180 if (ctxt->buf != NULL)
181 xmlOutputBufferClose(ctxt->buf);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000182 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)) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000473
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000474 if (ctxt->encoding == NULL) {
475 xmlChar *buffer;
476
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000477 buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
478 cur->content);
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000479 if (buffer != NULL) {
480 xmlOutputBufferWriteString(buf, (const char *)buffer);
481 xmlFree(buffer);
482 }
483 } else {
Daniel Veillardee8960b2004-05-14 03:25:14 +0000484 xmlOutputBufferWriteEscape(buf, cur->content, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000485 }
486 } else {
487 /*
488 * Disable escaping, needed for XSLT
489 */
490 xmlOutputBufferWriteString(buf, (const char *) cur->content);
491 }
492 }
493
494 return;
495 }
496 if (cur->type == XML_PI_NODE) {
497 if (cur->content != NULL) {
498 xmlOutputBufferWriteString(buf, "<?");
499 xmlOutputBufferWriteString(buf, (const char *)cur->name);
500 if (cur->content != NULL) {
501 xmlOutputBufferWriteString(buf, " ");
502 xmlOutputBufferWriteString(buf, (const char *)cur->content);
503 }
504 xmlOutputBufferWriteString(buf, "?>");
505 } else {
506 xmlOutputBufferWriteString(buf, "<?");
507 xmlOutputBufferWriteString(buf, (const char *)cur->name);
508 xmlOutputBufferWriteString(buf, "?>");
509 }
510 return;
511 }
512 if (cur->type == XML_COMMENT_NODE) {
513 if (cur->content != NULL) {
514 xmlOutputBufferWriteString(buf, "<!--");
515 xmlOutputBufferWriteString(buf, (const char *)cur->content);
516 xmlOutputBufferWriteString(buf, "-->");
517 }
518 return;
519 }
520 if (cur->type == XML_ENTITY_REF_NODE) {
521 xmlOutputBufferWriteString(buf, "&");
522 xmlOutputBufferWriteString(buf, (const char *)cur->name);
523 xmlOutputBufferWriteString(buf, ";");
524 return;
525 }
526 if (cur->type == XML_CDATA_SECTION_NODE) {
527 start = end = cur->content;
528 while (*end != '\0') {
529 if ((*end == ']') && (*(end + 1) == ']') && (*(end + 2) == '>')) {
530 end = end + 2;
531 xmlOutputBufferWriteString(buf, "<![CDATA[");
532 xmlOutputBufferWrite(buf, end - start, (const char *)start);
533 xmlOutputBufferWriteString(buf, "]]>");
534 start = end;
535 }
536 end++;
537 }
538 if (start != end) {
539 xmlOutputBufferWriteString(buf, "<![CDATA[");
540 xmlOutputBufferWriteString(buf, (const char *)start);
541 xmlOutputBufferWriteString(buf, "]]>");
542 }
543 return;
544 }
545 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000546 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000547 return;
548 }
549 if (cur->type == XML_NAMESPACE_DECL) {
550 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
551 return;
552 }
553
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000554 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000555 if (format == 1) {
556 tmp = cur->children;
557 while (tmp != NULL) {
558 if ((tmp->type == XML_TEXT_NODE) ||
559 (tmp->type == XML_CDATA_SECTION_NODE) ||
560 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000561 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000562 break;
563 }
564 tmp = tmp->next;
565 }
566 }
567 xmlOutputBufferWriteString(buf, "<");
568 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
569 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
570 xmlOutputBufferWriteString(buf, ":");
571 }
572
573 xmlOutputBufferWriteString(buf, (const char *)cur->name);
574 if (cur->nsDef)
575 xmlNsListDumpOutput(buf, cur->nsDef);
576 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000577 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000578
579 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
580 (cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
581 xmlOutputBufferWriteString(buf, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000582 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000583 return;
584 }
585 xmlOutputBufferWriteString(buf, ">");
586 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000587 if (ctxt->encoding == NULL) {
588 xmlChar *buffer;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000589
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000590 buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
591 cur->content);
592 if (buffer != NULL) {
593 xmlOutputBufferWriteString(buf, (const char *)buffer);
594 xmlFree(buffer);
595 }
596 } else {
Daniel Veillardee8960b2004-05-14 03:25:14 +0000597 xmlOutputBufferWriteEscape(buf, cur->content, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000598 }
599 }
600 if (cur->children != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000601 if (ctxt->format) xmlOutputBufferWriteString(buf, "\n");
602 if (ctxt->level >= 0) ctxt->level++;
603 xmlNodeListDumpOutput(ctxt, cur->children);
604 if (ctxt->level > 0) ctxt->level--;
605 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000606 xmlOutputBufferWrite(buf, ctxt->indent_size *
607 (ctxt->level > ctxt->indent_nr ?
608 ctxt->indent_nr : ctxt->level),
609 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000610 }
611 xmlOutputBufferWriteString(buf, "</");
612 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
613 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
614 xmlOutputBufferWriteString(buf, ":");
615 }
616
617 xmlOutputBufferWriteString(buf, (const char *)cur->name);
618 xmlOutputBufferWriteString(buf, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000619 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000620}
621
622/**
623 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000624 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000625 *
626 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000627 */
628static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000629xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000630#ifdef LIBXML_HTML_ENABLED
631 xmlDtdPtr dtd;
632 int is_xhtml = 0;
633#endif
634 const xmlChar *oldenc = cur->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000635 const xmlChar *encoding = ctxt->encoding;
636 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000637
638 xmlInitParser();
639
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000640 if (ctxt->encoding != NULL)
641 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000642
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000643 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000644 xmlOutputBufferWriteString(buf, "<?xml version=");
645 if (cur->version != NULL)
646 xmlBufferWriteQuotedString(buf->buffer, cur->version);
647 else
648 xmlOutputBufferWriteString(buf, "\"1.0\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000649 if (ctxt->encoding == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000650 if (cur->encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000651 encoding = cur->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000652 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000653 encoding = (const xmlChar *)
654 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000655 }
656 if (encoding != NULL) {
657 xmlOutputBufferWriteString(buf, " encoding=");
658 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
659 }
660 switch (cur->standalone) {
661 case 0:
662 xmlOutputBufferWriteString(buf, " standalone=\"no\"");
663 break;
664 case 1:
665 xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
666 break;
667 }
668 xmlOutputBufferWriteString(buf, "?>\n");
669
670#ifdef LIBXML_HTML_ENABLED
671 dtd = xmlGetIntSubset(cur);
672 if (dtd != NULL) {
673 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
674 if (is_xhtml < 0) is_xhtml = 0;
675 }
676 if (is_xhtml) {
677 if (encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000678 htmlSetMetaEncoding(cur, (const xmlChar *) ctxt->encoding);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000679 else
680 htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
681 }
682#endif
683 if (cur->children != NULL) {
684 xmlNodePtr child = cur->children;
685
686 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000687 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000688#ifdef LIBXML_HTML_ENABLED
689 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000690 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000691 else
692#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000693 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000694 xmlOutputBufferWriteString(buf, "\n");
695 child = child->next;
696 }
697 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000698 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000699 cur->encoding = oldenc;
700}
701
702#ifdef LIBXML_HTML_ENABLED
703/************************************************************************
704 * *
705 * Functions specific to XHTML serialization *
706 * *
707 ************************************************************************/
708
709/**
710 * xhtmlIsEmpty:
711 * @node: the node
712 *
713 * Check if a node is an empty xhtml node
714 *
715 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
716 */
717static int
718xhtmlIsEmpty(xmlNodePtr node) {
719 if (node == NULL)
720 return(-1);
721 if (node->type != XML_ELEMENT_NODE)
722 return(0);
723 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
724 return(0);
725 if (node->children != NULL)
726 return(0);
727 switch (node->name[0]) {
728 case 'a':
729 if (xmlStrEqual(node->name, BAD_CAST "area"))
730 return(1);
731 return(0);
732 case 'b':
733 if (xmlStrEqual(node->name, BAD_CAST "br"))
734 return(1);
735 if (xmlStrEqual(node->name, BAD_CAST "base"))
736 return(1);
737 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
738 return(1);
739 return(0);
740 case 'c':
741 if (xmlStrEqual(node->name, BAD_CAST "col"))
742 return(1);
743 return(0);
744 case 'f':
745 if (xmlStrEqual(node->name, BAD_CAST "frame"))
746 return(1);
747 return(0);
748 case 'h':
749 if (xmlStrEqual(node->name, BAD_CAST "hr"))
750 return(1);
751 return(0);
752 case 'i':
753 if (xmlStrEqual(node->name, BAD_CAST "img"))
754 return(1);
755 if (xmlStrEqual(node->name, BAD_CAST "input"))
756 return(1);
757 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
758 return(1);
759 return(0);
760 case 'l':
761 if (xmlStrEqual(node->name, BAD_CAST "link"))
762 return(1);
763 return(0);
764 case 'm':
765 if (xmlStrEqual(node->name, BAD_CAST "meta"))
766 return(1);
767 return(0);
768 case 'p':
769 if (xmlStrEqual(node->name, BAD_CAST "param"))
770 return(1);
771 return(0);
772 }
773 return(0);
774}
775
776/**
777 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000778 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000779 *
780 * Dump a list of XML attributes
781 */
782static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000783xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000784 xmlAttrPtr xml_lang = NULL;
785 xmlAttrPtr lang = NULL;
786 xmlAttrPtr name = NULL;
787 xmlAttrPtr id = NULL;
788 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000789 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000790
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000791 if (cur == NULL) return;
792 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000793 parent = cur->parent;
794 while (cur != NULL) {
795 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
796 id = cur;
797 else
798 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
799 name = cur;
800 else
801 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
802 lang = cur;
803 else
804 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
805 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
806 xml_lang = cur;
807 else if ((cur->ns == NULL) &&
808 ((cur->children == NULL) ||
809 (cur->children->content == NULL) ||
810 (cur->children->content[0] == 0)) &&
811 (htmlIsBooleanAttr(cur->name))) {
812 if (cur->children != NULL)
813 xmlFreeNode(cur->children);
814 cur->children = xmlNewText(cur->name);
815 if (cur->children != NULL)
816 cur->children->parent = (xmlNodePtr) cur;
817 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000818 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000819 cur = cur->next;
820 }
821 /*
822 * C.8
823 */
824 if ((name != NULL) && (id == NULL)) {
825 if ((parent != NULL) && (parent->name != NULL) &&
826 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
827 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
828 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
829 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
830 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
831 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
832 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
833 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
834 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
835 xmlOutputBufferWriteString(buf, " id=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000836 xmlAttrSerializeContent(buf->buffer, ctxt->doc, name);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000837 xmlOutputBufferWriteString(buf, "\"");
838 }
839 }
840 /*
841 * C.7.
842 */
843 if ((lang != NULL) && (xml_lang == NULL)) {
844 xmlOutputBufferWriteString(buf, " xml:lang=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000845 xmlAttrSerializeContent(buf->buffer, ctxt->doc, lang);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000846 xmlOutputBufferWriteString(buf, "\"");
847 } else
848 if ((xml_lang != NULL) && (lang == NULL)) {
849 xmlOutputBufferWriteString(buf, " lang=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000850 xmlAttrSerializeContent(buf->buffer, ctxt->doc, xml_lang);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000851 xmlOutputBufferWriteString(buf, "\"");
852 }
853}
854
855/**
856 * xhtmlNodeListDumpOutput:
857 * @buf: the XML buffer output
858 * @doc: the XHTML document
859 * @cur: the first node
860 * @level: the imbrication level for indenting
861 * @format: is formatting allowed
862 * @encoding: an optional encoding string
863 *
864 * Dump an XML node list, recursive behaviour, children are printed too.
865 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
866 * or xmlKeepBlanksDefault(0) was called
867 */
868static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000869xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000870 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000871
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000872 if (cur == NULL) return;
873 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000874 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000875 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000876 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000877 xmlOutputBufferWrite(buf, ctxt->indent_size *
878 (ctxt->level > ctxt->indent_nr ?
879 ctxt->indent_nr : ctxt->level),
880 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000881 xhtmlNodeDumpOutput(ctxt, cur);
882 if (ctxt->format) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000883 xmlOutputBufferWriteString(buf, "\n");
884 }
885 cur = cur->next;
886 }
887}
888
889/**
890 * xhtmlNodeDumpOutput:
891 * @buf: the XML buffer output
892 * @doc: the XHTML document
893 * @cur: the current node
894 * @level: the imbrication level for indenting
895 * @format: is formatting allowed
896 * @encoding: an optional encoding string
897 *
898 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000899 */
900static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000901xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000902 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000903 xmlNodePtr tmp;
904 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000905 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000906
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000907 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000908 if (cur->type == XML_XINCLUDE_START)
909 return;
910 if (cur->type == XML_XINCLUDE_END)
911 return;
912 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000913 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000914 return;
915 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000916 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000917 if (cur->type == XML_ELEMENT_DECL) {
918 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
919 return;
920 }
921 if (cur->type == XML_ATTRIBUTE_DECL) {
922 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
923 return;
924 }
925 if (cur->type == XML_ENTITY_DECL) {
926 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
927 return;
928 }
929 if (cur->type == XML_TEXT_NODE) {
930 if (cur->content != NULL) {
931 if ((cur->name == xmlStringText) ||
932 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000933
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000934 if (ctxt->encoding == NULL) {
935 xmlChar *buffer;
936
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000937 buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
938 cur->content);
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000939 if (buffer != NULL) {
940 xmlOutputBufferWriteString(buf, (const char *)buffer);
941 xmlFree(buffer);
942 }
943 } else {
Daniel Veillardee8960b2004-05-14 03:25:14 +0000944 xmlOutputBufferWriteEscape(buf, cur->content, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000945 }
946 } else {
947 /*
948 * Disable escaping, needed for XSLT
949 */
950 xmlOutputBufferWriteString(buf, (const char *) cur->content);
951 }
952 }
953
954 return;
955 }
956 if (cur->type == XML_PI_NODE) {
957 if (cur->content != NULL) {
958 xmlOutputBufferWriteString(buf, "<?");
959 xmlOutputBufferWriteString(buf, (const char *)cur->name);
960 if (cur->content != NULL) {
961 xmlOutputBufferWriteString(buf, " ");
962 xmlOutputBufferWriteString(buf, (const char *)cur->content);
963 }
964 xmlOutputBufferWriteString(buf, "?>");
965 } else {
966 xmlOutputBufferWriteString(buf, "<?");
967 xmlOutputBufferWriteString(buf, (const char *)cur->name);
968 xmlOutputBufferWriteString(buf, "?>");
969 }
970 return;
971 }
972 if (cur->type == XML_COMMENT_NODE) {
973 if (cur->content != NULL) {
974 xmlOutputBufferWriteString(buf, "<!--");
975 xmlOutputBufferWriteString(buf, (const char *)cur->content);
976 xmlOutputBufferWriteString(buf, "-->");
977 }
978 return;
979 }
980 if (cur->type == XML_ENTITY_REF_NODE) {
981 xmlOutputBufferWriteString(buf, "&");
982 xmlOutputBufferWriteString(buf, (const char *)cur->name);
983 xmlOutputBufferWriteString(buf, ";");
984 return;
985 }
986 if (cur->type == XML_CDATA_SECTION_NODE) {
987 start = end = cur->content;
988 while (*end != '\0') {
989 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
990 end = end + 2;
991 xmlOutputBufferWriteString(buf, "<![CDATA[");
992 xmlOutputBufferWrite(buf, end - start, (const char *)start);
993 xmlOutputBufferWriteString(buf, "]]>");
994 start = end;
995 }
996 end++;
997 }
998 if (start != end) {
999 xmlOutputBufferWriteString(buf, "<![CDATA[");
1000 xmlOutputBufferWriteString(buf, (const char *)start);
1001 xmlOutputBufferWriteString(buf, "]]>");
1002 }
1003 return;
1004 }
1005
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001006 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001007 if (format == 1) {
1008 tmp = cur->children;
1009 while (tmp != NULL) {
1010 if ((tmp->type == XML_TEXT_NODE) ||
1011 (tmp->type == XML_ENTITY_REF_NODE)) {
1012 format = 0;
1013 break;
1014 }
1015 tmp = tmp->next;
1016 }
1017 }
1018 xmlOutputBufferWriteString(buf, "<");
1019 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1020 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1021 xmlOutputBufferWriteString(buf, ":");
1022 }
1023
1024 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1025 if (cur->nsDef)
1026 xmlNsListDumpOutput(buf, cur->nsDef);
1027 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1028 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1029 /*
1030 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1031 */
1032 xmlOutputBufferWriteString(buf,
1033 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1034 }
1035 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001036 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001037
1038 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1039 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1040 (xhtmlIsEmpty(cur) == 1)) {
1041 /*
1042 * C.2. Empty Elements
1043 */
1044 xmlOutputBufferWriteString(buf, " />");
1045 } else {
1046 /*
1047 * C.3. Element Minimization and Empty Element Content
1048 */
1049 xmlOutputBufferWriteString(buf, "></");
1050 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1051 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1052 xmlOutputBufferWriteString(buf, ":");
1053 }
1054 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1055 xmlOutputBufferWriteString(buf, ">");
1056 }
1057 return;
1058 }
1059 xmlOutputBufferWriteString(buf, ">");
1060 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard5d1a4d82004-05-13 14:31:25 +00001061 if (ctxt->encoding == NULL) {
1062 xmlChar *buffer;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001063
Daniel Veillard5d1a4d82004-05-13 14:31:25 +00001064 buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
1065 cur->content);
1066 if (buffer != NULL) {
1067 xmlOutputBufferWriteString(buf, (const char *)buffer);
1068 xmlFree(buffer);
1069 }
1070 } else {
Daniel Veillardee8960b2004-05-14 03:25:14 +00001071 xmlOutputBufferWriteEscape(buf, cur->content, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001072 }
1073 }
1074
1075 /*
1076 * 4.8. Script and Style elements
1077 */
1078 if ((cur->type == XML_ELEMENT_NODE) &&
1079 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1080 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1081 ((cur->ns == NULL) ||
1082 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1083 xmlNodePtr child = cur->children;
1084
1085 while (child != NULL) {
1086 if ((child->type == XML_TEXT_NODE) ||
1087 (child->type == XML_CDATA_SECTION_NODE)) {
1088 /*
1089 * Apparently CDATA escaping for style just break on IE,
1090 * mozilla and galeon, so ...
1091 */
1092 if (xmlStrEqual(cur->name, BAD_CAST "style") &&
1093 (xmlStrchr(child->content, '<') == NULL) &&
1094 (xmlStrchr(child->content, '>') == NULL) &&
1095 (xmlStrchr(child->content, '&') == NULL)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001096 int level = ctxt->level;
1097 int indent = ctxt->format;
1098
1099 ctxt->level = 0;
1100 ctxt->format = 0;
1101 xhtmlNodeDumpOutput(ctxt, child);
1102 ctxt->level = level;
1103 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001104 } else {
1105 start = end = child->content;
1106 while (*end != '\0') {
1107 if (*end == ']' &&
1108 *(end + 1) == ']' &&
1109 *(end + 2) == '>') {
1110 end = end + 2;
1111 xmlOutputBufferWriteString(buf, "<![CDATA[");
1112 xmlOutputBufferWrite(buf, end - start,
1113 (const char *)start);
1114 xmlOutputBufferWriteString(buf, "]]>");
1115 start = end;
1116 }
1117 end++;
1118 }
1119 if (start != end) {
1120 xmlOutputBufferWriteString(buf, "<![CDATA[");
1121 xmlOutputBufferWriteString(buf, (const char *)start);
1122 xmlOutputBufferWriteString(buf, "]]>");
1123 }
1124 }
1125 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001126 int level = ctxt->level;
1127 int indent = ctxt->format;
1128
1129 ctxt->level = 0;
1130 ctxt->format = 0;
1131 xhtmlNodeDumpOutput(ctxt, child);
1132 ctxt->level = level;
1133 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001134 }
1135 child = child->next;
1136 }
1137 } else if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001138 int indent = ctxt->format;
1139
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001140 if (format) xmlOutputBufferWriteString(buf, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001141 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001142 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001143 xhtmlNodeListDumpOutput(ctxt, cur->children);
1144 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001145 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001146 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001147 xmlOutputBufferWrite(buf, ctxt->indent_size *
1148 (ctxt->level > ctxt->indent_nr ?
1149 ctxt->indent_nr : ctxt->level),
1150 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001151 }
1152 xmlOutputBufferWriteString(buf, "</");
1153 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1154 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1155 xmlOutputBufferWriteString(buf, ":");
1156 }
1157
1158 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1159 xmlOutputBufferWriteString(buf, ">");
1160}
1161#endif
1162
1163/************************************************************************
1164 * *
1165 * Public entry points *
1166 * *
1167 ************************************************************************/
1168
1169/**
1170 * xmlSaveToFd:
1171 * @fd: a file descriptor number
1172 * @encoding: the encoding name to use or NULL
1173 * @options: a set of xmlSaveOptions
1174 *
1175 * Create a document saving context serializing to a file descriptor
1176 * with the encoding and the options given.
1177 *
1178 * Returns a new serialization context or NULL in case of error.
1179 */
1180xmlSaveCtxtPtr
1181xmlSaveToFd(int fd, const char *encoding, int options)
1182{
1183 xmlSaveCtxtPtr ret;
1184
1185 ret = xmlNewSaveCtxt(encoding, options);
1186 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001187 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1188 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001189 xmlFreeSaveCtxt(ret);
1190 return(NULL);
1191 }
1192 return(ret);
1193}
1194
1195/**
1196 * xmlSaveToFilename:
1197 * @filename: a file name or an URL
1198 * @encoding: the encoding name to use or NULL
1199 * @options: a set of xmlSaveOptions
1200 *
1201 * Create a document saving context serializing to a filename or possibly
1202 * to an URL (but this is less reliable) with the encoding and the options
1203 * given.
1204 *
1205 * Returns a new serialization context or NULL in case of error.
1206 */
1207xmlSaveCtxtPtr
1208xmlSaveToFilename(const char *filename, const char *encoding, int options)
1209{
1210 xmlSaveCtxtPtr ret;
1211 int compression = 0; /* TODO handle compression option */
1212
1213 ret = xmlNewSaveCtxt(encoding, options);
1214 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001215 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001216 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001217 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001218 xmlFreeSaveCtxt(ret);
1219 return(NULL);
1220 }
1221 return(ret);
1222}
1223
1224#if 0
1225/**
1226 * xmlSaveToBuffer:
1227 * @buffer: a buffer
1228 * @encoding: the encoding name to use or NULL
1229 * @options: a set of xmlSaveOptions
1230 *
1231 * Create a document saving context serializing to a buffer
1232 * with the encoding and the options given
1233 *
1234 * Returns a new serialization context or NULL in case of error.
1235 */
1236xmlSaveCtxtPtr
1237xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1238{
1239 TODO
1240 return(NULL);
1241}
1242#endif
1243
1244/**
1245 * xmlSaveToIO:
1246 * @iowrite: an I/O write function
1247 * @ioclose: an I/O close function
1248 * @ioctx: an I/O handler
1249 * @encoding: the encoding name to use or NULL
1250 * @options: a set of xmlSaveOptions
1251 *
1252 * Create a document saving context serializing to a file descriptor
1253 * with the encoding and the options given
1254 *
1255 * Returns a new serialization context or NULL in case of error.
1256 */
1257xmlSaveCtxtPtr
1258xmlSaveToIO(xmlOutputWriteCallback iowrite,
1259 xmlOutputCloseCallback ioclose,
1260 void *ioctx, const char *encoding, int options)
1261{
1262 xmlSaveCtxtPtr ret;
1263
1264 ret = xmlNewSaveCtxt(encoding, options);
1265 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001266 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1267 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001268 xmlFreeSaveCtxt(ret);
1269 return(NULL);
1270 }
1271 return(ret);
1272}
1273
1274/**
1275 * xmlSaveDoc:
1276 * @ctxt: a document saving context
1277 * @doc: a document
1278 *
1279 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001280 * TODO: The function is not fully implemented yet as it does not return the
1281 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001282 *
1283 * Returns the number of byte written or -1 in case of error
1284 */
1285long
1286xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1287{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001288 long ret = 0;
1289
1290 xmlDocContentDumpOutput(ctxt, doc);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001291 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001292}
1293
1294/**
1295 * xmlSaveTree:
1296 * @ctxt: a document saving context
1297 * @node: a document
1298 *
1299 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001300 * TODO: The function is not fully implemented yet as it does not return the
1301 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001302 *
1303 * Returns the number of byte written or -1 in case of error
1304 */
1305long
1306xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1307{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001308 long ret = 0;
1309
1310 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001311 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001312}
1313
1314/**
1315 * xmlSaveFlush:
1316 * @ctxt: a document saving context
1317 *
1318 * Flush a document saving context, i.e. make sure that all bytes have
1319 * been output.
1320 *
1321 * Returns the number of byte written or -1 in case of error.
1322 */
1323int
1324xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1325{
1326 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001327 if (ctxt->buf == NULL) return(-1);
1328 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001329}
1330
1331/**
1332 * xmlSaveClose:
1333 * @ctxt: a document saving context
1334 *
1335 * Close a document saving context, i.e. make sure that all bytes have
1336 * been output and free the associated data.
1337 *
1338 * Returns the number of byte written or -1 in case of error.
1339 */
1340int
1341xmlSaveClose(xmlSaveCtxtPtr ctxt)
1342{
1343 int ret;
1344
1345 if (ctxt == NULL) return(-1);
1346 ret = xmlSaveFlush(ctxt);
1347 xmlFreeSaveCtxt(ctxt);
1348 return(ret);
1349}
1350
1351/************************************************************************
1352 * *
1353 * Public entry points based on buffers *
1354 * *
1355 ************************************************************************/
1356/**
1357 * xmlAttrSerializeTxtContent:
1358 * @buf: the XML buffer output
1359 * @doc: the document
1360 * @attr: the attribute node
1361 * @string: the text content
1362 *
1363 * Serialize text attribute values to an xml simple buffer
1364 */
1365void
1366xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
1367 xmlAttrPtr attr, const xmlChar *string) {
1368 xmlChar *base, *cur;
1369
1370 if (string == NULL) return;
1371 base = cur = (xmlChar *)string;
1372 while (*cur != 0) {
1373 if (*cur == '\n') {
1374 if (base != cur)
1375 xmlBufferAdd(buf, base, cur - base);
1376 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1377 cur++;
1378 base = cur;
1379 } else if (*cur == '\r') {
1380 if (base != cur)
1381 xmlBufferAdd(buf, base, cur - base);
1382 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1383 cur++;
1384 base = cur;
1385 } else if (*cur == '\t') {
1386 if (base != cur)
1387 xmlBufferAdd(buf, base, cur - base);
1388 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1389 cur++;
1390 base = cur;
1391 } else if (*cur == '"') {
1392 if (base != cur)
1393 xmlBufferAdd(buf, base, cur - base);
1394 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1395 cur++;
1396 base = cur;
1397 } else if (*cur == '<') {
1398 if (base != cur)
1399 xmlBufferAdd(buf, base, cur - base);
1400 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1401 cur++;
1402 base = cur;
1403 } else if (*cur == '>') {
1404 if (base != cur)
1405 xmlBufferAdd(buf, base, cur - base);
1406 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1407 cur++;
1408 base = cur;
1409 } else if (*cur == '&') {
1410 if (base != cur)
1411 xmlBufferAdd(buf, base, cur - base);
1412 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1413 cur++;
1414 base = cur;
1415 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1416 (doc->encoding == NULL))) {
1417 /*
1418 * We assume we have UTF-8 content.
1419 */
1420 char tmp[10];
1421 int val = 0, l = 1;
1422
1423 if (base != cur)
1424 xmlBufferAdd(buf, base, cur - base);
1425 if (*cur < 0xC0) {
1426 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1427 if (doc != NULL)
1428 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1429 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1430 tmp[sizeof(tmp) - 1] = 0;
1431 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1432 cur++;
1433 base = cur;
1434 continue;
1435 } else if (*cur < 0xE0) {
1436 val = (cur[0]) & 0x1F;
1437 val <<= 6;
1438 val |= (cur[1]) & 0x3F;
1439 l = 2;
1440 } else if (*cur < 0xF0) {
1441 val = (cur[0]) & 0x0F;
1442 val <<= 6;
1443 val |= (cur[1]) & 0x3F;
1444 val <<= 6;
1445 val |= (cur[2]) & 0x3F;
1446 l = 3;
1447 } else if (*cur < 0xF8) {
1448 val = (cur[0]) & 0x07;
1449 val <<= 6;
1450 val |= (cur[1]) & 0x3F;
1451 val <<= 6;
1452 val |= (cur[2]) & 0x3F;
1453 val <<= 6;
1454 val |= (cur[3]) & 0x3F;
1455 l = 4;
1456 }
1457 if ((l == 1) || (!IS_CHAR(val))) {
1458 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1459 if (doc != NULL)
1460 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1461 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1462 tmp[sizeof(tmp) - 1] = 0;
1463 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1464 cur++;
1465 base = cur;
1466 continue;
1467 }
1468 /*
1469 * We could do multiple things here. Just save
1470 * as a char ref
1471 */
1472 snprintf(tmp, sizeof(tmp), "&#x%X;", val);
1473 tmp[sizeof(tmp) - 1] = 0;
1474 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1475 cur += l;
1476 base = cur;
1477 } else {
1478 cur++;
1479 }
1480 }
1481 if (base != cur)
1482 xmlBufferAdd(buf, base, cur - base);
1483}
1484
1485/**
1486 * xmlNodeDump:
1487 * @buf: the XML buffer output
1488 * @doc: the document
1489 * @cur: the current node
1490 * @level: the imbrication level for indenting
1491 * @format: is formatting allowed
1492 *
1493 * Dump an XML node, recursive behaviour,children are printed too.
1494 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1495 * or xmlKeepBlanksDefault(0) was called
1496 *
1497 * Returns the number of bytes written to the buffer or -1 in case of error
1498 */
1499int
1500xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1501 int format)
1502{
1503 unsigned int use;
1504 int ret;
1505 xmlOutputBufferPtr outbuf;
1506
1507 xmlInitParser();
1508
1509 if (cur == NULL) {
1510#ifdef DEBUG_TREE
1511 xmlGenericError(xmlGenericErrorContext,
1512 "xmlNodeDump : node == NULL\n");
1513#endif
1514 return (-1);
1515 }
1516 if (buf == NULL) {
1517#ifdef DEBUG_TREE
1518 xmlGenericError(xmlGenericErrorContext,
1519 "xmlNodeDump : buf == NULL\n");
1520#endif
1521 return (-1);
1522 }
1523 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1524 if (outbuf == NULL) {
1525 xmlSaveErrMemory("creating buffer");
1526 return (-1);
1527 }
1528 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1529 outbuf->buffer = buf;
1530 outbuf->encoder = NULL;
1531 outbuf->writecallback = NULL;
1532 outbuf->closecallback = NULL;
1533 outbuf->context = NULL;
1534 outbuf->written = 0;
1535
1536 use = buf->use;
1537 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1538 xmlFree(outbuf);
1539 ret = buf->use - use;
1540 return (ret);
1541}
1542
1543/**
1544 * xmlElemDump:
1545 * @f: the FILE * for the output
1546 * @doc: the document
1547 * @cur: the current node
1548 *
1549 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1550 */
1551void
1552xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1553{
1554 xmlOutputBufferPtr outbuf;
1555
1556 xmlInitParser();
1557
1558 if (cur == NULL) {
1559#ifdef DEBUG_TREE
1560 xmlGenericError(xmlGenericErrorContext,
1561 "xmlElemDump : cur == NULL\n");
1562#endif
1563 return;
1564 }
1565#ifdef DEBUG_TREE
1566 if (doc == NULL) {
1567 xmlGenericError(xmlGenericErrorContext,
1568 "xmlElemDump : doc == NULL\n");
1569 }
1570#endif
1571
1572 outbuf = xmlOutputBufferCreateFile(f, NULL);
1573 if (outbuf == NULL)
1574 return;
1575 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1576#ifdef LIBXML_HTML_ENABLED
1577 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1578#else
1579 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1580#endif /* LIBXML_HTML_ENABLED */
1581 } else
1582 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1583 xmlOutputBufferClose(outbuf);
1584}
1585
1586/************************************************************************
1587 * *
1588 * Saving functions front-ends *
1589 * *
1590 ************************************************************************/
1591
1592/**
1593 * xmlNodeDumpOutput:
1594 * @buf: the XML buffer output
1595 * @doc: the document
1596 * @cur: the current node
1597 * @level: the imbrication level for indenting
1598 * @format: is formatting allowed
1599 * @encoding: an optional encoding string
1600 *
1601 * Dump an XML node, recursive behaviour, children are printed too.
1602 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1603 * or xmlKeepBlanksDefault(0) was called
1604 */
1605void
1606xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1607 int level, int format, const char *encoding)
1608{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001609 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001610#ifdef LIBXML_HTML_ENABLED
1611 xmlDtdPtr dtd;
1612 int is_xhtml = 0;
1613#endif
1614
1615 xmlInitParser();
1616
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001617 memset(&ctxt, 0, sizeof(ctxt));
1618 ctxt.doc = doc;
1619 ctxt.buf = buf;
1620 ctxt.level = level;
1621 ctxt.format = format;
1622 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001623 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001624
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001625#ifdef LIBXML_HTML_ENABLED
1626 dtd = xmlGetIntSubset(doc);
1627 if (dtd != NULL) {
1628 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1629 if (is_xhtml < 0)
1630 is_xhtml = 0;
1631 if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
1632 (cur->type == XML_ELEMENT_NODE) &&
1633 (xmlStrEqual(cur->name, BAD_CAST "html"))) {
1634 if (encoding != NULL)
1635 htmlSetMetaEncoding((htmlDocPtr) doc,
1636 (const xmlChar *) encoding);
1637 else
1638 htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
1639 }
1640 }
1641
1642 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001643 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001644 else
1645#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001646 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001647}
1648
1649/**
1650 * xmlDocDumpFormatMemoryEnc:
1651 * @out_doc: Document to generate XML text from
1652 * @doc_txt_ptr: Memory pointer for allocated XML text
1653 * @doc_txt_len: Length of the generated XML text
1654 * @txt_encoding: Character encoding to use when generating XML text
1655 * @format: should formatting spaces been added
1656 *
1657 * Dump the current DOM tree into memory using the character encoding specified
1658 * by the caller. Note it is up to the caller of this function to free the
1659 * allocated memory with xmlFree().
1660 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1661 * or xmlKeepBlanksDefault(0) was called
1662 */
1663
1664void
1665xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1666 int * doc_txt_len, const char * txt_encoding,
1667 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001668 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001669 int dummy = 0;
1670 xmlOutputBufferPtr out_buff = NULL;
1671 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1672
1673 if (doc_txt_len == NULL) {
1674 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1675 }
1676
1677 if (doc_txt_ptr == NULL) {
1678 *doc_txt_len = 0;
1679 return;
1680 }
1681
1682 *doc_txt_ptr = NULL;
1683 *doc_txt_len = 0;
1684
1685 if (out_doc == NULL) {
1686 /* No document, no output */
1687 return;
1688 }
1689
1690 /*
1691 * Validate the encoding value, if provided.
1692 * This logic is copied from xmlSaveFileEnc.
1693 */
1694
1695 if (txt_encoding == NULL)
1696 txt_encoding = (const char *) out_doc->encoding;
1697 if (txt_encoding != NULL) {
1698 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1699 if ( conv_hdlr == NULL ) {
1700 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1701 txt_encoding);
1702 return;
1703 }
1704 }
1705
1706 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1707 xmlSaveErrMemory("creating buffer");
1708 return;
1709 }
1710
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001711 memset(&ctxt, 0, sizeof(ctxt));
1712 ctxt.doc = out_doc;
1713 ctxt.buf = out_buff;
1714 ctxt.level = 0;
1715 ctxt.format = format;
1716 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001717 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001718 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001719 xmlOutputBufferFlush(out_buff);
1720 if (out_buff->conv != NULL) {
1721 *doc_txt_len = out_buff->conv->use;
1722 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1723 } else {
1724 *doc_txt_len = out_buff->buffer->use;
1725 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1726 }
1727 (void)xmlOutputBufferClose(out_buff);
1728
1729 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1730 *doc_txt_len = 0;
1731 xmlSaveErrMemory("creating output");
1732 }
1733
1734 return;
1735}
1736
1737/**
1738 * xmlDocDumpMemory:
1739 * @cur: the document
1740 * @mem: OUT: the memory pointer
1741 * @size: OUT: the memory length
1742 *
1743 * Dump an XML document in memory and return the #xmlChar * and it's size
1744 * in bytes. It's up to the caller to free the memory with xmlFree().
1745 * The resulting byte array is zero terminated, though the last 0 is not
1746 * included in the returned size.
1747 */
1748void
1749xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1750 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1751}
1752
1753/**
1754 * xmlDocDumpFormatMemory:
1755 * @cur: the document
1756 * @mem: OUT: the memory pointer
1757 * @size: OUT: the memory length
1758 * @format: should formatting spaces been added
1759 *
1760 *
1761 * Dump an XML document in memory and return the #xmlChar * and it's size.
1762 * It's up to the caller to free the memory with xmlFree().
1763 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1764 * or xmlKeepBlanksDefault(0) was called
1765 */
1766void
1767xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
1768 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
1769}
1770
1771/**
1772 * xmlDocDumpMemoryEnc:
1773 * @out_doc: Document to generate XML text from
1774 * @doc_txt_ptr: Memory pointer for allocated XML text
1775 * @doc_txt_len: Length of the generated XML text
1776 * @txt_encoding: Character encoding to use when generating XML text
1777 *
1778 * Dump the current DOM tree into memory using the character encoding specified
1779 * by the caller. Note it is up to the caller of this function to free the
1780 * allocated memory with xmlFree().
1781 */
1782
1783void
1784xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1785 int * doc_txt_len, const char * txt_encoding) {
1786 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
1787 txt_encoding, 0);
1788}
1789
1790/**
1791 * xmlDocFormatDump:
1792 * @f: the FILE*
1793 * @cur: the document
1794 * @format: should formatting spaces been added
1795 *
1796 * Dump an XML document to an open FILE.
1797 *
1798 * returns: the number of bytes written or -1 in case of failure.
1799 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1800 * or xmlKeepBlanksDefault(0) was called
1801 */
1802int
1803xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001804 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001805 xmlOutputBufferPtr buf;
1806 const char * encoding;
1807 xmlCharEncodingHandlerPtr handler = NULL;
1808 int ret;
1809
1810 if (cur == NULL) {
1811#ifdef DEBUG_TREE
1812 xmlGenericError(xmlGenericErrorContext,
1813 "xmlDocDump : document == NULL\n");
1814#endif
1815 return(-1);
1816 }
1817 encoding = (const char *) cur->encoding;
1818
1819 if (encoding != NULL) {
1820 handler = xmlFindCharEncodingHandler(encoding);
1821 if (handler == NULL) {
1822 xmlFree((char *) cur->encoding);
1823 cur->encoding = NULL;
1824 }
1825 }
1826 buf = xmlOutputBufferCreateFile(f, handler);
1827 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001828 memset(&ctxt, 0, sizeof(ctxt));
1829 ctxt.doc = cur;
1830 ctxt.buf = buf;
1831 ctxt.level = 0;
1832 ctxt.format = format;
1833 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001834 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001835 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001836
1837 ret = xmlOutputBufferClose(buf);
1838 return(ret);
1839}
1840
1841/**
1842 * xmlDocDump:
1843 * @f: the FILE*
1844 * @cur: the document
1845 *
1846 * Dump an XML document to an open FILE.
1847 *
1848 * returns: the number of bytes written or -1 in case of failure.
1849 */
1850int
1851xmlDocDump(FILE *f, xmlDocPtr cur) {
1852 return(xmlDocFormatDump (f, cur, 0));
1853}
1854
1855/**
1856 * xmlSaveFileTo:
1857 * @buf: an output I/O buffer
1858 * @cur: the document
1859 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
1860 *
1861 * Dump an XML document to an I/O buffer.
1862 *
1863 * returns: the number of bytes written or -1 in case of failure.
1864 */
1865int
1866xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001867 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001868 int ret;
1869
1870 if (buf == NULL) return(0);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001871 memset(&ctxt, 0, sizeof(ctxt));
1872 ctxt.doc = cur;
1873 ctxt.buf = buf;
1874 ctxt.level = 0;
1875 ctxt.format = 0;
1876 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001877 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001878 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001879 ret = xmlOutputBufferClose(buf);
1880 return(ret);
1881}
1882
1883/**
1884 * xmlSaveFormatFileTo:
1885 * @buf: an output I/O buffer
1886 * @cur: the document
1887 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
1888 * @format: should formatting spaces been added
1889 *
1890 * Dump an XML document to an I/O buffer.
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001891 * NOTE: the I/O buffer is closed as part of the call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001892 *
1893 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001894 */
1895int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001896xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
1897 const char *encoding, int format)
1898{
1899 xmlSaveCtxt ctxt;
1900 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001901
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001902 if (buf == NULL)
1903 return (0);
1904 memset(&ctxt, 0, sizeof(ctxt));
1905 ctxt.doc = cur;
1906 ctxt.buf = buf;
1907 ctxt.level = 0;
1908 ctxt.format = format;
1909 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001910 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001911 xmlDocContentDumpOutput(&ctxt, cur);
1912 ret = xmlOutputBufferClose(buf);
1913 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001914}
1915
1916/**
1917 * xmlSaveFormatFileEnc:
1918 * @filename: the filename or URL to output
1919 * @cur: the document being saved
1920 * @encoding: the name of the encoding to use or NULL.
1921 * @format: should formatting spaces be added.
1922 *
1923 * Dump an XML document to a file or an URL.
1924 *
1925 * Returns the number of bytes written or -1 in case of error.
1926 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1927 * or xmlKeepBlanksDefault(0) was called
1928 */
1929int
1930xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
1931 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001932 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001933 xmlOutputBufferPtr buf;
1934 xmlCharEncodingHandlerPtr handler = NULL;
1935 int ret;
1936
1937 if (cur == NULL)
1938 return(-1);
1939
1940 if (encoding == NULL)
1941 encoding = (const char *) cur->encoding;
1942
1943 if (encoding != NULL) {
1944
1945 handler = xmlFindCharEncodingHandler(encoding);
1946 if (handler == NULL)
1947 return(-1);
1948 }
1949
1950#ifdef HAVE_ZLIB_H
1951 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
1952#endif
1953 /*
1954 * save the content to a temp buffer.
1955 */
1956 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
1957 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001958 memset(&ctxt, 0, sizeof(ctxt));
1959 ctxt.doc = cur;
1960 ctxt.buf = buf;
1961 ctxt.level = 0;
1962 ctxt.format = format;
1963 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001964 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001965
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001966 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001967
1968 ret = xmlOutputBufferClose(buf);
1969 return(ret);
1970}
1971
1972
1973/**
1974 * xmlSaveFileEnc:
1975 * @filename: the filename (or URL)
1976 * @cur: the document
1977 * @encoding: the name of an encoding (or NULL)
1978 *
1979 * Dump an XML document, converting it to the given encoding
1980 *
1981 * returns: the number of bytes written or -1 in case of failure.
1982 */
1983int
1984xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
1985 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
1986}
1987
1988/**
1989 * xmlSaveFormatFile:
1990 * @filename: the filename (or URL)
1991 * @cur: the document
1992 * @format: should formatting spaces been added
1993 *
1994 * Dump an XML document to a file. Will use compression if
1995 * compiled in and enabled. If @filename is "-" the stdout file is
1996 * used. If @format is set then the document will be indented on output.
1997 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1998 * or xmlKeepBlanksDefault(0) was called
1999 *
2000 * returns: the number of bytes written or -1 in case of failure.
2001 */
2002int
2003xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2004 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2005}
2006
2007/**
2008 * xmlSaveFile:
2009 * @filename: the filename (or URL)
2010 * @cur: the document
2011 *
2012 * Dump an XML document to a file. Will use compression if
2013 * compiled in and enabled. If @filename is "-" the stdout file is
2014 * used.
2015 * returns: the number of bytes written or -1 in case of failure.
2016 */
2017int
2018xmlSaveFile(const char *filename, xmlDocPtr cur) {
2019 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2020}
2021
2022#endif /* LIBXML_OUTPUT_ENABLED */
2023