nokogiri 1.6.7.2-x86-mingw32 → 1.6.8.rc1-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of nokogiri might be problematic. Click here for more details.

Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +12 -9
  3. data/CHANGELOG.ja.rdoc +18 -0
  4. data/CHANGELOG.rdoc +12 -7
  5. data/CONTRIBUTING.md +42 -0
  6. data/Gemfile +1 -1
  7. data/Manifest.txt +6 -0
  8. data/README.md +1 -1
  9. data/Rakefile +1 -1
  10. data/bin/nokogiri +2 -2
  11. data/dependencies.yml +1 -1
  12. data/ext/nokogiri/extconf.rb +3 -3
  13. data/ext/nokogiri/nokogiri.c +0 -7
  14. data/ext/nokogiri/nokogiri.h +1 -34
  15. data/ext/nokogiri/xml_document.c +2 -4
  16. data/ext/nokogiri/xml_namespace.c +56 -17
  17. data/ext/nokogiri/xml_node.c +12 -36
  18. data/ext/nokogiri/xml_node_set.c +169 -143
  19. data/ext/nokogiri/xml_node_set.h +3 -4
  20. data/ext/nokogiri/xml_sax_parser.c +2 -5
  21. data/ext/nokogiri/xml_syntax_error.c +0 -4
  22. data/ext/nokogiri/xml_syntax_error.h +0 -1
  23. data/ext/nokogiri/xml_xpath_context.c +9 -18
  24. data/lib/nokogiri.rb +3 -0
  25. data/lib/nokogiri/1.9/nokogiri.so +0 -0
  26. data/lib/nokogiri/2.0/nokogiri.so +0 -0
  27. data/lib/nokogiri/2.1/nokogiri.so +0 -0
  28. data/lib/nokogiri/2.2/nokogiri.so +0 -0
  29. data/lib/nokogiri/css/parser.rb +8 -2
  30. data/lib/nokogiri/css/parser.y +7 -2
  31. data/lib/nokogiri/version.rb +1 -1
  32. data/lib/nokogiri/xml/document.rb +7 -1
  33. data/lib/nokogiri/xml/dtd.rb +4 -4
  34. data/lib/nokogiri/xml/node.rb +2 -2
  35. data/test/css/test_parser.rb +7 -1
  36. data/test/files/GH_1042.html +18 -0
  37. data/test/files/namespace_pressure_test.xml +1684 -0
  38. data/test/files/tlm.html +2 -1
  39. data/test/html/sax/test_parser.rb +2 -2
  40. data/test/html/test_document.rb +18 -8
  41. data/test/html/test_document_encoding.rb +46 -54
  42. data/test/html/test_document_fragment.rb +21 -22
  43. data/test/html/test_node.rb +16 -0
  44. data/test/html/test_node_encoding.rb +12 -14
  45. data/test/namespaces/test_namespaces_in_parsed_doc.rb +14 -0
  46. data/test/test_reader.rb +19 -0
  47. data/test/test_xslt_transforms.rb +5 -3
  48. data/test/xml/sax/test_parser.rb +36 -39
  49. data/test/xml/test_document.rb +7 -2
  50. data/test/xml/test_document_encoding.rb +14 -16
  51. data/test/xml/test_dtd_encoding.rb +0 -2
  52. data/test/xml/test_node_encoding.rb +78 -80
  53. data/test/xml/test_reader_encoding.rb +100 -102
  54. data/test/xslt/test_exception_handling.rb +1 -1
  55. metadata +11 -7
@@ -234,15 +234,7 @@ ok:
234
234
  * reparent the actual reparentee, so we reparent a duplicate.
235
235
  */
236
236
  nokogiri_root_node(reparentee);
