nokogiri 1.5.0 → 1.5.1.rc1

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 (61) hide show
  1. data/CHANGELOG.ja.rdoc +39 -12
  2. data/CHANGELOG.rdoc +28 -0
  3. data/C_CODING_STYLE.rdoc +27 -0
  4. data/Manifest.txt +4 -0
  5. data/README.rdoc +11 -7
  6. data/Rakefile +42 -29
  7. data/bin/nokogiri +10 -2
  8. data/ext/nokogiri/extconf.rb +9 -1
  9. data/ext/nokogiri/html_document.c +16 -0
  10. data/ext/nokogiri/html_sax_parser_context.c +59 -37
  11. data/ext/nokogiri/html_sax_push_parser.c +87 -0
  12. data/ext/nokogiri/html_sax_push_parser.h +9 -0
  13. data/ext/nokogiri/nokogiri.c +6 -8
  14. data/ext/nokogiri/nokogiri.h +3 -0
  15. data/ext/nokogiri/xml_document.c +101 -3
  16. data/ext/nokogiri/xml_document.h +3 -3
  17. data/ext/nokogiri/xml_node.c +150 -58
  18. data/ext/nokogiri/xml_node_set.c +169 -120
  19. data/ext/nokogiri/xml_node_set.h +5 -0
  20. data/ext/nokogiri/xml_sax_parser_context.c +64 -41
  21. data/ext/nokogiri/xml_text.c +2 -0
  22. data/ext/nokogiri/xml_xpath_context.c +30 -24
  23. data/ext/nokogiri/xslt_stylesheet.c +62 -16
  24. data/ext/nokogiri/xslt_stylesheet.h +5 -0
  25. data/lib/nokogiri/css/parser.rb +165 -159
  26. data/lib/nokogiri/css/parser.y +6 -3
  27. data/lib/nokogiri/css/tokenizer.rb +1 -1
  28. data/lib/nokogiri/css/tokenizer.rex +1 -1
  29. data/lib/nokogiri/html.rb +1 -0
  30. data/lib/nokogiri/html/document.rb +82 -42
  31. data/lib/nokogiri/html/sax/push_parser.rb +16 -0
  32. data/lib/nokogiri/version.rb +1 -1
  33. data/lib/nokogiri/xml.rb +6 -0
  34. data/lib/nokogiri/xml/builder.rb +7 -1
  35. data/lib/nokogiri/xml/document.rb +32 -17
  36. data/lib/nokogiri/xml/document_fragment.rb +6 -1
  37. data/lib/nokogiri/xml/node.rb +40 -9
  38. data/lib/nokogiri/xslt.rb +5 -1
  39. data/tasks/cross_compile.rb +1 -0
  40. data/tasks/nokogiri.org.rb +6 -0
  41. data/tasks/test.rb +1 -0
  42. data/test/css/test_xpath_visitor.rb +6 -0
  43. data/test/helper.rb +1 -0
  44. data/test/html/test_document.rb +26 -0
  45. data/test/html/test_document_fragment.rb +1 -2
  46. data/test/test_memory_leak.rb +81 -1
  47. data/test/test_xslt_transforms.rb +152 -123
  48. data/test/xml/test_builder.rb +24 -2
  49. data/test/xml/test_c14n.rb +151 -0
  50. data/test/xml/test_document.rb +48 -0
  51. data/test/xml/test_namespace.rb +5 -0
  52. data/test/xml/test_node.rb +82 -1
  53. data/test/xml/test_node_attributes.rb +19 -0
  54. data/test/xml/test_node_inheritance.rb +32 -0
  55. data/test/xml/test_node_reparenting.rb +32 -0
  56. data/test/xml/test_node_set.rb +16 -8
  57. data/test/xml/test_reader_encoding.rb +16 -0
  58. data/test/xml/test_unparented_node.rb +24 -0
  59. data/test/xml/test_xinclude.rb +83 -0
  60. data/test/xml/test_xpath.rb +22 -0
  61. metadata +208 -241
