blob: 3addd6528d65b1687190ab78e288204343b76e62 [file] [log] [blame]
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001/*
Haibo Huangcfd91dc2020-07-30 23:01:33 -07002 * xmlsave.c: Implementation of the document serializer
Daniel Veillard1a8741c2004-03-04 13:40:59 +00003 *
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 Veillard50cdab52012-07-16 14:52:00 +080022#include "buf.h"
23#include "enc.h"
24#include "save.h"
25
Daniel Veillard1a8741c2004-03-04 13:40:59 +000026/************************************************************************
27 * *
28 * XHTML detection *
29 * *
30 ************************************************************************/
31#define XHTML_STRICT_PUBLIC_ID BAD_CAST \
32 "-//W3C//DTD XHTML 1.0 Strict//EN"
33#define XHTML_STRICT_SYSTEM_ID BAD_CAST \
34 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
35#define XHTML_FRAME_PUBLIC_ID BAD_CAST \
36 "-//W3C//DTD XHTML 1.0 Frameset//EN"
37#define XHTML_FRAME_SYSTEM_ID BAD_CAST \
38 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
39#define XHTML_TRANS_PUBLIC_ID BAD_CAST \
40 "-//W3C//DTD XHTML 1.0 Transitional//EN"
41#define XHTML_TRANS_SYSTEM_ID BAD_CAST \
42 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
43
44#define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
45/**
46 * xmlIsXHTML:
47 * @systemID: the system identifier
48 * @publicID: the public identifier
49 *
50 * Try to find if the document correspond to an XHTML DTD
51 *
52 * Returns 1 if true, 0 if not and -1 in case of error
53 */
54int
55xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
56 if ((systemID == NULL) && (publicID == NULL))
57 return(-1);
58 if (publicID != NULL) {
59 if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
60 if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
61 if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
62 }
63 if (systemID != NULL) {
64 if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
65 if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
66 if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
67 }
68 return(0);
69}
Daniel Veillard1a8741c2004-03-04 13:40:59 +000070
71#ifdef LIBXML_OUTPUT_ENABLED
72
Daniel Veillardf8e3db02012-09-11 13:26:36 +080073#define TODO \
Daniel Veillard1a8741c2004-03-04 13:40:59 +000074 xmlGenericError(xmlGenericErrorContext, \
75 "Unimplemented block at %s:%d\n", \
76 __FILE__, __LINE__);
77
78struct _xmlSaveCtxt {
79 void *_private;
80 int type;
81 int fd;
82 const xmlChar *filename;
83 const xmlChar *encoding;
84 xmlCharEncodingHandlerPtr handler;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000085 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +000086 int options;
87 int level;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000088 int format;
Daniel Veillard3995bc32004-05-15 18:57:31 +000089 char indent[MAX_INDENT + 1]; /* array for indenting output */
Daniel Veillard753086a2004-03-28 16:12:44 +000090 int indent_nr;
91 int indent_size;
Daniel Veillard3995bc32004-05-15 18:57:31 +000092 xmlCharEncodingOutputFunc escape; /* used for element content */
93 xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */
Daniel Veillard1a8741c2004-03-04 13:40:59 +000094};
95
96/************************************************************************
97 * *
Daniel Veillardf8e3db02012-09-11 13:26:36 +080098 * Output error handlers *
Daniel Veillard1a8741c2004-03-04 13:40:59 +000099 * *
100 ************************************************************************/
101/**
102 * xmlSaveErrMemory:
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700103 * @extra: extra information
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000104 *
105 * Handle an out of memory condition
106 */
107static void
108xmlSaveErrMemory(const char *extra)
109{
110 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
111}
112
113/**
114 * xmlSaveErr:
115 * @code: the error number
116 * @node: the location of the error.
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700117 * @extra: extra information
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000118 *
119 * Handle an out of memory condition
120 */
121static void
122xmlSaveErr(int code, xmlNodePtr node, const char *extra)
123{
124 const char *msg = NULL;
125
126 switch(code) {
127 case XML_SAVE_NOT_UTF8:
Rob Richards417b74d2006-08-15 23:14:24 +0000128 msg = "string is not in UTF-8\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000129 break;
130 case XML_SAVE_CHAR_INVALID:
Rob Richards417b74d2006-08-15 23:14:24 +0000131 msg = "invalid character value\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000132 break;
133 case XML_SAVE_UNKNOWN_ENCODING:
Rob Richards417b74d2006-08-15 23:14:24 +0000134 msg = "unknown encoding %s\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000135 break;
136 case XML_SAVE_NO_DOCTYPE:
Rob Richards417b74d2006-08-15 23:14:24 +0000137 msg = "document has no DOCTYPE\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000138 break;
139 default:
Rob Richards417b74d2006-08-15 23:14:24 +0000140 msg = "unexpected error number\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000141 }
142 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
143}
144
145/************************************************************************
146 * *
Daniel Veillard83a75e02004-05-14 21:50:42 +0000147 * Special escaping routines *
148 * *
149 ************************************************************************/
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000150static unsigned char *
151xmlSerializeHexCharRef(unsigned char *out, int val) {
152 unsigned char *ptr;
153
154 *out++ = '&';
155 *out++ = '#';
156 *out++ = 'x';
157 if (val < 0x10) ptr = out;
158 else if (val < 0x100) ptr = out + 1;
159 else if (val < 0x1000) ptr = out + 2;
160 else if (val < 0x10000) ptr = out + 3;
161 else if (val < 0x100000) ptr = out + 4;
162 else ptr = out + 5;
163 out = ptr + 1;
164 while (val > 0) {
165 switch (val & 0xF) {
166 case 0: *ptr-- = '0'; break;
167 case 1: *ptr-- = '1'; break;
168 case 2: *ptr-- = '2'; break;
169 case 3: *ptr-- = '3'; break;
170 case 4: *ptr-- = '4'; break;
171 case 5: *ptr-- = '5'; break;
172 case 6: *ptr-- = '6'; break;
173 case 7: *ptr-- = '7'; break;
174 case 8: *ptr-- = '8'; break;
175 case 9: *ptr-- = '9'; break;
176 case 0xA: *ptr-- = 'A'; break;
177 case 0xB: *ptr-- = 'B'; break;
178 case 0xC: *ptr-- = 'C'; break;
179 case 0xD: *ptr-- = 'D'; break;
180 case 0xE: *ptr-- = 'E'; break;
181 case 0xF: *ptr-- = 'F'; break;
182 default: *ptr-- = '0'; break;
183 }
184 val >>= 4;
185 }
186 *out++ = ';';
187 *out = 0;
188 return(out);
189}
190
Daniel Veillard83a75e02004-05-14 21:50:42 +0000191/**
192 * xmlEscapeEntities:
193 * @out: a pointer to an array of bytes to store the result
194 * @outlen: the length of @out
195 * @in: a pointer to an array of unescaped UTF-8 bytes
196 * @inlen: the length of @in
197 *
198 * Take a block of UTF-8 chars in and escape them. Used when there is no
199 * encoding specified.
200 *
201 * Returns 0 if success, or -1 otherwise
202 * The value of @inlen after return is the number of octets consumed
203 * if the return value is positive, else unpredictable.
204 * The value of @outlen after return is the number of octets consumed.
205 */
206static int
207xmlEscapeEntities(unsigned char* out, int *outlen,
208 const xmlChar* in, int *inlen) {
209 unsigned char* outstart = out;
210 const unsigned char* base = in;
211 unsigned char* outend = out + *outlen;
212 const unsigned char* inend;
213 int val;
214
215 inend = in + (*inlen);
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800216
Daniel Veillard83a75e02004-05-14 21:50:42 +0000217 while ((in < inend) && (out < outend)) {
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800218 if (*in == '<') {
Daniel Veillard83a75e02004-05-14 21:50:42 +0000219 if (outend - out < 4) break;
220 *out++ = '&';
221 *out++ = 'l';
222 *out++ = 't';
223 *out++ = ';';
224 in++;
225 continue;
226 } else if (*in == '>') {
227 if (outend - out < 4) break;
228 *out++ = '&';
229 *out++ = 'g';
230 *out++ = 't';
231 *out++ = ';';
232 in++;
233 continue;
234 } else if (*in == '&') {
235 if (outend - out < 5) break;
236 *out++ = '&';
237 *out++ = 'a';
238 *out++ = 'm';
239 *out++ = 'p';
240 *out++ = ';';
241 in++;
242 continue;
243 } else if (((*in >= 0x20) && (*in < 0x80)) ||
244 (*in == '\n') || (*in == '\t')) {
245 /*
246 * default case, just copy !
247 */
248 *out++ = *in++;
249 continue;
250 } else if (*in >= 0x80) {
251 /*
252 * We assume we have UTF-8 input.
253 */
Daniel Veillard07953482012-01-22 17:42:35 +0800254 if (outend - out < 11) break;
Daniel Veillard83a75e02004-05-14 21:50:42 +0000255
256 if (*in < 0xC0) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000257 xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000258 in++;
259 goto error;
260 } else if (*in < 0xE0) {
261 if (inend - in < 2) break;
262 val = (in[0]) & 0x1F;
263 val <<= 6;
264 val |= (in[1]) & 0x3F;
265 in += 2;
266 } else if (*in < 0xF0) {
267 if (inend - in < 3) break;
268 val = (in[0]) & 0x0F;
269 val <<= 6;
270 val |= (in[1]) & 0x3F;
271 val <<= 6;
272 val |= (in[2]) & 0x3F;
273 in += 3;
274 } else if (*in < 0xF8) {
275 if (inend - in < 4) break;
276 val = (in[0]) & 0x07;
277 val <<= 6;
278 val |= (in[1]) & 0x3F;
279 val <<= 6;
280 val |= (in[2]) & 0x3F;
281 val <<= 6;
282 val |= (in[3]) & 0x3F;
283 in += 4;
284 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000285 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000286 in++;
287 goto error;
288 }
289 if (!IS_CHAR(val)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000290 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000291 in++;
292 goto error;
293 }
294
295 /*
296 * We could do multiple things here. Just save as a char ref
297 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000298 out = xmlSerializeHexCharRef(out, val);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000299 } else if (IS_BYTE_CHAR(*in)) {
300 if (outend - out < 6) break;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000301 out = xmlSerializeHexCharRef(out, *in++);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000302 } else {
303 xmlGenericError(xmlGenericErrorContext,
304 "xmlEscapeEntities : char out of range\n");
305 in++;
306 goto error;
307 }
308 }
309 *outlen = out - outstart;
310 *inlen = in - base;
311 return(0);
312error:
313 *outlen = out - outstart;
314 *inlen = in - base;
315 return(-1);
316}
317
318/************************************************************************
319 * *
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000320 * Allocation and deallocation *
321 * *
322 ************************************************************************/
Daniel Veillard753086a2004-03-28 16:12:44 +0000323/**
324 * xmlSaveCtxtInit:
325 * @ctxt: the saving context
326 *
327 * Initialize a saving context
328 */
329static void
330xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
331{
332 int i;
William M. Brack12d37ab2005-02-21 13:54:07 +0000333 int len;
Daniel Veillard753086a2004-03-28 16:12:44 +0000334
335 if (ctxt == NULL) return;
Daniel Veillard3995bc32004-05-15 18:57:31 +0000336 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
337 ctxt->escape = xmlEscapeEntities;
William M. Brack12d37ab2005-02-21 13:54:07 +0000338 len = xmlStrlen((xmlChar *)xmlTreeIndentString);
339 if ((xmlTreeIndentString == NULL) || (len == 0)) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000340 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
341 } else {
William M. Brack12d37ab2005-02-21 13:54:07 +0000342 ctxt->indent_size = len;
Daniel Veillard753086a2004-03-28 16:12:44 +0000343 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
344 for (i = 0;i < ctxt->indent_nr;i++)
345 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
346 ctxt->indent_size);
347 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
348 }
Rob Richards2ce51c02005-09-12 12:16:35 +0000349
Daniel Veillard9a00fd22005-11-09 08:56:26 +0000350 if (xmlSaveNoEmptyTags) {
351 ctxt->options |= XML_SAVE_NO_EMPTY;
352 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000353}
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000354
355/**
356 * xmlFreeSaveCtxt:
357 *
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700358 * Free a saving context, destroying the output in any remaining buffer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000359 */
360static void
361xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
362{
363 if (ctxt == NULL) return;
364 if (ctxt->encoding != NULL)
365 xmlFree((char *) ctxt->encoding);
Daniel Veillarde2161a62004-04-29 17:14:25 +0000366 if (ctxt->buf != NULL)
367 xmlOutputBufferClose(ctxt->buf);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000368 xmlFree(ctxt);
369}
370
371/**
372 * xmlNewSaveCtxt:
373 *
374 * Create a new saving context
375 *
376 * Returns the new structure or NULL in case of error
377 */
378static xmlSaveCtxtPtr
379xmlNewSaveCtxt(const char *encoding, int options)
380{
381 xmlSaveCtxtPtr ret;
382
383 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
384 if (ret == NULL) {
385 xmlSaveErrMemory("creating saving context");
386 return ( NULL );
387 }
388 memset(ret, 0, sizeof(xmlSaveCtxt));
Daniel Veillard6fc5db02005-01-16 00:05:58 +0000389
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 Veillarddab39b52006-10-16 23:22:10 +0000398 ret->escape = NULL;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000399 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000400 xmlSaveCtxtInit(ret);
401
Rob Richards2ce51c02005-09-12 12:16:35 +0000402 /*
403 * Use the options
404 */
405
Daniel Veillard9a00fd22005-11-09 08:56:26 +0000406 /* Re-check this option as it may already have been set */
407 if ((ret->options & XML_SAVE_NO_EMPTY) && ! (options & XML_SAVE_NO_EMPTY)) {
408 options |= XML_SAVE_NO_EMPTY;
409 }
Rob Richards2ce51c02005-09-12 12:16:35 +0000410
411 ret->options = options;
412 if (options & XML_SAVE_FORMAT)
413 ret->format = 1;
Adam Spraggd2e62312010-11-03 15:33:40 +0100414 else if (options & XML_SAVE_WSNONSIG)
415 ret->format = 2;
Rob Richards2ce51c02005-09-12 12:16:35 +0000416
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000417 return(ret);
418}
419
420/************************************************************************
421 * *
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800422 * Dumping XML tree content to a simple buffer *
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000423 * *
424 ************************************************************************/
425/**
426 * xmlAttrSerializeContent:
427 * @buf: the XML buffer output
428 * @doc: the document
429 * @attr: the attribute pointer
430 *
431 * Serialize the attribute in the buffer
432 */
433static void
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000434xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000435{
436 xmlNodePtr children;
437
438 children = attr->children;
439 while (children != NULL) {
440 switch (children->type) {
441 case XML_TEXT_NODE:
Daniel Veillard50cdab52012-07-16 14:52:00 +0800442 xmlBufAttrSerializeTxtContent(buf->buffer, attr->doc,
443 attr, children->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000444 break;
445 case XML_ENTITY_REF_NODE:
Daniel Veillard50cdab52012-07-16 14:52:00 +0800446 xmlBufAdd(buf->buffer, BAD_CAST "&", 1);
447 xmlBufAdd(buf->buffer, children->name,
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000448 xmlStrlen(children->name));
Daniel Veillard50cdab52012-07-16 14:52:00 +0800449 xmlBufAdd(buf->buffer, BAD_CAST ";", 1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000450 break;
451 default:
452 /* should not happen unless we have a badly built tree */
453 break;
454 }
455 children = children->next;
456 }
457}
458
Daniel Veillard50cdab52012-07-16 14:52:00 +0800459/**
460 * xmlBufDumpNotationTable:
461 * @buf: an xmlBufPtr output
462 * @table: A notation table
463 *
464 * This will dump the content of the notation table as an XML DTD definition
465 */
466void
467xmlBufDumpNotationTable(xmlBufPtr buf, xmlNotationTablePtr table) {
468 xmlBufferPtr buffer;
469
470 buffer = xmlBufferCreate();
471 if (buffer == NULL) {
472 /*
473 * TODO set the error in buf
474 */
475 return;
476 }
477 xmlDumpNotationTable(buffer, table);
478 xmlBufMergeBuffer(buf, buffer);
479}
480
481/**
482 * xmlBufDumpElementDecl:
483 * @buf: an xmlBufPtr output
484 * @elem: An element table
485 *
486 * This will dump the content of the element declaration as an XML
487 * DTD definition
488 */
489void
490xmlBufDumpElementDecl(xmlBufPtr buf, xmlElementPtr elem) {
491 xmlBufferPtr buffer;
492
493 buffer = xmlBufferCreate();
494 if (buffer == NULL) {
495 /*
496 * TODO set the error in buf
497 */
498 return;
499 }
500 xmlDumpElementDecl(buffer, elem);
501 xmlBufMergeBuffer(buf, buffer);
502}
503
504/**
505 * xmlBufDumpAttributeDecl:
506 * @buf: an xmlBufPtr output
507 * @attr: An attribute declaration
508 *
509 * This will dump the content of the attribute declaration as an XML
510 * DTD definition
511 */
512void
513xmlBufDumpAttributeDecl(xmlBufPtr buf, xmlAttributePtr attr) {
514 xmlBufferPtr buffer;
515
516 buffer = xmlBufferCreate();
517 if (buffer == NULL) {
518 /*
519 * TODO set the error in buf
520 */
521 return;
522 }
523 xmlDumpAttributeDecl(buffer, attr);
524 xmlBufMergeBuffer(buf, buffer);
525}
526
527/**
528 * xmlBufDumpEntityDecl:
529 * @buf: an xmlBufPtr output
530 * @ent: An entity table
531 *
532 * This will dump the content of the entity table as an XML DTD definition
533 */
534void
535xmlBufDumpEntityDecl(xmlBufPtr buf, xmlEntityPtr ent) {
536 xmlBufferPtr buffer;
537
538 buffer = xmlBufferCreate();
539 if (buffer == NULL) {
540 /*
541 * TODO set the error in buf
542 */
543 return;
544 }
545 xmlDumpEntityDecl(buffer, ent);
546 xmlBufMergeBuffer(buf, buffer);
547}
548
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000549/************************************************************************
550 * *
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800551 * Dumping XML tree content to an I/O output buffer *
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000552 * *
553 ************************************************************************/
554
Daniel Veillardda3fee42008-09-01 13:08:57 +0000555static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
556 xmlOutputBufferPtr buf = ctxt->buf;
557
558 if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
559 buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
560 if (buf->encoder == NULL) {
561 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
562 (const char *)encoding);
563 return(-1);
564 }
Daniel Veillard50cdab52012-07-16 14:52:00 +0800565 buf->conv = xmlBufCreate();
Daniel Veillardda3fee42008-09-01 13:08:57 +0000566 if (buf->conv == NULL) {
567 xmlCharEncCloseFunc(buf->encoder);
568 xmlSaveErrMemory("creating encoding buffer");
569 return(-1);
570 }
571 /*
572 * initialize the state, e.g. if outputting a BOM
573 */
Daniel Veillard50cdab52012-07-16 14:52:00 +0800574 xmlCharEncOutput(buf, 1);
Daniel Veillardda3fee42008-09-01 13:08:57 +0000575 }
576 return(0);
577}
578
579static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
580 xmlOutputBufferPtr buf = ctxt->buf;
581 xmlOutputBufferFlush(buf);
582 xmlCharEncCloseFunc(buf->encoder);
Daniel Veillard50cdab52012-07-16 14:52:00 +0800583 xmlBufFree(buf->conv);
Daniel Veillardda3fee42008-09-01 13:08:57 +0000584 buf->encoder = NULL;
585 buf->conv = NULL;
586 return(0);
587}
588
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000589#ifdef LIBXML_HTML_ENABLED
590static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000591xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000592#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000593static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000594void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
Daniel Veillarddab39b52006-10-16 23:22:10 +0000595static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000596
597/**
Adam Spraggd2e62312010-11-03 15:33:40 +0100598 * xmlOutputBufferWriteWSNonSig:
599 * @ctxt: The save context
600 * @extra: Number of extra indents to apply to ctxt->level
601 *
602 * Write out formatting for non-significant whitespace output.
603 */
604static void
605xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra)
606{
607 int i;
608 if ((ctxt == NULL) || (ctxt->buf == NULL))
609 return;
610 xmlOutputBufferWrite(ctxt->buf, 1, "\n");
611 for (i = 0; i < (ctxt->level + extra); i += ctxt->indent_nr) {
612 xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size *
613 ((ctxt->level + extra - i) > ctxt->indent_nr ?
614 ctxt->indent_nr : (ctxt->level + extra - i)),
615 ctxt->indent);
616 }
617}
618
619/**
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000620 * xmlNsDumpOutput:
621 * @buf: the XML buffer output
622 * @cur: a namespace
Adam Spraggd2e62312010-11-03 15:33:40 +0100623 * @ctxt: the output save context. Optional.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000624 *
625 * Dump a local Namespace definition.
626 * Should be called in the context of attributes dumps.
Adam Spraggd2e62312010-11-03 15:33:40 +0100627 * If @ctxt is supplied, @buf should be its buffer.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000628 */
629static void
Adam Spraggd2e62312010-11-03 15:33:40 +0100630xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
Daniel Veillardce244ad2004-11-05 10:03:46 +0000631 if ((cur == NULL) || (buf == NULL)) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000632 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
633 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
634 return;
635
Adam Spraggd2e62312010-11-03 15:33:40 +0100636 if (ctxt != NULL && ctxt->format == 2)
637 xmlOutputBufferWriteWSNonSig(ctxt, 2);
638 else
639 xmlOutputBufferWrite(buf, 1, " ");
640
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000641 /* Within the context of an element attributes */
642 if (cur->prefix != NULL) {
Adam Spraggd2e62312010-11-03 15:33:40 +0100643 xmlOutputBufferWrite(buf, 6, "xmlns:");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000644 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
645 } else
Adam Spraggd2e62312010-11-03 15:33:40 +0100646 xmlOutputBufferWrite(buf, 5, "xmlns");
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000647 xmlOutputBufferWrite(buf, 1, "=");
Daniel Veillard50cdab52012-07-16 14:52:00 +0800648 xmlBufWriteQuotedString(buf->buffer, cur->href);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000649 }
650}
651
652/**
Adam Spraggd2e62312010-11-03 15:33:40 +0100653 * xmlNsDumpOutputCtxt
654 * @ctxt: the save context
655 * @cur: a namespace
656 *
657 * Dump a local Namespace definition to a save context.
658 * Should be called in the context of attribute dumps.
659 */
660static void
661xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
662 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
663}
664
665/**
666 * xmlNsListDumpOutputCtxt
667 * @ctxt: the save context
668 * @cur: the first namespace
669 *
670 * Dump a list of local namespace definitions to a save context.
671 * Should be called in the context of attribute dumps.
672 */
673static void
674xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
675 while (cur != NULL) {
676 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
677 cur = cur->next;
678 }
679}
680
681/**
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000682 * xmlNsListDumpOutput:
683 * @buf: the XML buffer output
684 * @cur: the first namespace
685 *
686 * Dump a list of local Namespace definitions.
687 * Should be called in the context of attributes dumps.
688 */
689void
690xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
691 while (cur != NULL) {
Adam Spraggd2e62312010-11-03 15:33:40 +0100692 xmlNsDumpOutput(buf, cur, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000693 cur = cur->next;
694 }
695}
696
697/**
698 * xmlDtdDumpOutput:
699 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000700 * @dtd: the pointer to the DTD
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800701 *
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000702 * Dump the XML document DTD, if any.
703 */
704static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000705xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
706 xmlOutputBufferPtr buf;
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700707 xmlNodePtr cur;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000708 int format, level;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000709
710 if (dtd == NULL) return;
711 if ((ctxt == NULL) || (ctxt->buf == NULL))
712 return;
713 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000714 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000715 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
716 if (dtd->ExternalID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000717 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
Daniel Veillard50cdab52012-07-16 14:52:00 +0800718 xmlBufWriteQuotedString(buf->buffer, dtd->ExternalID);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000719 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard50cdab52012-07-16 14:52:00 +0800720 xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000721 } else if (dtd->SystemID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000722 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
Daniel Veillard50cdab52012-07-16 14:52:00 +0800723 xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000724 }
725 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
Daniel Veillard41c4a752004-09-08 20:55:38 +0000726 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
727 (dtd->pentities == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000728 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000729 return;
730 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000731 xmlOutputBufferWrite(buf, 3, " [\n");
Daniel Veillard41c4a752004-09-08 20:55:38 +0000732 /*
733 * Dump the notations first they are not in the DTD children list
734 * Do this only on a standalone DTD or on the internal subset though.
735 */
736 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
737 (dtd->doc->intSubset == dtd))) {
Daniel Veillard50cdab52012-07-16 14:52:00 +0800738 xmlBufDumpNotationTable(buf->buffer,
739 (xmlNotationTablePtr) dtd->notations);
Daniel Veillardda3b29a2004-08-14 11:15:13 +0000740 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000741 format = ctxt->format;
742 level = ctxt->level;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000743 ctxt->format = 0;
744 ctxt->level = -1;
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700745 for (cur = dtd->children; cur != NULL; cur = cur->next) {
746 xmlNodeDumpOutputInternal(ctxt, cur);
747 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000748 ctxt->format = format;
749 ctxt->level = level;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000750 xmlOutputBufferWrite(buf, 2, "]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000751}
752
753/**
754 * xmlAttrDumpOutput:
755 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000756 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000757 *
758 * Dump an XML attribute
759 */
760static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000761xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
762 xmlOutputBufferPtr buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000763
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000764 if (cur == NULL) return;
765 buf = ctxt->buf;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000766 if (buf == NULL) return;
Adam Spraggd2e62312010-11-03 15:33:40 +0100767 if (ctxt->format == 2)
768 xmlOutputBufferWriteWSNonSig(ctxt, 2);
769 else
770 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000771 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
772 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000773 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000774 }
775 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000776 xmlOutputBufferWrite(buf, 2, "=\"");
777 xmlAttrSerializeContent(buf, cur);
778 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000779}
780
Daniel Veillardda3fee42008-09-01 13:08:57 +0000781#ifdef LIBXML_HTML_ENABLED
782/**
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700783 * htmlNodeDumpOutputInternal:
Daniel Veillardda3fee42008-09-01 13:08:57 +0000784 * @cur: the current node
785 *
786 * Dump an HTML node, recursive behaviour, children are printed too.
787 */
788static int
789htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
790 const xmlChar *oldenc = NULL;
791 const xmlChar *oldctxtenc = ctxt->encoding;
792 const xmlChar *encoding = ctxt->encoding;
793 xmlOutputBufferPtr buf = ctxt->buf;
794 int switched_encoding = 0;
795 xmlDocPtr doc;
796
797 xmlInitParser();
798
Daniel Veillard141ebfa2009-09-02 14:58:13 +0200799 doc = cur->doc;
800 if (doc != NULL) {
Daniel Veillardda3fee42008-09-01 13:08:57 +0000801 oldenc = doc->encoding;
802 if (ctxt->encoding != NULL) {
803 doc->encoding = BAD_CAST ctxt->encoding;
804 } else if (doc->encoding != NULL) {
805 encoding = doc->encoding;
806 }
807 }
808
809 if ((encoding != NULL) && (doc != NULL))
810 htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
811 if ((encoding == NULL) && (doc != NULL))
812 encoding = htmlGetMetaEncoding(doc);
813 if (encoding == NULL)
814 encoding = BAD_CAST "HTML";
815 if ((encoding != NULL) && (oldctxtenc == NULL) &&
816 (buf->encoder == NULL) && (buf->conv == NULL)) {
817 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
818 doc->encoding = oldenc;
819 return(-1);
820 }
821 switched_encoding = 1;
822 }
823 if (ctxt->options & XML_SAVE_FORMAT)
824 htmlNodeDumpFormatOutput(buf, doc, cur,
825 (const char *)encoding, 1);
826 else
827 htmlNodeDumpFormatOutput(buf, doc, cur,
828 (const char *)encoding, 0);
829 /*
830 * Restore the state of the saving context at the end of the document
831 */
832 if ((switched_encoding) && (oldctxtenc == NULL)) {
833 xmlSaveClearEncoding(ctxt);
834 }
835 if (doc != NULL)
836 doc->encoding = oldenc;
837 return(0);
838}
839#endif
840
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000841/**
842 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000843 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000844 *
845 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000846 */
847static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000848xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700849 int format = ctxt->format;
Elliott Hughes60f5c162021-08-20 17:09:52 -0700850 xmlNodePtr tmp, root, unformattedNode = NULL, parent;
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700851 xmlAttrPtr attr;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000852 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000853 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000854
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000855 if (cur == NULL) return;
856 buf = ctxt->buf;
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700857
858 root = cur;
Elliott Hughes60f5c162021-08-20 17:09:52 -0700859 parent = cur->parent;
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700860 while (1) {
861 switch (cur->type) {
862 case XML_DOCUMENT_NODE:
863 case XML_HTML_DOCUMENT_NODE:
864 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
865 break;
866
867 case XML_DTD_NODE:
868 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
869 break;
870
871 case XML_DOCUMENT_FRAG_NODE:
Elliott Hughes60f5c162021-08-20 17:09:52 -0700872 /* Always validate cur->parent when descending. */
873 if ((cur->parent == parent) && (cur->children != NULL)) {
874 parent = cur;
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700875 cur = cur->children;
876 continue;
877 }
878 break;
879
880 case XML_ELEMENT_DECL:
881 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
882 break;
883
884 case XML_ATTRIBUTE_DECL:
885 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
886 break;
887
888 case XML_ENTITY_DECL:
889 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
890 break;
891
892 case XML_ELEMENT_NODE:
Elliott Hughes60f5c162021-08-20 17:09:52 -0700893 if ((cur != root) && (ctxt->format == 1) &&
894 (xmlIndentTreeOutput))
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700895 xmlOutputBufferWrite(buf, ctxt->indent_size *
896 (ctxt->level > ctxt->indent_nr ?
897 ctxt->indent_nr : ctxt->level),
898 ctxt->indent);
899
Elliott Hughes60f5c162021-08-20 17:09:52 -0700900 /*
901 * Some users like lxml are known to pass nodes with a corrupted
902 * tree structure. Fall back to a recursive call to handle this
903 * case.
904 */
905 if ((cur->parent != parent) && (cur->children != NULL)) {
906 xmlNodeDumpOutputInternal(ctxt, cur);
907 break;
908 }
909
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700910 xmlOutputBufferWrite(buf, 1, "<");
911 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
912 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
913 xmlOutputBufferWrite(buf, 1, ":");
914 }
915 xmlOutputBufferWriteString(buf, (const char *)cur->name);
916 if (cur->nsDef)
917 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
918 for (attr = cur->properties; attr != NULL; attr = attr->next)
919 xmlAttrDumpOutput(ctxt, attr);
920
921 if (cur->children == NULL) {
922 if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
923 if (ctxt->format == 2)
924 xmlOutputBufferWriteWSNonSig(ctxt, 0);
925 xmlOutputBufferWrite(buf, 2, "/>");
926 } else {
927 if (ctxt->format == 2)
928 xmlOutputBufferWriteWSNonSig(ctxt, 1);
929 xmlOutputBufferWrite(buf, 3, "></");
930 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
931 xmlOutputBufferWriteString(buf,
932 (const char *)cur->ns->prefix);
933 xmlOutputBufferWrite(buf, 1, ":");
934 }
935 xmlOutputBufferWriteString(buf, (const char *)cur->name);
936 if (ctxt->format == 2)
937 xmlOutputBufferWriteWSNonSig(ctxt, 0);
938 xmlOutputBufferWrite(buf, 1, ">");
939 }
940 } else {
941 if (ctxt->format == 1) {
942 tmp = cur->children;
943 while (tmp != NULL) {
944 if ((tmp->type == XML_TEXT_NODE) ||
945 (tmp->type == XML_CDATA_SECTION_NODE) ||
946 (tmp->type == XML_ENTITY_REF_NODE)) {
947 ctxt->format = 0;
948 unformattedNode = cur;
949 break;
950 }
951 tmp = tmp->next;
952 }
953 }
954 if (ctxt->format == 2)
955 xmlOutputBufferWriteWSNonSig(ctxt, 1);
956 xmlOutputBufferWrite(buf, 1, ">");
957 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
958 if (ctxt->level >= 0) ctxt->level++;
Elliott Hughes60f5c162021-08-20 17:09:52 -0700959 parent = cur;
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700960 cur = cur->children;
961 continue;
962 }
963
964 break;
965
966 case XML_TEXT_NODE:
967 if (cur->content == NULL)
968 break;
William M. Brack4e1c2db2005-02-11 10:58:55 +0000969 if (cur->name != xmlStringTextNoenc) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000970 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000971 } else {
972 /*
973 * Disable escaping, needed for XSLT
974 */
975 xmlOutputBufferWriteString(buf, (const char *) cur->content);
976 }
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700977 break;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000978
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700979 case XML_PI_NODE:
980 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
981 xmlOutputBufferWrite(buf, ctxt->indent_size *
982 (ctxt->level > ctxt->indent_nr ?
983 ctxt->indent_nr : ctxt->level),
984 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000985
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700986 if (cur->content != NULL) {
987 xmlOutputBufferWrite(buf, 2, "<?");
988 xmlOutputBufferWriteString(buf, (const char *)cur->name);
989 if (cur->content != NULL) {
990 if (ctxt->format == 2)
991 xmlOutputBufferWriteWSNonSig(ctxt, 0);
992 else
993 xmlOutputBufferWrite(buf, 1, " ");
994 xmlOutputBufferWriteString(buf,
995 (const char *)cur->content);
996 }
997 xmlOutputBufferWrite(buf, 2, "?>");
998 } else {
999 xmlOutputBufferWrite(buf, 2, "<?");
1000 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1001 if (ctxt->format == 2)
1002 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1003 xmlOutputBufferWrite(buf, 2, "?>");
1004 }
1005 break;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001006
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001007 case XML_COMMENT_NODE:
1008 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
1009 xmlOutputBufferWrite(buf, ctxt->indent_size *
1010 (ctxt->level > ctxt->indent_nr ?
1011 ctxt->indent_nr : ctxt->level),
1012 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001013
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001014 if (cur->content != NULL) {
1015 xmlOutputBufferWrite(buf, 4, "<!--");
1016 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1017 xmlOutputBufferWrite(buf, 3, "-->");
1018 }
1019 break;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001020
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001021 case XML_ENTITY_REF_NODE:
1022 xmlOutputBufferWrite(buf, 1, "&");
1023 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1024 xmlOutputBufferWrite(buf, 1, ";");
1025 break;
1026
1027 case XML_CDATA_SECTION_NODE:
1028 if (cur->content == NULL || *cur->content == '\0') {
1029 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1030 } else {
1031 start = end = cur->content;
1032 while (*end != '\0') {
1033 if ((*end == ']') && (*(end + 1) == ']') &&
1034 (*(end + 2) == '>')) {
1035 end = end + 2;
1036 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1037 xmlOutputBufferWrite(buf, end - start,
1038 (const char *)start);
1039 xmlOutputBufferWrite(buf, 3, "]]>");
1040 start = end;
1041 }
1042 end++;
1043 }
1044 if (start != end) {
1045 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1046 xmlOutputBufferWriteString(buf, (const char *)start);
1047 xmlOutputBufferWrite(buf, 3, "]]>");
1048 }
1049 }
1050 break;
1051
1052 case XML_ATTRIBUTE_NODE:
1053 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1054 break;
1055
1056 case XML_NAMESPACE_DECL:
1057 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1058 break;
1059
1060 default:
1061 break;
1062 }
1063
1064 while (1) {
1065 if (cur == root)
1066 return;
Haibo Huangf0a546b2020-09-01 20:28:19 -07001067 if ((ctxt->format == 1) &&
1068 (cur->type != XML_XINCLUDE_START) &&
1069 (cur->type != XML_XINCLUDE_END))
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001070 xmlOutputBufferWrite(buf, 1, "\n");
1071 if (cur->next != NULL) {
1072 cur = cur->next;
1073 break;
1074 }
1075
Elliott Hughes60f5c162021-08-20 17:09:52 -07001076 cur = parent;
1077 /* cur->parent was validated when descending. */
1078 parent = cur->parent;
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001079
1080 if (cur->type == XML_ELEMENT_NODE) {
1081 if (ctxt->level > 0) ctxt->level--;
1082 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1083 xmlOutputBufferWrite(buf, ctxt->indent_size *
1084 (ctxt->level > ctxt->indent_nr ?
1085 ctxt->indent_nr : ctxt->level),
1086 ctxt->indent);
1087
1088 xmlOutputBufferWrite(buf, 2, "</");
1089 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1090 xmlOutputBufferWriteString(buf,
1091 (const char *)cur->ns->prefix);
1092 xmlOutputBufferWrite(buf, 1, ":");
1093 }
1094
1095 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1096 if (ctxt->format == 2)
1097 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1098 xmlOutputBufferWrite(buf, 1, ">");
1099
1100 if (cur == unformattedNode) {
1101 ctxt->format = format;
1102 unformattedNode = NULL;
1103 }
1104 }
1105 }
1106 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001107}
1108
1109/**
1110 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001111 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001112 *
1113 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001114 */
Daniel Veillarddab39b52006-10-16 23:22:10 +00001115static int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001116xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001117#ifdef LIBXML_HTML_ENABLED
1118 xmlDtdPtr dtd;
1119 int is_xhtml = 0;
1120#endif
1121 const xmlChar *oldenc = cur->encoding;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001122 const xmlChar *oldctxtenc = ctxt->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001123 const xmlChar *encoding = ctxt->encoding;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001124 xmlCharEncodingOutputFunc oldescape = ctxt->escape;
1125 xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
1126 xmlOutputBufferPtr buf = ctxt->buf;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001127 xmlCharEncoding enc;
Daniel Veillardda3fee42008-09-01 13:08:57 +00001128 int switched_encoding = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001129
1130 xmlInitParser();
1131
Daniel Veillardda3fee42008-09-01 13:08:57 +00001132 if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
1133 (cur->type != XML_DOCUMENT_NODE))
1134 return(-1);
1135
Daniel Veillarddab39b52006-10-16 23:22:10 +00001136 if (ctxt->encoding != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001137 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001138 } else if (cur->encoding != NULL) {
1139 encoding = cur->encoding;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001140 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001141
Daniel Veillard856d9282008-09-25 14:31:40 +00001142 if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
1143 ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
1144 ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
1145 (ctxt->options & XML_SAVE_AS_HTML)) {
Daniel Veillardda3fee42008-09-01 13:08:57 +00001146#ifdef LIBXML_HTML_ENABLED
1147 if (encoding != NULL)
1148 htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1149 if (encoding == NULL)
1150 encoding = htmlGetMetaEncoding(cur);
1151 if (encoding == NULL)
1152 encoding = BAD_CAST "HTML";
1153 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1154 (buf->encoder == NULL) && (buf->conv == NULL)) {
1155 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1156 cur->encoding = oldenc;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001157 return(-1);
1158 }
Daniel Veillarddab39b52006-10-16 23:22:10 +00001159 }
Daniel Veillardda3fee42008-09-01 13:08:57 +00001160 if (ctxt->options & XML_SAVE_FORMAT)
1161 htmlDocContentDumpFormatOutput(buf, cur,
1162 (const char *)encoding, 1);
Daniel Veillard100e1802005-08-08 14:44:11 +00001163 else
Daniel Veillardda3fee42008-09-01 13:08:57 +00001164 htmlDocContentDumpFormatOutput(buf, cur,
1165 (const char *)encoding, 0);
1166 if (ctxt->encoding != NULL)
1167 cur->encoding = oldenc;
1168 return(0);
1169#else
1170 return(-1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001171#endif
Daniel Veillard856d9282008-09-25 14:31:40 +00001172 } else if ((cur->type == XML_DOCUMENT_NODE) ||
1173 (ctxt->options & XML_SAVE_AS_XML) ||
1174 (ctxt->options & XML_SAVE_XHTML)) {
Daniel Veillardda3fee42008-09-01 13:08:57 +00001175 enc = xmlParseCharEncoding((const char*) encoding);
1176 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1177 (buf->encoder == NULL) && (buf->conv == NULL) &&
1178 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
1179 if ((enc != XML_CHAR_ENCODING_UTF8) &&
1180 (enc != XML_CHAR_ENCODING_NONE) &&
1181 (enc != XML_CHAR_ENCODING_ASCII)) {
1182 /*
1183 * we need to switch to this encoding but just for this
1184 * document since we output the XMLDecl the conversion
1185 * must be done to not generate not well formed documents.
1186 */
1187 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1188 cur->encoding = oldenc;
1189 return(-1);
1190 }
1191 switched_encoding = 1;
1192 }
1193 if (ctxt->escape == xmlEscapeEntities)
1194 ctxt->escape = NULL;
1195 if (ctxt->escapeAttr == xmlEscapeEntities)
1196 ctxt->escapeAttr = NULL;
1197 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001198
Daniel Veillardda3fee42008-09-01 13:08:57 +00001199
1200 /*
1201 * Save the XML declaration
1202 */
1203 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1204 xmlOutputBufferWrite(buf, 14, "<?xml version=");
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001205 if (cur->version != NULL)
Daniel Veillard50cdab52012-07-16 14:52:00 +08001206 xmlBufWriteQuotedString(buf->buffer, cur->version);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001207 else
Daniel Veillardda3fee42008-09-01 13:08:57 +00001208 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1209 if (encoding != NULL) {
1210 xmlOutputBufferWrite(buf, 10, " encoding=");
Daniel Veillard50cdab52012-07-16 14:52:00 +08001211 xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
Daniel Veillardda3fee42008-09-01 13:08:57 +00001212 }
1213 switch (cur->standalone) {
1214 case 0:
1215 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1216 break;
1217 case 1:
1218 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1219 break;
1220 }
1221 xmlOutputBufferWrite(buf, 3, "?>\n");
1222 }
1223
1224#ifdef LIBXML_HTML_ENABLED
Daniel Veillard856d9282008-09-25 14:31:40 +00001225 if (ctxt->options & XML_SAVE_XHTML)
1226 is_xhtml = 1;
Daniel Veillardda3fee42008-09-01 13:08:57 +00001227 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1228 dtd = xmlGetIntSubset(cur);
1229 if (dtd != NULL) {
1230 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1231 if (is_xhtml < 0) is_xhtml = 0;
1232 }
1233 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001234#endif
Daniel Veillardda3fee42008-09-01 13:08:57 +00001235 if (cur->children != NULL) {
1236 xmlNodePtr child = cur->children;
1237
1238 while (child != NULL) {
1239 ctxt->level = 0;
1240#ifdef LIBXML_HTML_ENABLED
1241 if (is_xhtml)
1242 xhtmlNodeDumpOutput(ctxt, child);
1243 else
1244#endif
1245 xmlNodeDumpOutputInternal(ctxt, child);
Haibo Huangf0a546b2020-09-01 20:28:19 -07001246 if ((child->type != XML_XINCLUDE_START) &&
1247 (child->type != XML_XINCLUDE_END))
1248 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillardda3fee42008-09-01 13:08:57 +00001249 child = child->next;
1250 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001251 }
1252 }
Daniel Veillardda3fee42008-09-01 13:08:57 +00001253
Daniel Veillarddab39b52006-10-16 23:22:10 +00001254 /*
1255 * Restore the state of the saving context at the end of the document
1256 */
Daniel Veillardda3fee42008-09-01 13:08:57 +00001257 if ((switched_encoding) && (oldctxtenc == NULL)) {
1258 xmlSaveClearEncoding(ctxt);
Daniel Veillarddab39b52006-10-16 23:22:10 +00001259 ctxt->escape = oldescape;
1260 ctxt->escapeAttr = oldescapeAttr;
1261 }
Daniel Veillardda3fee42008-09-01 13:08:57 +00001262 cur->encoding = oldenc;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001263 return(0);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001264}
1265
1266#ifdef LIBXML_HTML_ENABLED
1267/************************************************************************
1268 * *
1269 * Functions specific to XHTML serialization *
1270 * *
1271 ************************************************************************/
1272
1273/**
1274 * xhtmlIsEmpty:
1275 * @node: the node
1276 *
1277 * Check if a node is an empty xhtml node
1278 *
1279 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1280 */
1281static int
1282xhtmlIsEmpty(xmlNodePtr node) {
1283 if (node == NULL)
1284 return(-1);
1285 if (node->type != XML_ELEMENT_NODE)
1286 return(0);
1287 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1288 return(0);
1289 if (node->children != NULL)
1290 return(0);
1291 switch (node->name[0]) {
1292 case 'a':
1293 if (xmlStrEqual(node->name, BAD_CAST "area"))
1294 return(1);
1295 return(0);
1296 case 'b':
1297 if (xmlStrEqual(node->name, BAD_CAST "br"))
1298 return(1);
1299 if (xmlStrEqual(node->name, BAD_CAST "base"))
1300 return(1);
1301 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1302 return(1);
1303 return(0);
1304 case 'c':
1305 if (xmlStrEqual(node->name, BAD_CAST "col"))
1306 return(1);
1307 return(0);
1308 case 'f':
1309 if (xmlStrEqual(node->name, BAD_CAST "frame"))
1310 return(1);
1311 return(0);
1312 case 'h':
1313 if (xmlStrEqual(node->name, BAD_CAST "hr"))
1314 return(1);
1315 return(0);
1316 case 'i':
1317 if (xmlStrEqual(node->name, BAD_CAST "img"))
1318 return(1);
1319 if (xmlStrEqual(node->name, BAD_CAST "input"))
1320 return(1);
1321 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1322 return(1);
1323 return(0);
1324 case 'l':
1325 if (xmlStrEqual(node->name, BAD_CAST "link"))
1326 return(1);
1327 return(0);
1328 case 'm':
1329 if (xmlStrEqual(node->name, BAD_CAST "meta"))
1330 return(1);
1331 return(0);
1332 case 'p':
1333 if (xmlStrEqual(node->name, BAD_CAST "param"))
1334 return(1);
1335 return(0);
1336 }
1337 return(0);
1338}
1339
1340/**
1341 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001342 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001343 *
1344 * Dump a list of XML attributes
1345 */
1346static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001347xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001348 xmlAttrPtr xml_lang = NULL;
1349 xmlAttrPtr lang = NULL;
1350 xmlAttrPtr name = NULL;
1351 xmlAttrPtr id = NULL;
1352 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001353 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001354
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001355 if (cur == NULL) return;
1356 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001357 parent = cur->parent;
1358 while (cur != NULL) {
1359 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1360 id = cur;
1361 else
1362 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1363 name = cur;
1364 else
1365 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1366 lang = cur;
1367 else
1368 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1369 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1370 xml_lang = cur;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001371 else if ((cur->ns == NULL) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001372 ((cur->children == NULL) ||
1373 (cur->children->content == NULL) ||
1374 (cur->children->content[0] == 0)) &&
1375 (htmlIsBooleanAttr(cur->name))) {
1376 if (cur->children != NULL)
1377 xmlFreeNode(cur->children);
1378 cur->children = xmlNewText(cur->name);
1379 if (cur->children != NULL)
1380 cur->children->parent = (xmlNodePtr) cur;
1381 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001382 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001383 cur = cur->next;
1384 }
1385 /*
1386 * C.8
1387 */
1388 if ((name != NULL) && (id == NULL)) {
1389 if ((parent != NULL) && (parent->name != NULL) &&
1390 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1391 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1392 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1393 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1394 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1395 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1396 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1397 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1398 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001399 xmlOutputBufferWrite(buf, 5, " id=\"");
1400 xmlAttrSerializeContent(buf, name);
1401 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001402 }
1403 }
1404 /*
1405 * C.7.
1406 */
1407 if ((lang != NULL) && (xml_lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001408 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1409 xmlAttrSerializeContent(buf, lang);
1410 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001411 } else
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001412 if ((xml_lang != NULL) && (lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001413 xmlOutputBufferWrite(buf, 7, " lang=\"");
1414 xmlAttrSerializeContent(buf, xml_lang);
1415 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001416 }
1417}
1418
1419/**
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001420 * xhtmlNodeDumpOutput:
1421 * @buf: the XML buffer output
1422 * @doc: the XHTML document
1423 * @cur: the current node
1424 * @level: the imbrication level for indenting
1425 * @format: is formatting allowed
1426 * @encoding: an optional encoding string
1427 *
1428 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001429 */
1430static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001431xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001432 int format = ctxt->format, addmeta;
1433 xmlNodePtr tmp, root, unformattedNode = NULL;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001434 xmlChar *start, *end;
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001435 xmlOutputBufferPtr buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001436
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001437 if (cur == NULL) return;
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001438
1439 root = cur;
1440 while (1) {
1441 switch (cur->type) {
1442 case XML_DOCUMENT_NODE:
1443 case XML_HTML_DOCUMENT_NODE:
1444 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1445 break;
1446
1447 case XML_NAMESPACE_DECL:
1448 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1449 break;
1450
1451 case XML_DTD_NODE:
1452 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1453 break;
1454
1455 case XML_DOCUMENT_FRAG_NODE:
1456 if (cur->children) {
1457 cur = cur->children;
1458 continue;
1459 }
1460 break;
1461
1462 case XML_ELEMENT_DECL:
1463 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1464 break;
1465
1466 case XML_ATTRIBUTE_DECL:
1467 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1468 break;
1469
1470 case XML_ENTITY_DECL:
1471 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1472 break;
1473
1474 case XML_ELEMENT_NODE:
1475 addmeta = 0;
1476
1477 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
1478 xmlOutputBufferWrite(buf, ctxt->indent_size *
1479 (ctxt->level > ctxt->indent_nr ?
1480 ctxt->indent_nr : ctxt->level),
1481 ctxt->indent);
1482
1483 xmlOutputBufferWrite(buf, 1, "<");
1484 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1485 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1486 xmlOutputBufferWrite(buf, 1, ":");
1487 }
1488
1489 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1490 if (cur->nsDef)
1491 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1492 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1493 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1494 /*
1495 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1496 */
1497 xmlOutputBufferWriteString(buf,
1498 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1499 }
1500 if (cur->properties != NULL)
1501 xhtmlAttrListDumpOutput(ctxt, cur->properties);
1502
1503 if ((cur->parent != NULL) &&
1504 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1505 xmlStrEqual(cur->name, BAD_CAST"head") &&
1506 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1507
1508 tmp = cur->children;
1509 while (tmp != NULL) {
1510 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1511 xmlChar *httpequiv;
1512
1513 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
1514 if (httpequiv != NULL) {
1515 if (xmlStrcasecmp(httpequiv,
1516 BAD_CAST"Content-Type") == 0) {
1517 xmlFree(httpequiv);
1518 break;
1519 }
1520 xmlFree(httpequiv);
1521 }
1522 }
1523 tmp = tmp->next;
1524 }
1525 if (tmp == NULL)
1526 addmeta = 1;
1527 }
1528
1529 if (cur->children == NULL) {
1530 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1531 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1532 /*
1533 * C.2. Empty Elements
1534 */
1535 xmlOutputBufferWrite(buf, 3, " />");
1536 } else {
1537 if (addmeta == 1) {
1538 xmlOutputBufferWrite(buf, 1, ">");
1539 if (ctxt->format == 1) {
1540 xmlOutputBufferWrite(buf, 1, "\n");
1541 if (xmlIndentTreeOutput)
1542 xmlOutputBufferWrite(buf, ctxt->indent_size *
1543 (ctxt->level + 1 > ctxt->indent_nr ?
1544 ctxt->indent_nr : ctxt->level + 1),
1545 ctxt->indent);
1546 }
1547 xmlOutputBufferWriteString(buf,
1548 "<meta http-equiv=\"Content-Type\" "
1549 "content=\"text/html; charset=");
1550 if (ctxt->encoding) {
1551 xmlOutputBufferWriteString(buf,
1552 (const char *)ctxt->encoding);
1553 } else {
1554 xmlOutputBufferWrite(buf, 5, "UTF-8");
1555 }
1556 xmlOutputBufferWrite(buf, 4, "\" />");
1557 if (ctxt->format == 1)
1558 xmlOutputBufferWrite(buf, 1, "\n");
1559 } else {
1560 xmlOutputBufferWrite(buf, 1, ">");
1561 }
1562 /*
1563 * C.3. Element Minimization and Empty Element Content
1564 */
1565 xmlOutputBufferWrite(buf, 2, "</");
1566 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1567 xmlOutputBufferWriteString(buf,
1568 (const char *)cur->ns->prefix);
1569 xmlOutputBufferWrite(buf, 1, ":");
1570 }
1571 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1572 xmlOutputBufferWrite(buf, 1, ">");
1573 }
1574 } else {
1575 xmlOutputBufferWrite(buf, 1, ">");
1576 if (addmeta == 1) {
1577 if (ctxt->format == 1) {
1578 xmlOutputBufferWrite(buf, 1, "\n");
1579 if (xmlIndentTreeOutput)
1580 xmlOutputBufferWrite(buf, ctxt->indent_size *
1581 (ctxt->level + 1 > ctxt->indent_nr ?
1582 ctxt->indent_nr : ctxt->level + 1),
1583 ctxt->indent);
1584 }
1585 xmlOutputBufferWriteString(buf,
1586 "<meta http-equiv=\"Content-Type\" "
1587 "content=\"text/html; charset=");
1588 if (ctxt->encoding) {
1589 xmlOutputBufferWriteString(buf,
1590 (const char *)ctxt->encoding);
1591 } else {
1592 xmlOutputBufferWrite(buf, 5, "UTF-8");
1593 }
1594 xmlOutputBufferWrite(buf, 4, "\" />");
1595 }
1596
1597 if (ctxt->format == 1) {
1598 tmp = cur->children;
1599 while (tmp != NULL) {
1600 if ((tmp->type == XML_TEXT_NODE) ||
1601 (tmp->type == XML_ENTITY_REF_NODE)) {
1602 unformattedNode = cur;
1603 ctxt->format = 0;
1604 break;
1605 }
1606 tmp = tmp->next;
1607 }
1608 }
1609
1610 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1611 if (ctxt->level >= 0) ctxt->level++;
1612 cur = cur->children;
1613 continue;
1614 }
1615
1616 break;
1617
1618 case XML_TEXT_NODE:
1619 if (cur->content == NULL)
1620 break;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001621 if ((cur->name == xmlStringText) ||
1622 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001623 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001624 } else {
1625 /*
1626 * Disable escaping, needed for XSLT
1627 */
1628 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1629 }
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001630 break;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001631
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001632 case XML_PI_NODE:
1633 if (cur->content != NULL) {
1634 xmlOutputBufferWrite(buf, 2, "<?");
1635 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1636 if (cur->content != NULL) {
1637 xmlOutputBufferWrite(buf, 1, " ");
1638 xmlOutputBufferWriteString(buf,
1639 (const char *)cur->content);
1640 }
1641 xmlOutputBufferWrite(buf, 2, "?>");
1642 } else {
1643 xmlOutputBufferWrite(buf, 2, "<?");
1644 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1645 xmlOutputBufferWrite(buf, 2, "?>");
1646 }
1647 break;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001648
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001649 case XML_COMMENT_NODE:
1650 if (cur->content != NULL) {
1651 xmlOutputBufferWrite(buf, 4, "<!--");
1652 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1653 xmlOutputBufferWrite(buf, 3, "-->");
1654 }
1655 break;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001656
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001657 case XML_ENTITY_REF_NODE:
1658 xmlOutputBufferWrite(buf, 1, "&");
1659 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1660 xmlOutputBufferWrite(buf, 1, ";");
1661 break;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001662
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001663 case XML_CDATA_SECTION_NODE:
1664 if (cur->content == NULL || *cur->content == '\0') {
1665 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1666 } else {
1667 start = end = cur->content;
1668 while (*end != '\0') {
1669 if (*end == ']' && *(end + 1) == ']' &&
1670 *(end + 2) == '>') {
1671 end = end + 2;
1672 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1673 xmlOutputBufferWrite(buf, end - start,
1674 (const char *)start);
1675 xmlOutputBufferWrite(buf, 3, "]]>");
1676 start = end;
Nick Wellnhofer359e7502017-11-13 21:13:46 +01001677 }
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001678 end++;
1679 }
1680 if (start != end) {
1681 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1682 xmlOutputBufferWriteString(buf, (const char *)start);
1683 xmlOutputBufferWrite(buf, 3, "]]>");
Nick Wellnhofer359e7502017-11-13 21:13:46 +01001684 }
1685 }
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001686 break;
1687
1688 case XML_ATTRIBUTE_NODE:
1689 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1690 break;
1691
1692 default:
1693 break;
Nick Wellnhofer359e7502017-11-13 21:13:46 +01001694 }
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001695
1696 while (1) {
1697 if (cur == root)
1698 return;
1699 if (ctxt->format == 1)
1700 xmlOutputBufferWrite(buf, 1, "\n");
1701 if (cur->next != NULL) {
1702 cur = cur->next;
1703 break;
1704 }
1705
Haibo Huangd23e46c2020-10-28 22:26:09 -07001706 /*
1707 * The parent should never be NULL here but we want to handle
1708 * corrupted documents gracefully.
1709 */
1710 if (cur->parent == NULL)
1711 return;
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001712 cur = cur->parent;
1713
1714 if (cur->type == XML_ELEMENT_NODE) {
1715 if (ctxt->level > 0) ctxt->level--;
1716 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1717 xmlOutputBufferWrite(buf, ctxt->indent_size *
1718 (ctxt->level > ctxt->indent_nr ?
1719 ctxt->indent_nr : ctxt->level),
1720 ctxt->indent);
1721
1722 xmlOutputBufferWrite(buf, 2, "</");
1723 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1724 xmlOutputBufferWriteString(buf,
1725 (const char *)cur->ns->prefix);
1726 xmlOutputBufferWrite(buf, 1, ":");
1727 }
1728
1729 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1730 xmlOutputBufferWrite(buf, 1, ">");
1731
1732 if (cur == unformattedNode) {
1733 ctxt->format = format;
1734 unformattedNode = NULL;
1735 }
1736 }
1737 }
Nick Wellnhofer359e7502017-11-13 21:13:46 +01001738 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001739}
1740#endif
1741
1742/************************************************************************
1743 * *
1744 * Public entry points *
1745 * *
1746 ************************************************************************/
1747
1748/**
1749 * xmlSaveToFd:
1750 * @fd: a file descriptor number
1751 * @encoding: the encoding name to use or NULL
1752 * @options: a set of xmlSaveOptions
1753 *
1754 * Create a document saving context serializing to a file descriptor
1755 * with the encoding and the options given.
1756 *
1757 * Returns a new serialization context or NULL in case of error.
1758 */
1759xmlSaveCtxtPtr
1760xmlSaveToFd(int fd, const char *encoding, int options)
1761{
1762 xmlSaveCtxtPtr ret;
1763
1764 ret = xmlNewSaveCtxt(encoding, options);
1765 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001766 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1767 if (ret->buf == NULL) {
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001768 xmlCharEncCloseFunc(ret->handler);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001769 xmlFreeSaveCtxt(ret);
1770 return(NULL);
1771 }
1772 return(ret);
1773}
1774
1775/**
1776 * xmlSaveToFilename:
1777 * @filename: a file name or an URL
1778 * @encoding: the encoding name to use or NULL
1779 * @options: a set of xmlSaveOptions
1780 *
1781 * Create a document saving context serializing to a filename or possibly
1782 * to an URL (but this is less reliable) with the encoding and the options
1783 * given.
1784 *
1785 * Returns a new serialization context or NULL in case of error.
1786 */
1787xmlSaveCtxtPtr
1788xmlSaveToFilename(const char *filename, const char *encoding, int options)
1789{
1790 xmlSaveCtxtPtr ret;
1791 int compression = 0; /* TODO handle compression option */
1792
1793 ret = xmlNewSaveCtxt(encoding, options);
1794 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001795 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001796 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001797 if (ret->buf == NULL) {
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001798 xmlCharEncCloseFunc(ret->handler);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001799 xmlFreeSaveCtxt(ret);
1800 return(NULL);
1801 }
1802 return(ret);
1803}
1804
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001805/**
1806 * xmlSaveToBuffer:
1807 * @buffer: a buffer
1808 * @encoding: the encoding name to use or NULL
1809 * @options: a set of xmlSaveOptions
1810 *
1811 * Create a document saving context serializing to a buffer
1812 * with the encoding and the options given
1813 *
1814 * Returns a new serialization context or NULL in case of error.
Daniel Veillard9a00fd22005-11-09 08:56:26 +00001815 */
1816
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001817xmlSaveCtxtPtr
1818xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1819{
Daniel Veillard9a00fd22005-11-09 08:56:26 +00001820 xmlSaveCtxtPtr ret;
Daniel Veillard9a00fd22005-11-09 08:56:26 +00001821
1822 ret = xmlNewSaveCtxt(encoding, options);
1823 if (ret == NULL) return(NULL);
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001824 ret->buf = xmlOutputBufferCreateBuffer(buffer, ret->handler);
1825 if (ret->buf == NULL) {
1826 xmlCharEncCloseFunc(ret->handler);
1827 xmlFreeSaveCtxt(ret);
1828 return(NULL);
Daniel Veillard9a00fd22005-11-09 08:56:26 +00001829 }
Daniel Veillard9a00fd22005-11-09 08:56:26 +00001830 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001831}
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001832
1833/**
1834 * xmlSaveToIO:
1835 * @iowrite: an I/O write function
1836 * @ioclose: an I/O close function
1837 * @ioctx: an I/O handler
1838 * @encoding: the encoding name to use or NULL
1839 * @options: a set of xmlSaveOptions
1840 *
1841 * Create a document saving context serializing to a file descriptor
1842 * with the encoding and the options given
1843 *
1844 * Returns a new serialization context or NULL in case of error.
1845 */
1846xmlSaveCtxtPtr
1847xmlSaveToIO(xmlOutputWriteCallback iowrite,
1848 xmlOutputCloseCallback ioclose,
1849 void *ioctx, const char *encoding, int options)
1850{
1851 xmlSaveCtxtPtr ret;
1852
1853 ret = xmlNewSaveCtxt(encoding, options);
1854 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001855 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1856 if (ret->buf == NULL) {
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001857 xmlCharEncCloseFunc(ret->handler);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001858 xmlFreeSaveCtxt(ret);
1859 return(NULL);
1860 }
1861 return(ret);
1862}
1863
1864/**
1865 * xmlSaveDoc:
1866 * @ctxt: a document saving context
1867 * @doc: a document
1868 *
1869 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001870 * TODO: The function is not fully implemented yet as it does not return the
1871 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001872 *
1873 * Returns the number of byte written or -1 in case of error
1874 */
1875long
1876xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1877{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001878 long ret = 0;
1879
Daniel Veillardce682bc2004-11-05 17:22:25 +00001880 if ((ctxt == NULL) || (doc == NULL)) return(-1);
Daniel Veillarddab39b52006-10-16 23:22:10 +00001881 if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1882 return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001883 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001884}
1885
1886/**
1887 * xmlSaveTree:
1888 * @ctxt: a document saving context
Daniel Veillard681e9042006-09-29 09:16:00 +00001889 * @node: the top node of the subtree to save
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001890 *
1891 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001892 * TODO: The function is not fully implemented yet as it does not return the
1893 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001894 *
1895 * Returns the number of byte written or -1 in case of error
1896 */
1897long
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001898xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr cur)
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001899{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001900 long ret = 0;
1901
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001902 if ((ctxt == NULL) || (cur == NULL)) return(-1);
1903#ifdef LIBXML_HTML_ENABLED
1904 if (ctxt->options & XML_SAVE_XHTML) {
1905 xhtmlNodeDumpOutput(ctxt, cur);
1906 return(ret);
1907 }
1908 if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
1909 (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
1910 ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
1911 (ctxt->options & XML_SAVE_AS_HTML)) {
1912 htmlNodeDumpOutputInternal(ctxt, cur);
1913 return(ret);
1914 }
1915#endif
1916 xmlNodeDumpOutputInternal(ctxt, cur);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001917 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001918}
1919
1920/**
1921 * xmlSaveFlush:
1922 * @ctxt: a document saving context
1923 *
1924 * Flush a document saving context, i.e. make sure that all bytes have
1925 * been output.
1926 *
1927 * Returns the number of byte written or -1 in case of error.
1928 */
1929int
1930xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1931{
1932 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001933 if (ctxt->buf == NULL) return(-1);
1934 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001935}
1936
1937/**
1938 * xmlSaveClose:
1939 * @ctxt: a document saving context
1940 *
1941 * Close a document saving context, i.e. make sure that all bytes have
1942 * been output and free the associated data.
1943 *
1944 * Returns the number of byte written or -1 in case of error.
1945 */
1946int
1947xmlSaveClose(xmlSaveCtxtPtr ctxt)
1948{
1949 int ret;
1950
1951 if (ctxt == NULL) return(-1);
1952 ret = xmlSaveFlush(ctxt);
1953 xmlFreeSaveCtxt(ctxt);
1954 return(ret);
1955}
1956
Daniel Veillard3995bc32004-05-15 18:57:31 +00001957/**
1958 * xmlSaveSetEscape:
1959 * @ctxt: a document saving context
1960 * @escape: the escaping function
1961 *
1962 * Set a custom escaping function to be used for text in element content
1963 *
1964 * Returns 0 if successful or -1 in case of error.
1965 */
1966int
1967xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1968{
1969 if (ctxt == NULL) return(-1);
1970 ctxt->escape = escape;
1971 return(0);
1972}
1973
1974/**
1975 * xmlSaveSetAttrEscape:
1976 * @ctxt: a document saving context
1977 * @escape: the escaping function
1978 *
1979 * Set a custom escaping function to be used for text in attribute content
1980 *
1981 * Returns 0 if successful or -1 in case of error.
1982 */
1983int
1984xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1985{
1986 if (ctxt == NULL) return(-1);
1987 ctxt->escapeAttr = escape;
1988 return(0);
1989}
1990
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001991/************************************************************************
1992 * *
1993 * Public entry points based on buffers *
1994 * *
1995 ************************************************************************/
Daniel Veillard50cdab52012-07-16 14:52:00 +08001996
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001997/**
Daniel Veillard50cdab52012-07-16 14:52:00 +08001998 * xmlBufAttrSerializeTxtContent:
1999 * @buf: and xmlBufPtr output
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002000 * @doc: the document
2001 * @attr: the attribute node
2002 * @string: the text content
2003 *
Daniel Veillard50cdab52012-07-16 14:52:00 +08002004 * Serialize text attribute values to an xmlBufPtr
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002005 */
2006void
Daniel Veillard50cdab52012-07-16 14:52:00 +08002007xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
2008 xmlAttrPtr attr, const xmlChar * string)
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002009{
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002010 xmlChar *base, *cur;
2011
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002012 if (string == NULL)
2013 return;
2014 base = cur = (xmlChar *) string;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002015 while (*cur != 0) {
2016 if (*cur == '\n') {
2017 if (base != cur)
Daniel Veillard50cdab52012-07-16 14:52:00 +08002018 xmlBufAdd(buf, base, cur - base);
2019 xmlBufAdd(buf, BAD_CAST "&#10;", 5);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002020 cur++;
2021 base = cur;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002022 } else if (*cur == '\r') {
2023 if (base != cur)
Daniel Veillard50cdab52012-07-16 14:52:00 +08002024 xmlBufAdd(buf, base, cur - base);
2025 xmlBufAdd(buf, BAD_CAST "&#13;", 5);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002026 cur++;
2027 base = cur;
2028 } else if (*cur == '\t') {
2029 if (base != cur)
Daniel Veillard50cdab52012-07-16 14:52:00 +08002030 xmlBufAdd(buf, base, cur - base);
2031 xmlBufAdd(buf, BAD_CAST "&#9;", 4);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002032 cur++;
2033 base = cur;
2034 } else if (*cur == '"') {
2035 if (base != cur)
Daniel Veillard50cdab52012-07-16 14:52:00 +08002036 xmlBufAdd(buf, base, cur - base);
2037 xmlBufAdd(buf, BAD_CAST "&quot;", 6);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002038 cur++;
2039 base = cur;
2040 } else if (*cur == '<') {
2041 if (base != cur)
Daniel Veillard50cdab52012-07-16 14:52:00 +08002042 xmlBufAdd(buf, base, cur - base);
2043 xmlBufAdd(buf, BAD_CAST "&lt;", 4);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002044 cur++;
2045 base = cur;
2046 } else if (*cur == '>') {
2047 if (base != cur)
Daniel Veillard50cdab52012-07-16 14:52:00 +08002048 xmlBufAdd(buf, base, cur - base);
2049 xmlBufAdd(buf, BAD_CAST "&gt;", 4);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002050 cur++;
2051 base = cur;
2052 } else if (*cur == '&') {
2053 if (base != cur)
Daniel Veillard50cdab52012-07-16 14:52:00 +08002054 xmlBufAdd(buf, base, cur - base);
2055 xmlBufAdd(buf, BAD_CAST "&amp;", 5);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002056 cur++;
2057 base = cur;
Daniel Veillardc97750d2016-05-23 13:39:13 +08002058 } else if ((*cur >= 0x80) && (cur[1] != 0) &&
2059 ((doc == NULL) || (doc->encoding == NULL))) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002060 /*
2061 * We assume we have UTF-8 content.
2062 */
Daniel Veillard07953482012-01-22 17:42:35 +08002063 unsigned char tmp[12];
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002064 int val = 0, l = 1;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002065
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002066 if (base != cur)
Daniel Veillard50cdab52012-07-16 14:52:00 +08002067 xmlBufAdd(buf, base, cur - base);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002068 if (*cur < 0xC0) {
2069 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002070 xmlSerializeHexCharRef(tmp, *cur);
Daniel Veillard50cdab52012-07-16 14:52:00 +08002071 xmlBufAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002072 cur++;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002073 base = cur;
2074 continue;
2075 } else if (*cur < 0xE0) {
2076 val = (cur[0]) & 0x1F;
2077 val <<= 6;
2078 val |= (cur[1]) & 0x3F;
2079 l = 2;
Daniel Veillardc97750d2016-05-23 13:39:13 +08002080 } else if ((*cur < 0xF0) && (cur [2] != 0)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002081 val = (cur[0]) & 0x0F;
2082 val <<= 6;
2083 val |= (cur[1]) & 0x3F;
2084 val <<= 6;
2085 val |= (cur[2]) & 0x3F;
2086 l = 3;
Daniel Veillardc97750d2016-05-23 13:39:13 +08002087 } else if ((*cur < 0xF8) && (cur [2] != 0) && (cur[3] != 0)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002088 val = (cur[0]) & 0x07;
2089 val <<= 6;
2090 val |= (cur[1]) & 0x3F;
2091 val <<= 6;
2092 val |= (cur[2]) & 0x3F;
2093 val <<= 6;
2094 val |= (cur[3]) & 0x3F;
2095 l = 4;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002096 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002097 if ((l == 1) || (!IS_CHAR(val))) {
2098 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002099 xmlSerializeHexCharRef(tmp, *cur);
Daniel Veillard50cdab52012-07-16 14:52:00 +08002100 xmlBufAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002101 cur++;
2102 base = cur;
2103 continue;
2104 }
2105 /*
2106 * We could do multiple things here. Just save
2107 * as a char ref
2108 */
2109 xmlSerializeHexCharRef(tmp, val);
Daniel Veillard50cdab52012-07-16 14:52:00 +08002110 xmlBufAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002111 cur += l;
2112 base = cur;
2113 } else {
2114 cur++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002115 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002116 }
2117 if (base != cur)
Daniel Veillard50cdab52012-07-16 14:52:00 +08002118 xmlBufAdd(buf, base, cur - base);
2119}
2120
2121/**
2122 * xmlAttrSerializeTxtContent:
2123 * @buf: the XML buffer output
2124 * @doc: the document
2125 * @attr: the attribute node
2126 * @string: the text content
2127 *
2128 * Serialize text attribute values to an xml simple buffer
2129 */
2130void
2131xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
2132 xmlAttrPtr attr, const xmlChar * string)
2133{
2134 xmlBufPtr buffer;
2135
2136 if ((buf == NULL) || (string == NULL))
2137 return;
2138 buffer = xmlBufFromBuffer(buf);
2139 if (buffer == NULL)
2140 return;
2141 xmlBufAttrSerializeTxtContent(buffer, doc, attr, string);
2142 xmlBufBackToBuffer(buffer);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002143}
2144
2145/**
2146 * xmlNodeDump:
2147 * @buf: the XML buffer output
2148 * @doc: the document
2149 * @cur: the current node
2150 * @level: the imbrication level for indenting
2151 * @format: is formatting allowed
2152 *
2153 * Dump an XML node, recursive behaviour,children are printed too.
2154 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
Haibo Huangcfd91dc2020-07-30 23:01:33 -07002155 * or xmlKeepBlanksDefault(0) was called.
Daniel Veillard50cdab52012-07-16 14:52:00 +08002156 * Since this is using xmlBuffer structures it is limited to 2GB and somehow
Haibo Huangcfd91dc2020-07-30 23:01:33 -07002157 * deprecated, use xmlNodeDumpOutput() instead.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002158 *
2159 * Returns the number of bytes written to the buffer or -1 in case of error
2160 */
2161int
2162xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2163 int format)
2164{
Daniel Veillard50cdab52012-07-16 14:52:00 +08002165 xmlBufPtr buffer;
Haibo Huangcfd91dc2020-07-30 23:01:33 -07002166 size_t ret;
Daniel Veillard50cdab52012-07-16 14:52:00 +08002167
2168 if ((buf == NULL) || (cur == NULL))
2169 return(-1);
2170 buffer = xmlBufFromBuffer(buf);
2171 if (buffer == NULL)
2172 return(-1);
2173 ret = xmlBufNodeDump(buffer, doc, cur, level, format);
2174 xmlBufBackToBuffer(buffer);
2175 if (ret > INT_MAX)
2176 return(-1);
2177 return((int) ret);
2178}
2179
2180/**
2181 * xmlBufNodeDump:
2182 * @buf: the XML buffer output
2183 * @doc: the document
2184 * @cur: the current node
2185 * @level: the imbrication level for indenting
2186 * @format: is formatting allowed
2187 *
2188 * Dump an XML node, recursive behaviour,children are printed too.
2189 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2190 * or xmlKeepBlanksDefault(0) was called
2191 *
2192 * Returns the number of bytes written to the buffer, in case of error 0
2193 * is returned or @buf stores the error
2194 */
2195
2196size_t
2197xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2198 int format)
2199{
2200 size_t use;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002201 int ret;
2202 xmlOutputBufferPtr outbuf;
Daniel Veillard23922c52013-02-11 11:52:44 +08002203 int oldalloc;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002204
2205 xmlInitParser();
2206
2207 if (cur == NULL) {
2208#ifdef DEBUG_TREE
2209 xmlGenericError(xmlGenericErrorContext,
2210 "xmlNodeDump : node == NULL\n");
2211#endif
2212 return (-1);
2213 }
2214 if (buf == NULL) {
2215#ifdef DEBUG_TREE
2216 xmlGenericError(xmlGenericErrorContext,
2217 "xmlNodeDump : buf == NULL\n");
2218#endif
2219 return (-1);
2220 }
2221 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2222 if (outbuf == NULL) {
2223 xmlSaveErrMemory("creating buffer");
2224 return (-1);
2225 }
2226 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2227 outbuf->buffer = buf;
2228 outbuf->encoder = NULL;
2229 outbuf->writecallback = NULL;
2230 outbuf->closecallback = NULL;
2231 outbuf->context = NULL;
2232 outbuf->written = 0;
2233
Daniel Veillard50cdab52012-07-16 14:52:00 +08002234 use = xmlBufUse(buf);
Daniel Veillard23922c52013-02-11 11:52:44 +08002235 oldalloc = xmlBufGetAllocationScheme(buf);
2236 xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002237 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
Daniel Veillard23922c52013-02-11 11:52:44 +08002238 xmlBufSetAllocationScheme(buf, oldalloc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002239 xmlFree(outbuf);
Daniel Veillard50cdab52012-07-16 14:52:00 +08002240 ret = xmlBufUse(buf) - use;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002241 return (ret);
2242}
2243
2244/**
2245 * xmlElemDump:
2246 * @f: the FILE * for the output
2247 * @doc: the document
2248 * @cur: the current node
2249 *
2250 * Dump an XML/HTML node, recursive behaviour, children are printed too.
2251 */
2252void
2253xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2254{
2255 xmlOutputBufferPtr outbuf;
2256
2257 xmlInitParser();
2258
2259 if (cur == NULL) {
2260#ifdef DEBUG_TREE
2261 xmlGenericError(xmlGenericErrorContext,
2262 "xmlElemDump : cur == NULL\n");
2263#endif
2264 return;
2265 }
2266#ifdef DEBUG_TREE
2267 if (doc == NULL) {
2268 xmlGenericError(xmlGenericErrorContext,
2269 "xmlElemDump : doc == NULL\n");
2270 }
2271#endif
2272
2273 outbuf = xmlOutputBufferCreateFile(f, NULL);
2274 if (outbuf == NULL)
2275 return;
2276 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
2277#ifdef LIBXML_HTML_ENABLED
2278 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2279#else
2280 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
2281#endif /* LIBXML_HTML_ENABLED */
2282 } else
2283 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2284 xmlOutputBufferClose(outbuf);
2285}
2286
2287/************************************************************************
2288 * *
2289 * Saving functions front-ends *
2290 * *
2291 ************************************************************************/
2292
2293/**
2294 * xmlNodeDumpOutput:
2295 * @buf: the XML buffer output
2296 * @doc: the document
2297 * @cur: the current node
2298 * @level: the imbrication level for indenting
2299 * @format: is formatting allowed
2300 * @encoding: an optional encoding string
2301 *
2302 * Dump an XML node, recursive behaviour, children are printed too.
2303 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2304 * or xmlKeepBlanksDefault(0) was called
2305 */
2306void
2307xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2308 int level, int format, const char *encoding)
2309{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002310 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002311#ifdef LIBXML_HTML_ENABLED
2312 xmlDtdPtr dtd;
2313 int is_xhtml = 0;
2314#endif
2315
2316 xmlInitParser();
2317
Daniel Veillardce244ad2004-11-05 10:03:46 +00002318 if ((buf == NULL) || (cur == NULL)) return;
2319
Daniel Veillard64354ea2005-03-31 15:22:56 +00002320 if (encoding == NULL)
2321 encoding = "UTF-8";
2322
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002323 memset(&ctxt, 0, sizeof(ctxt));
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002324 ctxt.buf = buf;
2325 ctxt.level = level;
Adam Spragg8b877132010-11-01 14:24:56 +01002326 ctxt.format = format ? 1 : 0;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002327 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002328 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002329 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002330
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002331#ifdef LIBXML_HTML_ENABLED
2332 dtd = xmlGetIntSubset(doc);
2333 if (dtd != NULL) {
Daniel Veillard33b20b72005-09-12 21:43:20 +00002334 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2335 if (is_xhtml < 0)
2336 is_xhtml = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002337 }
2338
2339 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002340 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002341 else
2342#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002343 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002344}
2345
2346/**
2347 * xmlDocDumpFormatMemoryEnc:
2348 * @out_doc: Document to generate XML text from
2349 * @doc_txt_ptr: Memory pointer for allocated XML text
2350 * @doc_txt_len: Length of the generated XML text
2351 * @txt_encoding: Character encoding to use when generating XML text
2352 * @format: should formatting spaces been added
2353 *
2354 * Dump the current DOM tree into memory using the character encoding specified
2355 * by the caller. Note it is up to the caller of this function to free the
2356 * allocated memory with xmlFree().
2357 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2358 * or xmlKeepBlanksDefault(0) was called
2359 */
2360
2361void
2362xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2363 int * doc_txt_len, const char * txt_encoding,
2364 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002365 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002366 int dummy = 0;
2367 xmlOutputBufferPtr out_buff = NULL;
2368 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
2369
2370 if (doc_txt_len == NULL) {
2371 doc_txt_len = &dummy; /* Continue, caller just won't get length */
2372 }
2373
2374 if (doc_txt_ptr == NULL) {
2375 *doc_txt_len = 0;
2376 return;
2377 }
2378
2379 *doc_txt_ptr = NULL;
2380 *doc_txt_len = 0;
2381
2382 if (out_doc == NULL) {
2383 /* No document, no output */
2384 return;
2385 }
2386
2387 /*
2388 * Validate the encoding value, if provided.
2389 * This logic is copied from xmlSaveFileEnc.
2390 */
2391
2392 if (txt_encoding == NULL)
2393 txt_encoding = (const char *) out_doc->encoding;
2394 if (txt_encoding != NULL) {
2395 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2396 if ( conv_hdlr == NULL ) {
2397 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2398 txt_encoding);
2399 return;
2400 }
2401 }
2402
2403 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2404 xmlSaveErrMemory("creating buffer");
2405 return;
2406 }
2407
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002408 memset(&ctxt, 0, sizeof(ctxt));
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002409 ctxt.buf = out_buff;
2410 ctxt.level = 0;
Adam Spragg8b877132010-11-01 14:24:56 +01002411 ctxt.format = format ? 1 : 0;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002412 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002413 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002414 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002415 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002416 xmlOutputBufferFlush(out_buff);
2417 if (out_buff->conv != NULL) {
Daniel Veillard50cdab52012-07-16 14:52:00 +08002418 *doc_txt_len = xmlBufUse(out_buff->conv);
2419 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002420 } else {
Daniel Veillard50cdab52012-07-16 14:52:00 +08002421 *doc_txt_len = xmlBufUse(out_buff->buffer);
2422 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002423 }
2424 (void)xmlOutputBufferClose(out_buff);
2425
2426 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2427 *doc_txt_len = 0;
2428 xmlSaveErrMemory("creating output");
2429 }
2430
2431 return;
2432}
2433
2434/**
2435 * xmlDocDumpMemory:
2436 * @cur: the document
2437 * @mem: OUT: the memory pointer
2438 * @size: OUT: the memory length
2439 *
2440 * Dump an XML document in memory and return the #xmlChar * and it's size
2441 * in bytes. It's up to the caller to free the memory with xmlFree().
2442 * The resulting byte array is zero terminated, though the last 0 is not
2443 * included in the returned size.
2444 */
2445void
2446xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2447 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2448}
2449
2450/**
2451 * xmlDocDumpFormatMemory:
2452 * @cur: the document
2453 * @mem: OUT: the memory pointer
2454 * @size: OUT: the memory length
2455 * @format: should formatting spaces been added
2456 *
2457 *
2458 * Dump an XML document in memory and return the #xmlChar * and it's size.
2459 * It's up to the caller to free the memory with xmlFree().
2460 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2461 * or xmlKeepBlanksDefault(0) was called
2462 */
2463void
2464xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2465 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2466}
2467
2468/**
2469 * xmlDocDumpMemoryEnc:
2470 * @out_doc: Document to generate XML text from
2471 * @doc_txt_ptr: Memory pointer for allocated XML text
2472 * @doc_txt_len: Length of the generated XML text
2473 * @txt_encoding: Character encoding to use when generating XML text
2474 *
2475 * Dump the current DOM tree into memory using the character encoding specified
2476 * by the caller. Note it is up to the caller of this function to free the
2477 * allocated memory with xmlFree().
2478 */
2479
2480void
2481xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2482 int * doc_txt_len, const char * txt_encoding) {
2483 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2484 txt_encoding, 0);
2485}
2486
2487/**
2488 * xmlDocFormatDump:
2489 * @f: the FILE*
2490 * @cur: the document
2491 * @format: should formatting spaces been added
2492 *
2493 * Dump an XML document to an open FILE.
2494 *
2495 * returns: the number of bytes written or -1 in case of failure.
2496 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2497 * or xmlKeepBlanksDefault(0) was called
2498 */
2499int
2500xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002501 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002502 xmlOutputBufferPtr buf;
2503 const char * encoding;
2504 xmlCharEncodingHandlerPtr handler = NULL;
2505 int ret;
2506
2507 if (cur == NULL) {
2508#ifdef DEBUG_TREE
2509 xmlGenericError(xmlGenericErrorContext,
2510 "xmlDocDump : document == NULL\n");
2511#endif
2512 return(-1);
2513 }
2514 encoding = (const char *) cur->encoding;
2515
2516 if (encoding != NULL) {
Daniel Veillard3814a362007-07-26 11:41:46 +00002517 handler = xmlFindCharEncodingHandler(encoding);
2518 if (handler == NULL) {
2519 xmlFree((char *) cur->encoding);
2520 cur->encoding = NULL;
2521 encoding = NULL;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002522 }
Daniel Veillard3814a362007-07-26 11:41:46 +00002523 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002524 buf = xmlOutputBufferCreateFile(f, handler);
2525 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002526 memset(&ctxt, 0, sizeof(ctxt));
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002527 ctxt.buf = buf;
2528 ctxt.level = 0;
Adam Spragg8b877132010-11-01 14:24:56 +01002529 ctxt.format = format ? 1 : 0;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002530 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002531 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002532 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002533 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002534
2535 ret = xmlOutputBufferClose(buf);
2536 return(ret);
2537}
2538
2539/**
2540 * xmlDocDump:
2541 * @f: the FILE*
2542 * @cur: the document
2543 *
2544 * Dump an XML document to an open FILE.
2545 *
2546 * returns: the number of bytes written or -1 in case of failure.
2547 */
2548int
2549xmlDocDump(FILE *f, xmlDocPtr cur) {
2550 return(xmlDocFormatDump (f, cur, 0));
2551}
2552
2553/**
2554 * xmlSaveFileTo:
2555 * @buf: an output I/O buffer
2556 * @cur: the document
Haibo Huangcfd91dc2020-07-30 23:01:33 -07002557 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002558 *
2559 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002560 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2561 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002562 *
2563 * returns: the number of bytes written or -1 in case of failure.
2564 */
2565int
2566xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002567 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002568 int ret;
2569
Daniel Veillard3d97e662004-11-04 10:49:00 +00002570 if (buf == NULL) return(-1);
2571 if (cur == NULL) {
2572 xmlOutputBufferClose(buf);
2573 return(-1);
2574 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002575 memset(&ctxt, 0, sizeof(ctxt));
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002576 ctxt.buf = buf;
2577 ctxt.level = 0;
2578 ctxt.format = 0;
2579 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002580 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002581 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002582 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002583 ret = xmlOutputBufferClose(buf);
2584 return(ret);
2585}
2586
2587/**
2588 * xmlSaveFormatFileTo:
2589 * @buf: an output I/O buffer
2590 * @cur: the document
Haibo Huangcfd91dc2020-07-30 23:01:33 -07002591 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002592 * @format: should formatting spaces been added
2593 *
2594 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002595 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2596 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002597 *
2598 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002599 */
2600int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002601xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2602 const char *encoding, int format)
2603{
2604 xmlSaveCtxt ctxt;
2605 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002606
Daniel Veillard3d97e662004-11-04 10:49:00 +00002607 if (buf == NULL) return(-1);
Daniel Veillardce244ad2004-11-05 10:03:46 +00002608 if ((cur == NULL) ||
2609 ((cur->type != XML_DOCUMENT_NODE) &&
2610 (cur->type != XML_HTML_DOCUMENT_NODE))) {
Daniel Veillard3d97e662004-11-04 10:49:00 +00002611 xmlOutputBufferClose(buf);
2612 return(-1);
2613 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002614 memset(&ctxt, 0, sizeof(ctxt));
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002615 ctxt.buf = buf;
2616 ctxt.level = 0;
Adam Spragg8b877132010-11-01 14:24:56 +01002617 ctxt.format = format ? 1 : 0;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002618 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002619 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002620 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002621 xmlDocContentDumpOutput(&ctxt, cur);
2622 ret = xmlOutputBufferClose(buf);
2623 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002624}
2625
2626/**
2627 * xmlSaveFormatFileEnc:
2628 * @filename: the filename or URL to output
2629 * @cur: the document being saved
2630 * @encoding: the name of the encoding to use or NULL.
2631 * @format: should formatting spaces be added.
2632 *
2633 * Dump an XML document to a file or an URL.
2634 *
2635 * Returns the number of bytes written or -1 in case of error.
2636 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2637 * or xmlKeepBlanksDefault(0) was called
2638 */
2639int
2640xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2641 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002642 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002643 xmlOutputBufferPtr buf;
2644 xmlCharEncodingHandlerPtr handler = NULL;
2645 int ret;
2646
2647 if (cur == NULL)
2648 return(-1);
2649
2650 if (encoding == NULL)
2651 encoding = (const char *) cur->encoding;
2652
2653 if (encoding != NULL) {
2654
2655 handler = xmlFindCharEncodingHandler(encoding);
2656 if (handler == NULL)
2657 return(-1);
2658 }
2659
Nick Wellnhofercb5541c2017-11-13 17:08:38 +01002660#ifdef LIBXML_ZLIB_ENABLED
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002661 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2662#endif
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002663 /*
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002664 * save the content to a temp buffer.
2665 */
2666 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2667 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002668 memset(&ctxt, 0, sizeof(ctxt));
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002669 ctxt.buf = buf;
2670 ctxt.level = 0;
Adam Spragg8b877132010-11-01 14:24:56 +01002671 ctxt.format = format ? 1 : 0;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002672 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002673 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002674 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002675
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002676 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002677
2678 ret = xmlOutputBufferClose(buf);
2679 return(ret);
2680}
2681
2682
2683/**
2684 * xmlSaveFileEnc:
2685 * @filename: the filename (or URL)
2686 * @cur: the document
2687 * @encoding: the name of an encoding (or NULL)
2688 *
2689 * Dump an XML document, converting it to the given encoding
2690 *
2691 * returns: the number of bytes written or -1 in case of failure.
2692 */
2693int
2694xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2695 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2696}
2697
2698/**
2699 * xmlSaveFormatFile:
2700 * @filename: the filename (or URL)
2701 * @cur: the document
2702 * @format: should formatting spaces been added
2703 *
2704 * Dump an XML document to a file. Will use compression if
2705 * compiled in and enabled. If @filename is "-" the stdout file is
2706 * used. If @format is set then the document will be indented on output.
2707 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2708 * or xmlKeepBlanksDefault(0) was called
2709 *
2710 * returns: the number of bytes written or -1 in case of failure.
2711 */
2712int
2713xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2714 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2715}
2716
2717/**
2718 * xmlSaveFile:
2719 * @filename: the filename (or URL)
2720 * @cur: the document
2721 *
2722 * Dump an XML document to a file. Will use compression if
2723 * compiled in and enabled. If @filename is "-" the stdout file is
2724 * used.
2725 * returns: the number of bytes written or -1 in case of failure.
2726 */
2727int
2728xmlSaveFile(const char *filename, xmlDocPtr cur) {
2729 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2730}
2731
2732#endif /* LIBXML_OUTPUT_ENABLED */
2733