blob: e596c58ca81df40aa66375c53b20979657f5a805 [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 Veillard3995bc32004-05-15 18:57:31 +000086 char indent[MAX_INDENT + 1]; /* array for indenting output */
Daniel Veillard753086a2004-03-28 16:12:44 +000087 int indent_nr;
88 int indent_size;
Daniel Veillard3995bc32004-05-15 18:57:31 +000089 xmlCharEncodingOutputFunc escape; /* used for element content */
90 xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */
Daniel Veillard1a8741c2004-03-04 13:40:59 +000091};
92
93/************************************************************************
94 * *
95 * Output error handlers *
96 * *
97 ************************************************************************/
98/**
99 * xmlSaveErrMemory:
100 * @extra: extra informations
101 *
102 * Handle an out of memory condition
103 */
104static void
105xmlSaveErrMemory(const char *extra)
106{
107 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
108}
109
110/**
111 * xmlSaveErr:
112 * @code: the error number
113 * @node: the location of the error.
114 * @extra: extra informations
115 *
116 * Handle an out of memory condition
117 */
118static void
119xmlSaveErr(int code, xmlNodePtr node, const char *extra)
120{
121 const char *msg = NULL;
122
123 switch(code) {
124 case XML_SAVE_NOT_UTF8:
Rob Richards417b74d2006-08-15 23:14:24 +0000125 msg = "string is not in UTF-8\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000126 break;
127 case XML_SAVE_CHAR_INVALID:
Rob Richards417b74d2006-08-15 23:14:24 +0000128 msg = "invalid character value\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000129 break;
130 case XML_SAVE_UNKNOWN_ENCODING:
Rob Richards417b74d2006-08-15 23:14:24 +0000131 msg = "unknown encoding %s\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000132 break;
133 case XML_SAVE_NO_DOCTYPE:
Rob Richards417b74d2006-08-15 23:14:24 +0000134 msg = "document has no DOCTYPE\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000135 break;
136 default:
Rob Richards417b74d2006-08-15 23:14:24 +0000137 msg = "unexpected error number\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000138 }
139 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
140}
141
142/************************************************************************
143 * *
Daniel Veillard83a75e02004-05-14 21:50:42 +0000144 * Special escaping routines *
145 * *
146 ************************************************************************/
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000147static unsigned char *
148xmlSerializeHexCharRef(unsigned char *out, int val) {
149 unsigned char *ptr;
150
151 *out++ = '&';
152 *out++ = '#';
153 *out++ = 'x';
154 if (val < 0x10) ptr = out;
155 else if (val < 0x100) ptr = out + 1;
156 else if (val < 0x1000) ptr = out + 2;
157 else if (val < 0x10000) ptr = out + 3;
158 else if (val < 0x100000) ptr = out + 4;
159 else ptr = out + 5;
160 out = ptr + 1;
161 while (val > 0) {
162 switch (val & 0xF) {
163 case 0: *ptr-- = '0'; break;
164 case 1: *ptr-- = '1'; break;
165 case 2: *ptr-- = '2'; break;
166 case 3: *ptr-- = '3'; break;
167 case 4: *ptr-- = '4'; break;
168 case 5: *ptr-- = '5'; break;
169 case 6: *ptr-- = '6'; break;
170 case 7: *ptr-- = '7'; break;
171 case 8: *ptr-- = '8'; break;
172 case 9: *ptr-- = '9'; break;
173 case 0xA: *ptr-- = 'A'; break;
174 case 0xB: *ptr-- = 'B'; break;
175 case 0xC: *ptr-- = 'C'; break;
176 case 0xD: *ptr-- = 'D'; break;
177 case 0xE: *ptr-- = 'E'; break;
178 case 0xF: *ptr-- = 'F'; break;
179 default: *ptr-- = '0'; break;
180 }
181 val >>= 4;
182 }
183 *out++ = ';';
184 *out = 0;
185 return(out);
186}
187
Daniel Veillard83a75e02004-05-14 21:50:42 +0000188/**
189 * xmlEscapeEntities:
190 * @out: a pointer to an array of bytes to store the result
191 * @outlen: the length of @out
192 * @in: a pointer to an array of unescaped UTF-8 bytes
193 * @inlen: the length of @in
194 *
195 * Take a block of UTF-8 chars in and escape them. Used when there is no
196 * encoding specified.
197 *
198 * Returns 0 if success, or -1 otherwise
199 * The value of @inlen after return is the number of octets consumed
200 * if the return value is positive, else unpredictable.
201 * The value of @outlen after return is the number of octets consumed.
202 */
203static int
204xmlEscapeEntities(unsigned char* out, int *outlen,
205 const xmlChar* in, int *inlen) {
206 unsigned char* outstart = out;
207 const unsigned char* base = in;
208 unsigned char* outend = out + *outlen;
209 const unsigned char* inend;
210 int val;
211
212 inend = in + (*inlen);
213
214 while ((in < inend) && (out < outend)) {
215 if (*in == '<') {
216 if (outend - out < 4) break;
217 *out++ = '&';
218 *out++ = 'l';
219 *out++ = 't';
220 *out++ = ';';
221 in++;
222 continue;
223 } else if (*in == '>') {
224 if (outend - out < 4) break;
225 *out++ = '&';
226 *out++ = 'g';
227 *out++ = 't';
228 *out++ = ';';
229 in++;
230 continue;
231 } else if (*in == '&') {
232 if (outend - out < 5) break;
233 *out++ = '&';
234 *out++ = 'a';
235 *out++ = 'm';
236 *out++ = 'p';
237 *out++ = ';';
238 in++;
239 continue;
240 } else if (((*in >= 0x20) && (*in < 0x80)) ||
241 (*in == '\n') || (*in == '\t')) {
242 /*
243 * default case, just copy !
244 */
245 *out++ = *in++;
246 continue;
247 } else if (*in >= 0x80) {
248 /*
249 * We assume we have UTF-8 input.
250 */
Daniel Veillard83a75e02004-05-14 21:50:42 +0000251 if (outend - out < 10) break;
252
253 if (*in < 0xC0) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000254 xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000255 in++;
256 goto error;
257 } else if (*in < 0xE0) {
258 if (inend - in < 2) break;
259 val = (in[0]) & 0x1F;
260 val <<= 6;
261 val |= (in[1]) & 0x3F;
262 in += 2;
263 } else if (*in < 0xF0) {
264 if (inend - in < 3) break;
265 val = (in[0]) & 0x0F;
266 val <<= 6;
267 val |= (in[1]) & 0x3F;
268 val <<= 6;
269 val |= (in[2]) & 0x3F;
270 in += 3;
271 } else if (*in < 0xF8) {
272 if (inend - in < 4) break;
273 val = (in[0]) & 0x07;
274 val <<= 6;
275 val |= (in[1]) & 0x3F;
276 val <<= 6;
277 val |= (in[2]) & 0x3F;
278 val <<= 6;
279 val |= (in[3]) & 0x3F;
280 in += 4;
281 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000282 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000283 in++;
284 goto error;
285 }
286 if (!IS_CHAR(val)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000287 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000288 in++;
289 goto error;
290 }
291
292 /*
293 * We could do multiple things here. Just save as a char ref
294 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000295 out = xmlSerializeHexCharRef(out, val);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000296 } else if (IS_BYTE_CHAR(*in)) {
297 if (outend - out < 6) break;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000298 out = xmlSerializeHexCharRef(out, *in++);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000299 } else {
300 xmlGenericError(xmlGenericErrorContext,
301 "xmlEscapeEntities : char out of range\n");
302 in++;
303 goto error;
304 }
305 }
306 *outlen = out - outstart;
307 *inlen = in - base;
308 return(0);
309error:
310 *outlen = out - outstart;
311 *inlen = in - base;
312 return(-1);
313}
314
315/************************************************************************
316 * *
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000317 * Allocation and deallocation *
318 * *
319 ************************************************************************/
Daniel Veillard753086a2004-03-28 16:12:44 +0000320/**
321 * xmlSaveCtxtInit:
322 * @ctxt: the saving context
323 *
324 * Initialize a saving context
325 */
326static void
327xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
328{
329 int i;
William M. Brack12d37ab2005-02-21 13:54:07 +0000330 int len;
Daniel Veillard753086a2004-03-28 16:12:44 +0000331
332 if (ctxt == NULL) return;
Daniel Veillard3995bc32004-05-15 18:57:31 +0000333 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
334 ctxt->escape = xmlEscapeEntities;
William M. Brack12d37ab2005-02-21 13:54:07 +0000335 len = xmlStrlen((xmlChar *)xmlTreeIndentString);
336 if ((xmlTreeIndentString == NULL) || (len == 0)) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000337 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
338 } else {
William M. Brack12d37ab2005-02-21 13:54:07 +0000339 ctxt->indent_size = len;
Daniel Veillard753086a2004-03-28 16:12:44 +0000340 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
341 for (i = 0;i < ctxt->indent_nr;i++)
342 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
343 ctxt->indent_size);
344 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
345 }
Rob Richards2ce51c02005-09-12 12:16:35 +0000346
Daniel Veillard9a00fd22005-11-09 08:56:26 +0000347 if (xmlSaveNoEmptyTags) {
348 ctxt->options |= XML_SAVE_NO_EMPTY;
349 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000350}
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000351
352/**
353 * xmlFreeSaveCtxt:
354 *
355 * Free a saving context, destroying the ouptut in any remaining buffer
356 */
357static void
358xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
359{
360 if (ctxt == NULL) return;
361 if (ctxt->encoding != NULL)
362 xmlFree((char *) ctxt->encoding);
Daniel Veillarde2161a62004-04-29 17:14:25 +0000363 if (ctxt->buf != NULL)
364 xmlOutputBufferClose(ctxt->buf);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000365 xmlFree(ctxt);
366}
367
368/**
369 * xmlNewSaveCtxt:
370 *
371 * Create a new saving context
372 *
373 * Returns the new structure or NULL in case of error
374 */
375static xmlSaveCtxtPtr
376xmlNewSaveCtxt(const char *encoding, int options)
377{
378 xmlSaveCtxtPtr ret;
379
380 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
381 if (ret == NULL) {
382 xmlSaveErrMemory("creating saving context");
383 return ( NULL );
384 }
385 memset(ret, 0, sizeof(xmlSaveCtxt));
Daniel Veillard6fc5db02005-01-16 00:05:58 +0000386
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000387 if (encoding != NULL) {
388 ret->handler = xmlFindCharEncodingHandler(encoding);
389 if (ret->handler == NULL) {
390 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
391 xmlFreeSaveCtxt(ret);
392 return(NULL);
393 }
394 ret->encoding = xmlStrdup((const xmlChar *)encoding);
Daniel Veillard3995bc32004-05-15 18:57:31 +0000395 ret->escape = xmlEscapeEntities;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000396 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000397 xmlSaveCtxtInit(ret);
398
Rob Richards2ce51c02005-09-12 12:16:35 +0000399 /*
400 * Use the options
401 */
402
Daniel Veillard9a00fd22005-11-09 08:56:26 +0000403 /* Re-check this option as it may already have been set */
404 if ((ret->options & XML_SAVE_NO_EMPTY) && ! (options & XML_SAVE_NO_EMPTY)) {
405 options |= XML_SAVE_NO_EMPTY;
406 }
Rob Richards2ce51c02005-09-12 12:16:35 +0000407
408 ret->options = options;
409 if (options & XML_SAVE_FORMAT)
410 ret->format = 1;
411
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000412 return(ret);
413}
414
415/************************************************************************
416 * *
417 * Dumping XML tree content to a simple buffer *
418 * *
419 ************************************************************************/
420/**
421 * xmlAttrSerializeContent:
422 * @buf: the XML buffer output
423 * @doc: the document
424 * @attr: the attribute pointer
425 *
426 * Serialize the attribute in the buffer
427 */
428static void
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000429xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000430{
431 xmlNodePtr children;
432
433 children = attr->children;
434 while (children != NULL) {
435 switch (children->type) {
436 case XML_TEXT_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000437 xmlAttrSerializeTxtContent(buf->buffer, attr->doc,
438 attr, children->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000439 break;
440 case XML_ENTITY_REF_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000441 xmlBufferAdd(buf->buffer, BAD_CAST "&", 1);
442 xmlBufferAdd(buf->buffer, children->name,
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000443 xmlStrlen(children->name));
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000444 xmlBufferAdd(buf->buffer, BAD_CAST ";", 1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000445 break;
446 default:
447 /* should not happen unless we have a badly built tree */
448 break;
449 }
450 children = children->next;
451 }
452}
453
454/************************************************************************
455 * *
456 * Dumping XML tree content to an I/O output buffer *
457 * *
458 ************************************************************************/
459
460#ifdef LIBXML_HTML_ENABLED
461static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000462xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000463#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000464static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
465static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000466void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
Daniel Veillardce244ad2004-11-05 10:03:46 +0000467static void xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000468
469/**
470 * xmlNsDumpOutput:
471 * @buf: the XML buffer output
472 * @cur: a namespace
473 *
474 * Dump a local Namespace definition.
475 * Should be called in the context of attributes dumps.
476 */
477static void
478xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
Daniel Veillardce244ad2004-11-05 10:03:46 +0000479 if ((cur == NULL) || (buf == NULL)) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000480 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
481 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
482 return;
483
484 /* Within the context of an element attributes */
485 if (cur->prefix != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000486 xmlOutputBufferWrite(buf, 7, " xmlns:");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000487 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
488 } else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000489 xmlOutputBufferWrite(buf, 6, " xmlns");
490 xmlOutputBufferWrite(buf, 1, "=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000491 xmlBufferWriteQuotedString(buf->buffer, cur->href);
492 }
493}
494
495/**
496 * xmlNsListDumpOutput:
497 * @buf: the XML buffer output
498 * @cur: the first namespace
499 *
500 * Dump a list of local Namespace definitions.
501 * Should be called in the context of attributes dumps.
502 */
503void
504xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
505 while (cur != NULL) {
506 xmlNsDumpOutput(buf, cur);
507 cur = cur->next;
508 }
509}
510
511/**
512 * xmlDtdDumpOutput:
513 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000514 * @dtd: the pointer to the DTD
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000515 *
516 * Dump the XML document DTD, if any.
517 */
518static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000519xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
520 xmlOutputBufferPtr buf;
521 int format, level;
522 xmlDocPtr doc;
523
524 if (dtd == NULL) return;
525 if ((ctxt == NULL) || (ctxt->buf == NULL))
526 return;
527 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000528 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000529 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
530 if (dtd->ExternalID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000531 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000532 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000533 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000534 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
535 } else if (dtd->SystemID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000536 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000537 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
538 }
539 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
Daniel Veillard41c4a752004-09-08 20:55:38 +0000540 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
541 (dtd->pentities == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000542 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000543 return;
544 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000545 xmlOutputBufferWrite(buf, 3, " [\n");
Daniel Veillard41c4a752004-09-08 20:55:38 +0000546 /*
547 * Dump the notations first they are not in the DTD children list
548 * Do this only on a standalone DTD or on the internal subset though.
549 */
550 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
551 (dtd->doc->intSubset == dtd))) {
Daniel Veillardda3b29a2004-08-14 11:15:13 +0000552 xmlDumpNotationTable(buf->buffer, (xmlNotationTablePtr) dtd->notations);
553 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000554 format = ctxt->format;
555 level = ctxt->level;
556 doc = ctxt->doc;
557 ctxt->format = 0;
558 ctxt->level = -1;
559 ctxt->doc = dtd->doc;
560 xmlNodeListDumpOutput(ctxt, dtd->children);
561 ctxt->format = format;
562 ctxt->level = level;
563 ctxt->doc = doc;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000564 xmlOutputBufferWrite(buf, 2, "]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000565}
566
567/**
568 * xmlAttrDumpOutput:
569 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000570 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000571 *
572 * Dump an XML attribute
573 */
574static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000575xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
576 xmlOutputBufferPtr buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000577
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000578 if (cur == NULL) return;
579 buf = ctxt->buf;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000580 if (buf == NULL) return;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000581 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000582 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
583 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000584 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000585 }
586 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000587 xmlOutputBufferWrite(buf, 2, "=\"");
588 xmlAttrSerializeContent(buf, cur);
589 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000590}
591
592/**
593 * xmlAttrListDumpOutput:
594 * @buf: the XML buffer output
595 * @doc: the document
596 * @cur: the first attribute pointer
597 * @encoding: an optional encoding string
598 *
599 * Dump a list of XML attributes
600 */
601static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000602xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
603 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000604 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000605 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000606 cur = cur->next;
607 }
608}
609
610
611
612/**
613 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000614 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000615 *
616 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000617 */
618static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000619xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000620 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000621
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000622 if (cur == NULL) return;
623 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000624 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000625 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000626 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000627 xmlOutputBufferWrite(buf, ctxt->indent_size *
628 (ctxt->level > ctxt->indent_nr ?
629 ctxt->indent_nr : ctxt->level),
630 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000631 xmlNodeDumpOutputInternal(ctxt, cur);
632 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000633 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000634 }
635 cur = cur->next;
636 }
637}
638
639/**
640 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000641 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000642 *
643 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000644 */
645static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000646xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000647 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000648 xmlNodePtr tmp;
649 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000650 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000651
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000652 if (cur == NULL) return;
653 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000654 if (cur->type == XML_XINCLUDE_START)
655 return;
656 if (cur->type == XML_XINCLUDE_END)
657 return;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000658 if ((cur->type == XML_DOCUMENT_NODE) ||
659 (cur->type == XML_HTML_DOCUMENT_NODE)) {
660 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
661 return;
662 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000663 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000664 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000665 return;
666 }
667 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000668 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000669 return;
670 }
671 if (cur->type == XML_ELEMENT_DECL) {
672 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
673 return;
674 }
675 if (cur->type == XML_ATTRIBUTE_DECL) {
676 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
677 return;
678 }
679 if (cur->type == XML_ENTITY_DECL) {
680 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
681 return;
682 }
683 if (cur->type == XML_TEXT_NODE) {
684 if (cur->content != NULL) {
William M. Brack4e1c2db2005-02-11 10:58:55 +0000685 if (cur->name != xmlStringTextNoenc) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000686 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000687 } else {
688 /*
689 * Disable escaping, needed for XSLT
690 */
691 xmlOutputBufferWriteString(buf, (const char *) cur->content);
692 }
693 }
694
695 return;
696 }
697 if (cur->type == XML_PI_NODE) {
698 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000699 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000700 xmlOutputBufferWriteString(buf, (const char *)cur->name);
701 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000702 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000703 xmlOutputBufferWriteString(buf, (const char *)cur->content);
704 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000705 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000706 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000707 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000708 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000709 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000710 }
711 return;
712 }
713 if (cur->type == XML_COMMENT_NODE) {
714 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000715 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000716 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000717 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000718 }
719 return;
720 }
721 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000722 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000723 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000724 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000725 return;
726 }
727 if (cur->type == XML_CDATA_SECTION_NODE) {
Daniel Veillard7cd517c2005-05-20 18:47:22 +0000728 if (cur->content == NULL) {
729 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
730 } else {
731 start = end = cur->content;
732 while (*end != '\0') {
733 if ((*end == ']') && (*(end + 1) == ']') &&
734 (*(end + 2) == '>')) {
735 end = end + 2;
736 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
737 xmlOutputBufferWrite(buf, end - start, (const char *)start);
738 xmlOutputBufferWrite(buf, 3, "]]>");
739 start = end;
740 }
741 end++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000742 }
Daniel Veillard7cd517c2005-05-20 18:47:22 +0000743 if (start != end) {
744 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
745 xmlOutputBufferWriteString(buf, (const char *)start);
746 xmlOutputBufferWrite(buf, 3, "]]>");
747 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000748 }
749 return;
750 }
751 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000752 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000753 return;
754 }
755 if (cur->type == XML_NAMESPACE_DECL) {
756 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
757 return;
758 }
759
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000760 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000761 if (format == 1) {
762 tmp = cur->children;
763 while (tmp != NULL) {
764 if ((tmp->type == XML_TEXT_NODE) ||
765 (tmp->type == XML_CDATA_SECTION_NODE) ||
766 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000767 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000768 break;
769 }
770 tmp = tmp->next;
771 }
772 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000773 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000774 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
775 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000776 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000777 }
778
779 xmlOutputBufferWriteString(buf, (const char *)cur->name);
780 if (cur->nsDef)
781 xmlNsListDumpOutput(buf, cur->nsDef);
782 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000783 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000784
785 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
Rob Richards2ce51c02005-09-12 12:16:35 +0000786 (cur->children == NULL) && ((ctxt->options & XML_SAVE_NO_EMPTY) == 0)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000787 xmlOutputBufferWrite(buf, 2, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000788 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000789 return;
790 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000791 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000792 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000793 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000794 }
795 if (cur->children != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000796 if (ctxt->format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000797 if (ctxt->level >= 0) ctxt->level++;
798 xmlNodeListDumpOutput(ctxt, cur->children);
799 if (ctxt->level > 0) ctxt->level--;
800 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000801 xmlOutputBufferWrite(buf, ctxt->indent_size *
802 (ctxt->level > ctxt->indent_nr ?
803 ctxt->indent_nr : ctxt->level),
804 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000805 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000806 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000807 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
808 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000809 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000810 }
811
812 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000813 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000814 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000815}
816
817/**
818 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000819 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000820 *
821 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000822 */
823static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000824xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000825#ifdef LIBXML_HTML_ENABLED
826 xmlDtdPtr dtd;
827 int is_xhtml = 0;
828#endif
829 const xmlChar *oldenc = cur->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000830 const xmlChar *encoding = ctxt->encoding;
831 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000832
833 xmlInitParser();
834
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000835 if (ctxt->encoding != NULL)
836 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000837
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000838 buf = ctxt->buf;
Daniel Veillard100e1802005-08-08 14:44:11 +0000839 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
840 xmlOutputBufferWrite(buf, 14, "<?xml version=");
841 if (cur->version != NULL)
842 xmlBufferWriteQuotedString(buf->buffer, cur->version);
843 else
844 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
845 if (ctxt->encoding == NULL) {
846 if (cur->encoding != NULL)
847 encoding = cur->encoding;
848 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
849 encoding = (const xmlChar *)
850 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
851 }
852 if (encoding != NULL) {
853 xmlOutputBufferWrite(buf, 10, " encoding=");
854 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
855 }
856 switch (cur->standalone) {
857 case 0:
858 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
859 break;
860 case 1:
861 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
862 break;
863 }
864 xmlOutputBufferWrite(buf, 3, "?>\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000865 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000866
867#ifdef LIBXML_HTML_ENABLED
Daniel Veillard33b20b72005-09-12 21:43:20 +0000868 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
869 dtd = xmlGetIntSubset(cur);
870 if (dtd != NULL) {
871 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
872 if (is_xhtml < 0) is_xhtml = 0;
873 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000874 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000875#endif
876 if (cur->children != NULL) {
877 xmlNodePtr child = cur->children;
878
879 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000880 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000881#ifdef LIBXML_HTML_ENABLED
882 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000883 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000884 else
885#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000886 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000887 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000888 child = child->next;
889 }
890 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000891 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000892 cur->encoding = oldenc;
893}
894
895#ifdef LIBXML_HTML_ENABLED
896/************************************************************************
897 * *
898 * Functions specific to XHTML serialization *
899 * *
900 ************************************************************************/
901
902/**
903 * xhtmlIsEmpty:
904 * @node: the node
905 *
906 * Check if a node is an empty xhtml node
907 *
908 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
909 */
910static int
911xhtmlIsEmpty(xmlNodePtr node) {
912 if (node == NULL)
913 return(-1);
914 if (node->type != XML_ELEMENT_NODE)
915 return(0);
916 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
917 return(0);
918 if (node->children != NULL)
919 return(0);
920 switch (node->name[0]) {
921 case 'a':
922 if (xmlStrEqual(node->name, BAD_CAST "area"))
923 return(1);
924 return(0);
925 case 'b':
926 if (xmlStrEqual(node->name, BAD_CAST "br"))
927 return(1);
928 if (xmlStrEqual(node->name, BAD_CAST "base"))
929 return(1);
930 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
931 return(1);
932 return(0);
933 case 'c':
934 if (xmlStrEqual(node->name, BAD_CAST "col"))
935 return(1);
936 return(0);
937 case 'f':
938 if (xmlStrEqual(node->name, BAD_CAST "frame"))
939 return(1);
940 return(0);
941 case 'h':
942 if (xmlStrEqual(node->name, BAD_CAST "hr"))
943 return(1);
944 return(0);
945 case 'i':
946 if (xmlStrEqual(node->name, BAD_CAST "img"))
947 return(1);
948 if (xmlStrEqual(node->name, BAD_CAST "input"))
949 return(1);
950 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
951 return(1);
952 return(0);
953 case 'l':
954 if (xmlStrEqual(node->name, BAD_CAST "link"))
955 return(1);
956 return(0);
957 case 'm':
958 if (xmlStrEqual(node->name, BAD_CAST "meta"))
959 return(1);
960 return(0);
961 case 'p':
962 if (xmlStrEqual(node->name, BAD_CAST "param"))
963 return(1);
964 return(0);
965 }
966 return(0);
967}
968
969/**
970 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000971 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000972 *
973 * Dump a list of XML attributes
974 */
975static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000976xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000977 xmlAttrPtr xml_lang = NULL;
978 xmlAttrPtr lang = NULL;
979 xmlAttrPtr name = NULL;
980 xmlAttrPtr id = NULL;
981 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000982 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000983
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000984 if (cur == NULL) return;
985 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000986 parent = cur->parent;
987 while (cur != NULL) {
988 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
989 id = cur;
990 else
991 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
992 name = cur;
993 else
994 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
995 lang = cur;
996 else
997 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
998 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
999 xml_lang = cur;
1000 else if ((cur->ns == NULL) &&
1001 ((cur->children == NULL) ||
1002 (cur->children->content == NULL) ||
1003 (cur->children->content[0] == 0)) &&
1004 (htmlIsBooleanAttr(cur->name))) {
1005 if (cur->children != NULL)
1006 xmlFreeNode(cur->children);
1007 cur->children = xmlNewText(cur->name);
1008 if (cur->children != NULL)
1009 cur->children->parent = (xmlNodePtr) cur;
1010 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001011 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001012 cur = cur->next;
1013 }
1014 /*
1015 * C.8
1016 */
1017 if ((name != NULL) && (id == NULL)) {
1018 if ((parent != NULL) && (parent->name != NULL) &&
1019 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1020 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1021 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1022 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1023 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1024 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1025 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1026 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1027 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001028 xmlOutputBufferWrite(buf, 5, " id=\"");
1029 xmlAttrSerializeContent(buf, name);
1030 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001031 }
1032 }
1033 /*
1034 * C.7.
1035 */
1036 if ((lang != NULL) && (xml_lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001037 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1038 xmlAttrSerializeContent(buf, lang);
1039 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001040 } else
1041 if ((xml_lang != NULL) && (lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001042 xmlOutputBufferWrite(buf, 7, " lang=\"");
1043 xmlAttrSerializeContent(buf, xml_lang);
1044 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001045 }
1046}
1047
1048/**
1049 * xhtmlNodeListDumpOutput:
1050 * @buf: the XML buffer output
1051 * @doc: the XHTML document
1052 * @cur: the first node
1053 * @level: the imbrication level for indenting
1054 * @format: is formatting allowed
1055 * @encoding: an optional encoding string
1056 *
1057 * Dump an XML node list, recursive behaviour, children are printed too.
1058 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1059 * or xmlKeepBlanksDefault(0) was called
1060 */
1061static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001062xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001063 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001064
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001065 if (cur == NULL) return;
1066 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001067 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001068 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001069 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +00001070 xmlOutputBufferWrite(buf, ctxt->indent_size *
1071 (ctxt->level > ctxt->indent_nr ?
1072 ctxt->indent_nr : ctxt->level),
1073 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001074 xhtmlNodeDumpOutput(ctxt, cur);
1075 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001076 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001077 }
1078 cur = cur->next;
1079 }
1080}
1081
1082/**
1083 * xhtmlNodeDumpOutput:
1084 * @buf: the XML buffer output
1085 * @doc: the XHTML document
1086 * @cur: the current node
1087 * @level: the imbrication level for indenting
1088 * @format: is formatting allowed
1089 * @encoding: an optional encoding string
1090 *
1091 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001092 */
1093static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001094xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Rob Richards31f73022005-08-26 15:33:26 +00001095 int format, addmeta = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001096 xmlNodePtr tmp;
1097 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001098 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001099
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001100 if (cur == NULL) return;
Daniel Veillard60071ae2005-09-12 00:03:43 +00001101 if ((cur->type == XML_DOCUMENT_NODE) ||
1102 (cur->type == XML_HTML_DOCUMENT_NODE)) {
1103 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1104 return;
1105 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001106 if (cur->type == XML_XINCLUDE_START)
1107 return;
1108 if (cur->type == XML_XINCLUDE_END)
1109 return;
1110 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001111 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001112 return;
1113 }
Rob Richards2e2691b2005-10-21 14:45:16 +00001114 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
1115 xhtmlNodeListDumpOutput(ctxt, cur->children);
1116 return;
1117 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001118 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001119 if (cur->type == XML_ELEMENT_DECL) {
1120 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1121 return;
1122 }
1123 if (cur->type == XML_ATTRIBUTE_DECL) {
1124 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1125 return;
1126 }
1127 if (cur->type == XML_ENTITY_DECL) {
1128 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1129 return;
1130 }
1131 if (cur->type == XML_TEXT_NODE) {
1132 if (cur->content != NULL) {
1133 if ((cur->name == xmlStringText) ||
1134 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001135 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001136 } else {
1137 /*
1138 * Disable escaping, needed for XSLT
1139 */
1140 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1141 }
1142 }
1143
1144 return;
1145 }
1146 if (cur->type == XML_PI_NODE) {
1147 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001148 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001149 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1150 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001151 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001152 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1153 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001154 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001155 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001156 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001157 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001158 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001159 }
1160 return;
1161 }
1162 if (cur->type == XML_COMMENT_NODE) {
1163 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001164 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001165 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001166 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001167 }
1168 return;
1169 }
1170 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001171 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001172 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001173 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001174 return;
1175 }
1176 if (cur->type == XML_CDATA_SECTION_NODE) {
1177 start = end = cur->content;
1178 while (*end != '\0') {
1179 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1180 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001181 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001182 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001183 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001184 start = end;
1185 }
1186 end++;
1187 }
1188 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001189 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001190 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001191 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001192 }
1193 return;
1194 }
1195
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001196 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001197 if (format == 1) {
1198 tmp = cur->children;
1199 while (tmp != NULL) {
1200 if ((tmp->type == XML_TEXT_NODE) ||
1201 (tmp->type == XML_ENTITY_REF_NODE)) {
1202 format = 0;
1203 break;
1204 }
1205 tmp = tmp->next;
1206 }
1207 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001208 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001209 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1210 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001211 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001212 }
1213
1214 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1215 if (cur->nsDef)
1216 xmlNsListDumpOutput(buf, cur->nsDef);
1217 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1218 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1219 /*
1220 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1221 */
1222 xmlOutputBufferWriteString(buf,
1223 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1224 }
1225 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001226 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001227
Rob Richards31f73022005-08-26 15:33:26 +00001228 if ((cur->type == XML_ELEMENT_NODE) &&
1229 (cur->parent != NULL) &&
1230 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1231 xmlStrEqual(cur->name, BAD_CAST"head") &&
1232 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1233
1234 tmp = cur->children;
1235 while (tmp != NULL) {
1236 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1237 xmlChar *httpequiv;
1238
1239 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
Rob Richards07b72002005-09-03 14:56:36 +00001240 if (httpequiv != NULL) {
1241 if (xmlStrcasecmp(httpequiv, BAD_CAST"Content-Type") == 0) {
1242 xmlFree(httpequiv);
1243 break;
1244 }
Rob Richards31f73022005-08-26 15:33:26 +00001245 xmlFree(httpequiv);
Rob Richards31f73022005-08-26 15:33:26 +00001246 }
Rob Richards31f73022005-08-26 15:33:26 +00001247 }
1248 tmp = tmp->next;
1249 }
1250 if (tmp == NULL)
1251 addmeta = 1;
1252 }
1253
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001254 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1255 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
Rob Richards31f73022005-08-26 15:33:26 +00001256 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001257 /*
1258 * C.2. Empty Elements
1259 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001260 xmlOutputBufferWrite(buf, 3, " />");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001261 } else {
Rob Richards31f73022005-08-26 15:33:26 +00001262 if (addmeta == 1) {
1263 xmlOutputBufferWrite(buf, 1, ">");
Rob Richards2ce51c02005-09-12 12:16:35 +00001264 if (ctxt->format) {
1265 xmlOutputBufferWrite(buf, 1, "\n");
1266 if (xmlIndentTreeOutput)
1267 xmlOutputBufferWrite(buf, ctxt->indent_size *
1268 (ctxt->level + 1 > ctxt->indent_nr ?
1269 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1270 }
Rob Richards31f73022005-08-26 15:33:26 +00001271 xmlOutputBufferWriteString(buf,
1272 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1273 if (ctxt->encoding) {
1274 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1275 } else {
1276 xmlOutputBufferWrite(buf, 5, "UTF-8");
1277 }
Rob Richards2ce51c02005-09-12 12:16:35 +00001278 xmlOutputBufferWrite(buf, 4, "\" />");
1279 if (ctxt->format)
1280 xmlOutputBufferWrite(buf, 1, "\n");
1281 } else {
1282 xmlOutputBufferWrite(buf, 1, ">");
Rob Richards31f73022005-08-26 15:33:26 +00001283 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001284 /*
1285 * C.3. Element Minimization and Empty Element Content
1286 */
Rob Richards2ce51c02005-09-12 12:16:35 +00001287 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001288 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1289 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001290 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001291 }
1292 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001293 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001294 }
1295 return;
1296 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001297 xmlOutputBufferWrite(buf, 1, ">");
Rob Richards31f73022005-08-26 15:33:26 +00001298 if (addmeta == 1) {
Rob Richards2ce51c02005-09-12 12:16:35 +00001299 if (ctxt->format) {
1300 xmlOutputBufferWrite(buf, 1, "\n");
1301 if (xmlIndentTreeOutput)
1302 xmlOutputBufferWrite(buf, ctxt->indent_size *
1303 (ctxt->level + 1 > ctxt->indent_nr ?
1304 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1305 }
Rob Richards31f73022005-08-26 15:33:26 +00001306 xmlOutputBufferWriteString(buf,
1307 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1308 if (ctxt->encoding) {
1309 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1310 } else {
1311 xmlOutputBufferWrite(buf, 5, "UTF-8");
1312 }
1313 xmlOutputBufferWrite(buf, 4, "\" />");
1314 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001315 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001316 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001317 }
1318
Kasimier T. Buchcik7b4e2e22006-07-13 13:07:11 +00001319#if 0
1320 /*
1321 * This was removed due to problems with HTML processors.
1322 * See bug #345147.
1323 *
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001324 /*
1325 * 4.8. Script and Style elements
1326 */
1327 if ((cur->type == XML_ELEMENT_NODE) &&
1328 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1329 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1330 ((cur->ns == NULL) ||
1331 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1332 xmlNodePtr child = cur->children;
1333
1334 while (child != NULL) {
Daniel Veillarddbd61052005-09-12 14:03:26 +00001335 if (child->type == XML_TEXT_NODE) {
1336 if ((xmlStrchr(child->content, '<') == NULL) &&
1337 (xmlStrchr(child->content, '&') == NULL) &&
1338 (xmlStrstr(child->content, BAD_CAST "]]>") == NULL)) {
1339 /* Nothing to escape, so just output as is... */
1340 /* FIXME: Should we do something about "--" also? */
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001341 int level = ctxt->level;
1342 int indent = ctxt->format;
1343
1344 ctxt->level = 0;
1345 ctxt->format = 0;
Daniel Veillarddbd61052005-09-12 14:03:26 +00001346 xmlOutputBufferWriteString(buf, (const char *) child->content);
1347 /* (We cannot use xhtmlNodeDumpOutput() here because
1348 * we wish to leave '>' unescaped!) */
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001349 ctxt->level = level;
1350 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001351 } else {
Daniel Veillarddbd61052005-09-12 14:03:26 +00001352 /* We must use a CDATA section. Unfortunately,
1353 * this will break CSS and JavaScript when read by
1354 * a browser in HTML4-compliant mode. :-( */
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001355 start = end = child->content;
1356 while (*end != '\0') {
1357 if (*end == ']' &&
1358 *(end + 1) == ']' &&
1359 *(end + 2) == '>') {
1360 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001361 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001362 xmlOutputBufferWrite(buf, end - start,
1363 (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001364 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001365 start = end;
1366 }
1367 end++;
1368 }
1369 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001370 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1371 xmlOutputBufferWrite(buf, end - start,
1372 (const char *)start);
1373 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001374 }
1375 }
1376 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001377 int level = ctxt->level;
1378 int indent = ctxt->format;
1379
1380 ctxt->level = 0;
1381 ctxt->format = 0;
1382 xhtmlNodeDumpOutput(ctxt, child);
1383 ctxt->level = level;
1384 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001385 }
1386 child = child->next;
1387 }
Kasimier T. Buchcik7b4e2e22006-07-13 13:07:11 +00001388 }
1389#endif
1390
1391 if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001392 int indent = ctxt->format;
1393
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001394 if (format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001395 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001396 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001397 xhtmlNodeListDumpOutput(ctxt, cur->children);
1398 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001399 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001400 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001401 xmlOutputBufferWrite(buf, ctxt->indent_size *
1402 (ctxt->level > ctxt->indent_nr ?
1403 ctxt->indent_nr : ctxt->level),
1404 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001405 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001406 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001407 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1408 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001409 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001410 }
1411
1412 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001413 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001414}
1415#endif
1416
1417/************************************************************************
1418 * *
1419 * Public entry points *
1420 * *
1421 ************************************************************************/
1422
1423/**
1424 * xmlSaveToFd:
1425 * @fd: a file descriptor number
1426 * @encoding: the encoding name to use or NULL
1427 * @options: a set of xmlSaveOptions
1428 *
1429 * Create a document saving context serializing to a file descriptor
1430 * with the encoding and the options given.
1431 *
1432 * Returns a new serialization context or NULL in case of error.
1433 */
1434xmlSaveCtxtPtr
1435xmlSaveToFd(int fd, const char *encoding, int options)
1436{
1437 xmlSaveCtxtPtr ret;
1438
1439 ret = xmlNewSaveCtxt(encoding, options);
1440 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001441 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1442 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001443 xmlFreeSaveCtxt(ret);
1444 return(NULL);
1445 }
1446 return(ret);
1447}
1448
1449/**
1450 * xmlSaveToFilename:
1451 * @filename: a file name or an URL
1452 * @encoding: the encoding name to use or NULL
1453 * @options: a set of xmlSaveOptions
1454 *
1455 * Create a document saving context serializing to a filename or possibly
1456 * to an URL (but this is less reliable) with the encoding and the options
1457 * given.
1458 *
1459 * Returns a new serialization context or NULL in case of error.
1460 */
1461xmlSaveCtxtPtr
1462xmlSaveToFilename(const char *filename, const char *encoding, int options)
1463{
1464 xmlSaveCtxtPtr ret;
1465 int compression = 0; /* TODO handle compression option */
1466
1467 ret = xmlNewSaveCtxt(encoding, options);
1468 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001469 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001470 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001471 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001472 xmlFreeSaveCtxt(ret);
1473 return(NULL);
1474 }
1475 return(ret);
1476}
1477
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001478/**
1479 * xmlSaveToBuffer:
1480 * @buffer: a buffer
1481 * @encoding: the encoding name to use or NULL
1482 * @options: a set of xmlSaveOptions
1483 *
1484 * Create a document saving context serializing to a buffer
1485 * with the encoding and the options given
1486 *
1487 * Returns a new serialization context or NULL in case of error.
Daniel Veillard9a00fd22005-11-09 08:56:26 +00001488 */
1489
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001490xmlSaveCtxtPtr
1491xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1492{
Daniel Veillard9a00fd22005-11-09 08:56:26 +00001493 xmlSaveCtxtPtr ret;
1494 xmlOutputBufferPtr out_buff;
1495 xmlCharEncodingHandlerPtr handler;
1496
1497 ret = xmlNewSaveCtxt(encoding, options);
1498 if (ret == NULL) return(NULL);
1499
1500 if (encoding != NULL) {
1501 handler = xmlFindCharEncodingHandler(encoding);
1502 if (handler == NULL) {
1503 xmlFree(ret);
1504 return(NULL);
1505 }
1506 } else
1507 handler = NULL;
1508 out_buff = xmlOutputBufferCreateBuffer(buffer, handler);
1509 if (out_buff == NULL) {
1510 xmlFree(ret);
1511 if (handler) xmlCharEncCloseFunc(handler);
1512 return(NULL);
1513 }
1514
1515 ret->buf = out_buff;
1516 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001517}
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001518
1519/**
1520 * xmlSaveToIO:
1521 * @iowrite: an I/O write function
1522 * @ioclose: an I/O close function
1523 * @ioctx: an I/O handler
1524 * @encoding: the encoding name to use or NULL
1525 * @options: a set of xmlSaveOptions
1526 *
1527 * Create a document saving context serializing to a file descriptor
1528 * with the encoding and the options given
1529 *
1530 * Returns a new serialization context or NULL in case of error.
1531 */
1532xmlSaveCtxtPtr
1533xmlSaveToIO(xmlOutputWriteCallback iowrite,
1534 xmlOutputCloseCallback ioclose,
1535 void *ioctx, const char *encoding, int options)
1536{
1537 xmlSaveCtxtPtr ret;
1538
1539 ret = xmlNewSaveCtxt(encoding, options);
1540 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001541 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1542 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001543 xmlFreeSaveCtxt(ret);
1544 return(NULL);
1545 }
1546 return(ret);
1547}
1548
1549/**
1550 * xmlSaveDoc:
1551 * @ctxt: a document saving context
1552 * @doc: a document
1553 *
1554 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001555 * TODO: The function is not fully implemented yet as it does not return the
1556 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001557 *
1558 * Returns the number of byte written or -1 in case of error
1559 */
1560long
1561xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1562{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001563 long ret = 0;
1564
Daniel Veillardce682bc2004-11-05 17:22:25 +00001565 if ((ctxt == NULL) || (doc == NULL)) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001566 xmlDocContentDumpOutput(ctxt, doc);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001567 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001568}
1569
1570/**
1571 * xmlSaveTree:
1572 * @ctxt: a document saving context
1573 * @node: a document
1574 *
1575 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001576 * TODO: The function is not fully implemented yet as it does not return the
1577 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001578 *
1579 * Returns the number of byte written or -1 in case of error
1580 */
1581long
1582xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1583{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001584 long ret = 0;
1585
Daniel Veillardce682bc2004-11-05 17:22:25 +00001586 if ((ctxt == NULL) || (node == NULL)) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001587 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001588 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001589}
1590
1591/**
1592 * xmlSaveFlush:
1593 * @ctxt: a document saving context
1594 *
1595 * Flush a document saving context, i.e. make sure that all bytes have
1596 * been output.
1597 *
1598 * Returns the number of byte written or -1 in case of error.
1599 */
1600int
1601xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1602{
1603 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001604 if (ctxt->buf == NULL) return(-1);
1605 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001606}
1607
1608/**
1609 * xmlSaveClose:
1610 * @ctxt: a document saving context
1611 *
1612 * Close a document saving context, i.e. make sure that all bytes have
1613 * been output and free the associated data.
1614 *
1615 * Returns the number of byte written or -1 in case of error.
1616 */
1617int
1618xmlSaveClose(xmlSaveCtxtPtr ctxt)
1619{
1620 int ret;
1621
1622 if (ctxt == NULL) return(-1);
1623 ret = xmlSaveFlush(ctxt);
1624 xmlFreeSaveCtxt(ctxt);
1625 return(ret);
1626}
1627
Daniel Veillard3995bc32004-05-15 18:57:31 +00001628/**
1629 * xmlSaveSetEscape:
1630 * @ctxt: a document saving context
1631 * @escape: the escaping function
1632 *
1633 * Set a custom escaping function to be used for text in element content
1634 *
1635 * Returns 0 if successful or -1 in case of error.
1636 */
1637int
1638xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1639{
1640 if (ctxt == NULL) return(-1);
1641 ctxt->escape = escape;
1642 return(0);
1643}
1644
1645/**
1646 * xmlSaveSetAttrEscape:
1647 * @ctxt: a document saving context
1648 * @escape: the escaping function
1649 *
1650 * Set a custom escaping function to be used for text in attribute content
1651 *
1652 * Returns 0 if successful or -1 in case of error.
1653 */
1654int
1655xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1656{
1657 if (ctxt == NULL) return(-1);
1658 ctxt->escapeAttr = escape;
1659 return(0);
1660}
1661
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001662/************************************************************************
1663 * *
1664 * Public entry points based on buffers *
1665 * *
1666 ************************************************************************/
1667/**
1668 * xmlAttrSerializeTxtContent:
1669 * @buf: the XML buffer output
1670 * @doc: the document
1671 * @attr: the attribute node
1672 * @string: the text content
1673 *
1674 * Serialize text attribute values to an xml simple buffer
1675 */
1676void
1677xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001678 xmlAttrPtr attr, const xmlChar * string)
1679{
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001680 xmlChar *base, *cur;
1681
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001682 if (string == NULL)
1683 return;
1684 base = cur = (xmlChar *) string;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001685 while (*cur != 0) {
1686 if (*cur == '\n') {
1687 if (base != cur)
1688 xmlBufferAdd(buf, base, cur - base);
1689 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1690 cur++;
1691 base = cur;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001692 } else if (*cur == '\r') {
1693 if (base != cur)
1694 xmlBufferAdd(buf, base, cur - base);
1695 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1696 cur++;
1697 base = cur;
1698 } else if (*cur == '\t') {
1699 if (base != cur)
1700 xmlBufferAdd(buf, base, cur - base);
1701 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1702 cur++;
1703 base = cur;
1704 } else if (*cur == '"') {
1705 if (base != cur)
1706 xmlBufferAdd(buf, base, cur - base);
1707 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1708 cur++;
1709 base = cur;
1710 } else if (*cur == '<') {
1711 if (base != cur)
1712 xmlBufferAdd(buf, base, cur - base);
1713 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1714 cur++;
1715 base = cur;
1716 } else if (*cur == '>') {
1717 if (base != cur)
1718 xmlBufferAdd(buf, base, cur - base);
1719 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1720 cur++;
1721 base = cur;
1722 } else if (*cur == '&') {
1723 if (base != cur)
1724 xmlBufferAdd(buf, base, cur - base);
1725 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1726 cur++;
1727 base = cur;
1728 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1729 (doc->encoding == NULL))) {
1730 /*
1731 * We assume we have UTF-8 content.
1732 */
1733 unsigned char tmp[10];
1734 int val = 0, l = 1;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001735
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001736 if (base != cur)
1737 xmlBufferAdd(buf, base, cur - base);
1738 if (*cur < 0xC0) {
1739 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1740 if (doc != NULL)
1741 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1742 xmlSerializeHexCharRef(tmp, *cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001743 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001744 cur++;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001745 base = cur;
1746 continue;
1747 } else if (*cur < 0xE0) {
1748 val = (cur[0]) & 0x1F;
1749 val <<= 6;
1750 val |= (cur[1]) & 0x3F;
1751 l = 2;
1752 } else if (*cur < 0xF0) {
1753 val = (cur[0]) & 0x0F;
1754 val <<= 6;
1755 val |= (cur[1]) & 0x3F;
1756 val <<= 6;
1757 val |= (cur[2]) & 0x3F;
1758 l = 3;
1759 } else if (*cur < 0xF8) {
1760 val = (cur[0]) & 0x07;
1761 val <<= 6;
1762 val |= (cur[1]) & 0x3F;
1763 val <<= 6;
1764 val |= (cur[2]) & 0x3F;
1765 val <<= 6;
1766 val |= (cur[3]) & 0x3F;
1767 l = 4;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001768 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001769 if ((l == 1) || (!IS_CHAR(val))) {
1770 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1771 if (doc != NULL)
1772 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1773
1774 xmlSerializeHexCharRef(tmp, *cur);
1775 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1776 cur++;
1777 base = cur;
1778 continue;
1779 }
1780 /*
1781 * We could do multiple things here. Just save
1782 * as a char ref
1783 */
1784 xmlSerializeHexCharRef(tmp, val);
1785 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1786 cur += l;
1787 base = cur;
1788 } else {
1789 cur++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001790 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001791 }
1792 if (base != cur)
1793 xmlBufferAdd(buf, base, cur - base);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001794}
1795
1796/**
1797 * xmlNodeDump:
1798 * @buf: the XML buffer output
1799 * @doc: the document
1800 * @cur: the current node
1801 * @level: the imbrication level for indenting
1802 * @format: is formatting allowed
1803 *
1804 * Dump an XML node, recursive behaviour,children are printed too.
1805 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1806 * or xmlKeepBlanksDefault(0) was called
1807 *
1808 * Returns the number of bytes written to the buffer or -1 in case of error
1809 */
1810int
1811xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1812 int format)
1813{
1814 unsigned int use;
1815 int ret;
1816 xmlOutputBufferPtr outbuf;
1817
1818 xmlInitParser();
1819
1820 if (cur == NULL) {
1821#ifdef DEBUG_TREE
1822 xmlGenericError(xmlGenericErrorContext,
1823 "xmlNodeDump : node == NULL\n");
1824#endif
1825 return (-1);
1826 }
1827 if (buf == NULL) {
1828#ifdef DEBUG_TREE
1829 xmlGenericError(xmlGenericErrorContext,
1830 "xmlNodeDump : buf == NULL\n");
1831#endif
1832 return (-1);
1833 }
1834 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1835 if (outbuf == NULL) {
1836 xmlSaveErrMemory("creating buffer");
1837 return (-1);
1838 }
1839 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1840 outbuf->buffer = buf;
1841 outbuf->encoder = NULL;
1842 outbuf->writecallback = NULL;
1843 outbuf->closecallback = NULL;
1844 outbuf->context = NULL;
1845 outbuf->written = 0;
1846
1847 use = buf->use;
1848 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1849 xmlFree(outbuf);
1850 ret = buf->use - use;
1851 return (ret);
1852}
1853
1854/**
1855 * xmlElemDump:
1856 * @f: the FILE * for the output
1857 * @doc: the document
1858 * @cur: the current node
1859 *
1860 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1861 */
1862void
1863xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1864{
1865 xmlOutputBufferPtr outbuf;
1866
1867 xmlInitParser();
1868
1869 if (cur == NULL) {
1870#ifdef DEBUG_TREE
1871 xmlGenericError(xmlGenericErrorContext,
1872 "xmlElemDump : cur == NULL\n");
1873#endif
1874 return;
1875 }
1876#ifdef DEBUG_TREE
1877 if (doc == NULL) {
1878 xmlGenericError(xmlGenericErrorContext,
1879 "xmlElemDump : doc == NULL\n");
1880 }
1881#endif
1882
1883 outbuf = xmlOutputBufferCreateFile(f, NULL);
1884 if (outbuf == NULL)
1885 return;
1886 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1887#ifdef LIBXML_HTML_ENABLED
1888 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1889#else
1890 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1891#endif /* LIBXML_HTML_ENABLED */
1892 } else
1893 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1894 xmlOutputBufferClose(outbuf);
1895}
1896
1897/************************************************************************
1898 * *
1899 * Saving functions front-ends *
1900 * *
1901 ************************************************************************/
1902
1903/**
1904 * xmlNodeDumpOutput:
1905 * @buf: the XML buffer output
1906 * @doc: the document
1907 * @cur: the current node
1908 * @level: the imbrication level for indenting
1909 * @format: is formatting allowed
1910 * @encoding: an optional encoding string
1911 *
1912 * Dump an XML node, recursive behaviour, children are printed too.
1913 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1914 * or xmlKeepBlanksDefault(0) was called
1915 */
1916void
1917xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1918 int level, int format, const char *encoding)
1919{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001920 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001921#ifdef LIBXML_HTML_ENABLED
1922 xmlDtdPtr dtd;
1923 int is_xhtml = 0;
1924#endif
1925
1926 xmlInitParser();
1927
Daniel Veillardce244ad2004-11-05 10:03:46 +00001928 if ((buf == NULL) || (cur == NULL)) return;
1929
Daniel Veillard64354ea2005-03-31 15:22:56 +00001930 if (encoding == NULL)
1931 encoding = "UTF-8";
1932
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001933 memset(&ctxt, 0, sizeof(ctxt));
1934 ctxt.doc = doc;
1935 ctxt.buf = buf;
1936 ctxt.level = level;
1937 ctxt.format = format;
1938 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001939 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001940
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001941#ifdef LIBXML_HTML_ENABLED
1942 dtd = xmlGetIntSubset(doc);
1943 if (dtd != NULL) {
Daniel Veillard33b20b72005-09-12 21:43:20 +00001944 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1945 if (is_xhtml < 0)
1946 is_xhtml = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001947 }
1948
1949 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001950 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001951 else
1952#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001953 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001954}
1955
1956/**
1957 * xmlDocDumpFormatMemoryEnc:
1958 * @out_doc: Document to generate XML text from
1959 * @doc_txt_ptr: Memory pointer for allocated XML text
1960 * @doc_txt_len: Length of the generated XML text
1961 * @txt_encoding: Character encoding to use when generating XML text
1962 * @format: should formatting spaces been added
1963 *
1964 * Dump the current DOM tree into memory using the character encoding specified
1965 * by the caller. Note it is up to the caller of this function to free the
1966 * allocated memory with xmlFree().
1967 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1968 * or xmlKeepBlanksDefault(0) was called
1969 */
1970
1971void
1972xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1973 int * doc_txt_len, const char * txt_encoding,
1974 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001975 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001976 int dummy = 0;
1977 xmlOutputBufferPtr out_buff = NULL;
1978 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1979
1980 if (doc_txt_len == NULL) {
1981 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1982 }
1983
1984 if (doc_txt_ptr == NULL) {
1985 *doc_txt_len = 0;
1986 return;
1987 }
1988
1989 *doc_txt_ptr = NULL;
1990 *doc_txt_len = 0;
1991
1992 if (out_doc == NULL) {
1993 /* No document, no output */
1994 return;
1995 }
1996
1997 /*
1998 * Validate the encoding value, if provided.
1999 * This logic is copied from xmlSaveFileEnc.
2000 */
2001
2002 if (txt_encoding == NULL)
2003 txt_encoding = (const char *) out_doc->encoding;
2004 if (txt_encoding != NULL) {
2005 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2006 if ( conv_hdlr == NULL ) {
2007 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2008 txt_encoding);
2009 return;
2010 }
2011 }
2012
2013 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2014 xmlSaveErrMemory("creating buffer");
2015 return;
2016 }
2017
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002018 memset(&ctxt, 0, sizeof(ctxt));
2019 ctxt.doc = out_doc;
2020 ctxt.buf = out_buff;
2021 ctxt.level = 0;
2022 ctxt.format = format;
2023 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002024 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002025 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002026 xmlOutputBufferFlush(out_buff);
2027 if (out_buff->conv != NULL) {
2028 *doc_txt_len = out_buff->conv->use;
2029 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
2030 } else {
2031 *doc_txt_len = out_buff->buffer->use;
2032 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
2033 }
2034 (void)xmlOutputBufferClose(out_buff);
2035
2036 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2037 *doc_txt_len = 0;
2038 xmlSaveErrMemory("creating output");
2039 }
2040
2041 return;
2042}
2043
2044/**
2045 * xmlDocDumpMemory:
2046 * @cur: the document
2047 * @mem: OUT: the memory pointer
2048 * @size: OUT: the memory length
2049 *
2050 * Dump an XML document in memory and return the #xmlChar * and it's size
2051 * in bytes. It's up to the caller to free the memory with xmlFree().
2052 * The resulting byte array is zero terminated, though the last 0 is not
2053 * included in the returned size.
2054 */
2055void
2056xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2057 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2058}
2059
2060/**
2061 * xmlDocDumpFormatMemory:
2062 * @cur: the document
2063 * @mem: OUT: the memory pointer
2064 * @size: OUT: the memory length
2065 * @format: should formatting spaces been added
2066 *
2067 *
2068 * Dump an XML document in memory and return the #xmlChar * and it's size.
2069 * It's up to the caller to free the memory with xmlFree().
2070 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2071 * or xmlKeepBlanksDefault(0) was called
2072 */
2073void
2074xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2075 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2076}
2077
2078/**
2079 * xmlDocDumpMemoryEnc:
2080 * @out_doc: Document to generate XML text from
2081 * @doc_txt_ptr: Memory pointer for allocated XML text
2082 * @doc_txt_len: Length of the generated XML text
2083 * @txt_encoding: Character encoding to use when generating XML text
2084 *
2085 * Dump the current DOM tree into memory using the character encoding specified
2086 * by the caller. Note it is up to the caller of this function to free the
2087 * allocated memory with xmlFree().
2088 */
2089
2090void
2091xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2092 int * doc_txt_len, const char * txt_encoding) {
2093 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2094 txt_encoding, 0);
2095}
2096
2097/**
2098 * xmlDocFormatDump:
2099 * @f: the FILE*
2100 * @cur: the document
2101 * @format: should formatting spaces been added
2102 *
2103 * Dump an XML document to an open FILE.
2104 *
2105 * returns: the number of bytes written or -1 in case of failure.
2106 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2107 * or xmlKeepBlanksDefault(0) was called
2108 */
2109int
2110xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002111 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002112 xmlOutputBufferPtr buf;
2113 const char * encoding;
2114 xmlCharEncodingHandlerPtr handler = NULL;
2115 int ret;
2116
2117 if (cur == NULL) {
2118#ifdef DEBUG_TREE
2119 xmlGenericError(xmlGenericErrorContext,
2120 "xmlDocDump : document == NULL\n");
2121#endif
2122 return(-1);
2123 }
2124 encoding = (const char *) cur->encoding;
2125
2126 if (encoding != NULL) {
2127 handler = xmlFindCharEncodingHandler(encoding);
2128 if (handler == NULL) {
2129 xmlFree((char *) cur->encoding);
2130 cur->encoding = NULL;
2131 }
2132 }
2133 buf = xmlOutputBufferCreateFile(f, handler);
2134 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002135 memset(&ctxt, 0, sizeof(ctxt));
2136 ctxt.doc = cur;
2137 ctxt.buf = buf;
2138 ctxt.level = 0;
2139 ctxt.format = format;
2140 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002141 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002142 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002143
2144 ret = xmlOutputBufferClose(buf);
2145 return(ret);
2146}
2147
2148/**
2149 * xmlDocDump:
2150 * @f: the FILE*
2151 * @cur: the document
2152 *
2153 * Dump an XML document to an open FILE.
2154 *
2155 * returns: the number of bytes written or -1 in case of failure.
2156 */
2157int
2158xmlDocDump(FILE *f, xmlDocPtr cur) {
2159 return(xmlDocFormatDump (f, cur, 0));
2160}
2161
2162/**
2163 * xmlSaveFileTo:
2164 * @buf: an output I/O buffer
2165 * @cur: the document
2166 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2167 *
2168 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002169 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2170 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002171 *
2172 * returns: the number of bytes written or -1 in case of failure.
2173 */
2174int
2175xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002176 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002177 int ret;
2178
Daniel Veillard3d97e662004-11-04 10:49:00 +00002179 if (buf == NULL) return(-1);
2180 if (cur == NULL) {
2181 xmlOutputBufferClose(buf);
2182 return(-1);
2183 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002184 memset(&ctxt, 0, sizeof(ctxt));
2185 ctxt.doc = cur;
2186 ctxt.buf = buf;
2187 ctxt.level = 0;
2188 ctxt.format = 0;
2189 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002190 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002191 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002192 ret = xmlOutputBufferClose(buf);
2193 return(ret);
2194}
2195
2196/**
2197 * xmlSaveFormatFileTo:
2198 * @buf: an output I/O buffer
2199 * @cur: the document
2200 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2201 * @format: should formatting spaces been added
2202 *
2203 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002204 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2205 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002206 *
2207 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002208 */
2209int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002210xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2211 const char *encoding, int format)
2212{
2213 xmlSaveCtxt ctxt;
2214 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002215
Daniel Veillard3d97e662004-11-04 10:49:00 +00002216 if (buf == NULL) return(-1);
Daniel Veillardce244ad2004-11-05 10:03:46 +00002217 if ((cur == NULL) ||
2218 ((cur->type != XML_DOCUMENT_NODE) &&
2219 (cur->type != XML_HTML_DOCUMENT_NODE))) {
Daniel Veillard3d97e662004-11-04 10:49:00 +00002220 xmlOutputBufferClose(buf);
2221 return(-1);
2222 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002223 memset(&ctxt, 0, sizeof(ctxt));
2224 ctxt.doc = cur;
2225 ctxt.buf = buf;
2226 ctxt.level = 0;
2227 ctxt.format = format;
2228 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002229 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002230 xmlDocContentDumpOutput(&ctxt, cur);
2231 ret = xmlOutputBufferClose(buf);
2232 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002233}
2234
2235/**
2236 * xmlSaveFormatFileEnc:
2237 * @filename: the filename or URL to output
2238 * @cur: the document being saved
2239 * @encoding: the name of the encoding to use or NULL.
2240 * @format: should formatting spaces be added.
2241 *
2242 * Dump an XML document to a file or an URL.
2243 *
2244 * Returns the number of bytes written or -1 in case of error.
2245 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2246 * or xmlKeepBlanksDefault(0) was called
2247 */
2248int
2249xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2250 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002251 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002252 xmlOutputBufferPtr buf;
2253 xmlCharEncodingHandlerPtr handler = NULL;
2254 int ret;
2255
2256 if (cur == NULL)
2257 return(-1);
2258
2259 if (encoding == NULL)
2260 encoding = (const char *) cur->encoding;
2261
2262 if (encoding != NULL) {
2263
2264 handler = xmlFindCharEncodingHandler(encoding);
2265 if (handler == NULL)
2266 return(-1);
2267 }
2268
2269#ifdef HAVE_ZLIB_H
2270 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2271#endif
2272 /*
2273 * save the content to a temp buffer.
2274 */
2275 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2276 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002277 memset(&ctxt, 0, sizeof(ctxt));
2278 ctxt.doc = cur;
2279 ctxt.buf = buf;
2280 ctxt.level = 0;
2281 ctxt.format = format;
2282 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002283 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002284
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002285 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002286
2287 ret = xmlOutputBufferClose(buf);
2288 return(ret);
2289}
2290
2291
2292/**
2293 * xmlSaveFileEnc:
2294 * @filename: the filename (or URL)
2295 * @cur: the document
2296 * @encoding: the name of an encoding (or NULL)
2297 *
2298 * Dump an XML document, converting it to the given encoding
2299 *
2300 * returns: the number of bytes written or -1 in case of failure.
2301 */
2302int
2303xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2304 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2305}
2306
2307/**
2308 * xmlSaveFormatFile:
2309 * @filename: the filename (or URL)
2310 * @cur: the document
2311 * @format: should formatting spaces been added
2312 *
2313 * Dump an XML document to a file. Will use compression if
2314 * compiled in and enabled. If @filename is "-" the stdout file is
2315 * used. If @format is set then the document will be indented on output.
2316 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2317 * or xmlKeepBlanksDefault(0) was called
2318 *
2319 * returns: the number of bytes written or -1 in case of failure.
2320 */
2321int
2322xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2323 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2324}
2325
2326/**
2327 * xmlSaveFile:
2328 * @filename: the filename (or URL)
2329 * @cur: the document
2330 *
2331 * Dump an XML document to a file. Will use compression if
2332 * compiled in and enabled. If @filename is "-" the stdout file is
2333 * used.
2334 * returns: the number of bytes written or -1 in case of failure.
2335 */
2336int
2337xmlSaveFile(const char *filename, xmlDocPtr cur) {
2338 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2339}
2340
2341#endif /* LIBXML_OUTPUT_ENABLED */
2342
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002343#define bottom_xmlsave
2344#include "elfgcchack.h"