@@ -3,6 +3,69 @@
3
3
 
4
4
  static ID decorate ;
5
5
 
6
+ static int dealloc_namespace(xmlNsPtr ns)
7
+ {
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;
14
+ }
15
+
16
+ static void deallocate(nokogiriNodeSetTuple *tuple)
17
+ {
18
+ /*
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
+ *
29
+ * as a result, xmlXPathFreeNodeSet() will perform unsafe memory operations,
30
+ * and calling it would be evil.
31
+ *
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().
35
+ *
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!
41
+ *
42
+ * "In Valgrind We Trust." seriously.
43
+ */
44
+ xmlNodeSetPtr node_set;
45
+
46
+ node_set = tuple->node_set;
47
+
48
+ if (!node_set)
49
+ return;
50
+
51
+ NOKOGIRI_DEBUG_START(node_set) ;
52
+ st_foreach(tuple->namespaces, dealloc_namespace, 0);
53
+
54
+ if (node_set->nodeTab != NULL)
55
+ xmlFree(node_set->nodeTab);
56
+
57
+ xmlFree(node_set);
58
+ st_free_table(tuple->namespaces);
59
+ free(tuple);
60
+ NOKOGIRI_DEBUG_END(node_set) ;
61
+ }
62
+
63
+ static VALUE allocate(VALUE klass)
64
+ {
65
+ return Nokogiri_wrap_xml_node_set(xmlXPathNodeSetCreate(NULL), Qnil);
66
+ }
67
+
68
+
6
69
  /*
7
70
  * call-seq:
8
71
  * dup
@@ -11,12 +74,12 @@ static ID decorate ;
11
74
  */
12
75
  static VALUE duplicate(VALUE self)
13
76
  {
14
- xmlNodeSetPtr node_set;
77
+ nokogiriNodeSetTuple *tuple;
15
78
  xmlNodeSetPtr dupl;
16
79
 
17
- Data_Get_Struct(self, xmlNodeSet, node_set);
80
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
18
81
 
19
- dupl = xmlXPathNodeSetMerge(NULL, node_set);
82
+ dupl = xmlXPathNodeSetMerge(NULL, tuple->node_set);
20
83
 
21
84
  return Nokogiri_wrap_xml_node_set(dupl, rb_iv_get(self, "@document"));
22
85
  }
@@ -29,13 +92,10 @@ static VALUE duplicate(VALUE self)
29
92
  */
30
93
  static VALUE length(VALUE self)
31
94
  {
32
- xmlNodeSetPtr node_set;
33
- Data_Get_Struct(self, xmlNodeSet, node_set);
95
+ nokogiriNodeSetTuple *tuple;
96
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
34
97
 
35
- if(node_set)
36
- return INT2NUM(node_set->nodeNr);
37
-
38
- return INT2NUM(0);
98
+ return tuple->node_set ? INT2NUM(tuple->node_set->nodeNr) : INT2NUM(0);
39
99
  }
40
100
 
41
101
  /*
@@ -46,15 +106,15 @@ static VALUE length(VALUE self)
46
106
  */
47
107
  static VALUE push(VALUE self, VALUE rb_node)
48
108
  {
49
- xmlNodeSetPtr node_set;
109
+ nokogiriNodeSetTuple *tuple;
50
110
  xmlNodePtr node;
51
111
 
52
112
  if(!(rb_obj_is_kind_of(rb_node, cNokogiriXmlNode) || rb_obj_is_kind_of(rb_node, cNokogiriXmlNamespace)))
53
113
  rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node or Nokogiri::XML::Namespace");
54
114
 
55
- Data_Get_Struct(self, xmlNodeSet, node_set);
115
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
56
116
  Data_Get_Struct(rb_node, xmlNode, node);
57
- xmlXPathNodeSetAdd(node_set, node);
117
+ xmlXPathNodeSetAdd(tuple->node_set, node);
58
118
  return self;
59
119
  }
