Esempio n. 1
0
/*
 * call-seq:
 *  dup
 *  dup(depth)
 *  dup(depth, new_parent_doc)
 *
 * Copy this node.
 * An optional depth may be passed in. 0 is a shallow copy, 1 (the default) is a deep copy.
 * An optional new_parent_doc may also be passed in, which will be the new
 * node's parent document. Defaults to the current node's document.
 * current document.
 */
static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
{
  VALUE r_level, r_new_parent_doc;
  int level;
  int n_args;
  xmlDocPtr new_parent_doc;
  xmlNodePtr node, dup;

  Data_Get_Struct(self, xmlNode, node);

  n_args = rb_scan_args(argc, argv, "02", &r_level, &r_new_parent_doc);

  if (n_args < 1) {
    r_level = INT2NUM((long)1);
  }
  level = (int)NUM2INT(r_level);

  if (n_args < 2) {
    new_parent_doc = node->doc;
  } else {
    Data_Get_Struct(r_new_parent_doc, xmlDoc, new_parent_doc);
  }

  dup = xmlDocCopyNode(node, new_parent_doc, level);
  if(dup == NULL) { return Qnil; }

  nokogiri_root_node(dup);

  return Nokogiri_wrap_xml_node(rb_obj_class(self), dup);
}
Esempio n. 2
0
/*
 * call-seq:
 *  create_external_subset(name, external_id, system_id)
 *
 * Create an external subset
 */
static VALUE create_external_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
{
  xmlNodePtr node;
  xmlDocPtr doc;
  xmlDtdPtr dtd;

  Data_Get_Struct(self, xmlNode, node);

  doc = node->doc;

  if(doc->extSubset) {
    rb_raise(rb_eRuntimeError, "Document already has an external subset");
  }

  dtd = xmlNewDtd(
          doc,
          NIL_P(name)        ? NULL : (const xmlChar *)StringValueCStr(name),
          NIL_P(external_id) ? NULL : (const xmlChar *)StringValueCStr(external_id),
          NIL_P(system_id)   ? NULL : (const xmlChar *)StringValueCStr(system_id)
        );

  if(!dtd) { return Qnil; }

  return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
}
Esempio n. 3
0
static void element_copier(void *_payload, void *data, xmlChar *name)
{
  VALUE hash = (VALUE)data;
  xmlNodePtr payload = (xmlNodePtr)_payload;

  VALUE element = Nokogiri_wrap_xml_node(Qnil, payload);

  rb_hash_aset(hash, NOKOGIRI_STR_NEW2(name), element);
}
Esempio n. 4
0
/*
 * call-seq:
 *  root
 *
 * Get the root node for this document.
 */
static VALUE root(VALUE self)
{
    xmlDocPtr doc;
    Data_Get_Struct(self, xmlDoc, doc);

    xmlNodePtr root = xmlDocGetRootElement(doc);

    if(!root) return Qnil;
    return Nokogiri_wrap_xml_node(Qnil, root) ;
}
Esempio n. 5
0
/*
 * call-seq:
 *  previous_sibling
 *
 * Returns the previous sibling node
 */
static VALUE previous_sibling(VALUE self)
{
  xmlNodePtr node, sibling;
  Data_Get_Struct(self, xmlNode, node);

  sibling = node->prev;
  if(!sibling) return Qnil;

  return Nokogiri_wrap_xml_node(Qnil, sibling);
}
Esempio n. 6
0
/*
 * call-seq:
 *   attribute(name)
 *
 * Get the attribute node with +name+
 */
static VALUE attr(VALUE self, VALUE name)
{
  xmlNodePtr node;
  xmlAttrPtr prop;
  Data_Get_Struct(self, xmlNode, node);
  prop = xmlHasProp(node, (xmlChar *)StringValuePtr(name));

  if(! prop) return Qnil;
  return Nokogiri_wrap_xml_node((xmlNodePtr)prop);
}
Esempio n. 7
0
/*
 * call-seq:
 *  next_sibling
 *
 * Returns the next sibling node
 */