237
-
238
- xmlResetLastError();
239
- xmlSetStructuredErrorFunc((void *)rb_iv_get(DOC_RUBY_OBJECT(pivot->doc), "@errors"), Nokogiri_error_array_pusher);
240
-
241
- reparentee = xmlDocCopyNode(reparentee, pivot->doc, 1) ;
242
-
243
- xmlSetStructuredErrorFunc(NULL, NULL);
244
-
245
- if (! reparentee) {
237
+ if (!(reparentee = xmlDocCopyNode(reparentee, pivot->doc, 1))) {
246
238
  rb_raise(rb_eRuntimeError, "Could not reparent node (xmlDocCopyNode)");
247
239
  }
248
240
  }
@@ -481,13 +473,7 @@ static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
481
473
 
482
474
  Data_Get_Struct(self, xmlNode, node);
483
475
 
484
- xmlResetLastError();
485
- xmlSetStructuredErrorFunc(NULL, Nokogiri_error_silencer);
486
-
487
476
  dup = xmlDocCopyNode(node, node->doc, (int)NUM2INT(level));
488
-
489
- xmlSetStructuredErrorFunc(NULL, NULL);
490
-
491
477
  if(dup == NULL) return Qnil;
492
478
 
493
479
  nokogiri_root_node(dup);
@@ -778,19 +764,11 @@ static VALUE namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
778
764
  *
779
765
  * Set the +property+ to +value+
780
766
  */
