libxml-ruby 2.0.2-x86-mingw32 → 2.0.3-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -1,6 +1,57 @@
1
1
  = Release History
2
2
 
3
- == 2.0.1 / 2011-04-17 Charlie Savage
3
+ == 2.0.3 / 2011-05-01 Charlie Savage
4
+
5
+ * The biggest change in this release is supporting the use of libxml-ruby in
6
+ native background Ruby threads. Previously, the use of libxml-ruby in
7
+ background threads in Ruby 1.9.x and higher would often cause
8
+ segmentation faults. This has now been fixed (Charlie Savage).
9
+
10
+ * Update Reader#expand so that returned node correctly remembers its
11
+ encoding in Ruby 1.9.x (zerebubuth).
12
+
13
+ * Add check to verify a node has not been deleted. This can happen when
14
+ a ruby variable holds a reference to a child node that gets freed
15
+ when its parent gets freed. Previously when this happened a
16
+ segmentation fault would occur, now an exception is raised (Charlie Savage, fixes
17
+ RubyForge #26839.
18
+
19
+ * Do not unlink nodes before internal validations have run - avoids
20
+ segmentation faults caused by freeing a node twice (Charlie Savage).
21
+
22
+ * Add support for Document#canonicalization (Victor Lin).
23
+
24
+ * Fix memory leak in Reader#lookup_namespace (Charlie Savage).
25
+
26
+ * Fix memory leak in Reader#[] (Nathan Kriege).
27
+
28
+ * Fix usage of @io instance variable (Jeffrey Taylor)
29
+
30
+ * Removed old sax error handling code that has been fixed in newer
31
+ versions of libxml (Charlie Savage).
32
+
33
+ * Code cleanup - remove unused variables and commented out code (Charlie Savage)
34
+
35
+ * Minor text changes and documentation fixes (Charlie Savage).
36
+
37
+ * Fix documentation error (fixes RubyForge #26888).
38
+
39
+ * Update documentation for Document#validation* methods (fixes RubyForge #24833).
40
+
41
+ * Update documentation and test (fixes Ruby Forge Issue #28770).
42
+
43
+ * Updated documentation in README (Anurag Priyam):
44
+ 1. rake doc does not work; use rake rdoc.
45
+ 2. gem mislav-hanna does not exist; use hanna.
46
+ 3. rake rdoc 'depends' on hanna; no need of RDOCOPTS
47
+ 4. Point to the github issue tracker instead of Ruby Forge
48
+ 5. Point to the github (gh-pages) site for docs
49
+
50
+ * Add tc_error to test suite (Charlie Savage).
51
+
52
+ * Add sax test (Stanislav O.Pogrebnyak).
53
+
54
+ == 2.0.2 / 2011-04-17 Charlie Savage
4
55
 
5
56
  * Added binaries for windows (Charlie Savage).
6
57
 
data/README.rdoc CHANGED
@@ -75,9 +75,9 @@ then install the libxslt gem which is available at
75
75
  http://rubyforge.org/projects/libxsl/.
76
76
 
77
77
  == Usage
78
- For information about using libxml-ruby please refer
79
- to its documentation at http://libxml.rubyforge.org/rdoc/index.html.
80
- Some tutorials are also available at https://github.com/xml4r/libxml-ruby/wiki.
78
+ For information about using libxml-ruby please refer to its documentation at
79
+ http://xml4r.github.com/libxml-ruby/rdoc/index.html Some tutorials are also
80
+ available at https://github.com/xml4r/libxml-ruby/wiki.
81
81
 
82
82
  All libxml classes are in the LibXML::XML module. The easiest
83
83
  way to use libxml is to require 'xml'. This will mixin
@@ -111,6 +111,34 @@ For example:
111
111
 
112
112
  For simplicity's sake, the documentation uses the xml module in its examples.
113
113
 
114
+ == Memory Management
115
+ libxml-ruby automatically manages memory associated with the
116
+ underlying libxml2 library. There is however one corner case that
117
+ your code must handle. If a node is imported into a document, but not
118
+ added to the document, a segmentation fault may occur on program termination.
119
+
120
+ # Do NOT do this
121
+ require 'xml'
122
+ doc1 = XML::Document.string("test1")
123
+ doc2 = XML::Document.string("test2")
124
+ node = doc2.import(doc1.root)
125
+
126
+ If doc2 is freed before node2 a segmentatin fault will occur since
127
+ node2 references the document. To avoid this, simply make sure to add the
128
+ node to the document:
129
+
130
+ # DO this instead
131
+ doc1 = XML::Document.string("test1")
132
+ doc2 = XML::Document.string("test2")
133
+ doc2.root << doc2.import(doc1.root)
134
+
135
+ Alternatively, you can call node2.remove! to disassociate node2 from doc2.
136
+
137
+ == Threading
138
+ libxml-ruby fully supports native, background Ruby threads. This of course
139
+ only applies to Ruby 1.9.x and higher since earlier versions of Ruby do not
140
+ support native threads.
141
+
114
142
  == Performance
115
143
  In addition to being feature rich and conformation, the main reason
116
144
  people use libxml-ruby is for performance. Here are the results
@@ -134,12 +162,14 @@ From https://svn.concord.org/svn/projects/trunk/common/ruby/xml_benchmarks/
134
162
 
135
163
 
136
164
  == Documentation
137
- Documentation is available via rdoc. To generate the documentation,
138
- run the the command 'rake doc'. libxml-ruby's online documentation
139
- is generated using Hanna. To use hanna:
165
+ Documentation is available via rdoc, and is installed automatically with the
166
+ gem.
167
+
168
+ libxml-ruby's online documentation is generated using Hanna. To generate
169
+ documentation from source:
140
170
 
141
- gem install mislav-hanna
142
- rake rdoc RDOCOPT="-S -T hanna"
171
+ gem install hanna
172
+ rake rdoc
143
173
 
144
174
  Note that older versions of Rdoc, which ship with Ruby 1.8.x, will report
145
175
  a number of errors. To avoid them, install Rdoc 2.1 or higher from
@@ -152,8 +182,8 @@ ruby/lib/ruby/1.8/rdoc_old.
152
182
 
153
183
  If you have any questions about using libxml-ruby, please send them to
154
184
  libxml-devel@rubyforge.org. If you have found any bugs in libxml-devel,
155
- or have developed new patches, please submit them to Ruby Forge at
156
- http://rubyforge.org/tracker/?group_id=494.
185
+ or have developed new patches, please submit them to Git Hub at
186
+ https://github.com/xml4r/libxml-ruby/issues.
157
187
 
158
188
  == License
159
189
  See LICENSE for license information.
@@ -14,6 +14,7 @@
14
14
  #include <libxml/catalog.h>
15
15
  #include <libxml/HTMLparser.h>
16
16
  #include <libxml/xmlreader.h>
17
+ #include <libxml/c14n.h>
17
18
 
18
19
  /* Needed for Ruby 1.8.5 */
19
20
  #ifndef RARRAY_LEN
@@ -186,7 +186,7 @@ static VALUE rxml_attr_name_get(VALUE self)
186
186
  if (xattr->name == NULL)
187
187
  return Qnil;
188
188
  else
189
- return rxml_str_new2((const char*) xattr->name, xattr->doc->encoding);
189
+ return rxml_str_new2((const char*) xattr->name, (xattr->doc ? xattr->doc->encoding : NULL));
190
190
  }
191
191
 
192
192
  /*
@@ -302,7 +302,7 @@ VALUE rxml_attr_value_get(VALUE self)
302
302
 
303
303
  if (value != NULL)
304
304
  {
305
- result = rxml_str_new2((const char*) value, xattr->doc->encoding);
305
+ result = rxml_str_new2((const char*) value, (xattr->doc ? xattr->doc->encoding : NULL));
306
306
  xmlFree(value);
307
307
  }
308
308
  return result;
@@ -20,7 +20,7 @@
20
20
  * doc = XML::Document.new()
21
21
  * doc.root = XML::Node.new('root_node')
22
22
  * doc.root << XML::Node.new('elem1')
23
- * doc.save(filename, :indent => true, :encoding => 'UTF-8')
23
+ * doc.save(filename, :indent => true, :encoding => XML::Encoding::UTF_8)
24
24
  *
25
25
  * To write a document to a file:
26
26
  *
@@ -45,7 +45,7 @@
45
45
  *
46
46
  * elem3['attr'] = 'baz'
47
47
  *
48
- * doc.save(filename, :indent => true, :encoding => 'UTF-8')
48
+ * doc.save(filename, :indent => true, :encoding => XML::Encoding::UTF_8)
49
49
  */
50
50
 
51
51
  #include <stdarg.h>
@@ -123,6 +123,40 @@ static VALUE rxml_document_initialize(int argc, VALUE *argv, VALUE self)
123
123
  return self;
124
124
  }
125
125
 
126
+ /*
127
+ * call-seq:
128
+ * document.canonicalize(comments) -> String
129
+ *
130
+ * Returns a string containing the canonicalized form of the document.
131
+ *
132
+ * :comments - Specifies if comments should be output. This is an optional
133
+ * parameter whose default value is false.
134
+ */
135
+ static VALUE rxml_document_canonicalize(int argc, VALUE *argv, VALUE self)
136
+ {
137
+ VALUE result = Qnil;
138
+ VALUE comments = Qnil ;
139
+ xmlDocPtr xdoc;
140
+ xmlChar *buffer = NULL;
141
+ int length;
142
+
143
+ rb_scan_args(argc, argv, "01", &comments);
144
+
145
+ Data_Get_Struct(self, xmlDoc, xdoc);
146
+ length = xmlC14NDocDumpMemory(xdoc, NULL, XML_C14N_1_1, NULL,
147
+ (comments == Qtrue ? 1 : 0),
148
+ &buffer);
149
+
150
+ if (buffer)
151
+ {
152
+ result = rxml_str_new2((const char*) buffer, (const char*)xdoc->encoding);
153
+ xmlFree(buffer);
154
+ }
155
+
156
+ return result;
157
+ }
158
+
159
+
126
160
  /*
127
161
  * call-seq:
128
162
  * document.compression -> num
@@ -285,16 +319,17 @@ static VALUE rxml_document_encoding_get(VALUE self)
285
319
  * Returns the Ruby encoding specified by this document
286
320
  * (available on Ruby 1.9.x and higher).
287
321
  */
322
+ #ifdef HAVE_RUBY_ENCODING_H
288
323
  static VALUE rxml_document_rb_encoding_get(VALUE self)
289
324
  {
290
325
  xmlDocPtr xdoc;
291
326
  const char *xencoding;
292
- VALUE encoding;
293
327
  Data_Get_Struct(self, xmlDoc, xdoc);
294
328
 
295
329
  xencoding = (const char*)xdoc->encoding;
296
330
  return rxml_xml_encoding_to_rb_encoding(mXMLEncoding, xmlParseCharEncoding(xencoding));
297
331
  }
332
+ #endif
298
333
 
299
334
  /*
300
335
  * call-seq:
@@ -322,6 +357,11 @@ static VALUE rxml_document_encoding_set(VALUE self, VALUE encoding)
322
357
  *
323
358
  * Creates a copy of the node that can be inserted into the
324
359
  * current document.
360
+ *
361
+ * IMPORTANT - The returned node MUST be inserted into the document.
362
+ * This is because the returned node refereces internal LibXML data
363
+ * structures owned by the document. Therefore, if the document is
364
+ * is freed before the the node is freed a segmentation fault will occur.
325
365
  */
326
366
  static VALUE rxml_document_import(VALUE self, VALUE node)
327
367
  {
@@ -534,6 +574,9 @@ static VALUE rxml_document_root_set(VALUE self, VALUE node)
534
574
  Data_Get_Struct(self, xmlDoc, xdoc);
535
575
  Data_Get_Struct(node, xmlNode, xnode);
536
576
 
577
+ if (xnode->doc != NULL && xnode->doc != xdoc)
578
+ rb_raise(eXMLError, "Nodes belong to different documents. You must first import the node by calling XML::Document.import");
579
+
537
580
  xroot = xmlDocSetRootElement(xdoc, xnode);
538
581
  return node;
539
582
  }
@@ -541,7 +584,7 @@ static VALUE rxml_document_root_set(VALUE self, VALUE node)
541
584
  /*
542
585
  * call-seq:
543
586
  * document.save(filename) -> int
544
- * document.save(filename, :indent => true, :encoding => 'UTF-8') -> int
587
+ * document.save(filename, :indent => true, :encoding => XML::Encoding::UTF_8) -> int
545
588
  *
546
589
  * Saves a document to a file. You may provide an optional hash table
547
590
  * to control how the string is generated. Valid options are:
@@ -619,7 +662,7 @@ static VALUE rxml_document_standalone_q(VALUE self)
619
662
  /*
620
663
  * call-seq:
621
664
  * document.to_s -> "string"
622
- * document.to_s(:indent => true, :encoding => 'UTF-8') -> "string"
665
+ * document.to_s(:indent => true, :encoding => XML::Encoding::UTF_8) -> "string"
623
666
  *
624
667
  * Converts a document, and all of its children, to a string representation.
625
668
  * You may provide an optional hash table to control how the string is
@@ -771,9 +814,11 @@ static VALUE rxml_document_order_elements(VALUE self)
771
814
 
772
815
  /*
773
816
  * call-seq:
774
- * document.validate_schema(schema) -> (true|false)
817
+ * document.validate_schema(schema)
775
818
  *
776
819
  * Validate this document against the specified XML::Schema.
820
+ * If the document is valid the method returns true. Otherwise an
821
+ * exception is raised with validation information.
777
822
  */
778
823
  static VALUE rxml_document_validate_schema(VALUE self, VALUE schema)
779
824
  {
@@ -802,9 +847,11 @@ static VALUE rxml_document_validate_schema(VALUE self, VALUE schema)
802
847
 
803
848
  /*
804
849
  * call-seq:
805
- * document.validate_schema(relaxng) -> (true|false)
850
+ * document.validate_relaxng(relaxng)
806
851
  *
807
852
  * Validate this document against the specified XML::RelaxNG.
853
+ * If the document is valid the method returns true. Otherwise an
854
+ * exception is raised with validation information.
808
855
  */
809
856
  static VALUE rxml_document_validate_relaxng(VALUE self, VALUE relaxng)
810
857
  {
@@ -836,6 +883,8 @@ static VALUE rxml_document_validate_relaxng(VALUE self, VALUE relaxng)
836
883
  * document.validate(dtd) -> (true|false)
837
884
  *
838
885
  * Validate this document against the specified XML::DTD.
886
+ * If the document is valid the method returns true. Otherwise an
887
+ * exception is raised with validation information.
839
888
  */
840
889
  static VALUE rxml_document_validate_dtd(VALUE self, VALUE dtd)
841
890
  {
@@ -871,6 +920,7 @@ void rxml_init_document(void)
871
920
  rb_define_alloc_func(cXMLDocument, rxml_document_alloc);
872
921
 
873
922
  rb_define_method(cXMLDocument, "initialize", rxml_document_initialize, -1);
923
+ rb_define_method(cXMLDocument, "canonicalize", rxml_document_canonicalize, -1);
874
924
  rb_define_method(cXMLDocument, "child", rxml_document_child_get, 0);
875
925
  rb_define_method(cXMLDocument, "child?", rxml_document_child_q, 0);
876
926
  rb_define_method(cXMLDocument, "compression", rxml_document_compression_get, 0);
@@ -131,18 +131,6 @@ static void structuredErrorFunc(void *userData, xmlErrorPtr xerror)
131
131
  /* Wrap error up as Ruby object and send it off to ruby */
132
132
  VALUE block = rb_cvar_get(eXMLError, ERROR_HANDLER_ID);
133
133
 
134
- /* This next bit of code is a total hack to get around a bug
135
- in libxml which causes error handlers on sax handlers to
136
- be ignored in favor of the global handler. In addition,
137
- the correct context is also not passed in. So try to
138
- dig it out. */
139
- if (!userData && xerror->ctxt)
140
- {
141
- xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) xerror->ctxt;
142
- if (ctxt != ctxt->userData)
143
- userData = ctxt->userData;
144
- }
145
-
146
134
  /* Now call global handler */
147
135
  if (block != Qnil)
148
136
  {
@@ -33,11 +33,11 @@ VALUE cXMLNode;
33
33
  * a libxml document. When a Ruby node is freed, the _private
34
34
  * field is set back to null.
35
35
  *
36
- * In the sweep phase in Ruby 1.9.1, the document is freed before
37
- * the nodes. To support that, the bindingds register a callback
38
- * function with libxml that is called each time a node is freed.
39
- * In that case, the data_ptr is set to null, so the bindings
40
- * can recognize the situtation.
36
+ * In the sweep phase in Ruby 1.9.*, the document tends to be
37
+ * freed before the nodes. To support this, the bindings register
38
+ * a callback function with libxml that is called each time a node
39
+ * is freed. In that case, the data_ptr is set to null, so the bindings
40
+ * can recognize the situation.
41
41
  */
42
42
 
43
43
  static void rxml_node_deregisterNode(xmlNodePtr xnode)
@@ -50,6 +50,8 @@ static void rxml_node_deregisterNode(xmlNodePtr xnode)
50
50
  try to free the node a second time. */
51
51
  VALUE node = (VALUE) xnode->_private;
52
52
  RDATA(node)->data = NULL;
53
+ RDATA(node)->dfree = NULL;
54
+ RDATA(node)->dmark = NULL;
53
55
  }
54
56
  }
55
57
 
@@ -64,40 +66,46 @@ static void rxml_node_free(xmlNodePtr xnode)
64
66
  /* The ruby object wrapping the xml object no longer exists. */
65
67
  xnode->_private = NULL;
66
68
 
67
- /* Ruby is responsible for freeing this node since it
68
- has no parent. */
69
+ /* Ruby is responsible for freeing this node if it does not
70
+ have a parent and is not owned by a document. Note a corner
71
+ case here - calling node2 = doc.import(node1) will cause node2
72
+ to not have a parent but to have a document. */
69
73
  if (xnode->parent == NULL)
74
+ {
70
75
  xmlFreeNode(xnode);
76
+ }
71
77
  }
72
78
 
73
79
  void rxml_node_mark(xmlNodePtr xnode)
74
80
  {
75
- /* Either the node has been created yet in initialize
81
+ /* Either the node has not been created yet in initialize
76
82
  or it has been freed by libxml already in Ruby's
77
83
  mark phase. */
78
84
  if (xnode == NULL)
79
85
  return;
80
86
 
81
- if (xnode->doc != NULL)
87
+ if (xnode->doc && xnode->doc->_private)
82
88
  rb_gc_mark((VALUE) xnode->doc->_private);
83
89
 
84
- if (xnode->parent != NULL)
90
+ if (xnode->parent && xnode->parent->_private)
85
91
  rb_gc_mark((VALUE) xnode->_private);
86
92
  }
87
93
 
88
94
  VALUE rxml_node_wrap(xmlNodePtr xnode)
89
95
  {
96
+ VALUE result;
97
+
90
98
  /* Is the node already wrapped? */
91
99
  if (xnode->_private != NULL)
92
100
  {
93
- return (VALUE) xnode->_private;
101
+ result = (VALUE) xnode->_private;
94
102
  }
95
103
  else
96
104
  {
97
- VALUE node = Data_Wrap_Struct(cXMLNode, rxml_node_mark, rxml_node_free, xnode);
98
- xnode->_private = (void*) node;
99
- return node;
105
+ result = Data_Wrap_Struct(cXMLNode, rxml_node_mark, rxml_node_free, xnode);
106
+ xnode->_private = (void*) result;
100
107
  }
108
+ return result;
101
109
  }
102
110
 
103
111
  static VALUE rxml_node_alloc(VALUE klass)
@@ -107,6 +115,17 @@ static VALUE rxml_node_alloc(VALUE klass)
107
115
  return Data_Wrap_Struct(klass, rxml_node_mark, rxml_node_free, NULL);
108
116
  }
