/* TODO: DOCUMENT ME */ static VALUE in_context(VALUE self, VALUE _str, VALUE _options) { xmlNodePtr node, list, child_iter, tmp, node_children, doc_children; xmlNodeSetPtr set; xmlParserErrors error; VALUE doc, err; int doc_is_empty; Data_Get_Struct(self, xmlNode, node); doc = DOC_RUBY_OBJECT(node->doc); err = rb_iv_get(doc, "@errors"); doc_is_empty = (node->doc->children == NULL) ? 1 : 0; node_children = node->children; doc_children = node->doc->children; xmlSetStructuredErrorFunc((void *)err, Nokogiri_error_array_pusher); /* Twiddle global variable because of a bug in libxml2. * http://git.gnome.org/browse/libxml2/commit/?id=e20fb5a72c83cbfc8e4a8aa3943c6be8febadab7 */ #ifndef HTML_PARSE_NOIMPLIED htmlHandleOmittedElem(0); #endif /* This function adds a fake node to the child of +node+. If the parser * does not exit cleanly with XML_ERR_OK, the list is freed. This can * leave the child pointers in a bad state if they were originally empty. * * http://git.gnome.org/browse/libxml2/tree/parser.c#n13177 * */ error = xmlParseInNodeContext(node, StringValuePtr(_str), (int)RSTRING_LEN(_str), (int)NUM2INT(_options), &list); /* xmlParseInNodeContext should not mutate the original document or node, * so reassigning these pointers should be OK. The reason we're reassigning * is because if there were errors, it's possible for the child pointers * to be manipulated. */ if (error != XML_ERR_OK) { node->doc->children = doc_children; node->children = node_children; } /* make sure parent/child pointers are coherent so an unlink will work * properly (#331) */ child_iter = node->doc->children ; while (child_iter) { if (child_iter->parent != (xmlNodePtr)node->doc) child_iter->parent = (xmlNodePtr)node->doc; child_iter = child_iter->next; } #ifndef HTML_PARSE_NOIMPLIED htmlHandleOmittedElem(1); #endif xmlSetStructuredErrorFunc(NULL, NULL); /* Workaround for a libxml2 bug where a parsing error may leave a broken * node reference in node->doc->children. * This workaround is limited to when a parse error occurs, the document * went from having no children to having children, and the context node is * part of a document fragment. * https://bugzilla.gnome.org/show_bug.cgi?id=668155 */ if (error != XML_ERR_OK && doc_is_empty && node->doc->children != NULL) { tmp = node; while (tmp->parent) tmp = tmp->parent; if (tmp->type == XML_DOCUMENT_FRAG_NODE) node->doc->children = NULL; } /* FIXME: This probably needs to handle more constants... */ switch (error) { case XML_ERR_INTERNAL_ERROR: case XML_ERR_NO_MEMORY: rb_raise(rb_eRuntimeError, "error parsing fragment (%d)", error); break; default: break; } set = xmlXPathNodeSetCreate(NULL); while (list) { xmlXPathNodeSetAddUnique(set, list); list = list->next; } return Nokogiri_wrap_xml_node_set(set, doc); }