blob: 769899133eae4bbacac8b179677f5c78f350302e [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 Veillard32b7cdb2004-03-15 13:46:37 +0000526 format = ctxt->format;
527 level = ctxt->level;
528 doc = ctxt->doc;
529 ctxt->format = 0;
530 ctxt->level = -1;
531 ctxt->doc = dtd->doc;
532 xmlNodeListDumpOutput(ctxt, dtd->children);
533 ctxt->format = format;
534 ctxt->level = level;
535 ctxt->doc = doc;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000536 xmlOutputBufferWrite(buf, 2, "]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000537}
538
539/**
540 * xmlAttrDumpOutput:
541 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000542 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000543 *
544 * Dump an XML attribute
545 */
546static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000547xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
548 xmlOutputBufferPtr buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000549
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000550 if (cur == NULL) return;
551 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000552 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000553 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
554 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000555 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000556 }
557 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000558 xmlOutputBufferWrite(buf, 2, "=\"");
559 xmlAttrSerializeContent(buf, cur);
560 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000561}
562
563/**
564 * xmlAttrListDumpOutput:
565 * @buf: the XML buffer output
566 * @doc: the document
567 * @cur: the first attribute pointer
568 * @encoding: an optional encoding string
569 *
570 * Dump a list of XML attributes
571 */
572static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000573xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
574 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000575 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000576 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000577 cur = cur->next;
578 }
579}
580
581
582
583/**
584 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000585 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000586 *
587 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000588 */
589static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000590xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000591 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000592
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000593 if (cur == NULL) return;
594 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000595 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000596 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000597 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000598 xmlOutputBufferWrite(buf, ctxt->indent_size *
599 (ctxt->level > ctxt->indent_nr ?
600 ctxt->indent_nr : ctxt->level),
601 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000602 xmlNodeDumpOutputInternal(ctxt, cur);
603 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000604 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000605 }
606 cur = cur->next;
607 }
608}
609
610/**
611 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000612 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000613 *
614 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000615 */
616static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000617xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000618 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000619 xmlNodePtr tmp;
620 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000621 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000622
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000623 if (cur == NULL) return;
624 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000625 if (cur->type == XML_XINCLUDE_START)
626 return;
627 if (cur->type == XML_XINCLUDE_END)
628 return;
629 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000630 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000631 return;
632 }
633 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000634 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000635 return;
636 }
637 if (cur->type == XML_ELEMENT_DECL) {
638 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
639 return;
640 }
641 if (cur->type == XML_ATTRIBUTE_DECL) {
642 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
643 return;
644 }
645 if (cur->type == XML_ENTITY_DECL) {
646 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
647 return;
648 }
649 if (cur->type == XML_TEXT_NODE) {
650 if (cur->content != NULL) {
651 if ((cur->name == xmlStringText) ||
652 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000653
Daniel Veillard3995bc32004-05-15 18:57:31 +0000654 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000655 } else {
656 /*
657 * Disable escaping, needed for XSLT
658 */
659 xmlOutputBufferWriteString(buf, (const char *) cur->content);
660 }
661 }
662
663 return;
664 }
665 if (cur->type == XML_PI_NODE) {
666 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000667 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000668 xmlOutputBufferWriteString(buf, (const char *)cur->name);
669 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000670 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000671 xmlOutputBufferWriteString(buf, (const char *)cur->content);
672 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000673 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000674 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000675 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000676 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000677 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000678 }
679 return;
680 }
681 if (cur->type == XML_COMMENT_NODE) {
682 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000683 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000684 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000685 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000686 }
687 return;
688 }
689 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000690 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000691 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000692 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000693 return;
694 }
695 if (cur->type == XML_CDATA_SECTION_NODE) {
696 start = end = cur->content;
697 while (*end != '\0') {
698 if ((*end == ']') && (*(end + 1) == ']') && (*(end + 2) == '>')) {
699 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000700 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000701 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000702 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000703 start = end;
704 }
705 end++;
706 }
707 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000708 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000709 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000710 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000711 }
712 return;
713 }
714 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000715 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000716 return;
717 }
718 if (cur->type == XML_NAMESPACE_DECL) {
719 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
720 return;
721 }
722
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000723 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000724 if (format == 1) {
725 tmp = cur->children;
726 while (tmp != NULL) {
727 if ((tmp->type == XML_TEXT_NODE) ||
728 (tmp->type == XML_CDATA_SECTION_NODE) ||
729 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000730 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000731 break;
732 }
733 tmp = tmp->next;
734 }
735 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000736 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000737 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
738 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000739 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000740 }
741
742 xmlOutputBufferWriteString(buf, (const char *)cur->name);
743 if (cur->nsDef)
744 xmlNsListDumpOutput(buf, cur->nsDef);
745 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000746 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000747
748 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
749 (cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000750 xmlOutputBufferWrite(buf, 2, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000751 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000752 return;
753 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000754 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000755 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000756 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000757 }
758 if (cur->children != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000759 if (ctxt->format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000760 if (ctxt->level >= 0) ctxt->level++;
761 xmlNodeListDumpOutput(ctxt, cur->children);
762 if (ctxt->level > 0) ctxt->level--;
763 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000764 xmlOutputBufferWrite(buf, ctxt->indent_size *
765 (ctxt->level > ctxt->indent_nr ?
766 ctxt->indent_nr : ctxt->level),
767 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000768 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000769 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000770 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
771 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000772 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000773 }
774
775 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000776 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000777 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000778}
779
780/**
781 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000782 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000783 *
784 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000785 */
786static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000787xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000788#ifdef LIBXML_HTML_ENABLED
789 xmlDtdPtr dtd;
790 int is_xhtml = 0;
791#endif
792 const xmlChar *oldenc = cur->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000793 const xmlChar *encoding = ctxt->encoding;
794 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000795
796 xmlInitParser();
797
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000798 if (ctxt->encoding != NULL)
799 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000800
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000801 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000802 xmlOutputBufferWrite(buf, 14, "<?xml version=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000803 if (cur->version != NULL)
804 xmlBufferWriteQuotedString(buf->buffer, cur->version);
805 else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000806 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000807 if (ctxt->encoding == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000808 if (cur->encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000809 encoding = cur->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000810 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000811 encoding = (const xmlChar *)
812 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000813 }
814 if (encoding != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000815 xmlOutputBufferWrite(buf, 10, " encoding=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000816 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
817 }
818 switch (cur->standalone) {
819 case 0:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000820 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000821 break;
822 case 1:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000823 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000824 break;
825 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000826 xmlOutputBufferWrite(buf, 3, "?>\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000827
828#ifdef LIBXML_HTML_ENABLED
829 dtd = xmlGetIntSubset(cur);
830 if (dtd != NULL) {
831 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
832 if (is_xhtml < 0) is_xhtml = 0;
833 }
834 if (is_xhtml) {
835 if (encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000836 htmlSetMetaEncoding(cur, (const xmlChar *) ctxt->encoding);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000837 else
838 htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
839 }
840#endif
841 if (cur->children != NULL) {
842 xmlNodePtr child = cur->children;
843
844 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000845 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000846#ifdef LIBXML_HTML_ENABLED
847 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000848 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000849 else
850#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000851 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000852 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000853 child = child->next;
854 }
855 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000856 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000857 cur->encoding = oldenc;
858}
859
860#ifdef LIBXML_HTML_ENABLED
861/************************************************************************
862 * *
863 * Functions specific to XHTML serialization *
864 * *
865 ************************************************************************/
866
867/**
868 * xhtmlIsEmpty:
869 * @node: the node
870 *
871 * Check if a node is an empty xhtml node
872 *
873 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
874 */
875static int
876xhtmlIsEmpty(xmlNodePtr node) {
877 if (node == NULL)
878 return(-1);
879 if (node->type != XML_ELEMENT_NODE)
880 return(0);
881 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
882 return(0);
883 if (node->children != NULL)
884 return(0);
885 switch (node->name[0]) {
886 case 'a':
887 if (xmlStrEqual(node->name, BAD_CAST "area"))
888 return(1);
889 return(0);
890 case 'b':
891 if (xmlStrEqual(node->name, BAD_CAST "br"))
892 return(1);
893 if (xmlStrEqual(node->name, BAD_CAST "base"))
894 return(1);
895 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
896 return(1);
897 return(0);
898 case 'c':
899 if (xmlStrEqual(node->name, BAD_CAST "col"))
900 return(1);
901 return(0);
902 case 'f':
903 if (xmlStrEqual(node->name, BAD_CAST "frame"))
904 return(1);
905 return(0);
906 case 'h':
907 if (xmlStrEqual(node->name, BAD_CAST "hr"))
908 return(1);
909 return(0);
910 case 'i':
911 if (xmlStrEqual(node->name, BAD_CAST "img"))
912 return(1);
913 if (xmlStrEqual(node->name, BAD_CAST "input"))
914 return(1);
915 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
916 return(1);
917 return(0);
918 case 'l':
919 if (xmlStrEqual(node->name, BAD_CAST "link"))
920 return(1);
921 return(0);
922 case 'm':
923 if (xmlStrEqual(node->name, BAD_CAST "meta"))
924 return(1);
925 return(0);
926 case 'p':
927 if (xmlStrEqual(node->name, BAD_CAST "param"))
928 return(1);
929 return(0);
930 }
931 return(0);
932}
933
934/**
935 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000936 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000937 *
938 * Dump a list of XML attributes
939 */
940static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000941xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000942 xmlAttrPtr xml_lang = NULL;
943 xmlAttrPtr lang = NULL;
944 xmlAttrPtr name = NULL;
945 xmlAttrPtr id = NULL;
946 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000947 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000948
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000949 if (cur == NULL) return;
950 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000951 parent = cur->parent;
952 while (cur != NULL) {
953 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
954 id = cur;
955 else
956 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
957 name = cur;
958 else
959 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
960 lang = cur;
961 else
962 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
963 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
964 xml_lang = cur;
965 else if ((cur->ns == NULL) &&
966 ((cur->children == NULL) ||
967 (cur->children->content == NULL) ||
968 (cur->children->content[0] == 0)) &&
969 (htmlIsBooleanAttr(cur->name))) {
970 if (cur->children != NULL)
971 xmlFreeNode(cur->children);
972 cur->children = xmlNewText(cur->name);
973 if (cur->children != NULL)
974 cur->children->parent = (xmlNodePtr) cur;
975 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000976 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000977 cur = cur->next;
978 }
979 /*
980 * C.8
981 */
982 if ((name != NULL) && (id == NULL)) {
983 if ((parent != NULL) && (parent->name != NULL) &&
984 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
985 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
986 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
987 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
988 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
989 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
990 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
991 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
992 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000993 xmlOutputBufferWrite(buf, 5, " id=\"");
994 xmlAttrSerializeContent(buf, name);
995 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000996 }
997 }
998 /*
999 * C.7.
1000 */
1001 if ((lang != NULL) && (xml_lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001002 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1003 xmlAttrSerializeContent(buf, lang);
1004 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001005 } else
1006 if ((xml_lang != NULL) && (lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001007 xmlOutputBufferWrite(buf, 7, " lang=\"");
1008 xmlAttrSerializeContent(buf, xml_lang);
1009 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001010 }
1011}
1012
1013/**
1014 * xhtmlNodeListDumpOutput:
1015 * @buf: the XML buffer output
1016 * @doc: the XHTML document
1017 * @cur: the first node
1018 * @level: the imbrication level for indenting
1019 * @format: is formatting allowed
1020 * @encoding: an optional encoding string
1021 *
1022 * Dump an XML node list, recursive behaviour, children are printed too.
1023 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1024 * or xmlKeepBlanksDefault(0) was called
1025 */
1026static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001027xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001028 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001029
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001030 if (cur == NULL) return;
1031 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001032 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001033 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001034 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +00001035 xmlOutputBufferWrite(buf, ctxt->indent_size *
1036 (ctxt->level > ctxt->indent_nr ?
1037 ctxt->indent_nr : ctxt->level),
1038 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001039 xhtmlNodeDumpOutput(ctxt, cur);
1040 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001041 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001042 }
1043 cur = cur->next;
1044 }
1045}
1046
1047/**
1048 * xhtmlNodeDumpOutput:
1049 * @buf: the XML buffer output
1050 * @doc: the XHTML document
1051 * @cur: the current node
1052 * @level: the imbrication level for indenting
1053 * @format: is formatting allowed
1054 * @encoding: an optional encoding string
1055 *
1056 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001057 */
1058static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001059xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +00001060 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001061 xmlNodePtr tmp;
1062 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001063 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001064
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001065 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001066 if (cur->type == XML_XINCLUDE_START)
1067 return;
1068 if (cur->type == XML_XINCLUDE_END)
1069 return;
1070 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001071 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001072 return;
1073 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001074 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001075 if (cur->type == XML_ELEMENT_DECL) {
1076 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1077 return;
1078 }
1079 if (cur->type == XML_ATTRIBUTE_DECL) {
1080 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1081 return;
1082 }
1083 if (cur->type == XML_ENTITY_DECL) {
1084 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1085 return;
1086 }
1087 if (cur->type == XML_TEXT_NODE) {
1088 if (cur->content != NULL) {
1089 if ((cur->name == xmlStringText) ||
1090 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001091 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001092 } else {
1093 /*
1094 * Disable escaping, needed for XSLT
1095 */
1096 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1097 }
1098 }
1099
1100 return;
1101 }
1102 if (cur->type == XML_PI_NODE) {
1103 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001104 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001105 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1106 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001107 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001108 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1109 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001110 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001111 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001112 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001113 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001114 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001115 }
1116 return;
1117 }
1118 if (cur->type == XML_COMMENT_NODE) {
1119 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001120 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001121 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001122 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001123 }
1124 return;
1125 }
1126 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001127 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001128 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001129 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001130 return;
1131 }
1132 if (cur->type == XML_CDATA_SECTION_NODE) {
1133 start = end = cur->content;
1134 while (*end != '\0') {
1135 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1136 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001137 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001138 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001139 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001140 start = end;
1141 }
1142 end++;
1143 }
1144 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001145 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001146 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001147 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001148 }
1149 return;
1150 }
1151
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001152 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001153 if (format == 1) {
1154 tmp = cur->children;
1155 while (tmp != NULL) {
1156 if ((tmp->type == XML_TEXT_NODE) ||
1157 (tmp->type == XML_ENTITY_REF_NODE)) {
1158 format = 0;
1159 break;
1160 }
1161 tmp = tmp->next;
1162 }
1163 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001164 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001165 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1166 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001167 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001168 }
1169
1170 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1171 if (cur->nsDef)
1172 xmlNsListDumpOutput(buf, cur->nsDef);
1173 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1174 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1175 /*
1176 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1177 */
1178 xmlOutputBufferWriteString(buf,
1179 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1180 }
1181 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001182 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001183
1184 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1185 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1186 (xhtmlIsEmpty(cur) == 1)) {
1187 /*
1188 * C.2. Empty Elements
1189 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001190 xmlOutputBufferWrite(buf, 3, " />");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001191 } else {
1192 /*
1193 * C.3. Element Minimization and Empty Element Content
1194 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001195 xmlOutputBufferWrite(buf, 3, "></");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001196 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1197 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001198 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001199 }
1200 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001201 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001202 }
1203 return;
1204 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001205 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001206 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001207 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001208 }
1209
1210 /*
1211 * 4.8. Script and Style elements
1212 */
1213 if ((cur->type == XML_ELEMENT_NODE) &&
1214 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1215 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1216 ((cur->ns == NULL) ||
1217 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1218 xmlNodePtr child = cur->children;
1219
1220 while (child != NULL) {
1221 if ((child->type == XML_TEXT_NODE) ||
1222 (child->type == XML_CDATA_SECTION_NODE)) {
1223 /*
1224 * Apparently CDATA escaping for style just break on IE,
1225 * mozilla and galeon, so ...
1226 */
1227 if (xmlStrEqual(cur->name, BAD_CAST "style") &&
1228 (xmlStrchr(child->content, '<') == NULL) &&
1229 (xmlStrchr(child->content, '>') == NULL) &&
1230 (xmlStrchr(child->content, '&') == NULL)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001231 int level = ctxt->level;
1232 int indent = ctxt->format;
1233
1234 ctxt->level = 0;
1235 ctxt->format = 0;
1236 xhtmlNodeDumpOutput(ctxt, child);
1237 ctxt->level = level;
1238 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001239 } else {
1240 start = end = child->content;
1241 while (*end != '\0') {
1242 if (*end == ']' &&
1243 *(end + 1) == ']' &&
1244 *(end + 2) == '>') {
1245 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001246 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001247 xmlOutputBufferWrite(buf, end - start,
1248 (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001249 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001250 start = end;
1251 }
1252 end++;
1253 }
1254 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001255 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1256 xmlOutputBufferWrite(buf, end - start,
1257 (const char *)start);
1258 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001259 }
1260 }
1261 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001262 int level = ctxt->level;
1263 int indent = ctxt->format;
1264
1265 ctxt->level = 0;
1266 ctxt->format = 0;
1267 xhtmlNodeDumpOutput(ctxt, child);
1268 ctxt->level = level;
1269 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001270 }
1271 child = child->next;
1272 }
1273 } else if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001274 int indent = ctxt->format;
1275
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001276 if (format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001277 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001278 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001279 xhtmlNodeListDumpOutput(ctxt, cur->children);
1280 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001281 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001282 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001283 xmlOutputBufferWrite(buf, ctxt->indent_size *
1284 (ctxt->level > ctxt->indent_nr ?
1285 ctxt->indent_nr : ctxt->level),
1286 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001287 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001288 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001289 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1290 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001291 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001292 }
1293
1294 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001295 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001296}
1297#endif
1298
1299/************************************************************************
1300 * *
1301 * Public entry points *
1302 * *
1303 ************************************************************************/
1304
1305/**
1306 * xmlSaveToFd:
1307 * @fd: a file descriptor number
1308 * @encoding: the encoding name to use or NULL
1309 * @options: a set of xmlSaveOptions
1310 *
1311 * Create a document saving context serializing to a file descriptor
1312 * with the encoding and the options given.
1313 *
1314 * Returns a new serialization context or NULL in case of error.
1315 */
1316xmlSaveCtxtPtr
1317xmlSaveToFd(int fd, const char *encoding, int options)
1318{
1319 xmlSaveCtxtPtr ret;
1320
1321 ret = xmlNewSaveCtxt(encoding, options);
1322 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001323 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1324 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001325 xmlFreeSaveCtxt(ret);
1326 return(NULL);
1327 }
1328 return(ret);
1329}
1330
1331/**
1332 * xmlSaveToFilename:
1333 * @filename: a file name or an URL
1334 * @encoding: the encoding name to use or NULL
1335 * @options: a set of xmlSaveOptions
1336 *
1337 * Create a document saving context serializing to a filename or possibly
1338 * to an URL (but this is less reliable) with the encoding and the options
1339 * given.
1340 *
1341 * Returns a new serialization context or NULL in case of error.
1342 */
1343xmlSaveCtxtPtr
1344xmlSaveToFilename(const char *filename, const char *encoding, int options)
1345{
1346 xmlSaveCtxtPtr ret;
1347 int compression = 0; /* TODO handle compression option */
1348
1349 ret = xmlNewSaveCtxt(encoding, options);
1350 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001351 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001352 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001353 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001354 xmlFreeSaveCtxt(ret);
1355 return(NULL);
1356 }
1357 return(ret);
1358}
1359
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001360/**
1361 * xmlSaveToBuffer:
1362 * @buffer: a buffer
1363 * @encoding: the encoding name to use or NULL
1364 * @options: a set of xmlSaveOptions
1365 *
1366 * Create a document saving context serializing to a buffer
1367 * with the encoding and the options given
1368 *
1369 * Returns a new serialization context or NULL in case of error.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001370xmlSaveCtxtPtr
1371xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1372{
1373 TODO
1374 return(NULL);
1375}
Daniel Veillarda2351322004-06-27 12:08:10 +00001376 */
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001377
1378/**
1379 * xmlSaveToIO:
1380 * @iowrite: an I/O write function
1381 * @ioclose: an I/O close function
1382 * @ioctx: an I/O handler
1383 * @encoding: the encoding name to use or NULL
1384 * @options: a set of xmlSaveOptions
1385 *
1386 * Create a document saving context serializing to a file descriptor
1387 * with the encoding and the options given
1388 *
1389 * Returns a new serialization context or NULL in case of error.
1390 */
1391xmlSaveCtxtPtr
1392xmlSaveToIO(xmlOutputWriteCallback iowrite,
1393 xmlOutputCloseCallback ioclose,
1394 void *ioctx, const char *encoding, int options)
1395{
1396 xmlSaveCtxtPtr ret;
1397
1398 ret = xmlNewSaveCtxt(encoding, options);
1399 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001400 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1401 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001402 xmlFreeSaveCtxt(ret);
1403 return(NULL);
1404 }
1405 return(ret);
1406}
1407
1408/**
1409 * xmlSaveDoc:
1410 * @ctxt: a document saving context
1411 * @doc: a document
1412 *
1413 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001414 * TODO: The function is not fully implemented yet as it does not return the
1415 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001416 *
1417 * Returns the number of byte written or -1 in case of error
1418 */
1419long
1420xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1421{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001422 long ret = 0;
1423
1424 xmlDocContentDumpOutput(ctxt, doc);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001425 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001426}
1427
1428/**
1429 * xmlSaveTree:
1430 * @ctxt: a document saving context
1431 * @node: a document
1432 *
1433 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001434 * TODO: The function is not fully implemented yet as it does not return the
1435 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001436 *
1437 * Returns the number of byte written or -1 in case of error
1438 */
1439long
1440xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1441{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001442 long ret = 0;
1443
1444 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001445 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001446}
1447
1448/**
1449 * xmlSaveFlush:
1450 * @ctxt: a document saving context
1451 *
1452 * Flush a document saving context, i.e. make sure that all bytes have
1453 * been output.
1454 *
1455 * Returns the number of byte written or -1 in case of error.
1456 */
1457int
1458xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1459{
1460 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001461 if (ctxt->buf == NULL) return(-1);
1462 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001463}
1464
1465/**
1466 * xmlSaveClose:
1467 * @ctxt: a document saving context
1468 *
1469 * Close a document saving context, i.e. make sure that all bytes have
1470 * been output and free the associated data.
1471 *
1472 * Returns the number of byte written or -1 in case of error.
1473 */
1474int
1475xmlSaveClose(xmlSaveCtxtPtr ctxt)
1476{
1477 int ret;
1478
1479 if (ctxt == NULL) return(-1);
1480 ret = xmlSaveFlush(ctxt);
1481 xmlFreeSaveCtxt(ctxt);
1482 return(ret);
1483}
1484
Daniel Veillard3995bc32004-05-15 18:57:31 +00001485/**
1486 * xmlSaveSetEscape:
1487 * @ctxt: a document saving context
1488 * @escape: the escaping function
1489 *
1490 * Set a custom escaping function to be used for text in element content
1491 *
1492 * Returns 0 if successful or -1 in case of error.
1493 */
1494int
1495xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1496{
1497 if (ctxt == NULL) return(-1);
1498 ctxt->escape = escape;
1499 return(0);
1500}
1501
1502/**
1503 * xmlSaveSetAttrEscape:
1504 * @ctxt: a document saving context
1505 * @escape: the escaping function
1506 *
1507 * Set a custom escaping function to be used for text in attribute content
1508 *
1509 * Returns 0 if successful or -1 in case of error.
1510 */
1511int
1512xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1513{
1514 if (ctxt == NULL) return(-1);
1515 ctxt->escapeAttr = escape;
1516 return(0);
1517}
1518
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001519/************************************************************************
1520 * *
1521 * Public entry points based on buffers *
1522 * *
1523 ************************************************************************/
1524/**
1525 * xmlAttrSerializeTxtContent:
1526 * @buf: the XML buffer output
1527 * @doc: the document
1528 * @attr: the attribute node
1529 * @string: the text content
1530 *
1531 * Serialize text attribute values to an xml simple buffer
1532 */
1533void
1534xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001535 xmlAttrPtr attr, const xmlChar * string)
1536{
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001537 xmlChar *base, *cur;
1538
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001539 if (string == NULL)
1540 return;
1541 base = cur = (xmlChar *) string;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001542 while (*cur != 0) {
1543 if (*cur == '\n') {
1544 if (base != cur)
1545 xmlBufferAdd(buf, base, cur - base);
1546 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1547 cur++;
1548 base = cur;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001549 } else if (*cur == '\r') {
1550 if (base != cur)
1551 xmlBufferAdd(buf, base, cur - base);
1552 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1553 cur++;
1554 base = cur;
1555 } else if (*cur == '\t') {
1556 if (base != cur)
1557 xmlBufferAdd(buf, base, cur - base);
1558 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1559 cur++;
1560 base = cur;
1561 } else if (*cur == '"') {
1562 if (base != cur)
1563 xmlBufferAdd(buf, base, cur - base);
1564 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1565 cur++;
1566 base = cur;
1567 } else if (*cur == '<') {
1568 if (base != cur)
1569 xmlBufferAdd(buf, base, cur - base);
1570 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1571 cur++;
1572 base = cur;
1573 } else if (*cur == '>') {
1574 if (base != cur)
1575 xmlBufferAdd(buf, base, cur - base);
1576 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1577 cur++;
1578 base = cur;
1579 } else if (*cur == '&') {
1580 if (base != cur)
1581 xmlBufferAdd(buf, base, cur - base);
1582 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1583 cur++;
1584 base = cur;
1585 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1586 (doc->encoding == NULL))) {
1587 /*
1588 * We assume we have UTF-8 content.
1589 */
1590 unsigned char tmp[10];
1591 int val = 0, l = 1;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001592
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001593 if (base != cur)
1594 xmlBufferAdd(buf, base, cur - base);
1595 if (*cur < 0xC0) {
1596 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1597 if (doc != NULL)
1598 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1599 xmlSerializeHexCharRef(tmp, *cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001600 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001601 cur++;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001602 base = cur;
1603 continue;
1604 } else if (*cur < 0xE0) {
1605 val = (cur[0]) & 0x1F;
1606 val <<= 6;
1607 val |= (cur[1]) & 0x3F;
1608 l = 2;
1609 } else if (*cur < 0xF0) {
1610 val = (cur[0]) & 0x0F;
1611 val <<= 6;
1612 val |= (cur[1]) & 0x3F;
1613 val <<= 6;
1614 val |= (cur[2]) & 0x3F;
1615 l = 3;
1616 } else if (*cur < 0xF8) {
1617 val = (cur[0]) & 0x07;
1618 val <<= 6;
1619 val |= (cur[1]) & 0x3F;
1620 val <<= 6;
1621 val |= (cur[2]) & 0x3F;
1622 val <<= 6;
1623 val |= (cur[3]) & 0x3F;
1624 l = 4;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001625 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001626 if ((l == 1) || (!IS_CHAR(val))) {
1627 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1628 if (doc != NULL)
1629 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1630
1631 xmlSerializeHexCharRef(tmp, *cur);
1632 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1633 cur++;
1634 base = cur;
1635 continue;
1636 }
1637 /*
1638 * We could do multiple things here. Just save
1639 * as a char ref
1640 */
1641 xmlSerializeHexCharRef(tmp, val);
1642 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1643 cur += l;
1644 base = cur;
1645 } else {
1646 cur++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001647 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001648 }
1649 if (base != cur)
1650 xmlBufferAdd(buf, base, cur - base);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001651}
1652
1653/**
1654 * xmlNodeDump:
1655 * @buf: the XML buffer output
1656 * @doc: the document
1657 * @cur: the current node
1658 * @level: the imbrication level for indenting
1659 * @format: is formatting allowed
1660 *
1661 * Dump an XML node, recursive behaviour,children are printed too.
1662 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1663 * or xmlKeepBlanksDefault(0) was called
1664 *
1665 * Returns the number of bytes written to the buffer or -1 in case of error
1666 */
1667int
1668xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1669 int format)
1670{
1671 unsigned int use;
1672 int ret;
1673 xmlOutputBufferPtr outbuf;
1674
1675 xmlInitParser();
1676
1677 if (cur == NULL) {
1678#ifdef DEBUG_TREE
1679 xmlGenericError(xmlGenericErrorContext,
1680 "xmlNodeDump : node == NULL\n");
1681#endif
1682 return (-1);
1683 }
1684 if (buf == NULL) {
1685#ifdef DEBUG_TREE
1686 xmlGenericError(xmlGenericErrorContext,
1687 "xmlNodeDump : buf == NULL\n");
1688#endif
1689 return (-1);
1690 }
1691 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1692 if (outbuf == NULL) {
1693 xmlSaveErrMemory("creating buffer");
1694 return (-1);
1695 }
1696 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1697 outbuf->buffer = buf;
1698 outbuf->encoder = NULL;
1699 outbuf->writecallback = NULL;
1700 outbuf->closecallback = NULL;
1701 outbuf->context = NULL;
1702 outbuf->written = 0;
1703
1704 use = buf->use;
1705 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1706 xmlFree(outbuf);
1707 ret = buf->use - use;
1708 return (ret);
1709}
1710
1711/**
1712 * xmlElemDump:
1713 * @f: the FILE * for the output
1714 * @doc: the document
1715 * @cur: the current node
1716 *
1717 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1718 */
1719void
1720xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1721{
1722 xmlOutputBufferPtr outbuf;
1723
1724 xmlInitParser();
1725
1726 if (cur == NULL) {
1727#ifdef DEBUG_TREE
1728 xmlGenericError(xmlGenericErrorContext,
1729 "xmlElemDump : cur == NULL\n");
1730#endif
1731 return;
1732 }
1733#ifdef DEBUG_TREE
1734 if (doc == NULL) {
1735 xmlGenericError(xmlGenericErrorContext,
1736 "xmlElemDump : doc == NULL\n");
1737 }
1738#endif
1739
1740 outbuf = xmlOutputBufferCreateFile(f, NULL);
1741 if (outbuf == NULL)
1742 return;
1743 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1744#ifdef LIBXML_HTML_ENABLED
1745 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1746#else
1747 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1748#endif /* LIBXML_HTML_ENABLED */
1749 } else
1750 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1751 xmlOutputBufferClose(outbuf);
1752}
1753
1754/************************************************************************
1755 * *
1756 * Saving functions front-ends *
1757 * *
1758 ************************************************************************/
1759
1760/**
1761 * xmlNodeDumpOutput:
1762 * @buf: the XML buffer output
1763 * @doc: the document
1764 * @cur: the current node
1765 * @level: the imbrication level for indenting
1766 * @format: is formatting allowed
1767 * @encoding: an optional encoding string
1768 *
1769 * Dump an XML node, recursive behaviour, children are printed too.
1770 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1771 * or xmlKeepBlanksDefault(0) was called
1772 */
1773void
1774xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1775 int level, int format, const char *encoding)
1776{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001777 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001778#ifdef LIBXML_HTML_ENABLED
1779 xmlDtdPtr dtd;
1780 int is_xhtml = 0;
1781#endif
1782
1783 xmlInitParser();
1784
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001785 memset(&ctxt, 0, sizeof(ctxt));
1786 ctxt.doc = doc;
1787 ctxt.buf = buf;
1788 ctxt.level = level;
1789 ctxt.format = format;
1790 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001791 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001792
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001793#ifdef LIBXML_HTML_ENABLED
1794 dtd = xmlGetIntSubset(doc);
1795 if (dtd != NULL) {
1796 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1797 if (is_xhtml < 0)
1798 is_xhtml = 0;
1799 if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
1800 (cur->type == XML_ELEMENT_NODE) &&
1801 (xmlStrEqual(cur->name, BAD_CAST "html"))) {
1802 if (encoding != NULL)
1803 htmlSetMetaEncoding((htmlDocPtr) doc,
1804 (const xmlChar *) encoding);
1805 else
1806 htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
1807 }
1808 }
1809
1810 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001811 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001812 else
1813#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001814 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001815}
1816
1817/**
1818 * xmlDocDumpFormatMemoryEnc:
1819 * @out_doc: Document to generate XML text from
1820 * @doc_txt_ptr: Memory pointer for allocated XML text
1821 * @doc_txt_len: Length of the generated XML text
1822 * @txt_encoding: Character encoding to use when generating XML text
1823 * @format: should formatting spaces been added
1824 *
1825 * Dump the current DOM tree into memory using the character encoding specified
1826 * by the caller. Note it is up to the caller of this function to free the
1827 * allocated memory with xmlFree().
1828 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1829 * or xmlKeepBlanksDefault(0) was called
1830 */
1831
1832void
1833xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1834 int * doc_txt_len, const char * txt_encoding,
1835 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001836 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001837 int dummy = 0;
1838 xmlOutputBufferPtr out_buff = NULL;
1839 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1840
1841 if (doc_txt_len == NULL) {
1842 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1843 }
1844
1845 if (doc_txt_ptr == NULL) {
1846 *doc_txt_len = 0;
1847 return;
1848 }
1849
1850 *doc_txt_ptr = NULL;
1851 *doc_txt_len = 0;
1852
1853 if (out_doc == NULL) {
1854 /* No document, no output */
1855 return;
1856 }
1857
1858 /*
1859 * Validate the encoding value, if provided.
1860 * This logic is copied from xmlSaveFileEnc.
1861 */
1862
1863 if (txt_encoding == NULL)
1864 txt_encoding = (const char *) out_doc->encoding;
1865 if (txt_encoding != NULL) {
1866 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1867 if ( conv_hdlr == NULL ) {
1868 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1869 txt_encoding);
1870 return;
1871 }
1872 }
1873
1874 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1875 xmlSaveErrMemory("creating buffer");
1876 return;
1877 }
1878
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001879 memset(&ctxt, 0, sizeof(ctxt));
1880 ctxt.doc = out_doc;
1881 ctxt.buf = out_buff;
1882 ctxt.level = 0;
1883 ctxt.format = format;
1884 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001885 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001886 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001887 xmlOutputBufferFlush(out_buff);
1888 if (out_buff->conv != NULL) {
1889 *doc_txt_len = out_buff->conv->use;
1890 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1891 } else {
1892 *doc_txt_len = out_buff->buffer->use;
1893 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1894 }
1895 (void)xmlOutputBufferClose(out_buff);
1896
1897 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1898 *doc_txt_len = 0;
1899 xmlSaveErrMemory("creating output");
1900 }
1901
1902 return;
1903}
1904
1905/**
1906 * xmlDocDumpMemory:
1907 * @cur: the document
1908 * @mem: OUT: the memory pointer
1909 * @size: OUT: the memory length
1910 *
1911 * Dump an XML document in memory and return the #xmlChar * and it's size
1912 * in bytes. It's up to the caller to free the memory with xmlFree().
1913 * The resulting byte array is zero terminated, though the last 0 is not
1914 * included in the returned size.
1915 */
1916void
1917xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1918 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1919}
1920
1921/**
1922 * xmlDocDumpFormatMemory:
1923 * @cur: the document
1924 * @mem: OUT: the memory pointer
1925 * @size: OUT: the memory length
1926 * @format: should formatting spaces been added
1927 *
1928 *
1929 * Dump an XML document in memory and return the #xmlChar * and it's size.
1930 * It's up to the caller to free the memory with xmlFree().
1931 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1932 * or xmlKeepBlanksDefault(0) was called
1933 */
1934void
1935xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
1936 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
1937}
1938
1939/**
1940 * xmlDocDumpMemoryEnc:
1941 * @out_doc: Document to generate XML text from
1942 * @doc_txt_ptr: Memory pointer for allocated XML text
1943 * @doc_txt_len: Length of the generated XML text
1944 * @txt_encoding: Character encoding to use when generating XML text
1945 *
1946 * Dump the current DOM tree into memory using the character encoding specified
1947 * by the caller. Note it is up to the caller of this function to free the
1948 * allocated memory with xmlFree().
1949 */
1950
1951void
1952xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1953 int * doc_txt_len, const char * txt_encoding) {
1954 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
1955 txt_encoding, 0);
1956}
1957
1958/**
1959 * xmlDocFormatDump:
1960 * @f: the FILE*
1961 * @cur: the document
1962 * @format: should formatting spaces been added
1963 *
1964 * Dump an XML document to an open FILE.
1965 *
1966 * returns: the number of bytes written or -1 in case of failure.
1967 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1968 * or xmlKeepBlanksDefault(0) was called
1969 */
1970int
1971xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001972 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001973 xmlOutputBufferPtr buf;
1974 const char * encoding;
1975 xmlCharEncodingHandlerPtr handler = NULL;
1976 int ret;
1977
1978 if (cur == NULL) {
1979#ifdef DEBUG_TREE
1980 xmlGenericError(xmlGenericErrorContext,
1981 "xmlDocDump : document == NULL\n");
1982#endif
1983 return(-1);
1984 }
1985 encoding = (const char *) cur->encoding;
1986
1987 if (encoding != NULL) {
1988 handler = xmlFindCharEncodingHandler(encoding);
1989 if (handler == NULL) {
1990 xmlFree((char *) cur->encoding);
1991 cur->encoding = NULL;
1992 }
1993 }
1994 buf = xmlOutputBufferCreateFile(f, handler);
1995 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001996 memset(&ctxt, 0, sizeof(ctxt));
1997 ctxt.doc = cur;
1998 ctxt.buf = buf;
1999 ctxt.level = 0;
2000 ctxt.format = format;
2001 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002002 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002003 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002004
2005 ret = xmlOutputBufferClose(buf);
2006 return(ret);
2007}
2008
2009/**
2010 * xmlDocDump:
2011 * @f: the FILE*
2012 * @cur: the document
2013 *
2014 * Dump an XML document to an open FILE.
2015 *
2016 * returns: the number of bytes written or -1 in case of failure.
2017 */
2018int
2019xmlDocDump(FILE *f, xmlDocPtr cur) {
2020 return(xmlDocFormatDump (f, cur, 0));
2021}
2022
2023/**
2024 * xmlSaveFileTo:
2025 * @buf: an output I/O buffer
2026 * @cur: the document
2027 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2028 *
2029 * Dump an XML document to an I/O buffer.
2030 *
2031 * returns: the number of bytes written or -1 in case of failure.
2032 */
2033int
2034xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002035 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002036 int ret;
2037
2038 if (buf == NULL) return(0);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002039 memset(&ctxt, 0, sizeof(ctxt));
2040 ctxt.doc = cur;
2041 ctxt.buf = buf;
2042 ctxt.level = 0;
2043 ctxt.format = 0;
2044 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002045 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002046 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002047 ret = xmlOutputBufferClose(buf);
2048 return(ret);
2049}
2050
2051/**
2052 * xmlSaveFormatFileTo:
2053 * @buf: an output I/O buffer
2054 * @cur: the document
2055 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2056 * @format: should formatting spaces been added
2057 *
2058 * Dump an XML document to an I/O buffer.
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002059 * NOTE: the I/O buffer is closed as part of the call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002060 *
2061 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002062 */
2063int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002064xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2065 const char *encoding, int format)
2066{
2067 xmlSaveCtxt ctxt;
2068 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002069
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002070 if (buf == NULL)
2071 return (0);
2072 memset(&ctxt, 0, sizeof(ctxt));
2073 ctxt.doc = cur;
2074 ctxt.buf = buf;
2075 ctxt.level = 0;
2076 ctxt.format = format;
2077 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002078 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002079 xmlDocContentDumpOutput(&ctxt, cur);
2080 ret = xmlOutputBufferClose(buf);
2081 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002082}
2083
2084/**
2085 * xmlSaveFormatFileEnc:
2086 * @filename: the filename or URL to output
2087 * @cur: the document being saved
2088 * @encoding: the name of the encoding to use or NULL.
2089 * @format: should formatting spaces be added.
2090 *
2091 * Dump an XML document to a file or an URL.
2092 *
2093 * Returns the number of bytes written or -1 in case of error.
2094 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2095 * or xmlKeepBlanksDefault(0) was called
2096 */
2097int
2098xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2099 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002100 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002101 xmlOutputBufferPtr buf;
2102 xmlCharEncodingHandlerPtr handler = NULL;
2103 int ret;
2104
2105 if (cur == NULL)
2106 return(-1);
2107
2108 if (encoding == NULL)
2109 encoding = (const char *) cur->encoding;
2110
2111 if (encoding != NULL) {
2112
2113 handler = xmlFindCharEncodingHandler(encoding);
2114 if (handler == NULL)
2115 return(-1);
2116 }
2117
2118#ifdef HAVE_ZLIB_H
2119 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2120#endif
2121 /*
2122 * save the content to a temp buffer.
2123 */
2124 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2125 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002126 memset(&ctxt, 0, sizeof(ctxt));
2127 ctxt.doc = cur;
2128 ctxt.buf = buf;
2129 ctxt.level = 0;
2130 ctxt.format = format;
2131 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002132 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002133
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002134 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002135
2136 ret = xmlOutputBufferClose(buf);
2137 return(ret);
2138}
2139
2140
2141/**
2142 * xmlSaveFileEnc:
2143 * @filename: the filename (or URL)
2144 * @cur: the document
2145 * @encoding: the name of an encoding (or NULL)
2146 *
2147 * Dump an XML document, converting it to the given encoding
2148 *
2149 * returns: the number of bytes written or -1 in case of failure.
2150 */
2151int
2152xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2153 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2154}
2155
2156/**
2157 * xmlSaveFormatFile:
2158 * @filename: the filename (or URL)
2159 * @cur: the document
2160 * @format: should formatting spaces been added
2161 *
2162 * Dump an XML document to a file. Will use compression if
2163 * compiled in and enabled. If @filename is "-" the stdout file is
2164 * used. If @format is set then the document will be indented on output.
2165 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2166 * or xmlKeepBlanksDefault(0) was called
2167 *
2168 * returns: the number of bytes written or -1 in case of failure.
2169 */
2170int
2171xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2172 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2173}
2174
2175/**
2176 * xmlSaveFile:
2177 * @filename: the filename (or URL)
2178 * @cur: the document
2179 *
2180 * Dump an XML document to a file. Will use compression if
2181 * compiled in and enabled. If @filename is "-" the stdout file is
2182 * used.
2183 * returns: the number of bytes written or -1 in case of failure.
2184 */
2185int
2186xmlSaveFile(const char *filename, xmlDocPtr cur) {
2187 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2188}
2189
2190#endif /* LIBXML_OUTPUT_ENABLED */
2191