/* $Id: ruby_xml_document.c,v 1.34 2003/02/06 10:50:57 sean Exp $ */ /* Please see the LICENSE file for copyright and distribution information */ #include "libxml.h" #include "ruby_xml_document.h" VALUE cXMLDocument; VALUE ruby_xml_document_compression_get(VALUE self) { #ifdef HAVE_ZLIB_H ruby_xml_document *rxd; int compmode; Data_Get_Struct(self, ruby_xml_document, rxd); compmode = xmlGetDocCompressMode(rxd->doc); if (compmode == -1) return(Qnil); else return(INT2NUM(compmode)); #else rb_warn("libxml not compiled with zlib support"); return(Qfalse); #endif } VALUE ruby_xml_document_compression_set(VALUE self, VALUE num) { #ifdef HAVE_ZLIB_H ruby_xml_document *rxd; int compmode; Check_Type(num, T_FIXNUM); Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc == NULL) { return(Qnil); } else { xmlSetDocCompressMode(rxd->doc, NUM2INT(num)); compmode = xmlGetDocCompressMode(rxd->doc); if (compmode == -1) return(Qnil); else return(INT2NUM(compmode)); } #else rb_warn("libxml compiled without zlib support"); return(Qfalse); #endif } VALUE ruby_xml_document_compression_q(VALUE self) { #ifdef HAVE_ZLIB_H ruby_xml_document *rxd; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->compression != -1) return(Qtrue); else return(Qfalse); #else rb_warn("libxml compiled without zlib support"); return(Qfalse); #endif } VALUE ruby_xml_document_child_get(VALUE self) { ruby_xml_document *rxd; ruby_xml_node *rxn; VALUE node; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->children == NULL) return(Qnil); node = ruby_xml_node_new2(cXMLNode, self, rxd->doc->children); Data_Get_Struct(node, ruby_xml_node, rxn); rxn->xd = self; return(node); } VALUE ruby_xml_document_child_q(VALUE self) { ruby_xml_document *rxd; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->children == NULL) return(Qfalse); else return(Qtrue); } VALUE ruby_xml_document_dump(int argc, VALUE *argv, VALUE self) { OpenFile *fptr; VALUE io; FILE *out; ruby_xml_document *rxd; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc == NULL) return(Qnil); switch (argc) { case 0: io = rb_stdout; break; case 1: io = argv[0]; if (!rb_obj_is_kind_of(io, rb_cIO)) rb_raise(rb_eTypeError, "need an IO object"); break; default: rb_raise(rb_eArgError, "wrong number of arguments (0 or 1)"); } GetOpenFile(io, fptr); rb_io_check_writable(fptr); out = GetWriteFile(fptr); xmlDocDump(out, rxd->doc); return(Qtrue); } VALUE ruby_xml_document_debug_dump(int argc, VALUE *argv, VALUE self) { #ifdef LIBXML_DEBUG_ENABLED OpenFile *fptr; VALUE io; FILE *out; ruby_xml_document *rxd; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc == NULL) return(Qnil); switch (argc) { case 0: io = rb_stderr; break; case 1: io = argv[0]; if (!rb_obj_is_kind_of(io, rb_cIO)) rb_raise(rb_eTypeError, "need an IO object"); break; default: rb_raise(rb_eArgError, "wrong number of arguments (0 or 1)"); } GetOpenFile(io, fptr); rb_io_check_writable(fptr); out = GetWriteFile(fptr); xmlDebugDumpDocument(out, rxd->doc); return(Qtrue); #else rb_warn("libxml was compiled without debugging support. Please recompile libxml and ruby-libxml"); return(Qfalse); #endif } VALUE ruby_xml_document_debug_dump_head(int argc, VALUE *argv, VALUE self) { #ifdef LIBXML_DEBUG_ENABLED OpenFile *fptr; VALUE io; FILE *out; ruby_xml_document *rxd; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc == NULL) return(Qnil); switch (argc) { case 0: io = rb_stdout; break; case 1: io = argv[0]; if (!rb_obj_is_kind_of(io, rb_cIO)) rb_raise(rb_eTypeError, "need an IO object"); break; default: rb_raise(rb_eArgError, "wrong number of arguments (0 or 1)"); } GetOpenFile(io, fptr); rb_io_check_writable(fptr); out = GetWriteFile(fptr); xmlDebugDumpDocumentHead(out, rxd->doc); return(Qtrue); #else rb_warn("libxml was compiled without debugging support. Please recompile libxml and ruby-libxml"); return(Qfalse); #endif } VALUE ruby_xml_document_format_dump(int argc, VALUE *argv, VALUE self) { OpenFile *fptr; VALUE bool, io; FILE *out; ruby_xml_document *rxd; int size, spacing; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc == NULL) return(Qnil); switch (argc) { case 0: io = rb_stdout; spacing = 1; break; case 1: io = argv[0]; if (!rb_obj_is_kind_of(io, rb_cIO)) rb_raise(rb_eTypeError, "need an IO object"); spacing = 1; break; case 2: io = argv[0]; if (!rb_obj_is_kind_of(io, rb_cIO)) rb_raise(rb_eTypeError, "need an IO object"); bool = argv[1]; if (TYPE(bool) == T_TRUE) spacing = 1; else if (TYPE(bool) == T_FALSE) spacing = 0; else rb_raise(rb_eTypeError, "incorect argument type, second argument must be bool"); break; default: rb_raise(rb_eArgError, "wrong number of arguments (0 or 1)"); } GetOpenFile(io, fptr); rb_io_check_writable(fptr); out = GetWriteFile(fptr); size = xmlDocFormatDump(out, rxd->doc, spacing); return(INT2NUM(size)); } VALUE ruby_xml_document_debug_format_dump(int argc, VALUE *argv, VALUE self) { rb_warn("debug_format_dump has been deprecaited, use format_dump instead"); return(ruby_xml_document_format_dump(argc, argv, self)); } VALUE ruby_xml_document_encoding_get(VALUE self) { ruby_xml_document *rxd; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->encoding == NULL) return(Qnil); else return(rb_str_new2(rxd->doc->encoding)); } VALUE ruby_xml_document_encoding_set(VALUE self, VALUE encoding) { ruby_xml_document *rxd; Check_Type(encoding, T_STRING); Data_Get_Struct(self, ruby_xml_document, rxd); rxd->doc->encoding = ruby_strdup(STR2CSTR(encoding)); return(ruby_xml_document_encoding_get(self)); } VALUE ruby_xml_document_filename_get(VALUE self) { ruby_xml_document *rxd; rx_file_data *data; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->data == NULL) return(Qnil); switch (rxd->data_type) { case RUBY_LIBXML_SRC_TYPE_NULL: return(Qnil); case RUBY_LIBXML_SRC_TYPE_FILE: data = (rx_file_data *)rxd->data; return(data->filename); default: rb_fatal("Unknown document type in libxml"); } return(Qnil); } VALUE ruby_xml_document_find(VALUE self, VALUE xpath_str) { ruby_xml_document *doc; VALUE root; Data_Get_Struct(self, ruby_xml_document, doc); root = ruby_xml_node_new2(cXMLNode, self, xmlDocGetRootElement(doc->doc)); return(ruby_xml_xpath_find2(root, xpath_str)); } void ruby_xml_document_free(ruby_xml_document *rxd) { void *data; if (rxd->doc != NULL && !rxd->is_ptr) { xmlFreeDoc(rxd->doc); ruby_xml_parser_count--; } if (ruby_xml_parser_count == 0) xmlCleanupParser(); switch(rxd->data_type) { case RUBY_LIBXML_SRC_TYPE_NULL: break; case RUBY_LIBXML_SRC_TYPE_FILE: (rx_file_data *)data = (rx_file_data *)rxd->data; free((rx_file_data *)data); break; case RUBY_LIBXML_SRC_TYPE_STRING: (rx_string_data *)data = (rx_string_data *)rxd->data; free((rx_string_data *)data); break; case RUBY_LIBXML_SRC_TYPE_IO: (rx_io_data *)data = (rx_io_data *)rxd->data; free((rx_io_data *)data); break; default: rb_fatal("Unknown data type, %d", rxd->data_type); } free(rxd); } VALUE ruby_xml_document_initialize(int argc, VALUE *argv, VALUE class) { VALUE docobj, xmlver; switch (argc) { case 0: xmlver = rb_str_new2("1.0"); break; case 1: rb_scan_args(argc, argv, "01", &xmlver); break; default: rb_raise(rb_eArgError, "wrong number of arguments (need 0 or 1)"); } docobj = ruby_xml_document_new2(cXMLDocument, xmlver); return(docobj); } VALUE ruby_xml_document_last_get(VALUE self) { ruby_xml_document *rxd; ruby_xml_node *rxn; VALUE node; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->last == NULL) return(Qnil); node = ruby_xml_node_new2(cXMLNode, self, rxd->doc->last); Data_Get_Struct(node, ruby_xml_node, rxn); rxn->xd = self; return(node); } VALUE ruby_xml_document_last_q(VALUE self) { ruby_xml_document *rxd; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->last == NULL) return(Qfalse); else return(Qtrue); } static void ruby_xml_document_mark(ruby_xml_document *rxd) { if (rxd == NULL) return; if (!NIL_P(rxd->xmlver)) rb_gc_mark(rxd->xmlver); } VALUE ruby_xml_document_new(VALUE class, xmlDocPtr doc) { ruby_xml_document *rxd; rxd = ALLOC(ruby_xml_document); ruby_xml_parser_count++; rxd->data = NULL; rxd->data_type = RUBY_LIBXML_SRC_TYPE_NULL; rxd->doc = doc; rxd->is_ptr = 0; rxd->xmlver = Qnil; return(Data_Wrap_Struct(cXMLDocument, ruby_xml_document_mark, ruby_xml_document_free, rxd)); } VALUE ruby_xml_document_new2(VALUE class, VALUE xmlver) { ruby_xml_document *rxd; Check_Type(xmlver, T_STRING); rxd = ALLOC(ruby_xml_document); ruby_xml_parser_count++; rxd->data = NULL; rxd->data_type = RUBY_LIBXML_SRC_TYPE_NULL; rxd->doc = xmlNewDoc(STR2CSTR(xmlver)); rxd->is_ptr = 0; rxd->xmlver = xmlver; if (rxd->doc == NULL) rb_fatal("bad"); return(Data_Wrap_Struct(cXMLDocument, ruby_xml_document_mark, ruby_xml_document_free, rxd)); } VALUE ruby_xml_document_new3(VALUE class) { return(ruby_xml_document_new2(class, rb_str_new2("1.0"))); } VALUE ruby_xml_document_new4(VALUE class, xmlDocPtr doc) { ruby_xml_document *rxd; rxd = ALLOC(ruby_xml_document); rxd->data = NULL; rxd->data_type = RUBY_LIBXML_SRC_TYPE_NULL; rxd->doc = doc; rxd->is_ptr = 1; rxd->xmlver = Qnil; return(Data_Wrap_Struct(cXMLDocument, ruby_xml_document_mark, ruby_xml_document_free, rxd)); } VALUE ruby_xml_document_new_file(VALUE class, VALUE filename) { VALUE parser; parser = ruby_xml_parser_new(cXMLParser); ruby_xml_parser_filename_set(parser, filename); return(ruby_xml_parser_parse(parser)); } VALUE ruby_xml_document_next_get(VALUE self) { ruby_xml_document *rxd; ruby_xml_node *rxn; VALUE node; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->next == NULL) return(Qnil); node = ruby_xml_node_new2(cXMLNode, self, rxd->doc->next); Data_Get_Struct(node, ruby_xml_node, rxn); rxn->xd = self; return(node); } VALUE ruby_xml_document_next_q(VALUE self) { ruby_xml_document *rxd; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->next == NULL) return(Qfalse); else return(Qtrue); } VALUE ruby_xml_document_parent_get(VALUE self) { ruby_xml_document *rxd; ruby_xml_node *rxn; VALUE node; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->parent == NULL) return(Qnil); node = ruby_xml_node_new2(cXMLNode, self, rxd->doc->parent); Data_Get_Struct(node, ruby_xml_node, rxn); rxn->xd = self; return(node); } VALUE ruby_xml_document_parent_q(VALUE self) { ruby_xml_document *rxd; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->parent == NULL) return(Qfalse); else return(Qtrue); } VALUE ruby_xml_document_prev_get(VALUE self) { ruby_xml_document *rxd; ruby_xml_node *rxn; VALUE node; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->prev == NULL) return(Qnil); node = ruby_xml_node_new2(cXMLNode, self, rxd->doc->prev); Data_Get_Struct(node, ruby_xml_node, rxn); rxn->xd = self; return(node); } VALUE ruby_xml_document_prev_q(VALUE self) { ruby_xml_document *rxd; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->prev == NULL) return(Qfalse); else return(Qtrue); } VALUE ruby_xml_document_property_get(VALUE self, VALUE key) { VALUE node; node = ruby_xml_document_root_get(self); return(ruby_xml_node_property_get(node, key)); } VALUE ruby_xml_document_property_set(VALUE self, VALUE key, VALUE val) { VALUE node; node = ruby_xml_document_root_get(self); return(ruby_xml_node_property_set(node, key, val)); } VALUE ruby_xml_document_root_get(VALUE self) { ruby_xml_document *rxd; ruby_xml_node *rxn; VALUE node; xmlNodePtr root; Data_Get_Struct(self, ruby_xml_document, rxd); root = xmlDocGetRootElement(rxd->doc); if (root == NULL) return(Qnil); node = ruby_xml_node_new2(cXMLNode, self, root); Data_Get_Struct(node, ruby_xml_node, rxn); rxn->xd = self; return(node); } VALUE ruby_xml_document_root_set(VALUE self, VALUE node) { ruby_xml_document *rxd; ruby_xml_node *rxn; VALUE retnode; xmlNodePtr root; if (rb_obj_is_kind_of(node, cXMLNode) == Qfalse) rb_raise(rb_eTypeError, "must pass an XML::Node type object"); Data_Get_Struct(self, ruby_xml_document, rxd); Data_Get_Struct(node, ruby_xml_node, rxn); ruby_xml_node_set_ptr(node, 1); root = xmlDocSetRootElement(rxd->doc, rxn->node); if (root == NULL) return(Qnil); retnode = ruby_xml_node_new2(cXMLNode, self, root); return(retnode); } VALUE ruby_xml_document_save(int argc, VALUE *argv, VALUE self) { ruby_xml_document *rxd; xmlChar *filename; int format, len; format = 0; switch (argc) { case 1: break; case 2: if (TYPE(argv[1]) == T_TRUE) format = 1; else if (TYPE(argv[1]) == T_FALSE) format = 0; else rb_raise(rb_eTypeError, "wrong type of argument, must be bool"); break; default: rb_raise(rb_eArgError, "wrong number of arguments (0 or 1)"); } Check_Type(argv[0], T_STRING); filename = STR2CSTR(argv[0]); Data_Get_Struct(self, ruby_xml_document, rxd); len = xmlSaveFormatFileEnc(filename, rxd->doc, rxd->doc->encoding, format); if (len == -1) rb_fatal("Unable to write out file"); else return(INT2NUM(len)); } VALUE ruby_xml_document_standalone_q(VALUE self) { ruby_xml_document *rxd; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->standalone) return(Qtrue); else return(Qfalse); } VALUE ruby_xml_document_to_s(int argc, VALUE *argv, VALUE self) { ruby_xml_document *rxd; xmlChar *result; int format, len; switch (argc) { case 0: format = 1; break; case 1: if (TYPE(argv[0]) == T_TRUE) format = 1; else if (TYPE(argv[0]) == T_FALSE) format = 0; else rb_raise(rb_eTypeError, "wrong type of argument, must be bool"); break; default: rb_raise(rb_eArgError, "wrong number of arguments (0 or 1)"); } Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc == NULL) { return(Qnil); } else if (rxd->doc->encoding != NULL) { if (format) { xmlDocDumpFormatMemoryEnc(rxd->doc, &result, &len, rxd->doc->encoding, format); } else { xmlDocDumpMemoryEnc(rxd->doc, &result, &len, rxd->doc->encoding); } } else { if (format) xmlDocDumpFormatMemory(rxd->doc, &result, &len, format); else xmlDocDumpMemory(rxd->doc, &result, &len); } return(rb_str_new2(result)); } VALUE ruby_xml_document_url_get(VALUE self) { ruby_xml_document *rxd; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->URL == NULL) return(Qnil); else return(rb_str_new2(rxd->doc->URL)); } VALUE ruby_xml_document_version_get(VALUE self) { ruby_xml_document *rxd; Data_Get_Struct(self, ruby_xml_document, rxd); if (rxd->doc->version == NULL) return(Qnil); else return(rb_str_new2(rxd->doc->version)); } VALUE ruby_xml_document_xinclude(VALUE self) { #ifdef LIBXML_XINCLUDE_ENABLED ruby_xml_document *rxd; int ret; Data_Get_Struct(self, ruby_xml_document, rxd); ret = xmlXIncludeProcess(rxd->doc); if (ret >= 0) return(INT2NUM(ret)); else rb_raise(eXMLXIncludeError, "error processing xinclude directives in document"); #else rb_warn("libxml was compiled without XInclude support. Please recompile libxml and ruby-libxml"); return(Qfalse); #endif } void ruby_init_xml_document(void) { cXMLDocument = rb_define_class_under(mXML, "Document", rb_cObject); rb_define_singleton_method(cXMLDocument, "file", ruby_xml_document_new_file, 1); rb_define_singleton_method(cXMLDocument, "new", ruby_xml_document_initialize, -1); rb_define_method(cXMLDocument, "[]", ruby_xml_document_property_get, 1); rb_define_method(cXMLDocument, "[]=", ruby_xml_document_property_set, 2); rb_define_method(cXMLDocument, "child", ruby_xml_document_child_get, 0); rb_define_method(cXMLDocument, "child?", ruby_xml_document_child_q, 0); rb_define_method(cXMLDocument, "compression", ruby_xml_document_compression_get, 0); rb_define_method(cXMLDocument, "compression=", ruby_xml_document_compression_set, 1); rb_define_method(cXMLDocument, "compression?", ruby_xml_document_compression_q, 0); rb_define_method(cXMLDocument, "dump", ruby_xml_document_dump, -1); rb_define_method(cXMLDocument, "debug_dump", ruby_xml_document_debug_dump, -1); rb_define_method(cXMLDocument, "debug_dump_head", ruby_xml_document_debug_dump_head, -1); rb_define_method(cXMLDocument, "debug_format_dump", ruby_xml_document_debug_format_dump, -1); rb_define_method(cXMLDocument, "encoding", ruby_xml_document_encoding_get, 0); rb_define_method(cXMLDocument, "encoding=", ruby_xml_document_encoding_set, 1); rb_define_method(cXMLDocument, "filename", ruby_xml_document_filename_get, 0); rb_define_method(cXMLDocument, "find", ruby_xml_document_find, 1); rb_define_method(cXMLDocument, "format_dump", ruby_xml_document_format_dump, -1); rb_define_method(cXMLDocument, "last", ruby_xml_document_last_get, 0); rb_define_method(cXMLDocument, "last?", ruby_xml_document_last_q, 0); rb_define_method(cXMLDocument, "next", ruby_xml_document_next_get, 0); rb_define_method(cXMLDocument, "next?", ruby_xml_document_next_q, 0); rb_define_method(cXMLDocument, "parent", ruby_xml_document_parent_get, 0); rb_define_method(cXMLDocument, "parent?", ruby_xml_document_parent_q, 0); rb_define_method(cXMLDocument, "prev", ruby_xml_document_prev_get, 0); rb_define_method(cXMLDocument, "prev?", ruby_xml_document_prev_q, 0); rb_define_method(cXMLDocument, "root", ruby_xml_document_root_get, 0); rb_define_method(cXMLDocument, "root=", ruby_xml_document_root_set, 1); rb_define_method(cXMLDocument, "save", ruby_xml_document_save, -1); rb_define_method(cXMLDocument, "standalone?", ruby_xml_document_standalone_q, 0); rb_define_method(cXMLDocument, "to_s", ruby_xml_document_to_s, -1); rb_define_method(cXMLDocument, "url", ruby_xml_document_url_get, 0); rb_define_method(cXMLDocument, "version", ruby_xml_document_version_get, 0); rb_define_method(cXMLDocument, "xinclude", ruby_xml_document_xinclude, 0); }