109
117
 
118
+ static xmlNodePtr rxml_get_xnode(VALUE node)
119
+ {
120
+ xmlNodePtr result;
121
+ Data_Get_Struct(node, xmlNode, result);
122
+
123
+ if (!result)
124
+ rb_raise(rb_eRuntimeError, "This node has already been freed.");
125
+
126
+ return result;
127
+ }
128
+
110
129
  /*
111
130
  * call-seq:
112
131
  * XML::Node.new_cdata(content = nil) -> XML::Node
@@ -237,11 +256,13 @@ static VALUE rxml_node_modify_dom(VALUE self, VALUE target,
237
256
  if (rb_obj_is_kind_of(target, cXMLNode) == Qfalse)
238
257
  rb_raise(rb_eTypeError, "Must pass an XML::Node object");
239
258
 
240
- Data_Get_Struct(self, xmlNode, xnode);
241
- Data_Get_Struct(target, xmlNode, xtarget);
259
+ xnode = rxml_get_xnode(self);
260
+ xtarget = rxml_get_xnode(target);
242
261
 
243
262
  if (xtarget->doc != NULL && xtarget->doc != xnode->doc)
244
- rb_raise(eXMLError, "Nodes belong to different documents. You must first import the by calling XML::Document.import");
263
+ rb_raise(eXMLError, "Nodes belong to different documents. You must first import the node by calling XML::Document.import");
264
+
265
+ xmlUnlinkNode(xtarget);
245
266
 
246
267
  /* This target node could be freed here. */
