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