1 /* libxml2 - Library for parsing XML documents
2 * Copyright (C) 2006-2019 Free Software Foundation, Inc.
3 *
4 * This file is not part of the GNU gettext program, but is used with
5 * GNU gettext.
6 *
7 * The original copyright notice is as follows:
8 */
9
10 /*
11 * Copyright (C) 1998-2012 Daniel Veillard. All Rights Reserved.
12 *
13 * Permission is hereby granted, free of charge, to any person obtaining a copy
14 * of this software and associated documentation files (the "Software"), to deal
15 * in the Software without restriction, including without limitation the rights
16 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 * copies of the Software, and to permit persons to whom the Software is fur-
18 * nished to do so, subject to the following conditions:
19 *
20 * The above copyright notice and this permission notice shall be included in
21 * all copies or substantial portions of the Software.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
25 * NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 * THE SOFTWARE.
30 *
31 * daniel@veillard.com
32 */
33
34 /*
35 * xmlsave.c: Implemetation of the document serializer
36 */
37
38 #define IN_LIBXML
39 #include "libxml.h"
40
41 #include <string.h>
42 #include <libxml/xmlmemory.h>
43 #include <libxml/parserInternals.h>
44 #include <libxml/tree.h>
45 #include <libxml/xmlsave.h>
46
47 #define MAX_INDENT 60
48
49 #include <libxml/HTMLtree.h>
50
51 #include "buf.h"
52 #include "enc.h"
53 #include "save.h"
54
55 /************************************************************************
56 * *
57 * XHTML detection *
58 * *
59 ************************************************************************/
60 #define XHTML_STRICT_PUBLIC_ID BAD_CAST \
61 "-//W3C//DTD XHTML 1.0 Strict//EN"
62 #define XHTML_STRICT_SYSTEM_ID BAD_CAST \
63 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
64 #define XHTML_FRAME_PUBLIC_ID BAD_CAST \
65 "-//W3C//DTD XHTML 1.0 Frameset//EN"
66 #define XHTML_FRAME_SYSTEM_ID BAD_CAST \
67 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
68 #define XHTML_TRANS_PUBLIC_ID BAD_CAST \
69 "-//W3C//DTD XHTML 1.0 Transitional//EN"
70 #define XHTML_TRANS_SYSTEM_ID BAD_CAST \
71 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
72
73 #define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
74 /**
75 * xmlIsXHTML:
76 * @systemID: the system identifier
77 * @publicID: the public identifier
78 *
79 * Try to find if the document correspond to an XHTML DTD
80 *
81 * Returns 1 if true, 0 if not and -1 in case of error
82 */
83 int
xmlIsXHTML(const xmlChar * systemID,const xmlChar * publicID)84 xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
85 if ((systemID == NULL) && (publicID == NULL))
86 return(-1);
87 if (publicID != NULL) {
88 if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
89 if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
90 if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
91 }
92 if (systemID != NULL) {
93 if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
94 if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
95 if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
96 }
97 return(0);
98 }
99
100 #ifdef LIBXML_OUTPUT_ENABLED
101
102 #define TODO \
103 xmlGenericError(xmlGenericErrorContext, \
104 "Unimplemented block at %s:%d\n", \
105 __FILE__, __LINE__);
106
107 struct _xmlSaveCtxt {
108 void *_private;
109 int type;
110 int fd;
111 const xmlChar *filename;
112 const xmlChar *encoding;
113 xmlCharEncodingHandlerPtr handler;
114 xmlOutputBufferPtr buf;
115 xmlDocPtr doc;
116 int options;
117 int level;
118 int format;
119 char indent[MAX_INDENT + 1]; /* array for indenting output */
120 int indent_nr;
121 int indent_size;
122 xmlCharEncodingOutputFunc escape; /* used for element content */
123 xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */
124 };
125
126 /************************************************************************
127 * *
128 * Output error handlers *
129 * *
130 ************************************************************************/
131 /**
132 * xmlSaveErrMemory:
133 * @extra: extra informations
134 *
135 * Handle an out of memory condition
136 */
137 static void
xmlSaveErrMemory(const char * extra)138 xmlSaveErrMemory(const char *extra)
139 {
140 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
141 }
142
143 /**
144 * xmlSaveErr:
145 * @code: the error number
146 * @node: the location of the error.
147 * @extra: extra informations
148 *
149 * Handle an out of memory condition
150 */
151 static void
xmlSaveErr(int code,xmlNodePtr node,const char * extra)152 xmlSaveErr(int code, xmlNodePtr node, const char *extra)
153 {
154 const char *msg = NULL;
155
156 switch(code) {
157 case XML_SAVE_NOT_UTF8:
158 msg = "string is not in UTF-8\n";
159 break;
160 case XML_SAVE_CHAR_INVALID:
161 msg = "invalid character value\n";
162 break;
163 case XML_SAVE_UNKNOWN_ENCODING:
164 msg = "unknown encoding %s\n";
165 break;
166 case XML_SAVE_NO_DOCTYPE:
167 msg = "document has no DOCTYPE\n";
168 break;
169 default:
170 msg = "unexpected error number\n";
171 }
172 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
173 }
174
175 /************************************************************************
176 * *
177 * Special escaping routines *
178 * *
179 ************************************************************************/
180 static unsigned char *
xmlSerializeHexCharRef(unsigned char * out,int val)181 xmlSerializeHexCharRef(unsigned char *out, int val) {
182 unsigned char *ptr;
183
184 *out++ = '&';
185 *out++ = '#';
186 *out++ = 'x';
187 if (val < 0x10) ptr = out;
188 else if (val < 0x100) ptr = out + 1;
189 else if (val < 0x1000) ptr = out + 2;
190 else if (val < 0x10000) ptr = out + 3;
191 else if (val < 0x100000) ptr = out + 4;
192 else ptr = out + 5;
193 out = ptr + 1;
194 while (val > 0) {
195 switch (val & 0xF) {
196 case 0: *ptr-- = '0'; break;
197 case 1: *ptr-- = '1'; break;
198 case 2: *ptr-- = '2'; break;
199 case 3: *ptr-- = '3'; break;
200 case 4: *ptr-- = '4'; break;
201 case 5: *ptr-- = '5'; break;
202 case 6: *ptr-- = '6'; break;
203 case 7: *ptr-- = '7'; break;
204 case 8: *ptr-- = '8'; break;
205 case 9: *ptr-- = '9'; break;
206 case 0xA: *ptr-- = 'A'; break;
207 case 0xB: *ptr-- = 'B'; break;
208 case 0xC: *ptr-- = 'C'; break;
209 case 0xD: *ptr-- = 'D'; break;
210 case 0xE: *ptr-- = 'E'; break;
211 case 0xF: *ptr-- = 'F'; break;
212 default: *ptr-- = '0'; break;
213 }
214 val >>= 4;
215 }
216 *out++ = ';';
217 *out = 0;
218 return(out);
219 }
220
221 /**
222 * xmlEscapeEntities:
223 * @out: a pointer to an array of bytes to store the result
224 * @outlen: the length of @out
225 * @in: a pointer to an array of unescaped UTF-8 bytes
226 * @inlen: the length of @in
227 *
228 * Take a block of UTF-8 chars in and escape them. Used when there is no
229 * encoding specified.
230 *
231 * Returns 0 if success, or -1 otherwise
232 * The value of @inlen after return is the number of octets consumed
233 * if the return value is positive, else unpredictable.
234 * The value of @outlen after return is the number of octets consumed.
235 */
236 static int
xmlEscapeEntities(unsigned char * out,int * outlen,const xmlChar * in,int * inlen)237 xmlEscapeEntities(unsigned char* out, int *outlen,
238 const xmlChar* in, int *inlen) {
239 unsigned char* outstart = out;
240 const unsigned char* base = in;
241 unsigned char* outend = out + *outlen;
242 const unsigned char* inend;
243 int val;
244
245 inend = in + (*inlen);
246
247 while ((in < inend) && (out < outend)) {
248 if (*in == '<') {
249 if (outend - out < 4) break;
250 *out++ = '&';
251 *out++ = 'l';
252 *out++ = 't';
253 *out++ = ';';
254 in++;
255 continue;
256 } else if (*in == '>') {
257 if (outend - out < 4) break;
258 *out++ = '&';
259 *out++ = 'g';
260 *out++ = 't';
261 *out++ = ';';
262 in++;
263 continue;
264 } else if (*in == '&') {
265 if (outend - out < 5) break;
266 *out++ = '&';
267 *out++ = 'a';
268 *out++ = 'm';
269 *out++ = 'p';
270 *out++ = ';';
271 in++;
272 continue;
273 } else if (((*in >= 0x20) && (*in < 0x80)) ||
274 (*in == '\n') || (*in == '\t')) {
275 /*
276 * default case, just copy !
277 */
278 *out++ = *in++;
279 continue;
280 } else if (*in >= 0x80) {
281 /*
282 * We assume we have UTF-8 input.
283 */
284 if (outend - out < 11) break;
285
286 if (*in < 0xC0) {
287 xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
288 in++;
289 goto error;
290 } else if (*in < 0xE0) {
291 if (inend - in < 2) break;
292 val = (in[0]) & 0x1F;
293 val <<= 6;
294 val |= (in[1]) & 0x3F;
295 in += 2;
296 } else if (*in < 0xF0) {
297 if (inend - in < 3) break;
298 val = (in[0]) & 0x0F;
299 val <<= 6;
300 val |= (in[1]) & 0x3F;
301 val <<= 6;
302 val |= (in[2]) & 0x3F;
303 in += 3;
304 } else if (*in < 0xF8) {
305 if (inend - in < 4) break;
306 val = (in[0]) & 0x07;
307 val <<= 6;
308 val |= (in[1]) & 0x3F;
309 val <<= 6;
310 val |= (in[2]) & 0x3F;
311 val <<= 6;
312 val |= (in[3]) & 0x3F;
313 in += 4;
314 } else {
315 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
316 in++;
317 goto error;
318 }
319 if (!IS_CHAR(val)) {
320 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
321 in++;
322 goto error;
323 }
324
325 /*
326 * We could do multiple things here. Just save as a char ref
327 */
328 out = xmlSerializeHexCharRef(out, val);
329 } else if (IS_BYTE_CHAR(*in)) {
330 if (outend - out < 6) break;
331 out = xmlSerializeHexCharRef(out, *in++);
332 } else {
333 xmlGenericError(xmlGenericErrorContext,
334 "xmlEscapeEntities : char out of range\n");
335 in++;
336 goto error;
337 }
338 }
339 *outlen = out - outstart;
340 *inlen = in - base;
341 return(0);
342 error:
343 *outlen = out - outstart;
344 *inlen = in - base;
345 return(-1);
346 }
347
348 /************************************************************************
349 * *
350 * Allocation and deallocation *
351 * *
352 ************************************************************************/
353 /**
354 * xmlSaveCtxtInit:
355 * @ctxt: the saving context
356 *
357 * Initialize a saving context
358 */
359 static void
xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)360 xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
361 {
362 int i;
363 int len;
364
365 if (ctxt == NULL) return;
366 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
367 ctxt->escape = xmlEscapeEntities;
368 len = xmlStrlen((xmlChar *)xmlTreeIndentString);
369 if ((xmlTreeIndentString == NULL) || (len == 0)) {
370 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
371 } else {
372 ctxt->indent_size = len;
373 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
374 for (i = 0;i < ctxt->indent_nr;i++)
375 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
376 ctxt->indent_size);
377 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
378 }
379
380 if (xmlSaveNoEmptyTags) {
381 ctxt->options |= XML_SAVE_NO_EMPTY;
382 }
383 }
384
385 /**
386 * xmlFreeSaveCtxt:
387 *
388 * Free a saving context, destroying the ouptut in any remaining buffer
389 */
390 static void
xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)391 xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
392 {
393 if (ctxt == NULL) return;
394 if (ctxt->encoding != NULL)
395 xmlFree((char *) ctxt->encoding);
396 if (ctxt->buf != NULL)
397 xmlOutputBufferClose(ctxt->buf);
398 xmlFree(ctxt);
399 }
400
401 /**
402 * xmlNewSaveCtxt:
403 *
404 * Create a new saving context
405 *
406 * Returns the new structure or NULL in case of error
407 */
408 static xmlSaveCtxtPtr
xmlNewSaveCtxt(const char * encoding,int options)409 xmlNewSaveCtxt(const char *encoding, int options)
410 {
411 xmlSaveCtxtPtr ret;
412
413 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
414 if (ret == NULL) {
415 xmlSaveErrMemory("creating saving context");
416 return ( NULL );
417 }
418 memset(ret, 0, sizeof(xmlSaveCtxt));
419
420 if (encoding != NULL) {
421 ret->handler = xmlFindCharEncodingHandler(encoding);
422 if (ret->handler == NULL) {
423 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
424 xmlFreeSaveCtxt(ret);
425 return(NULL);
426 }
427 ret->encoding = xmlStrdup((const xmlChar *)encoding);
428 ret->escape = NULL;
429 }
430 xmlSaveCtxtInit(ret);
431
432 /*
433 * Use the options
434 */
435
436 /* Re-check this option as it may already have been set */
437 if ((ret->options & XML_SAVE_NO_EMPTY) && ! (options & XML_SAVE_NO_EMPTY)) {
438 options |= XML_SAVE_NO_EMPTY;
439 }
440
441 ret->options = options;
442 if (options & XML_SAVE_FORMAT)
443 ret->format = 1;
444 else if (options & XML_SAVE_WSNONSIG)
445 ret->format = 2;
446
447 return(ret);
448 }
449
450 /************************************************************************
451 * *
452 * Dumping XML tree content to a simple buffer *
453 * *
454 ************************************************************************/
455 /**
456 * xmlAttrSerializeContent:
457 * @buf: the XML buffer output
458 * @doc: the document
459 * @attr: the attribute pointer
460 *
461 * Serialize the attribute in the buffer
462 */
463 static void
xmlAttrSerializeContent(xmlOutputBufferPtr buf,xmlAttrPtr attr)464 xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
465 {
466 xmlNodePtr children;
467
468 children = attr->children;
469 while (children != NULL) {
470 switch (children->type) {
471 case XML_TEXT_NODE:
472 xmlBufAttrSerializeTxtContent(buf->buffer, attr->doc,
473 attr, children->content);
474 break;
475 case XML_ENTITY_REF_NODE:
476 xmlBufAdd(buf->buffer, BAD_CAST "&", 1);
477 xmlBufAdd(buf->buffer, children->name,
478 xmlStrlen(children->name));
479 xmlBufAdd(buf->buffer, BAD_CAST ";", 1);
480 break;
481 default:
482 /* should not happen unless we have a badly built tree */
483 break;
484 }
485 children = children->next;
486 }
487 }
488
489 /**
490 * xmlBufDumpNotationTable:
491 * @buf: an xmlBufPtr output
492 * @table: A notation table
493 *
494 * This will dump the content of the notation table as an XML DTD definition
495 */
496 void
xmlBufDumpNotationTable(xmlBufPtr buf,xmlNotationTablePtr table)497 xmlBufDumpNotationTable(xmlBufPtr buf, xmlNotationTablePtr table) {
498 xmlBufferPtr buffer;
499
500 buffer = xmlBufferCreate();
501 if (buffer == NULL) {
502 /*
503 * TODO set the error in buf
504 */
505 return;
506 }
507 xmlDumpNotationTable(buffer, table);
508 xmlBufMergeBuffer(buf, buffer);
509 }
510
511 /**
512 * xmlBufDumpElementDecl:
513 * @buf: an xmlBufPtr output
514 * @elem: An element table
515 *
516 * This will dump the content of the element declaration as an XML
517 * DTD definition
518 */
519 void
xmlBufDumpElementDecl(xmlBufPtr buf,xmlElementPtr elem)520 xmlBufDumpElementDecl(xmlBufPtr buf, xmlElementPtr elem) {
521 xmlBufferPtr buffer;
522
523 buffer = xmlBufferCreate();
524 if (buffer == NULL) {
525 /*
526 * TODO set the error in buf
527 */
528 return;
529 }
530 xmlDumpElementDecl(buffer, elem);
531 xmlBufMergeBuffer(buf, buffer);
532 }
533
534 /**
535 * xmlBufDumpAttributeDecl:
536 * @buf: an xmlBufPtr output
537 * @attr: An attribute declaration
538 *
539 * This will dump the content of the attribute declaration as an XML
540 * DTD definition
541 */
542 void
xmlBufDumpAttributeDecl(xmlBufPtr buf,xmlAttributePtr attr)543 xmlBufDumpAttributeDecl(xmlBufPtr buf, xmlAttributePtr attr) {
544 xmlBufferPtr buffer;
545
546 buffer = xmlBufferCreate();
547 if (buffer == NULL) {
548 /*
549 * TODO set the error in buf
550 */
551 return;
552 }
553 xmlDumpAttributeDecl(buffer, attr);
554 xmlBufMergeBuffer(buf, buffer);
555 }
556
557 /**
558 * xmlBufDumpEntityDecl:
559 * @buf: an xmlBufPtr output
560 * @ent: An entity table
561 *
562 * This will dump the content of the entity table as an XML DTD definition
563 */
564 void
xmlBufDumpEntityDecl(xmlBufPtr buf,xmlEntityPtr ent)565 xmlBufDumpEntityDecl(xmlBufPtr buf, xmlEntityPtr ent) {
566 xmlBufferPtr buffer;
567
568 buffer = xmlBufferCreate();
569 if (buffer == NULL) {
570 /*
571 * TODO set the error in buf
572 */
573 return;
574 }
575 xmlDumpEntityDecl(buffer, ent);
576 xmlBufMergeBuffer(buf, buffer);
577 }
578
579 /************************************************************************
580 * *
581 * Dumping XML tree content to an I/O output buffer *
582 * *
583 ************************************************************************/
584
xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt,const char * encoding)585 static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
586 xmlOutputBufferPtr buf = ctxt->buf;
587
588 if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
589 buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
590 if (buf->encoder == NULL) {
591 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
592 (const char *)encoding);
593 return(-1);
594 }
595 buf->conv = xmlBufCreate();
596 if (buf->conv == NULL) {
597 xmlCharEncCloseFunc(buf->encoder);
598 xmlSaveErrMemory("creating encoding buffer");
599 return(-1);
600 }
601 /*
602 * initialize the state, e.g. if outputting a BOM
603 */
604 xmlCharEncOutput(buf, 1);
605 }
606 return(0);
607 }
608
xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt)609 static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
610 xmlOutputBufferPtr buf = ctxt->buf;
611 xmlOutputBufferFlush(buf);
612 xmlCharEncCloseFunc(buf->encoder);
613 xmlBufFree(buf->conv);
614 buf->encoder = NULL;
615 buf->conv = NULL;
616 return(0);
617 }
618
619 #ifdef LIBXML_HTML_ENABLED
620 static void
621 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
622 #endif
623 static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
624 static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
625 void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
626 static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
627
628 /**
629 * xmlOutputBufferWriteWSNonSig:
630 * @ctxt: The save context
631 * @extra: Number of extra indents to apply to ctxt->level
632 *
633 * Write out formatting for non-significant whitespace output.
634 */
635 static void
xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt,int extra)636 xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra)
637 {
638 int i;
639 if ((ctxt == NULL) || (ctxt->buf == NULL))
640 return;
641 xmlOutputBufferWrite(ctxt->buf, 1, "\n");
642 for (i = 0; i < (ctxt->level + extra); i += ctxt->indent_nr) {
643 xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size *
644 ((ctxt->level + extra - i) > ctxt->indent_nr ?
645 ctxt->indent_nr : (ctxt->level + extra - i)),
646 ctxt->indent);
647 }
648 }
649
650 /**
651 * xmlNsDumpOutput:
652 * @buf: the XML buffer output
653 * @cur: a namespace
654 * @ctxt: the output save context. Optional.
655 *
656 * Dump a local Namespace definition.
657 * Should be called in the context of attributes dumps.
658 * If @ctxt is supplied, @buf should be its buffer.
659 */
660 static void
xmlNsDumpOutput(xmlOutputBufferPtr buf,xmlNsPtr cur,xmlSaveCtxtPtr ctxt)661 xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
662 if ((cur == NULL) || (buf == NULL)) return;
663 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
664 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
665 return;
666
667 if (ctxt != NULL && ctxt->format == 2)
668 xmlOutputBufferWriteWSNonSig(ctxt, 2);
669 else
670 xmlOutputBufferWrite(buf, 1, " ");
671
672 /* Within the context of an element attributes */
673 if (cur->prefix != NULL) {
674 xmlOutputBufferWrite(buf, 6, "xmlns:");
675 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
676 } else
677 xmlOutputBufferWrite(buf, 5, "xmlns");
678 xmlOutputBufferWrite(buf, 1, "=");
679 xmlBufWriteQuotedString(buf->buffer, cur->href);
680 }
681 }
682
683 /**
684 * xmlNsDumpOutputCtxt
685 * @ctxt: the save context
686 * @cur: a namespace
687 *
688 * Dump a local Namespace definition to a save context.
689 * Should be called in the context of attribute dumps.
690 */
691 static void
xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt,xmlNsPtr cur)692 xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
693 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
694 }
695
696 /**
697 * xmlNsListDumpOutputCtxt
698 * @ctxt: the save context
699 * @cur: the first namespace
700 *
701 * Dump a list of local namespace definitions to a save context.
702 * Should be called in the context of attribute dumps.
703 */
704 static void
xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt,xmlNsPtr cur)705 xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
706 while (cur != NULL) {
707 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
708 cur = cur->next;
709 }
710 }
711
712 /**
713 * xmlNsListDumpOutput:
714 * @buf: the XML buffer output
715 * @cur: the first namespace
716 *
717 * Dump a list of local Namespace definitions.
718 * Should be called in the context of attributes dumps.
719 */
720 void
xmlNsListDumpOutput(xmlOutputBufferPtr buf,xmlNsPtr cur)721 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
722 while (cur != NULL) {
723 xmlNsDumpOutput(buf, cur, NULL);
724 cur = cur->next;
725 }
726 }
727
728 /**
729 * xmlDtdDumpOutput:
730 * @buf: the XML buffer output
731 * @dtd: the pointer to the DTD
732 *
733 * Dump the XML document DTD, if any.
734 */
735 static void
xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt,xmlDtdPtr dtd)736 xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
737 xmlOutputBufferPtr buf;
738 int format, level;
739 xmlDocPtr doc;
740
741 if (dtd == NULL) return;
742 if ((ctxt == NULL) || (ctxt->buf == NULL))
743 return;
744 buf = ctxt->buf;
745 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
746 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
747 if (dtd->ExternalID != NULL) {
748 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
749 xmlBufWriteQuotedString(buf->buffer, dtd->ExternalID);
750 xmlOutputBufferWrite(buf, 1, " ");
751 xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
752 } else if (dtd->SystemID != NULL) {
753 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
754 xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
755 }
756 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
757 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
758 (dtd->pentities == NULL)) {
759 xmlOutputBufferWrite(buf, 1, ">");
760 return;
761 }
762 xmlOutputBufferWrite(buf, 3, " [\n");
763 /*
764 * Dump the notations first they are not in the DTD children list
765 * Do this only on a standalone DTD or on the internal subset though.
766 */
767 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
768 (dtd->doc->intSubset == dtd))) {
769 xmlBufDumpNotationTable(buf->buffer,
770 (xmlNotationTablePtr) dtd->notations);
771 }
772 format = ctxt->format;
773 level = ctxt->level;
774 doc = ctxt->doc;
775 ctxt->format = 0;
776 ctxt->level = -1;
777 ctxt->doc = dtd->doc;
778 xmlNodeListDumpOutput(ctxt, dtd->children);
779 ctxt->format = format;
780 ctxt->level = level;
781 ctxt->doc = doc;
782 xmlOutputBufferWrite(buf, 2, "]>");
783 }
784
785 /**
786 * xmlAttrDumpOutput:
787 * @buf: the XML buffer output
788 * @cur: the attribute pointer
789 *
790 * Dump an XML attribute
791 */
792 static void
xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt,xmlAttrPtr cur)793 xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
794 xmlOutputBufferPtr buf;
795
796 if (cur == NULL) return;
797 buf = ctxt->buf;
798 if (buf == NULL) return;
799 if (ctxt->format == 2)
800 xmlOutputBufferWriteWSNonSig(ctxt, 2);
801 else
802 xmlOutputBufferWrite(buf, 1, " ");
803 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
804 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
805 xmlOutputBufferWrite(buf, 1, ":");
806 }
807 xmlOutputBufferWriteString(buf, (const char *)cur->name);
808 xmlOutputBufferWrite(buf, 2, "=\"");
809 xmlAttrSerializeContent(buf, cur);
810 xmlOutputBufferWrite(buf, 1, "\"");
811 }
812
813 /**
814 * xmlAttrListDumpOutput:
815 * @buf: the XML buffer output
816 * @doc: the document
817 * @cur: the first attribute pointer
818 * @encoding: an optional encoding string
819 *
820 * Dump a list of XML attributes
821 */
822 static void
xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt,xmlAttrPtr cur)823 xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
824 if (cur == NULL) return;
825 while (cur != NULL) {
826 xmlAttrDumpOutput(ctxt, cur);
827 cur = cur->next;
828 }
829 }
830
831
832
833 /**
834 * xmlNodeListDumpOutput:
835 * @cur: the first node
836 *
837 * Dump an XML node list, recursive behaviour, children are printed too.
838 */
839 static void
xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)840 xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
841 xmlOutputBufferPtr buf;
842
843 if (cur == NULL) return;
844 buf = ctxt->buf;
845 while (cur != NULL) {
846 if ((ctxt->format == 1) && (xmlIndentTreeOutput) &&
847 ((cur->type == XML_ELEMENT_NODE) ||
848 (cur->type == XML_COMMENT_NODE) ||
849 (cur->type == XML_PI_NODE)))
850 xmlOutputBufferWrite(buf, ctxt->indent_size *
851 (ctxt->level > ctxt->indent_nr ?
852 ctxt->indent_nr : ctxt->level),
853 ctxt->indent);
854 xmlNodeDumpOutputInternal(ctxt, cur);
855 if (ctxt->format == 1) {
856 xmlOutputBufferWrite(buf, 1, "\n");
857 }
858 cur = cur->next;
859 }
860 }
861
862 #ifdef LIBXML_HTML_ENABLED
863 /**
864 * xmlNodeDumpOutputInternal:
865 * @cur: the current node
866 *
867 * Dump an HTML node, recursive behaviour, children are printed too.
868 */
869 static int
htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)870 htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
871 const xmlChar *oldenc = NULL;
872 const xmlChar *oldctxtenc = ctxt->encoding;
873 const xmlChar *encoding = ctxt->encoding;
874 xmlOutputBufferPtr buf = ctxt->buf;
875 int switched_encoding = 0;
876 xmlDocPtr doc;
877
878 xmlInitParser();
879
880 doc = cur->doc;
881 if (doc != NULL) {
882 oldenc = doc->encoding;
883 if (ctxt->encoding != NULL) {
884 doc->encoding = BAD_CAST ctxt->encoding;
885 } else if (doc->encoding != NULL) {
886 encoding = doc->encoding;
887 }
888 }
889
890 if ((encoding != NULL) && (doc != NULL))
891 htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
892 if ((encoding == NULL) && (doc != NULL))
893 encoding = htmlGetMetaEncoding(doc);
894 if (encoding == NULL)
895 encoding = BAD_CAST "HTML";
896 if ((encoding != NULL) && (oldctxtenc == NULL) &&
897 (buf->encoder == NULL) && (buf->conv == NULL)) {
898 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
899 doc->encoding = oldenc;
900 return(-1);
901 }
902 switched_encoding = 1;
903 }
904 if (ctxt->options & XML_SAVE_FORMAT)
905 htmlNodeDumpFormatOutput(buf, doc, cur,
906 (const char *)encoding, 1);
907 else
908 htmlNodeDumpFormatOutput(buf, doc, cur,
909 (const char *)encoding, 0);
910 /*
911 * Restore the state of the saving context at the end of the document
912 */
913 if ((switched_encoding) && (oldctxtenc == NULL)) {
914 xmlSaveClearEncoding(ctxt);
915 }
916 if (doc != NULL)
917 doc->encoding = oldenc;
918 return(0);
919 }
920 #endif
921
922 /**
923 * xmlNodeDumpOutputInternal:
924 * @cur: the current node
925 *
926 * Dump an XML node, recursive behaviour, children are printed too.
927 */
928 static void
xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)929 xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
930 int format;
931 xmlNodePtr tmp;
932 xmlChar *start, *end;
933 xmlOutputBufferPtr buf;
934
935 if (cur == NULL) return;
936 buf = ctxt->buf;
937 if (cur->type == XML_XINCLUDE_START)
938 return;
939 if (cur->type == XML_XINCLUDE_END)
940 return;
941 if ((cur->type == XML_DOCUMENT_NODE) ||
942 (cur->type == XML_HTML_DOCUMENT_NODE)) {
943 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
944 return;
945 }
946 #ifdef LIBXML_HTML_ENABLED
947 if (ctxt->options & XML_SAVE_XHTML) {
948 xhtmlNodeDumpOutput(ctxt, cur);
949 return;
950 }
951 if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
952 (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
953 ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
954 (ctxt->options & XML_SAVE_AS_HTML)) {
955 htmlNodeDumpOutputInternal(ctxt, cur);
956 return;
957 }
958 #endif
959 if (cur->type == XML_DTD_NODE) {
960 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
961 return;
962 }
963 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
964 xmlNodeListDumpOutput(ctxt, cur->children);
965 return;
966 }
967 if (cur->type == XML_ELEMENT_DECL) {
968 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
969 return;
970 }
971 if (cur->type == XML_ATTRIBUTE_DECL) {
972 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
973 return;
974 }
975 if (cur->type == XML_ENTITY_DECL) {
976 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
977 return;
978 }
979 if (cur->type == XML_TEXT_NODE) {
980 if (cur->content != NULL) {
981 if (cur->name != xmlStringTextNoenc) {
982 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
983 } else {
984 /*
985 * Disable escaping, needed for XSLT
986 */
987 xmlOutputBufferWriteString(buf, (const char *) cur->content);
988 }
989 }
990
991 return;
992 }
993 if (cur->type == XML_PI_NODE) {
994 if (cur->content != NULL) {
995 xmlOutputBufferWrite(buf, 2, "<?");
996 xmlOutputBufferWriteString(buf, (const char *)cur->name);
997 if (cur->content != NULL) {
998 if (ctxt->format == 2)
999 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1000 else
1001 xmlOutputBufferWrite(buf, 1, " ");
1002 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1003 }
1004 xmlOutputBufferWrite(buf, 2, "?>");
1005 } else {
1006 xmlOutputBufferWrite(buf, 2, "<?");
1007 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1008 if (ctxt->format == 2)
1009 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1010 xmlOutputBufferWrite(buf, 2, "?>");
1011 }
1012 return;
1013 }
1014 if (cur->type == XML_COMMENT_NODE) {
1015 if (cur->content != NULL) {
1016 xmlOutputBufferWrite(buf, 4, "<!--");
1017 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1018 xmlOutputBufferWrite(buf, 3, "-->");
1019 }
1020 return;
1021 }
1022 if (cur->type == XML_ENTITY_REF_NODE) {
1023 xmlOutputBufferWrite(buf, 1, "&");
1024 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1025 xmlOutputBufferWrite(buf, 1, ";");
1026 return;
1027 }
1028 if (cur->type == XML_CDATA_SECTION_NODE) {
1029 if (cur->content == NULL || *cur->content == '\0') {
1030 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1031 } else {
1032 start = end = cur->content;
1033 while (*end != '\0') {
1034 if ((*end == ']') && (*(end + 1) == ']') &&
1035 (*(end + 2) == '>')) {
1036 end = end + 2;
1037 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1038 xmlOutputBufferWrite(buf, end - start, (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 return;
1051 }
1052 if (cur->type == XML_ATTRIBUTE_NODE) {
1053 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1054 return;
1055 }
1056 if (cur->type == XML_NAMESPACE_DECL) {
1057 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1058 return;
1059 }
1060
1061 format = ctxt->format;
1062 if (format == 1) {
1063 tmp = cur->children;
1064 while (tmp != NULL) {
1065 if ((tmp->type == XML_TEXT_NODE) ||
1066 (tmp->type == XML_CDATA_SECTION_NODE) ||
1067 (tmp->type == XML_ENTITY_REF_NODE)) {
1068 ctxt->format = 0;
1069 break;
1070 }
1071 tmp = tmp->next;
1072 }
1073 }
1074 xmlOutputBufferWrite(buf, 1, "<");
1075 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1076 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1077 xmlOutputBufferWrite(buf, 1, ":");
1078 }
1079
1080 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1081 if (cur->nsDef)
1082 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1083 if (cur->properties != NULL)
1084 xmlAttrListDumpOutput(ctxt, cur->properties);
1085
1086 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
1087 (cur->children == NULL) && ((ctxt->options & XML_SAVE_NO_EMPTY) == 0)) {
1088 if (ctxt->format == 2)
1089 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1090 xmlOutputBufferWrite(buf, 2, "/>");
1091 ctxt->format = format;
1092 return;
1093 }
1094 if (ctxt->format == 2)
1095 xmlOutputBufferWriteWSNonSig(ctxt, 1);
1096 xmlOutputBufferWrite(buf, 1, ">");
1097 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
1098 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1099 }
1100 if (cur->children != NULL) {
1101 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1102 if (ctxt->level >= 0) ctxt->level++;
1103 xmlNodeListDumpOutput(ctxt, cur->children);
1104 if (ctxt->level > 0) ctxt->level--;
1105 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1106 xmlOutputBufferWrite(buf, ctxt->indent_size *
1107 (ctxt->level > ctxt->indent_nr ?
1108 ctxt->indent_nr : ctxt->level),
1109 ctxt->indent);
1110 }
1111 xmlOutputBufferWrite(buf, 2, "</");
1112 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1113 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1114 xmlOutputBufferWrite(buf, 1, ":");
1115 }
1116
1117 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1118 if (ctxt->format == 2)
1119 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1120 xmlOutputBufferWrite(buf, 1, ">");
1121 ctxt->format = format;
1122 }
1123
1124 /**
1125 * xmlDocContentDumpOutput:
1126 * @cur: the document
1127 *
1128 * Dump an XML document.
1129 */
1130 static int
xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt,xmlDocPtr cur)1131 xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
1132 #ifdef LIBXML_HTML_ENABLED
1133 xmlDtdPtr dtd;
1134 int is_xhtml = 0;
1135 #endif
1136 const xmlChar *oldenc = cur->encoding;
1137 const xmlChar *oldctxtenc = ctxt->encoding;
1138 const xmlChar *encoding = ctxt->encoding;
1139 xmlCharEncodingOutputFunc oldescape = ctxt->escape;
1140 xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
1141 xmlOutputBufferPtr buf = ctxt->buf;
1142 xmlCharEncoding enc;
1143 int switched_encoding = 0;
1144
1145 xmlInitParser();
1146
1147 if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
1148 (cur->type != XML_DOCUMENT_NODE))
1149 return(-1);
1150
1151 if (ctxt->encoding != NULL) {
1152 cur->encoding = BAD_CAST ctxt->encoding;
1153 } else if (cur->encoding != NULL) {
1154 encoding = cur->encoding;
1155 }
1156
1157 if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
1158 ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
1159 ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
1160 (ctxt->options & XML_SAVE_AS_HTML)) {
1161 #ifdef LIBXML_HTML_ENABLED
1162 if (encoding != NULL)
1163 htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1164 if (encoding == NULL)
1165 encoding = htmlGetMetaEncoding(cur);
1166 if (encoding == NULL)
1167 encoding = BAD_CAST "HTML";
1168 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1169 (buf->encoder == NULL) && (buf->conv == NULL)) {
1170 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1171 cur->encoding = oldenc;
1172 return(-1);
1173 }
1174 }
1175 if (ctxt->options & XML_SAVE_FORMAT)
1176 htmlDocContentDumpFormatOutput(buf, cur,
1177 (const char *)encoding, 1);
1178 else
1179 htmlDocContentDumpFormatOutput(buf, cur,
1180 (const char *)encoding, 0);
1181 if (ctxt->encoding != NULL)
1182 cur->encoding = oldenc;
1183 return(0);
1184 #else
1185 return(-1);
1186 #endif
1187 } else if ((cur->type == XML_DOCUMENT_NODE) ||
1188 (ctxt->options & XML_SAVE_AS_XML) ||
1189 (ctxt->options & XML_SAVE_XHTML)) {
1190 enc = xmlParseCharEncoding((const char*) encoding);
1191 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1192 (buf->encoder == NULL) && (buf->conv == NULL) &&
1193 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
1194 if ((enc != XML_CHAR_ENCODING_UTF8) &&
1195 (enc != XML_CHAR_ENCODING_NONE) &&
1196 (enc != XML_CHAR_ENCODING_ASCII)) {
1197 /*
1198 * we need to switch to this encoding but just for this
1199 * document since we output the XMLDecl the conversion
1200 * must be done to not generate not well formed documents.
1201 */
1202 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1203 cur->encoding = oldenc;
1204 return(-1);
1205 }
1206 switched_encoding = 1;
1207 }
1208 if (ctxt->escape == xmlEscapeEntities)
1209 ctxt->escape = NULL;
1210 if (ctxt->escapeAttr == xmlEscapeEntities)
1211 ctxt->escapeAttr = NULL;
1212 }
1213
1214
1215 /*
1216 * Save the XML declaration
1217 */
1218 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1219 xmlOutputBufferWrite(buf, 14, "<?xml version=");
1220 if (cur->version != NULL)
1221 xmlBufWriteQuotedString(buf->buffer, cur->version);
1222 else
1223 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1224 if (encoding != NULL) {
1225 xmlOutputBufferWrite(buf, 10, " encoding=");
1226 xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
1227 }
1228 switch (cur->standalone) {
1229 case 0:
1230 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1231 break;
1232 case 1:
1233 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1234 break;
1235 }
1236 xmlOutputBufferWrite(buf, 3, "?>\n");
1237 }
1238
1239 #ifdef LIBXML_HTML_ENABLED
1240 if (ctxt->options & XML_SAVE_XHTML)
1241 is_xhtml = 1;
1242 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1243 dtd = xmlGetIntSubset(cur);
1244 if (dtd != NULL) {
1245 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1246 if (is_xhtml < 0) is_xhtml = 0;
1247 }
1248 }
1249 #endif
1250 if (cur->children != NULL) {
1251 xmlNodePtr child = cur->children;
1252
1253 while (child != NULL) {
1254 ctxt->level = 0;
1255 #ifdef LIBXML_HTML_ENABLED
1256 if (is_xhtml)
1257 xhtmlNodeDumpOutput(ctxt, child);
1258 else
1259 #endif
1260 xmlNodeDumpOutputInternal(ctxt, child);
1261 xmlOutputBufferWrite(buf, 1, "\n");
1262 child = child->next;
1263 }
1264 }
1265 }
1266
1267 /*
1268 * Restore the state of the saving context at the end of the document
1269 */
1270 if ((switched_encoding) && (oldctxtenc == NULL)) {
1271 xmlSaveClearEncoding(ctxt);
1272 ctxt->escape = oldescape;
1273 ctxt->escapeAttr = oldescapeAttr;
1274 }
1275 cur->encoding = oldenc;
1276 return(0);
1277 }
1278
1279 #ifdef LIBXML_HTML_ENABLED
1280 /************************************************************************
1281 * *
1282 * Functions specific to XHTML serialization *
1283 * *
1284 ************************************************************************/
1285
1286 /**
1287 * xhtmlIsEmpty:
1288 * @node: the node
1289 *
1290 * Check if a node is an empty xhtml node
1291 *
1292 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1293 */
1294 static int
xhtmlIsEmpty(xmlNodePtr node)1295 xhtmlIsEmpty(xmlNodePtr node) {
1296 if (node == NULL)
1297 return(-1);
1298 if (node->type != XML_ELEMENT_NODE)
1299 return(0);
1300 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1301 return(0);
1302 if (node->children != NULL)
1303 return(0);
1304 switch (node->name[0]) {
1305 case 'a':
1306 if (xmlStrEqual(node->name, BAD_CAST "area"))
1307 return(1);
1308 return(0);
1309 case 'b':
1310 if (xmlStrEqual(node->name, BAD_CAST "br"))
1311 return(1);
1312 if (xmlStrEqual(node->name, BAD_CAST "base"))
1313 return(1);
1314 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1315 return(1);
1316 return(0);
1317 case 'c':
1318 if (xmlStrEqual(node->name, BAD_CAST "col"))
1319 return(1);
1320 return(0);
1321 case 'f':
1322 if (xmlStrEqual(node->name, BAD_CAST "frame"))
1323 return(1);
1324 return(0);
1325 case 'h':
1326 if (xmlStrEqual(node->name, BAD_CAST "hr"))
1327 return(1);
1328 return(0);
1329 case 'i':
1330 if (xmlStrEqual(node->name, BAD_CAST "img"))
1331 return(1);
1332 if (xmlStrEqual(node->name, BAD_CAST "input"))
1333 return(1);
1334 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1335 return(1);
1336 return(0);
1337 case 'l':
1338 if (xmlStrEqual(node->name, BAD_CAST "link"))
1339 return(1);
1340 return(0);
1341 case 'm':
1342 if (xmlStrEqual(node->name, BAD_CAST "meta"))
1343 return(1);
1344 return(0);
1345 case 'p':
1346 if (xmlStrEqual(node->name, BAD_CAST "param"))
1347 return(1);
1348 return(0);
1349 }
1350 return(0);
1351 }
1352
1353 /**
1354 * xhtmlAttrListDumpOutput:
1355 * @cur: the first attribute pointer
1356 *
1357 * Dump a list of XML attributes
1358 */
1359 static void
xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt,xmlAttrPtr cur)1360 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1361 xmlAttrPtr xml_lang = NULL;
1362 xmlAttrPtr lang = NULL;
1363 xmlAttrPtr name = NULL;
1364 xmlAttrPtr id = NULL;
1365 xmlNodePtr parent;
1366 xmlOutputBufferPtr buf;
1367
1368 if (cur == NULL) return;
1369 buf = ctxt->buf;
1370 parent = cur->parent;
1371 while (cur != NULL) {
1372 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1373 id = cur;
1374 else
1375 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1376 name = cur;
1377 else
1378 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1379 lang = cur;
1380 else
1381 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1382 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1383 xml_lang = cur;
1384 else if ((cur->ns == NULL) &&
1385 ((cur->children == NULL) ||
1386 (cur->children->content == NULL) ||
1387 (cur->children->content[0] == 0)) &&
1388 (htmlIsBooleanAttr(cur->name))) {
1389 if (cur->children != NULL)
1390 xmlFreeNode(cur->children);
1391 cur->children = xmlNewText(cur->name);
1392 if (cur->children != NULL)
1393 cur->children->parent = (xmlNodePtr) cur;
1394 }
1395 xmlAttrDumpOutput(ctxt, cur);
1396 cur = cur->next;
1397 }
1398 /*
1399 * C.8
1400 */
1401 if ((name != NULL) && (id == NULL)) {
1402 if ((parent != NULL) && (parent->name != NULL) &&
1403 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1404 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1405 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1406 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1407 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1408 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1409 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1410 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1411 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
1412 xmlOutputBufferWrite(buf, 5, " id=\"");
1413 xmlAttrSerializeContent(buf, name);
1414 xmlOutputBufferWrite(buf, 1, "\"");
1415 }
1416 }
1417 /*
1418 * C.7.
1419 */
1420 if ((lang != NULL) && (xml_lang == NULL)) {
1421 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1422 xmlAttrSerializeContent(buf, lang);
1423 xmlOutputBufferWrite(buf, 1, "\"");
1424 } else
1425 if ((xml_lang != NULL) && (lang == NULL)) {
1426 xmlOutputBufferWrite(buf, 7, " lang=\"");
1427 xmlAttrSerializeContent(buf, xml_lang);
1428 xmlOutputBufferWrite(buf, 1, "\"");
1429 }
1430 }
1431
1432 /**
1433 * xhtmlNodeListDumpOutput:
1434 * @buf: the XML buffer output
1435 * @doc: the XHTML document
1436 * @cur: the first node
1437 * @level: the imbrication level for indenting
1438 * @format: is formatting allowed
1439 * @encoding: an optional encoding string
1440 *
1441 * Dump an XML node list, recursive behaviour, children are printed too.
1442 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1443 * or xmlKeepBlanksDefault(0) was called
1444 */
1445 static void
xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)1446 xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1447 xmlOutputBufferPtr buf;
1448
1449 if (cur == NULL) return;
1450 buf = ctxt->buf;
1451 while (cur != NULL) {
1452 if ((ctxt->format == 1) && (xmlIndentTreeOutput) &&
1453 (cur->type == XML_ELEMENT_NODE))
1454 xmlOutputBufferWrite(buf, ctxt->indent_size *
1455 (ctxt->level > ctxt->indent_nr ?
1456 ctxt->indent_nr : ctxt->level),
1457 ctxt->indent);
1458 xhtmlNodeDumpOutput(ctxt, cur);
1459 if (ctxt->format == 1) {
1460 xmlOutputBufferWrite(buf, 1, "\n");
1461 }
1462 cur = cur->next;
1463 }
1464 }
1465
1466 /**
1467 * xhtmlNodeDumpOutput:
1468 * @buf: the XML buffer output
1469 * @doc: the XHTML document
1470 * @cur: the current node
1471 * @level: the imbrication level for indenting
1472 * @format: is formatting allowed
1473 * @encoding: an optional encoding string
1474 *
1475 * Dump an XHTML node, recursive behaviour, children are printed too.
1476 */
1477 static void
xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)1478 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1479 int format, addmeta = 0;
1480 xmlNodePtr tmp;
1481 xmlChar *start, *end;
1482 xmlOutputBufferPtr buf;
1483
1484 if (cur == NULL) return;
1485 if ((cur->type == XML_DOCUMENT_NODE) ||
1486 (cur->type == XML_HTML_DOCUMENT_NODE)) {
1487 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1488 return;
1489 }
1490 if (cur->type == XML_XINCLUDE_START)
1491 return;
1492 if (cur->type == XML_XINCLUDE_END)
1493 return;
1494 if (cur->type == XML_NAMESPACE_DECL) {
1495 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1496 return;
1497 }
1498 if (cur->type == XML_DTD_NODE) {
1499 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1500 return;
1501 }
1502 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
1503 xhtmlNodeListDumpOutput(ctxt, cur->children);
1504 return;
1505 }
1506 buf = ctxt->buf;
1507 if (cur->type == XML_ELEMENT_DECL) {
1508 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1509 return;
1510 }
1511 if (cur->type == XML_ATTRIBUTE_DECL) {
1512 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1513 return;
1514 }
1515 if (cur->type == XML_ENTITY_DECL) {
1516 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1517 return;
1518 }
1519 if (cur->type == XML_TEXT_NODE) {
1520 if (cur->content != NULL) {
1521 if ((cur->name == xmlStringText) ||
1522 (cur->name != xmlStringTextNoenc)) {
1523 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1524 } else {
1525 /*
1526 * Disable escaping, needed for XSLT
1527 */
1528 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1529 }
1530 }
1531
1532 return;
1533 }
1534 if (cur->type == XML_PI_NODE) {
1535 if (cur->content != NULL) {
1536 xmlOutputBufferWrite(buf, 2, "<?");
1537 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1538 if (cur->content != NULL) {
1539 xmlOutputBufferWrite(buf, 1, " ");
1540 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1541 }
1542 xmlOutputBufferWrite(buf, 2, "?>");
1543 } else {
1544 xmlOutputBufferWrite(buf, 2, "<?");
1545 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1546 xmlOutputBufferWrite(buf, 2, "?>");
1547 }
1548 return;
1549 }
1550 if (cur->type == XML_COMMENT_NODE) {
1551 if (cur->content != NULL) {
1552 xmlOutputBufferWrite(buf, 4, "<!--");
1553 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1554 xmlOutputBufferWrite(buf, 3, "-->");
1555 }
1556 return;
1557 }
1558 if (cur->type == XML_ENTITY_REF_NODE) {
1559 xmlOutputBufferWrite(buf, 1, "&");
1560 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1561 xmlOutputBufferWrite(buf, 1, ";");
1562 return;
1563 }
1564 if (cur->type == XML_CDATA_SECTION_NODE) {
1565 if (cur->content == NULL || *cur->content == '\0') {
1566 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1567 } else {
1568 start = end = cur->content;
1569 while (*end != '\0') {
1570 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1571 end = end + 2;
1572 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1573 xmlOutputBufferWrite(buf, end - start, (const char *)start);
1574 xmlOutputBufferWrite(buf, 3, "]]>");
1575 start = end;
1576 }
1577 end++;
1578 }
1579 if (start != end) {
1580 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1581 xmlOutputBufferWriteString(buf, (const char *)start);
1582 xmlOutputBufferWrite(buf, 3, "]]>");
1583 }
1584 }
1585 return;
1586 }
1587 if (cur->type == XML_ATTRIBUTE_NODE) {
1588 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1589 return;
1590 }
1591
1592 format = ctxt->format;
1593 if (format == 1) {
1594 tmp = cur->children;
1595 while (tmp != NULL) {
1596 if ((tmp->type == XML_TEXT_NODE) ||
1597 (tmp->type == XML_ENTITY_REF_NODE)) {
1598 format = 0;
1599 break;
1600 }
1601 tmp = tmp->next;
1602 }
1603 }
1604 xmlOutputBufferWrite(buf, 1, "<");
1605 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1606 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1607 xmlOutputBufferWrite(buf, 1, ":");
1608 }
1609
1610 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1611 if (cur->nsDef)
1612 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1613 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1614 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1615 /*
1616 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1617 */
1618 xmlOutputBufferWriteString(buf,
1619 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1620 }
1621 if (cur->properties != NULL)
1622 xhtmlAttrListDumpOutput(ctxt, cur->properties);
1623
1624 if ((cur->type == XML_ELEMENT_NODE) &&
1625 (cur->parent != NULL) &&
1626 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1627 xmlStrEqual(cur->name, BAD_CAST"head") &&
1628 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1629
1630 tmp = cur->children;
1631 while (tmp != NULL) {
1632 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1633 xmlChar *httpequiv;
1634
1635 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
1636 if (httpequiv != NULL) {
1637 if (xmlStrcasecmp(httpequiv, BAD_CAST"Content-Type") == 0) {
1638 xmlFree(httpequiv);
1639 break;
1640 }
1641 xmlFree(httpequiv);
1642 }
1643 }
1644 tmp = tmp->next;
1645 }
1646 if (tmp == NULL)
1647 addmeta = 1;
1648 }
1649
1650 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1651 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1652 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1653 /*
1654 * C.2. Empty Elements
1655 */
1656 xmlOutputBufferWrite(buf, 3, " />");
1657 } else {
1658 if (addmeta == 1) {
1659 xmlOutputBufferWrite(buf, 1, ">");
1660 if (ctxt->format == 1) {
1661 xmlOutputBufferWrite(buf, 1, "\n");
1662 if (xmlIndentTreeOutput)
1663 xmlOutputBufferWrite(buf, ctxt->indent_size *
1664 (ctxt->level + 1 > ctxt->indent_nr ?
1665 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1666 }
1667 xmlOutputBufferWriteString(buf,
1668 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1669 if (ctxt->encoding) {
1670 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1671 } else {
1672 xmlOutputBufferWrite(buf, 5, "UTF-8");
1673 }
1674 xmlOutputBufferWrite(buf, 4, "\" />");
1675 if (ctxt->format == 1)
1676 xmlOutputBufferWrite(buf, 1, "\n");
1677 } else {
1678 xmlOutputBufferWrite(buf, 1, ">");
1679 }
1680 /*
1681 * C.3. Element Minimization and Empty Element Content
1682 */
1683 xmlOutputBufferWrite(buf, 2, "</");
1684 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1685 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1686 xmlOutputBufferWrite(buf, 1, ":");
1687 }
1688 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1689 xmlOutputBufferWrite(buf, 1, ">");
1690 }
1691 return;
1692 }
1693 xmlOutputBufferWrite(buf, 1, ">");
1694 if (addmeta == 1) {
1695 if (ctxt->format == 1) {
1696 xmlOutputBufferWrite(buf, 1, "\n");
1697 if (xmlIndentTreeOutput)
1698 xmlOutputBufferWrite(buf, ctxt->indent_size *
1699 (ctxt->level + 1 > ctxt->indent_nr ?
1700 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1701 }
1702 xmlOutputBufferWriteString(buf,
1703 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1704 if (ctxt->encoding) {
1705 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1706 } else {
1707 xmlOutputBufferWrite(buf, 5, "UTF-8");
1708 }
1709 xmlOutputBufferWrite(buf, 4, "\" />");
1710 }
1711 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
1712 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1713 }
1714
1715 #if 0
1716 /*
1717 * This was removed due to problems with HTML processors.
1718 * See bug #345147.
1719 */
1720 /*
1721 * 4.8. Script and Style elements
1722 */
1723 if ((cur->type == XML_ELEMENT_NODE) &&
1724 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1725 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1726 ((cur->ns == NULL) ||
1727 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1728 xmlNodePtr child = cur->children;
1729
1730 while (child != NULL) {
1731 if (child->type == XML_TEXT_NODE) {
1732 if ((xmlStrchr(child->content, '<') == NULL) &&
1733 (xmlStrchr(child->content, '&') == NULL) &&
1734 (xmlStrstr(child->content, BAD_CAST "]]>") == NULL)) {
1735 /* Nothing to escape, so just output as is... */
1736 /* FIXME: Should we do something about "--" also? */
1737 int level = ctxt->level;
1738 int indent = ctxt->format;
1739
1740 ctxt->level = 0;
1741 ctxt->format = 0;
1742 xmlOutputBufferWriteString(buf, (const char *) child->content);
1743 /* (We cannot use xhtmlNodeDumpOutput() here because
1744 * we wish to leave '>' unescaped!) */
1745 ctxt->level = level;
1746 ctxt->format = indent;
1747 } else {
1748 /* We must use a CDATA section. Unfortunately,
1749 * this will break CSS and JavaScript when read by
1750 * a browser in HTML4-compliant mode. :-( */
1751 start = end = child->content;
1752 while (*end != '\0') {
1753 if (*end == ']' &&
1754 *(end + 1) == ']' &&
1755 *(end + 2) == '>') {
1756 end = end + 2;
1757 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1758 xmlOutputBufferWrite(buf, end - start,
1759 (const char *)start);
1760 xmlOutputBufferWrite(buf, 3, "]]>");
1761 start = end;
1762 }
1763 end++;
1764 }
1765 if (start != end) {
1766 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1767 xmlOutputBufferWrite(buf, end - start,
1768 (const char *)start);
1769 xmlOutputBufferWrite(buf, 3, "]]>");
1770 }
1771 }
1772 } else {
1773 int level = ctxt->level;
1774 int indent = ctxt->format;
1775
1776 ctxt->level = 0;
1777 ctxt->format = 0;
1778 xhtmlNodeDumpOutput(ctxt, child);
1779 ctxt->level = level;
1780 ctxt->format = indent;
1781 }
1782 child = child->next;
1783 }
1784 }
1785 #endif
1786
1787 if (cur->children != NULL) {
1788 int indent = ctxt->format;
1789
1790 if (format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1791 if (ctxt->level >= 0) ctxt->level++;
1792 ctxt->format = format;
1793 xhtmlNodeListDumpOutput(ctxt, cur->children);
1794 if (ctxt->level > 0) ctxt->level--;
1795 ctxt->format = indent;
1796 if ((xmlIndentTreeOutput) && (format == 1))
1797 xmlOutputBufferWrite(buf, ctxt->indent_size *
1798 (ctxt->level > ctxt->indent_nr ?
1799 ctxt->indent_nr : ctxt->level),
1800 ctxt->indent);
1801 }
1802 xmlOutputBufferWrite(buf, 2, "</");
1803 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1804 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1805 xmlOutputBufferWrite(buf, 1, ":");
1806 }
1807
1808 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1809 xmlOutputBufferWrite(buf, 1, ">");
1810 }
1811 #endif
1812
1813 /************************************************************************
1814 * *
1815 * Public entry points *
1816 * *
1817 ************************************************************************/
1818
1819 /**
1820 * xmlSaveToFd:
1821 * @fd: a file descriptor number
1822 * @encoding: the encoding name to use or NULL
1823 * @options: a set of xmlSaveOptions
1824 *
1825 * Create a document saving context serializing to a file descriptor
1826 * with the encoding and the options given.
1827 *
1828 * Returns a new serialization context or NULL in case of error.
1829 */
1830 xmlSaveCtxtPtr
xmlSaveToFd(int fd,const char * encoding,int options)1831 xmlSaveToFd(int fd, const char *encoding, int options)
1832 {
1833 xmlSaveCtxtPtr ret;
1834
1835 ret = xmlNewSaveCtxt(encoding, options);
1836 if (ret == NULL) return(NULL);
1837 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1838 if (ret->buf == NULL) {
1839 xmlFreeSaveCtxt(ret);
1840 return(NULL);
1841 }
1842 return(ret);
1843 }
1844
1845 /**
1846 * xmlSaveToFilename:
1847 * @filename: a file name or an URL
1848 * @encoding: the encoding name to use or NULL
1849 * @options: a set of xmlSaveOptions
1850 *
1851 * Create a document saving context serializing to a filename or possibly
1852 * to an URL (but this is less reliable) with the encoding and the options
1853 * given.
1854 *
1855 * Returns a new serialization context or NULL in case of error.
1856 */
1857 xmlSaveCtxtPtr
xmlSaveToFilename(const char * filename,const char * encoding,int options)1858 xmlSaveToFilename(const char *filename, const char *encoding, int options)
1859 {
1860 xmlSaveCtxtPtr ret;
1861 int compression = 0; /* TODO handle compression option */
1862
1863 ret = xmlNewSaveCtxt(encoding, options);
1864 if (ret == NULL) return(NULL);
1865 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
1866 compression);
1867 if (ret->buf == NULL) {
1868 xmlFreeSaveCtxt(ret);
1869 return(NULL);
1870 }
1871 return(ret);
1872 }
1873
1874 /**
1875 * xmlSaveToBuffer:
1876 * @buffer: a buffer
1877 * @encoding: the encoding name to use or NULL
1878 * @options: a set of xmlSaveOptions
1879 *
1880 * Create a document saving context serializing to a buffer
1881 * with the encoding and the options given
1882 *
1883 * Returns a new serialization context or NULL in case of error.
1884 */
1885
1886 xmlSaveCtxtPtr
xmlSaveToBuffer(xmlBufferPtr buffer,const char * encoding,int options)1887 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1888 {
1889 xmlSaveCtxtPtr ret;
1890 xmlOutputBufferPtr out_buff;
1891 xmlCharEncodingHandlerPtr handler;
1892
1893 ret = xmlNewSaveCtxt(encoding, options);
1894 if (ret == NULL) return(NULL);
1895
1896 if (encoding != NULL) {
1897 handler = xmlFindCharEncodingHandler(encoding);
1898 if (handler == NULL) {
1899 xmlFree(ret);
1900 return(NULL);
1901 }
1902 } else
1903 handler = NULL;
1904 out_buff = xmlOutputBufferCreateBuffer(buffer, handler);
1905 if (out_buff == NULL) {
1906 xmlFree(ret);
1907 if (handler) xmlCharEncCloseFunc(handler);
1908 return(NULL);
1909 }
1910
1911 ret->buf = out_buff;
1912 return(ret);
1913 }
1914
1915 /**
1916 * xmlSaveToIO:
1917 * @iowrite: an I/O write function
1918 * @ioclose: an I/O close function
1919 * @ioctx: an I/O handler
1920 * @encoding: the encoding name to use or NULL
1921 * @options: a set of xmlSaveOptions
1922 *
1923 * Create a document saving context serializing to a file descriptor
1924 * with the encoding and the options given
1925 *
1926 * Returns a new serialization context or NULL in case of error.
1927 */
1928 xmlSaveCtxtPtr
xmlSaveToIO(xmlOutputWriteCallback iowrite,xmlOutputCloseCallback ioclose,void * ioctx,const char * encoding,int options)1929 xmlSaveToIO(xmlOutputWriteCallback iowrite,
1930 xmlOutputCloseCallback ioclose,
1931 void *ioctx, const char *encoding, int options)
1932 {
1933 xmlSaveCtxtPtr ret;
1934
1935 ret = xmlNewSaveCtxt(encoding, options);
1936 if (ret == NULL) return(NULL);
1937 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1938 if (ret->buf == NULL) {
1939 xmlFreeSaveCtxt(ret);
1940 return(NULL);
1941 }
1942 return(ret);
1943 }
1944
1945 /**
1946 * xmlSaveDoc:
1947 * @ctxt: a document saving context
1948 * @doc: a document
1949 *
1950 * Save a full document to a saving context
1951 * TODO: The function is not fully implemented yet as it does not return the
1952 * byte count but 0 instead
1953 *
1954 * Returns the number of byte written or -1 in case of error
1955 */
1956 long
xmlSaveDoc(xmlSaveCtxtPtr ctxt,xmlDocPtr doc)1957 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1958 {
1959 long ret = 0;
1960
1961 if ((ctxt == NULL) || (doc == NULL)) return(-1);
1962 if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1963 return(-1);
1964 return(ret);
1965 }
1966
1967 /**
1968 * xmlSaveTree:
1969 * @ctxt: a document saving context
1970 * @node: the top node of the subtree to save
1971 *
1972 * Save a subtree starting at the node parameter to a saving context
1973 * TODO: The function is not fully implemented yet as it does not return the
1974 * byte count but 0 instead
1975 *
1976 * Returns the number of byte written or -1 in case of error
1977 */
1978 long
xmlSaveTree(xmlSaveCtxtPtr ctxt,xmlNodePtr node)1979 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1980 {
1981 long ret = 0;
1982
1983 if ((ctxt == NULL) || (node == NULL)) return(-1);
1984 xmlNodeDumpOutputInternal(ctxt, node);
1985 return(ret);
1986 }
1987
1988 /**
1989 * xmlSaveFlush:
1990 * @ctxt: a document saving context
1991 *
1992 * Flush a document saving context, i.e. make sure that all bytes have
1993 * been output.
1994 *
1995 * Returns the number of byte written or -1 in case of error.
1996 */
1997 int
xmlSaveFlush(xmlSaveCtxtPtr ctxt)1998 xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1999 {
2000 if (ctxt == NULL) return(-1);
2001 if (ctxt->buf == NULL) return(-1);
2002 return(xmlOutputBufferFlush(ctxt->buf));
2003 }
2004
2005 /**
2006 * xmlSaveClose:
2007 * @ctxt: a document saving context
2008 *
2009 * Close a document saving context, i.e. make sure that all bytes have
2010 * been output and free the associated data.
2011 *
2012 * Returns the number of byte written or -1 in case of error.
2013 */
2014 int
xmlSaveClose(xmlSaveCtxtPtr ctxt)2015 xmlSaveClose(xmlSaveCtxtPtr ctxt)
2016 {
2017 int ret;
2018
2019 if (ctxt == NULL) return(-1);
2020 ret = xmlSaveFlush(ctxt);
2021 xmlFreeSaveCtxt(ctxt);
2022 return(ret);
2023 }
2024
2025 /**
2026 * xmlSaveSetEscape:
2027 * @ctxt: a document saving context
2028 * @escape: the escaping function
2029 *
2030 * Set a custom escaping function to be used for text in element content
2031 *
2032 * Returns 0 if successful or -1 in case of error.
2033 */
2034 int
xmlSaveSetEscape(xmlSaveCtxtPtr ctxt,xmlCharEncodingOutputFunc escape)2035 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
2036 {
2037 if (ctxt == NULL) return(-1);
2038 ctxt->escape = escape;
2039 return(0);
2040 }
2041
2042 /**
2043 * xmlSaveSetAttrEscape:
2044 * @ctxt: a document saving context
2045 * @escape: the escaping function
2046 *
2047 * Set a custom escaping function to be used for text in attribute content
2048 *
2049 * Returns 0 if successful or -1 in case of error.
2050 */
2051 int
xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt,xmlCharEncodingOutputFunc escape)2052 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
2053 {
2054 if (ctxt == NULL) return(-1);
2055 ctxt->escapeAttr = escape;
2056 return(0);
2057 }
2058
2059 /************************************************************************
2060 * *
2061 * Public entry points based on buffers *
2062 * *
2063 ************************************************************************/
2064
2065 /**
2066 * xmlBufAttrSerializeTxtContent:
2067 * @buf: and xmlBufPtr output
2068 * @doc: the document
2069 * @attr: the attribute node
2070 * @string: the text content
2071 *
2072 * Serialize text attribute values to an xmlBufPtr
2073 */
2074 void
xmlBufAttrSerializeTxtContent(xmlBufPtr buf,xmlDocPtr doc,xmlAttrPtr attr,const xmlChar * string)2075 xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
2076 xmlAttrPtr attr, const xmlChar * string)
2077 {
2078 xmlChar *base, *cur;
2079
2080 if (string == NULL)
2081 return;
2082 base = cur = (xmlChar *) string;
2083 while (*cur != 0) {
2084 if (*cur == '\n') {
2085 if (base != cur)
2086 xmlBufAdd(buf, base, cur - base);
2087 xmlBufAdd(buf, BAD_CAST " ", 5);
2088 cur++;
2089 base = cur;
2090 } else if (*cur == '\r') {
2091 if (base != cur)
2092 xmlBufAdd(buf, base, cur - base);
2093 xmlBufAdd(buf, BAD_CAST " ", 5);
2094 cur++;
2095 base = cur;
2096 } else if (*cur == '\t') {
2097 if (base != cur)
2098 xmlBufAdd(buf, base, cur - base);
2099 xmlBufAdd(buf, BAD_CAST "	", 4);
2100 cur++;
2101 base = cur;
2102 } else if (*cur == '"') {
2103 if (base != cur)
2104 xmlBufAdd(buf, base, cur - base);
2105 xmlBufAdd(buf, BAD_CAST """, 6);
2106 cur++;
2107 base = cur;
2108 } else if (*cur == '<') {
2109 if (base != cur)
2110 xmlBufAdd(buf, base, cur - base);
2111 xmlBufAdd(buf, BAD_CAST "<", 4);
2112 cur++;
2113 base = cur;
2114 } else if (*cur == '>') {
2115 if (base != cur)
2116 xmlBufAdd(buf, base, cur - base);
2117 xmlBufAdd(buf, BAD_CAST ">", 4);
2118 cur++;
2119 base = cur;
2120 } else if (*cur == '&') {
2121 if (base != cur)
2122 xmlBufAdd(buf, base, cur - base);
2123 xmlBufAdd(buf, BAD_CAST "&", 5);
2124 cur++;
2125 base = cur;
2126 } else if ((*cur >= 0x80) && (cur[1] != 0) &&
2127 ((doc == NULL) || (doc->encoding == NULL))) {
2128 /*
2129 * We assume we have UTF-8 content.
2130 */
2131 unsigned char tmp[12];
2132 int val = 0, l = 1;
2133
2134 if (base != cur)
2135 xmlBufAdd(buf, base, cur - base);
2136 if (*cur < 0xC0) {
2137 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
2138 xmlSerializeHexCharRef(tmp, *cur);
2139 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2140 cur++;
2141 base = cur;
2142 continue;
2143 } else if (*cur < 0xE0) {
2144 val = (cur[0]) & 0x1F;
2145 val <<= 6;
2146 val |= (cur[1]) & 0x3F;
2147 l = 2;
2148 } else if ((*cur < 0xF0) && (cur [2] != 0)) {
2149 val = (cur[0]) & 0x0F;
2150 val <<= 6;
2151 val |= (cur[1]) & 0x3F;
2152 val <<= 6;
2153 val |= (cur[2]) & 0x3F;
2154 l = 3;
2155 } else if ((*cur < 0xF8) && (cur [2] != 0) && (cur[3] != 0)) {
2156 val = (cur[0]) & 0x07;
2157 val <<= 6;
2158 val |= (cur[1]) & 0x3F;
2159 val <<= 6;
2160 val |= (cur[2]) & 0x3F;
2161 val <<= 6;
2162 val |= (cur[3]) & 0x3F;
2163 l = 4;
2164 }
2165 if ((l == 1) || (!IS_CHAR(val))) {
2166 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
2167 xmlSerializeHexCharRef(tmp, *cur);
2168 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2169 cur++;
2170 base = cur;
2171 continue;
2172 }
2173 /*
2174 * We could do multiple things here. Just save
2175 * as a char ref
2176 */
2177 xmlSerializeHexCharRef(tmp, val);
2178 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2179 cur += l;
2180 base = cur;
2181 } else {
2182 cur++;
2183 }
2184 }
2185 if (base != cur)
2186 xmlBufAdd(buf, base, cur - base);
2187 }
2188
2189 /**
2190 * xmlAttrSerializeTxtContent:
2191 * @buf: the XML buffer output
2192 * @doc: the document
2193 * @attr: the attribute node
2194 * @string: the text content
2195 *
2196 * Serialize text attribute values to an xml simple buffer
2197 */
2198 void
xmlAttrSerializeTxtContent(xmlBufferPtr buf,xmlDocPtr doc,xmlAttrPtr attr,const xmlChar * string)2199 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
2200 xmlAttrPtr attr, const xmlChar * string)
2201 {
2202 xmlBufPtr buffer;
2203
2204 if ((buf == NULL) || (string == NULL))
2205 return;
2206 buffer = xmlBufFromBuffer(buf);
2207 if (buffer == NULL)
2208 return;
2209 xmlBufAttrSerializeTxtContent(buffer, doc, attr, string);
2210 xmlBufBackToBuffer(buffer);
2211 }
2212
2213 /**
2214 * xmlNodeDump:
2215 * @buf: the XML buffer output
2216 * @doc: the document
2217 * @cur: the current node
2218 * @level: the imbrication level for indenting
2219 * @format: is formatting allowed
2220 *
2221 * Dump an XML node, recursive behaviour,children are printed too.
2222 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2223 * or xmlKeepBlanksDefault(0) was called
2224 * Since this is using xmlBuffer structures it is limited to 2GB and somehow
2225 * deprecated, use xmlBufNodeDump() instead.
2226 *
2227 * Returns the number of bytes written to the buffer or -1 in case of error
2228 */
2229 int
xmlNodeDump(xmlBufferPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format)2230 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2231 int format)
2232 {
2233 xmlBufPtr buffer;
2234 int ret;
2235
2236 if ((buf == NULL) || (cur == NULL))
2237 return(-1);
2238 buffer = xmlBufFromBuffer(buf);
2239 if (buffer == NULL)
2240 return(-1);
2241 ret = xmlBufNodeDump(buffer, doc, cur, level, format);
2242 xmlBufBackToBuffer(buffer);
2243 if (ret > INT_MAX)
2244 return(-1);
2245 return((int) ret);
2246 }
2247
2248 /**
2249 * xmlBufNodeDump:
2250 * @buf: the XML buffer output
2251 * @doc: the document
2252 * @cur: the current node
2253 * @level: the imbrication level for indenting
2254 * @format: is formatting allowed
2255 *
2256 * Dump an XML node, recursive behaviour,children are printed too.
2257 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2258 * or xmlKeepBlanksDefault(0) was called
2259 *
2260 * Returns the number of bytes written to the buffer, in case of error 0
2261 * is returned or @buf stores the error
2262 */
2263
2264 size_t
xmlBufNodeDump(xmlBufPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format)2265 xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2266 int format)
2267 {
2268 size_t use;
2269 int ret;
2270 xmlOutputBufferPtr outbuf;
2271 int oldalloc;
2272
2273 xmlInitParser();
2274
2275 if (cur == NULL) {
2276 #ifdef DEBUG_TREE
2277 xmlGenericError(xmlGenericErrorContext,
2278 "xmlNodeDump : node == NULL\n");
2279 #endif
2280 return (-1);
2281 }
2282 if (buf == NULL) {
2283 #ifdef DEBUG_TREE
2284 xmlGenericError(xmlGenericErrorContext,
2285 "xmlNodeDump : buf == NULL\n");
2286 #endif
2287 return (-1);
2288 }
2289 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2290 if (outbuf == NULL) {
2291 xmlSaveErrMemory("creating buffer");
2292 return (-1);
2293 }
2294 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2295 outbuf->buffer = buf;
2296 outbuf->encoder = NULL;
2297 outbuf->writecallback = NULL;
2298 outbuf->closecallback = NULL;
2299 outbuf->context = NULL;
2300 outbuf->written = 0;
2301
2302 use = xmlBufUse(buf);
2303 oldalloc = xmlBufGetAllocationScheme(buf);
2304 xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
2305 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
2306 xmlBufSetAllocationScheme(buf, oldalloc);
2307 xmlFree(outbuf);
2308 ret = xmlBufUse(buf) - use;
2309 return (ret);
2310 }
2311
2312 /**
2313 * xmlElemDump:
2314 * @f: the FILE * for the output
2315 * @doc: the document
2316 * @cur: the current node
2317 *
2318 * Dump an XML/HTML node, recursive behaviour, children are printed too.
2319 */
2320 void
xmlElemDump(FILE * f,xmlDocPtr doc,xmlNodePtr cur)2321 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2322 {
2323 xmlOutputBufferPtr outbuf;
2324
2325 xmlInitParser();
2326
2327 if (cur == NULL) {
2328 #ifdef DEBUG_TREE
2329 xmlGenericError(xmlGenericErrorContext,
2330 "xmlElemDump : cur == NULL\n");
2331 #endif
2332 return;
2333 }
2334 #ifdef DEBUG_TREE
2335 if (doc == NULL) {
2336 xmlGenericError(xmlGenericErrorContext,
2337 "xmlElemDump : doc == NULL\n");
2338 }
2339 #endif
2340
2341 outbuf = xmlOutputBufferCreateFile(f, NULL);
2342 if (outbuf == NULL)
2343 return;
2344 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
2345 #ifdef LIBXML_HTML_ENABLED
2346 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2347 #else
2348 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
2349 #endif /* LIBXML_HTML_ENABLED */
2350 } else
2351 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2352 xmlOutputBufferClose(outbuf);
2353 }
2354
2355 /************************************************************************
2356 * *
2357 * Saving functions front-ends *
2358 * *
2359 ************************************************************************/
2360
2361 /**
2362 * xmlNodeDumpOutput:
2363 * @buf: the XML buffer output
2364 * @doc: the document
2365 * @cur: the current node
2366 * @level: the imbrication level for indenting
2367 * @format: is formatting allowed
2368 * @encoding: an optional encoding string
2369 *
2370 * Dump an XML node, recursive behaviour, children are printed too.
2371 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2372 * or xmlKeepBlanksDefault(0) was called
2373 */
2374 void
xmlNodeDumpOutput(xmlOutputBufferPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format,const char * encoding)2375 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2376 int level, int format, const char *encoding)
2377 {
2378 xmlSaveCtxt ctxt;
2379 #ifdef LIBXML_HTML_ENABLED
2380 xmlDtdPtr dtd;
2381 int is_xhtml = 0;
2382 #endif
2383
2384 xmlInitParser();
2385
2386 if ((buf == NULL) || (cur == NULL)) return;
2387
2388 if (encoding == NULL)
2389 encoding = "UTF-8";
2390
2391 memset(&ctxt, 0, sizeof(ctxt));
2392 ctxt.doc = doc;
2393 ctxt.buf = buf;
2394 ctxt.level = level;
2395 ctxt.format = format ? 1 : 0;
2396 ctxt.encoding = (const xmlChar *) encoding;
2397 xmlSaveCtxtInit(&ctxt);
2398 ctxt.options |= XML_SAVE_AS_XML;
2399
2400 #ifdef LIBXML_HTML_ENABLED
2401 dtd = xmlGetIntSubset(doc);
2402 if (dtd != NULL) {
2403 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2404 if (is_xhtml < 0)
2405 is_xhtml = 0;
2406 }
2407
2408 if (is_xhtml)
2409 xhtmlNodeDumpOutput(&ctxt, cur);
2410 else
2411 #endif
2412 xmlNodeDumpOutputInternal(&ctxt, cur);
2413 }
2414
2415 /**
2416 * xmlDocDumpFormatMemoryEnc:
2417 * @out_doc: Document to generate XML text from
2418 * @doc_txt_ptr: Memory pointer for allocated XML text
2419 * @doc_txt_len: Length of the generated XML text
2420 * @txt_encoding: Character encoding to use when generating XML text
2421 * @format: should formatting spaces been added
2422 *
2423 * Dump the current DOM tree into memory using the character encoding specified
2424 * by the caller. Note it is up to the caller of this function to free the
2425 * allocated memory with xmlFree().
2426 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2427 * or xmlKeepBlanksDefault(0) was called
2428 */
2429
2430 void
xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc,xmlChar ** doc_txt_ptr,int * doc_txt_len,const char * txt_encoding,int format)2431 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2432 int * doc_txt_len, const char * txt_encoding,
2433 int format) {
2434 xmlSaveCtxt ctxt;
2435 int dummy = 0;
2436 xmlOutputBufferPtr out_buff = NULL;
2437 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
2438
2439 if (doc_txt_len == NULL) {
2440 doc_txt_len = &dummy; /* Continue, caller just won't get length */
2441 }
2442
2443 if (doc_txt_ptr == NULL) {
2444 *doc_txt_len = 0;
2445 return;
2446 }
2447
2448 *doc_txt_ptr = NULL;
2449 *doc_txt_len = 0;
2450
2451 if (out_doc == NULL) {
2452 /* No document, no output */
2453 return;
2454 }
2455
2456 /*
2457 * Validate the encoding value, if provided.
2458 * This logic is copied from xmlSaveFileEnc.
2459 */
2460
2461 if (txt_encoding == NULL)
2462 txt_encoding = (const char *) out_doc->encoding;
2463 if (txt_encoding != NULL) {
2464 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2465 if ( conv_hdlr == NULL ) {
2466 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2467 txt_encoding);
2468 return;
2469 }
2470 }
2471
2472 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2473 xmlSaveErrMemory("creating buffer");
2474 return;
2475 }
2476
2477 memset(&ctxt, 0, sizeof(ctxt));
2478 ctxt.doc = out_doc;
2479 ctxt.buf = out_buff;
2480 ctxt.level = 0;
2481 ctxt.format = format ? 1 : 0;
2482 ctxt.encoding = (const xmlChar *) txt_encoding;
2483 xmlSaveCtxtInit(&ctxt);
2484 ctxt.options |= XML_SAVE_AS_XML;
2485 xmlDocContentDumpOutput(&ctxt, out_doc);
2486 xmlOutputBufferFlush(out_buff);
2487 if (out_buff->conv != NULL) {
2488 *doc_txt_len = xmlBufUse(out_buff->conv);
2489 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len);
2490 } else {
2491 *doc_txt_len = xmlBufUse(out_buff->buffer);
2492 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len);
2493 }
2494 (void)xmlOutputBufferClose(out_buff);
2495
2496 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2497 *doc_txt_len = 0;
2498 xmlSaveErrMemory("creating output");
2499 }
2500
2501 return;
2502 }
2503
2504 /**
2505 * xmlDocDumpMemory:
2506 * @cur: the document
2507 * @mem: OUT: the memory pointer
2508 * @size: OUT: the memory length
2509 *
2510 * Dump an XML document in memory and return the #xmlChar * and it's size
2511 * in bytes. It's up to the caller to free the memory with xmlFree().
2512 * The resulting byte array is zero terminated, though the last 0 is not
2513 * included in the returned size.
2514 */
2515 void
xmlDocDumpMemory(xmlDocPtr cur,xmlChar ** mem,int * size)2516 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2517 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2518 }
2519
2520 /**
2521 * xmlDocDumpFormatMemory:
2522 * @cur: the document
2523 * @mem: OUT: the memory pointer
2524 * @size: OUT: the memory length
2525 * @format: should formatting spaces been added
2526 *
2527 *
2528 * Dump an XML document in memory and return the #xmlChar * and it's size.
2529 * It's up to the caller to free the memory with xmlFree().
2530 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2531 * or xmlKeepBlanksDefault(0) was called
2532 */
2533 void
xmlDocDumpFormatMemory(xmlDocPtr cur,xmlChar ** mem,int * size,int format)2534 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2535 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2536 }
2537
2538 /**
2539 * xmlDocDumpMemoryEnc:
2540 * @out_doc: Document to generate XML text from
2541 * @doc_txt_ptr: Memory pointer for allocated XML text
2542 * @doc_txt_len: Length of the generated XML text
2543 * @txt_encoding: Character encoding to use when generating XML text
2544 *
2545 * Dump the current DOM tree into memory using the character encoding specified
2546 * by the caller. Note it is up to the caller of this function to free the
2547 * allocated memory with xmlFree().
2548 */
2549
2550 void
xmlDocDumpMemoryEnc(xmlDocPtr out_doc,xmlChar ** doc_txt_ptr,int * doc_txt_len,const char * txt_encoding)2551 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2552 int * doc_txt_len, const char * txt_encoding) {
2553 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2554 txt_encoding, 0);
2555 }
2556
2557 /**
2558 * xmlDocFormatDump:
2559 * @f: the FILE*
2560 * @cur: the document
2561 * @format: should formatting spaces been added
2562 *
2563 * Dump an XML document to an open FILE.
2564 *
2565 * returns: the number of bytes written or -1 in case of failure.
2566 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2567 * or xmlKeepBlanksDefault(0) was called
2568 */
2569 int
xmlDocFormatDump(FILE * f,xmlDocPtr cur,int format)2570 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2571 xmlSaveCtxt ctxt;
2572 xmlOutputBufferPtr buf;
2573 const char * encoding;
2574 xmlCharEncodingHandlerPtr handler = NULL;
2575 int ret;
2576
2577 if (cur == NULL) {
2578 #ifdef DEBUG_TREE
2579 xmlGenericError(xmlGenericErrorContext,
2580 "xmlDocDump : document == NULL\n");
2581 #endif
2582 return(-1);
2583 }
2584 encoding = (const char *) cur->encoding;
2585
2586 if (encoding != NULL) {
2587 handler = xmlFindCharEncodingHandler(encoding);
2588 if (handler == NULL) {
2589 xmlFree((char *) cur->encoding);
2590 cur->encoding = NULL;
2591 encoding = NULL;
2592 }
2593 }
2594 buf = xmlOutputBufferCreateFile(f, handler);
2595 if (buf == NULL) return(-1);
2596 memset(&ctxt, 0, sizeof(ctxt));
2597 ctxt.doc = cur;
2598 ctxt.buf = buf;
2599 ctxt.level = 0;
2600 ctxt.format = format ? 1 : 0;
2601 ctxt.encoding = (const xmlChar *) encoding;
2602 xmlSaveCtxtInit(&ctxt);
2603 ctxt.options |= XML_SAVE_AS_XML;
2604 xmlDocContentDumpOutput(&ctxt, cur);
2605
2606 ret = xmlOutputBufferClose(buf);
2607 return(ret);
2608 }
2609
2610 /**
2611 * xmlDocDump:
2612 * @f: the FILE*
2613 * @cur: the document
2614 *
2615 * Dump an XML document to an open FILE.
2616 *
2617 * returns: the number of bytes written or -1 in case of failure.
2618 */
2619 int
xmlDocDump(FILE * f,xmlDocPtr cur)2620 xmlDocDump(FILE *f, xmlDocPtr cur) {
2621 return(xmlDocFormatDump (f, cur, 0));
2622 }
2623
2624 /**
2625 * xmlSaveFileTo:
2626 * @buf: an output I/O buffer
2627 * @cur: the document
2628 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2629 *
2630 * Dump an XML document to an I/O buffer.
2631 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2632 * after this call.
2633 *
2634 * returns: the number of bytes written or -1 in case of failure.
2635 */
2636 int
xmlSaveFileTo(xmlOutputBufferPtr buf,xmlDocPtr cur,const char * encoding)2637 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2638 xmlSaveCtxt ctxt;
2639 int ret;
2640
2641 if (buf == NULL) return(-1);
2642 if (cur == NULL) {
2643 xmlOutputBufferClose(buf);
2644 return(-1);
2645 }
2646 memset(&ctxt, 0, sizeof(ctxt));
2647 ctxt.doc = cur;
2648 ctxt.buf = buf;
2649 ctxt.level = 0;
2650 ctxt.format = 0;
2651 ctxt.encoding = (const xmlChar *) encoding;
2652 xmlSaveCtxtInit(&ctxt);
2653 ctxt.options |= XML_SAVE_AS_XML;
2654 xmlDocContentDumpOutput(&ctxt, cur);
2655 ret = xmlOutputBufferClose(buf);
2656 return(ret);
2657 }
2658
2659 /**
2660 * xmlSaveFormatFileTo:
2661 * @buf: an output I/O buffer
2662 * @cur: the document
2663 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2664 * @format: should formatting spaces been added
2665 *
2666 * Dump an XML document to an I/O buffer.
2667 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2668 * after this call.
2669 *
2670 * returns: the number of bytes written or -1 in case of failure.
2671 */
2672 int
xmlSaveFormatFileTo(xmlOutputBufferPtr buf,xmlDocPtr cur,const char * encoding,int format)2673 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2674 const char *encoding, int format)
2675 {
2676 xmlSaveCtxt ctxt;
2677 int ret;
2678
2679 if (buf == NULL) return(-1);
2680 if ((cur == NULL) ||
2681 ((cur->type != XML_DOCUMENT_NODE) &&
2682 (cur->type != XML_HTML_DOCUMENT_NODE))) {
2683 xmlOutputBufferClose(buf);
2684 return(-1);
2685 }
2686 memset(&ctxt, 0, sizeof(ctxt));
2687 ctxt.doc = cur;
2688 ctxt.buf = buf;
2689 ctxt.level = 0;
2690 ctxt.format = format ? 1 : 0;
2691 ctxt.encoding = (const xmlChar *) encoding;
2692 xmlSaveCtxtInit(&ctxt);
2693 ctxt.options |= XML_SAVE_AS_XML;
2694 xmlDocContentDumpOutput(&ctxt, cur);
2695 ret = xmlOutputBufferClose(buf);
2696 return (ret);
2697 }
2698
2699 /**
2700 * xmlSaveFormatFileEnc:
2701 * @filename: the filename or URL to output
2702 * @cur: the document being saved
2703 * @encoding: the name of the encoding to use or NULL.
2704 * @format: should formatting spaces be added.
2705 *
2706 * Dump an XML document to a file or an URL.
2707 *
2708 * Returns the number of bytes written or -1 in case of error.
2709 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2710 * or xmlKeepBlanksDefault(0) was called
2711 */
2712 int
xmlSaveFormatFileEnc(const char * filename,xmlDocPtr cur,const char * encoding,int format)2713 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2714 const char * encoding, int format ) {
2715 xmlSaveCtxt ctxt;
2716 xmlOutputBufferPtr buf;
2717 xmlCharEncodingHandlerPtr handler = NULL;
2718 int ret;
2719
2720 if (cur == NULL)
2721 return(-1);
2722
2723 if (encoding == NULL)
2724 encoding = (const char *) cur->encoding;
2725
2726 if (encoding != NULL) {
2727
2728 handler = xmlFindCharEncodingHandler(encoding);
2729 if (handler == NULL)
2730 return(-1);
2731 }
2732
2733 #ifdef LIBXML_ZLIB_ENABLED
2734 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2735 #endif
2736 /*
2737 * save the content to a temp buffer.
2738 */
2739 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2740 if (buf == NULL) return(-1);
2741 memset(&ctxt, 0, sizeof(ctxt));
2742 ctxt.doc = cur;
2743 ctxt.buf = buf;
2744 ctxt.level = 0;
2745 ctxt.format = format ? 1 : 0;
2746 ctxt.encoding = (const xmlChar *) encoding;
2747 xmlSaveCtxtInit(&ctxt);
2748 ctxt.options |= XML_SAVE_AS_XML;
2749
2750 xmlDocContentDumpOutput(&ctxt, cur);
2751
2752 ret = xmlOutputBufferClose(buf);
2753 return(ret);
2754 }
2755
2756
2757 /**
2758 * xmlSaveFileEnc:
2759 * @filename: the filename (or URL)
2760 * @cur: the document
2761 * @encoding: the name of an encoding (or NULL)
2762 *
2763 * Dump an XML document, converting it to the given encoding
2764 *
2765 * returns: the number of bytes written or -1 in case of failure.
2766 */
2767 int
xmlSaveFileEnc(const char * filename,xmlDocPtr cur,const char * encoding)2768 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2769 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2770 }
2771
2772 /**
2773 * xmlSaveFormatFile:
2774 * @filename: the filename (or URL)
2775 * @cur: the document
2776 * @format: should formatting spaces been added
2777 *
2778 * Dump an XML document to a file. Will use compression if
2779 * compiled in and enabled. If @filename is "-" the stdout file is
2780 * used. If @format is set then the document will be indented on output.
2781 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2782 * or xmlKeepBlanksDefault(0) was called
2783 *
2784 * returns: the number of bytes written or -1 in case of failure.
2785 */
2786 int
xmlSaveFormatFile(const char * filename,xmlDocPtr cur,int format)2787 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2788 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2789 }
2790
2791 /**
2792 * xmlSaveFile:
2793 * @filename: the filename (or URL)
2794 * @cur: the document
2795 *
2796 * Dump an XML document to a file. Will use compression if
2797 * compiled in and enabled. If @filename is "-" the stdout file is
2798 * used.
2799 * returns: the number of bytes written or -1 in case of failure.
2800 */
2801 int
xmlSaveFile(const char * filename,xmlDocPtr cur)2802 xmlSaveFile(const char *filename, xmlDocPtr cur) {
2803 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2804 }
2805
2806 #endif /* LIBXML_OUTPUT_ENABLED */
2807
2808 #define bottom_xmlsave
2809 #include "elfgcchack.h"
2810