Anonymous avatar Anonymous committed 7f5ccab

Initial revision

Comments (0)

Files changed (30)

+Revision history for Perl extension XML::LibXSLT.
+
+0.01  Sat Mar  3 17:08:00 2001
+	- original version; created by h2xs 1.19
+
+# $Id$
+
+package XML::LibXML;
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT);
+use Carp;
+
+$VERSION = "0.91";
+require Exporter;
+require DynaLoader;
+
+@ISA = qw(DynaLoader Exporter);
+
+bootstrap XML::LibXML $VERSION;
+
+@EXPORT = qw( XML_ELEMENT_NODE 
+	      XML_ATTRIBUTE_NODE
+	      XML_TEXT_NODE
+	      XML_CDATA_SECTION_NODE
+	      XML_ENTITY_REF_NODE
+	      XML_ENTITY_NODE
+	      XML_PI_NODE
+	      XML_COMMENT_NODE
+	      XML_DOCUMENT_NODE
+	      XML_DOCUMENT_TYPE_NODE
+	      XML_DOCUMENT_FRAG_NODE
+	      XML_NOTATION_NODE
+	      XML_HTML_DOCUMENT_NODE
+	      XML_DTD_NODE
+	      XML_ELEMENT_DECL
+	      XML_ATTRIBUTE_DECL
+	      XML_ENTITY_DECL
+	      XML_NAMESPACE_DECL
+	      XML_XINCLUDE_START
+	      XML_XINCLUDE_END );
+
+
+sub new {
+    my $class = shift;
+    my %options = @_;
+    my $self = bless \%options, $class;
+    return $self;
+}
+
+sub parse_string {
+    my $self = shift;
+    croak("parse already in progress") if $self->{_State_};
+    $self->{_State_} = 1;
+    $self->_prepare();
+    my $result;
+    eval {
+        $result = $self->_parse_string(@_);
+    };
+    my $err = $@;
+    if ($err) {
+        $self->{_State_} = 0;
+        $self->_release();
+        croak $err;
+    }
+    $self->{_State_} = 0;
+    $self->_release();
+    return $result;
+}
+
+sub parse_fh {
+    my $self = shift;
+    croak("parse already in progress") if $self->{_State_};
+    $self->{_State_} = 1;
+    $self->_prepare();
+    my $result;
+    eval {
+        $result = $self->_parse_fh(@_);
+    };
+    my $err = $@;
+    if ($err) {
+        $self->{_State_} = 0;
+        $self->_release();
+        croak $err;
+    }
+    $self->{_State_} = 0;
+    $self->_release();
+    return $result;
+}
+
+sub parse_file {
+    my $self = shift;
+    croak("parse already in progress") if $self->{_State_};
+    $self->{_State_} = 1;
+    $self->_prepare();
+    my $result;
+    eval {
+        $result = $self->_parse_file(@_);
+    };
+    my $err = $@;
+    if ($err) {
+        $self->{_State_} = 0;
+        $self->_release();
+        croak $err;
+    }
+    $self->{_State_} = 0;
+    $self->_release();
+    return $result;
+}
+
+
+sub XML_ELEMENT_NODE(){1;}
+sub XML_ATTRIBUTE_NODE(){2;}
+sub XML_TEXT_NODE(){3;}
+sub XML_CDATA_SECTION_NODE(){4;}
+sub XML_ENTITY_REF_NODE(){5;}
+sub XML_ENTITY_NODE(){6;}
+sub XML_PI_NODE(){7;}
+sub XML_COMMENT_NODE(){8;}
+sub XML_DOCUMENT_NODE(){9;}
+sub XML_DOCUMENT_TYPE_NODE(){10;}
+sub XML_DOCUMENT_FRAG_NODE(){11;}
+sub XML_NOTATION_NODE(){12;}
+sub XML_HTML_DOCUMENT_NODE(){13;}
+sub XML_DTD_NODE(){14;}
+sub XML_ELEMENT_DECL_NODE(){15;}
+sub XML_ATTRIBUTE_DECL_NODE(){16;}
+sub XML_ENTITY_DECL_NODE(){17;}
+sub XML_NAMESPACE_DECL_NODE(){18;}
+sub XML_XINCLUDE_START(){19;}
+sub XML_XINCLUDE_END(){20;}
+
+@XML::LibXML::Element::ISA      = 'XML::LibXML::Node';
+@XML::LibXML::Text::ISA         = 'XML::LibXML::Node';
+@XML::LibXML::Comment::ISA      = 'XML::LibXML::Text';
+@XML::LibXML::CDATASection::ISA = 'XML::LibXML::Text';
+
+1;
+__END__
+
+=head1 NAME
+
+XML::LibXML - Interface to the gnome libxml2 library
+
+=head1 SYNOPSIS
+
+  use XML::LibXML;
+  my $parser = XML::LibXML->new();
+
+  my $doc = $parser->parse_string(<<'EOT');
+  <xml/>
+  EOT
+
+=head1 DESCRIPTION
+
+Currently this module doesn't actually do much but parse files and give
+you back a document (DOM) handle. You can't actually call DOM methods on
+that document though (because I haven't written the code to do it yet!).
+
+=head1 OPTIONS
+
+LibXML options are global (unfortunately this is a limitation of the
+underlying implementation, not this interface). They can either be set
+using C<$parser-E<gt>option(...)>, or C<XML::LibXML-E<gt>option(...)>, both
+are treated in the same manner. Note that even two forked processes
+will share some of the same options, so be careful out there!
+
+Every option returns the previous value, and can be called without
+parameters to get the current value.
+
+=head2 validation
+
+  XML::LibXML->validation(1);
+
+Turn validation on (or off). Defaults to off.
+
+=head2 expand_entities
+
+  XML::LibXML->expand_entities(0);
+
+Turn entity expansion on or off, enabled by default. If entity expansion
+is off, any external parsed entities in the document are left as entities.
+Probably not very useful for most purposes.
+
+=head2 keep_blanks
+
+  XML::LibXML->keep_blanks(0);
+
+Allows you to turn off XML::LibXML's default behaviour of maintaining
+whitespace in the document.
+
+=head2 pedantic_parser
+
+  XML::LibXML->pedantic_parser(1);
+
+You can make XML::LibXML more pedantic if you want to.
+
+=head2 load_ext_dtd
+
+  XML::LibXML->load_ext_dtd(1);
+
+Load external DTD subsets while parsing.
+
+=head2 match_callback
+
+  XML::LibXML->match_callback($subref);
+
+Sets a "match" callback. See L<"Input Callbacks"> below.
+
+=head2 open_callback
+
+  XML::LibXML->open_callback($subref);
+
+Sets an open callback. See L<"Input Callbacks"> below.
+
+=head2 read_callback
+
+  XML::LibXML->read_callback($subref);
+
+Sets a read callback. See L<"Input Callbacks"> below.
+
+=head2 close_callback
+
+  XML::LibXML->close_callback($subref);
+
+Sets a close callback. See L<"Input Callbacks"> below.
+
+=head1 CONSTRUCTOR
+
+The XML::LibXML constructor, C<new()>, takes the following parameters:
+
+=head2 ext_ent_handler
+
+  my $parser = XML::LibXML->new(ext_ent_handler => sub { ... });
+
+The ext_ent_handler sub is called whenever libxml needs to load an external
+parsed entity. The handler sub will be passed two parameters: a
+URL (SYSTEM identifier) and an ID (PUBLIC identifier). It should return
+a string containing the resource at the given URI.
+
+Note that you do not need to enable this - if not supplied libxml will
+get the resource either directly from the filesystem, or using an internal
+http client library.
+
+=head1 PARSING
+
+There are three ways to parse documents - as a string, as a Perl filehandle,
+or as a filename. The return value from each is a XML::LibXML::Document
+object, which is a DOM object (although no DOM methods are implemented
+yet). See L<"XML::LibXML::Document"> below for more details on the methods
+available on documents.
+
+Each of the below methods will throw an exception if the document is invalid.
+To prevent this causing your program exiting, wrap the call in an eval{}
+block.
+
+=head2 parse_string
+
+  my $doc = $parser->parse_string($string);
+
+=head2 parse_fh
+
+  my $doc = $parser->parse_fh($fh);
+
+Here, C<$fh> can be an IOREF, or a subclass of IO::Handle.
+
+=head2 parse_file
+
+  my $doc = $parser->parse_file($filename);
+
+=head1 XML::LibXML::Document
+
+The objects returned above have a few methods available to them:
+
+=head2 C<$doc-E<gt>toString>
+
+Convert the document to a string.
+
+=head2 C<$doc-E<gt>is_valid>
+
+Post parse validation. Returns true if the document is valid against the
+DTD specified in the DOCTYPE declaration
+
+=head2 C<$doc-E<gt>is_valid($dtd)>
+
+Same as the above, but allows you to pass in a DTD created from 
+L<"XML::LibXML::Dtd">.
+
+=head2 C<$doc-E<gt>process_xinclude>
+
+Process any xinclude tags in the file.
+
+=head1 XML::LibXML::Dtd
+
+This module allows you to parse and return a DTD object. It has one method
+right now, C<new()>.
+
+=head2 new()
+
+  my $dtd = XML::LibXML::Dtd->new($public, $system);
+
+Creates a new DTD object from the public and system identifiers. It will
+automatically load the objects from the filesystem, or use the input
+callbacks (see L<"Input Callbacks"> below) to load the DTD.
+
+=head1 Input Callbacks
+
+The input callbacks are used whenever LibXML has to get something B<other
+than external parsed entities> from somewhere. The input callbacks in LibXML
+are stacked on top of the original input callbacks within the libxml library.
+This means that if you decide not to use your own callbacks (see C<match()>),
+then you can revert to the default way of handling input. This allows, for
+example, to only handle certain URI schemes.
+
+The following callbacks are defined:
+
+=head2 match(uri)
+
+If you want to handle the URI, simply return a true value from this callback.
+
+=head2 open(uri)
+
+Open something and return it to handle that resource.
+
+=head2 read(handle, bytes)
+
+Read a certain number of bytes from the resource.
+
+=head2 close(handle)
+
+Close the handle associated with the resource.
+
+=head2 Example
+
+This is a purely fictitious example that uses a MyScheme::Handler object
+that responds to methods similar to an IO::Handle.
+
+  XML::LibXML->match_callback(\&match_uri);
+  
+  XML::LibXML->open_callback(\&open_uri);
+  
+  XML::LibXML->read_callback(\&read_uri);
+  
+  XML::LibXML->close_callback(\&close_uri);
+  
+  sub match_uri {
+    my $uri = shift;
+    return $uri =~ /^myscheme:/;
+  }
+  
+  sub open_uri {
+    my $uri = shift;
+    return MyScheme::Handler->new($uri);
+  }
+  
+  sub read_uri {
+    my $handler = shift;
+    my $length = shift;
+    my $buffer;
+    read($handler, $buffer, $length);
+    return $buffer;
+  }
+  
+  sub close_uri {
+    my $handler = shift;
+    close($handler);
+  }
+
+=head1 AUTHOR
+
+Matt Sergeant, matt@sergeant.org
+
+Copyright 2001, AxKit.com Ltd. All rights reserved.
+
+=head1 SEE ALSO
+
+XML::LibXSLT
+
+=cut
+/* $Id$ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#include <libxml/HTMLparser.h>
+#include <libxml/HTMLtree.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/debugXML.h>
+#include <libxml/xmlerror.h>
+#include <libxml/xinclude.h>
+
+#include "dom.h"
+#include "xpath.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#define BUFSIZE 32768
+
+#ifdef VMS
+extern int xmlDoValidityCheckingDefaultVal;
+#define xmlDoValidityCheckingDefaultValue xmlDoValidityCheckingDefaultVal
+extern int xmlSubstituteEntitiesDefaultVal;
+#define xmlSubstituteEntitiesDefaultValue xmlSubstituteEntitiesDefaultVal
+#else
+extern int xmlDoValidityCheckingDefaultValue;
+extern int xmlSubstituteEntitiesDefaultValue;
+#endif
+extern int xmlGetWarningsDefaultValue;
+extern int xmlKeepBlanksDefaultValue;
+extern int xmlLoadExtDtdDefaultValue;
+extern int xmlPedanticParserDefaultValue;
+
+#define SET_CB(cb, fld) \
+    RETVAL = cb ? newSVsv(cb) : &PL_sv_undef;\
+    if (cb) {\
+        if (cb != fld) {\
+            sv_setsv(cb, fld);\
+        }\
+    }\
+    else {\
+        cb = newSVsv(fld);\
+    }
+
+static SV * LibXML_match_cb = NULL;
+static SV * LibXML_read_cb = NULL;
+static SV * LibXML_open_cb = NULL;
+static SV * LibXML_close_cb = NULL;
+static SV * LibXML_error = NULL;
+
+void
+LibXML_free_all_callbacks(void)
+{
+    if (LibXML_match_cb) {
+        SvREFCNT_dec(LibXML_match_cb);
+    }
+    
+    if (LibXML_read_cb) {
+        SvREFCNT_dec(LibXML_read_cb);
+    }
+    
+    if (LibXML_open_cb) {
+        SvREFCNT_dec(LibXML_open_cb);
+    }
+    
+    if (LibXML_close_cb) {
+        SvREFCNT_dec(LibXML_close_cb);
+    }
+
+}
+
+xmlParserInputPtr
+LibXML_load_external_entity(
+        const char * URL, 
+        const char * ID, 
+        xmlParserCtxtPtr ctxt)
+{
+    SV * self;
+    HV * real_obj;
+    SV ** func;
+    int count;
+    SV * results;
+    STRLEN results_len;
+    const char * results_pv;
+    xmlParserInputBufferPtr input_buf;
+
+    if (ctxt->_private == NULL) {
+        return xmlNewInputFromFile(ctxt, URL);
+    }
+    
+    self = (SV *)ctxt->_private;
+    real_obj = (HV *)SvRV(self);
+    func = hv_fetch(real_obj, "ext_ent_handler", 15, 0);
+    
+    if (func) {
+        dSP;
+        
+        ENTER;
+        SAVETMPS;
+
+        PUSHMARK(SP) ;
+        XPUSHs(sv_2mortal(newSVpv((char*)URL, 0)));
+        XPUSHs(sv_2mortal(newSVpv((char*)ID, 0)));
+        PUTBACK;
+        
+        count = perl_call_sv(*func, G_SCALAR);
+        
+        SPAGAIN;
+        
+        if (!count) {
+            croak("external entity handler did not return a value");
+        }
+        
+        results = POPs;
+        
+        results_pv = SvPV(results, results_len);
+        input_buf = xmlParserInputBufferCreateMem(
+                        results_pv,
+                        results_len,
+                        XML_CHAR_ENCODING_NONE
+                        );
+        
+        FREETMPS;
+        LEAVE;
+        
+        return xmlNewIOInputStream(ctxt, input_buf, XML_CHAR_ENCODING_NONE);
+    }
+    else {
+        if (URL == NULL) {
+            return NULL;
+        }
+        return xmlNewInputFromFile(ctxt, URL);
+    }
+    
+}
+
+int 
+LibXML_input_match(char const * filename)
+{
+    int results = 0;
+    
+    if (LibXML_match_cb && SvTRUE(LibXML_match_cb)) {
+        int count;
+        SV * res;
+
+        dSP;
+
+        ENTER;
+        SAVETMPS;
+
+        PUSHMARK(SP);
+        EXTEND(SP, 1);
+        PUSHs(sv_2mortal(newSVpv((char*)filename, 0)));
+        PUTBACK;
+
+        count = perl_call_sv(LibXML_match_cb, G_SCALAR);
+
+        SPAGAIN;
+        
+        if (count != 1) {
+            croak("match callback must return a single value");
+        }
+        
+        res = POPs;
+
+        if (SvTRUE(res)) {
+            results = 1;
+        }
+        
+        PUTBACK;
+        FREETMPS;
+        LEAVE;
+    }
+    
+    return results;
+}
+
+void * 
+LibXML_input_open(char const * filename)
+{
+    SV * results;
+    
+    if (LibXML_open_cb && SvTRUE(LibXML_open_cb)) {
+        int count;
+
+        dSP;
+
+        ENTER;
+        SAVETMPS;
+
+        PUSHMARK(SP);
+        EXTEND(SP, 1);
+        PUSHs(sv_2mortal(newSVpv((char*)filename, 0)));
+        PUTBACK;
+
+        count = perl_call_sv(LibXML_open_cb, G_SCALAR);
+
+        SPAGAIN;
+        
+        if (count != 1) {
+            croak("open callback must return a single value");
+        }
+
+        results = POPs;
+        
+        SvREFCNT_inc(results);
+        
+        PUTBACK;
+        FREETMPS;
+        LEAVE;
+    }
+    
+    return (void *)results;
+}
+
+int 
+LibXML_input_read(void * context, char * buffer, int len)
+{
+    SV * results = NULL;
+    STRLEN res_len = 0;
+    const char * output;
+    
+    SV * ctxt = (SV *)context;
+    
+    if (LibXML_read_cb && SvTRUE(LibXML_read_cb)) {
+        int count;
+
+        dSP;
+
+        ENTER;
+        SAVETMPS;
+
+        PUSHMARK(SP);
+        EXTEND(SP, 2);
+        PUSHs(ctxt);
+        PUSHs(sv_2mortal(newSViv(len)));
+        PUTBACK;
+
+        count = perl_call_sv(LibXML_read_cb, G_SCALAR);
+
+        SPAGAIN;
+        
+        if (count != 1) {
+            croak("read callback must return a single value");
+        }
+
+        output = POPp;
+        if (output != NULL) {
+            res_len = strlen(output);
+            if (res_len) {
+                strncpy(buffer, output, res_len);
+            }
+            else {
+                buffer[0] = 0;
+            }
+        }
+        
+        FREETMPS;
+        LEAVE;
+    }
+    
+    /* warn("read, asked for: %d, returning: [%d] %s\n", len, res_len, buffer); */
+    return res_len;
+}
+
+void 
+LibXML_input_close(void * context)
+{
+    SV * ctxt = (SV *)context;
+    
+    if (LibXML_close_cb && SvTRUE(LibXML_close_cb)) {
+        int count;
+
+        dSP;
+
+        ENTER;
+        SAVETMPS;
+
+        PUSHMARK(SP);
+        EXTEND(SP, 1);
+        PUSHs(ctxt);
+        PUTBACK;
+
+        count = perl_call_sv(LibXML_close_cb, G_SCALAR);
+
+        SPAGAIN;
+
+        SvREFCNT_dec(ctxt);
+        
+        if (!count) {
+            croak("close callback failed");
+        }
+
+        PUTBACK;
+        FREETMPS;
+        LEAVE;
+    }
+}
+
+void
+LibXML_error_handler(void * ctxt, const char * msg, ...)
+{
+    va_list args;
+    char buffer[50000];
+    
+    buffer[0] = 0;
+    
+    va_start(args, msg);
+    vsprintf(&buffer[strlen(buffer)], msg, args);
+    va_end(args);
+    
+    sv_catpv(LibXML_error, buffer);
+/*    croak(buffer); */
+}
+
+void
+LibXML_validity_error(void * ctxt, const char * msg, ...)
+{
+    va_list args;
+    char buffer[50000];
+    
+    buffer[0] = 0;
+    
+    va_start(args, msg);
+    vsprintf(&buffer[strlen(buffer)], msg, args);
+    va_end(args);
+    
+    sv_catpv(LibXML_error, buffer);
+/*    croak(buffer); */
+}
+
+void
+LibXML_validity_warning(void * ctxt, const char * msg, ...)
+{
+    va_list args;
+    char buffer[50000];
+    
+    buffer[0] = 0;
+    
+    va_start(args, msg);
+    vsprintf(&buffer[strlen(buffer)], msg, args);
+    va_end(args);
+    
+    warn(buffer);
+}
+
+xmlParserCtxtPtr
+LibXML_get_context(SV * self)
+{
+    SV ** ctxt_sv;
+    ctxt_sv = hv_fetch((HV *)SvRV(self), "_context", 8, 0);
+    if (!ctxt_sv) {
+        croak("cannot fetch context!");
+    }
+    return (xmlParserCtxtPtr)SvIV((SV*)SvRV(*ctxt_sv));
+}
+
+xmlDocPtr
+LibXML_parse_stream(SV * self, SV * ioref)
+{
+    dSP;
+    
+    xmlDocPtr doc;
+    xmlParserCtxtPtr ctxt;
+    int well_formed;
+    
+    SV * tbuff;
+    SV * tsize;
+    
+    int done = 0;
+    
+    ENTER;
+    SAVETMPS;
+    
+    tbuff = newSV(0);
+    tsize = newSViv(BUFSIZE);
+    
+    ctxt = LibXML_get_context(self);
+    
+    while (!done) {
+        int cnt;
+        SV * read_results;
+        STRLEN read_length;
+        char * chars;
+        
+        SAVETMPS;
+        
+        PUSHMARK(SP);
+        EXTEND(SP, 3);
+        PUSHs(ioref);
+        PUSHs(tbuff);
+        PUSHs(tsize);
+        PUTBACK;
+        
+        cnt = perl_call_method("read", G_SCALAR);
+        
+        SPAGAIN;
+        
+        if (cnt != 1) {
+            croak("read method call failed");
+        }
+        
+        read_results = POPs;
+        
+        if (!SvOK(read_results)) {
+            croak("read error");
+        }
+        
+        read_length = SvIV(read_results);
+        
+        chars = SvPV(tbuff, read_length);
+        
+        if (read_length > 0) {
+            if (read_length == BUFSIZE) {
+                xmlParseChunk(ctxt, chars, read_length, 0);
+            }
+            else {
+                xmlParseChunk(ctxt, chars, read_length, 1);
+                done = 1;
+            }
+        }
+        else {
+            done = 1;
+        }
+        
+        PUTBACK;
+        
+        FREETMPS;
+    }
+    
+    doc = ctxt->myDoc;
+    well_formed = ctxt->wellFormed;
+    
+    FREETMPS;
+    LEAVE;
+    
+    if (!well_formed) {
+        xmlFreeDoc(doc);
+        return NULL;
+    }
+    
+    return doc;
+}
+
+MODULE = XML::LibXML         PACKAGE = XML::LibXML
+
+PROTOTYPES: DISABLE
+
+BOOT:
+    LIBXML_TEST_VERSION
+    xmlInitParser();
+    xmlRegisterInputCallbacks(
+            (xmlInputMatchCallback)LibXML_input_match,
+            (xmlInputOpenCallback)LibXML_input_open,
+            (xmlInputReadCallback)LibXML_input_read,
+            (xmlInputCloseCallback)LibXML_input_close
+        );
+    xmlSubstituteEntitiesDefaultValue = 1;
+    xmlKeepBlanksDefaultValue = 1;
+    xmlSetExternalEntityLoader((xmlExternalEntityLoader)LibXML_load_external_entity);
+    xmlSetGenericErrorFunc(PerlIO_stderr(), (xmlGenericErrorFunc)LibXML_error_handler);
+    LibXML_error = newSVpv("", 0);
+    xmlGetWarningsDefaultValue = 0;
+    xmlLoadExtDtdDefaultValue = 1;
+
+void
+END()
+    CODE:
+        LibXML_free_all_callbacks();
+        xmlCleanupParser();
+        SvREFCNT_dec(LibXML_error);
+
+SV *
+match_callback(self, ...)
+        SV * self
+    CODE:
+        if (items > 1) {
+            SET_CB(LibXML_match_cb, ST(1));
+        }
+        else {
+            RETVAL = LibXML_match_cb ? sv_2mortal(LibXML_match_cb) : &PL_sv_undef;
+        }
+    OUTPUT:
+        RETVAL
+
+SV *
+open_callback(self, ...)
+        SV * self
+    CODE:
+        if (items > 1) {
+            SET_CB(LibXML_open_cb, ST(1));
+        }
+        else {
+            RETVAL = LibXML_open_cb ? sv_2mortal(LibXML_open_cb) : &PL_sv_undef;
+        }
+    OUTPUT:
+        RETVAL
+
+SV *
+read_callback(self, ...)
+        SV * self
+    CODE:
+        if (items > 1) {
+            SET_CB(LibXML_read_cb, ST(1));
+        }
+        else {
+            RETVAL = LibXML_read_cb ? sv_2mortal(LibXML_read_cb) : &PL_sv_undef;
+        }
+    OUTPUT:
+        RETVAL
+
+SV *
+close_callback(self, ...)
+        SV * self
+    CODE:
+        if (items > 1) {
+            SET_CB(LibXML_close_cb, ST(1));
+        }
+        else {
+            RETVAL = LibXML_close_cb ? sv_2mortal(LibXML_close_cb) : &PL_sv_undef;
+        }
+    OUTPUT:
+        RETVAL
+
+int
+validation(self, ...)
+        SV * self
+    CODE:
+        RETVAL = xmlDoValidityCheckingDefaultValue;
+        if (items > 1) {
+            xmlDoValidityCheckingDefaultValue = SvTRUE(ST(1)) ? 1 : 0;
+        }
+    OUTPUT:
+        RETVAL
+
+int
+expand_entities(self, ...)
+        SV * self
+    CODE:
+        RETVAL = xmlSubstituteEntitiesDefaultValue;
+        if (items > 1) {
+            xmlSubstituteEntitiesDefaultValue = SvTRUE(ST(1)) ? 1 : 0;
+        }
+    OUTPUT:
+        RETVAL
+
+int
+keep_blanks(self, ...)
+        SV * self
+    CODE:
+        RETVAL = xmlKeepBlanksDefaultValue;
+        if (items > 1) {
+            xmlKeepBlanksDefaultValue = SvTRUE(ST(1)) ? 1 : 0;
+        }
+    OUTPUT:
+        RETVAL
+
+int
+pedantic_parser(self, ...)
+        SV * self
+    CODE:
+        RETVAL = xmlPedanticParserDefaultValue;
+        if (items > 1) {
+            xmlPedanticParserDefaultValue = SvTRUE(ST(1)) ? 1 : 0;
+        }
+    OUTPUT:
+        RETVAL
+
+int
+load_ext_dtd(self, ...)
+        SV * self
+    CODE:
+        RETVAL = xmlLoadExtDtdDefaultValue;
+        if (items > 1) {
+            xmlLoadExtDtdDefaultValue = SvTRUE(ST(1)) ? 1 : 0;
+        }
+    OUTPUT:
+        RETVAL
+
+void
+_prepare(self)
+        SV * self
+    PREINIT:
+        xmlParserCtxtPtr ctxt;
+        SV * ctxt_sv;
+    CODE:
+        sv_setpvn(LibXML_error, "", 0);
+        ctxt = xmlCreatePushParserCtxt(NULL, NULL, "", 0, NULL);
+        ctxt->_private = (void*)self;
+        ctxt_sv = NEWSV(0, 0);
+        sv_setref_pv(ctxt_sv, "XML::LibXML::Context", (void*)ctxt);
+        hv_store((HV *)SvRV(self), "_context", 8, ctxt_sv, 0);
+
+void
+_release(self)
+        SV * self
+    PREINIT:
+        xmlParserCtxtPtr ctxt;
+        SV * hval;
+    CODE:
+        hval = hv_delete((HV *)SvRV(self), "_context", 8, 0);
+        ctxt = (xmlParserCtxtPtr)SvIV( (SV*)SvRV(hval) );
+
+char *
+get_last_error(CLASS)
+        char * CLASS
+    PREINIT:
+        STRLEN len;
+    CODE:
+        RETVAL = SvPV(LibXML_error, len);
+    OUTPUT:
+        RETVAL
+
+xmlDocPtr
+_parse_string(self, string)
+        SV * self
+        SV * string
+    PREINIT:
+        xmlParserCtxtPtr ctxt;
+        char * CLASS = "XML::LibXML::Document";
+        STRLEN len;
+        char * ptr;
+        int well_formed;
+    CODE:
+        ptr = SvPV(string, len);
+        ctxt = LibXML_get_context(self);
+        xmlParseChunk(ctxt, ptr, len, 0);
+        xmlParseChunk(ctxt, ptr, 0, 1);
+        well_formed = ctxt->wellFormed;
+        RETVAL = ctxt->myDoc;
+        if (!well_formed) {
+            xmlFreeDoc(RETVAL);
+            croak(SvPV(LibXML_error, len));
+        }
+    OUTPUT:
+        RETVAL
+
+xmlDocPtr
+_parse_fh(self, fh)
+        SV * self
+        SV * fh
+    PREINIT:
+        char * CLASS = "XML::LibXML::Document";
+        STRLEN len;
+    CODE:
+        RETVAL = LibXML_parse_stream(self, fh);
+        if (RETVAL == NULL) {
+            croak(SvPV(LibXML_error, len));
+        }
+    OUTPUT:
+        RETVAL
+        
+xmlDocPtr
+_parse_file(self, filename)
+        SV * self
+        const char * filename
+    PREINIT:
+        xmlParserCtxtPtr ctxt;
+        char * CLASS = "XML::LibXML::Document";
+        PerlIO *f;
+        int ret;
+        int res;
+        STRLEN len;
+        char chars[BUFSIZE];
+    CODE:
+        if ((filename[0] == '-') && (filename[1] == 0)) {
+	    f = PerlIO_stdin();
+	} else {
+	    f = PerlIO_open(filename, "r");
+	}
+	if (f != NULL) {
+            ctxt = LibXML_get_context(self);
+	    res = PerlIO_read(f, chars, 4);
+	    if (res > 0) {
+                xmlParseChunk(ctxt, chars, res, 0);
+		while ((res = PerlIO_read(f, chars, BUFSIZE)) > 0) {
+		    xmlParseChunk(ctxt, chars, res, 0);
+		}
+                xmlParseChunk(ctxt, chars, 0, 1);
+		RETVAL = ctxt->myDoc;
+		ret = ctxt->wellFormed;
+		if (!ret) {
+                    PerlIO_close(f);
+		    xmlFreeDoc(RETVAL);
+		    croak(SvPV(LibXML_error, len));
+		}
+	    }
+            PerlIO_close(f);
+	}
+        else {
+            croak("cannot open file %s", filename);
+        }
+    OUTPUT:
+        RETVAL
+
+
+MODULE = XML::LibXML         PACKAGE = XML::LibXML::Document
+
+void
+DESTROY(self)
+        xmlDocPtr self
+    CODE:
+        if (self == NULL) {
+            XSRETURN_UNDEF;
+        }
+        xmlFreeDoc(self);
+
+SV *
+toString(self)
+        xmlDocPtr self
+    PREINIT:
+        xmlChar *result;
+        int len;
+    CODE:
+        xmlDocDumpMemory(self, &result, &len);
+	if (result == NULL) {
+	    croak("Failed to convert doc to string");
+	} else {
+            RETVAL = newSVpvn((char *)result, (STRLEN)len);
+	    xmlFree(result);
+	}
+    OUTPUT:
+        RETVAL
+
+int
+is_valid(self, ...)
+        xmlDocPtr self
+    PREINIT:
+        xmlValidCtxt cvp;
+        xmlDtdPtr dtd;
+        SV * dtd_sv;
+    CODE:
+        if (items > 1) {
+            dtd_sv = ST(1);
+            if ( sv_isobject(dtd_sv) && (SvTYPE(SvRV(dtd_sv)) == SVt_PVMG) ) {
+                dtd = (xmlDtdPtr)SvIV((SV*)SvRV( dtd_sv ));
+            }
+            else {
+                croak("is_valid: argument must be a DTD object");
+            }
+            cvp.userData = (void*)PerlIO_stderr();
+            cvp.error = (xmlValidityErrorFunc)LibXML_validity_error;
+            cvp.warning = (xmlValidityWarningFunc)LibXML_validity_warning;
+            RETVAL = xmlValidateDtd(&cvp, self, dtd);
+        }
+        else {
+            RETVAL = xmlValidateDocument(&cvp, self);
+        }
+    OUTPUT:
+        RETVAL
+
+void
+process_xinclude(self)
+        xmlDocPtr self
+    CODE:
+        xmlXIncludeProcess(self);
+
+xmlDocPtr
+new( CLASS, version, encoding )
+        char * CLASS
+        char * version 
+        char * encoding
+    CODE:
+        RETVAL = domCreateDocument( version, encoding ); 
+    OUTPUT:
+        RETVAL
+
+xmlDocPtr
+createDocument( CLASS, version, encoding )
+        char * CLASS
+        char * version 
+        char * encoding
+    CODE:
+        RETVAL = domCreateDocument( version, encoding ); 
+    OUTPUT:
+        RETVAL
+
+xmlNodePtr
+createElement( dom, name )
+        xmlDocPtr dom
+        char* name
+    PREINIT:
+        char * CLASS = "XML::LibXML::Element";
+    CODE:
+        RETVAL = xmlNewNode( 0 , name );
+        RETVAL->doc = dom;
+    OUTPUT:
+        RETVAL
+
+xmlNodePtr
+createTextNode( dom, content )
+        xmlDocPtr dom
+        char * content
+    PREINIT:
+        char * CLASS = "XML::LibXML::Text";
+    CODE:
+        RETVAL = xmlNewDocText( dom, content );
+    OUTPUT:
+        RETVAL
+
+xmlNodePtr 
+createComment( dom , content )
+        xmlDocPtr dom
+        char * content
+    PREINIT:
+        char * CLASS = "XML::LibXML::Comment";
+    CODE:
+        RETVAL = xmlNewDocComment( dom, content );
+    OUTPUT:
+        RETVAL
+
+xmlNodePtr
+createCDATASection( dom, content )
+        xmlDocPtr dom
+        char * content
+    PREINIT:
+        char * CLASS = "XML::LibXML::CDATASection";
+    CODE:
+        RETVAL = domCreateCDATASection( dom, content );
+    OUTPUT:
+        RETVAL
+
+void 
+setDocumentElement( dom , elem )
+        xmlDocPtr dom
+        xmlNodePtr elem
+    CODE:
+        domSetDocumentElement( dom, elem );
+
+xmlNodePtr
+getDocumentElement( dom )
+        xmlDocPtr dom
+    PREINIT:
+        const char * CLASS = "XML::LibXML::Node";
+    CODE:
+        RETVAL = domDocumentElement( dom ) ;
+        if ( RETVAL ) {
+            CLASS = domNodeTypeName( RETVAL );
+        }
+    OUTPUT:
+        RETVAL
+
+MODULE = XML::LibXML         PACKAGE = XML::LibXML::Context
+
+void
+DESTROY(self)
+        xmlParserCtxtPtr self
+    CODE:
+        xmlFreeParserCtxt(self);
+
+MODULE = XML::LibXML         PACKAGE = XML::LibXML::Dtd
+
+xmlDtdPtr
+new(CLASS, external, system)
+        char * CLASS
+        char * external
+        char * system
+    CODE:
+        RETVAL = xmlParseDTD((const xmlChar*)external, (const xmlChar*)system);
+    OUTPUT:
+        RETVAL
+
+
+
+MODULE = XML::LibXML         PACKAGE = XML::LibXML::Node
+
+void
+DESTROY( node )
+        xmlNodePtr node 
+    CODE:
+        if ( node->parent == 0 ) {
+        /**
+         * this block should remove old (unbound) nodes from the system
+         * but for some reason this condition is not valid ... :(
+         **/
+        /* warn( "Free node\n" ); */
+        /*domUnbindNode( node );  * before freeing we unbind the node from
+		                          * possible siblings */
+        /* xmlFreeNode( node ); */
+    }
+	
+int 
+getType( node ) 
+        xmlNodePtr node
+    CODE:
+        RETVAL = node->type;
+    OUTPUT:
+        RETVAL
+
+void
+unbindNode( elem )
+        xmlNodePtr elem
+    CODE:
+        domUnbindNode( elem );
+
+xmlNodePtr
+removeChild( paren, child ) 
+        xmlNodePtr paren
+        xmlNodePtr child
+    PREINIT:
+        const char * CLASS = "XML::LibXML::Node";
+    CODE:
+        RETVAL = domRemoveNode( paren, child );
+    OUTPUT:
+        RETVAL
+
+xmlNodePtr
+replaceChild( paren, newChild, oldChild ) 
+        xmlNodePtr paren
+        xmlNodePtr newChild
+        xmlNodePtr oldChild
+    PREINIT:
+        const char * CLASS = "XML::LibXML::Node";
+    CODE:
+        RETVAL = domReplaceChild( paren, newChild, oldChild );
+    OUTPUT:
+        RETVAL
+
+void
+appendChild( parent, child )
+        xmlNodePtr parent
+        xmlNodePtr child
+    CODE:
+        domAppendChild( parent, child );
+
+xmlNodePtr
+cloneNode( self, deep ) 
+        xmlNodePtr self
+        int deep
+    PREINIT:
+        const char * CLASS = "XML::LibXML::Node";
+    CODE:
+        RETVAL = xmlCopyNode( self, deep );
+    OUTPUT:
+        RETVAL
+
+
+xmlNodePtr
+getParentNode( self )
+        xmlNodePtr self
+    PREINIT:
+        const char * CLASS = "XML::LibXML::Element";
+    CODE:
+        RETVAL = self->parent;
+    OUTPUT:
+        RETVAL
+
+int 
+hasChildNodes( elem )
+        xmlNodePtr elem
+    CODE:
+        RETVAL = elem->children == 0 ? 0 : 1 ;
+    OUTPUT:
+        RETVAL
+
+xmlNodePtr
+getNextSibling( elem )
+        xmlNodePtr elem
+    PREINIT:
+        const char * CLASS = "XML::LibXML::Node";
+    CODE:
+        RETVAL = elem->next ;
+        if ( RETVAL ) {
+            CLASS = domNodeTypeName( RETVAL );
+        }	
+    OUTPUT:
+        RETVAL
+
+xmlNodePtr
+getPreviousSibling( elem )
+        xmlNodePtr elem
+    PREINIT:
+        const char * CLASS = "XML::LibXML::Node";
+    CODE:
+        RETVAL = elem->prev;
+        if ( RETVAL ) {
+            CLASS = domNodeTypeName( RETVAL );
+        }
+    OUTPUT:
+        RETVAL
+
+xmlNodePtr
+getFirstChild( elem )
+        xmlNodePtr elem
+    PREINIT:
+        const char * CLASS = "XML::LibXML::Node";
+    CODE:
+        RETVAL = elem->children;
+        if ( RETVAL ) {
+            CLASS = domNodeTypeName( RETVAL );
+        }
+    OUTPUT:
+        RETVAL
+
+
+xmlNodePtr
+getLastChild( elem )
+        xmlNodePtr elem
+    PREINIT:
+        const char * CLASS = "XML::LibXML::Node";
+    CODE:
+        RETVAL = elem->last;
+        if ( RETVAL ) {
+            CLASS = domNodeTypeName( RETVAL );
+        }
+    OUTPUT:
+        RETVAL
+
+
+xmlDocPtr
+getOwnerDocument( elem )
+        xmlNodePtr elem
+    PREINIT:
+        const char * CLASS = "XML::LibXML::Document";
+    CODE:
+        RETVAL = elem->doc;
+    OUTPUT:
+        RETVAL
+
+void
+setOwnerDocument( elem, doc )
+        xmlNodePtr elem
+        xmlDocPtr doc
+    CODE:
+        if ( doc ) {
+            if ( elem->doc != doc ) {
+                domUnbindNode( elem );
+            }
+            elem->doc = doc;
+        }
+
+SV*
+getName( node )
+        xmlNodePtr node
+    PREINIT:
+        const char * name;
+    CODE:
+        if( node != NULL ) {
+            name =  node->name;
+        }
+        RETVAL = newSVpvn( name, xmlStrlen( name ) );
+    OUTPUT:
+        RETVAL
+
+SV*
+getData( node ) 
+        xmlNodePtr node 
+    PREINIT:
+        const char * content;
+    CODE:
+        if( node != NULL ) {
+            content = node->content;
+        }
+        if ( content != 0 ){
+            RETVAL = newSVpvn( content, xmlStrlen( content ) );
+        }
+        else {
+            RETVAL = &PL_sv_undef;
+        }
+    OUTPUT:
+        RETVAL
+
+
+SV*
+findnodes( node, xpath )
+        xmlNodePtr node
+        char * xpath 
+    PREINIT:
+        xmlNodeSetPtr nodelist;
+        SV * element;
+        int len;
+    PPCODE:
+        len = 0;
+        nodelist = domXPathSelect( node, xpath );
+        if ( nodelist && nodelist->nodeNr > 0 ) {
+            int i = 0 ;
+            const char * cls = "XML::LibXML::Node";
+            xmlNodePtr tnode;
+
+            len = nodelist->nodeNr;
+         
+            for( i ; i < len; i++){
+                /* we have to create a new instance of an objectptr. and then 
+                 * place the current node into the new object. afterwards we can 
+                 * push the object to the array!
+                 */
+                element = 0;
+                tnode = nodelist->nodeTab[i];
+                element = sv_newmortal(); 
+
+                cls = domNodeTypeName( tnode );
+                XPUSHs( sv_setref_pv( element, cls, (void*)tnode ) );
+            }
+
+            xmlXPathFreeNodeSet( nodelist );
+        }
+        XSRETURN(len);
+
+SV*
+getChildnodes( node )
+        xmlNodePtr node
+    PREINIT:
+        xmlNodePtr cld;
+        SV * element;
+        int len;
+        const char * cls = "XML::LibXML::Node";
+    PPCODE:
+        len = 0;
+	
+        cld = node->children;
+        while ( cld ) {	
+            element = sv_newmortal();
+            cls = domNodeTypeName( cld );
+            XPUSHs( sv_setref_pv( element, cls, (void*)cld ) );
+            cld = cld->next;
+            len++;
+        }
+        XSRETURN(len);
+
+SV*
+toString( self )
+        xmlNodePtr self
+    PREINIT:
+        xmlBufferPtr buffer;
+    CODE:
+        buffer = xmlBufferCreate();
+        xmlNodeDump( buffer, self->doc, self, 0, 0 );
+        if ( buffer->content != 0 ) {
+            RETVAL = newSVpvn( buffer->content, buffer->use );
+        }
+        else {
+            RETVAL = &PL_sv_undef;
+        }
+        xmlBufferFree( buffer );
+    OUTPUT:
+        RETVAL
+
+MODULE = XML::LibXML         PACKAGE = XML::LibXML::Element
+
+xmlNodePtr
+new(CLASS, name )
+        char * CLASS
+        char * name
+    CODE:
+        CLASS = "XML::LibXML::Element";
+        RETVAL = xmlNewNode( 0, name );
+        if ( RETVAL != 0 ) {
+            RETVAL->next     = 0;
+            RETVAL->prev     = 0;
+            RETVAL->children = 0 ;
+            RETVAL->last     = 0;
+            RETVAL->doc      = 0;
+        }
+    OUTPUT:
+        RETVAL
+
+void
+DESTROY( node )
+        xmlNodePtr node 
+    CODE:
+
+void
+setAttribute( elem, name, value )
+        xmlNodePtr elem	
+        char * name
+        char * value
+    CODE:
+        xmlSetProp( elem, name, value );
+
+int 
+hasAttribute( elem, name ) 
+        xmlNodePtr elem
+        char * name
+    PREINIT:
+        xmlAttrPtr att;
+    CODE:
+        /**
+         * xmlHasProp() returns the attribute node, which is not exactly what 
+         * we want as a boolean value 
+         **/
+        att = xmlHasProp( elem, name );
+        RETVAL = att == NULL ? 0 : 1 ;
+    OUTPUT:
+        RETVAL
+
+SV*
+getAttribute( elem, name ) 
+        xmlNodePtr elem
+        char * name 
+    PREINIT:
+	    char * content;
+    CODE:
+        content = xmlGetProp( elem, name );
+        if ( content != NULL ) {
+            RETVAL  = newSVpvn( content, xmlStrlen( content ) );
+        }
+        else {
+            RETVAL = NULL;
+        }
+    OUTPUT:
+        RETVAL
+
+void
+removeAttribute( elem, name ) 	
+        xmlNodePtr elem
+        char * name
+    CODE:
+        xmlRemoveProp( xmlHasProp( elem, name ) );	
+
+SV*
+getElementsByTagName( elem, name )
+        xmlNodePtr elem
+        char * name 
+    PREINIT:
+        xmlNodeSetPtr nodelist;
+        SV * element;
+        int len;
+    PPCODE:
+        len = 0;
+        nodelist = domGetElementsByTagName( elem , name );
+        if ( nodelist && nodelist->nodeNr > 0 ) {
+            int i = 0 ;
+            const char * cls = "XML::LibXML::Node";
+            xmlNodePtr tnode;
+
+            len = nodelist->nodeNr;
+         
+            for( i ; i < len; i++){
+                /* we have to create a new instance of an objectptr. and then 
+                 * place the current node into the new object. afterwards we can 
+                 * push the object to the array!
+                 */
+                element = 0;
+                tnode = nodelist->nodeTab[i];
+                element = sv_newmortal(); 
+
+                cls = domNodeTypeName( tnode ); 
+                XPUSHs( sv_setref_pv( element, cls, (void*)tnode ) );
+            }
+
+            xmlXPathFreeNodeSet( nodelist );
+        }
+        XSRETURN(len);
+
+void
+appendWellBalancedChunk( self, chunk )
+        xmlNodePtr self
+        char * chunk
+    PREINIT:
+        xmlNodePtr rv;
+    CODE:
+        rv = domReadWellBalancedString( self->doc, chunk );
+        if ( rv != NULL ) {
+            xmlAddChildList( self , rv );
+        }	
+
+void 
+appendTextNode( self, xmlString )
+        xmlNodePtr self
+        char * xmlString
+    PREINIT: 
+        xmlNodePtr tn;
+    CODE:
+        if ( self->doc != NULL && xmlString != NULL ) {
+            tn == xmlNewDocText( self->doc, xmlString ); 
+            domAppendChild( self, tn );
+        }
+
+MODULE = XML::LibXML         PACKAGE = XML::LibXML::Text
+
+void
+setData( node, value )
+        xmlNodePtr node
+        char * value 
+    CODE:
+        domSetNodeValue( node, value );
+
+xmlNodePtr
+new( CLASS, content )
+        const char * CLASS
+        char * content
+    PREINIT:
+        xmlBufferPtr in, out;
+    CODE:
+        in = xmlBufferCreate();
+        out =xmlBufferCreate();
+    
+        xmlBufferCat( in, content );
+        xmlCharEncInFunc( xmlGetCharEncodingHandler( xmlParseCharEncoding("UTF-8") ), 
+                          out, 
+                          in);
+        RETVAL = xmlNewText( out->content );
+    OUTPUT:
+        RETVAL
+
+void
+DESTROY( node )
+        xmlNodePtr node 
+    CODE:
+
+MODULE = XML::LibXML         PACKAGE = XML::LibXML::Comment
+
+xmlNodePtr
+new( CLASS, content ) 
+        const char * CLASS
+        char * content
+    PREINIT:
+        xmlBufferPtr in, out;
+    CODE:
+        in = xmlBufferCreate();
+        out =xmlBufferCreate();
+    
+        xmlBufferCat( in, content );
+        xmlCharEncInFunc( xmlGetCharEncodingHandler( xmlParseCharEncoding("UTF-8") ), 
+                          out, 
+                          in);
+        RETVAL = xmlNewComment( out->content );
+    OUTPUT:
+        RETVAL
+
+void
+DESTROY( node )
+        xmlNodePtr node 
+    CODE:
+
+MODULE = XML::LibXML         PACKAGE = XML::LibXML::CDATASection
+
+xmlNodePtr
+new( CLASS , content )
+        const char * CLASS
+        char * content
+    PREINIT:
+        xmlBufferPtr in, out;
+    CODE:
+        in = xmlBufferCreate();
+        out =xmlBufferCreate();
+    
+        xmlBufferCat( in, content );
+        xmlCharEncInFunc( xmlGetCharEncodingHandler( xmlParseCharEncoding("UTF-8") ), 
+                          out, 
+                          in);
+        RETVAL = xmlNewCDataBlock( 0 , out->content, xmlStrlen( out->content ) );
+    OUTPUT:
+    RETVAL
+
+void
+DESTROY( node )
+        xmlNodePtr node 
+    CODE:
+Changes
+LibXML.pm
+LibXML.xs
+Makefile.PL
+MANIFEST
+PHISHS.CHANGES
+README
+dom.c
+dom.h
+xpath.c
+xpath.h
+typemap
+example/dromeds.xml
+example/bad.xml
+example/libxml.xml
+example/xml2pod.pl
+pod/XML::LibXML::Document.pod
+pod/XML::LibXML::Comment.pod
+pod/XML::LibXML::Text.pod
+pod/XML::LibXML::CDATASection.pod
+pod/XML::LibXML::Element.pod
+pod/XML::LibXML::Node.pod
+t/01basic.t
+t/02parsestring.t
+t/03parsefile.t
+t/04parsefh.t
+t/05dombasic.t
+t/06nodetypes.t
+t/07nodelist.t
+t/08findnodes.t
+# $Id$
+
+use ExtUtils::MakeMaker;
+use Config;
+use Symbol;
+
+$|=0;
+
+my %config;
+
+while($_ = shift) {
+    my ($key, $val) = split(/=/, $_, 2);
+    $config{$key} = $val;
+}
+
+my $DEBUG = delete $config{DEBUG};
+
+# get libs and inc from gnome-config
+eval {
+    print "running xml2-config... ";
+    $config{LIBS} ||= backtick('xml2-config --libs');
+    $config{INC} ||= backtick('xml2-config --cflags');
+    print "ok\n";
+};
+if ($@) {
+    print "failed\n";
+    warn "*** ", $@ if $DEBUG;
+    warn "using fallback values for LIBS and INC\n";
+    # backtick fails if gnome-config didn't exist...
+    $config{LIBS} = '-L/usr/local/lib -L/usr/lib -lxml2 -lz -lm';
+    $config{INC} = '-I/usr/local/include -I/usr/include';
+    
+    print <<OPT;
+options:
+  LIBS='$config{LIBS}'
+  INC='$config{INC}'
+If this is wrong, Re-run as:
+  \$ $^X Makefile.PL LIBS='-L/path/to/lib' INC='-I/path/to/include'
+
+OPT
+
+}
+
+my $LINK = "$Config{ld} -o conftest $Config{ccflags} -I$Config{incpath} %s $Config{lddlflags} $Config{ldflags} conftest.c $Config{libs} %s %s";
+
+if ($config{LIBS} !~ /\-lxml2\b/) {
+    $config{LIBS} .= ' -lxml2';
+}
+
+if ($config{LIBS} !~ /\-lz\b/) {
+    $config{LIBS} .= ' -lz';
+}
+
+if ($config{LIBS} !~ /\-lm\b/) {
+    $config{LIBS} .= ' -lm';
+}
+
+if (!have_library("xml2")) {
+    die <<DEATH;
+libxml2 not found
+Try setting LIBS and INC values on the command line
+Or get libxml2 from 
+  http://www.libxml.org/
+If you install via RPMs, make sure you also install the -devel
+RPMs, as this is where the headers (.h files) are.
+DEATH
+}
+
+# have_library("xml2", "ghttp_get_header_names");
+
+WriteMakefile(
+    'NAME'	=> 'XML::LibXML',
+    'VERSION_FROM' => 'LibXML.pm', # finds $VERSION
+    'AUTHOR'    => 'Matt Sergeant',
+    'ABSTRACT'  => 'Interface to Gnome libxml2 xml parsing and DOM library',
+    'OBJECT'    => '$(O_FILES)', # add the DOM extensions to libxml2
+    %config,
+);
+
+###################################################################
+# Functions
+#  - these should really be in MakeMaker... But &shrug;
+###################################################################
+
+sub rm_f {
+    my @files = @_;
+    my @realfiles;
+    foreach (@files) {
+        push @realfiles, glob($_);
+    }
+    if (@realfiles) {
+        chmod(0777, @realfiles);
+        unlink(@realfiles);
+    }
+}
+
+sub xsystem {
+    my $command = shift;
+    if ($DEBUG) {
+        print $command, "\n";
+        if (system($command) != 0) {
+            die "system call to '$command' failed";
+        }
+        return 1;
+    }
+    open(OLDOUT, ">&STDOUT");
+    open(OLDERR, ">&STDERR");
+    open(STDOUT, ">/dev/null");
+    open(STDERR, ">/dev/null");
+    my $retval = system($command);
+    open(STDOUT, ">&OLDOUT");
+    open(STDERR, ">&OLDERR");
+    if ($retval != 0) {
+        die "system call to '$command' failed";
+    }
+    return 1;
+}
+
+sub backtick {
+    my $command = shift;
+    if ($DEBUG) {
+        print $command, "\n";
+        my $results = `$command`;
+        chomp $results;
+        if ($? != 0) {
+            die "backticks call to '$command' failed";
+        }
+        return $results;
+    }
+    open(OLDOUT, ">&STDOUT");
+    open(OLDERR, ">&STDERR");
+    open(STDOUT, ">/dev/null");
+    open(STDERR, ">/dev/null");
+    my $results = `$command`;
+    my $retval = $?;
+    open(STDOUT, ">&OLDOUT");
+    open(STDERR, ">&OLDERR");
+    if ($retval != 0) {
+        die "backticks call to '$command' failed";
+    }
+    chomp $results;
+    return $results;
+}
+
+sub try_link0 {
+    my ($src, $opt) = @_;
+    my $cfile = gensym();
+    open($cfile, ">conftest.c") || die "Cannot write to file conftest.c";
+    print $cfile $src;
+    close($cfile);
+    xsystem(sprintf($LINK, $config{INC}, $config{LIBS}, $opt));
+}
+
+sub try_link {
+    my $result = eval {
+        try_link0(@_);
+    };
+    my $err = $@;
+    rm_f("conftest*");
+    if ($err) {
+        die $err;
+    }
+    return $result;
+}
+
+sub have_library {
+    my ($lib, $func) = (@_, "main");
+    printf("checking for %s() in -l%s... ", $func, $lib);
+    
+    my $result;
+    if ($func) {
+        my $libs = "-l$lib";
+        eval {
+        $result = try_link(<<"SRC", $libs);
+int main() { return 0; }
+int t() { ${func}(); return 0; }
+SRC
+        };
+        if ($@) {
+            warn $@ if $DEBUG;
+        }
+    }
+    
+    unless ($result) {
+        print "no\n";
+        return 0;
+    }
+    
+    if ($func ne "main") {
+        $config{DEFINE} .= uc(" -Dhave_$func");
+    }
+    
+    print "yes\n";
+    return 1;
+}
+Ok, I don't implement namespace handling yet. 
+
+But I introduced the node types in XML::LibXML and by default I 
+export them. I am aware this is not very beautyfull -- well, the 
+way it's done ... 
+
+Since most functions need more than just a simple call to libxml2
+I wrapped them in dom.c. I believe this is a better way than just put
+the whole code in the XS file.
+
+Then I patched LibXML.xs and added the parts
+ XML::LibXML::Node 
+ XML::LibXML::Element
+ XML::LibXML::Text
+ XML::LibXML::Comment
+ XML::LibXML::CDATASection
+As well some extra functions in XML::LibXML::Document.
+
+Common node related functions are implemented in XML::LibXML::Node, 
+so only specialized functions had to be reimplemented for sublevel 
+classes. 
+
+I treat XML::LibXML::Text as the CharacterData Class from the 
+DOM spec. Since libxml2 implements all character classes slightly
+different, each constructor had to be implemented seperatly.
+
+This implementation is aware about all subclasses that are supported:
+The CLASS Constant is set depending on the type of the node currently 
+handled. Differently to the previous implementation it does not 
+coredump that much any more. ;-)
+
+As well I added a chunk of documentation and a new example.
+
+_VERSION_F_ (05.17.2001)
+
+code cleanup and better documentation
+"dom.h" , "dom.c" are both much smaller after I removed redundant code
+"dom.c" is sorted analogue to "dom.h"
+
+all documentation about my extensions can be found in "examples/libxml.xml"
+the script "examples/xml2pod.pl" will transform this XML document into 
+various .pod documents.  
+better testing the encoding
+
+
+_VERSION_D_
+
+added nodelist functions 
+and 
+element->getElementsByTagName 
+
+
+_VERSION_C_
+
+the current version includes some securety fixes in dom.c and LibXML.xs
+
+also a better testsuite
+This module is a minimal interface to the Gnome libxml2 library which you
+can find at http://www.xmlsoft.org/
+
+Currently this is just a helper module for XML::LibXSLT (and as such is
+included with the XML::LibXSLT distribution), but libxml2 has a full DOM
+API, and so I hope to be able to offer full access to that API one day.
+
+This is free software, you may use it and distribute it under the same
+terms as Perl itself. Copyright 2001 AxKit.com Ltd, All rights reserved.
+#include <libxml/tree.h>
+#include <libxml/encoding.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/xmlIO.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+xmlDocPtr
+domCreateDocument( xmlChar *version, xmlChar *enc ){
+  xmlDocPtr doc = 0;
+    doc = xmlNewDoc( version );  
+    doc->charset  = XML_CHAR_ENCODING_UTF8;
+    doc->encoding = xmlStrdup(enc);
+    return doc;
+}
+
+/* this function is pretty neat, since you can read in well balanced 
+ * strings and get a list of nodes, which can be added to any other node.
+ *
+ * the code is pretty heavy i think, but deep in my heard i believe it's 
+ * worth it :) (e.g. if you like to read a chunk of well-balanced code 
+ * from a databasefield)
+ *
+ * in 99% i believe it is faster to create the dom by hand, and skip the 
+ * parsing job which has to be done here.
+ */
+xmlNodePtr 
+domReadWellBalancedString( xmlDocPtr doc, xmlChar* block ) {
+  int retCode       = -1;
+  xmlNodePtr helper = NULL;
+  xmlNodePtr nodes  = NULL;
+  
+  if ( doc && block ) {
+    /* read and encode the chunk */
+    retCode = xmlParseBalancedChunkMemory( doc, 
+                                           NULL,
+                                           NULL,
+                                           0,
+                                           block,
+                                           &nodes );
+    
+    /* error handling */
+    if ( retCode != 0 ) {
+      /* if the code was not well balanced, we will not return 
+       * a bad node list, but we have to free the nodes */
+      while( nodes != NULL ) {
+        helper = nodes->next;
+        xmlFreeNode( nodes );
+        nodes = helper;
+      }
+    }
+  }
+
+  return nodes;
+}
+
+xmlNodePtr
+domUnbindNode( xmlNodePtr );
+
+xmlNodePtr
+domAppendChild( xmlNodePtr self,
+		xmlNodePtr newChild ){
+  /* unbind the new node if nessecary ...  */
+  if ( newChild == 0 ){
+    return 0;
+  }
+  if ( self == 0 ) {
+    return newChild;
+  }
+
+  newChild= domUnbindNode( newChild );
+  /* fix the document if they are from different documents 
+   * actually this has to be done for ALL nodes in the subtree... 
+   **/
+  if ( self->doc != newChild->doc ) {
+    newChild->doc = self->doc;
+  }
+  
+  if ( self->children != 0 ) {
+    if ( newChild->type   == XML_TEXT_NODE && 
+	 self->last->type == XML_TEXT_NODE ) {
+      int len = xmlStrlen(newChild->content);
+      xmlNodeAddContentLen(self->last, newChild->content, len);
+      xmlFreeNode( newChild );
+      return self->last;
+    }
+    else {
+      self->last->next = newChild;
+      newChild->prev = self->last;
+      self->last = newChild;
+      newChild->parent= self;
+    }
+  }
+  else {
+    self->children = newChild;
+    self->last     = newChild;
+    newChild->parent= self;
+  }
+  return newChild;
+}
+
+xmlNodePtr
+domReplaceChild( xmlNodePtr self, xmlNodePtr new, xmlNodePtr old ) {
+  if ( new == 0 ) {
+    return old;
+  }
+  if ( self== 0 ){
+    return 0;
+  }
+  if ( old == 0 ) {
+    domAppendChild( self, new );
+    return old;
+  }
+  if ( old->parent != self ) {
+    /* should not do this!!! */
+    return new;
+  }
+  new = domUnbindNode( new ) ;
+  new->parent = self;
+  
+  /* this piece is quite important */
+  if ( new->doc != self->doc ) {
+    new->doc = self->doc;
+  }
+
+  if ( old->next != 0 ) 
+    old->next->prev = new;
+  if ( old->prev != 0 ) 
+    old->prev->next = new;
+  
+  new->next = old->next;
+  new->prev = old->prev;
+  
+  if ( old == self->children )
+    self->children = new;
+  if ( old == self->last )
+    self->last = new;
+  
+  old->parent = 0;
+  old->next   = 0;
+  old->prev   = 0;
+ 
+  return old;
+}
+
+xmlNodePtr
+domRemoveNode( xmlNodePtr self, xmlNodePtr old ) {
+  if ( (self != 0)  && (old!=0) && (self == old->parent ) ) {
+    domUnbindNode( old );
+  }
+  return old ;
+}
+
+void
+domSetNodeValue( xmlNodePtr n , xmlChar* val ){
+  if ( n == 0 ) 
+    return;
+  if( n->content != 0 ) {
+    xmlFree( n->content );
+  }
+  n->content = xmlStrdup( val );
+}
+
+
+void
+domSetParentNode( xmlNodePtr self, xmlNodePtr p ) {
+  if( self != 0 ){
+    if( self->parent != p ){
+      domUnbindNode( self );
+      self->parent = p;
+      if( p->doc != self->doc ) {
+	self->doc = p->doc;
+      }
+    }
+  }
+}
+
+xmlNodePtr
+domUnbindNode( xmlNodePtr self ) {
+  if ( (self != 0) && (self->parent != 0) ) { 
+    if ( self->next != 0 )
+      self->next->prev = self->prev;
+    if ( self->prev != 0 )
+      self->prev->next = self->next;
+    if ( self == self->parent->last ) 
+      self->parent->last = self->prev;
+    if ( self == self->parent->children ) 
+      self->parent->children = self->next;
+    
+    self->parent = 0;
+    self->next   = 0;
+    self->prev   = 0;
+  }
+
+  return self;
+}
+
+/**
+ * this is a wrapper function that does the type evaluation for the 
+ * node. this makes the code a little more readable in the .XS
+ * 
+ * the code is not really portable, but i think we'll avoid some 
+ * memory leak problems that way.
+ **/
+
+const char*
+domNodeTypeName( xmlNodePtr elem ){
+  const char *name = "XML::LibXML::Node";
+
+  if ( elem != 0 ) {
+    char * ptrHlp;
+    switch ( elem->type ) {
+    case XML_ELEMENT_NODE:
+      name = "XML::LibXML::Element";   
+      break;
+    case XML_TEXT_NODE:
+      name = "XML::LibXML::Text";
+      break;
+    case XML_COMMENT_NODE:
+      name = "XML::LibXML::Comment";
+      break;
+    case XML_CDATA_SECTION_NODE:
+      name = "XML::LibXML::CDATASection";
+      break;
+    default:
+      name = "XML::LibXML::Node";
+      break;
+    };
+    return name;
+  }
+
+  return "";
+}
+
+xmlNodePtr
+domCreateCDATASection( xmlDocPtr self , xmlChar * strNodeContent ){
+  xmlNodePtr elem = 0;
+
+  if ( ( self != 0 ) && ( strNodeContent != 0 ) ) {
+    elem = xmlNewCDataBlock( self, strNodeContent, xmlStrlen(strNodeContent) );
+    elem->next = 0;
+    elem->prev = 0;
+    elem->children = 0 ;
+    elem->last = 0;
+    elem->doc = self->doc;   
+  }
+
+  return elem;
+}
+
+
+xmlNodePtr 
+domDocumentElement( xmlDocPtr doc ) {
+  xmlNodePtr cld=0;
+  if ( doc != 0 && doc->doc != 0 && doc->doc->children != 0 ) {
+    cld= doc->doc->children;
+    while ( cld != 0 && cld->type != XML_ELEMENT_NODE ) 
+      cld= cld->next;
+  
+  }
+  return cld;
+}
+
+/**
+ * setDocumentElement:
+ * @doc: the document
+ * @newRoot: the new rootnode
+ *
+ * a document can have only ONE root node, so this function searches
+ * the first element and relaces this element with newRoot.
+ * 
+ * Returns the old root node.
+ **/
+xmlNodePtr
+domSetDocumentElement( xmlDocPtr doc, xmlNodePtr newRoot ) { 
+  return domReplaceChild( (xmlNodePtr)doc->doc, 
+			  newRoot, 
+			  domDocumentElement( doc )) ;
+}
+
+
+
+xmlNodeSetPtr
+domGetElementsByTagName( xmlNodePtr n, xmlChar* name ){
+  xmlNodeSetPtr rv = 0;
+  xmlNodePtr cld = 0;
+
+  if ( n != 0 && name != 0 ) {
+    cld = n->children;
+    while ( cld ) {
+      if ( xmlStrcmp( name, cld->name ) == 0 ){
+	if ( rv == 0 ) {
+	  rv = xmlXPathNodeSetCreate( cld ) ;
+	}
+	else {
+	  xmlXPathNodeSetAdd( rv, cld );
+	}
+      }
+      cld = cld->next;
+    }
+  }
+  
+  return rv;
+}
+/* dom.h
+ * Author: Christian Glahn (2001)
+ * 
+ * This header file provides some definitions for wrapper functions.
+ * These functions hide most of libxml2 code, and should make the
+ * code in the XS file more readable . 
+ *
+ * The Functions are sorted in four parts:
+ * part 0 ..... general wrapper functions which do not belong 
+ *              to any of the other parts and not specified in DOM. 
+ * part A ..... wrapper functions for general nodeaccess
+ * part B ..... document wrapper 
+ * part C ..... element wrapper
+ * 
+ * I did not implement any Text, CDATASection or comment wrapper functions,
+ * since it is pretty straightforeward to access these nodes. 
+ */
+
+#ifndef __LIBXML_DOM_H__
+#define __LIBXML_DOM_H__
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+
+/**
+ * part 0:
+ *
+ * unsortet. 
+ **/
+
+xmlDocPtr
+domCreateDocument( xmlChar* version, 
+                   xmlChar *encoding );
+
+xmlNodePtr 
+domReadWellBalancedString( xmlDocPtr doc, xmlChar* string );
+
+/**
+ * part A:
+ *
+ * class Node
+ **/
+
+/* A.1 DOM specified section */
+
+xmlNodePtr
+domAppendChild( xmlNodePtr self,
+                xmlNodePtr newChild );
+xmlNodePtr
+domReplaceChild( xmlNodePtr self,
+                 xmlNodePtr oldChlid,
+                 xmlNodePtr newChild );
+/* xmlNodePtr */
+/* domInsertBefore( xmlNodePtr self,  */
+/* 		 xmlNodePtr newChild, */
+/* 		 xmlNodePtr refChild ); */
+xmlNodePtr
+domRemoveNode( xmlNodePtr self,
+               xmlNodePtr Child );
+
+
+/* A.3 extra functionality not specified in DOM L1/2*/
+void
+domSetNodeValue( xmlNodePtr self, xmlChar* value );
+void
+domSetParentNode( xmlNodePtr self, 
+		  xmlNodePtr newParent );
+xmlNodePtr
+domUnbindNode(  xmlNodePtr self );
+
+const char*
+domNodeTypeName( xmlNodePtr self );
+
+/** 
+ * part B:
+ *
+ * class Document
+ **/
+
+xmlNodePtr
+domCreateCDATASection( xmlDocPtr self, xmlChar *content );
+/* extra document functions */ 
+xmlNodePtr
+domDocumentElement( xmlDocPtr document );
+xmlNodePtr
+domSetDocumentElement( xmlDocPtr document, 
+		       xmlNodePtr newRoot);
+
+/**
+ * part C:
+ *
+ * class Element
+ **/
+
+xmlNodeSetPtr
+domGetElementsByTagName( xmlNodePtr self, xmlChar* name );
+
+#endif