コード例 #1
0
static void end_element_ns_callback(void *ctx,
  					                        const xmlChar *xlocalname, const xmlChar *xprefix, const xmlChar *xURI)
{
  VALUE handler = (VALUE) ctx;

  if (handler == Qnil)
    return;

  /* Call end element for old-times sake */
  if (rb_respond_to(handler, cbidOnEndElement))
  {
    VALUE name;
    if (xprefix)
    {
      name = rxml_new_cstr(xprefix, NULL);
      rb_str_cat2(name, ":"); 
      rb_str_cat2(name, xlocalname); 
    }
    else
    {
      name = rxml_new_cstr(xlocalname, NULL);
    }
    rb_funcall(handler, cbidOnEndElement, 1, name);
  }

  rb_funcall(handler, cbidOnEndElementNs, 3, 
             rxml_new_cstr(xlocalname, NULL),
             xprefix ? rxml_new_cstr(xprefix, NULL) : Qnil,
             xURI ? rxml_new_cstr(xURI, NULL) : Qnil);
}
コード例 #2
0
static void processing_instruction_callback(void *ctx, const char *target, const char *data)
{
  VALUE handler = (VALUE) ctx;

  if (handler != Qnil)
  {
    VALUE rtarget = target ? rxml_new_cstr(target, NULL) : Qnil;
    VALUE rdata = data ? rxml_new_cstr(data, NULL) : Qnil;
    rb_funcall(handler, cbidOnProcessingInstruction, 2, rtarget, rdata);
  }
}
コード例 #3
0
static void external_subset_callback(void *ctx, const char *name, const char *extid, const char *sysid)
{
  VALUE handler = (VALUE) ctx;

  if (handler != Qnil)
  {
    VALUE rname = name ? rxml_new_cstr(name, NULL) : Qnil;
    VALUE rextid = extid ? rxml_new_cstr(extid, NULL) : Qnil;
    VALUE rsysid = sysid ? rxml_new_cstr(sysid, NULL) : Qnil;
    rb_funcall(handler, cbidOnExternalSubset, 3, rname, rextid, rsysid);
  }
}
コード例 #4
0
ファイル: ruby_xml_node.c プロジェクト: nikitug/libxml-ruby
static VALUE rxml_node_to_s(int argc, VALUE *argv, VALUE self)
{
  VALUE result = Qnil;
  VALUE options = Qnil;
  xmlNodePtr xnode;
  xmlCharEncodingHandlerPtr encodingHandler;
  xmlOutputBufferPtr output;

  int level = 0;
  int indent = 1;
  const char *xencoding = "UTF-8";

  rb_scan_args(argc, argv, "01", &options);

  if (!NIL_P(options))
  {
    VALUE rencoding, rindent, rlevel;
    Check_Type(options, T_HASH);
    rencoding = rb_hash_aref(options, ID2SYM(rb_intern("encoding")));
    rindent = rb_hash_aref(options, ID2SYM(rb_intern("indent")));
    rlevel = rb_hash_aref(options, ID2SYM(rb_intern("level")));

    if (rindent == Qfalse)
      indent = 0;

    if (rlevel != Qnil)
      level = NUM2INT(rlevel);

    if (rencoding != Qnil)
    {
      xencoding = xmlGetCharEncodingName((xmlCharEncoding)NUM2INT(rencoding));
      if (!xencoding)
        rb_raise(rb_eArgError, "Unknown encoding value: %d", NUM2INT(rencoding));
    }
  }

  encodingHandler = xmlFindCharEncodingHandler(xencoding);
  output = xmlAllocOutputBuffer(encodingHandler);

  xnode = rxml_get_xnode(self);

  xmlNodeDumpOutput(output, xnode->doc, xnode, level, indent, xencoding);
  xmlOutputBufferFlush(output);

  if (output->conv)
    result = rxml_new_cstr((const char*) output->conv->content, xencoding);
  else
    result = rxml_new_cstr((const char*) output->buffer->content, xencoding);

  xmlOutputBufferClose(output);
  
  return result;
}
コード例 #5
0
static void start_element_ns_callback(void *ctx, 
                                      const xmlChar *xlocalname, const xmlChar *xprefix, const xmlChar *xURI,
                            		  int nb_namespaces, const xmlChar **xnamespaces,
					                  int nb_attributes, int nb_defaulted, const xmlChar **xattributes)
{
  VALUE handler = (VALUE) ctx;
  VALUE attributes = rb_hash_new();
  VALUE namespaces = rb_hash_new();

  if (handler == Qnil)
    return;

  if (xattributes)
  {
    /* Each attribute is an array of [localname, prefix, URI, value, end] */
    int i;
    for (i = 0;i < nb_attributes * 5; i+=5) 
    {
      VALUE attrName = rxml_new_cstr(xattributes[i+0], NULL);
      VALUE attrValue = rxml_new_cstr_len(xattributes[i+3], xattributes[i+4] - xattributes[i+3], NULL);

      rb_hash_aset(attributes, attrName, attrValue);
    }
  }

  if (xnamespaces)
  {
    int i;
    for (i = 0;i < nb_namespaces * 2; i+=2) 
    {
      VALUE nsPrefix = xnamespaces[i+0] ? rxml_new_cstr(xnamespaces[i+0], NULL) : Qnil;
      VALUE nsURI = xnamespaces[i+1] ? rxml_new_cstr(xnamespaces[i+1], NULL) : Qnil;
      rb_hash_aset(namespaces, nsPrefix, nsURI);
    }
  }

  /* Call start element for old-times sake */
  if (rb_respond_to(handler, cbidOnStartElement))
  {
    VALUE name;
    if (xprefix)
    {
      name = rxml_new_cstr(xprefix, NULL);
      rb_str_cat2(name, ":"); 
      rb_str_cat2(name, xlocalname); 
    }
    else
    {
      name = rxml_new_cstr(xlocalname, NULL);
    }
    rb_funcall(handler, cbidOnStartElement, 2, name, attributes);
  }

  rb_funcall(handler, cbidOnStartElementNs, 5, 
             rxml_new_cstr(xlocalname, NULL),
             attributes,
             xprefix ? rxml_new_cstr(xprefix, NULL) : Qnil,
             xURI ? rxml_new_cstr(xURI, NULL) : Qnil,
             namespaces);
}
コード例 #6
0
/* call-seq:
 *    context.register_namespaces_from_node(node) -> self
 *
 * Helper method to read in namespaces defined on a node.
 *
 *  doc = XML::Document.string('<header><first>hi</first></header>')
 *  context = XPath::Context.new(doc)
 *  context.register_namespaces_from_node(doc.root)
 */
