blob: 1e8e0d73a6d4ff0b23f73035af34f6cd76cc3250 [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:
125 msg = "string is not in UTF-8";
126 break;
127 case XML_SAVE_CHAR_INVALID:
128 msg = "invalid character value";
129 break;
130 case XML_SAVE_UNKNOWN_ENCODING:
131 msg = "unknown encoding %s";
132 break;
133 case XML_SAVE_NO_DOCTYPE:
134 msg = "document has no DOCTYPE";
135 break;
136 default:
137 msg = "unexpected error number";
138 }
139 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
140}
141
142/************************************************************************
143 * *
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;
330
331 if (ctxt == NULL) return;
Daniel Veillard3995bc32004-05-15 18:57:31 +0000332 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
333 ctxt->escape = xmlEscapeEntities;
Daniel Veillard753086a2004-03-28 16:12:44 +0000334 if (xmlTreeIndentString == NULL) {
335 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
336 } else {
337 ctxt->indent_size = xmlStrlen((const xmlChar *) xmlTreeIndentString);
338 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
339 for (i = 0;i < ctxt->indent_nr;i++)
340 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
341 ctxt->indent_size);
342 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
343 }
344}
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000345
346/**
347 * xmlFreeSaveCtxt:
348 *
349 * Free a saving context, destroying the ouptut in any remaining buffer
350 */
351static void
352xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
353{
354 if (ctxt == NULL) return;
355 if (ctxt->encoding != NULL)
356 xmlFree((char *) ctxt->encoding);
Daniel Veillarde2161a62004-04-29 17:14:25 +0000357 if (ctxt->buf != NULL)
358 xmlOutputBufferClose(ctxt->buf);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000359 xmlFree(ctxt);
360}
361
362/**
363 * xmlNewSaveCtxt:
364 *
365 * Create a new saving context
366 *
367 * Returns the new structure or NULL in case of error
368 */
369static xmlSaveCtxtPtr
370xmlNewSaveCtxt(const char *encoding, int options)
371{
372 xmlSaveCtxtPtr ret;
373
374 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
375 if (ret == NULL) {
376 xmlSaveErrMemory("creating saving context");
377 return ( NULL );
378 }
379 memset(ret, 0, sizeof(xmlSaveCtxt));
380 ret->options = options;
381 if (encoding != NULL) {
382 ret->handler = xmlFindCharEncodingHandler(encoding);
383 if (ret->handler == NULL) {
384 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
385 xmlFreeSaveCtxt(ret);
386 return(NULL);
387 }
388 ret->encoding = xmlStrdup((const xmlChar *)encoding);
Daniel Veillard3995bc32004-05-15 18:57:31 +0000389 ret->escape = xmlEscapeEntities;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000390 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000391 xmlSaveCtxtInit(ret);
392
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000393 return(ret);
394}
395
396/************************************************************************
397 * *
398 * Dumping XML tree content to a simple buffer *
399 * *
400 ************************************************************************/
401/**
402 * xmlAttrSerializeContent:
403 * @buf: the XML buffer output
404 * @doc: the document
405 * @attr: the attribute pointer
406 *
407 * Serialize the attribute in the buffer
408 */
409static void
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000410xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000411{
412 xmlNodePtr children;
413
414 children = attr->children;
415 while (children != NULL) {
416 switch (children->type) {
417 case XML_TEXT_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000418 xmlAttrSerializeTxtContent(buf->buffer, attr->doc,
419 attr, children->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000420 break;
421 case XML_ENTITY_REF_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000422 xmlBufferAdd(buf->buffer, BAD_CAST "&", 1);
423 xmlBufferAdd(buf->buffer, children->name,
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000424 xmlStrlen(children->name));
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000425 xmlBufferAdd(buf->buffer, BAD_CAST ";", 1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000426 break;
427 default:
428 /* should not happen unless we have a badly built tree */
429 break;
430 }
431 children = children->next;
432 }
433}
434
435/************************************************************************
436 * *
437 * Dumping XML tree content to an I/O output buffer *
438 * *
439 ************************************************************************/
440
441#ifdef LIBXML_HTML_ENABLED
442static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000443xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000444#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000445static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
446static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000447void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
448
449/**
450 * xmlNsDumpOutput:
451 * @buf: the XML buffer output
452 * @cur: a namespace
453 *
454 * Dump a local Namespace definition.
455 * Should be called in the context of attributes dumps.
456 */
457static void
458xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000459 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000460 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
461 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
462 return;
463
464 /* Within the context of an element attributes */
465 if (cur->prefix != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000466 xmlOutputBufferWrite(buf, 7, " xmlns:");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000467 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
468 } else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000469 xmlOutputBufferWrite(buf, 6, " xmlns");
470 xmlOutputBufferWrite(buf, 1, "=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000471 xmlBufferWriteQuotedString(buf->buffer, cur->href);
472 }
473}
474
475/**
476 * xmlNsListDumpOutput:
477 * @buf: the XML buffer output
478 * @cur: the first namespace
479 *
480 * Dump a list of local Namespace definitions.
481 * Should be called in the context of attributes dumps.
482 */
483void
484xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
485 while (cur != NULL) {
486 xmlNsDumpOutput(buf, cur);
487 cur = cur->next;
488 }
489}
490
491/**
492 * xmlDtdDumpOutput:
493 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000494 * @dtd: the pointer to the DTD
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000495 *
496 * Dump the XML document DTD, if any.
497 */
498static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000499xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
500 xmlOutputBufferPtr buf;
501 int format, level;
502 xmlDocPtr doc;
503
504 if (dtd == NULL) return;
505 if ((ctxt == NULL) || (ctxt->buf == NULL))
506 return;
507 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000508 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000509 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
510 if (dtd->ExternalID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000511 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000512 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000513 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000514 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
515 } else if (dtd->SystemID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000516 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000517 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
518 }
519 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
520 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
521 (dtd->pentities == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000522 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000523 return;
524 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000525 xmlOutputBufferWrite(buf, 3, " [\n");
Daniel Veillardda3b29a2004-08-14 11:15:13 +0000526 /* Dump the notations first they are not in the DTD children list */
527 if (dtd->notations != NULL) {
528 xmlDumpNotationTable(buf->buffer, (xmlNotationTablePtr) dtd->notations);
529 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000530 format = ctxt->format;
531 level = ctxt->level;
532 doc = ctxt->doc;
533 ctxt->format = 0;
534 ctxt->level = -1;
535 ctxt->doc = dtd->doc;
536 xmlNodeListDumpOutput(ctxt, dtd->children);
537 ctxt->format = format;
538 ctxt->level = level;
539 ctxt->doc = doc;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000540 xmlOutputBufferWrite(buf, 2, "]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000541}
542
543/**
544 * xmlAttrDumpOutput:
545 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000546 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000547 *
548 * Dump an XML attribute
549 */
550static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000551xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
552 xmlOutputBufferPtr buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000553
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000554 if (cur == NULL) return;
555 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000556 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000557 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
558 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000559 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000560 }
561 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000562 xmlOutputBufferWrite(buf, 2, "=\"");
563 xmlAttrSerializeContent(buf, cur);
564 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000565}
566
567/**
568 * xmlAttrListDumpOutput:
569 * @buf: the XML buffer output
570 * @doc: the document
571 * @cur: the first attribute pointer
572 * @encoding: an optional encoding string
573 *
574 * Dump a list of XML attributes
575 */
576static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000577xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
578 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000579 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000580 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000581 cur = cur->next;
582 }
583}
584
585
586
587/**
588 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000589 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000590 *
591 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000592 */
593static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000594xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000595 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000596
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000597 if (cur == NULL) return;
598 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000599 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000600 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000601 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000602 xmlOutputBufferWrite(buf, ctxt->indent_size *
603 (ctxt->level > ctxt->indent_nr ?
604 ctxt->indent_nr : ctxt->level),
605 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000606 xmlNodeDumpOutputInternal(ctxt, cur);
607 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000608 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000609 }
610 cur = cur->next;
611 }
612}
613
614/**
615 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000616 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000617 *
618 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000619 */
620static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000621xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000622 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000623 xmlNodePtr tmp;
624 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000625 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000626
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000627 if (cur == NULL) return;
628 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000629 if (cur->type == XML_XINCLUDE_START)
630 return;
631 if (cur->type == XML_XINCLUDE_END)
632 return;
633 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000634 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000635 return;
636 }
637 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000638 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000639 return;
640 }
641 if (cur->type == XML_ELEMENT_DECL) {
642 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
643 return;
644 }
645 if (cur->type == XML_ATTRIBUTE_DECL) {
646 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
647 return;
648 }
649 if (cur->type == XML_ENTITY_DECL) {
650 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
651 return;
652 }
653 if (cur->type == XML_TEXT_NODE) {
654 if (cur->content != NULL) {
655 if ((cur->name == xmlStringText) ||
656 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000657
Daniel Veillard3995bc32004-05-15 18:57:31 +0000658 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000659 } else {
660 /*
661 * Disable escaping, needed for XSLT
662 */
663 xmlOutputBufferWriteString(buf, (const char *) cur->content);
664 }
665 }
666
667 return;
668 }
669 if (cur->type == XML_PI_NODE) {
670 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000671 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000672 xmlOutputBufferWriteString(buf, (const char *)cur->name);
673 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000674 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000675 xmlOutputBufferWriteString(buf, (const char *)cur->content);
676 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000677 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000678 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000679 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000680 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000681 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000682 }
683 return;
684 }
685 if (cur->type == XML_COMMENT_NODE) {
686 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000687 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000688 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000689 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000690 }
691 return;
692 }
693 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000694 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000695 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000696 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000697 return;
698 }
699 if (cur->type == XML_CDATA_SECTION_NODE) {
700 start = end = cur->content;
701 while (*end != '\0') {
702 if ((*end == ']') && (*(end + 1) == ']') && (*(end + 2) == '>')) {
703 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000704 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000705 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000706 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000707 start = end;
708 }
709 end++;
710 }
711 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000712 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000713 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000714 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000715 }
716 return;
717 }
718 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000719 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000720 return;
721 }
722 if (cur->type == XML_NAMESPACE_DECL) {
723 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
724 return;
725 }
726
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000727 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000728 if (format == 1) {
729 tmp = cur->children;
730 while (tmp != NULL) {
731 if ((tmp->type == XML_TEXT_NODE) ||
732 (tmp->type == XML_CDATA_SECTION_NODE) ||
733 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000734 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000735 break;
736 }
737 tmp = tmp->next;
738 }
739 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000740 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000741 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
742 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000743 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000744 }
745
746 xmlOutputBufferWriteString(buf, (const char *)cur->name);
747 if (cur->nsDef)
748 xmlNsListDumpOutput(buf, cur->nsDef);
749 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000750 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000751
752 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
753 (cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000754 xmlOutputBufferWrite(buf, 2, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000755 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000756 return;
757 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000758 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000759 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000760 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000761 }
762 if (cur->children != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000763 if (ctxt->format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000764 if (ctxt->level >= 0) ctxt->level++;
765 xmlNodeListDumpOutput(ctxt, cur->children);
766 if (ctxt->level > 0) ctxt->level--;
767 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000768 xmlOutputBufferWrite(buf, ctxt->indent_size *
769 (ctxt->level > ctxt->indent_nr ?
770 ctxt->indent_nr : ctxt->level),
771 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000772 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000773 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000774 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
775 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000776 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000777 }
778
779 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000780 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000781 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000782}
783
784/**
785 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000786 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000787 *
788 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000789 */
790static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000791xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000792#ifdef LIBXML_HTML_ENABLED
793 xmlDtdPtr dtd;
794 int is_xhtml = 0;
795#endif
796 const xmlChar *oldenc = cur->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000797 const xmlChar *encoding = ctxt->encoding;
798 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000799
800 xmlInitParser();
801
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000802 if (ctxt->encoding != NULL)
803 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000804
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000805 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000806 xmlOutputBufferWrite(buf, 14, "<?xml version=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000807 if (cur->version != NULL)
808 xmlBufferWriteQuotedString(buf->buffer, cur->version);
809 else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000810 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000811 if (ctxt->encoding == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000812 if (cur->encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000813 encoding = cur->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000814 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000815 encoding = (const xmlChar *)
816 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000817 }
818 if (encoding != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000819 xmlOutputBufferWrite(buf, 10, " encoding=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000820 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
821 }
822 switch (cur->standalone) {
823 case 0:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000824 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000825 break;
826 case 1:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000827 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000828 break;
829 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000830 xmlOutputBufferWrite(buf, 3, "?>\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000831
832#ifdef LIBXML_HTML_ENABLED
833 dtd = xmlGetIntSubset(cur);
834 if (dtd != NULL) {
835 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
836 if (is_xhtml < 0) is_xhtml = 0;
837 }
838 if (is_xhtml) {
839 if (encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000840 htmlSetMetaEncoding(cur, (const xmlChar *) ctxt->encoding);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000841 else
842 htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
843 }
844#endif
845 if (cur->children != NULL) {
846 xmlNodePtr child = cur->children;
847
848 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000849 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000850#ifdef LIBXML_HTML_ENABLED
851 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000852 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000853 else
854#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000855 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000856 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000857 child = child->next;
858 }
859 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000860 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000861 cur->encoding = oldenc;
862}
863
864#ifdef LIBXML_HTML_ENABLED
865/************************************************************************
866 * *
867 * Functions specific to XHTML serialization *
868 * *
869 ************************************************************************/
870
871/**
872 * xhtmlIsEmpty:
873 * @node: the node
874 *
875 * Check if a node is an empty xhtml node
876 *
877 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
878 */
879static int
880xhtmlIsEmpty(xmlNodePtr node) {
881 if (node == NULL)
882 return(-1);
883 if (node->type != XML_ELEMENT_NODE)
884 return(0);
885 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
886 return(0);
887 if (node->children != NULL)
888 return(0);
889 switch (node->name[0]) {
890 case 'a':
891 if (xmlStrEqual(node->name, BAD_CAST "area"))
892 return(1);
893 return(0);
894 case 'b':
895 if (xmlStrEqual(node->name, BAD_CAST "br"))
896 return(1);
897 if (xmlStrEqual(node->name, BAD_CAST "base"))
898 return(1);
899 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
900 return(1);
901 return(0);
902 case 'c':
903 if (xmlStrEqual(node->name, BAD_CAST "col"))
904 return(1);
905 return(0);
906 case 'f':
907 if (xmlStrEqual(node->name, BAD_CAST "frame"))
908 return(1);
909 return(0);
910 case 'h':
911 if (xmlStrEqual(node->name, BAD_CAST "hr"))
912 return(1);
913 return(0);
914 case 'i':
915 if (xmlStrEqual(node->name, BAD_CAST "img"))
916 return(1);
917 if (xmlStrEqual(node->name, BAD_CAST "input"))
918 return(1);
919 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
920 return(1);
921 return(0);
922 case 'l':
923 if (xmlStrEqual(node->name, BAD_CAST "link"))
924 return(1);
925 return(0);
926 case 'm':
927 if (xmlStrEqual(node->name, BAD_CAST "meta"))
928 return(1);
929 return(0);
930 case 'p':
931 if (xmlStrEqual(node->name, BAD_CAST "param"))
932 return(1);
933 return(0);
934 }
935 return(0);
936}
937
938/**
939 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000940 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000941 *
942 * Dump a list of XML attributes
943 */
944static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000945xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000946 xmlAttrPtr xml_lang = NULL;
947 xmlAttrPtr lang = NULL;
948 xmlAttrPtr name = NULL;
949 xmlAttrPtr id = NULL;
950 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000951 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000952
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000953 if (cur == NULL) return;
954 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000955 parent = cur->parent;
956 while (cur != NULL) {
957 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
958 id = cur;
959 else
960 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
961 name = cur;
962 else
963 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
964 lang = cur;
965 else
966 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
967 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
968 xml_lang = cur;
969 else if ((cur->ns == NULL) &&
970 ((cur->children == NULL) ||
971 (cur->children->content == NULL) ||
972 (cur->children->content[0] == 0)) &&
973 (htmlIsBooleanAttr(cur->name))) {
974 if (cur->children != NULL)
975 xmlFreeNode(cur->children);
976 cur->children = xmlNewText(cur->name);
977 if (cur->children != NULL)
978 cur->children->parent = (xmlNodePtr) cur;
979 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000980 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000981 cur = cur->next;
982 }
983 /*
984 * C.8
985 */
986 if ((name != NULL) && (id == NULL)) {
987 if ((parent != NULL) && (parent->name != NULL) &&
988 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
989 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
990 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
991 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
992 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
993 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
994 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
995 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
996 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000997 xmlOutputBufferWrite(buf, 5, " id=\"");
998 xmlAttrSerializeContent(buf, name);
999 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001000 }
1001 }
1002 /*
1003 * C.7.
1004 */
1005 if ((lang != NULL) && (xml_lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001006 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1007 xmlAttrSerializeContent(buf, lang);
1008 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001009 } else
1010 if ((xml_lang != NULL) && (lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001011 xmlOutputBufferWrite(buf, 7, " lang=\"");
1012 xmlAttrSerializeContent(buf, xml_lang);
1013 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001014 }
1015}
1016
1017/**
1018 * xhtmlNodeListDumpOutput:
1019 * @buf: the XML buffer output
1020 * @doc: the XHTML document
1021 * @cur: the first node
1022 * @level: the imbrication level for indenting
1023 * @format: is formatting allowed
1024 * @encoding: an optional encoding string
1025 *
1026 * Dump an XML node list, recursive behaviour, children are printed too.
1027 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1028 * or xmlKeepBlanksDefault(0) was called
1029 */
1030static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001031xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001032 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001033
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001034 if (cur == NULL) return;
1035 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001036 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001037 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001038 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +00001039 xmlOutputBufferWrite(buf, ctxt->indent_size *
1040 (ctxt->level > ctxt->indent_nr ?
1041 ctxt->indent_nr : ctxt->level),
1042 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001043 xhtmlNodeDumpOutput(ctxt, cur);
1044 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001045 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001046 }
1047 cur = cur->next;
1048 }
1049}
1050
1051/**
1052 * xhtmlNodeDumpOutput:
1053 * @buf: the XML buffer output
1054 * @doc: the XHTML document
1055 * @cur: the current node
1056 * @level: the imbrication level for indenting
1057 * @format: is formatting allowed
1058 * @encoding: an optional encoding string
1059 *
1060 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001061 */
1062static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001063xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +00001064 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001065 xmlNodePtr tmp;
1066 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001067 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001068
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001069 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001070 if (cur->type == XML_XINCLUDE_START)
1071 return;
1072 if (cur->type == XML_XINCLUDE_END)
1073 return;
1074 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001075 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001076 return;
1077 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001078 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001079 if (cur->type == XML_ELEMENT_DECL) {
1080 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1081 return;
1082 }
1083 if (cur->type == XML_ATTRIBUTE_DECL) {
1084 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1085 return;
1086 }
1087 if (cur->type == XML_ENTITY_DECL) {
1088 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1089 return;
1090 }
1091 if (cur->type == XML_TEXT_NODE) {
1092 if (cur->content != NULL) {
1093 if ((cur->name == xmlStringText) ||
1094 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001095 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001096 } else {
1097 /*
1098 * Disable escaping, needed for XSLT
1099 */
1100 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1101 }
1102 }
1103
1104 return;
1105 }
1106 if (cur->type == XML_PI_NODE) {
1107 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001108 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001109 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1110 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001111 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001112 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1113 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001114 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001115 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001116 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001117 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001118 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001119 }
1120 return;
1121 }
1122 if (cur->type == XML_COMMENT_NODE) {
1123 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001124 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001125 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001126 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001127 }
1128 return;
1129 }
1130 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001131 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001132 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001133 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001134 return;
1135 }
1136 if (cur->type == XML_CDATA_SECTION_NODE) {
1137 start = end = cur->content;
1138 while (*end != '\0') {
1139 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1140 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001141 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001142 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001143 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001144 start = end;
1145 }
1146 end++;
1147 }
1148 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001149 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001150 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001151 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001152 }
1153 return;
1154 }
1155
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001156 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001157 if (format == 1) {
1158 tmp = cur->children;
1159 while (tmp != NULL) {
1160 if ((tmp->type == XML_TEXT_NODE) ||
1161 (tmp->type == XML_ENTITY_REF_NODE)) {
1162 format = 0;
1163 break;
1164 }
1165 tmp = tmp->next;
1166 }
1167 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001168 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001169 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1170 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001171 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001172 }
1173
1174 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1175 if (cur->nsDef)
1176 xmlNsListDumpOutput(buf, cur->nsDef);
1177 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1178 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1179 /*
1180 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1181 */
1182 xmlOutputBufferWriteString(buf,
1183 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1184 }
1185 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001186 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001187
1188 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1189 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1190 (xhtmlIsEmpty(cur) == 1)) {
1191 /*
1192 * C.2. Empty Elements
1193 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001194 xmlOutputBufferWrite(buf, 3, " />");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001195 } else {
1196 /*
1197 * C.3. Element Minimization and Empty Element Content
1198 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001199 xmlOutputBufferWrite(buf, 3, "></");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001200 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1201 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001202 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001203 }
1204 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001205 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001206 }
1207 return;
1208 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001209 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001210 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001211 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001212 }
1213
1214 /*
1215 * 4.8. Script and Style elements
1216 */
1217 if ((cur->type == XML_ELEMENT_NODE) &&
1218 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1219 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1220 ((cur->ns == NULL) ||
1221 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1222 xmlNodePtr child = cur->children;
1223
1224 while (child != NULL) {
1225 if ((child->type == XML_TEXT_NODE) ||
1226 (child->type == XML_CDATA_SECTION_NODE)) {
1227 /*
1228 * Apparently CDATA escaping for style just break on IE,
1229 * mozilla and galeon, so ...
1230 */
1231 if (xmlStrEqual(cur->name, BAD_CAST "style") &&
1232 (xmlStrchr(child->content, '<') == NULL) &&
1233 (xmlStrchr(child->content, '>') == NULL) &&
1234 (xmlStrchr(child->content, '&') == NULL)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001235 int level = ctxt->level;
1236 int indent = ctxt->format;
1237
1238 ctxt->level = 0;
1239 ctxt->format = 0;
1240 xhtmlNodeDumpOutput(ctxt, child);
1241 ctxt->level = level;
1242 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001243 } else {
1244 start = end = child->content;
1245 while (*end != '\0') {
1246 if (*end == ']' &&
1247 *(end + 1) == ']' &&
1248 *(end + 2) == '>') {
1249 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001250 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001251 xmlOutputBufferWrite(buf, end - start,
1252 (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001253 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001254 start = end;
1255 }
1256 end++;
1257 }
1258 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001259 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1260 xmlOutputBufferWrite(buf, end - start,
1261 (const char *)start);
1262 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001263 }
1264 }
1265 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001266 int level = ctxt->level;
1267 int indent = ctxt->format;
1268
1269 ctxt->level = 0;
1270 ctxt->format = 0;
1271 xhtmlNodeDumpOutput(ctxt, child);
1272 ctxt->level = level;
1273 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001274 }
1275 child = child->next;
1276 }
1277 } else if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001278 int indent = ctxt->format;
1279
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001280 if (format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001281 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001282 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001283 xhtmlNodeListDumpOutput(ctxt, cur->children);
1284 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001285 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001286 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001287 xmlOutputBufferWrite(buf, ctxt->indent_size *
1288 (ctxt->level > ctxt->indent_nr ?
1289 ctxt->indent_nr : ctxt->level),
1290 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001291 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001292 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001293 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1294 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001295 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001296 }
1297
1298 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001299 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001300}
1301#endif
1302
1303/************************************************************************
1304 * *
1305 * Public entry points *
1306 * *
1307 ************************************************************************/
1308
1309/**
1310 * xmlSaveToFd:
1311 * @fd: a file descriptor number
1312 * @encoding: the encoding name to use or NULL
1313 * @options: a set of xmlSaveOptions
1314 *
1315 * Create a document saving context serializing to a file descriptor
1316 * with the encoding and the options given.
1317 *
1318 * Returns a new serialization context or NULL in case of error.
1319 */
1320xmlSaveCtxtPtr
1321xmlSaveToFd(int fd, const char *encoding, int options)
1322{
1323 xmlSaveCtxtPtr ret;
1324
1325 ret = xmlNewSaveCtxt(encoding, options);
1326 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001327 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1328 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001329 xmlFreeSaveCtxt(ret);
1330 return(NULL);
1331 }
1332 return(ret);
1333}
1334
1335/**
1336 * xmlSaveToFilename:
1337 * @filename: a file name or an URL
1338 * @encoding: the encoding name to use or NULL
1339 * @options: a set of xmlSaveOptions
1340 *
1341 * Create a document saving context serializing to a filename or possibly
1342 * to an URL (but this is less reliable) with the encoding and the options
1343 * given.
1344 *
1345 * Returns a new serialization context or NULL in case of error.
1346 */
1347xmlSaveCtxtPtr
1348xmlSaveToFilename(const char *filename, const char *encoding, int options)
1349{
1350 xmlSaveCtxtPtr ret;
1351 int compression = 0; /* TODO handle compression option */
1352
1353 ret = xmlNewSaveCtxt(encoding, options);
1354 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001355 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001356 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001357 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001358 xmlFreeSaveCtxt(ret);
1359 return(NULL);
1360 }
1361 return(ret);
1362}
1363
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001364/**
1365 * xmlSaveToBuffer:
1366 * @buffer: a buffer
1367 * @encoding: the encoding name to use or NULL
1368 * @options: a set of xmlSaveOptions
1369 *
1370 * Create a document saving context serializing to a buffer
1371 * with the encoding and the options given
1372 *
1373 * Returns a new serialization context or NULL in case of error.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001374xmlSaveCtxtPtr
1375xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1376{
1377 TODO
1378 return(NULL);
1379}
Daniel Veillarda2351322004-06-27 12:08:10 +00001380 */
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001381
1382/**
1383 * xmlSaveToIO:
1384 * @iowrite: an I/O write function
1385 * @ioclose: an I/O close function
1386 * @ioctx: an I/O handler
1387 * @encoding: the encoding name to use or NULL
1388 * @options: a set of xmlSaveOptions
1389 *
1390 * Create a document saving context serializing to a file descriptor
1391 * with the encoding and the options given
1392 *
1393 * Returns a new serialization context or NULL in case of error.
1394 */
1395xmlSaveCtxtPtr
1396xmlSaveToIO(xmlOutputWriteCallback iowrite,
1397 xmlOutputCloseCallback ioclose,
1398 void *ioctx, const char *encoding, int options)
1399{
1400 xmlSaveCtxtPtr ret;
1401
1402 ret = xmlNewSaveCtxt(encoding, options);
1403 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001404 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1405 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001406 xmlFreeSaveCtxt(ret);
1407 return(NULL);
1408 }
1409 return(ret);
1410}
1411
1412/**
1413 * xmlSaveDoc:
1414 * @ctxt: a document saving context
1415 * @doc: a document
1416 *
1417 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001418 * TODO: The function is not fully implemented yet as it does not return the
1419 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001420 *
1421 * Returns the number of byte written or -1 in case of error
1422 */
1423long
1424xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1425{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001426 long ret = 0;
1427
1428 xmlDocContentDumpOutput(ctxt, doc);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001429 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001430}
1431
1432/**
1433 * xmlSaveTree:
1434 * @ctxt: a document saving context
1435 * @node: a document
1436 *
1437 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001438 * TODO: The function is not fully implemented yet as it does not return the
1439 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001440 *
1441 * Returns the number of byte written or -1 in case of error
1442 */
1443long
1444xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1445{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001446 long ret = 0;
1447
1448 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001449 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001450}
1451
1452/**
1453 * xmlSaveFlush:
1454 * @ctxt: a document saving context
1455 *
1456 * Flush a document saving context, i.e. make sure that all bytes have
1457 * been output.
1458 *
1459 * Returns the number of byte written or -1 in case of error.
1460 */
1461int
1462xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1463{
1464 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001465 if (ctxt->buf == NULL) return(-1);
1466 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001467}
1468
1469/**
1470 * xmlSaveClose:
1471 * @ctxt: a document saving context
1472 *
1473 * Close a document saving context, i.e. make sure that all bytes have
1474 * been output and free the associated data.
1475 *
1476 * Returns the number of byte written or -1 in case of error.
1477 */
1478int
1479xmlSaveClose(xmlSaveCtxtPtr ctxt)
1480{
1481 int ret;
1482
1483 if (ctxt == NULL) return(-1);
1484 ret = xmlSaveFlush(ctxt);
1485 xmlFreeSaveCtxt(ctxt);
1486 return(ret);
1487}
1488
Daniel Veillard3995bc32004-05-15 18:57:31 +00001489/**
1490 * xmlSaveSetEscape:
1491 * @ctxt: a document saving context
1492 * @escape: the escaping function
1493 *
1494 * Set a custom escaping function to be used for text in element content
1495 *
1496 * Returns 0 if successful or -1 in case of error.
1497 */
1498int
1499xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1500{
1501 if (ctxt == NULL) return(-1);
1502 ctxt->escape = escape;
1503 return(0);
1504}
1505
1506/**
1507 * xmlSaveSetAttrEscape:
1508 * @ctxt: a document saving context
1509 * @escape: the escaping function
1510 *
1511 * Set a custom escaping function to be used for text in attribute content
1512 *
1513 * Returns 0 if successful or -1 in case of error.
1514 */
1515int
1516xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1517{
1518 if (ctxt == NULL) return(-1);
1519 ctxt->escapeAttr = escape;
1520 return(0);
1521}
1522
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001523/************************************************************************
1524 * *
1525 * Public entry points based on buffers *
1526 * *
1527 ************************************************************************/
1528/**
1529 * xmlAttrSerializeTxtContent:
1530 * @buf: the XML buffer output
1531 * @doc: the document
1532 * @attr: the attribute node
1533 * @string: the text content
1534 *
1535 * Serialize text attribute values to an xml simple buffer
1536 */
1537void
1538xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001539 xmlAttrPtr attr, const xmlChar * string)
1540{
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001541 xmlChar *base, *cur;
1542
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001543 if (string == NULL)
1544 return;
1545 base = cur = (xmlChar *) string;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001546 while (*cur != 0) {
1547 if (*cur == '\n') {
1548 if (base != cur)
1549 xmlBufferAdd(buf, base, cur - base);
1550 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1551 cur++;
1552 base = cur;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001553 } else if (*cur == '\r') {
1554 if (base != cur)
1555 xmlBufferAdd(buf, base, cur - base);
1556 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1557 cur++;
1558 base = cur;
1559 } else if (*cur == '\t') {
1560 if (base != cur)
1561 xmlBufferAdd(buf, base, cur - base);
1562 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1563 cur++;
1564 base = cur;
1565 } else if (*cur == '"') {
1566 if (base != cur)
1567 xmlBufferAdd(buf, base, cur - base);
1568 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1569 cur++;
1570 base = cur;
1571 } else if (*cur == '<') {
1572 if (base != cur)
1573 xmlBufferAdd(buf, base, cur - base);
1574 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1575 cur++;
1576 base = cur;
1577 } else if (*cur == '>') {
1578 if (base != cur)
1579 xmlBufferAdd(buf, base, cur - base);
1580 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1581 cur++;
1582 base = cur;
1583 } else if (*cur == '&') {
1584 if (base != cur)
1585 xmlBufferAdd(buf, base, cur - base);
1586 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1587 cur++;
1588 base = cur;
1589 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1590 (doc->encoding == NULL))) {
1591 /*
1592 * We assume we have UTF-8 content.
1593 */
1594 unsigned char tmp[10];
1595 int val = 0, l = 1;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001596
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001597 if (base != cur)
1598 xmlBufferAdd(buf, base, cur - base);
1599 if (*cur < 0xC0) {
1600 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1601 if (doc != NULL)
1602 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1603 xmlSerializeHexCharRef(tmp, *cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001604 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001605 cur++;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001606 base = cur;
1607 continue;
1608 } else if (*cur < 0xE0) {
1609 val = (cur[0]) & 0x1F;
1610 val <<= 6;
1611 val |= (cur[1]) & 0x3F;
1612 l = 2;
1613 } else if (*cur < 0xF0) {
1614 val = (cur[0]) & 0x0F;
1615 val <<= 6;
1616 val |= (cur[1]) & 0x3F;
1617 val <<= 6;
1618 val |= (cur[2]) & 0x3F;
1619 l = 3;
1620 } else if (*cur < 0xF8) {
1621 val = (cur[0]) & 0x07;
1622 val <<= 6;
1623 val |= (cur[1]) & 0x3F;
1624 val <<= 6;
1625 val |= (cur[2]) & 0x3F;
1626 val <<= 6;
1627 val |= (cur[3]) & 0x3F;
1628 l = 4;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001629 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001630 if ((l == 1) || (!IS_CHAR(val))) {
1631 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1632 if (doc != NULL)
1633 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1634
1635 xmlSerializeHexCharRef(tmp, *cur);
1636 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1637 cur++;
1638 base = cur;
1639 continue;
1640 }
1641 /*
1642 * We could do multiple things here. Just save
1643 * as a char ref
1644 */
1645 xmlSerializeHexCharRef(tmp, val);
1646 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1647 cur += l;
1648 base = cur;
1649 } else {
1650 cur++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001651 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001652 }
1653 if (base != cur)
1654 xmlBufferAdd(buf, base, cur - base);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001655}
1656
1657/**
1658 * xmlNodeDump:
1659 * @buf: the XML buffer output
1660 * @doc: the document
1661 * @cur: the current node
1662 * @level: the imbrication level for indenting
1663 * @format: is formatting allowed
1664 *
1665 * Dump an XML node, recursive behaviour,children are printed too.
1666 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1667 * or xmlKeepBlanksDefault(0) was called
1668 *
1669 * Returns the number of bytes written to the buffer or -1 in case of error
1670 */
1671int
1672xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1673 int format)
1674{
1675 unsigned int use;
1676 int ret;
1677 xmlOutputBufferPtr outbuf;
1678
1679 xmlInitParser();
1680
1681 if (cur == NULL) {
1682#ifdef DEBUG_TREE
1683 xmlGenericError(xmlGenericErrorContext,
1684 "xmlNodeDump : node == NULL\n");
1685#endif
1686 return (-1);
1687 }
1688 if (buf == NULL) {
1689#ifdef DEBUG_TREE
1690 xmlGenericError(xmlGenericErrorContext,
1691 "xmlNodeDump : buf == NULL\n");
1692#endif
1693 return (-1);
1694 }
1695 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1696 if (outbuf == NULL) {
1697 xmlSaveErrMemory("creating buffer");
1698 return (-1);
1699 }
1700 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1701 outbuf->buffer = buf;
1702 outbuf->encoder = NULL;
1703 outbuf->writecallback = NULL;
1704 outbuf->closecallback = NULL;
1705 outbuf->context = NULL;
1706 outbuf->written = 0;
1707
1708 use = buf->use;
1709 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1710 xmlFree(outbuf);
1711 ret = buf->use - use;
1712 return (ret);
1713}
1714
1715/**
1716 * xmlElemDump:
1717 * @f: the FILE * for the output
1718 * @doc: the document
1719 * @cur: the current node
1720 *
1721 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1722 */
1723void
1724xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1725{
1726 xmlOutputBufferPtr outbuf;
1727
1728 xmlInitParser();
1729
1730 if (cur == NULL) {
1731#ifdef DEBUG_TREE
1732 xmlGenericError(xmlGenericErrorContext,
1733 "xmlElemDump : cur == NULL\n");
1734#endif
1735 return;
1736 }
1737#ifdef DEBUG_TREE
1738 if (doc == NULL) {
1739 xmlGenericError(xmlGenericErrorContext,
1740 "xmlElemDump : doc == NULL\n");
1741 }
1742#endif
1743
1744 outbuf = xmlOutputBufferCreateFile(f, NULL);
1745 if (outbuf == NULL)
1746 return;
1747 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1748#ifdef LIBXML_HTML_ENABLED
1749 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1750#else
1751 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1752#endif /* LIBXML_HTML_ENABLED */
1753 } else
1754 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1755 xmlOutputBufferClose(outbuf);
1756}
1757
1758/************************************************************************
1759 * *
1760 * Saving functions front-ends *
1761 * *
1762 ************************************************************************/
1763
1764/**
1765 * xmlNodeDumpOutput:
1766 * @buf: the XML buffer output
1767 * @doc: the document
1768 * @cur: the current node
1769 * @level: the imbrication level for indenting
1770 * @format: is formatting allowed
1771 * @encoding: an optional encoding string
1772 *
1773 * Dump an XML node, recursive behaviour, children are printed too.
1774 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1775 * or xmlKeepBlanksDefault(0) was called
1776 */
1777void
1778xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1779 int level, int format, const char *encoding)
1780{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001781 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001782#ifdef LIBXML_HTML_ENABLED
1783 xmlDtdPtr dtd;
1784 int is_xhtml = 0;
1785#endif
1786
1787 xmlInitParser();
1788
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001789 memset(&ctxt, 0, sizeof(ctxt));
1790 ctxt.doc = doc;
1791 ctxt.buf = buf;
1792 ctxt.level = level;
1793 ctxt.format = format;
1794 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001795 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001796
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001797#ifdef LIBXML_HTML_ENABLED
1798 dtd = xmlGetIntSubset(doc);
1799 if (dtd != NULL) {
1800 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1801 if (is_xhtml < 0)
1802 is_xhtml = 0;
1803 if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
1804 (cur->type == XML_ELEMENT_NODE) &&
1805 (xmlStrEqual(cur->name, BAD_CAST "html"))) {
1806 if (encoding != NULL)
1807 htmlSetMetaEncoding((htmlDocPtr) doc,
1808 (const xmlChar *) encoding);
1809 else
1810 htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
1811 }
1812 }
1813
1814 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001815 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001816 else
1817#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001818 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001819}
1820
1821/**
1822 * xmlDocDumpFormatMemoryEnc:
1823 * @out_doc: Document to generate XML text from
1824 * @doc_txt_ptr: Memory pointer for allocated XML text
1825 * @doc_txt_len: Length of the generated XML text
1826 * @txt_encoding: Character encoding to use when generating XML text
1827 * @format: should formatting spaces been added
1828 *
1829 * Dump the current DOM tree into memory using the character encoding specified
1830 * by the caller. Note it is up to the caller of this function to free the
1831 * allocated memory with xmlFree().
1832 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1833 * or xmlKeepBlanksDefault(0) was called
1834 */
1835
1836void
1837xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1838 int * doc_txt_len, const char * txt_encoding,
1839 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001840 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001841 int dummy = 0;
1842 xmlOutputBufferPtr out_buff = NULL;
1843 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1844
1845 if (doc_txt_len == NULL) {
1846 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1847 }
1848
1849 if (doc_txt_ptr == NULL) {
1850 *doc_txt_len = 0;
1851 return;
1852 }
1853
1854 *doc_txt_ptr = NULL;
1855 *doc_txt_len = 0;
1856
1857 if (out_doc == NULL) {
1858 /* No document, no output */
1859 return;
1860 }
1861
1862 /*
1863 * Validate the encoding value, if provided.
1864 * This logic is copied from xmlSaveFileEnc.
1865 */
1866
1867 if (txt_encoding == NULL)
1868 txt_encoding = (const char *) out_doc->encoding;
1869 if (txt_encoding != NULL) {
1870 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1871 if ( conv_hdlr == NULL ) {
1872 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1873 txt_encoding);
1874 return;
1875 }
1876 }
1877
1878 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1879 xmlSaveErrMemory("creating buffer");
1880 return;
1881 }
1882
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001883 memset(&ctxt, 0, sizeof(ctxt));
1884 ctxt.doc = out_doc;
1885 ctxt.buf = out_buff;
1886 ctxt.level = 0;
1887 ctxt.format = format;
1888 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001889 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001890 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001891 xmlOutputBufferFlush(out_buff);
1892 if (out_buff->conv != NULL) {
1893 *doc_txt_len = out_buff->conv->use;
1894 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1895 } else {
1896 *doc_txt_len = out_buff->buffer->use;
1897 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1898 }
1899 (void)xmlOutputBufferClose(out_buff);
1900
1901 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1902 *doc_txt_len = 0;
1903 xmlSaveErrMemory("creating output");
1904 }
1905
1906 return;
1907}
1908
1909/**
1910 * xmlDocDumpMemory:
1911 * @cur: the document
1912 * @mem: OUT: the memory pointer
1913 * @size: OUT: the memory length
1914 *
1915 * Dump an XML document in memory and return the #xmlChar * and it's size
1916 * in bytes. It's up to the caller to free the memory with xmlFree().
1917 * The resulting byte array is zero terminated, though the last 0 is not
1918 * included in the returned size.
1919 */
1920void
1921xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1922 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1923}
1924
1925/**
1926 * xmlDocDumpFormatMemory:
1927 * @cur: the document
1928 * @mem: OUT: the memory pointer
1929 * @size: OUT: the memory length
1930 * @format: should formatting spaces been added
1931 *
1932 *
1933 * Dump an XML document in memory and return the #xmlChar * and it's size.
1934 * It's up to the caller to free the memory with xmlFree().
1935 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1936 * or xmlKeepBlanksDefault(0) was called
1937 */
1938void
1939xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
1940 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
1941}
1942
1943/**
1944 * xmlDocDumpMemoryEnc:
1945 * @out_doc: Document to generate XML text from
1946 * @doc_txt_ptr: Memory pointer for allocated XML text
1947 * @doc_txt_len: Length of the generated XML text
1948 * @txt_encoding: Character encoding to use when generating XML text
1949 *
1950 * Dump the current DOM tree into memory using the character encoding specified
1951 * by the caller. Note it is up to the caller of this function to free the
1952 * allocated memory with xmlFree().
1953 */
1954
1955void
1956xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1957 int * doc_txt_len, const char * txt_encoding) {
1958 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
1959 txt_encoding, 0);
1960}
1961
1962/**
1963 * xmlDocFormatDump:
1964 * @f: the FILE*
1965 * @cur: the document
1966 * @format: should formatting spaces been added
1967 *
1968 * Dump an XML document to an open FILE.
1969 *
1970 * returns: the number of bytes written or -1 in case of failure.
1971 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1972 * or xmlKeepBlanksDefault(0) was called
1973 */
1974int
1975xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001976 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001977 xmlOutputBufferPtr buf;
1978 const char * encoding;
1979 xmlCharEncodingHandlerPtr handler = NULL;
1980 int ret;
1981
1982 if (cur == NULL) {
1983#ifdef DEBUG_TREE
1984 xmlGenericError(xmlGenericErrorContext,
1985 "xmlDocDump : document == NULL\n");
1986#endif
1987 return(-1);
1988 }
1989 encoding = (const char *) cur->encoding;
1990
1991 if (encoding != NULL) {
1992 handler = xmlFindCharEncodingHandler(encoding);
1993 if (handler == NULL) {
1994 xmlFree((char *) cur->encoding);
1995 cur->encoding = NULL;
1996 }
1997 }
1998 buf = xmlOutputBufferCreateFile(f, handler);
1999 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002000 memset(&ctxt, 0, sizeof(ctxt));
2001 ctxt.doc = cur;
2002 ctxt.buf = buf;
2003 ctxt.level = 0;
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 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002008
2009 ret = xmlOutputBufferClose(buf);
2010 return(ret);
2011}
2012
2013/**
2014 * xmlDocDump:
2015 * @f: the FILE*
2016 * @cur: the document
2017 *
2018 * Dump an XML document to an open FILE.
2019 *
2020 * returns: the number of bytes written or -1 in case of failure.
2021 */
2022int
2023xmlDocDump(FILE *f, xmlDocPtr cur) {
2024 return(xmlDocFormatDump (f, cur, 0));
2025}
2026
2027/**
2028 * xmlSaveFileTo:
2029 * @buf: an output I/O buffer
2030 * @cur: the document
2031 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2032 *
2033 * Dump an XML document to an I/O buffer.
2034 *
2035 * returns: the number of bytes written or -1 in case of failure.
2036 */
2037int
2038xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002039 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002040 int ret;
2041
2042 if (buf == NULL) return(0);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002043 memset(&ctxt, 0, sizeof(ctxt));
2044 ctxt.doc = cur;
2045 ctxt.buf = buf;
2046 ctxt.level = 0;
2047 ctxt.format = 0;
2048 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002049 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002050 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002051 ret = xmlOutputBufferClose(buf);
2052 return(ret);
2053}
2054
2055/**
2056 * xmlSaveFormatFileTo:
2057 * @buf: an output I/O buffer
2058 * @cur: the document
2059 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2060 * @format: should formatting spaces been added
2061 *
2062 * Dump an XML document to an I/O buffer.
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002063 * NOTE: the I/O buffer is closed as part of the call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002064 *
2065 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002066 */
2067int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002068xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2069 const char *encoding, int format)
2070{
2071 xmlSaveCtxt ctxt;
2072 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002073
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002074 if (buf == NULL)
2075 return (0);
2076 memset(&ctxt, 0, sizeof(ctxt));
2077 ctxt.doc = cur;
2078 ctxt.buf = buf;
2079 ctxt.level = 0;
2080 ctxt.format = format;
2081 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002082 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002083 xmlDocContentDumpOutput(&ctxt, cur);
2084 ret = xmlOutputBufferClose(buf);
2085 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002086}
2087
2088/**
2089 * xmlSaveFormatFileEnc:
2090 * @filename: the filename or URL to output
2091 * @cur: the document being saved
2092 * @encoding: the name of the encoding to use or NULL.
2093 * @format: should formatting spaces be added.
2094 *
2095 * Dump an XML document to a file or an URL.
2096 *
2097 * Returns the number of bytes written or -1 in case of error.
2098 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2099 * or xmlKeepBlanksDefault(0) was called
2100 */
2101int
2102xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2103 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002104 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002105 xmlOutputBufferPtr buf;
2106 xmlCharEncodingHandlerPtr handler = NULL;
2107 int ret;
2108
2109 if (cur == NULL)
2110 return(-1);
2111
2112 if (encoding == NULL)
2113 encoding = (const char *) cur->encoding;
2114
2115 if (encoding != NULL) {
2116
2117 handler = xmlFindCharEncodingHandler(encoding);
2118 if (handler == NULL)
2119 return(-1);
2120 }
2121
2122#ifdef HAVE_ZLIB_H
2123 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2124#endif
2125 /*
2126 * save the content to a temp buffer.
2127 */
2128 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2129 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002130 memset(&ctxt, 0, sizeof(ctxt));
2131 ctxt.doc = cur;
2132 ctxt.buf = buf;
2133 ctxt.level = 0;
2134 ctxt.format = format;
2135 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002136 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002137
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002138 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002139
2140 ret = xmlOutputBufferClose(buf);
2141 return(ret);
2142}
2143
2144
2145/**
2146 * xmlSaveFileEnc:
2147 * @filename: the filename (or URL)
2148 * @cur: the document
2149 * @encoding: the name of an encoding (or NULL)
2150 *
2151 * Dump an XML document, converting it to the given encoding
2152 *
2153 * returns: the number of bytes written or -1 in case of failure.
2154 */
2155int
2156xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2157 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2158}
2159
2160/**
2161 * xmlSaveFormatFile:
2162 * @filename: the filename (or URL)
2163 * @cur: the document
2164 * @format: should formatting spaces been added
2165 *
2166 * Dump an XML document to a file. Will use compression if
2167 * compiled in and enabled. If @filename is "-" the stdout file is
2168 * used. If @format is set then the document will be indented on output.
2169 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2170 * or xmlKeepBlanksDefault(0) was called
2171 *
2172 * returns: the number of bytes written or -1 in case of failure.
2173 */
2174int
2175xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2176 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2177}
2178
2179/**
2180 * xmlSaveFile:
2181 * @filename: the filename (or URL)
2182 * @cur: the document
2183 *
2184 * Dump an XML document to a file. Will use compression if
2185 * compiled in and enabled. If @filename is "-" the stdout file is
2186 * used.
2187 * returns: the number of bytes written or -1 in case of failure.
2188 */
2189int
2190xmlSaveFile(const char *filename, xmlDocPtr cur) {
2191 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2192}
2193
2194#endif /* LIBXML_OUTPUT_ENABLED */
2195