60
120
 
@@ -65,23 +125,32 @@ static VALUE push(VALUE self, VALUE rb_node)
65
125
  * Delete +node+ from the Nodeset, if it is a member. Returns the deleted node
66
126
  * if found, otherwise returns nil.
67
127
  */
68
- static VALUE delete(VALUE self, VALUE rb_node)
128
+ static VALUE
129
+ delete(VALUE self, VALUE rb_node)
69
130
  {
70
- xmlNodeSetPtr node_set ;
71
- xmlNodePtr node ;
72
-
73
- if(!(rb_obj_is_kind_of(rb_node, cNokogiriXmlNode) || rb_obj_is_kind_of(rb_node, cNokogiriXmlNamespace)))
74
- rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node or Nokogiri::XML::Namespace");
75
-
76
- Data_Get_Struct(self, xmlNodeSet, node_set);
77
- Data_Get_Struct(rb_node, xmlNode, node);
78
-
79
- if (xmlXPathNodeSetContains(node_set, node)) {
80
- xmlXPathNodeSetDel(node_set, node);
81
- return rb_node ;
82
- }
83
-
84
- return Qnil ;
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 ;
85
154
  }
86
155
 
87
156
 
@@ -93,16 +162,17 @@ static VALUE delete(VALUE self, VALUE rb_node)
93
162
  */
94
163
  static VALUE intersection(VALUE self, VALUE rb_other)
95
164
  {
96
- xmlNodeSetPtr node_set;
97
- xmlNodeSetPtr other;
165
+ nokogiriNodeSetTuple *tuple, *other;
166
+ xmlNodeSetPtr intersection;
98
167
 
99
168
  if(!rb_obj_is_kind_of(rb_other, cNokogiriXmlNodeSet))
100
169
  rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet");
101
170
 
102
- Data_Get_Struct(self, xmlNodeSet, node_set);
103
- Data_Get_Struct(rb_other, xmlNodeSet, other);
171
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
172
+ Data_Get_Struct(rb_other, nokogiriNodeSetTuple, other);
104
173
 
105
- return Nokogiri_wrap_xml_node_set(xmlXPathIntersection(node_set, other), rb_iv_get(self, "@document"));
174
+ intersection = xmlXPathIntersection(tuple->node_set, other->node_set);
175
+ return Nokogiri_wrap_xml_node_set(intersection, rb_iv_get(self, "@document"));
106
176
  }
107
177
 
108
178
 
@@ -114,16 +184,16 @@ static VALUE intersection(VALUE self, VALUE rb_other)
114
184
  */
115
185
  static VALUE include_eh(VALUE self, VALUE rb_node)
116
186
  {
117
- xmlNodeSetPtr node_set;
187
+ nokogiriNodeSetTuple *tuple;
118
188
  xmlNodePtr node;
119
189
 
120
190
  if(!(rb_obj_is_kind_of(rb_node, cNokogiriXmlNode) || rb_obj_is_kind_of(rb_node, cNokogiriXmlNamespace)))
121
191
  rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node or Nokogiri::XML::Namespace");
122
192
 
123
- Data_Get_Struct(self, xmlNodeSet, node_set);
193
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
124
194
  Data_Get_Struct(rb_node, xmlNode, node);
125
195
 
126
- return (xmlXPathNodeSetContains(node_set, node) ? Qtrue : Qfalse);
196
+ return (xmlXPathNodeSetContains(tuple->node_set, node) ? Qtrue : Qfalse);
127
197
  }
128
198
 
129
199
 
@@ -136,18 +206,17 @@ static VALUE include_eh(VALUE self, VALUE rb_node)
136
206
  */
137
207
  static VALUE set_union(VALUE self, VALUE rb_other)