static VALUE rxml_xpath_context_register_namespaces_from_node(VALUE self,
    VALUE node)
{
  xmlXPathContextPtr xctxt;
  xmlNodePtr xnode;
  xmlNsPtr *xnsArr;

  Data_Get_Struct(self, xmlXPathContext, xctxt);

  if (rb_obj_is_kind_of(node, cXMLDocument) == Qtrue)
  {
    xmlDocPtr xdoc;
    Data_Get_Struct(node, xmlDoc, xdoc);
    xnode = xmlDocGetRootElement(xdoc);
  }
  else if (rb_obj_is_kind_of(node, cXMLNode) == Qtrue)
  {
    Data_Get_Struct(node, xmlNode, xnode);
  }
  else
  {
    rb_raise(rb_eTypeError, "The first argument must be a document or node.");
  }

  xnsArr = xmlGetNsList(xnode->doc, xnode);

  if (xnsArr)
  {
    xmlNsPtr xns = *xnsArr;

    while (xns)
    {
      /* If there is no prefix, then this is the default namespace.
       Skip it for now. */
      if (xns->prefix)
      {
        VALUE prefix = rxml_new_cstr(xns->prefix, xctxt->doc->encoding);
        VALUE uri = rxml_new_cstr(xns->href, xctxt->doc->encoding);
        rxml_xpath_context_register_namespace(self, prefix, uri);
      }
      xns = xns->next;
    }
    xmlFree(xnsArr);
  }

  return self;
}
コード例 #7
0
ファイル: ruby_xml_reader.c プロジェクト: GREENMASK/mgr
/*
 * call-seq:
 *    reader.namespace_uri -> URI
 *
 * Determine the namespace URI of the node.
 */