static VALUE next_sibling(VALUE self)
{
  xmlNodePtr node, sibling;
  Data_Get_Struct(self, xmlNode, node);

  sibling = node->next;
  if(!sibling) return Qnil;

  return Nokogiri_wrap_xml_node(sibling) ;
}
Esempio n. 8
0
/*
 * call-seq:
 *  parent
 *
 * Get the parent Node for this Node
 */
static VALUE get_parent(VALUE self)
{
  xmlNodePtr node, parent;
  Data_Get_Struct(self, xmlNode, node);

  parent = node->parent;
  if(!parent) return Qnil;

  return Nokogiri_wrap_xml_node(parent) ;
}
Esempio n. 9
0
/*
 * call-seq:
 *  next_element
 *
 * Returns the next Nokogiri::XML::Element type sibling node.
 */
static VALUE next_element(VALUE self)
{
  xmlNodePtr node, sibling;
  Data_Get_Struct(self, xmlNode, node);

  sibling = xmlNextElementSibling(node);
  if(!sibling) return Qnil;

  return Nokogiri_wrap_xml_node(Qnil, sibling);
}
Esempio n. 10
0
/*
 * call-seq:
 *  child
 *
 * Returns the child node
 */
static VALUE child(VALUE self)
{
  xmlNodePtr node, child;
  Data_Get_Struct(self, xmlNode, node);

  child = node->children;
  if(!child) return Qnil;

  return Nokogiri_wrap_xml_node(Qnil, child);
}
Esempio n. 11
0
/*
 * call-seq:
 *  last_element_child
 *
 * Returns the last child node of this node that is an element.
 *
 * Example:
 *
 *   @doc.root.last_element_child.element? # => true
 */
static VALUE last_element_child(VALUE self)
{
  xmlNodePtr node, child;
  Data_Get_Struct(self, xmlNode, node);

  child = xmlLastElementChild(node);
  if(!child) return Qnil;

  return Nokogiri_wrap_xml_node(Qnil, child);
}
Esempio n. 12
0
/*
 * call-seq:
 *  previous_element
 *
 * Returns the previous Nokogiri::XML::Element type sibling node.
 */
static VALUE previous_element(VALUE self)
{
  xmlNodePtr node, sibling;
  Data_Get_Struct(self, xmlNode, node);

  sibling = node->prev;
  if(!sibling) return Qnil;

  while(sibling && sibling->type != XML_ELEMENT_NODE)
    sibling = sibling->prev;

  return sibling ? Nokogiri_wrap_xml_node(Qnil, sibling) : Qnil ;
}
Esempio n. 13
0
/*
 * call-seq:
 *  dup
 *
 * Copy this node
 */
static VALUE duplicate_node(VALUE self)
{
  xmlNodePtr node, dup;
  Data_Get_Struct(self, xmlNode, node);

  dup = xmlCopyNode(node, 1);
  if(dup == NULL) return Qnil;
  dup->doc = node->doc;
  assert(node->parent);

  xmlAddChild(node->parent, dup);

  return Nokogiri_wrap_xml_node(dup);
}
Esempio n. 14
0
/*
 * call-seq:
 *  internal_subset
 *
 * Get the internal subset
 */
static VALUE internal_subset(VALUE self)
{
  xmlNodePtr node;
  xmlDocPtr doc;
  Data_Get_Struct(self, xmlNode, node);

  if(!node->doc) return Qnil;

  doc = node->doc;

  if(!doc->intSubset) return Qnil;

  return Nokogiri_wrap_xml_node((xmlNodePtr)doc->intSubset);
}
Esempio n. 15
0
/*
 * call-seq:
 *  dup
 *
 * Copy this node.  An optional depth may be passed in, but it defaults
 * to a deep copy.  0 is a shallow copy, 1 is a deep copy.
 */
static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
{
  VALUE level;

  if(rb_scan_args(argc, argv, "01", &level) == 0)
    level = INT2NUM(1);

  xmlNodePtr node, dup;
  Data_Get_Struct(self, xmlNode, node);

  dup = xmlDocCopyNode(node, node->doc, NUM2INT(level));
  if(dup == NULL) return Qnil;

  return Nokogiri_wrap_xml_node(dup);
}
Esempio n. 16
0
/*
 * call-seq:
 *  internal_subset
 *
 * Get the internal subset
 */