247
268
  xresult = xmlFunc(xnode, xtarget);
@@ -271,7 +292,7 @@ static VALUE rxml_node_base_uri_get(VALUE self)
271
292
  xmlChar* base_uri;
272
293
  VALUE result = Qnil;
273
294
 
274
- Data_Get_Struct(self, xmlNode, xnode);
295
+ xnode = rxml_get_xnode(self);
275
296
 
276
297
  if (xnode->doc == NULL)
277
298
  return (result);
@@ -299,7 +320,7 @@ static VALUE rxml_node_base_uri_set(VALUE self, VALUE uri)
299
320
  xmlNodePtr xnode;
300
321
 
301
322
  Check_Type(uri, T_STRING);
302
- Data_Get_Struct(self, xmlNode, xnode);
323
+ xnode = rxml_get_xnode(self);
303
324
  if (xnode->doc == NULL)
304
325
  return (Qnil);
305
326
 
@@ -319,7 +340,7 @@ static VALUE rxml_node_content_get(VALUE self)
319
340
  xmlChar *content;
320
341
  VALUE result = Qnil;
321
342
 
322
- Data_Get_Struct(self, xmlNode, xnode);
343
+ xnode = rxml_get_xnode(self);
323
344
  content = xmlNodeGetContent(xnode);
