blob: 75f76a05d0ea54114d6c1ebe17b68861cc460d0a [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;
William M. Brack12d37ab2005-02-21 13:54:07 +0000330 int len;
Daniel Veillard753086a2004-03-28 16:12:44 +0000331
332 if (ctxt == NULL) return;
Daniel Veillard3995bc32004-05-15 18:57:31 +0000333 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
334 ctxt->escape = xmlEscapeEntities;
William M. Brack12d37ab2005-02-21 13:54:07 +0000335 len = xmlStrlen((xmlChar *)xmlTreeIndentString);
336 if ((xmlTreeIndentString == NULL) || (len == 0)) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000337 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
338 } else {
William M. Brack12d37ab2005-02-21 13:54:07 +0000339 ctxt->indent_size = len;
Daniel Veillard753086a2004-03-28 16:12:44 +0000340 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
341 for (i = 0;i < ctxt->indent_nr;i++)
342 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
343 ctxt->indent_size);
344 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
345 }
346}
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000347
348/**
349 * xmlFreeSaveCtxt:
350 *
351 * Free a saving context, destroying the ouptut in any remaining buffer
352 */
353static void
354xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
355{
356 if (ctxt == NULL) return;
357 if (ctxt->encoding != NULL)
358 xmlFree((char *) ctxt->encoding);
Daniel Veillarde2161a62004-04-29 17:14:25 +0000359 if (ctxt->buf != NULL)
360 xmlOutputBufferClose(ctxt->buf);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000361 xmlFree(ctxt);
362}
363
364/**
365 * xmlNewSaveCtxt:
366 *
367 * Create a new saving context
368 *
369 * Returns the new structure or NULL in case of error
370 */
371static xmlSaveCtxtPtr
372xmlNewSaveCtxt(const char *encoding, int options)
373{
374 xmlSaveCtxtPtr ret;
375
376 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
377 if (ret == NULL) {
378 xmlSaveErrMemory("creating saving context");
379 return ( NULL );
380 }
381 memset(ret, 0, sizeof(xmlSaveCtxt));
Daniel Veillard6fc5db02005-01-16 00:05:58 +0000382
383 /*
384 * Use the options
385 */
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000386 ret->options = options;
Daniel Veillard6fc5db02005-01-16 00:05:58 +0000387 if (options & XML_SAVE_FORMAT)
388 ret->format = 1;
389
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000390 if (encoding != NULL) {
391 ret->handler = xmlFindCharEncodingHandler(encoding);
392 if (ret->handler == NULL) {
393 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
394 xmlFreeSaveCtxt(ret);
395 return(NULL);
396 }
397 ret->encoding = xmlStrdup((const xmlChar *)encoding);
Daniel Veillard3995bc32004-05-15 18:57:31 +0000398 ret->escape = xmlEscapeEntities;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000399 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000400 xmlSaveCtxtInit(ret);
401
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000402 return(ret);
403}
404
405/************************************************************************
406 * *
407 * Dumping XML tree content to a simple buffer *
408 * *
409 ************************************************************************/
410/**
411 * xmlAttrSerializeContent:
412 * @buf: the XML buffer output
413 * @doc: the document
414 * @attr: the attribute pointer
415 *
416 * Serialize the attribute in the buffer
417 */
418static void
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000419xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000420{
421 xmlNodePtr children;
422
423 children = attr->children;
424 while (children != NULL) {
425 switch (children->type) {
426 case XML_TEXT_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000427 xmlAttrSerializeTxtContent(buf->buffer, attr->doc,
428 attr, children->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000429 break;
430 case XML_ENTITY_REF_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000431 xmlBufferAdd(buf->buffer, BAD_CAST "&", 1);
432 xmlBufferAdd(buf->buffer, children->name,
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000433 xmlStrlen(children->name));
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000434 xmlBufferAdd(buf->buffer, BAD_CAST ";", 1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000435 break;
436 default:
437 /* should not happen unless we have a badly built tree */
438 break;
439 }
440 children = children->next;
441 }
442}
443
444/************************************************************************
445 * *
446 * Dumping XML tree content to an I/O output buffer *
447 * *
448 ************************************************************************/
449
450#ifdef LIBXML_HTML_ENABLED
451static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000452xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000453#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000454static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
455static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000456void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
Daniel Veillardce244ad2004-11-05 10:03:46 +0000457static void xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000458
459/**
460 * xmlNsDumpOutput:
461 * @buf: the XML buffer output
462 * @cur: a namespace
463 *
464 * Dump a local Namespace definition.
465 * Should be called in the context of attributes dumps.
466 */
467static void
468xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
Daniel Veillardce244ad2004-11-05 10:03:46 +0000469 if ((cur == NULL) || (buf == NULL)) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000470 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
471 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
472 return;
473
474 /* Within the context of an element attributes */
475 if (cur->prefix != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000476 xmlOutputBufferWrite(buf, 7, " xmlns:");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000477 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
478 } else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000479 xmlOutputBufferWrite(buf, 6, " xmlns");
480 xmlOutputBufferWrite(buf, 1, "=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000481 xmlBufferWriteQuotedString(buf->buffer, cur->href);
482 }
483}
484
485/**
486 * xmlNsListDumpOutput:
487 * @buf: the XML buffer output
488 * @cur: the first namespace
489 *
490 * Dump a list of local Namespace definitions.
491 * Should be called in the context of attributes dumps.
492 */
493void
494xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
495 while (cur != NULL) {
496 xmlNsDumpOutput(buf, cur);
497 cur = cur->next;
498 }
499}
500
501/**
502 * xmlDtdDumpOutput:
503 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000504 * @dtd: the pointer to the DTD
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000505 *
506 * Dump the XML document DTD, if any.
507 */
508static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000509xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
510 xmlOutputBufferPtr buf;
511 int format, level;
512 xmlDocPtr doc;
513
514 if (dtd == NULL) return;
515 if ((ctxt == NULL) || (ctxt->buf == NULL))
516 return;
517 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000518 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000519 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
520 if (dtd->ExternalID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000521 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000522 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000523 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000524 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
525 } else if (dtd->SystemID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000526 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000527 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
528 }
529 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
Daniel Veillard41c4a752004-09-08 20:55:38 +0000530 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
531 (dtd->pentities == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000532 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000533 return;
534 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000535 xmlOutputBufferWrite(buf, 3, " [\n");
Daniel Veillard41c4a752004-09-08 20:55:38 +0000536 /*
537 * Dump the notations first they are not in the DTD children list
538 * Do this only on a standalone DTD or on the internal subset though.
539 */
540 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
541 (dtd->doc->intSubset == dtd))) {
Daniel Veillardda3b29a2004-08-14 11:15:13 +0000542 xmlDumpNotationTable(buf->buffer, (xmlNotationTablePtr) dtd->notations);
543 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000544 format = ctxt->format;
545 level = ctxt->level;
546 doc = ctxt->doc;
547 ctxt->format = 0;
548 ctxt->level = -1;
549 ctxt->doc = dtd->doc;
550 xmlNodeListDumpOutput(ctxt, dtd->children);
551 ctxt->format = format;
552 ctxt->level = level;
553 ctxt->doc = doc;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000554 xmlOutputBufferWrite(buf, 2, "]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000555}
556
557/**
558 * xmlAttrDumpOutput:
559 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000560 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000561 *
562 * Dump an XML attribute
563 */
564static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000565xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
566 xmlOutputBufferPtr buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000567
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000568 if (cur == NULL) return;
569 buf = ctxt->buf;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000570 if (buf == NULL) return;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000571 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000572 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
573 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000574 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000575 }
576 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000577 xmlOutputBufferWrite(buf, 2, "=\"");
578 xmlAttrSerializeContent(buf, cur);
579 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000580}
581
582/**
583 * xmlAttrListDumpOutput:
584 * @buf: the XML buffer output
585 * @doc: the document
586 * @cur: the first attribute pointer
587 * @encoding: an optional encoding string
588 *
589 * Dump a list of XML attributes
590 */
591static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000592xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
593 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000594 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000595 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000596 cur = cur->next;
597 }
598}
599
600
601
602/**
603 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000604 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000605 *
606 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000607 */
608static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000609xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000610 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000611
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000612 if (cur == NULL) return;
613 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000614 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000615 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000616 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000617 xmlOutputBufferWrite(buf, ctxt->indent_size *
618 (ctxt->level > ctxt->indent_nr ?
619 ctxt->indent_nr : ctxt->level),
620 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000621 xmlNodeDumpOutputInternal(ctxt, cur);
622 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000623 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000624 }
625 cur = cur->next;
626 }
627}
628
629/**
630 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000631 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000632 *
633 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000634 */
635static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000636xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000637 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000638 xmlNodePtr tmp;
639 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000640 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000641
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000642 if (cur == NULL) return;
643 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000644 if (cur->type == XML_XINCLUDE_START)
645 return;
646 if (cur->type == XML_XINCLUDE_END)
647 return;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000648 if ((cur->type == XML_DOCUMENT_NODE) ||
649 (cur->type == XML_HTML_DOCUMENT_NODE)) {
650 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
651 return;
652 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000653 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000654 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000655 return;
656 }
657 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000658 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000659 return;
660 }
661 if (cur->type == XML_ELEMENT_DECL) {
662 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
663 return;
664 }
665 if (cur->type == XML_ATTRIBUTE_DECL) {
666 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
667 return;
668 }
669 if (cur->type == XML_ENTITY_DECL) {
670 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
671 return;
672 }
673 if (cur->type == XML_TEXT_NODE) {
674 if (cur->content != NULL) {
William M. Brack4e1c2db2005-02-11 10:58:55 +0000675 if (cur->name != xmlStringTextNoenc) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000676 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000677 } else {
678 /*
679 * Disable escaping, needed for XSLT
680 */
681 xmlOutputBufferWriteString(buf, (const char *) cur->content);
682 }
683 }
684
685 return;
686 }
687 if (cur->type == XML_PI_NODE) {
688 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000689 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000690 xmlOutputBufferWriteString(buf, (const char *)cur->name);
691 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000692 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000693 xmlOutputBufferWriteString(buf, (const char *)cur->content);
694 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000695 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000696 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000697 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000698 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000699 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000700 }
701 return;
702 }
703 if (cur->type == XML_COMMENT_NODE) {
704 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000705 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000706 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000707 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000708 }
709 return;
710 }
711 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000712 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000713 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000714 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000715 return;
716 }
717 if (cur->type == XML_CDATA_SECTION_NODE) {
Daniel Veillard7cd517c2005-05-20 18:47:22 +0000718 if (cur->content == NULL) {
719 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
720 } else {
721 start = end = cur->content;
722 while (*end != '\0') {
723 if ((*end == ']') && (*(end + 1) == ']') &&
724 (*(end + 2) == '>')) {
725 end = end + 2;
726 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
727 xmlOutputBufferWrite(buf, end - start, (const char *)start);
728 xmlOutputBufferWrite(buf, 3, "]]>");
729 start = end;
730 }
731 end++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000732 }
Daniel Veillard7cd517c2005-05-20 18:47:22 +0000733 if (start != end) {
734 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
735 xmlOutputBufferWriteString(buf, (const char *)start);
736 xmlOutputBufferWrite(buf, 3, "]]>");
737 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000738 }
739 return;
740 }
741 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000742 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000743 return;
744 }
745 if (cur->type == XML_NAMESPACE_DECL) {
746 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
747 return;
748 }
749
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000750 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000751 if (format == 1) {
752 tmp = cur->children;
753 while (tmp != NULL) {
754 if ((tmp->type == XML_TEXT_NODE) ||
755 (tmp->type == XML_CDATA_SECTION_NODE) ||
756 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000757 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000758 break;
759 }
760 tmp = tmp->next;
761 }
762 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000763 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000764 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
765 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000766 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000767 }
768
769 xmlOutputBufferWriteString(buf, (const char *)cur->name);
770 if (cur->nsDef)
771 xmlNsListDumpOutput(buf, cur->nsDef);
772 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000773 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000774
775 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
776 (cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000777 xmlOutputBufferWrite(buf, 2, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000778 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000779 return;
780 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000781 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000782 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000783 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000784 }
785 if (cur->children != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000786 if (ctxt->format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000787 if (ctxt->level >= 0) ctxt->level++;
788 xmlNodeListDumpOutput(ctxt, cur->children);
789 if (ctxt->level > 0) ctxt->level--;
790 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000791 xmlOutputBufferWrite(buf, ctxt->indent_size *
792 (ctxt->level > ctxt->indent_nr ?
793 ctxt->indent_nr : ctxt->level),
794 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000795 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000796 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000797 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
798 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000799 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000800 }
801
802 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000803 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000804 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000805}
806
807/**
808 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000809 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000810 *
811 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000812 */
813static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000814xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000815#ifdef LIBXML_HTML_ENABLED
816 xmlDtdPtr dtd;
817 int is_xhtml = 0;
818#endif
819 const xmlChar *oldenc = cur->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000820 const xmlChar *encoding = ctxt->encoding;
821 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000822
823 xmlInitParser();
824
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000825 if (ctxt->encoding != NULL)
826 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000827
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000828 buf = ctxt->buf;
Daniel Veillard100e1802005-08-08 14:44:11 +0000829 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
830 xmlOutputBufferWrite(buf, 14, "<?xml version=");
831 if (cur->version != NULL)
832 xmlBufferWriteQuotedString(buf->buffer, cur->version);
833 else
834 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
835 if (ctxt->encoding == NULL) {
836 if (cur->encoding != NULL)
837 encoding = cur->encoding;
838 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
839 encoding = (const xmlChar *)
840 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
841 }
842 if (encoding != NULL) {
843 xmlOutputBufferWrite(buf, 10, " encoding=");
844 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
845 }
846 switch (cur->standalone) {
847 case 0:
848 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
849 break;
850 case 1:
851 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
852 break;
853 }
854 xmlOutputBufferWrite(buf, 3, "?>\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000855 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000856
857#ifdef LIBXML_HTML_ENABLED
858 dtd = xmlGetIntSubset(cur);
859 if (dtd != NULL) {
860 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
861 if (is_xhtml < 0) is_xhtml = 0;
862 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000863#endif
864 if (cur->children != NULL) {
865 xmlNodePtr child = cur->children;
866
867 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000868 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000869#ifdef LIBXML_HTML_ENABLED
870 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000871 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000872 else
873#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000874 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000875 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000876 child = child->next;
877 }
878 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000879 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000880 cur->encoding = oldenc;
881}
882
883#ifdef LIBXML_HTML_ENABLED
884/************************************************************************
885 * *
886 * Functions specific to XHTML serialization *
887 * *
888 ************************************************************************/
889
890/**
891 * xhtmlIsEmpty:
892 * @node: the node
893 *
894 * Check if a node is an empty xhtml node
895 *
896 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
897 */
898static int
899xhtmlIsEmpty(xmlNodePtr node) {
900 if (node == NULL)
901 return(-1);
902 if (node->type != XML_ELEMENT_NODE)
903 return(0);
904 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
905 return(0);
906 if (node->children != NULL)
907 return(0);
908 switch (node->name[0]) {
909 case 'a':
910 if (xmlStrEqual(node->name, BAD_CAST "area"))
911 return(1);
912 return(0);
913 case 'b':
914 if (xmlStrEqual(node->name, BAD_CAST "br"))
915 return(1);
916 if (xmlStrEqual(node->name, BAD_CAST "base"))
917 return(1);
918 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
919 return(1);
920 return(0);
921 case 'c':
922 if (xmlStrEqual(node->name, BAD_CAST "col"))
923 return(1);
924 return(0);
925 case 'f':
926 if (xmlStrEqual(node->name, BAD_CAST "frame"))
927 return(1);
928 return(0);
929 case 'h':
930 if (xmlStrEqual(node->name, BAD_CAST "hr"))
931 return(1);
932 return(0);
933 case 'i':
934 if (xmlStrEqual(node->name, BAD_CAST "img"))
935 return(1);
936 if (xmlStrEqual(node->name, BAD_CAST "input"))
937 return(1);
938 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
939 return(1);
940 return(0);
941 case 'l':
942 if (xmlStrEqual(node->name, BAD_CAST "link"))
943 return(1);
944 return(0);
945 case 'm':
946 if (xmlStrEqual(node->name, BAD_CAST "meta"))
947 return(1);
948 return(0);
949 case 'p':
950 if (xmlStrEqual(node->name, BAD_CAST "param"))
951 return(1);
952 return(0);
953 }
954 return(0);
955}
956
957/**
958 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000959 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000960 *
961 * Dump a list of XML attributes
962 */
963static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000964xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000965 xmlAttrPtr xml_lang = NULL;
966 xmlAttrPtr lang = NULL;
967 xmlAttrPtr name = NULL;
968 xmlAttrPtr id = NULL;
969 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000970 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000971
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000972 if (cur == NULL) return;
973 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000974 parent = cur->parent;
975 while (cur != NULL) {
976 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
977 id = cur;
978 else
979 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
980 name = cur;
981 else
982 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
983 lang = cur;
984 else
985 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
986 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
987 xml_lang = cur;
988 else if ((cur->ns == NULL) &&
989 ((cur->children == NULL) ||
990 (cur->children->content == NULL) ||
991 (cur->children->content[0] == 0)) &&
992 (htmlIsBooleanAttr(cur->name))) {
993 if (cur->children != NULL)
994 xmlFreeNode(cur->children);
995 cur->children = xmlNewText(cur->name);
996 if (cur->children != NULL)
997 cur->children->parent = (xmlNodePtr) cur;
998 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000999 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001000 cur = cur->next;
1001 }
1002 /*
1003 * C.8
1004 */
1005 if ((name != NULL) && (id == NULL)) {
1006 if ((parent != NULL) && (parent->name != NULL) &&
1007 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1008 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1009 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1010 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1011 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1012 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1013 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1014 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1015 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001016 xmlOutputBufferWrite(buf, 5, " id=\"");
1017 xmlAttrSerializeContent(buf, name);
1018 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001019 }
1020 }
1021 /*
1022 * C.7.
1023 */
1024 if ((lang != NULL) && (xml_lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001025 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1026 xmlAttrSerializeContent(buf, lang);
1027 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001028 } else
1029 if ((xml_lang != NULL) && (lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001030 xmlOutputBufferWrite(buf, 7, " lang=\"");
1031 xmlAttrSerializeContent(buf, xml_lang);
1032 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001033 }
1034}
1035
1036/**
1037 * xhtmlNodeListDumpOutput:
1038 * @buf: the XML buffer output
1039 * @doc: the XHTML document
1040 * @cur: the first node
1041 * @level: the imbrication level for indenting
1042 * @format: is formatting allowed
1043 * @encoding: an optional encoding string
1044 *
1045 * Dump an XML node list, recursive behaviour, children are printed too.
1046 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1047 * or xmlKeepBlanksDefault(0) was called
1048 */
1049static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001050xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001051 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001052
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001053 if (cur == NULL) return;
1054 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001055 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001056 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001057 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +00001058 xmlOutputBufferWrite(buf, ctxt->indent_size *
1059 (ctxt->level > ctxt->indent_nr ?
1060 ctxt->indent_nr : ctxt->level),
1061 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001062 xhtmlNodeDumpOutput(ctxt, cur);
1063 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001064 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001065 }
1066 cur = cur->next;
1067 }
1068}
1069
1070/**
1071 * xhtmlNodeDumpOutput:
1072 * @buf: the XML buffer output
1073 * @doc: the XHTML document
1074 * @cur: the current node
1075 * @level: the imbrication level for indenting
1076 * @format: is formatting allowed
1077 * @encoding: an optional encoding string
1078 *
1079 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001080 */
1081static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001082xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Rob Richards31f73022005-08-26 15:33:26 +00001083 int format, addmeta = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001084 xmlNodePtr tmp;
1085 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001086 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001087
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001088 if (cur == NULL) return;
Daniel Veillard60071ae2005-09-12 00:03:43 +00001089 if ((cur->type == XML_DOCUMENT_NODE) ||
1090 (cur->type == XML_HTML_DOCUMENT_NODE)) {
1091 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1092 return;
1093 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001094 if (cur->type == XML_XINCLUDE_START)
1095 return;
1096 if (cur->type == XML_XINCLUDE_END)
1097 return;
1098 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001099 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001100 return;
1101 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001102 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001103 if (cur->type == XML_ELEMENT_DECL) {
1104 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1105 return;
1106 }
1107 if (cur->type == XML_ATTRIBUTE_DECL) {
1108 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1109 return;
1110 }
1111 if (cur->type == XML_ENTITY_DECL) {
1112 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1113 return;
1114 }
1115 if (cur->type == XML_TEXT_NODE) {
1116 if (cur->content != NULL) {
1117 if ((cur->name == xmlStringText) ||
1118 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001119 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001120 } else {
1121 /*
1122 * Disable escaping, needed for XSLT
1123 */
1124 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1125 }
1126 }
1127
1128 return;
1129 }
1130 if (cur->type == XML_PI_NODE) {
1131 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001132 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001133 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1134 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001135 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001136 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1137 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001138 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001139 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001140 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001141 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001142 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001143 }
1144 return;
1145 }
1146 if (cur->type == XML_COMMENT_NODE) {
1147 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001148 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001149 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001150 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001151 }
1152 return;
1153 }
1154 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001155 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001156 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001157 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001158 return;
1159 }
1160 if (cur->type == XML_CDATA_SECTION_NODE) {
1161 start = end = cur->content;
1162 while (*end != '\0') {
1163 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1164 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001165 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001166 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001167 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001168 start = end;
1169 }
1170 end++;
1171 }
1172 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001173 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001174 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001175 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001176 }
1177 return;
1178 }
1179
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001180 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001181 if (format == 1) {
1182 tmp = cur->children;
1183 while (tmp != NULL) {
1184 if ((tmp->type == XML_TEXT_NODE) ||
1185 (tmp->type == XML_ENTITY_REF_NODE)) {
1186 format = 0;
1187 break;
1188 }
1189 tmp = tmp->next;
1190 }
1191 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001192 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001193 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1194 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001195 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001196 }
1197
1198 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1199 if (cur->nsDef)
1200 xmlNsListDumpOutput(buf, cur->nsDef);
1201 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1202 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1203 /*
1204 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1205 */
1206 xmlOutputBufferWriteString(buf,
1207 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1208 }
1209 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001210 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001211
Rob Richards31f73022005-08-26 15:33:26 +00001212 if ((cur->type == XML_ELEMENT_NODE) &&
1213 (cur->parent != NULL) &&
1214 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1215 xmlStrEqual(cur->name, BAD_CAST"head") &&
1216 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1217
1218 tmp = cur->children;
1219 while (tmp != NULL) {
1220 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1221 xmlChar *httpequiv;
1222
1223 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
Rob Richards07b72002005-09-03 14:56:36 +00001224 if (httpequiv != NULL) {
1225 if (xmlStrcasecmp(httpequiv, BAD_CAST"Content-Type") == 0) {
1226 xmlFree(httpequiv);
1227 break;
1228 }
Rob Richards31f73022005-08-26 15:33:26 +00001229 xmlFree(httpequiv);
Rob Richards31f73022005-08-26 15:33:26 +00001230 }
Rob Richards31f73022005-08-26 15:33:26 +00001231 }
1232 tmp = tmp->next;
1233 }
1234 if (tmp == NULL)
1235 addmeta = 1;
1236 }
1237
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001238 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1239 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
Rob Richards31f73022005-08-26 15:33:26 +00001240 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001241 /*
1242 * C.2. Empty Elements
1243 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001244 xmlOutputBufferWrite(buf, 3, " />");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001245 } else {
Rob Richards31f73022005-08-26 15:33:26 +00001246 if (addmeta == 1) {
1247 xmlOutputBufferWrite(buf, 1, ">");
1248 xmlOutputBufferWriteString(buf,
1249 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1250 if (ctxt->encoding) {
1251 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1252 } else {
1253 xmlOutputBufferWrite(buf, 5, "UTF-8");
1254 }
1255 xmlOutputBufferWrite(buf, 3, "\" /");
1256 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001257 /*
1258 * C.3. Element Minimization and Empty Element Content
1259 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001260 xmlOutputBufferWrite(buf, 3, "></");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001261 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1262 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001263 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001264 }
1265 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001266 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001267 }
1268 return;
1269 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001270 xmlOutputBufferWrite(buf, 1, ">");
Rob Richards31f73022005-08-26 15:33:26 +00001271 if (addmeta == 1) {
1272 xmlOutputBufferWriteString(buf,
1273 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1274 if (ctxt->encoding) {
1275 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1276 } else {
1277 xmlOutputBufferWrite(buf, 5, "UTF-8");
1278 }
1279 xmlOutputBufferWrite(buf, 4, "\" />");
1280 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001281 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001282 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001283 }
1284
1285 /*
1286 * 4.8. Script and Style elements
1287 */
1288 if ((cur->type == XML_ELEMENT_NODE) &&
1289 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1290 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1291 ((cur->ns == NULL) ||
1292 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1293 xmlNodePtr child = cur->children;
1294
1295 while (child != NULL) {
1296 if ((child->type == XML_TEXT_NODE) ||
1297 (child->type == XML_CDATA_SECTION_NODE)) {
1298 /*
1299 * Apparently CDATA escaping for style just break on IE,
1300 * mozilla and galeon, so ...
1301 */
1302 if (xmlStrEqual(cur->name, BAD_CAST "style") &&
1303 (xmlStrchr(child->content, '<') == NULL) &&
1304 (xmlStrchr(child->content, '>') == NULL) &&
1305 (xmlStrchr(child->content, '&') == NULL)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001306 int level = ctxt->level;
1307 int indent = ctxt->format;
1308
1309 ctxt->level = 0;
1310 ctxt->format = 0;
1311 xhtmlNodeDumpOutput(ctxt, child);
1312 ctxt->level = level;
1313 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001314 } else {
1315 start = end = child->content;
1316 while (*end != '\0') {
1317 if (*end == ']' &&
1318 *(end + 1) == ']' &&
1319 *(end + 2) == '>') {
1320 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001321 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001322 xmlOutputBufferWrite(buf, end - start,
1323 (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001324 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001325 start = end;
1326 }
1327 end++;
1328 }
1329 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001330 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1331 xmlOutputBufferWrite(buf, end - start,
1332 (const char *)start);
1333 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001334 }
1335 }
1336 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001337 int level = ctxt->level;
1338 int indent = ctxt->format;
1339
1340 ctxt->level = 0;
1341 ctxt->format = 0;
1342 xhtmlNodeDumpOutput(ctxt, child);
1343 ctxt->level = level;
1344 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001345 }
1346 child = child->next;
1347 }
1348 } else if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001349 int indent = ctxt->format;
1350
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001351 if (format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001352 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001353 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001354 xhtmlNodeListDumpOutput(ctxt, cur->children);
1355 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001356 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001357 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001358 xmlOutputBufferWrite(buf, ctxt->indent_size *
1359 (ctxt->level > ctxt->indent_nr ?
1360 ctxt->indent_nr : ctxt->level),
1361 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001362 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001363 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001364 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1365 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001366 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001367 }
1368
1369 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001370 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001371}
1372#endif
1373
1374/************************************************************************
1375 * *
1376 * Public entry points *
1377 * *
1378 ************************************************************************/
1379
1380/**
1381 * xmlSaveToFd:
1382 * @fd: a file descriptor number
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
1392xmlSaveToFd(int fd, const char *encoding, int options)
1393{
1394 xmlSaveCtxtPtr ret;
1395
1396 ret = xmlNewSaveCtxt(encoding, options);
1397 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001398 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1399 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001400 xmlFreeSaveCtxt(ret);
1401 return(NULL);
1402 }
1403 return(ret);
1404}
1405
1406/**
1407 * xmlSaveToFilename:
1408 * @filename: a file name or an URL
1409 * @encoding: the encoding name to use or NULL
1410 * @options: a set of xmlSaveOptions
1411 *
1412 * Create a document saving context serializing to a filename or possibly
1413 * to an URL (but this is less reliable) with the encoding and the options
1414 * given.
1415 *
1416 * Returns a new serialization context or NULL in case of error.
1417 */
1418xmlSaveCtxtPtr
1419xmlSaveToFilename(const char *filename, const char *encoding, int options)
1420{
1421 xmlSaveCtxtPtr ret;
1422 int compression = 0; /* TODO handle compression option */
1423
1424 ret = xmlNewSaveCtxt(encoding, options);
1425 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001426 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001427 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001428 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001429 xmlFreeSaveCtxt(ret);
1430 return(NULL);
1431 }
1432 return(ret);
1433}
1434
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001435/**
1436 * xmlSaveToBuffer:
1437 * @buffer: a buffer
1438 * @encoding: the encoding name to use or NULL
1439 * @options: a set of xmlSaveOptions
1440 *
1441 * Create a document saving context serializing to a buffer
1442 * with the encoding and the options given
1443 *
1444 * Returns a new serialization context or NULL in case of error.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001445xmlSaveCtxtPtr
1446xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1447{
1448 TODO
1449 return(NULL);
1450}
Daniel Veillarda2351322004-06-27 12:08:10 +00001451 */
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001452
1453/**
1454 * xmlSaveToIO:
1455 * @iowrite: an I/O write function
1456 * @ioclose: an I/O close function
1457 * @ioctx: an I/O handler
1458 * @encoding: the encoding name to use or NULL
1459 * @options: a set of xmlSaveOptions
1460 *
1461 * Create a document saving context serializing to a file descriptor
1462 * with the encoding and the options given
1463 *
1464 * Returns a new serialization context or NULL in case of error.
1465 */
1466xmlSaveCtxtPtr
1467xmlSaveToIO(xmlOutputWriteCallback iowrite,
1468 xmlOutputCloseCallback ioclose,
1469 void *ioctx, const char *encoding, int options)
1470{
1471 xmlSaveCtxtPtr ret;
1472
1473 ret = xmlNewSaveCtxt(encoding, options);
1474 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001475 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1476 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001477 xmlFreeSaveCtxt(ret);
1478 return(NULL);
1479 }
1480 return(ret);
1481}
1482
1483/**
1484 * xmlSaveDoc:
1485 * @ctxt: a document saving context
1486 * @doc: a document
1487 *
1488 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001489 * TODO: The function is not fully implemented yet as it does not return the
1490 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001491 *
1492 * Returns the number of byte written or -1 in case of error
1493 */
1494long
1495xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1496{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001497 long ret = 0;
1498
Daniel Veillardce682bc2004-11-05 17:22:25 +00001499 if ((ctxt == NULL) || (doc == NULL)) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001500 xmlDocContentDumpOutput(ctxt, doc);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001501 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001502}
1503
1504/**
1505 * xmlSaveTree:
1506 * @ctxt: a document saving context
1507 * @node: a document
1508 *
1509 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001510 * TODO: The function is not fully implemented yet as it does not return the
1511 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001512 *
1513 * Returns the number of byte written or -1 in case of error
1514 */
1515long
1516xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1517{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001518 long ret = 0;
1519
Daniel Veillardce682bc2004-11-05 17:22:25 +00001520 if ((ctxt == NULL) || (node == NULL)) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001521 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001522 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001523}
1524
1525/**
1526 * xmlSaveFlush:
1527 * @ctxt: a document saving context
1528 *
1529 * Flush a document saving context, i.e. make sure that all bytes have
1530 * been output.
1531 *
1532 * Returns the number of byte written or -1 in case of error.
1533 */
1534int
1535xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1536{
1537 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001538 if (ctxt->buf == NULL) return(-1);
1539 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001540}
1541
1542/**
1543 * xmlSaveClose:
1544 * @ctxt: a document saving context
1545 *
1546 * Close a document saving context, i.e. make sure that all bytes have
1547 * been output and free the associated data.
1548 *
1549 * Returns the number of byte written or -1 in case of error.
1550 */
1551int
1552xmlSaveClose(xmlSaveCtxtPtr ctxt)
1553{
1554 int ret;
1555
1556 if (ctxt == NULL) return(-1);
1557 ret = xmlSaveFlush(ctxt);
1558 xmlFreeSaveCtxt(ctxt);
1559 return(ret);
1560}
1561
Daniel Veillard3995bc32004-05-15 18:57:31 +00001562/**
1563 * xmlSaveSetEscape:
1564 * @ctxt: a document saving context
1565 * @escape: the escaping function
1566 *
1567 * Set a custom escaping function to be used for text in element content
1568 *
1569 * Returns 0 if successful or -1 in case of error.
1570 */
1571int
1572xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1573{
1574 if (ctxt == NULL) return(-1);
1575 ctxt->escape = escape;
1576 return(0);
1577}
1578
1579/**
1580 * xmlSaveSetAttrEscape:
1581 * @ctxt: a document saving context
1582 * @escape: the escaping function
1583 *
1584 * Set a custom escaping function to be used for text in attribute content
1585 *
1586 * Returns 0 if successful or -1 in case of error.
1587 */
1588int
1589xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1590{
1591 if (ctxt == NULL) return(-1);
1592 ctxt->escapeAttr = escape;
1593 return(0);
1594}
1595
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001596/************************************************************************
1597 * *
1598 * Public entry points based on buffers *
1599 * *
1600 ************************************************************************/
1601/**
1602 * xmlAttrSerializeTxtContent:
1603 * @buf: the XML buffer output
1604 * @doc: the document
1605 * @attr: the attribute node
1606 * @string: the text content
1607 *
1608 * Serialize text attribute values to an xml simple buffer
1609 */
1610void
1611xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001612 xmlAttrPtr attr, const xmlChar * string)
1613{
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001614 xmlChar *base, *cur;
1615
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001616 if (string == NULL)
1617 return;
1618 base = cur = (xmlChar *) string;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001619 while (*cur != 0) {
1620 if (*cur == '\n') {
1621 if (base != cur)
1622 xmlBufferAdd(buf, base, cur - base);
1623 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1624 cur++;
1625 base = cur;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001626 } else if (*cur == '\r') {
1627 if (base != cur)
1628 xmlBufferAdd(buf, base, cur - base);
1629 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1630 cur++;
1631 base = cur;
1632 } else if (*cur == '\t') {
1633 if (base != cur)
1634 xmlBufferAdd(buf, base, cur - base);
1635 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1636 cur++;
1637 base = cur;
1638 } else if (*cur == '"') {
1639 if (base != cur)
1640 xmlBufferAdd(buf, base, cur - base);
1641 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1642 cur++;
1643 base = cur;
1644 } else if (*cur == '<') {
1645 if (base != cur)
1646 xmlBufferAdd(buf, base, cur - base);
1647 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1648 cur++;
1649 base = cur;
1650 } else if (*cur == '>') {
1651 if (base != cur)
1652 xmlBufferAdd(buf, base, cur - base);
1653 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1654 cur++;
1655 base = cur;
1656 } else if (*cur == '&') {
1657 if (base != cur)
1658 xmlBufferAdd(buf, base, cur - base);
1659 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1660 cur++;
1661 base = cur;
1662 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1663 (doc->encoding == NULL))) {
1664 /*
1665 * We assume we have UTF-8 content.
1666 */
1667 unsigned char tmp[10];
1668 int val = 0, l = 1;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001669
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001670 if (base != cur)
1671 xmlBufferAdd(buf, base, cur - base);
1672 if (*cur < 0xC0) {
1673 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1674 if (doc != NULL)
1675 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1676 xmlSerializeHexCharRef(tmp, *cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001677 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001678 cur++;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001679 base = cur;
1680 continue;
1681 } else if (*cur < 0xE0) {
1682 val = (cur[0]) & 0x1F;
1683 val <<= 6;
1684 val |= (cur[1]) & 0x3F;
1685 l = 2;
1686 } else if (*cur < 0xF0) {
1687 val = (cur[0]) & 0x0F;
1688 val <<= 6;
1689 val |= (cur[1]) & 0x3F;
1690 val <<= 6;
1691 val |= (cur[2]) & 0x3F;
1692 l = 3;
1693 } else if (*cur < 0xF8) {
1694 val = (cur[0]) & 0x07;
1695 val <<= 6;
1696 val |= (cur[1]) & 0x3F;
1697 val <<= 6;
1698 val |= (cur[2]) & 0x3F;
1699 val <<= 6;
1700 val |= (cur[3]) & 0x3F;
1701 l = 4;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001702 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001703 if ((l == 1) || (!IS_CHAR(val))) {
1704 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1705 if (doc != NULL)
1706 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1707
1708 xmlSerializeHexCharRef(tmp, *cur);
1709 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1710 cur++;
1711 base = cur;
1712 continue;
1713 }
1714 /*
1715 * We could do multiple things here. Just save
1716 * as a char ref
1717 */
1718 xmlSerializeHexCharRef(tmp, val);
1719 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1720 cur += l;
1721 base = cur;
1722 } else {
1723 cur++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001724 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001725 }
1726 if (base != cur)
1727 xmlBufferAdd(buf, base, cur - base);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001728}
1729
1730/**
1731 * xmlNodeDump:
1732 * @buf: the XML buffer output
1733 * @doc: the document
1734 * @cur: the current node
1735 * @level: the imbrication level for indenting
1736 * @format: is formatting allowed
1737 *
1738 * Dump an XML node, recursive behaviour,children are printed too.
1739 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1740 * or xmlKeepBlanksDefault(0) was called
1741 *
1742 * Returns the number of bytes written to the buffer or -1 in case of error
1743 */
1744int
1745xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1746 int format)
1747{
1748 unsigned int use;
1749 int ret;
1750 xmlOutputBufferPtr outbuf;
1751
1752 xmlInitParser();
1753
1754 if (cur == NULL) {
1755#ifdef DEBUG_TREE
1756 xmlGenericError(xmlGenericErrorContext,
1757 "xmlNodeDump : node == NULL\n");
1758#endif
1759 return (-1);
1760 }
1761 if (buf == NULL) {
1762#ifdef DEBUG_TREE
1763 xmlGenericError(xmlGenericErrorContext,
1764 "xmlNodeDump : buf == NULL\n");
1765#endif
1766 return (-1);
1767 }
1768 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1769 if (outbuf == NULL) {
1770 xmlSaveErrMemory("creating buffer");
1771 return (-1);
1772 }
1773 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1774 outbuf->buffer = buf;
1775 outbuf->encoder = NULL;
1776 outbuf->writecallback = NULL;
1777 outbuf->closecallback = NULL;
1778 outbuf->context = NULL;
1779 outbuf->written = 0;
1780
1781 use = buf->use;
1782 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1783 xmlFree(outbuf);
1784 ret = buf->use - use;
1785 return (ret);
1786}
1787
1788/**
1789 * xmlElemDump:
1790 * @f: the FILE * for the output
1791 * @doc: the document
1792 * @cur: the current node
1793 *
1794 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1795 */
1796void
1797xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1798{
1799 xmlOutputBufferPtr outbuf;
1800
1801 xmlInitParser();
1802
1803 if (cur == NULL) {
1804#ifdef DEBUG_TREE
1805 xmlGenericError(xmlGenericErrorContext,
1806 "xmlElemDump : cur == NULL\n");
1807#endif
1808 return;
1809 }
1810#ifdef DEBUG_TREE
1811 if (doc == NULL) {
1812 xmlGenericError(xmlGenericErrorContext,
1813 "xmlElemDump : doc == NULL\n");
1814 }
1815#endif
1816
1817 outbuf = xmlOutputBufferCreateFile(f, NULL);
1818 if (outbuf == NULL)
1819 return;
1820 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1821#ifdef LIBXML_HTML_ENABLED
1822 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1823#else
1824 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1825#endif /* LIBXML_HTML_ENABLED */
1826 } else
1827 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1828 xmlOutputBufferClose(outbuf);
1829}
1830
1831/************************************************************************
1832 * *
1833 * Saving functions front-ends *
1834 * *
1835 ************************************************************************/
1836
1837/**
1838 * xmlNodeDumpOutput:
1839 * @buf: the XML buffer output
1840 * @doc: the document
1841 * @cur: the current node
1842 * @level: the imbrication level for indenting
1843 * @format: is formatting allowed
1844 * @encoding: an optional encoding string
1845 *
1846 * Dump an XML node, recursive behaviour, children are printed too.
1847 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1848 * or xmlKeepBlanksDefault(0) was called
1849 */
1850void
1851xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1852 int level, int format, const char *encoding)
1853{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001854 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001855#ifdef LIBXML_HTML_ENABLED
1856 xmlDtdPtr dtd;
1857 int is_xhtml = 0;
1858#endif
1859
1860 xmlInitParser();
1861
Daniel Veillardce244ad2004-11-05 10:03:46 +00001862 if ((buf == NULL) || (cur == NULL)) return;
1863
Daniel Veillard64354ea2005-03-31 15:22:56 +00001864 if (encoding == NULL)
1865 encoding = "UTF-8";
1866
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001867 memset(&ctxt, 0, sizeof(ctxt));
1868 ctxt.doc = doc;
1869 ctxt.buf = buf;
1870 ctxt.level = level;
1871 ctxt.format = format;
1872 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001873 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001874
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001875#ifdef LIBXML_HTML_ENABLED
1876 dtd = xmlGetIntSubset(doc);
1877 if (dtd != NULL) {
1878 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1879 if (is_xhtml < 0)
1880 is_xhtml = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001881 }
1882
1883 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001884 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001885 else
1886#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001887 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001888}
1889
1890/**
1891 * xmlDocDumpFormatMemoryEnc:
1892 * @out_doc: Document to generate XML text from
1893 * @doc_txt_ptr: Memory pointer for allocated XML text
1894 * @doc_txt_len: Length of the generated XML text
1895 * @txt_encoding: Character encoding to use when generating XML text
1896 * @format: should formatting spaces been added
1897 *
1898 * Dump the current DOM tree into memory using the character encoding specified
1899 * by the caller. Note it is up to the caller of this function to free the
1900 * allocated memory with xmlFree().
1901 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1902 * or xmlKeepBlanksDefault(0) was called
1903 */
1904
1905void
1906xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1907 int * doc_txt_len, const char * txt_encoding,
1908 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001909 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001910 int dummy = 0;
1911 xmlOutputBufferPtr out_buff = NULL;
1912 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1913
1914 if (doc_txt_len == NULL) {
1915 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1916 }
1917
1918 if (doc_txt_ptr == NULL) {
1919 *doc_txt_len = 0;
1920 return;
1921 }
1922
1923 *doc_txt_ptr = NULL;
1924 *doc_txt_len = 0;
1925
1926 if (out_doc == NULL) {
1927 /* No document, no output */
1928 return;
1929 }
1930
1931 /*
1932 * Validate the encoding value, if provided.
1933 * This logic is copied from xmlSaveFileEnc.
1934 */
1935
1936 if (txt_encoding == NULL)
1937 txt_encoding = (const char *) out_doc->encoding;
1938 if (txt_encoding != NULL) {
1939 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1940 if ( conv_hdlr == NULL ) {
1941 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1942 txt_encoding);
1943 return;
1944 }
1945 }
1946
1947 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1948 xmlSaveErrMemory("creating buffer");
1949 return;
1950 }
1951
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001952 memset(&ctxt, 0, sizeof(ctxt));
1953 ctxt.doc = out_doc;
1954 ctxt.buf = out_buff;
1955 ctxt.level = 0;
1956 ctxt.format = format;
1957 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001958 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001959 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001960 xmlOutputBufferFlush(out_buff);
1961 if (out_buff->conv != NULL) {
1962 *doc_txt_len = out_buff->conv->use;
1963 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1964 } else {
1965 *doc_txt_len = out_buff->buffer->use;
1966 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1967 }
1968 (void)xmlOutputBufferClose(out_buff);
1969
1970 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1971 *doc_txt_len = 0;
1972 xmlSaveErrMemory("creating output");
1973 }
1974
1975 return;
1976}
1977
1978/**
1979 * xmlDocDumpMemory:
1980 * @cur: the document
1981 * @mem: OUT: the memory pointer
1982 * @size: OUT: the memory length
1983 *
1984 * Dump an XML document in memory and return the #xmlChar * and it's size
1985 * in bytes. It's up to the caller to free the memory with xmlFree().
1986 * The resulting byte array is zero terminated, though the last 0 is not
1987 * included in the returned size.
1988 */
1989void
1990xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1991 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1992}
1993
1994/**
1995 * xmlDocDumpFormatMemory:
1996 * @cur: the document
1997 * @mem: OUT: the memory pointer
1998 * @size: OUT: the memory length
1999 * @format: should formatting spaces been added
2000 *
2001 *
2002 * Dump an XML document in memory and return the #xmlChar * and it's size.
2003 * It's up to the caller to free the memory with xmlFree().
2004 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2005 * or xmlKeepBlanksDefault(0) was called
2006 */
2007void
2008xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2009 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2010}
2011
2012/**
2013 * xmlDocDumpMemoryEnc:
2014 * @out_doc: Document to generate XML text from
2015 * @doc_txt_ptr: Memory pointer for allocated XML text
2016 * @doc_txt_len: Length of the generated XML text
2017 * @txt_encoding: Character encoding to use when generating XML text
2018 *
2019 * Dump the current DOM tree into memory using the character encoding specified
2020 * by the caller. Note it is up to the caller of this function to free the
2021 * allocated memory with xmlFree().
2022 */
2023
2024void
2025xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2026 int * doc_txt_len, const char * txt_encoding) {
2027 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2028 txt_encoding, 0);
2029}
2030
2031/**
2032 * xmlDocFormatDump:
2033 * @f: the FILE*
2034 * @cur: the document
2035 * @format: should formatting spaces been added
2036 *
2037 * Dump an XML document to an open FILE.
2038 *
2039 * returns: the number of bytes written or -1 in case of failure.
2040 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2041 * or xmlKeepBlanksDefault(0) was called
2042 */
2043int
2044xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002045 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002046 xmlOutputBufferPtr buf;
2047 const char * encoding;
2048 xmlCharEncodingHandlerPtr handler = NULL;
2049 int ret;
2050
2051 if (cur == NULL) {
2052#ifdef DEBUG_TREE
2053 xmlGenericError(xmlGenericErrorContext,
2054 "xmlDocDump : document == NULL\n");
2055#endif
2056 return(-1);
2057 }
2058 encoding = (const char *) cur->encoding;
2059
2060 if (encoding != NULL) {
2061 handler = xmlFindCharEncodingHandler(encoding);
2062 if (handler == NULL) {
2063 xmlFree((char *) cur->encoding);
2064 cur->encoding = NULL;
2065 }
2066 }
2067 buf = xmlOutputBufferCreateFile(f, handler);
2068 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002069 memset(&ctxt, 0, sizeof(ctxt));
2070 ctxt.doc = cur;
2071 ctxt.buf = buf;
2072 ctxt.level = 0;
2073 ctxt.format = format;
2074 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002075 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002076 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002077
2078 ret = xmlOutputBufferClose(buf);
2079 return(ret);
2080}
2081
2082/**
2083 * xmlDocDump:
2084 * @f: the FILE*
2085 * @cur: the document
2086 *
2087 * Dump an XML document to an open FILE.
2088 *
2089 * returns: the number of bytes written or -1 in case of failure.
2090 */
2091int
2092xmlDocDump(FILE *f, xmlDocPtr cur) {
2093 return(xmlDocFormatDump (f, cur, 0));
2094}
2095
2096/**
2097 * xmlSaveFileTo:
2098 * @buf: an output I/O buffer
2099 * @cur: the document
2100 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2101 *
2102 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002103 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2104 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002105 *
2106 * returns: the number of bytes written or -1 in case of failure.
2107 */
2108int
2109xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002110 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002111 int ret;
2112
Daniel Veillard3d97e662004-11-04 10:49:00 +00002113 if (buf == NULL) return(-1);
2114 if (cur == NULL) {
2115 xmlOutputBufferClose(buf);
2116 return(-1);
2117 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002118 memset(&ctxt, 0, sizeof(ctxt));
2119 ctxt.doc = cur;
2120 ctxt.buf = buf;
2121 ctxt.level = 0;
2122 ctxt.format = 0;
2123 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002124 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002125 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002126 ret = xmlOutputBufferClose(buf);
2127 return(ret);
2128}
2129
2130/**
2131 * xmlSaveFormatFileTo:
2132 * @buf: an output I/O buffer
2133 * @cur: the document
2134 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2135 * @format: should formatting spaces been added
2136 *
2137 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002138 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2139 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002140 *
2141 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002142 */
2143int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002144xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2145 const char *encoding, int format)
2146{
2147 xmlSaveCtxt ctxt;
2148 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002149
Daniel Veillard3d97e662004-11-04 10:49:00 +00002150 if (buf == NULL) return(-1);
Daniel Veillardce244ad2004-11-05 10:03:46 +00002151 if ((cur == NULL) ||
2152 ((cur->type != XML_DOCUMENT_NODE) &&
2153 (cur->type != XML_HTML_DOCUMENT_NODE))) {
Daniel Veillard3d97e662004-11-04 10:49:00 +00002154 xmlOutputBufferClose(buf);
2155 return(-1);
2156 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002157 memset(&ctxt, 0, sizeof(ctxt));
2158 ctxt.doc = cur;
2159 ctxt.buf = buf;
2160 ctxt.level = 0;
2161 ctxt.format = format;
2162 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002163 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002164 xmlDocContentDumpOutput(&ctxt, cur);
2165 ret = xmlOutputBufferClose(buf);
2166 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002167}
2168
2169/**
2170 * xmlSaveFormatFileEnc:
2171 * @filename: the filename or URL to output
2172 * @cur: the document being saved
2173 * @encoding: the name of the encoding to use or NULL.
2174 * @format: should formatting spaces be added.
2175 *
2176 * Dump an XML document to a file or an URL.
2177 *
2178 * Returns the number of bytes written or -1 in case of error.
2179 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2180 * or xmlKeepBlanksDefault(0) was called
2181 */
2182int
2183xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2184 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002185 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002186 xmlOutputBufferPtr buf;
2187 xmlCharEncodingHandlerPtr handler = NULL;
2188 int ret;
2189
2190 if (cur == NULL)
2191 return(-1);
2192
2193 if (encoding == NULL)
2194 encoding = (const char *) cur->encoding;
2195
2196 if (encoding != NULL) {
2197
2198 handler = xmlFindCharEncodingHandler(encoding);
2199 if (handler == NULL)
2200 return(-1);
2201 }
2202
2203#ifdef HAVE_ZLIB_H
2204 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2205#endif
2206 /*
2207 * save the content to a temp buffer.
2208 */
2209 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2210 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002211 memset(&ctxt, 0, sizeof(ctxt));
2212 ctxt.doc = cur;
2213 ctxt.buf = buf;
2214 ctxt.level = 0;
2215 ctxt.format = format;
2216 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002217 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002218
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002219 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002220
2221 ret = xmlOutputBufferClose(buf);
2222 return(ret);
2223}
2224
2225
2226/**
2227 * xmlSaveFileEnc:
2228 * @filename: the filename (or URL)
2229 * @cur: the document
2230 * @encoding: the name of an encoding (or NULL)
2231 *
2232 * Dump an XML document, converting it to the given encoding
2233 *
2234 * returns: the number of bytes written or -1 in case of failure.
2235 */
2236int
2237xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2238 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2239}
2240
2241/**
2242 * xmlSaveFormatFile:
2243 * @filename: the filename (or URL)
2244 * @cur: the document
2245 * @format: should formatting spaces been added
2246 *
2247 * Dump an XML document to a file. Will use compression if
2248 * compiled in and enabled. If @filename is "-" the stdout file is
2249 * used. If @format is set then the document will be indented on output.
2250 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2251 * or xmlKeepBlanksDefault(0) was called
2252 *
2253 * returns: the number of bytes written or -1 in case of failure.
2254 */
2255int
2256xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2257 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2258}
2259
2260/**
2261 * xmlSaveFile:
2262 * @filename: the filename (or URL)
2263 * @cur: the document
2264 *
2265 * Dump an XML document to a file. Will use compression if
2266 * compiled in and enabled. If @filename is "-" the stdout file is
2267 * used.
2268 * returns: the number of bytes written or -1 in case of failure.
2269 */
2270int
2271xmlSaveFile(const char *filename, xmlDocPtr cur) {
2272 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2273}
2274
2275#endif /* LIBXML_OUTPUT_ENABLED */
2276
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002277#define bottom_xmlsave
2278#include "elfgcchack.h"