static VALUE internal_subset(VALUE self)
{
  xmlNodePtr node;
  xmlDocPtr doc;
  Data_Get_Struct(self, xmlNode, node);

  if(!node->doc) return Qnil;

  doc = node->doc;
  xmlDtdPtr dtd = xmlGetIntSubset(doc);

  if(!dtd) return Qnil;

  return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
}
Esempio n. 17
0
/*
 * call-seq:
 *  previous_element
 *
 * Returns the previous Nokogiri::XML::Element type sibling node.
 */
static VALUE previous_element(VALUE self)
{
  xmlNodePtr node, sibling;
  Data_Get_Struct(self, xmlNode, node);

  /*
   *  note that we don't use xmlPreviousElementSibling here because it's buggy pre-2.7.7.
   */
  sibling = node->prev;
  if(!sibling) return Qnil;

  while(sibling && sibling->type != XML_ELEMENT_NODE)
    sibling = sibling->prev;

  return sibling ? Nokogiri_wrap_xml_node(Qnil, sibling) : Qnil ;
}
Esempio n. 18
0
/*
 * call-seq:
 *  external_subset
 *
 * Get the external subset
 */
static VALUE external_subset(VALUE self)
{
  xmlNodePtr node;
  xmlDocPtr doc;
  xmlDtdPtr dtd;

  Data_Get_Struct(self, xmlNode, node);

  if(!node->doc) { return Qnil; }

  doc = node->doc;
  dtd = doc->extSubset;

  if(!dtd) { return Qnil; }

  return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
}
Esempio n. 19
0
/*
 * call-seq:
 *  dup
 *
 * Copy this node.  An optional depth may be passed in, but it defaults
 * to a deep copy.  0 is a shallow copy, 1 is a deep copy.
 */