324
345
  if (content)
325
346
  {
@@ -341,7 +362,7 @@ static VALUE rxml_node_content_set(VALUE self, VALUE content)
341
362
  xmlNodePtr xnode;
342
363
 
343
364
  Check_Type(content, T_STRING);
344
- Data_Get_Struct(self, xmlNode, xnode);
365
+ xnode = rxml_get_xnode(self);
345
366
  // XXX docs indicate need for escaping entites, need to be done? danj
346
367
  xmlNodeSetContent(xnode, (xmlChar*) StringValuePtr(content));
347
368
  return (Qtrue);
@@ -362,7 +383,7 @@ static VALUE rxml_node_content_stripped_get(VALUE self)
362
383
  xmlChar* content;
363
384
  VALUE result = Qnil;
364
385
 
365
- Data_Get_Struct(self, xmlNode, xnode);
386
+ xnode = rxml_get_xnode(self);
366
387
 
367
388
  if (!xnode->content)
368
389
  return result;
@@ -387,7 +408,7 @@ static VALUE rxml_node_debug(VALUE self)
387
408
  {
388
409
  #ifdef LIBXML_DEBUG_ENABLED
389
410
  xmlNodePtr xnode;
390
- Data_Get_Struct(self, xmlNode, xnode);
411
+ xnode = rxml_get_xnode(self);
391
412
  xmlDebugDumpNode(NULL, xnode, 2);
392
413
  return Qtrue;
393
414
  #else
@@ -406,7 +427,7 @@ static VALUE rxml_node_first_get(VALUE self)
406
427
  {
407
428
  xmlNodePtr xnode;
408
429
 
409
- Data_Get_Struct(self, xmlNode, xnode);
430
+ xnode = rxml_get_xnode(self);
410
431
 
411
432
  if (xnode->children)
412
433
  return (rxml_node_wrap(xnode->children));
@@ -434,16 +455,14 @@ static VALUE rxml_node_content_add(VALUE self, VALUE obj)
434
455
  xmlNodePtr xnode;
435
456
  VALUE str;
436
457
 
437
- Data_Get_Struct(self, xmlNode, xnode);
458
+ xnode = rxml_get_xnode(self);
459
+
438
460
  /* XXX This should only be legal for a CDATA type node, I think,
439
461
  * resulting in a merge of content, as if a string were passed
440
462
  * danj 070827
441
463
  */
442
464
  if (rb_obj_is_kind_of(obj, cXMLNode))
443
465
  {
444
- xmlNodePtr xtarget;
445
- Data_Get_Struct(obj, xmlNode, xtarget);
446
- xmlUnlinkNode(xtarget);
447
466
  rxml_node_modify_dom(self, obj, xmlAddChild);
448
467
  }
449
468
  else
@@ -465,41 +484,32 @@ static VALUE rxml_node_content_add(VALUE self, VALUE obj)
465
484
  */
466
485
  static VALUE rxml_node_doc(VALUE self)
467
486
  {
468
- xmlNodePtr xnode;
469
- xmlDocPtr doc = NULL;
470
-
471
- Data_Get_Struct(self, xmlNode, xnode);
487
+ xmlDocPtr xdoc = NULL;
488
+ xmlNodePtr xnode = rxml_get_xnode(self);
472
489
 
473
490
  switch (xnode->type)
474
491
  {
475
492
  case XML_DOCUMENT_NODE:
476
493
  #ifdef LIBXML_DOCB_ENABLED
477
- case XML_DOCB_DOCUMENT_NODE:
494
+ case XML_DOCB_DOCUMENT_NODE:
478
495
  #endif
479
496
  case XML_HTML_DOCUMENT_NODE:
480
- doc = NULL;
497
+ case XML_NAMESPACE_DECL:
481
498
  break;
482
499
  case XML_ATTRIBUTE_NODE:
483
- {
484
- xmlAttrPtr attr = (xmlAttrPtr) xnode;
485
- doc = attr->doc;
486
- break;
487
- }
488
- case XML_NAMESPACE_DECL:
489
- doc = NULL;
500
+ xdoc = (xmlDocPtr)((xmlAttrPtr) xnode->doc);
490
501
  break;
491
502
  default:
492
- doc = xnode->doc;
493
- break;
503
+ xdoc = xnode->doc;
494
504
  }
495
505
 
496
- if (doc == NULL)
506
+ if (xdoc == NULL)
497
507
  return (Qnil);
498
-
499
- if (doc->_private == NULL)
500
- rb_raise(rb_eRuntimeError, "existing document object has no ruby-instance");
501
-
502
- return (VALUE) doc->_private;
508
+ else if (xdoc->_private)
509
+ return (VALUE) xdoc->_private;
510
+ else
511
+ /* This can happen by calling Reader#expand.doc */
512
+ rb_raise(eXMLError, "Document is not accessible to Ruby (hint - did you call Reader#expand?)");
503
513
  }
504
514
 
505
515
  /*
@@ -567,7 +577,8 @@ static VALUE rxml_node_to_s(int argc, VALUE *argv, VALUE self)
567
577
  encodingHandler = xmlFindCharEncodingHandler(xencoding);
568
578
  output = xmlAllocOutputBuffer(encodingHandler);
569
579
 
570
- Data_Get_Struct(self, xmlNode, xnode);
580
+ xnode = rxml_get_xnode(self);
581
+
571
582
  xmlNodeDumpOutput(output, xnode->doc, xnode, level, indent, xencoding);
572
583
  xmlOutputBufferFlush(output);
573
584
 
@@ -597,7 +608,7 @@ static VALUE rxml_node_each(VALUE self)
597
608
  {
598
609
  xmlNodePtr xnode;
599
610
  xmlNodePtr xcurrent;
600
- Data_Get_Struct(self, xmlNode, xnode);
611
+ xnode = rxml_get_xnode(self);
601
612
 
602
613
  xcurrent = xnode->children;
603
614
 
@@ -617,12 +628,12 @@ static VALUE rxml_node_each(VALUE self)
617
628
  * call-seq:
618
629
  * node.empty? -> (true|false)
619
630
  *
620
- * Determine whether this node is empty.
631
+ * Determine whether this node is an empty or whitespace only text-node.
621
632
  */
622
633
  static VALUE rxml_node_empty_q(VALUE self)
623
634
  {
624
635
  xmlNodePtr xnode;
625
- Data_Get_Struct(self, xmlNode, xnode);
636
+ xnode = rxml_get_xnode(self);
626
637
  if (xnode == NULL)
627
638
  return (Qnil);
628
639
 
@@ -638,26 +649,26 @@ static VALUE rxml_node_empty_q(VALUE self)
638
649
  * if they are the same node or have the same XML representation.*/
639
650
  static VALUE rxml_node_eql_q(VALUE self, VALUE other)
640
651
  {
641
- if(self == other)
642
- {
643
- return Qtrue;
644
- }
645
- else if (NIL_P(other))
646
- {
647
- return Qfalse;
648
- }
649
- else
650
- {
651
- VALUE self_xml;
652
- VALUE other_xml;
652
+ if(self == other)
653
+ {
654
+ return Qtrue;
655
+ }
656
+ else if (NIL_P(other))
657
+ {
658
+ return Qfalse;
659
+ }
660
+ else
661
+ {
662
+ VALUE self_xml;
663
+ VALUE other_xml;
653
664
 
654
- if (rb_obj_is_kind_of(other, cXMLNode) == Qfalse)
655
- rb_raise(rb_eTypeError, "Nodes can only be compared against other nodes");
665
+ if (rb_obj_is_kind_of(other, cXMLNode) == Qfalse)
666
+ rb_raise(rb_eTypeError, "Nodes can only be compared against other nodes");
656
667
 
657
- self_xml = rxml_node_to_s(0, NULL, self);
658
- other_xml = rxml_node_to_s(0, NULL, other);
659
- return(rb_funcall(self_xml, rb_intern("=="), 1, other_xml));
660
- }
668
+ self_xml = rxml_node_to_s(0, NULL, self);
669
+ other_xml = rxml_node_to_s(0, NULL, other);
670
+ return(rb_funcall(self_xml, rb_intern("=="), 1, other_xml));
671
+ }
661
672
  }
662
673
 
663
674
  /*
@@ -673,7 +684,7 @@ static VALUE rxml_node_lang_get(VALUE self)
673
684
  xmlChar *lang;
674
685
  VALUE result = Qnil;
675
686
 
676
- Data_Get_Struct(self, xmlNode, xnode);
687
+ xnode = rxml_get_xnode(self);
677
688
  lang = xmlNodeGetLang(xnode);
678
689
 
679
690
  if (lang)
@@ -699,7 +710,7 @@ static VALUE rxml_node_lang_set(VALUE self, VALUE lang)
699
710
  xmlNodePtr xnode;
700
711
 
701
712
  Check_Type(lang, T_STRING);
702
- Data_Get_Struct(self, xmlNode, xnode);
713
+ xnode = rxml_get_xnode(self);
703
714
  xmlNodeSetLang(xnode, (xmlChar*) StringValuePtr(lang));
704
715
 
705
716
  return (Qtrue);
@@ -715,7 +726,7 @@ static VALUE rxml_node_last_get(VALUE self)
715
726
  {
716
727
  xmlNodePtr xnode;
717
728
 
718
- Data_Get_Struct(self, xmlNode, xnode);
729
+ xnode = rxml_get_xnode(self);
719
730
 
720
731
  if (xnode->last)
721
732
  return (rxml_node_wrap(xnode->last));
@@ -735,7 +746,7 @@ static VALUE rxml_node_line_num(VALUE self)
735
746
  {
736
747
  xmlNodePtr xnode;
737
748
  long line_num;
738
- Data_Get_Struct(self, xmlNode, xnode);
749
+ xnode = rxml_get_xnode(self);
739
750
 
740
751
  if (!xmlLineNumbersDefaultValue)
741
752
  rb_warn(
@@ -759,7 +770,7 @@ static VALUE rxml_node_xlink_q(VALUE self)
759
770
  xmlNodePtr xnode;
760
771
  xlinkType xlt;
761
772
 
762
- Data_Get_Struct(self, xmlNode, xnode);
773
+ xnode = rxml_get_xnode(self);
763
774
  xlt = xlinkIsLink(xnode->doc, xnode);
764
775
 
765
776
  if (xlt == XLINK_TYPE_NONE)
@@ -781,7 +792,7 @@ static VALUE rxml_node_xlink_type(VALUE self)
781
792
  xmlNodePtr xnode;
782
793
  xlinkType xlt;
783
794
 
784
- Data_Get_Struct(self, xmlNode, xnode);
795
+ xnode = rxml_get_xnode(self);
785
796
  xlt = xlinkIsLink(xnode->doc, xnode);
786
797
 
787
798
  if (xlt == XLINK_TYPE_NONE)
@@ -803,7 +814,7 @@ static VALUE rxml_node_xlink_type_name(VALUE self)
803
814
  xmlNodePtr xnode;
804
815
  xlinkType xlt;
805
816
 
806
- Data_Get_Struct(self, xmlNode, xnode);
817
+ xnode = rxml_get_xnode(self);
807
818
  xlt = xlinkIsLink(xnode->doc, xnode);
808
819
 
809
820
  switch (xlt)
@@ -832,7 +843,7 @@ static VALUE rxml_node_name_get(VALUE self)
832
843
  xmlNodePtr xnode;
833
844
  const xmlChar *name;
834
845
 
835
- Data_Get_Struct(self, xmlNode, xnode);
846
+ xnode = rxml_get_xnode(self);
836
847
 
837
848
  switch (xnode->type)
838
849
  {
@@ -881,7 +892,7 @@ static VALUE rxml_node_name_set(VALUE self, VALUE name)
881
892
  const xmlChar *xname;
882
893
 
883
894
  Check_Type(name, T_STRING);
884
- Data_Get_Struct(self, xmlNode, xnode);
895
+ xnode = rxml_get_xnode(self);
885
896
  xname = (const xmlChar*)StringValuePtr(name);
886
897
 
887
898
  /* Note: calling xmlNodeSetName() for a text node is ignored by libXML. */
@@ -900,7 +911,7 @@ static VALUE rxml_node_next_get(VALUE self)
900
911
  {
901
912
  xmlNodePtr xnode;
902
913
 
903
- Data_Get_Struct(self, xmlNode, xnode);
914
+ xnode = rxml_get_xnode(self);
904
915
 
905
916
  if (xnode->next)
906
917
  return (rxml_node_wrap(xnode->next));
@@ -933,7 +944,7 @@ static VALUE rxml_node_parent_get(VALUE self)
933
944
  {
934
945
  xmlNodePtr xnode;
935
946
 
936
- Data_Get_Struct(self, xmlNode, xnode);
947
+ xnode = rxml_get_xnode(self);
937
948
 
938
949
  if (xnode->parent)
939
950
  return (rxml_node_wrap(xnode->parent));
@@ -952,7 +963,7 @@ static VALUE rxml_node_path(VALUE self)
952
963
  xmlNodePtr xnode;
953
964
  xmlChar *path;
954
965
 
955
- Data_Get_Struct(self, xmlNode, xnode);
966
+ xnode = rxml_get_xnode(self);
956
967
  path = xmlGetNodePath(xnode);
957
968
 
958
969
  if (path == NULL)
@@ -982,7 +993,7 @@ static VALUE rxml_node_prev_get(VALUE self)
982
993
  {
983
994
  xmlNodePtr xnode;
984
995
  xmlNodePtr node;
985
- Data_Get_Struct(self, xmlNode, xnode);
996
+ xnode = rxml_get_xnode(self);
986
997
 
987
998
  switch (xnode->type)
988
999
  {
@@ -1036,7 +1047,7 @@ static VALUE rxml_node_attributes_get(VALUE self)
1036
1047
  {
1037
1048
  xmlNodePtr xnode;
1038
1049
 
1039
- Data_Get_Struct(self, xmlNode, xnode);
1050
+ xnode = rxml_get_xnode(self);
1040
1051
  return rxml_attributes_new(xnode);
1041
1052
  }
1042
1053
 
@@ -1077,7 +1088,7 @@ static VALUE rxml_node_property_set(VALUE self, VALUE name, VALUE value)
1077
1088
  static VALUE rxml_node_remove_ex(VALUE self)
1078
1089
  {
1079
1090
  xmlNodePtr xnode, xresult;
1080
- Data_Get_Struct(self, xmlNode, xnode);
1091
+ xnode = rxml_get_xnode(self);
1081
1092
 
1082
1093
  /* First unlink the node from its parent. */
1083
1094
  xmlUnlinkNode(xnode);
@@ -1137,7 +1148,7 @@ static VALUE rxml_node_sibling_set(VALUE self, VALUE sibling)
1137
1148
  static VALUE rxml_node_output_escaping_q(VALUE self)
1138
1149
  {
1139
1150
  xmlNodePtr xnode;
1140
- Data_Get_Struct(self, xmlNode, xnode);
1151
+ xnode = rxml_get_xnode(self);
1141
1152
 
1142
1153
  switch (xnode->type) {
1143
1154
  case XML_TEXT_NODE:
@@ -1184,7 +1195,7 @@ static VALUE rxml_node_output_escaping_q(VALUE self)
1184
1195
  static VALUE rxml_node_output_escaping_set(VALUE self, VALUE bool)
1185
1196
  {
1186
1197
  xmlNodePtr xnode;
1187
- Data_Get_Struct(self, xmlNode, xnode);
1198
+ xnode = rxml_get_xnode(self);
1188
1199
 
1189
1200
  switch (xnode->type) {
1190
1201
  case XML_TEXT_NODE:
@@ -1217,7 +1228,7 @@ static VALUE rxml_node_space_preserve_get(VALUE self)
1217
1228
  {
1218
1229
  xmlNodePtr xnode;
1219
1230
 
1220
- Data_Get_Struct(self, xmlNode, xnode);
1231
+ xnode = rxml_get_xnode(self);
1221
1232
  return (INT2NUM(xmlNodeGetSpacePreserve(xnode)));
1222
1233
  }
1223
1234
 
@@ -1230,7 +1241,7 @@ static VALUE rxml_node_space_preserve_get(VALUE self)
1230
1241
  static VALUE rxml_node_space_preserve_set(VALUE self, VALUE bool)
1231
1242
  {
1232
1243
  xmlNodePtr xnode;
1233
- Data_Get_Struct(self, xmlNode, xnode);
1244
+ xnode = rxml_get_xnode(self);
1234
1245
 
1235
1246
  if (TYPE(bool) == T_FALSE)
1236
1247
  xmlNodeSetSpacePreserve(xnode, 0);
@@ -1249,7 +1260,7 @@ static VALUE rxml_node_space_preserve_set(VALUE self, VALUE bool)
1249
1260
  static VALUE rxml_node_type(VALUE self)
1250
1261
  {
1251
1262
  xmlNodePtr xnode;
1252
- Data_Get_Struct(self, xmlNode, xnode);
1263
+ xnode = rxml_get_xnode(self);
1253
1264
  return (INT2NUM(xnode->type));
1254
1265
  }
1255
1266
 
@@ -1268,7 +1279,7 @@ static VALUE rxml_node_copy(VALUE self, VALUE deep)
1268
1279
  xmlNodePtr xnode;
1269
1280
  xmlNodePtr xcopy;
1270
1281
  int recursive = (deep == Qnil || deep == Qfalse) ? 0 : 1;
1271
- Data_Get_Struct(self, xmlNode, xnode);
1282
+ xnode = rxml_get_xnode(self);
1272
1283
 
1273
1284
  xcopy = xmlCopyNode(xnode, recursive);
1274
1285
 
@@ -1280,8 +1291,12 @@ static VALUE rxml_node_copy(VALUE self, VALUE deep)
1280
1291
 
1281
1292
  void rxml_init_node(void)
1282
1293
  {
1294
+ /* Register callback for main thread */
1283
1295
  xmlDeregisterNodeDefault(rxml_node_deregisterNode);
1284
1296
 
1297
+ /* Register callback for all other threads */
1298
+ xmlThrDefDeregisterNodeDefault(rxml_node_deregisterNode);
1299
+
1285
1300
  cXMLNode = rb_define_class_under(mXML, "Node", rb_cObject);
1286
1301
 
1287
1302
  rb_define_const(cXMLNode, "SPACE_DEFAULT", INT2NUM(0));