blob: cbabd786aa083a2a5850eac55b71f3d39c65c7ae [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 Veillarddab39b52006-10-16 23:22:10 +0000395 ret->escape = NULL;
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 Veillarddab39b52006-10-16 23:22:10 +0000467static int 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 Veillardbd444842007-03-20 08:47:29 +0000626 ((cur->type == XML_ELEMENT_NODE) ||
627 (cur->type == XML_COMMENT_NODE) ||
628 (cur->type == XML_PI_NODE)))
Daniel Veillard753086a2004-03-28 16:12:44 +0000629 xmlOutputBufferWrite(buf, ctxt->indent_size *
630 (ctxt->level > ctxt->indent_nr ?
631 ctxt->indent_nr : ctxt->level),
632 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000633 xmlNodeDumpOutputInternal(ctxt, cur);
634 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000635 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000636 }
637 cur = cur->next;
638 }
639}
640
641/**
642 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000643 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000644 *
645 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000646 */
647static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000648xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000649 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000650 xmlNodePtr tmp;
651 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000652 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000653
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000654 if (cur == NULL) return;
655 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000656 if (cur->type == XML_XINCLUDE_START)
657 return;
658 if (cur->type == XML_XINCLUDE_END)
659 return;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000660 if ((cur->type == XML_DOCUMENT_NODE) ||
661 (cur->type == XML_HTML_DOCUMENT_NODE)) {
662 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
663 return;
664 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000665 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000666 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000667 return;
668 }
669 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000670 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000671 return;
672 }
673 if (cur->type == XML_ELEMENT_DECL) {
674 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
675 return;
676 }
677 if (cur->type == XML_ATTRIBUTE_DECL) {
678 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
679 return;
680 }
681 if (cur->type == XML_ENTITY_DECL) {
682 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
683 return;
684 }
685 if (cur->type == XML_TEXT_NODE) {
686 if (cur->content != NULL) {
William M. Brack4e1c2db2005-02-11 10:58:55 +0000687 if (cur->name != xmlStringTextNoenc) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000688 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000689 } else {
690 /*
691 * Disable escaping, needed for XSLT
692 */
693 xmlOutputBufferWriteString(buf, (const char *) cur->content);
694 }
695 }
696
697 return;
698 }
699 if (cur->type == XML_PI_NODE) {
700 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000701 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000702 xmlOutputBufferWriteString(buf, (const char *)cur->name);
703 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000704 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000705 xmlOutputBufferWriteString(buf, (const char *)cur->content);
706 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000707 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000708 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000709 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000710 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000711 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000712 }
713 return;
714 }
715 if (cur->type == XML_COMMENT_NODE) {
716 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000717 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000718 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000719 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000720 }
721 return;
722 }
723 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000724 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000725 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000726 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000727 return;
728 }
729 if (cur->type == XML_CDATA_SECTION_NODE) {
Daniel Veillard7cd517c2005-05-20 18:47:22 +0000730 if (cur->content == NULL) {
731 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
732 } else {
733 start = end = cur->content;
734 while (*end != '\0') {
735 if ((*end == ']') && (*(end + 1) == ']') &&
736 (*(end + 2) == '>')) {
737 end = end + 2;
738 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
739 xmlOutputBufferWrite(buf, end - start, (const char *)start);
740 xmlOutputBufferWrite(buf, 3, "]]>");
741 start = end;
742 }
743 end++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000744 }
Daniel Veillard7cd517c2005-05-20 18:47:22 +0000745 if (start != end) {
746 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
747 xmlOutputBufferWriteString(buf, (const char *)start);
748 xmlOutputBufferWrite(buf, 3, "]]>");
749 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000750 }
751 return;
752 }
753 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000754 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000755 return;
756 }
757 if (cur->type == XML_NAMESPACE_DECL) {
758 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
759 return;
760 }
761
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000762 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000763 if (format == 1) {
764 tmp = cur->children;
765 while (tmp != NULL) {
766 if ((tmp->type == XML_TEXT_NODE) ||
767 (tmp->type == XML_CDATA_SECTION_NODE) ||
768 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000769 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000770 break;
771 }
772 tmp = tmp->next;
773 }
774 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000775 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000776 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
777 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000778 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000779 }
780
781 xmlOutputBufferWriteString(buf, (const char *)cur->name);
782 if (cur->nsDef)
783 xmlNsListDumpOutput(buf, cur->nsDef);
784 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000785 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000786
787 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
Rob Richards2ce51c02005-09-12 12:16:35 +0000788 (cur->children == NULL) && ((ctxt->options & XML_SAVE_NO_EMPTY) == 0)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000789 xmlOutputBufferWrite(buf, 2, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000790 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000791 return;
792 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000793 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000794 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000795 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000796 }
797 if (cur->children != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000798 if (ctxt->format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000799 if (ctxt->level >= 0) ctxt->level++;
800 xmlNodeListDumpOutput(ctxt, cur->children);
801 if (ctxt->level > 0) ctxt->level--;
802 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000803 xmlOutputBufferWrite(buf, ctxt->indent_size *
804 (ctxt->level > ctxt->indent_nr ?
805 ctxt->indent_nr : ctxt->level),
806 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000807 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000808 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000809 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
810 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000811 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000812 }
813
814 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000815 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000816 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000817}
818
819/**
820 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000821 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000822 *
823 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000824 */
Daniel Veillarddab39b52006-10-16 23:22:10 +0000825static int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000826xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000827#ifdef LIBXML_HTML_ENABLED
828 xmlDtdPtr dtd;
829 int is_xhtml = 0;
830#endif
831 const xmlChar *oldenc = cur->encoding;
Daniel Veillarddab39b52006-10-16 23:22:10 +0000832 const xmlChar *oldctxtenc = ctxt->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000833 const xmlChar *encoding = ctxt->encoding;
Daniel Veillarddab39b52006-10-16 23:22:10 +0000834 xmlCharEncodingOutputFunc oldescape = ctxt->escape;
835 xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
836 xmlOutputBufferPtr buf = ctxt->buf;
Daniel Veillarddab39b52006-10-16 23:22:10 +0000837 xmlCharEncoding enc;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000838
839 xmlInitParser();
840
Daniel Veillarddab39b52006-10-16 23:22:10 +0000841 if (ctxt->encoding != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000842 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillarddab39b52006-10-16 23:22:10 +0000843 } else if (cur->encoding != NULL) {
844 encoding = cur->encoding;
845 } else if (cur->charset != XML_CHAR_ENCODING_UTF8) {
846 encoding = (const xmlChar *)
847 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
848 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000849
Daniel Veillarddab39b52006-10-16 23:22:10 +0000850 enc = xmlParseCharEncoding((const char*) encoding);
851 if ((encoding != NULL) && (oldctxtenc == NULL) &&
852 (buf->encoder == NULL) && (buf->conv == NULL) &&
853 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
854 if ((enc != XML_CHAR_ENCODING_UTF8) &&
855 (enc != XML_CHAR_ENCODING_NONE) &&
856 (enc != XML_CHAR_ENCODING_ASCII)) {
857 /*
858 * we need to switch to this encoding but just for this document
859 * since we output the XMLDecl the conversion must be done to not
860 * generate not well formed documents.
861 */
862 buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
863 if (buf->encoder == NULL) {
864 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
865 (const char *)encoding);
866 return(-1);
867 }
868 buf->conv = xmlBufferCreate();
869 if (buf->conv == NULL) {
870 xmlCharEncCloseFunc(buf->encoder);
871 xmlSaveErrMemory("creating encoding buffer");
872 return(-1);
873 }
874 /*
875 * initialize the state, e.g. if outputting a BOM
876 */
877 xmlCharEncOutFunc(buf->encoder, buf->conv, NULL);
878 }
879 if (ctxt->escape == xmlEscapeEntities)
880 ctxt->escape = NULL;
881 if (ctxt->escapeAttr == xmlEscapeEntities)
882 ctxt->escapeAttr = NULL;
883 }
884
885
886 /*
887 * Save the XML declaration
888 */
Daniel Veillard100e1802005-08-08 14:44:11 +0000889 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
890 xmlOutputBufferWrite(buf, 14, "<?xml version=");
891 if (cur->version != NULL)
892 xmlBufferWriteQuotedString(buf->buffer, cur->version);
893 else
894 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
Daniel Veillard100e1802005-08-08 14:44:11 +0000895 if (encoding != NULL) {
896 xmlOutputBufferWrite(buf, 10, " encoding=");
897 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
898 }
899 switch (cur->standalone) {
900 case 0:
901 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
902 break;
903 case 1:
904 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
905 break;
906 }
907 xmlOutputBufferWrite(buf, 3, "?>\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000908 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000909
910#ifdef LIBXML_HTML_ENABLED
Daniel Veillard33b20b72005-09-12 21:43:20 +0000911 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
912 dtd = xmlGetIntSubset(cur);
913 if (dtd != NULL) {
914 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
915 if (is_xhtml < 0) is_xhtml = 0;
916 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000917 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000918#endif
919 if (cur->children != NULL) {
920 xmlNodePtr child = cur->children;
921
922 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000923 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000924#ifdef LIBXML_HTML_ENABLED
925 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000926 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000927 else
928#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000929 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000930 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000931 child = child->next;
932 }
933 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000934 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000935 cur->encoding = oldenc;
Daniel Veillarddab39b52006-10-16 23:22:10 +0000936
937 /*
938 * Restore the state of the saving context at the end of the document
939 */
940 if ((encoding != NULL) && (oldctxtenc == NULL) &&
941 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
942 if ((enc != XML_CHAR_ENCODING_UTF8) &&
943 (enc != XML_CHAR_ENCODING_NONE) &&
944 (enc != XML_CHAR_ENCODING_ASCII)) {
945 xmlOutputBufferFlush(buf);
946 xmlCharEncCloseFunc(buf->encoder);
947 xmlBufferFree(buf->conv);
948 buf->encoder = NULL;
949 buf->conv = NULL;
950 }
951 ctxt->escape = oldescape;
952 ctxt->escapeAttr = oldescapeAttr;
953 }
954 return(0);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000955}
956
957#ifdef LIBXML_HTML_ENABLED
958/************************************************************************
959 * *
960 * Functions specific to XHTML serialization *
961 * *
962 ************************************************************************/
963
964/**
965 * xhtmlIsEmpty:
966 * @node: the node
967 *
968 * Check if a node is an empty xhtml node
969 *
970 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
971 */
972static int
973xhtmlIsEmpty(xmlNodePtr node) {
974 if (node == NULL)
975 return(-1);
976 if (node->type != XML_ELEMENT_NODE)
977 return(0);
978 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
979 return(0);
980 if (node->children != NULL)
981 return(0);
982 switch (node->name[0]) {
983 case 'a':
984 if (xmlStrEqual(node->name, BAD_CAST "area"))
985 return(1);
986 return(0);
987 case 'b':
988 if (xmlStrEqual(node->name, BAD_CAST "br"))
989 return(1);
990 if (xmlStrEqual(node->name, BAD_CAST "base"))
991 return(1);
992 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
993 return(1);
994 return(0);
995 case 'c':
996 if (xmlStrEqual(node->name, BAD_CAST "col"))
997 return(1);
998 return(0);
999 case 'f':
1000 if (xmlStrEqual(node->name, BAD_CAST "frame"))
1001 return(1);
1002 return(0);
1003 case 'h':
1004 if (xmlStrEqual(node->name, BAD_CAST "hr"))
1005 return(1);
1006 return(0);
1007 case 'i':
1008 if (xmlStrEqual(node->name, BAD_CAST "img"))
1009 return(1);
1010 if (xmlStrEqual(node->name, BAD_CAST "input"))
1011 return(1);
1012 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1013 return(1);
1014 return(0);
1015 case 'l':
1016 if (xmlStrEqual(node->name, BAD_CAST "link"))
1017 return(1);
1018 return(0);
1019 case 'm':
1020 if (xmlStrEqual(node->name, BAD_CAST "meta"))
1021 return(1);
1022 return(0);
1023 case 'p':
1024 if (xmlStrEqual(node->name, BAD_CAST "param"))
1025 return(1);
1026 return(0);
1027 }
1028 return(0);
1029}
1030
1031/**
1032 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001033 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001034 *
1035 * Dump a list of XML attributes
1036 */
1037static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001038xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001039 xmlAttrPtr xml_lang = NULL;
1040 xmlAttrPtr lang = NULL;
1041 xmlAttrPtr name = NULL;
1042 xmlAttrPtr id = NULL;
1043 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001044 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001045
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001046 if (cur == NULL) return;
1047 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001048 parent = cur->parent;
1049 while (cur != NULL) {
1050 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1051 id = cur;
1052 else
1053 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1054 name = cur;
1055 else
1056 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1057 lang = cur;
1058 else
1059 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1060 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1061 xml_lang = cur;
1062 else if ((cur->ns == NULL) &&
1063 ((cur->children == NULL) ||
1064 (cur->children->content == NULL) ||
1065 (cur->children->content[0] == 0)) &&
1066 (htmlIsBooleanAttr(cur->name))) {
1067 if (cur->children != NULL)
1068 xmlFreeNode(cur->children);
1069 cur->children = xmlNewText(cur->name);
1070 if (cur->children != NULL)
1071 cur->children->parent = (xmlNodePtr) cur;
1072 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001073 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001074 cur = cur->next;
1075 }
1076 /*
1077 * C.8
1078 */
1079 if ((name != NULL) && (id == NULL)) {
1080 if ((parent != NULL) && (parent->name != NULL) &&
1081 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1082 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1083 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1084 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1085 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1086 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1087 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1088 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1089 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001090 xmlOutputBufferWrite(buf, 5, " id=\"");
1091 xmlAttrSerializeContent(buf, name);
1092 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001093 }
1094 }
1095 /*
1096 * C.7.
1097 */
1098 if ((lang != NULL) && (xml_lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001099 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1100 xmlAttrSerializeContent(buf, lang);
1101 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001102 } else
1103 if ((xml_lang != NULL) && (lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001104 xmlOutputBufferWrite(buf, 7, " lang=\"");
1105 xmlAttrSerializeContent(buf, xml_lang);
1106 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001107 }
1108}
1109
1110/**
1111 * xhtmlNodeListDumpOutput:
1112 * @buf: the XML buffer output
1113 * @doc: the XHTML document
1114 * @cur: the first node
1115 * @level: the imbrication level for indenting
1116 * @format: is formatting allowed
1117 * @encoding: an optional encoding string
1118 *
1119 * Dump an XML node list, recursive behaviour, children are printed too.
1120 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1121 * or xmlKeepBlanksDefault(0) was called
1122 */
1123static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001124xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001125 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001126
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001127 if (cur == NULL) return;
1128 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001129 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001130 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001131 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +00001132 xmlOutputBufferWrite(buf, ctxt->indent_size *
1133 (ctxt->level > ctxt->indent_nr ?
1134 ctxt->indent_nr : ctxt->level),
1135 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001136 xhtmlNodeDumpOutput(ctxt, cur);
1137 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001138 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001139 }
1140 cur = cur->next;
1141 }
1142}
1143
1144/**
1145 * xhtmlNodeDumpOutput:
1146 * @buf: the XML buffer output
1147 * @doc: the XHTML document
1148 * @cur: the current node
1149 * @level: the imbrication level for indenting
1150 * @format: is formatting allowed
1151 * @encoding: an optional encoding string
1152 *
1153 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001154 */
1155static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001156xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Rob Richards31f73022005-08-26 15:33:26 +00001157 int format, addmeta = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001158 xmlNodePtr tmp;
1159 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001160 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001161
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001162 if (cur == NULL) return;
Daniel Veillard60071ae2005-09-12 00:03:43 +00001163 if ((cur->type == XML_DOCUMENT_NODE) ||
1164 (cur->type == XML_HTML_DOCUMENT_NODE)) {
1165 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1166 return;
1167 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001168 if (cur->type == XML_XINCLUDE_START)
1169 return;
1170 if (cur->type == XML_XINCLUDE_END)
1171 return;
1172 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001173 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001174 return;
1175 }
Rob Richards2e2691b2005-10-21 14:45:16 +00001176 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
1177 xhtmlNodeListDumpOutput(ctxt, cur->children);
1178 return;
1179 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001180 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001181 if (cur->type == XML_ELEMENT_DECL) {
1182 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1183 return;
1184 }
1185 if (cur->type == XML_ATTRIBUTE_DECL) {
1186 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1187 return;
1188 }
1189 if (cur->type == XML_ENTITY_DECL) {
1190 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1191 return;
1192 }
1193 if (cur->type == XML_TEXT_NODE) {
1194 if (cur->content != NULL) {
1195 if ((cur->name == xmlStringText) ||
1196 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001197 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001198 } else {
1199 /*
1200 * Disable escaping, needed for XSLT
1201 */
1202 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1203 }
1204 }
1205
1206 return;
1207 }
1208 if (cur->type == XML_PI_NODE) {
1209 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001210 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001211 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1212 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001213 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001214 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1215 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001216 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001217 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001218 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001219 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001220 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001221 }
1222 return;
1223 }
1224 if (cur->type == XML_COMMENT_NODE) {
1225 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001226 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001227 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001228 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001229 }
1230 return;
1231 }
1232 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001233 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001234 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001235 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001236 return;
1237 }
1238 if (cur->type == XML_CDATA_SECTION_NODE) {
1239 start = end = cur->content;
1240 while (*end != '\0') {
1241 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1242 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001243 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001244 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001245 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001246 start = end;
1247 }
1248 end++;
1249 }
1250 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001251 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001252 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001253 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001254 }
1255 return;
1256 }
Daniel Veillarda76a81f2007-10-10 08:28:18 +00001257 if (cur->type == XML_ATTRIBUTE_NODE) {
1258 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1259 return;
1260 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001261
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001262 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001263 if (format == 1) {
1264 tmp = cur->children;
1265 while (tmp != NULL) {
1266 if ((tmp->type == XML_TEXT_NODE) ||
1267 (tmp->type == XML_ENTITY_REF_NODE)) {
1268 format = 0;
1269 break;
1270 }
1271 tmp = tmp->next;
1272 }
1273 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001274 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001275 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1276 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001277 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001278 }
1279
1280 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1281 if (cur->nsDef)
1282 xmlNsListDumpOutput(buf, cur->nsDef);
1283 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1284 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1285 /*
1286 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1287 */
1288 xmlOutputBufferWriteString(buf,
1289 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1290 }
1291 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001292 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001293
Rob Richards31f73022005-08-26 15:33:26 +00001294 if ((cur->type == XML_ELEMENT_NODE) &&
1295 (cur->parent != NULL) &&
1296 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1297 xmlStrEqual(cur->name, BAD_CAST"head") &&
1298 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1299
1300 tmp = cur->children;
1301 while (tmp != NULL) {
1302 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1303 xmlChar *httpequiv;
1304
1305 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
Rob Richards07b72002005-09-03 14:56:36 +00001306 if (httpequiv != NULL) {
1307 if (xmlStrcasecmp(httpequiv, BAD_CAST"Content-Type") == 0) {
1308 xmlFree(httpequiv);
1309 break;
1310 }
Rob Richards31f73022005-08-26 15:33:26 +00001311 xmlFree(httpequiv);
Rob Richards31f73022005-08-26 15:33:26 +00001312 }
Rob Richards31f73022005-08-26 15:33:26 +00001313 }
1314 tmp = tmp->next;
1315 }
1316 if (tmp == NULL)
1317 addmeta = 1;
1318 }
1319
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001320 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1321 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
Rob Richards31f73022005-08-26 15:33:26 +00001322 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001323 /*
1324 * C.2. Empty Elements
1325 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001326 xmlOutputBufferWrite(buf, 3, " />");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001327 } else {
Rob Richards31f73022005-08-26 15:33:26 +00001328 if (addmeta == 1) {
1329 xmlOutputBufferWrite(buf, 1, ">");
Rob Richards2ce51c02005-09-12 12:16:35 +00001330 if (ctxt->format) {
1331 xmlOutputBufferWrite(buf, 1, "\n");
1332 if (xmlIndentTreeOutput)
1333 xmlOutputBufferWrite(buf, ctxt->indent_size *
1334 (ctxt->level + 1 > ctxt->indent_nr ?
1335 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1336 }
Rob Richards31f73022005-08-26 15:33:26 +00001337 xmlOutputBufferWriteString(buf,
1338 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1339 if (ctxt->encoding) {
1340 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1341 } else {
1342 xmlOutputBufferWrite(buf, 5, "UTF-8");
1343 }
Rob Richards2ce51c02005-09-12 12:16:35 +00001344 xmlOutputBufferWrite(buf, 4, "\" />");
1345 if (ctxt->format)
1346 xmlOutputBufferWrite(buf, 1, "\n");
1347 } else {
1348 xmlOutputBufferWrite(buf, 1, ">");
Rob Richards31f73022005-08-26 15:33:26 +00001349 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001350 /*
1351 * C.3. Element Minimization and Empty Element Content
1352 */
Rob Richards2ce51c02005-09-12 12:16:35 +00001353 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001354 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1355 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001356 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001357 }
1358 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001359 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001360 }
1361 return;
1362 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001363 xmlOutputBufferWrite(buf, 1, ">");
Rob Richards31f73022005-08-26 15:33:26 +00001364 if (addmeta == 1) {
Rob Richards2ce51c02005-09-12 12:16:35 +00001365 if (ctxt->format) {
1366 xmlOutputBufferWrite(buf, 1, "\n");
1367 if (xmlIndentTreeOutput)
1368 xmlOutputBufferWrite(buf, ctxt->indent_size *
1369 (ctxt->level + 1 > ctxt->indent_nr ?
1370 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1371 }
Rob Richards31f73022005-08-26 15:33:26 +00001372 xmlOutputBufferWriteString(buf,
1373 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1374 if (ctxt->encoding) {
1375 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1376 } else {
1377 xmlOutputBufferWrite(buf, 5, "UTF-8");
1378 }
1379 xmlOutputBufferWrite(buf, 4, "\" />");
1380 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001381 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001382 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001383 }
1384
Kasimier T. Buchcik7b4e2e22006-07-13 13:07:11 +00001385#if 0
1386 /*
1387 * This was removed due to problems with HTML processors.
1388 * See bug #345147.
Daniel Veillarddab39b52006-10-16 23:22:10 +00001389 */
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001390 /*
1391 * 4.8. Script and Style elements
1392 */
1393 if ((cur->type == XML_ELEMENT_NODE) &&
1394 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1395 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1396 ((cur->ns == NULL) ||
1397 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1398 xmlNodePtr child = cur->children;
1399
1400 while (child != NULL) {
Daniel Veillarddbd61052005-09-12 14:03:26 +00001401 if (child->type == XML_TEXT_NODE) {
1402 if ((xmlStrchr(child->content, '<') == NULL) &&
1403 (xmlStrchr(child->content, '&') == NULL) &&
1404 (xmlStrstr(child->content, BAD_CAST "]]>") == NULL)) {
1405 /* Nothing to escape, so just output as is... */
1406 /* FIXME: Should we do something about "--" also? */
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001407 int level = ctxt->level;
1408 int indent = ctxt->format;
1409
1410 ctxt->level = 0;
1411 ctxt->format = 0;
Daniel Veillarddbd61052005-09-12 14:03:26 +00001412 xmlOutputBufferWriteString(buf, (const char *) child->content);
1413 /* (We cannot use xhtmlNodeDumpOutput() here because
1414 * we wish to leave '>' unescaped!) */
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001415 ctxt->level = level;
1416 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001417 } else {
Daniel Veillarddbd61052005-09-12 14:03:26 +00001418 /* We must use a CDATA section. Unfortunately,
1419 * this will break CSS and JavaScript when read by
1420 * a browser in HTML4-compliant mode. :-( */
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001421 start = end = child->content;
1422 while (*end != '\0') {
1423 if (*end == ']' &&
1424 *(end + 1) == ']' &&
1425 *(end + 2) == '>') {
1426 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001427 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001428 xmlOutputBufferWrite(buf, end - start,
1429 (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001430 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001431 start = end;
1432 }
1433 end++;
1434 }
1435 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001436 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1437 xmlOutputBufferWrite(buf, end - start,
1438 (const char *)start);
1439 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001440 }
1441 }
1442 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001443 int level = ctxt->level;
1444 int indent = ctxt->format;
1445
1446 ctxt->level = 0;
1447 ctxt->format = 0;
1448 xhtmlNodeDumpOutput(ctxt, child);
1449 ctxt->level = level;
1450 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001451 }
1452 child = child->next;
1453 }
Kasimier T. Buchcik7b4e2e22006-07-13 13:07:11 +00001454 }
1455#endif
1456
1457 if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001458 int indent = ctxt->format;
1459
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001460 if (format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001461 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001462 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001463 xhtmlNodeListDumpOutput(ctxt, cur->children);
1464 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001465 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001466 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001467 xmlOutputBufferWrite(buf, ctxt->indent_size *
1468 (ctxt->level > ctxt->indent_nr ?
1469 ctxt->indent_nr : ctxt->level),
1470 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001471 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001472 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001473 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1474 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001475 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001476 }
1477
1478 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001479 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001480}
1481#endif
1482
1483/************************************************************************
1484 * *
1485 * Public entry points *
1486 * *
1487 ************************************************************************/
1488
1489/**
1490 * xmlSaveToFd:
1491 * @fd: a file descriptor number
1492 * @encoding: the encoding name to use or NULL
1493 * @options: a set of xmlSaveOptions
1494 *
1495 * Create a document saving context serializing to a file descriptor
1496 * with the encoding and the options given.
1497 *
1498 * Returns a new serialization context or NULL in case of error.
1499 */
1500xmlSaveCtxtPtr
1501xmlSaveToFd(int fd, const char *encoding, int options)
1502{
1503 xmlSaveCtxtPtr ret;
1504
1505 ret = xmlNewSaveCtxt(encoding, options);
1506 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001507 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1508 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001509 xmlFreeSaveCtxt(ret);
1510 return(NULL);
1511 }
1512 return(ret);
1513}
1514
1515/**
1516 * xmlSaveToFilename:
1517 * @filename: a file name or an URL
1518 * @encoding: the encoding name to use or NULL
1519 * @options: a set of xmlSaveOptions
1520 *
1521 * Create a document saving context serializing to a filename or possibly
1522 * to an URL (but this is less reliable) with the encoding and the options
1523 * given.
1524 *
1525 * Returns a new serialization context or NULL in case of error.
1526 */
1527xmlSaveCtxtPtr
1528xmlSaveToFilename(const char *filename, const char *encoding, int options)
1529{
1530 xmlSaveCtxtPtr ret;
1531 int compression = 0; /* TODO handle compression option */
1532
1533 ret = xmlNewSaveCtxt(encoding, options);
1534 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001535 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001536 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001537 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001538 xmlFreeSaveCtxt(ret);
1539 return(NULL);
1540 }
1541 return(ret);
1542}
1543
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001544/**
1545 * xmlSaveToBuffer:
1546 * @buffer: a buffer
1547 * @encoding: the encoding name to use or NULL
1548 * @options: a set of xmlSaveOptions
1549 *
1550 * Create a document saving context serializing to a buffer
1551 * with the encoding and the options given
1552 *
1553 * Returns a new serialization context or NULL in case of error.
Daniel Veillard9a00fd22005-11-09 08:56:26 +00001554 */
1555
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001556xmlSaveCtxtPtr
1557xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1558{
Daniel Veillard9a00fd22005-11-09 08:56:26 +00001559 xmlSaveCtxtPtr ret;
1560 xmlOutputBufferPtr out_buff;
1561 xmlCharEncodingHandlerPtr handler;
1562
1563 ret = xmlNewSaveCtxt(encoding, options);
1564 if (ret == NULL) return(NULL);
1565
1566 if (encoding != NULL) {
1567 handler = xmlFindCharEncodingHandler(encoding);
1568 if (handler == NULL) {
1569 xmlFree(ret);
1570 return(NULL);
1571 }
1572 } else
1573 handler = NULL;
1574 out_buff = xmlOutputBufferCreateBuffer(buffer, handler);
1575 if (out_buff == NULL) {
1576 xmlFree(ret);
1577 if (handler) xmlCharEncCloseFunc(handler);
1578 return(NULL);
1579 }
1580
1581 ret->buf = out_buff;
1582 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001583}
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001584
1585/**
1586 * xmlSaveToIO:
1587 * @iowrite: an I/O write function
1588 * @ioclose: an I/O close function
1589 * @ioctx: an I/O handler
1590 * @encoding: the encoding name to use or NULL
1591 * @options: a set of xmlSaveOptions
1592 *
1593 * Create a document saving context serializing to a file descriptor
1594 * with the encoding and the options given
1595 *
1596 * Returns a new serialization context or NULL in case of error.
1597 */
1598xmlSaveCtxtPtr
1599xmlSaveToIO(xmlOutputWriteCallback iowrite,
1600 xmlOutputCloseCallback ioclose,
1601 void *ioctx, const char *encoding, int options)
1602{
1603 xmlSaveCtxtPtr ret;
1604
1605 ret = xmlNewSaveCtxt(encoding, options);
1606 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001607 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1608 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001609 xmlFreeSaveCtxt(ret);
1610 return(NULL);
1611 }
1612 return(ret);
1613}
1614
1615/**
1616 * xmlSaveDoc:
1617 * @ctxt: a document saving context
1618 * @doc: a document
1619 *
1620 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001621 * TODO: The function is not fully implemented yet as it does not return the
1622 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001623 *
1624 * Returns the number of byte written or -1 in case of error
1625 */
1626long
1627xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1628{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001629 long ret = 0;
1630
Daniel Veillardce682bc2004-11-05 17:22:25 +00001631 if ((ctxt == NULL) || (doc == NULL)) return(-1);
Daniel Veillarddab39b52006-10-16 23:22:10 +00001632 if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1633 return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001634 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001635}
1636
1637/**
1638 * xmlSaveTree:
1639 * @ctxt: a document saving context
Daniel Veillard681e9042006-09-29 09:16:00 +00001640 * @node: the top node of the subtree to save
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001641 *
1642 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001643 * TODO: The function is not fully implemented yet as it does not return the
1644 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001645 *
1646 * Returns the number of byte written or -1 in case of error
1647 */
1648long
1649xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1650{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001651 long ret = 0;
1652
Daniel Veillardce682bc2004-11-05 17:22:25 +00001653 if ((ctxt == NULL) || (node == NULL)) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001654 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001655 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001656}
1657
1658/**
1659 * xmlSaveFlush:
1660 * @ctxt: a document saving context
1661 *
1662 * Flush a document saving context, i.e. make sure that all bytes have
1663 * been output.
1664 *
1665 * Returns the number of byte written or -1 in case of error.
1666 */
1667int
1668xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1669{
1670 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001671 if (ctxt->buf == NULL) return(-1);
1672 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001673}
1674
1675/**
1676 * xmlSaveClose:
1677 * @ctxt: a document saving context
1678 *
1679 * Close a document saving context, i.e. make sure that all bytes have
1680 * been output and free the associated data.
1681 *
1682 * Returns the number of byte written or -1 in case of error.
1683 */
1684int
1685xmlSaveClose(xmlSaveCtxtPtr ctxt)
1686{
1687 int ret;
1688
1689 if (ctxt == NULL) return(-1);
1690 ret = xmlSaveFlush(ctxt);
1691 xmlFreeSaveCtxt(ctxt);
1692 return(ret);
1693}
1694
Daniel Veillard3995bc32004-05-15 18:57:31 +00001695/**
1696 * xmlSaveSetEscape:
1697 * @ctxt: a document saving context
1698 * @escape: the escaping function
1699 *
1700 * Set a custom escaping function to be used for text in element content
1701 *
1702 * Returns 0 if successful or -1 in case of error.
1703 */
1704int
1705xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1706{
1707 if (ctxt == NULL) return(-1);
1708 ctxt->escape = escape;
1709 return(0);
1710}
1711
1712/**
1713 * xmlSaveSetAttrEscape:
1714 * @ctxt: a document saving context
1715 * @escape: the escaping function
1716 *
1717 * Set a custom escaping function to be used for text in attribute content
1718 *
1719 * Returns 0 if successful or -1 in case of error.
1720 */
1721int
1722xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1723{
1724 if (ctxt == NULL) return(-1);
1725 ctxt->escapeAttr = escape;
1726 return(0);
1727}
1728
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001729/************************************************************************
1730 * *
1731 * Public entry points based on buffers *
1732 * *
1733 ************************************************************************/
1734/**
1735 * xmlAttrSerializeTxtContent:
1736 * @buf: the XML buffer output
1737 * @doc: the document
1738 * @attr: the attribute node
1739 * @string: the text content
1740 *
1741 * Serialize text attribute values to an xml simple buffer
1742 */
1743void
1744xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001745 xmlAttrPtr attr, const xmlChar * string)
1746{
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001747 xmlChar *base, *cur;
1748
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001749 if (string == NULL)
1750 return;
1751 base = cur = (xmlChar *) string;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001752 while (*cur != 0) {
1753 if (*cur == '\n') {
1754 if (base != cur)
1755 xmlBufferAdd(buf, base, cur - base);
1756 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1757 cur++;
1758 base = cur;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001759 } else if (*cur == '\r') {
1760 if (base != cur)
1761 xmlBufferAdd(buf, base, cur - base);
1762 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1763 cur++;
1764 base = cur;
1765 } else if (*cur == '\t') {
1766 if (base != cur)
1767 xmlBufferAdd(buf, base, cur - base);
1768 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1769 cur++;
1770 base = cur;
1771 } else if (*cur == '"') {
1772 if (base != cur)
1773 xmlBufferAdd(buf, base, cur - base);
1774 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1775 cur++;
1776 base = cur;
1777 } else if (*cur == '<') {
1778 if (base != cur)
1779 xmlBufferAdd(buf, base, cur - base);
1780 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1781 cur++;
1782 base = cur;
1783 } else if (*cur == '>') {
1784 if (base != cur)
1785 xmlBufferAdd(buf, base, cur - base);
1786 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1787 cur++;
1788 base = cur;
1789 } else if (*cur == '&') {
1790 if (base != cur)
1791 xmlBufferAdd(buf, base, cur - base);
1792 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1793 cur++;
1794 base = cur;
1795 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1796 (doc->encoding == NULL))) {
1797 /*
1798 * We assume we have UTF-8 content.
1799 */
1800 unsigned char tmp[10];
1801 int val = 0, l = 1;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001802
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001803 if (base != cur)
1804 xmlBufferAdd(buf, base, cur - base);
1805 if (*cur < 0xC0) {
1806 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1807 if (doc != NULL)
1808 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1809 xmlSerializeHexCharRef(tmp, *cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001810 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001811 cur++;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001812 base = cur;
1813 continue;
1814 } else if (*cur < 0xE0) {
1815 val = (cur[0]) & 0x1F;
1816 val <<= 6;
1817 val |= (cur[1]) & 0x3F;
1818 l = 2;
1819 } else if (*cur < 0xF0) {
1820 val = (cur[0]) & 0x0F;
1821 val <<= 6;
1822 val |= (cur[1]) & 0x3F;
1823 val <<= 6;
1824 val |= (cur[2]) & 0x3F;
1825 l = 3;
1826 } else if (*cur < 0xF8) {
1827 val = (cur[0]) & 0x07;
1828 val <<= 6;
1829 val |= (cur[1]) & 0x3F;
1830 val <<= 6;
1831 val |= (cur[2]) & 0x3F;
1832 val <<= 6;
1833 val |= (cur[3]) & 0x3F;
1834 l = 4;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001835 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001836 if ((l == 1) || (!IS_CHAR(val))) {
1837 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1838 if (doc != NULL)
1839 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1840
1841 xmlSerializeHexCharRef(tmp, *cur);
1842 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1843 cur++;
1844 base = cur;
1845 continue;
1846 }
1847 /*
1848 * We could do multiple things here. Just save
1849 * as a char ref
1850 */
1851 xmlSerializeHexCharRef(tmp, val);
1852 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1853 cur += l;
1854 base = cur;
1855 } else {
1856 cur++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001857 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001858 }
1859 if (base != cur)
1860 xmlBufferAdd(buf, base, cur - base);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001861}
1862
1863/**
1864 * xmlNodeDump:
1865 * @buf: the XML buffer output
1866 * @doc: the document
1867 * @cur: the current node
1868 * @level: the imbrication level for indenting
1869 * @format: is formatting allowed
1870 *
1871 * Dump an XML node, recursive behaviour,children are printed too.
1872 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1873 * or xmlKeepBlanksDefault(0) was called
1874 *
1875 * Returns the number of bytes written to the buffer or -1 in case of error
1876 */
1877int
1878xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1879 int format)
1880{
1881 unsigned int use;
1882 int ret;
1883 xmlOutputBufferPtr outbuf;
1884
1885 xmlInitParser();
1886
1887 if (cur == NULL) {
1888#ifdef DEBUG_TREE
1889 xmlGenericError(xmlGenericErrorContext,
1890 "xmlNodeDump : node == NULL\n");
1891#endif
1892 return (-1);
1893 }
1894 if (buf == NULL) {
1895#ifdef DEBUG_TREE
1896 xmlGenericError(xmlGenericErrorContext,
1897 "xmlNodeDump : buf == NULL\n");
1898#endif
1899 return (-1);
1900 }
1901 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1902 if (outbuf == NULL) {
1903 xmlSaveErrMemory("creating buffer");
1904 return (-1);
1905 }
1906 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1907 outbuf->buffer = buf;
1908 outbuf->encoder = NULL;
1909 outbuf->writecallback = NULL;
1910 outbuf->closecallback = NULL;
1911 outbuf->context = NULL;
1912 outbuf->written = 0;
1913
1914 use = buf->use;
1915 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1916 xmlFree(outbuf);
1917 ret = buf->use - use;
1918 return (ret);
1919}
1920
1921/**
1922 * xmlElemDump:
1923 * @f: the FILE * for the output
1924 * @doc: the document
1925 * @cur: the current node
1926 *
1927 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1928 */
1929void
1930xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1931{
1932 xmlOutputBufferPtr outbuf;
1933
1934 xmlInitParser();
1935
1936 if (cur == NULL) {
1937#ifdef DEBUG_TREE
1938 xmlGenericError(xmlGenericErrorContext,
1939 "xmlElemDump : cur == NULL\n");
1940#endif
1941 return;
1942 }
1943#ifdef DEBUG_TREE
1944 if (doc == NULL) {
1945 xmlGenericError(xmlGenericErrorContext,
1946 "xmlElemDump : doc == NULL\n");
1947 }
1948#endif
1949
1950 outbuf = xmlOutputBufferCreateFile(f, NULL);
1951 if (outbuf == NULL)
1952 return;
1953 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1954#ifdef LIBXML_HTML_ENABLED
1955 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1956#else
1957 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1958#endif /* LIBXML_HTML_ENABLED */
1959 } else
1960 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1961 xmlOutputBufferClose(outbuf);
1962}
1963
1964/************************************************************************
1965 * *
1966 * Saving functions front-ends *
1967 * *
1968 ************************************************************************/
1969
1970/**
1971 * xmlNodeDumpOutput:
1972 * @buf: the XML buffer output
1973 * @doc: the document
1974 * @cur: the current node
1975 * @level: the imbrication level for indenting
1976 * @format: is formatting allowed
1977 * @encoding: an optional encoding string
1978 *
1979 * Dump an XML node, recursive behaviour, children are printed too.
1980 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1981 * or xmlKeepBlanksDefault(0) was called
1982 */
1983void
1984xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1985 int level, int format, const char *encoding)
1986{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001987 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001988#ifdef LIBXML_HTML_ENABLED
1989 xmlDtdPtr dtd;
1990 int is_xhtml = 0;
1991#endif
1992
1993 xmlInitParser();
1994
Daniel Veillardce244ad2004-11-05 10:03:46 +00001995 if ((buf == NULL) || (cur == NULL)) return;
1996
Daniel Veillard64354ea2005-03-31 15:22:56 +00001997 if (encoding == NULL)
1998 encoding = "UTF-8";
1999
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002000 memset(&ctxt, 0, sizeof(ctxt));
2001 ctxt.doc = doc;
2002 ctxt.buf = buf;
2003 ctxt.level = level;
2004 ctxt.format = format;
2005 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002006 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002007
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002008#ifdef LIBXML_HTML_ENABLED
2009 dtd = xmlGetIntSubset(doc);
2010 if (dtd != NULL) {
Daniel Veillard33b20b72005-09-12 21:43:20 +00002011 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2012 if (is_xhtml < 0)
2013 is_xhtml = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002014 }
2015
2016 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002017 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002018 else
2019#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002020 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002021}
2022
2023/**
2024 * xmlDocDumpFormatMemoryEnc:
2025 * @out_doc: Document to generate XML text from
2026 * @doc_txt_ptr: Memory pointer for allocated XML text
2027 * @doc_txt_len: Length of the generated XML text
2028 * @txt_encoding: Character encoding to use when generating XML text
2029 * @format: should formatting spaces been added
2030 *
2031 * Dump the current DOM tree into memory using the character encoding specified
2032 * by the caller. Note it is up to the caller of this function to free the
2033 * allocated memory with xmlFree().
2034 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2035 * or xmlKeepBlanksDefault(0) was called
2036 */
2037
2038void
2039xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2040 int * doc_txt_len, const char * txt_encoding,
2041 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002042 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002043 int dummy = 0;
2044 xmlOutputBufferPtr out_buff = NULL;
2045 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
2046
2047 if (doc_txt_len == NULL) {
2048 doc_txt_len = &dummy; /* Continue, caller just won't get length */
2049 }
2050
2051 if (doc_txt_ptr == NULL) {
2052 *doc_txt_len = 0;
2053 return;
2054 }
2055
2056 *doc_txt_ptr = NULL;
2057 *doc_txt_len = 0;
2058
2059 if (out_doc == NULL) {
2060 /* No document, no output */
2061 return;
2062 }
2063
2064 /*
2065 * Validate the encoding value, if provided.
2066 * This logic is copied from xmlSaveFileEnc.
2067 */
2068
2069 if (txt_encoding == NULL)
2070 txt_encoding = (const char *) out_doc->encoding;
2071 if (txt_encoding != NULL) {
2072 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2073 if ( conv_hdlr == NULL ) {
2074 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2075 txt_encoding);
2076 return;
2077 }
2078 }
2079
2080 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2081 xmlSaveErrMemory("creating buffer");
2082 return;
2083 }
2084
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002085 memset(&ctxt, 0, sizeof(ctxt));
2086 ctxt.doc = out_doc;
2087 ctxt.buf = out_buff;
2088 ctxt.level = 0;
2089 ctxt.format = format;
2090 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002091 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002092 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002093 xmlOutputBufferFlush(out_buff);
2094 if (out_buff->conv != NULL) {
2095 *doc_txt_len = out_buff->conv->use;
2096 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
2097 } else {
2098 *doc_txt_len = out_buff->buffer->use;
2099 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
2100 }
2101 (void)xmlOutputBufferClose(out_buff);
2102
2103 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2104 *doc_txt_len = 0;
2105 xmlSaveErrMemory("creating output");
2106 }
2107
2108 return;
2109}
2110
2111/**
2112 * xmlDocDumpMemory:
2113 * @cur: the document
2114 * @mem: OUT: the memory pointer
2115 * @size: OUT: the memory length
2116 *
2117 * Dump an XML document in memory and return the #xmlChar * and it's size
2118 * in bytes. It's up to the caller to free the memory with xmlFree().
2119 * The resulting byte array is zero terminated, though the last 0 is not
2120 * included in the returned size.
2121 */
2122void
2123xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2124 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2125}
2126
2127/**
2128 * xmlDocDumpFormatMemory:
2129 * @cur: the document
2130 * @mem: OUT: the memory pointer
2131 * @size: OUT: the memory length
2132 * @format: should formatting spaces been added
2133 *
2134 *
2135 * Dump an XML document in memory and return the #xmlChar * and it's size.
2136 * It's up to the caller to free the memory with xmlFree().
2137 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2138 * or xmlKeepBlanksDefault(0) was called
2139 */
2140void
2141xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2142 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2143}
2144
2145/**
2146 * xmlDocDumpMemoryEnc:
2147 * @out_doc: Document to generate XML text from
2148 * @doc_txt_ptr: Memory pointer for allocated XML text
2149 * @doc_txt_len: Length of the generated XML text
2150 * @txt_encoding: Character encoding to use when generating XML text
2151 *
2152 * Dump the current DOM tree into memory using the character encoding specified
2153 * by the caller. Note it is up to the caller of this function to free the
2154 * allocated memory with xmlFree().
2155 */
2156
2157void
2158xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2159 int * doc_txt_len, const char * txt_encoding) {
2160 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2161 txt_encoding, 0);
2162}
2163
2164/**
2165 * xmlDocFormatDump:
2166 * @f: the FILE*
2167 * @cur: the document
2168 * @format: should formatting spaces been added
2169 *
2170 * Dump an XML document to an open FILE.
2171 *
2172 * returns: the number of bytes written or -1 in case of failure.
2173 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2174 * or xmlKeepBlanksDefault(0) was called
2175 */
2176int
2177xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002178 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002179 xmlOutputBufferPtr buf;
2180 const char * encoding;
2181 xmlCharEncodingHandlerPtr handler = NULL;
2182 int ret;
2183
2184 if (cur == NULL) {
2185#ifdef DEBUG_TREE
2186 xmlGenericError(xmlGenericErrorContext,
2187 "xmlDocDump : document == NULL\n");
2188#endif
2189 return(-1);
2190 }
2191 encoding = (const char *) cur->encoding;
2192
2193 if (encoding != NULL) {
Daniel Veillard3814a362007-07-26 11:41:46 +00002194 handler = xmlFindCharEncodingHandler(encoding);
2195 if (handler == NULL) {
2196 xmlFree((char *) cur->encoding);
2197 cur->encoding = NULL;
2198 encoding = NULL;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002199 }
Daniel Veillard3814a362007-07-26 11:41:46 +00002200 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002201 buf = xmlOutputBufferCreateFile(f, handler);
2202 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002203 memset(&ctxt, 0, sizeof(ctxt));
2204 ctxt.doc = cur;
2205 ctxt.buf = buf;
2206 ctxt.level = 0;
2207 ctxt.format = format;
2208 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002209 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002210 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002211
2212 ret = xmlOutputBufferClose(buf);
2213 return(ret);
2214}
2215
2216/**
2217 * xmlDocDump:
2218 * @f: the FILE*
2219 * @cur: the document
2220 *
2221 * Dump an XML document to an open FILE.
2222 *
2223 * returns: the number of bytes written or -1 in case of failure.
2224 */
2225int
2226xmlDocDump(FILE *f, xmlDocPtr cur) {
2227 return(xmlDocFormatDump (f, cur, 0));
2228}
2229
2230/**
2231 * xmlSaveFileTo:
2232 * @buf: an output I/O buffer
2233 * @cur: the document
2234 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2235 *
2236 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002237 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2238 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002239 *
2240 * returns: the number of bytes written or -1 in case of failure.
2241 */
2242int
2243xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002244 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002245 int ret;
2246
Daniel Veillard3d97e662004-11-04 10:49:00 +00002247 if (buf == NULL) return(-1);
2248 if (cur == NULL) {
2249 xmlOutputBufferClose(buf);
2250 return(-1);
2251 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002252 memset(&ctxt, 0, sizeof(ctxt));
2253 ctxt.doc = cur;
2254 ctxt.buf = buf;
2255 ctxt.level = 0;
2256 ctxt.format = 0;
2257 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002258 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002259 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002260 ret = xmlOutputBufferClose(buf);
2261 return(ret);
2262}
2263
2264/**
2265 * xmlSaveFormatFileTo:
2266 * @buf: an output I/O buffer
2267 * @cur: the document
2268 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2269 * @format: should formatting spaces been added
2270 *
2271 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002272 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2273 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002274 *
2275 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002276 */
2277int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002278xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2279 const char *encoding, int format)
2280{
2281 xmlSaveCtxt ctxt;
2282 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002283
Daniel Veillard3d97e662004-11-04 10:49:00 +00002284 if (buf == NULL) return(-1);
Daniel Veillardce244ad2004-11-05 10:03:46 +00002285 if ((cur == NULL) ||
2286 ((cur->type != XML_DOCUMENT_NODE) &&
2287 (cur->type != XML_HTML_DOCUMENT_NODE))) {
Daniel Veillard3d97e662004-11-04 10:49:00 +00002288 xmlOutputBufferClose(buf);
2289 return(-1);
2290 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002291 memset(&ctxt, 0, sizeof(ctxt));
2292 ctxt.doc = cur;
2293 ctxt.buf = buf;
2294 ctxt.level = 0;
2295 ctxt.format = format;
2296 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002297 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002298 xmlDocContentDumpOutput(&ctxt, cur);
2299 ret = xmlOutputBufferClose(buf);
2300 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002301}
2302
2303/**
2304 * xmlSaveFormatFileEnc:
2305 * @filename: the filename or URL to output
2306 * @cur: the document being saved
2307 * @encoding: the name of the encoding to use or NULL.
2308 * @format: should formatting spaces be added.
2309 *
2310 * Dump an XML document to a file or an URL.
2311 *
2312 * Returns the number of bytes written or -1 in case of error.
2313 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2314 * or xmlKeepBlanksDefault(0) was called
2315 */
2316int
2317xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2318 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002319 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002320 xmlOutputBufferPtr buf;
2321 xmlCharEncodingHandlerPtr handler = NULL;
2322 int ret;
2323
2324 if (cur == NULL)
2325 return(-1);
2326
2327 if (encoding == NULL)
2328 encoding = (const char *) cur->encoding;
2329
2330 if (encoding != NULL) {
2331
2332 handler = xmlFindCharEncodingHandler(encoding);
2333 if (handler == NULL)
2334 return(-1);
2335 }
2336
2337#ifdef HAVE_ZLIB_H
2338 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2339#endif
2340 /*
2341 * save the content to a temp buffer.
2342 */
2343 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2344 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002345 memset(&ctxt, 0, sizeof(ctxt));
2346 ctxt.doc = cur;
2347 ctxt.buf = buf;
2348 ctxt.level = 0;
2349 ctxt.format = format;
2350 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002351 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002352
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002353 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002354
2355 ret = xmlOutputBufferClose(buf);
2356 return(ret);
2357}
2358
2359
2360/**
2361 * xmlSaveFileEnc:
2362 * @filename: the filename (or URL)
2363 * @cur: the document
2364 * @encoding: the name of an encoding (or NULL)
2365 *
2366 * Dump an XML document, converting it to the given encoding
2367 *
2368 * returns: the number of bytes written or -1 in case of failure.
2369 */
2370int
2371xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2372 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2373}
2374
2375/**
2376 * xmlSaveFormatFile:
2377 * @filename: the filename (or URL)
2378 * @cur: the document
2379 * @format: should formatting spaces been added
2380 *
2381 * Dump an XML document to a file. Will use compression if
2382 * compiled in and enabled. If @filename is "-" the stdout file is
2383 * used. If @format is set then the document will be indented on output.
2384 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2385 * or xmlKeepBlanksDefault(0) was called
2386 *
2387 * returns: the number of bytes written or -1 in case of failure.
2388 */
2389int
2390xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2391 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2392}
2393
2394/**
2395 * xmlSaveFile:
2396 * @filename: the filename (or URL)
2397 * @cur: the document
2398 *
2399 * Dump an XML document to a file. Will use compression if
2400 * compiled in and enabled. If @filename is "-" the stdout file is
2401 * used.
2402 * returns: the number of bytes written or -1 in case of failure.
2403 */
2404int
2405xmlSaveFile(const char *filename, xmlDocPtr cur) {
2406 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2407}
2408
2409#endif /* LIBXML_OUTPUT_ENABLED */
2410
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002411#define bottom_xmlsave
2412#include "elfgcchack.h"