static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
{
  VALUE level;
  xmlNodePtr node, dup;

  if(rb_scan_args(argc, argv, "01", &level) == 0)
    level = INT2NUM((long)1);

  Data_Get_Struct(self, xmlNode, node);

  dup = xmlDocCopyNode(node, node->doc, (int)NUM2INT(level));
  if(dup == NULL) return Qnil;

  NOKOGIRI_ROOT_NODE(dup);

  return Nokogiri_wrap_xml_node(rb_obj_class(self), dup);
}
Esempio n. 20
0
/* :nodoc: */
static VALUE reparent_node_with(VALUE node_obj, VALUE other_obj, node_other_func func)
{
  VALUE reparented_obj ;
  xmlNodePtr node, other, reparented ;

  if(!rb_obj_is_kind_of(node_obj, cNokogiriXmlNode))
    rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");

  Data_Get_Struct(node_obj, xmlNode, node);
  Data_Get_Struct(other_obj, xmlNode, other);

  if(XML_DOCUMENT_NODE == node->type || XML_HTML_DOCUMENT_NODE == node->type)
    rb_raise(rb_eArgError, "cannot reparent a document node");

  if(node->type == XML_TEXT_NODE) {
    NOKOGIRI_ROOT_NODE(node);
    node = xmlDocCopyNode(node, other->doc, 1);
  }

  if (node->doc == other->doc) {
    xmlUnlinkNode(node) ;

    // TODO: I really want to remove this.  We shouldn't support 2.6.16 anymore
    if ( node->type == XML_TEXT_NODE
         && other->type == XML_TEXT_NODE
         && is_2_6_16() ) {

      // we'd rather leak than segfault.
      other->content = xmlStrdup(other->content);

    }

    if(!(reparented = (*func)(other, node))) {
      rb_raise(rb_eRuntimeError, "Could not reparent node (%s:%d)", __FILE__, __LINE__);
    }
  } else {
    xmlNodePtr duped_node ;
    // recursively copy to the new document
    if (!(duped_node = xmlDocCopyNode(node, other->doc, 1))) {
      rb_raise(rb_eRuntimeError, "Could not reparent node (xmlDocCopyNode)");
    }
    if(!(reparented = (*func)(other, duped_node))) {
      rb_raise(rb_eRuntimeError, "Could not reparent node (%s:%d)", __FILE__, __LINE__);
    }
    xmlUnlinkNode(node);
    NOKOGIRI_ROOT_NODE(node);
  }

  // the child was a text node that was coalesced. we need to have the object
  // point at SOMETHING, or we'll totally bomb out.
  if (reparented != node) {
    DATA_PTR(node_obj) = reparented ;
  }

  // Appropriately link in namespaces
  relink_namespace(reparented);

  reparented_obj = Nokogiri_wrap_xml_node(Qnil, reparented);

  rb_funcall(reparented_obj, decorate_bang, 0);

  return reparented_obj ;
}
Esempio n. 21
0
/* :nodoc: */
static VALUE reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_reparentee_func prf)
{
  VALUE reparented_obj ;
  xmlNodePtr reparentee, pivot, reparented, next_text, new_next_text ;

  if(!rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlNode))
    rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
  if(rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlDocument))
    rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");

  Data_Get_Struct(reparentee_obj, xmlNode, reparentee);
  Data_Get_Struct(pivot_obj, xmlNode, pivot);

  if(XML_DOCUMENT_NODE == reparentee->type || XML_HTML_DOCUMENT_NODE == reparentee->type)
    rb_raise(rb_eArgError, "cannot reparent a document node");

  xmlUnlinkNode(reparentee);

  if (reparentee->doc != pivot->doc || reparentee->type == XML_TEXT_NODE) {
    /*
     *  if the reparentee is a text node, there's a very good chance it will be
     *  merged with an adjacent text node after being reparented, and in that case
     *  libxml will free the underlying C struct.
     *
     *  since we clearly have a ruby object which references the underlying
     *  memory, we can't let the C struct get freed. let's pickle the original
     *  reparentee by rooting it; and then we'll reparent a duplicate of the
     *  node that we don't care about preserving.
     *
     *  alternatively, if the reparentee is from a different document than the
     *  pivot node, libxml2 is going to get confused about which document's
     *  "dictionary" the node's strings belong to (this is an otherwise
     *  uninteresting libxml2 implementation detail). as a result, we cannot
     *  reparent the actual reparentee, so we reparent a duplicate.
     */
    nokogiri_root_node(reparentee);
    if (!(reparentee = xmlDocCopyNode(reparentee, pivot->doc, 1))) {
      rb_raise(rb_eRuntimeError, "Could not reparent node (xmlDocCopyNode)");
    }
  }

  if (prf != xmlAddPrevSibling && prf != xmlAddNextSibling
      && reparentee->type == XML_TEXT_NODE && pivot->next && pivot->next->type == XML_TEXT_NODE) {
    /*
     *  libxml merges text nodes in a right-to-left fashion, meaning that if
     *  there are two text nodes who would be adjacent, the right (or following,
     *  or next) node will be merged into the left (or preceding, or previous)
     *  node.
     *
     *  and by "merged" I mean the string contents will be concatenated onto the
     *  left node's contents, and then the node will be freed.
     *
     *  which means that if we have a ruby object wrapped around the right node,
     *  its memory would be freed out from under it.
     *
     *  so, we detect this edge case and unlink-and-root the text node before it gets
     *  merged. then we dup the node and insert that duplicate back into the
     *  document where the real node was.
     *
     *  yes, this is totally lame.
     */
    next_text     = pivot->next ;
    new_next_text = xmlDocCopyNode(next_text, pivot->doc, 1) ;

    xmlUnlinkNode(next_text);
    nokogiri_root_node(next_text);

    xmlAddNextSibling(pivot, new_next_text);
  }

  if(!(reparented = (*prf)(pivot, reparentee))) {
    rb_raise(rb_eRuntimeError, "Could not reparent node");
  }

  /*
   *  make sure the ruby object is pointed at the just-reparented node, which
   *  might be a duplicate (see above) or might be the result of merging
   *  adjacent text nodes.
   */
  DATA_PTR(reparentee_obj) = reparented ;

  relink_namespace(reparented);

  reparented_obj = Nokogiri_wrap_xml_node(Qnil, reparented);

  rb_funcall(reparented_obj, decorate_bang, 0);

  return reparented_obj ;
}
Esempio n. 22
0
/* :nodoc: */
static VALUE reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_reparentee_func prf)
{
  VALUE reparented_obj ;
  xmlNodePtr reparentee, pivot, reparented, next_text, new_next_text, parent ;

  if(!rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlNode))
    rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
  if(rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlDocument))
    rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");

  Data_Get_Struct(reparentee_obj, xmlNode, reparentee);
  Data_Get_Struct(pivot_obj, xmlNode, pivot);

  /*
   * Check if nodes given are appropriate to have a parent-child
   * relationship, based on the DOM specification.
   *
   * cf. http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-1590626202
   */
  if (prf == xmlAddChild) {
    parent = pivot;
  } else {
    parent = pivot->parent;
  }

  if (parent) {
    switch (parent->type) {
    case XML_DOCUMENT_NODE:
    case XML_HTML_DOCUMENT_NODE:
      switch (reparentee->type) {
        case XML_ELEMENT_NODE:
        case XML_PI_NODE:
        case XML_COMMENT_NODE:
        case XML_DOCUMENT_TYPE_NODE:
      /*
       * The DOM specification says no to adding text-like nodes
       * directly to a document, but we allow it for compatibility.
       */
        case XML_TEXT_NODE:
        case XML_CDATA_SECTION_NODE:
        case XML_ENTITY_REF_NODE:
          goto ok;
      }
      break;
    case XML_DOCUMENT_FRAG_NODE:
    case XML_ENTITY_REF_NODE:
    case XML_ELEMENT_NODE:
      switch (reparentee->type) {
      case XML_ELEMENT_NODE:
      case XML_PI_NODE:
      case XML_COMMENT_NODE:
      case XML_TEXT_NODE:
      case XML_CDATA_SECTION_NODE:
      case XML_ENTITY_REF_NODE:
        goto ok;
      }
      break;
    case XML_ATTRIBUTE_NODE:
      switch (reparentee->type) {
      case XML_TEXT_NODE:
      case XML_ENTITY_REF_NODE:
        goto ok;
      }
      break;
    case XML_TEXT_NODE:
      /*
       * xmlAddChild() breaks the DOM specification in that it allows
       * adding a text node to another, in which case text nodes are
       * coalesced, but since our JRuby version does not support such
       * operation, we should inhibit it.
       */
      break;
    }

    rb_raise(rb_eArgError, "cannot reparent %s there", rb_obj_classname(reparentee_obj));
  }