static VALUE rxml_reader_namespace_uri(VALUE self)
{
  xmlTextReaderPtr xReader = rxml_text_reader_get(self);
  const xmlChar *result = xmlTextReaderConstNamespaceUri(xReader);
  const xmlChar *xencoding = xmlTextReaderConstEncoding(xReader);

  return (result == NULL ? Qnil : rxml_new_cstr(result, xencoding));
}
コード例 #8
0
ファイル: ruby_xml_reader.c プロジェクト: GREENMASK/mgr
/*
 * call-seq:
 *    reader.xml_version -> version
 *
 * Determine the XML version of the document being read.
 */
static VALUE rxml_reader_xml_version(VALUE self)
{
  xmlTextReaderPtr xReader = rxml_text_reader_get(self);
  const xmlChar *result = xmlTextReaderConstXmlVersion(xReader);
  const xmlChar *xencoding = xmlTextReaderConstEncoding(xReader);

  return (result == NULL ? Qnil : rxml_new_cstr(result, xencoding));
}
コード例 #9
0
static void comment_callback(void *ctx, const char *msg)
{
  VALUE handler = (VALUE) ctx;

  if (handler != Qnil)
  {
    rb_funcall(handler, cbidOnComment,1,rxml_new_cstr(msg, NULL));
  }
}
コード例 #10
0
static void reference_callback(void *ctx, const char *name)
{
  VALUE handler = (VALUE) ctx;

  if (handler != Qnil)
  {
    rb_funcall(handler, cbidOnReference,1,rxml_new_cstr(name, NULL));
  }
}
コード例 #11
0
ファイル: ruby_xml_encoding.c プロジェクト: GREENMASK/mgr
/*
 * call-seq:
 *    Input.encoding_to_s(Input::ENCODING) -> "encoding"
 *
 * Converts an encoding constant defined on the XML::Encoding
 * class to its text representation.
 */
static VALUE rxml_encoding_to_s(VALUE klass, VALUE encoding)
{
  const char* xencoding = xmlGetCharEncodingName(NUM2INT(encoding));

  if (!xencoding)
    return Qnil;
  else
    return rxml_new_cstr(xencoding, xencoding);
}
コード例 #12
0
ファイル: ruby_xml_dtd.c プロジェクト: boutil/libxml-ruby
/*
 * call-seq:
 *    dtd.uri -> "string"
 *
 * Obtain this dtd's URI (for a SYSTEM or PUBLIC DTD).
 */
static VALUE rxml_dtd_uri_get(VALUE self)
{
  xmlDtdPtr xdtd;
  Data_Get_Struct(self, xmlDtd, xdtd);

  if (xdtd->SystemID == NULL)
    return (Qnil);
  else
    return (rxml_new_cstr( xdtd->SystemID, NULL));
}
コード例 #13
0
/*
 * call-seq:
 *    attr_decl.name -> "name"
 *
 * Obtain this attribute declaration's name.
 */
static VALUE rxml_attr_decl_name_get(VALUE self)
{
  xmlAttributePtr xattr;
  Data_Get_Struct(self, xmlAttribute, xattr);

  if (xattr->name == NULL)
    return Qnil;
  else
    return rxml_new_cstr((const char*) xattr->name, xattr->doc->encoding);
}
コード例 #14
0
/*
 * call-seq:
 *    document.version -> "version"
 *
 * Obtain the XML version specified by this document.
 */