138
208
  {
139
- xmlNodeSetPtr node_set;
140
- xmlNodeSetPtr other;
209
+ nokogiriNodeSetTuple *tuple, *other;
141
210
  xmlNodeSetPtr new;
142
211
 
143
212
  if(!rb_obj_is_kind_of(rb_other, cNokogiriXmlNodeSet))
144
213
  rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet");
145
214
 
146
- Data_Get_Struct(self, xmlNodeSet, node_set);
147
- Data_Get_Struct(rb_other, xmlNodeSet, other);
215
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
216
+ Data_Get_Struct(rb_other, nokogiriNodeSetTuple, other);
148
217
 
149
- new = xmlXPathNodeSetMerge(NULL, node_set);
150
- new = xmlXPathNodeSetMerge(new, other);
218
+ new = xmlXPathNodeSetMerge(NULL, tuple->node_set);
219
+ new = xmlXPathNodeSetMerge(new, other->node_set);
151
220
 
152
221
  return Nokogiri_wrap_xml_node_set(new, rb_iv_get(self, "@document"));
153
222
  }
@@ -161,20 +230,19 @@ static VALUE set_union(VALUE self, VALUE rb_other)
161
230
  */
162
231
  static VALUE minus(VALUE self, VALUE rb_other)
163
232
  {
164
- xmlNodeSetPtr node_set;
165
- xmlNodeSetPtr other;
233
+ nokogiriNodeSetTuple *tuple, *other;
166
234
  xmlNodeSetPtr new;
167
235
  int j ;
168
236
 
169
237
  if(!rb_obj_is_kind_of(rb_other, cNokogiriXmlNodeSet))
170
238
  rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet");
171
239
 
172
- Data_Get_Struct(self, xmlNodeSet, node_set);
173
- Data_Get_Struct(rb_other, xmlNodeSet, other);
240
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
241
+ Data_Get_Struct(rb_other, nokogiriNodeSetTuple, other);
174
242
 
175
- new = xmlXPathNodeSetMerge(NULL, node_set);
176
- for (j = 0 ; j < other->nodeNr ; ++j) {
177
- xmlXPathNodeSetDel(new, other->nodeTab[j]);
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]);
178
246
  }
179
247
 
180
248
  return Nokogiri_wrap_xml_node_set(new, rb_iv_get(self, "@document"));
@@ -184,10 +252,16 @@ static VALUE minus(VALUE self, VALUE rb_other)
184
252
  static VALUE index_at(VALUE self, long offset)
