evaluate (Nokogiri::XML::XPathContext)

    /*
     * call-seq:
     *  evaluate(search_path)
     *
     * Evaluate the +search_path+ returning an XML::XPath object.
     */
    static VALUE evaluate(int argc, VALUE *argv, VALUE self)
    {
      VALUE search_path, xpath_handler;
      xmlXPathContextPtr ctx;
      Data_Get_Struct(self, xmlXPathContext, ctx);

      if(rb_scan_args(argc, argv, "11", &search_path, &xpath_handler) == 1)
        xpath_handler = Qnil;

      xmlChar* query = (xmlChar *)StringValuePtr(search_path);

      if(Qnil != xpath_handler) {
        // FIXME: not sure if this is the correct place to shove private data.
        ctx->userData = (void *)xpath_handler;
        xmlXPathRegisterFuncLookup(ctx, lookup, (void *)xpath_handler);
      }

      xmlResetLastError();
      xmlSetStructuredErrorFunc(NULL, xpath_exception_handler);

      // For some reason, xmlXPathEvalExpression will blow up with a generic error
      // when there is a non existent function.
      xmlSetGenericErrorFunc(NULL, xpath_generic_exception_handler);

      xmlXPathObjectPtr xpath = xmlXPathEvalExpression(query, ctx);
      xmlSetStructuredErrorFunc(NULL, NULL);
      xmlSetGenericErrorFunc(NULL, NULL);

      if(xpath == NULL) {
        VALUE xpath = rb_const_get(mNokogiriXml, rb_intern("XPath"));
        VALUE klass = rb_const_get(xpath, rb_intern("SyntaxError"));

        xmlErrorPtr error = xmlGetLastError();
        rb_funcall(rb_mKernel, rb_intern("raise"), 1,
            Nokogiri_wrap_xml_syntax_error(klass, error)
        );
      }

      VALUE xpath_object = Nokogiri_wrap_xml_xpath(xpath);

      assert(ctx->doc);
      assert(DOC_RUBY_OBJECT_TEST(ctx->doc));

      rb_funcall( xpath_object,
                  rb_intern("document="),
                  1,
                  DOC_RUBY_OBJECT(ctx->doc)
       );
      return xpath_object;
    }