781
- static VALUE set(VALUE node_rb, VALUE property_name_rb, VALUE property_value_rb)
767
+ static VALUE set(VALUE self, VALUE property, VALUE value)
782
768
  {
783
769
  xmlNodePtr node, cur;
784
- xmlChar* property_name ;
785
- xmlAttrPtr property;
786
-
787
- Data_Get_Struct(node_rb, xmlNode, node);
788
-
789
- if (node->type != XML_ELEMENT_NODE) {
790
- return(Qnil); // TODO: would raising an exception be more appropriate?
791
- }
792
-
793
- property_name = (xmlChar *)StringValuePtr(property_name_rb);
770
+ xmlAttrPtr prop;
771
+ Data_Get_Struct(self, xmlNode, node);
794
772
 
795
773
  /* If a matching attribute node already exists, then xmlSetProp will destroy
796
774
  * the existing node's children. However, if Nokogiri has a node object
@@ -798,9 +776,11 @@ static VALUE set(VALUE node_rb, VALUE property_name_rb, VALUE property_value_rb)
798
776
  *
799
777
  * We can avoid this by unlinking these nodes first.
800
778
  */
801
- property = xmlHasProp(node, property_name);
802
- if (property && property->children) {
803
- for (cur = property->children; cur; cur = cur->next) {
779
+ if (node->type != XML_ELEMENT_NODE)
780
+ return(Qnil);
781
+ prop = xmlHasProp(node, (xmlChar *)StringValuePtr(property));
782
+ if (prop && prop->children) {
783
+ for (cur = prop->children; cur; cur = cur->next) {
804
784
  if (cur->_private) {
805
785
  nokogiri_root_node(cur);
806
786
  xmlUnlinkNode(cur);
@@ -808,14 +788,10 @@ static VALUE set(VALUE node_rb, VALUE property_name_rb, VALUE property_value_rb)
808
788
  }
809
789
  }
810
790
 
811
- xmlResetLastError();
812
- xmlSetStructuredErrorFunc(NULL, Nokogiri_error_silencer);
813
-
814
- xmlSetProp(node, property_name, (xmlChar *)StringValuePtr(property_value_rb));
815
-
816
- xmlSetStructuredErrorFunc(NULL, NULL);
791
+ xmlSetProp(node, (xmlChar *)StringValuePtr(property),
792
+ (xmlChar *)StringValuePtr(value));
817
793
 
818
- return property_value_rb;
794
+ return value;
819
795
  }
820
796
 
821
797
  /*
@@ -1,62 +1,42 @@
1
1
  #include <xml_node_set.h>
2
+ #include <xml_namespace.h>
2
3
  #include <libxml/xpathInternals.h>
3
4
 
4
5
  static ID decorate ;
6
+ static void xpath_node_set_del(xmlNodeSetPtr cur, xmlNodePtr val);
5
7
 
6
- static int dealloc_namespace(xmlNsPtr ns)
8
+
9
+ static void Check_Node_Set_Node_Type(VALUE node)
7
10
  {
8
- if (ns->href)
9
- xmlFree((xmlChar *)ns->href);
10
- if (ns->prefix)
11
- xmlFree((xmlChar *)ns->prefix);
12
- xmlFree(ns);
13
- return ST_CONTINUE;
11
+ if (!(rb_obj_is_kind_of(node, cNokogiriXmlNode) ||
12
+ rb_obj_is_kind_of(node, cNokogiriXmlNamespace))) {
13
+ rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node or Nokogiri::XML::Namespace");
14
+ }
14
15
  }
15
16
 
16
- static void deallocate(nokogiriNodeSetTuple *tuple)
17
+
18
+ static void deallocate(xmlNodeSetPtr node_set)
17
19
  {
18
20
  /*
19
- * xmlXPathFreeNodeSet() contains an implicit assumption that it is being
20
- * called before any of its pointed-to nodes have been free()d. this
21
- * assumption lies in the operation where it dereferences nodeTab pointers
22
- * while searching for namespace nodes to free.
23
- *
24
- * however, since Ruby's GC mechanism cannot guarantee the strict order in
25
- * which ruby objects will be GC'd, nodes may be garbage collected before a
26
- * nodeset containing pointers to those nodes. (this is true regardless of
27
- * how we declare dependencies between objects with rb_gc_mark().)
28
21
  *
29
- * as a result, xmlXPathFreeNodeSet() will perform unsafe memory operations,
30
- * and calling it would be evil.
22
+ * since xpath queries return copies of the xmlNs structs,
23
+ * xmlXPathFreeNodeSet() frees those xmlNs structs that are in the
24
+ * NodeSet.
31
25
  *
32
- * so here, we *manually* free the set of namespace nodes that was
33
- * constructed at initialization time (see Nokogiri_wrap_xml_node_set()), as
34
- * well as the NodeSet, without using the official xmlXPathFreeNodeSet().
26
+ * this is bad if someone is still trying to use the Namespace object wrapped
27
+ * around the xmlNs, so we need to avoid that.
35
28
  *
36
- * there's probably a lesson in here somewhere about intermingling, within a
37
- * single array, structs with different memory-ownership semantics. or more
38
- * generally, a lesson about building an API in C/C++ that does not contain
39
- * assumptions about the strict order in which memory will be released. hey,
40
- * that sounds like a great idea for a blog post! get to it!
29
+ * here we reproduce xmlXPathFreeNodeSet() without the xmlNs logic.
41
30
  *
42
- * "In Valgrind We Trust." seriously.
31
+ * this doesn't cause a leak because Namespace objects that are in an XPath
32
+ * query NodeSet are given their own lifecycle in
33
+ * Nokogiri_wrap_xml_namespace().
43
34
  */
44
- xmlNodeSetPtr node_set;
45
-
46
- node_set = tuple->node_set;
47
-
48
- if (!node_set)
49
- return;
50
-
51
35
  NOKOGIRI_DEBUG_START(node_set) ;
52
- st_foreach(tuple->namespaces, dealloc_namespace, 0);
53
-
54
36
  if (node_set->nodeTab != NULL)
55
37
  xmlFree(node_set->nodeTab);
56
38
 
57
39
  xmlFree(node_set);
58
- st_free_table(tuple->namespaces);
59
- free(tuple);
60
40
  NOKOGIRI_DEBUG_END(node_set) ;
61
41
  }
62
42
 
@@ -74,12 +54,12 @@ static VALUE allocate(VALUE klass)
74
54
  */
75
55
  static VALUE duplicate(VALUE self)
76
56
  {
77
- nokogiriNodeSetTuple *tuple;
57
+ xmlNodeSetPtr node_set;
78
58
  xmlNodeSetPtr dupl;
79
59
 
80
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
60
+ Data_Get_Struct(self, xmlNodeSet, node_set);
81
61
 
82
- dupl = xmlXPathNodeSetMerge(NULL, tuple->node_set);
62
+ dupl = xmlXPathNodeSetMerge(NULL, node_set);
83
63
 
84
64
  return Nokogiri_wrap_xml_node_set(dupl, rb_iv_get(self, "@document"));
85
65
  }
@@ -92,10 +72,11 @@ static VALUE duplicate(VALUE self)
92
72
  */
93
73
  static VALUE length(VALUE self)
94
74
  {
95
- nokogiriNodeSetTuple *tuple;
96
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
75
+ xmlNodeSetPtr node_set;
97
76
 
98
- return tuple->node_set ? INT2NUM(tuple->node_set->nodeNr) : INT2NUM(0);
77
+ Data_Get_Struct(self, xmlNodeSet, node_set);
78
+
79
+ return node_set ? INT2NUM(node_set->nodeNr) : INT2NUM(0);
99
80
  }
100
81
 
101
82
  /*
@@ -106,15 +87,16 @@ static VALUE length(VALUE self)
106
87
  */
107
88
  static VALUE push(VALUE self, VALUE rb_node)
108
89
  {
109
- nokogiriNodeSetTuple *tuple;
90
+ xmlNodeSetPtr node_set;
110
91
  xmlNodePtr node;
111
92
 
112
- if(!(rb_obj_is_kind_of(rb_node, cNokogiriXmlNode) || rb_obj_is_kind_of(rb_node, cNokogiriXmlNamespace)))
113
- rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node or Nokogiri::XML::Namespace");
93
+ Check_Node_Set_Node_Type(rb_node);
114
94
 
115
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
95
+ Data_Get_Struct(self, xmlNodeSet, node_set);
116
96
  Data_Get_Struct(rb_node, xmlNode, node);
117
- xmlXPathNodeSetAdd(tuple->node_set, node);
97
+
98
+ xmlXPathNodeSetAdd(node_set, node);
99
+
118
100
  return self;
119
101
  }
120
102
 
@@ -128,29 +110,19 @@ static VALUE push(VALUE self, VALUE rb_node)
128
110
  static VALUE
129
111
  delete(VALUE self, VALUE rb_node)
130
112
  {
131
- nokogiriNodeSetTuple *tuple;
132
- xmlNodePtr node;
133
- xmlNodeSetPtr cur;
134
- int i;
135
-
136
- if (!(rb_obj_is_kind_of(rb_node, cNokogiriXmlNode) || rb_obj_is_kind_of(rb_node, cNokogiriXmlNamespace)))
137
- rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node or Nokogiri::XML::Namespace");
138
-
139
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
140
- Data_Get_Struct(rb_node, xmlNode, node);
141
- cur = tuple->node_set;
142
-
143
- if (xmlXPathNodeSetContains(cur, node)) {
144
- for (i = 0; i < cur->nodeNr; i++)
145
- if (cur->nodeTab[i] == node) break;
146
-
147
- cur->nodeNr--;
148
- for (;i < cur->nodeNr;i++)
149
- cur->nodeTab[i] = cur->nodeTab[i + 1];
150
- cur->nodeTab[cur->nodeNr] = NULL;
151
- return rb_node;
152
- }
153
- return Qnil ;
113
+ xmlNodeSetPtr node_set;
114
+ xmlNodePtr node;
115
+
116
+ Check_Node_Set_Node_Type(rb_node);
117
+
118
+ Data_Get_Struct(self, xmlNodeSet, node_set);
119
+ Data_Get_Struct(rb_node, xmlNode, node);
120
+
121
+ if (xmlXPathNodeSetContains(node_set, node)) {
122
+ xpath_node_set_del(node_set, node);
123
+ return rb_node;
124
+ }
125
+ return Qnil ;
154
126
  }
155
127
 
156
128
 
@@ -162,16 +134,16 @@ delete(VALUE self, VALUE rb_node)
162
134
  */
163
135
  static VALUE intersection(VALUE self, VALUE rb_other)
164
136
  {
165
- nokogiriNodeSetTuple *tuple, *other;
137
+ xmlNodeSetPtr node_set, other ;
166
138
  xmlNodeSetPtr intersection;
167
139
 
168
140
  if(!rb_obj_is_kind_of(rb_other, cNokogiriXmlNodeSet))
169
141
  rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet");
170
142
 
171
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
172
- Data_Get_Struct(rb_other, nokogiriNodeSetTuple, other);
143
+ Data_Get_Struct(self, xmlNodeSet, node_set);
144
+ Data_Get_Struct(rb_other, xmlNodeSet, other);
173
145
 
174
- intersection = xmlXPathIntersection(tuple->node_set, other->node_set);
146
+ intersection = xmlXPathIntersection(node_set, other);
175
147
  return Nokogiri_wrap_xml_node_set(intersection, rb_iv_get(self, "@document"));
176
148
  }
177
149
 
@@ -184,16 +156,15 @@ static VALUE intersection(VALUE self, VALUE rb_other)
184
156
  */
185
157
  static VALUE include_eh(VALUE self, VALUE rb_node)
186
158
  {
187
- nokogiriNodeSetTuple *tuple;
159
+ xmlNodeSetPtr node_set;
188
160
  xmlNodePtr node;
189
161
 
190
- if(!(rb_obj_is_kind_of(rb_node, cNokogiriXmlNode) || rb_obj_is_kind_of(rb_node, cNokogiriXmlNamespace)))
191
- rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node or Nokogiri::XML::Namespace");
162
+ Check_Node_Set_Node_Type(rb_node);
192
163
 
193
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
164
+ Data_Get_Struct(self, xmlNodeSet, node_set);
194
165
  Data_Get_Struct(rb_node, xmlNode, node);
195
166
 
196
- return (xmlXPathNodeSetContains(tuple->node_set, node) ? Qtrue : Qfalse);
167
+ return (xmlXPathNodeSetContains(node_set, node) ? Qtrue : Qfalse);
197
168
  }
198
169
 
199
170
 
@@ -206,17 +177,17 @@ static VALUE include_eh(VALUE self, VALUE rb_node)
206
177
  */
207
178
  static VALUE set_union(VALUE self, VALUE rb_other)
208
179
  {
209
- nokogiriNodeSetTuple *tuple, *other;
180
+ xmlNodeSetPtr node_set, other;
210
181
  xmlNodeSetPtr new;
211
182
 
212
183
  if(!rb_obj_is_kind_of(rb_other, cNokogiriXmlNodeSet))
213
184
  rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet");
214
185
 
215
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
216
- Data_Get_Struct(rb_other, nokogiriNodeSetTuple, other);
186
+ Data_Get_Struct(self, xmlNodeSet, node_set);
187
+ Data_Get_Struct(rb_other, xmlNodeSet, other);
217
188
 
218
- new = xmlXPathNodeSetMerge(NULL, tuple->node_set);
219
- new = xmlXPathNodeSetMerge(new, other->node_set);
189
+ new = xmlXPathNodeSetMerge(NULL, node_set);
190
+ new = xmlXPathNodeSetMerge(new, other);
220
191
 
221
192
  return Nokogiri_wrap_xml_node_set(new, rb_iv_get(self, "@document"));
222
193
  }
@@ -230,19 +201,19 @@ static VALUE set_union(VALUE self, VALUE rb_other)
230
201
  */
231
202
  static VALUE minus(VALUE self, VALUE rb_other)
232
203
  {
233
- nokogiriNodeSetTuple *tuple, *other;
204
+ xmlNodeSetPtr node_set, other;
234
205
  xmlNodeSetPtr new;
235
206
  int j ;
236
207
 
237
208
  if(!rb_obj_is_kind_of(rb_other, cNokogiriXmlNodeSet))
238
209
  rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet");
239
210
 
240
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
241
- Data_Get_Struct(rb_other, nokogiriNodeSetTuple, other);
211
+ Data_Get_Struct(self, xmlNodeSet, node_set);
212
+ Data_Get_Struct(rb_other, xmlNodeSet, other);
242
213
 
243
- new = xmlXPathNodeSetMerge(NULL, tuple->node_set);
244
- for (j = 0 ; j < other->node_set->nodeNr ; ++j) {
245
- xmlXPathNodeSetDel(new, other->node_set->nodeTab[j]);
214
+ new = xmlXPathNodeSetMerge(NULL, node_set);
215
+ for (j = 0 ; j < other->nodeNr ; ++j) {
216
+ xpath_node_set_del(new, other->nodeTab[j]);
246
217
  }
247
218
 
248
219
  return Nokogiri_wrap_xml_node_set(new, rb_iv_get(self, "@document"));
@@ -252,31 +223,25 @@ static VALUE minus(VALUE self, VALUE rb_other)
252
223
  static VALUE index_at(VALUE self, long offset)
253
224
  {
254
225
  xmlNodeSetPtr node_set;
255
- nokogiriNodeSetTuple *tuple;
256
226
 
257
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
258
- node_set = tuple->node_set;
227
+ Data_Get_Struct(self, xmlNodeSet, node_set);
259
228
 
260
- if (offset >= node_set->nodeNr || abs((int)offset) > node_set->nodeNr)
229
+ if (offset >= node_set->nodeNr || abs((int)offset) > node_set->nodeNr) {
261
230
  return Qnil;
231
+ }
262
232
 
263
- if (offset < 0)
264
- offset += node_set->nodeNr;
233
+ if (offset < 0) { offset += node_set->nodeNr ; }
265
234
 
266
- if (XML_NAMESPACE_DECL == node_set->nodeTab[offset]->type)
267
- return Nokogiri_wrap_xml_namespace2(rb_iv_get(self, "@document"), (xmlNsPtr)(node_set->nodeTab[offset]));
268
- return Nokogiri_wrap_xml_node(Qnil, node_set->nodeTab[offset]);
235
+ return Nokogiri_wrap_xml_node_set_node(node_set->nodeTab[offset], self);
269
236
  }
270
237
 
271
238
  static VALUE subseq(VALUE self, long beg, long len)
272
239
  {
273
240
  long j;
274
- nokogiriNodeSetTuple *tuple;
275
241
  xmlNodeSetPtr node_set;
276
242
  xmlNodeSetPtr new_set ;
277
243
 
278
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
279
- node_set = tuple->node_set;
244
+ Data_Get_Struct(self, xmlNodeSet, node_set);
280
245
 
281
246
  if (beg > node_set->nodeNr) return Qnil ;
282
247
  if (beg < 0 || len < 0) return Qnil ;
@@ -312,9 +277,8 @@ static VALUE slice(int argc, VALUE *argv, VALUE self)
312
277
  VALUE arg ;
313
278
  long beg, len ;
314
279
  xmlNodeSetPtr node_set;
315
- nokogiriNodeSetTuple *tuple;
316
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
317
- node_set = tuple->node_set;
280
+
281
+ Data_Get_Struct(self, xmlNodeSet, node_set);
318
282
 
319
283
  if (argc == 2) {
320
284
  beg = NUM2LONG(argv[0]);
@@ -333,7 +297,7 @@ static VALUE slice(int argc, VALUE *argv, VALUE self)
333
297
  if (FIXNUM_P(arg)) {
334
298
  return index_at(self, FIX2LONG(arg));
335
299
  }
336
-
300
+
337
301
  /* if arg is Range */
338
302
  switch (rb_range_beg_len(arg, &beg, &len, (long)node_set->nodeNr, 0)) {
339
303
  case Qfalse:
@@ -356,26 +320,25 @@ static VALUE slice(int argc, VALUE *argv, VALUE self)
356
320
  */
357
321
  static VALUE to_array(VALUE self, VALUE rb_node)
358
322
  {
359
- xmlNodeSetPtr set;
323
+ xmlNodeSetPtr node_set ;
360
324
  VALUE *elts;
361
325
  VALUE list;
362
326
  int i;
363
- nokogiriNodeSetTuple *tuple;
364
327
 
365
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
366
- set = tuple->node_set;
328
+ Data_Get_Struct(self, xmlNodeSet, node_set);
367
329
 
368
- elts = calloc((size_t)set->nodeNr, sizeof(VALUE *));
369
- for(i = 0; i < set->nodeNr; i++) {
370
- if (XML_NAMESPACE_DECL == set->nodeTab[i]->type)
371
- elts[i] = Nokogiri_wrap_xml_namespace2(rb_iv_get(self, "@document"), (xmlNsPtr)(set->nodeTab[i]));
372
- else
373
- elts[i] = Nokogiri_wrap_xml_node(Qnil, set->nodeTab[i]);
330
+ elts = (VALUE *)calloc((size_t)(node_set->nodeNr), sizeof(VALUE));
331
+ for(i = 0; i < node_set->nodeNr; i++) {
332
+ elts[i] = Nokogiri_wrap_xml_node_set_node(node_set->nodeTab[i], self);
333
+ rb_gc_register_address(&elts[i]);
374
334
  }
375
335
 
376
- list = rb_ary_new4((long)set->nodeNr, elts);
336
+ list = rb_ary_new4((long)node_set->nodeNr, elts);
377
337
 
378
- /*free(elts); */
338
+ for(i = 0; i < node_set->nodeNr; i++) {
339
+ rb_gc_unregister_address(&elts[i]);
340
+ }
341
+ free(elts);
379
342
 
380
343
  return list;
381
344
  }
@@ -390,13 +353,12 @@ static VALUE unlink_nodeset(VALUE self)
390
353
  {
391
354
  xmlNodeSetPtr node_set;
392
355
  int j, nodeNr ;
393
- nokogiriNodeSetTuple *tuple;
394
356
 
395
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
396
- node_set = tuple->node_set;
357
+ Data_Get_Struct(self, xmlNodeSet, node_set);
358
+
397
359
  nodeNr = node_set->nodeNr ;
398
360
  for (j = 0 ; j < nodeNr ; j++) {
399
- if (XML_NAMESPACE_DECL != node_set->nodeTab[j]->type) {
361
+ if (! Nokogiri_namespace_eh(node_set->nodeTab[j])) {
400
362
  VALUE node ;
401
363
  xmlNodePtr node_ptr;
402
364
  node = Nokogiri_wrap_xml_node(Qnil, node_set->nodeTab[j]);
@@ -408,39 +370,103 @@ static VALUE unlink_nodeset(VALUE self)
408
370
  return self ;
409
371
  }
410
372
 
373
+
374
+ static void reify_node_set_namespaces(VALUE self)
375
+ {
376
+ /*
377
+ * as mentioned in deallocate() above, xmlNs structs returned in an XPath
378
+ * NodeSet are duplicates, and we don't clean them up at deallocate() time.
379
+ *
380
+ * as a result, we need to make sure the Ruby manages this memory. we do this
381
+ * by forcing the creation of a Ruby object wrapped around the xmlNs.
382
+ *
383
+ * we also have to make sure that the NodeSet has a reference to the
384
+ * Namespace object, otherwise GC will kick in and the Namespace won't be
385
+ * marked.
386
+ *
387
+ * we *could* do this safely with *all* the nodes in the NodeSet, but we only
388
+ * *need* to do it for xmlNs structs, and so you get the code we have here.
389
+ */
390
+ int j ;
391
+ xmlNodeSetPtr node_set ;
392
+ VALUE namespace_cache ;
393
+
394
+ Data_Get_Struct(self, xmlNodeSet, node_set);
395
+
396
+ namespace_cache = rb_iv_get(self, "@namespace_cache");
397
+
398
+ for (j = 0 ; j < node_set->nodeNr ; j++) {
399
+ if (Nokogiri_namespace_eh(node_set->nodeTab[j])) {
400
+ rb_ary_push(namespace_cache, Nokogiri_wrap_xml_node_set_node(node_set->nodeTab[j], self));
401
+ }
402
+ }
403
+ }
404
+
405
+
411
406
  VALUE Nokogiri_wrap_xml_node_set(xmlNodeSetPtr node_set, VALUE document)
412
407
  {
413
408
  VALUE new_set ;
414
- int i;
415
- xmlNodePtr cur;
416
- xmlNsPtr ns;
417
- nokogiriNodeSetTuple *tuple;
418
409
 
419
- new_set = Data_Make_Struct(cNokogiriXmlNodeSet, nokogiriNodeSetTuple, 0,
420
- deallocate, tuple);
410
+ if (node_set == NULL) {
411
+ node_set = xmlXPathNodeSetCreate(NULL);
412
+ }
421
413
 
422
- tuple->node_set = node_set;
423
- tuple->namespaces = st_init_numtable();
414
+ new_set = Data_Wrap_Struct(cNokogiriXmlNodeSet, 0, deallocate, node_set);
424
415
 
425
416
  if (!NIL_P(document)) {
426
417
  rb_iv_set(new_set, "@document", document);
427
418
  rb_funcall(document, decorate, 1, new_set);
428
419
  }
429
420
 
430
- if (node_set && node_set->nodeTab) {
431
- for (i = 0; i < node_set->nodeNr; i++) {
432
- cur = node_set->nodeTab[i];
433
- if (cur && cur->type == XML_NAMESPACE_DECL) {
434
- ns = (xmlNsPtr)cur;
435
- if (ns->next && ns->next->type != XML_NAMESPACE_DECL)
436
- st_insert(tuple->namespaces, (st_data_t)cur, (st_data_t)0);
437
- }
438
- }
439
- }
421
+ rb_iv_set(new_set, "@namespace_cache", rb_ary_new());
422
+ reify_node_set_namespaces(new_set);
440
423
 
441
424
  return new_set ;
442
425
  }
443
426
 
427
+ VALUE Nokogiri_wrap_xml_node_set_node(xmlNodePtr node, VALUE node_set)
428
+ {
429
+ xmlDocPtr document ;
430
+
431
+ if (Nokogiri_namespace_eh(node)) {
432
+ Data_Get_Struct(rb_iv_get(node_set, "@document"), xmlDoc, document);
433
+ return Nokogiri_wrap_xml_namespace(document, (xmlNsPtr)node);
434
+ } else {
435
+ return Nokogiri_wrap_xml_node(Qnil, node);
436
+ }
437
+ }
438
+
439
+
440
+ static void xpath_node_set_del(xmlNodeSetPtr cur, xmlNodePtr val)
441
+ {
442
+ /*
443
+ * as mentioned a few times above, we do not want to free xmlNs structs
444
+ * outside of the Namespace lifecycle.
445
+ *
446
+ * xmlXPathNodeSetDel() frees xmlNs structs, and so here we reproduce that
447
+ * function with the xmlNs logic.
448
+ */
449
+ int i;
450
+
451
+ if (cur == NULL) return;
452
+ if (val == NULL) return;
453
+
454
+ /*
455
+ * find node in nodeTab
456
+ */
457
+ for (i = 0;i < cur->nodeNr;i++)
458
+ if (cur->nodeTab[i] == val) break;
459
+
460
+ if (i >= cur->nodeNr) { /* not found */
461
+ return;
462
+ }
463
+ cur->nodeNr--;
464
+ for (;i < cur->nodeNr;i++)
465
+ cur->nodeTab[i] = cur->nodeTab[i + 1];
466
+ cur->nodeTab[cur->nodeNr] = NULL;
467
+ }
468
+
469
+
444
470
  VALUE cNokogiriXmlNodeSet ;
445
471
  void init_xml_node_set(void)
446
472
  {