ok:
  xmlUnlinkNode(reparentee);

  if (reparentee->doc != pivot->doc || reparentee->type == XML_TEXT_NODE) {
    /*
     *  if the reparentee is a text node, there's a very good chance it will be
     *  merged with an adjacent text node after being reparented, and in that case
     *  libxml will free the underlying C struct.
     *
     *  since we clearly have a ruby object which references the underlying
     *  memory, we can't let the C struct get freed. let's pickle the original
     *  reparentee by rooting it; and then we'll reparent a duplicate of the
     *  node that we don't care about preserving.
     *
     *  alternatively, if the reparentee is from a different document than the
     *  pivot node, libxml2 is going to get confused about which document's
     *  "dictionary" the node's strings belong to (this is an otherwise
     *  uninteresting libxml2 implementation detail). as a result, we cannot
     *  reparent the actual reparentee, so we reparent a duplicate.
     */
    nokogiri_root_node(reparentee);
    if (!(reparentee = xmlDocCopyNode(reparentee, pivot->doc, 1))) {
      rb_raise(rb_eRuntimeError, "Could not reparent node (xmlDocCopyNode)");
    }
  }

  if (prf != xmlAddPrevSibling && prf != xmlAddNextSibling
      && reparentee->type == XML_TEXT_NODE && pivot->next && pivot->next->type == XML_TEXT_NODE) {
    /*
     *  libxml merges text nodes in a right-to-left fashion, meaning that if
     *  there are two text nodes who would be adjacent, the right (or following,
     *  or next) node will be merged into the left (or preceding, or previous)
     *  node.
     *
     *  and by "merged" I mean the string contents will be concatenated onto the
     *  left node's contents, and then the node will be freed.
     *
     *  which means that if we have a ruby object wrapped around the right node,
     *  its memory would be freed out from under it.
     *
     *  so, we detect this edge case and unlink-and-root the text node before it gets
     *  merged. then we dup the node and insert that duplicate back into the
     *  document where the real node was.
     *
     *  yes, this is totally lame.
     */
    next_text     = pivot->next ;
    new_next_text = xmlDocCopyNode(next_text, pivot->doc, 1) ;

    xmlUnlinkNode(next_text);
    nokogiri_root_node(next_text);

    xmlAddNextSibling(pivot, new_next_text);
  }

  if(!(reparented = (*prf)(pivot, reparentee))) {
    rb_raise(rb_eRuntimeError, "Could not reparent node");
  }

  /*
   *  make sure the ruby object is pointed at the just-reparented node, which
   *  might be a duplicate (see above) or might be the result of merging
   *  adjacent text nodes.
   */
  DATA_PTR(reparentee_obj) = reparented ;

  relink_namespace(reparented);

  reparented_obj = Nokogiri_wrap_xml_node(Qnil, reparented);

  rb_funcall(reparented_obj, decorate_bang, 0);

  return reparented_obj ;
}
Esempio n. 23
0
 */