185
253
  {
186
254
  xmlNodeSetPtr node_set;
187
- Data_Get_Struct(self, xmlNodeSet, node_set);
255
+ nokogiriNodeSetTuple *tuple;
256
+
257
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
258
+ node_set = tuple->node_set;
259
+
260
+ if (offset >= node_set->nodeNr || abs((int)offset) > node_set->nodeNr)
261
+ return Qnil;
188
262
 
189
- if(offset >= node_set->nodeNr || abs((int)offset) > node_set->nodeNr) return Qnil;
190
- if(offset < 0) offset = offset + node_set->nodeNr;
263
+ if (offset < 0)
264
+ offset += node_set->nodeNr;
191
265
 
192
266
  if (XML_NAMESPACE_DECL == node_set->nodeTab[offset]->type)
193
267
  return Nokogiri_wrap_xml_namespace2(rb_iv_get(self, "@document"), (xmlNsPtr)(node_set->nodeTab[offset]));
@@ -197,10 +271,12 @@ static VALUE index_at(VALUE self, long offset)
197
271
  static VALUE subseq(VALUE self, long beg, long len)
198
272
  {
199
273
  long j;
274
+ nokogiriNodeSetTuple *tuple;
200
275
  xmlNodeSetPtr node_set;
201
276
  xmlNodeSetPtr new_set ;
202
277
 
203
- Data_Get_Struct(self, xmlNodeSet, node_set);
278
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
279
+ node_set = tuple->node_set;
204
280
 
205
281
  if (beg > node_set->nodeNr) return Qnil ;
206
282
  if (beg < 0 || len < 0) return Qnil ;
@@ -236,7 +312,9 @@ static VALUE slice(int argc, VALUE *argv, VALUE self)
236
312
  VALUE arg ;
237
313
  long beg, len ;
238
314
  xmlNodeSetPtr node_set;
239
- Data_Get_Struct(self, xmlNodeSet, node_set);
315
+ nokogiriNodeSetTuple *tuple;
316
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
317
+ node_set = tuple->node_set;
240
318
 
241
319
  if (argc == 2) {
242
320
  beg = NUM2LONG(argv[0]);
@@ -282,25 +360,17 @@ static VALUE to_array(VALUE self, VALUE rb_node)
282
360
  VALUE *elts;
283
361
  VALUE list;
284
362
  int i;
363
+ nokogiriNodeSetTuple *tuple;
285
364
 
286
- Data_Get_Struct(self, xmlNodeSet, set);
365
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
366
+ set = tuple->node_set;
287
367
 
288
368
  elts = calloc((size_t)set->nodeNr, sizeof(VALUE *));
289
369
  for(i = 0; i < set->nodeNr; i++) {
290
- if (XML_NAMESPACE_DECL == set->nodeTab[i]->type) {
370
+ if (XML_NAMESPACE_DECL == set->nodeTab[i]->type)
291
371
  elts[i] = Nokogiri_wrap_xml_namespace2(rb_iv_get(self, "@document"), (xmlNsPtr)(set->nodeTab[i]));
292
- } else {
293
- xmlNodePtr node = set->nodeTab[i];
294
-
295
- if(node->_private) {
296
- if(node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE)
297
- elts[i] = DOC_RUBY_OBJECT(node->doc);
298
- else
299
- elts[i] = (VALUE)node->_private;
300
- } else {
301
- elts[i] = Nokogiri_wrap_xml_node(Qnil, node);
302
- }
303
- }
372
+ else
373
+ elts[i] = Nokogiri_wrap_xml_node(Qnil, set->nodeTab[i]);
304
374
  }
305
375
 
306
376
  list = rb_ary_new4((long)set->nodeNr, elts);
@@ -320,8 +390,10 @@ static VALUE unlink_nodeset(VALUE self)
320
390
  {
321
391
  xmlNodeSetPtr node_set;
322
392
  int j, nodeNr ;
393
+ nokogiriNodeSetTuple *tuple;
323
394
 
324
- Data_Get_Struct(self, xmlNodeSet, node_set);
395
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
396
+ node_set = tuple->node_set;
325
397
  nodeNr = node_set->nodeNr ;
326
398
  for (j = 0 ; j < nodeNr ; j++) {
327
399
  if (XML_NAMESPACE_DECL != node_set->nodeTab[j]->type) {
@@ -336,68 +408,45 @@ static VALUE unlink_nodeset(VALUE self)
336
408
  return self ;
337
409
  }
338
410
 
339
-
340
- static void deallocate(xmlNodeSetPtr node_set)
341
- {
342
- /*
343
- * xmlXPathFreeNodeSet() contains an implicit assumption that it is being
344
- * called before any of its pointed-to nodes have been free()d. this
345
- * assumption lies in the operation where it dereferences nodeTab pointers
346
- * while searching for namespace nodes to free.
347
- *
348
- * however, since Ruby's GC mechanism cannot guarantee the strict order in
349
- * which ruby objects will be GC'd, nodes may be garbage collected before a
350
- * nodeset containing pointers to those nodes. (this is true regardless of
351
- * how we declare dependencies between objects with rb_gc_mark().)
352
- *
353
- * as a result, xmlXPathFreeNodeSet() will perform unsafe memory operations,
354
- * and calling it would be evil.
355
- *
356
- * on the bright side, though, Nokogiri's API currently does not cause
357
- * namespace nodes to be included in node sets, ever.
358
- *
359
- * armed with that fact, we examined xmlXPathFreeNodeSet() and related libxml
360
- * code and determined that, within the Nokogiri abstraction, we will not
361
- * leak memory if we simply free the node set's memory directly. that's only
362
- * quasi-evil!
363
- *
364
- * there's probably a lesson in here somewhere about intermingling, within a
365
- * single array, structs with different memory-ownership semantics. or more
366
- * generally, a lesson about building an API in C/C++ that does not contain
367
- * assumptions about the strict order in which memory will be released. hey,
368
- * that sounds like a great idea for a blog post! get to it!
369
- *
370
- * "In Valgrind We Trust." seriously.
371
- */
372
- NOKOGIRI_DEBUG_START(node_set) ;
373
- if (node_set->nodeTab != NULL)
374
- xmlFree(node_set->nodeTab);
375
- xmlFree(node_set);
376
- NOKOGIRI_DEBUG_END(node_set) ;
377
- }
378
-
379
- static VALUE allocate(VALUE klass)
380
- {
381
- return Nokogiri_wrap_xml_node_set(xmlXPathNodeSetCreate(NULL), Qnil);
382
- }
383
-
384
411
  VALUE Nokogiri_wrap_xml_node_set(xmlNodeSetPtr node_set, VALUE document)
385
412
  {
386
413
  VALUE new_set ;
387
- new_set = Data_Wrap_Struct(cNokogiriXmlNodeSet, 0, deallocate, node_set);
388
- if (document != Qnil) {
414
+ int i;
415
+ xmlNodePtr cur;
416
+ xmlNsPtr ns;
417
+ nokogiriNodeSetTuple *tuple;
418
+
419
+ new_set = Data_Make_Struct(cNokogiriXmlNodeSet, nokogiriNodeSetTuple, 0,
420
+ deallocate, tuple);
421
+
422
+ tuple->node_set = node_set;
423
+ tuple->namespaces = st_init_numtable();
424
+
425
+ if (!NIL_P(document)) {
389
426
  rb_iv_set(new_set, "@document", document);
390
427
  rb_funcall(document, decorate, 1, new_set);
391
428
  }
429
+
430
+ if (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
+ }
440
+
392
441
  return new_set ;
393
442
  }
394
443
 
395
444
  VALUE cNokogiriXmlNodeSet ;
396
445
  void init_xml_node_set(void)
397
446
  {
398
- VALUE nokogiri = rb_define_module("Nokogiri");
399
- VALUE xml = rb_define_module_under(nokogiri, "XML");
400
- VALUE klass = rb_define_class_under(xml, "NodeSet", rb_cObject);
447
+ VALUE nokogiri = rb_define_module("Nokogiri");
448
+ VALUE xml = rb_define_module_under(nokogiri, "XML");
449
+ VALUE klass = rb_define_class_under(xml, "NodeSet", rb_cObject);
401
450
  cNokogiriXmlNodeSet = klass;
402
451
 
403
452
  rb_define_alloc_func(klass, allocate);
@@ -414,5 +463,5 @@ void init_xml_node_set(void)
414
463
  rb_define_method(klass, "&", intersection, 1);
415
464
  rb_define_method(klass, "include?", include_eh, 1);
416
465
 
417
- decorate = rb_intern("decorate");
466
+ decorate = rb_intern("decorate");
418
467
  }
@@ -6,4 +6,9 @@ void init_xml_node_set();
6
6
 
7
7
  extern VALUE cNokogiriXmlNodeSet ;
8
8
  VALUE Nokogiri_wrap_xml_node_set(xmlNodeSetPtr node_set, VALUE document) ;
9
+
10
+ typedef struct _nokogiriNodeSetTuple {
11
+ xmlNodeSetPtr node_set;
12
+ st_table *namespaces;
13
+ } nokogiriNodeSetTuple;
9
14
  #endif