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