static VALUE new(int argc, VALUE *argv, VALUE klass)
{
  xmlDocPtr doc;
  VALUE string;
  VALUE document;
  VALUE rest;

  rb_scan_args(argc, argv, "2*", &string, &document, &rest);

  Data_Get_Struct(document, xmlDoc, doc);

  xmlNodePtr node = xmlNewText((xmlChar *)StringValuePtr(string));
  node->doc = doc;

  VALUE rb_node = Nokogiri_wrap_xml_node(klass, node) ;
  rb_obj_call_init(rb_node, argc, argv);

  if(rb_block_given_p()) rb_yield(rb_node);

  return rb_node;
}

VALUE cNokogiriXmlText ;
void init_xml_text()
{
  VALUE nokogiri = rb_define_module("Nokogiri");
  VALUE xml = rb_define_module_under(nokogiri, "XML");
  /* */
  VALUE node = rb_define_class_under(xml, "Node", rb_cObject);
  VALUE char_data = rb_define_class_under(xml, "CharacterData", node);
Esempio n. 24
0
  xmlAttrPtr node;
  VALUE rb_node;

  rb_scan_args(argc, argv, "2*", &document, &name, &rest);

  Data_Get_Struct(document, xmlDoc, xml_doc);

  node = xmlNewDocProp(
      xml_doc,
      (const xmlChar *)StringValuePtr(name),
      NULL
  );

  NOKOGIRI_ROOT_NODE((xmlNodePtr)node);

  rb_node = Nokogiri_wrap_xml_node(klass, (xmlNodePtr)node);
  rb_obj_call_init(rb_node, argc, argv);

  if(rb_block_given_p()) rb_yield(rb_node);

  return rb_node;
}

VALUE cNokogiriXmlAttr;
void init_xml_attr()
{
  VALUE nokogiri = rb_define_module("Nokogiri");
  VALUE xml = rb_define_module_under(nokogiri, "XML");
  VALUE node = rb_define_class_under(xml, "Node", rb_cObject);

  /*
Esempio n. 25
0
 *  new(document, content)
 *
 * Create a new CData element on the +document+ with +content+
 */
static VALUE new(VALUE klass, VALUE doc, VALUE content)
{
  xmlDocPtr xml_doc;
  Data_Get_Struct(doc, xmlDoc, xml_doc);

  xmlNodePtr node = xmlNewCDataBlock(
      xml_doc,
      (const xmlChar *)StringValuePtr(content),
      RSTRING_LEN(content)
  );

  VALUE rb_node = Nokogiri_wrap_xml_node(node);

  if(rb_block_given_p()) rb_yield(rb_node);

  return rb_node;
}

VALUE cNokogiriXmlCData;
void init_xml_cdata()
{
  VALUE nokogiri = rb_define_module("Nokogiri");
  VALUE xml = rb_define_module_under(nokogiri, "XML");
  VALUE node = rb_define_class_under(xml, "Node", rb_cObject);
  VALUE text = rb_define_class_under(xml, "Text", node);

  /*