static VALUE rxml_document_version_get(VALUE self)
{
  xmlDocPtr xdoc;

  Data_Get_Struct(self, xmlDoc, xdoc);
  if (xdoc->version == NULL)
    return (Qnil);
  else
    return (rxml_new_cstr((const char*) xdoc->version, NULL));
}
コード例 #15
0
/*
 * call-seq:
 *    xpath_object.string -> String
 *
 * Returns the original XPath expression as a string.
 */
static VALUE rxml_xpath_object_string(VALUE self)
{
  rxml_xpath_object *rxpop;

  Data_Get_Struct(self, rxml_xpath_object, rxpop);

  if (rxpop->xpop->stringval == NULL)
    return Qnil;

  return rxml_new_cstr((const char*) rxpop->xpop->stringval, rxpop->xdoc->encoding);
}
コード例 #16
0
ファイル: ruby_xml_dtd.c プロジェクト: boutil/libxml-ruby
/*
 * call-seq:
 *    dtd.external_id -> "string"
 *
 * Obtain this dtd's external identifer (for a PUBLIC DTD).
 */
static VALUE rxml_dtd_external_id_get(VALUE self)
{
  xmlDtdPtr xdtd;
  Data_Get_Struct(self, xmlDtd, xdtd);


  if (xdtd->ExternalID == NULL)
    return (Qnil);
  else
    return (rxml_new_cstr( xdtd->ExternalID, NULL));
}
コード例 #17
0
/*
 * call-seq:
 *    attr_decl.value -> "value"
 *
 * Obtain the default value of this attribute declaration.
 */
VALUE rxml_attr_decl_value_get(VALUE self)
{
  xmlAttributePtr xattr;

  Data_Get_Struct(self, xmlAttribute, xattr);

  if (xattr->defaultValue)
    return rxml_new_cstr((const char *)xattr->defaultValue, NULL);
  else
    return Qnil;
}
コード例 #18
0
ファイル: ruby_xml_dtd.c プロジェクト: boutil/libxml-ruby
/*
 * call-seq:
 *    dtd.name -> "string"
 *
 * Obtain this dtd's name.
 */
static VALUE rxml_dtd_name_get(VALUE self)
{
  xmlDtdPtr xdtd;
  Data_Get_Struct(self, xmlDtd, xdtd);


  if (xdtd->name == NULL)
    return (Qnil);
  else
    return (rxml_new_cstr( xdtd->name, NULL));
}
コード例 #19
0
ファイル: ruby_xml_node.c プロジェクト: nikitug/libxml-ruby
/*
 * call-seq:
 *    node.path -> path
 *
 * Obtain this node's path.
 */
static VALUE rxml_node_path(VALUE self)
{
  xmlNodePtr xnode;
  xmlChar *path;

  xnode = rxml_get_xnode(self);
  path = xmlGetNodePath(xnode);

  if (path == NULL)
    return (Qnil);
  else
    return (rxml_new_cstr((const char*) path, NULL));
}
コード例 #20
0
ファイル: ruby_xml_node.c プロジェクト: nikitug/libxml-ruby
/*
 * call-seq:
 *    node.xlink_type_name -> "string"
 *
 * Obtain the type name for this xlink, if applicable.
 * If this is not an xlink node (see +xlink?+), will return
 * nil.
 */
static VALUE rxml_node_xlink_type_name(VALUE self)
{
  xmlNodePtr xnode;
  xlinkType xlt;

  xnode = rxml_get_xnode(self);
  xlt = xlinkIsLink(xnode->doc, xnode);

  switch (xlt)
  {
  case XLINK_TYPE_NONE:
    return (Qnil);
  case XLINK_TYPE_SIMPLE:
    return (rxml_new_cstr("simple", NULL));
  case XLINK_TYPE_EXTENDED:
    return (rxml_new_cstr("extended", NULL));
  case XLINK_TYPE_EXTENDED_SET:
    return (rxml_new_cstr("extended_set", NULL));
  default:
    rb_fatal("Unknowng xlink type, %d", xlt);
  }
}
コード例 #21
0
ファイル: ruby_xml_reader.c プロジェクト: GREENMASK/mgr
/*
 * call-seq:
 *    reader.lookup_namespace(prefix) -> value
 *
 * Resolve a namespace prefix in the scope of the current element.
 * To return the default namespace, specify nil as +prefix+.
 */
