blob: b4f6de9c7fa694fefe3a69a3c06492f19160a55c [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)) {
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
Daniel Veillard377e1a92004-04-16 16:30:05 +00001268 * TODO: The function is not fully implemented yet as it does not return the
1269 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001270 *
1271 * Returns the number of byte written or -1 in case of error
1272 */
1273long
1274xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1275{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001276 long ret = 0;
1277
1278 xmlDocContentDumpOutput(ctxt, doc);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001279 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001280}
1281
1282/**
1283 * xmlSaveTree:
1284 * @ctxt: a document saving context
1285 * @node: a document
1286 *
1287 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001288 * TODO: The function is not fully implemented yet as it does not return the
1289 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001290 *
1291 * Returns the number of byte written or -1 in case of error
1292 */
1293long
1294xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1295{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001296 long ret = 0;
1297
1298 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001299 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001300}
1301
1302/**
1303 * xmlSaveFlush:
1304 * @ctxt: a document saving context
1305 *
1306 * Flush a document saving context, i.e. make sure that all bytes have
1307 * been output.
1308 *
1309 * Returns the number of byte written or -1 in case of error.
1310 */
1311int
1312xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1313{
1314 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001315 if (ctxt->buf == NULL) return(-1);
1316 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001317}
1318
1319/**
1320 * xmlSaveClose:
1321 * @ctxt: a document saving context
1322 *
1323 * Close a document saving context, i.e. make sure that all bytes have
1324 * been output and free the associated data.
1325 *
1326 * Returns the number of byte written or -1 in case of error.
1327 */
1328int
1329xmlSaveClose(xmlSaveCtxtPtr ctxt)
1330{
1331 int ret;
1332
1333 if (ctxt == NULL) return(-1);
1334 ret = xmlSaveFlush(ctxt);
1335 xmlFreeSaveCtxt(ctxt);
1336 return(ret);
1337}
1338
1339/************************************************************************
1340 * *
1341 * Public entry points based on buffers *
1342 * *
1343 ************************************************************************/
1344/**
1345 * xmlAttrSerializeTxtContent:
1346 * @buf: the XML buffer output
1347 * @doc: the document
1348 * @attr: the attribute node
1349 * @string: the text content
1350 *
1351 * Serialize text attribute values to an xml simple buffer
1352 */
1353void
1354xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
1355 xmlAttrPtr attr, const xmlChar *string) {
1356 xmlChar *base, *cur;
1357
1358 if (string == NULL) return;
1359 base = cur = (xmlChar *)string;
1360 while (*cur != 0) {
1361 if (*cur == '\n') {
1362 if (base != cur)
1363 xmlBufferAdd(buf, base, cur - base);
1364 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1365 cur++;
1366 base = cur;
1367 } else if (*cur == '\r') {
1368 if (base != cur)
1369 xmlBufferAdd(buf, base, cur - base);
1370 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1371 cur++;
1372 base = cur;
1373 } else if (*cur == '\t') {
1374 if (base != cur)
1375 xmlBufferAdd(buf, base, cur - base);
1376 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1377 cur++;
1378 base = cur;
1379 } else if (*cur == '"') {
1380 if (base != cur)
1381 xmlBufferAdd(buf, base, cur - base);
1382 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1383 cur++;
1384 base = cur;
1385 } else if (*cur == '<') {
1386 if (base != cur)
1387 xmlBufferAdd(buf, base, cur - base);
1388 xmlBufferAdd(buf, BAD_CAST "&lt;", 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 "&gt;", 4);
1395 cur++;
1396 base = cur;
1397 } else if (*cur == '&') {
1398 if (base != cur)
1399 xmlBufferAdd(buf, base, cur - base);
1400 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1401 cur++;
1402 base = cur;
1403 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1404 (doc->encoding == NULL))) {
1405 /*
1406 * We assume we have UTF-8 content.
1407 */
1408 char tmp[10];
1409 int val = 0, l = 1;
1410
1411 if (base != cur)
1412 xmlBufferAdd(buf, base, cur - base);
1413 if (*cur < 0xC0) {
1414 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1415 if (doc != NULL)
1416 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1417 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1418 tmp[sizeof(tmp) - 1] = 0;
1419 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1420 cur++;
1421 base = cur;
1422 continue;
1423 } else if (*cur < 0xE0) {
1424 val = (cur[0]) & 0x1F;
1425 val <<= 6;
1426 val |= (cur[1]) & 0x3F;
1427 l = 2;
1428 } else if (*cur < 0xF0) {
1429 val = (cur[0]) & 0x0F;
1430 val <<= 6;
1431 val |= (cur[1]) & 0x3F;
1432 val <<= 6;
1433 val |= (cur[2]) & 0x3F;
1434 l = 3;
1435 } else if (*cur < 0xF8) {
1436 val = (cur[0]) & 0x07;
1437 val <<= 6;
1438 val |= (cur[1]) & 0x3F;
1439 val <<= 6;
1440 val |= (cur[2]) & 0x3F;
1441 val <<= 6;
1442 val |= (cur[3]) & 0x3F;
1443 l = 4;
1444 }
1445 if ((l == 1) || (!IS_CHAR(val))) {
1446 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1447 if (doc != NULL)
1448 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1449 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1450 tmp[sizeof(tmp) - 1] = 0;
1451 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1452 cur++;
1453 base = cur;
1454 continue;
1455 }
1456 /*
1457 * We could do multiple things here. Just save
1458 * as a char ref
1459 */
1460 snprintf(tmp, sizeof(tmp), "&#x%X;", val);
1461 tmp[sizeof(tmp) - 1] = 0;
1462 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1463 cur += l;
1464 base = cur;
1465 } else {
1466 cur++;
1467 }
1468 }
1469 if (base != cur)
1470 xmlBufferAdd(buf, base, cur - base);
1471}
1472
1473/**
1474 * xmlNodeDump:
1475 * @buf: the XML buffer output
1476 * @doc: the document
1477 * @cur: the current node
1478 * @level: the imbrication level for indenting
1479 * @format: is formatting allowed
1480 *
1481 * Dump an XML node, recursive behaviour,children are printed too.
1482 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1483 * or xmlKeepBlanksDefault(0) was called
1484 *
1485 * Returns the number of bytes written to the buffer or -1 in case of error
1486 */
1487int
1488xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1489 int format)
1490{
1491 unsigned int use;
1492 int ret;
1493 xmlOutputBufferPtr outbuf;
1494
1495 xmlInitParser();
1496
1497 if (cur == NULL) {
1498#ifdef DEBUG_TREE
1499 xmlGenericError(xmlGenericErrorContext,
1500 "xmlNodeDump : node == NULL\n");
1501#endif
1502 return (-1);
1503 }
1504 if (buf == NULL) {
1505#ifdef DEBUG_TREE
1506 xmlGenericError(xmlGenericErrorContext,
1507 "xmlNodeDump : buf == NULL\n");
1508#endif
1509 return (-1);
1510 }
1511 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1512 if (outbuf == NULL) {
1513 xmlSaveErrMemory("creating buffer");
1514 return (-1);
1515 }
1516 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1517 outbuf->buffer = buf;
1518 outbuf->encoder = NULL;
1519 outbuf->writecallback = NULL;
1520 outbuf->closecallback = NULL;
1521 outbuf->context = NULL;
1522 outbuf->written = 0;
1523
1524 use = buf->use;
1525 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1526 xmlFree(outbuf);
1527 ret = buf->use - use;
1528 return (ret);
1529}
1530
1531/**
1532 * xmlElemDump:
1533 * @f: the FILE * for the output
1534 * @doc: the document
1535 * @cur: the current node
1536 *
1537 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1538 */
1539void
1540xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1541{
1542 xmlOutputBufferPtr outbuf;
1543
1544 xmlInitParser();
1545
1546 if (cur == NULL) {
1547#ifdef DEBUG_TREE
1548 xmlGenericError(xmlGenericErrorContext,
1549 "xmlElemDump : cur == NULL\n");
1550#endif
1551 return;
1552 }
1553#ifdef DEBUG_TREE
1554 if (doc == NULL) {
1555 xmlGenericError(xmlGenericErrorContext,
1556 "xmlElemDump : doc == NULL\n");
1557 }
1558#endif
1559
1560 outbuf = xmlOutputBufferCreateFile(f, NULL);
1561 if (outbuf == NULL)
1562 return;
1563 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1564#ifdef LIBXML_HTML_ENABLED
1565 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1566#else
1567 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1568#endif /* LIBXML_HTML_ENABLED */
1569 } else
1570 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1571 xmlOutputBufferClose(outbuf);
1572}
1573
1574/************************************************************************
1575 * *
1576 * Saving functions front-ends *
1577 * *
1578 ************************************************************************/
1579
1580/**
1581 * xmlNodeDumpOutput:
1582 * @buf: the XML buffer output
1583 * @doc: the document
1584 * @cur: the current node
1585 * @level: the imbrication level for indenting
1586 * @format: is formatting allowed
1587 * @encoding: an optional encoding string
1588 *
1589 * Dump an XML node, recursive behaviour, children are printed too.
1590 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1591 * or xmlKeepBlanksDefault(0) was called
1592 */
1593void
1594xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1595 int level, int format, const char *encoding)
1596{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001597 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001598#ifdef LIBXML_HTML_ENABLED
1599 xmlDtdPtr dtd;
1600 int is_xhtml = 0;
1601#endif
1602
1603 xmlInitParser();
1604
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001605 memset(&ctxt, 0, sizeof(ctxt));
1606 ctxt.doc = doc;
1607 ctxt.buf = buf;
1608 ctxt.level = level;
1609 ctxt.format = format;
1610 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001611 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001612
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001613#ifdef LIBXML_HTML_ENABLED
1614 dtd = xmlGetIntSubset(doc);
1615 if (dtd != NULL) {
1616 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1617 if (is_xhtml < 0)
1618 is_xhtml = 0;
1619 if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
1620 (cur->type == XML_ELEMENT_NODE) &&
1621 (xmlStrEqual(cur->name, BAD_CAST "html"))) {
1622 if (encoding != NULL)
1623 htmlSetMetaEncoding((htmlDocPtr) doc,
1624 (const xmlChar *) encoding);
1625 else
1626 htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
1627 }
1628 }
1629
1630 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001631 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001632 else
1633#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001634 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001635}
1636
1637/**
1638 * xmlDocDumpFormatMemoryEnc:
1639 * @out_doc: Document to generate XML text from
1640 * @doc_txt_ptr: Memory pointer for allocated XML text
1641 * @doc_txt_len: Length of the generated XML text
1642 * @txt_encoding: Character encoding to use when generating XML text
1643 * @format: should formatting spaces been added
1644 *
1645 * Dump the current DOM tree into memory using the character encoding specified
1646 * by the caller. Note it is up to the caller of this function to free the
1647 * allocated memory with xmlFree().
1648 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1649 * or xmlKeepBlanksDefault(0) was called
1650 */
1651
1652void
1653xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1654 int * doc_txt_len, const char * txt_encoding,
1655 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001656 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001657 int dummy = 0;
1658 xmlOutputBufferPtr out_buff = NULL;
1659 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1660
1661 if (doc_txt_len == NULL) {
1662 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1663 }
1664
1665 if (doc_txt_ptr == NULL) {
1666 *doc_txt_len = 0;
1667 return;
1668 }
1669
1670 *doc_txt_ptr = NULL;
1671 *doc_txt_len = 0;
1672
1673 if (out_doc == NULL) {
1674 /* No document, no output */
1675 return;
1676 }
1677
1678 /*
1679 * Validate the encoding value, if provided.
1680 * This logic is copied from xmlSaveFileEnc.
1681 */
1682
1683 if (txt_encoding == NULL)
1684 txt_encoding = (const char *) out_doc->encoding;
1685 if (txt_encoding != NULL) {
1686 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1687 if ( conv_hdlr == NULL ) {
1688 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1689 txt_encoding);
1690 return;
1691 }
1692 }
1693
1694 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1695 xmlSaveErrMemory("creating buffer");
1696 return;
1697 }
1698
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001699 memset(&ctxt, 0, sizeof(ctxt));
1700 ctxt.doc = out_doc;
1701 ctxt.buf = out_buff;
1702 ctxt.level = 0;
1703 ctxt.format = format;
1704 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001705 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001706 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001707 xmlOutputBufferFlush(out_buff);
1708 if (out_buff->conv != NULL) {
1709 *doc_txt_len = out_buff->conv->use;
1710 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1711 } else {
1712 *doc_txt_len = out_buff->buffer->use;
1713 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1714 }
1715 (void)xmlOutputBufferClose(out_buff);
1716
1717 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1718 *doc_txt_len = 0;
1719 xmlSaveErrMemory("creating output");
1720 }
1721
1722 return;
1723}
1724
1725/**
1726 * xmlDocDumpMemory:
1727 * @cur: the document
1728 * @mem: OUT: the memory pointer
1729 * @size: OUT: the memory length
1730 *
1731 * Dump an XML document in memory and return the #xmlChar * and it's size
1732 * in bytes. It's up to the caller to free the memory with xmlFree().
1733 * The resulting byte array is zero terminated, though the last 0 is not
1734 * included in the returned size.
1735 */
1736void
1737xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1738 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1739}
1740
1741/**
1742 * xmlDocDumpFormatMemory:
1743 * @cur: the document
1744 * @mem: OUT: the memory pointer
1745 * @size: OUT: the memory length
1746 * @format: should formatting spaces been added
1747 *
1748 *
1749 * Dump an XML document in memory and return the #xmlChar * and it's size.
1750 * It's up to the caller to free the memory with xmlFree().
1751 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1752 * or xmlKeepBlanksDefault(0) was called
1753 */
1754void
1755xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
1756 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
1757}
1758
1759/**
1760 * xmlDocDumpMemoryEnc:
1761 * @out_doc: Document to generate XML text from
1762 * @doc_txt_ptr: Memory pointer for allocated XML text
1763 * @doc_txt_len: Length of the generated XML text
1764 * @txt_encoding: Character encoding to use when generating XML text
1765 *
1766 * Dump the current DOM tree into memory using the character encoding specified
1767 * by the caller. Note it is up to the caller of this function to free the
1768 * allocated memory with xmlFree().
1769 */
1770
1771void
1772xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1773 int * doc_txt_len, const char * txt_encoding) {
1774 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
1775 txt_encoding, 0);
1776}
1777
1778/**
1779 * xmlDocFormatDump:
1780 * @f: the FILE*
1781 * @cur: the document
1782 * @format: should formatting spaces been added
1783 *
1784 * Dump an XML document to an open FILE.
1785 *
1786 * returns: the number of bytes written or -1 in case of failure.
1787 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1788 * or xmlKeepBlanksDefault(0) was called
1789 */
1790int
1791xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001792 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001793 xmlOutputBufferPtr buf;
1794 const char * encoding;
1795 xmlCharEncodingHandlerPtr handler = NULL;
1796 int ret;
1797
1798 if (cur == NULL) {
1799#ifdef DEBUG_TREE
1800 xmlGenericError(xmlGenericErrorContext,
1801 "xmlDocDump : document == NULL\n");
1802#endif
1803 return(-1);
1804 }
1805 encoding = (const char *) cur->encoding;
1806
1807 if (encoding != NULL) {
1808 handler = xmlFindCharEncodingHandler(encoding);
1809 if (handler == NULL) {
1810 xmlFree((char *) cur->encoding);
1811 cur->encoding = NULL;
1812 }
1813 }
1814 buf = xmlOutputBufferCreateFile(f, handler);
1815 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001816 memset(&ctxt, 0, sizeof(ctxt));
1817 ctxt.doc = cur;
1818 ctxt.buf = buf;
1819 ctxt.level = 0;
1820 ctxt.format = format;
1821 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001822 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001823 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001824
1825 ret = xmlOutputBufferClose(buf);
1826 return(ret);
1827}
1828
1829/**
1830 * xmlDocDump:
1831 * @f: the FILE*
1832 * @cur: the document
1833 *
1834 * Dump an XML document to an open FILE.
1835 *
1836 * returns: the number of bytes written or -1 in case of failure.
1837 */
1838int
1839xmlDocDump(FILE *f, xmlDocPtr cur) {
1840 return(xmlDocFormatDump (f, cur, 0));
1841}
1842
1843/**
1844 * xmlSaveFileTo:
1845 * @buf: an output I/O buffer
1846 * @cur: the document
1847 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
1848 *
1849 * Dump an XML document to an I/O buffer.
1850 *
1851 * returns: the number of bytes written or -1 in case of failure.
1852 */
1853int
1854xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001855 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001856 int ret;
1857
1858 if (buf == NULL) return(0);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001859 memset(&ctxt, 0, sizeof(ctxt));
1860 ctxt.doc = cur;
1861 ctxt.buf = buf;
1862 ctxt.level = 0;
1863 ctxt.format = 0;
1864 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001865 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001866 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001867 ret = xmlOutputBufferClose(buf);
1868 return(ret);
1869}
1870
1871/**
1872 * xmlSaveFormatFileTo:
1873 * @buf: an output I/O buffer
1874 * @cur: the document
1875 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
1876 * @format: should formatting spaces been added
1877 *
1878 * Dump an XML document to an I/O buffer.
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001879 * NOTE: the I/O buffer is closed as part of the call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001880 *
1881 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001882 */
1883int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001884xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
1885 const char *encoding, int format)
1886{
1887 xmlSaveCtxt ctxt;
1888 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001889
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001890 if (buf == NULL)
1891 return (0);
1892 memset(&ctxt, 0, sizeof(ctxt));
1893 ctxt.doc = cur;
1894 ctxt.buf = buf;
1895 ctxt.level = 0;
1896 ctxt.format = format;
1897 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001898 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001899 xmlDocContentDumpOutput(&ctxt, cur);
1900 ret = xmlOutputBufferClose(buf);
1901 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001902}
1903
1904/**
1905 * xmlSaveFormatFileEnc:
1906 * @filename: the filename or URL to output
1907 * @cur: the document being saved
1908 * @encoding: the name of the encoding to use or NULL.
1909 * @format: should formatting spaces be added.
1910 *
1911 * Dump an XML document to a file or an URL.
1912 *
1913 * Returns the number of bytes written or -1 in case of error.
1914 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1915 * or xmlKeepBlanksDefault(0) was called
1916 */
1917int
1918xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
1919 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001920 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001921 xmlOutputBufferPtr buf;
1922 xmlCharEncodingHandlerPtr handler = NULL;
1923 int ret;
1924
1925 if (cur == NULL)
1926 return(-1);
1927
1928 if (encoding == NULL)
1929 encoding = (const char *) cur->encoding;
1930
1931 if (encoding != NULL) {
1932
1933 handler = xmlFindCharEncodingHandler(encoding);
1934 if (handler == NULL)
1935 return(-1);
1936 }
1937
1938#ifdef HAVE_ZLIB_H
1939 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
1940#endif
1941 /*
1942 * save the content to a temp buffer.
1943 */
1944 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
1945 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001946 memset(&ctxt, 0, sizeof(ctxt));
1947 ctxt.doc = cur;
1948 ctxt.buf = buf;
1949 ctxt.level = 0;
1950 ctxt.format = format;
1951 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001952 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001953
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001954 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001955
1956 ret = xmlOutputBufferClose(buf);
1957 return(ret);
1958}
1959
1960
1961/**
1962 * xmlSaveFileEnc:
1963 * @filename: the filename (or URL)
1964 * @cur: the document
1965 * @encoding: the name of an encoding (or NULL)
1966 *
1967 * Dump an XML document, converting it to the given encoding
1968 *
1969 * returns: the number of bytes written or -1 in case of failure.
1970 */
1971int
1972xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
1973 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
1974}
1975
1976/**
1977 * xmlSaveFormatFile:
1978 * @filename: the filename (or URL)
1979 * @cur: the document
1980 * @format: should formatting spaces been added
1981 *
1982 * Dump an XML document to a file. Will use compression if
1983 * compiled in and enabled. If @filename is "-" the stdout file is
1984 * used. If @format is set then the document will be indented on output.
1985 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1986 * or xmlKeepBlanksDefault(0) was called
1987 *
1988 * returns: the number of bytes written or -1 in case of failure.
1989 */
1990int
1991xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
1992 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
1993}
1994
1995/**
1996 * xmlSaveFile:
1997 * @filename: the filename (or URL)
1998 * @cur: the document
1999 *
2000 * Dump an XML document to a file. Will use compression if
2001 * compiled in and enabled. If @filename is "-" the stdout file is
2002 * used.
2003 * returns: the number of bytes written or -1 in case of failure.
2004 */
2005int
2006xmlSaveFile(const char *filename, xmlDocPtr cur) {
2007 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2008}
2009
2010#endif /* LIBXML_OUTPUT_ENABLED */
2011