static VALUE rxml_reader_lookup_namespace(VALUE self, VALUE prefix)
{
  VALUE result = Qnil;
  xmlTextReaderPtr xReader = rxml_text_reader_get(self);
  const xmlChar *xnamespace = xmlTextReaderLookupNamespace(xReader, (const xmlChar *) StringValueCStr(prefix));
  const xmlChar *xencoding = xmlTextReaderConstEncoding(xReader);

  if (xnamespace)
  {
    result = rxml_new_cstr((const char*)xnamespace, (const char*)xencoding);
    xmlFree((void *)xnamespace);
  }
  return result;
}
コード例 #22
0
ファイル: ruby_xml_node.c プロジェクト: nikitug/libxml-ruby
/*
 * call-seq:
 *    node.content -> "string"
 *
 * Obtain this node's content as a string.
 */
static VALUE rxml_node_content_get(VALUE self)
{
  xmlNodePtr xnode;
  xmlChar *content;
  VALUE result = Qnil;

  xnode = rxml_get_xnode(self);
  content = xmlNodeGetContent(xnode);
  if (content)
  {
    result = rxml_new_cstr((const char *) content, NULL);
    xmlFree(content);
  }

  return result;
}
コード例 #23
0
ファイル: ruby_xml_reader.c プロジェクト: GREENMASK/mgr
/*
 * call-seq:
 *    reader.read_string -> string
 *
 * Read the contents of an element or a text node as a string.
 *
 * Return a string containing the contents of the Element or Text node, or nil
 * if the reader is positioned on any other type of node.
 */
static VALUE rxml_reader_read_string(VALUE self)
{
  VALUE result = Qnil;
  xmlTextReaderPtr xReader = rxml_text_reader_get(self);

  xmlChar *xml = xmlTextReaderReadString(xReader);

  if (xml)
  {
    const xmlChar *xencoding = xmlTextReaderConstEncoding(xReader);
    result = rxml_new_cstr((const char*) xml, xencoding);
    xmlFree(xml);
  }

  return result;
}
コード例 #24
0
ファイル: ruby_xml_node.c プロジェクト: nikitug/libxml-ruby
/*
 * call-seq:
 *    node.lang -> "string"
 *
 * Obtain the language set for this node, if any.
 * This is set in XML via the xml:lang attribute.
 */
static VALUE rxml_node_lang_get(VALUE self)
{
  xmlNodePtr xnode;
  xmlChar *lang;
  VALUE result = Qnil;

  xnode = rxml_get_xnode(self);
  lang = xmlNodeGetLang(xnode);

  if (lang)
  {
    result = rxml_new_cstr((const char*) lang, NULL);
    xmlFree(lang);
  }

  return (result);
}
コード例 #25
0
/*
 * call-seq:
 *    context.register_namespaces(["prefix:uri"]) -> self
 *
 * Register the specified namespaces in this context.  There are
 * three different forms that libxml accepts.  These include
 * a string, an array of strings, or a hash table:
 *
 *   context.register_namespaces('xi:http://www.w3.org/2001/XInclude')
 *   context.register_namespaces(['xlink:http://www.w3.org/1999/xlink',
 *                                'xi:http://www.w3.org/2001/XInclude')
 *   context.register_namespaces('xlink' => 'http://www.w3.org/1999/xlink',
 *                                  'xi' => 'http://www.w3.org/2001/XInclude')
 */
static VALUE rxml_xpath_context_register_namespaces(VALUE self, VALUE nslist)
{
  char *cp;
  long i;
  VALUE rprefix, ruri;
  xmlXPathContextPtr xctxt;

  Data_Get_Struct(self, xmlXPathContext, xctxt);

  /* Need to loop through the 2nd argument and iterate through the
   * list of namespaces that we want to allow */
  switch (TYPE(nslist))
  {
  case T_STRING:
    cp = strchr(StringValuePtr(nslist), (int) ':');
    if (cp == NULL)
    {
      rprefix = nslist;
      ruri = Qnil;
    }
    else
    {
      rprefix = rb_str_new(StringValuePtr(nslist), (long) ((intptr_t) cp - (intptr_t)StringValuePtr(nslist)));
      ruri = rxml_new_cstr((const xmlChar*)&cp[1], xctxt->doc->encoding);
    }
    /* Should test the results of this */
    rxml_xpath_context_register_namespace(self, rprefix, ruri);
    break;
  case T_ARRAY:
    for (i = 0; i < RARRAY_LEN(nslist); i++)
    {
      rxml_xpath_context_register_namespaces(self, RARRAY_PTR(nslist)[i]);
    }
    break;
  case T_HASH:
    rb_hash_foreach(nslist, iterate_ns_hash, self);
    break;
  default:
    rb_raise(
        rb_eArgError,
        "Invalid argument type, only accept string, array of strings, or an array of arrays");
  }
  return self;
}
コード例 #26
0
ファイル: ruby_xml_node.c プロジェクト: nikitug/libxml-ruby
/*
 * call-seq:
 *    node.base_uri -> "uri"
 *
 * Obtain this node's base URI.
 */
static VALUE rxml_node_base_uri_get(VALUE self)
{
  xmlNodePtr xnode;
  xmlChar* base_uri;
  VALUE result = Qnil;

  xnode = rxml_get_xnode(self);

  if (xnode->doc == NULL)
    return (result);

  base_uri = xmlNodeGetBase(xnode->doc, xnode);
  if (base_uri)
  {
    result = rxml_new_cstr((const char*) base_uri, NULL);
    xmlFree(base_uri);
  }

  return (result);
}
コード例 #27
0
ファイル: ruby_xml_node.c プロジェクト: nikitug/libxml-ruby
/*
 * call-seq:
 *    node.name -> "string"
 *
 * Obtain this node's name.
 */
static VALUE rxml_node_name_get(VALUE self)
{
  xmlNodePtr xnode;
  const xmlChar *name;

  xnode = rxml_get_xnode(self);

  switch (xnode->type)
  {
  case XML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
    case XML_DOCB_DOCUMENT_NODE:
#endif
  case XML_HTML_DOCUMENT_NODE:
  {
    xmlDocPtr doc = (xmlDocPtr) xnode;
    name = doc->URL;
    break;
  }
  case XML_ATTRIBUTE_NODE:
  {
    xmlAttrPtr attr = (xmlAttrPtr) xnode;
    name = attr->name;
    break;
  }
  case XML_NAMESPACE_DECL:
  {
    xmlNsPtr ns = (xmlNsPtr) xnode;
    name = ns->prefix;
    break;
  }
  default:
    name = xnode->name;
    break;
  }

  if (xnode->name == NULL)
    return (Qnil);
  else
    return (rxml_new_cstr((const char*) name, NULL));
}
コード例 #28
0
ファイル: ruby_xml_xpath.c プロジェクト: boutil/libxml-ruby
VALUE
rxml_xpath_to_value(xmlXPathContextPtr xctxt, xmlXPathObjectPtr xobject) {
  VALUE result;
  int type;

  if (xobject == NULL) {
    /* xmlLastError is different than xctxt->lastError.  Use
     xmlLastError since it has the message set while xctxt->lastError
     does not. */
    xmlErrorPtr xerror = xmlGetLastError();
    rxml_raise(xerror);
  }

  switch (type = xobject->type) {
    case XPATH_NODESET:
      result = rxml_xpath_object_wrap(xctxt->doc, xobject);
      break;
    case XPATH_BOOLEAN:
      result = (xobject->boolval != 0) ? Qtrue : Qfalse;
      xmlXPathFreeObject(xobject);
      break;
    case XPATH_NUMBER:
      result = rb_float_new(xobject->floatval);
      xmlXPathFreeObject(xobject);
      break;
    case XPATH_STRING:
      result = rxml_new_cstr(xobject->stringval, xctxt->doc->encoding);
      xmlXPathFreeObject(xobject);
      break;
    default:
      xmlXPathFreeObject(xobject);
      rb_raise(rb_eTypeError,
        "can't convert XPath object of type %d to Ruby value", type
      );
  }

  return result;
}
コード例 #29
0
ファイル: ruby_xml_reader.c プロジェクト: GREENMASK/mgr
/*
 * call-seq:
 *    reader[key] -> value
 *
 * Provide the value of the attribute with the specified index (if +key+ is an
 * integer) or with the specified name (if +key+ is a string) relative to the
 * containing element, as a string.
 */
static VALUE rxml_reader_attribute(VALUE self, VALUE key)
{
  VALUE result = Qnil;
  xmlChar *xattr;
  xmlTextReaderPtr xReader = rxml_text_reader_get(self);
  const xmlChar *xencoding = xmlTextReaderConstEncoding(xReader);

  if (TYPE(key) == T_FIXNUM)
  {
    xattr = xmlTextReaderGetAttributeNo(xReader, FIX2INT(key));
  }
  else
  {
    xattr = xmlTextReaderGetAttribute(xReader, (const xmlChar *) StringValueCStr(key));
  }

  if (xattr)
  {
    result = rxml_new_cstr(xattr, xencoding);
    xmlFree(xattr);
  }
  return result;
}
コード例 #30
0
/*
 * call-seq:
 *    document.to_s -> "string"
 *    document.to_s(:indent => true, :encoding => XML::Encoding::UTF_8) -> "string"
 *
 * Converts a document, and all of its children, to a string representation.
 * You may provide an optional hash table to control how the string is
 * generated.  Valid options are:
 *
 * :indent - Specifies if the string should be indented.  The default value
 * is true.  Note that indentation is only added if both :indent is
 * true and XML.indent_tree_output is true.  If :indent is set to false,
 * then both indentation and line feeds are removed from the result.
 *
 * :encoding - Specifies the output encoding of the string.  It
 * defaults to XML::Encoding::UTF8.  To change it, use one of the
 * XML::Encoding encoding constants. */
static VALUE rxml_document_to_s(int argc, VALUE *argv, VALUE self)
{
  VALUE result;
  VALUE options = Qnil;
  xmlDocPtr xdoc;
  int indent = 1;
  const char *xencoding = "UTF-8";
  xmlChar *buffer;
  int length;

  rb_scan_args(argc, argv, "01", &options);

  if (!NIL_P(options))
  {
    VALUE rencoding, rindent;
    Check_Type(options, T_HASH);
    rencoding = rb_hash_aref(options, ID2SYM(rb_intern("encoding")));
    rindent = rb_hash_aref(options, ID2SYM(rb_intern("indent")));

    if (rindent == Qfalse)
      indent = 0;

    if (rencoding != Qnil)
    {
      xencoding = xmlGetCharEncodingName((xmlCharEncoding)NUM2INT(rencoding));
      if (!xencoding)
        rb_raise(rb_eArgError, "Unknown encoding value: %d", NUM2INT(rencoding));
    }
  }

  Data_Get_Struct(self, xmlDoc, xdoc);
  xmlDocDumpFormatMemoryEnc(xdoc, &buffer, &length, xencoding, indent);

  result = rxml_new_cstr((const char*) buffer, xencoding);
  xmlFree(buffer);
  return result;
}