nokogiri 1.6.7.2-java → 1.6.8-java

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/.cross_rubies +2 -0
  3. data/.travis.yml +19 -9
  4. data/CHANGELOG.rdoc +73 -5
  5. data/CONTRIBUTING.md +42 -0
  6. data/Gemfile +10 -9
  7. data/LICENSE.txt +1 -1
  8. data/Manifest.txt +7 -2
  9. data/README.md +23 -27
  10. data/ROADMAP.md +11 -1
  11. data/Rakefile +36 -17
  12. data/bin/nokogiri +2 -2
  13. data/dependencies.yml +29 -4
  14. data/ext/java/nokogiri/HtmlElementDescription.java +5 -2
  15. data/ext/java/nokogiri/NokogiriService.java +19 -0
  16. data/ext/java/nokogiri/XmlAttr.java +3 -1
  17. data/ext/java/nokogiri/XmlDocumentFragment.java +0 -14
  18. data/ext/java/nokogiri/XmlNode.java +106 -63
  19. data/ext/java/nokogiri/XmlXpathContext.java +12 -12
  20. data/ext/java/nokogiri/XsltStylesheet.java +11 -4
  21. data/ext/java/nokogiri/internals/HtmlDomParserContext.java +8 -1
  22. data/ext/java/nokogiri/internals/NokogiriErrorHandler.java +1 -2
  23. data/ext/java/nokogiri/internals/NokogiriHelpers.java +7 -7
  24. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler4NekoHtml.java +1 -1
  25. data/ext/java/nokogiri/internals/NokogiriStrictErrorHandler.java +0 -1
  26. data/ext/java/nokogiri/internals/NokogiriXsltErrorListener.java +3 -3
  27. data/ext/java/nokogiri/internals/ParserContext.java +4 -0
  28. data/ext/java/nokogiri/internals/SaveContextVisitor.java +18 -13
  29. data/ext/nokogiri/extconf.rb +163 -79
  30. data/ext/nokogiri/html_document.c +6 -6
  31. data/ext/nokogiri/html_element_description.c +1 -1
  32. data/ext/nokogiri/html_entity_lookup.c +1 -1
  33. data/ext/nokogiri/html_sax_parser_context.c +4 -4
  34. data/ext/nokogiri/html_sax_push_parser.c +2 -2
  35. data/ext/nokogiri/nokogiri.c +0 -7
  36. data/ext/nokogiri/nokogiri.h +1 -34
  37. data/ext/nokogiri/xml_attr.c +2 -2
  38. data/ext/nokogiri/xml_comment.c +1 -1
  39. data/ext/nokogiri/xml_document.c +20 -22
  40. data/ext/nokogiri/xml_encoding_handler.c +3 -3
  41. data/ext/nokogiri/xml_entity_reference.c +1 -1
  42. data/ext/nokogiri/xml_namespace.c +56 -17
  43. data/ext/nokogiri/xml_node.c +73 -67
  44. data/ext/nokogiri/xml_node_set.c +164 -146
  45. data/ext/nokogiri/xml_node_set.h +3 -4
  46. data/ext/nokogiri/xml_processing_instruction.c +2 -2
  47. data/ext/nokogiri/xml_reader.c +5 -18
  48. data/ext/nokogiri/xml_sax_parser.c +9 -12
  49. data/ext/nokogiri/xml_sax_parser_context.c +1 -1
  50. data/ext/nokogiri/xml_sax_push_parser.c +1 -1
  51. data/ext/nokogiri/xml_schema.c +1 -1
  52. data/ext/nokogiri/xml_syntax_error.c +0 -4
  53. data/ext/nokogiri/xml_syntax_error.h +0 -1
  54. data/ext/nokogiri/xml_text.c +1 -1
  55. data/ext/nokogiri/xml_xpath_context.c +15 -24
  56. data/ext/nokogiri/xslt_stylesheet.c +6 -6
  57. data/lib/nekohtml.jar +0 -0
  58. data/lib/nokogiri.rb +14 -7
  59. data/lib/nokogiri/css/parser.rb +8 -2
  60. data/lib/nokogiri/css/parser.y +7 -2
  61. data/lib/nokogiri/html/document.rb +4 -2
  62. data/lib/nokogiri/nokogiri.jar +0 -0
  63. data/lib/nokogiri/version.rb +1 -1
  64. data/lib/nokogiri/xml/document.rb +7 -1
  65. data/lib/nokogiri/xml/dtd.rb +4 -4
  66. data/lib/nokogiri/xml/node.rb +6 -10
  67. data/lib/nokogiri/xml/node_set.rb +3 -3
  68. data/lib/nokogiri/xml/parse_options.rb +22 -0
  69. data/lib/serializer.jar +0 -0
  70. data/lib/xalan.jar +0 -0
  71. data/lib/xercesImpl.jar +0 -0
  72. data/lib/xml-apis.jar +0 -0
  73. data/tasks/test.rb +5 -0
  74. data/test/css/test_parser.rb +7 -1
  75. data/test/files/GH_1042.html +18 -0
  76. data/test/files/namespace_pressure_test.xml +1684 -0
  77. data/test/files/tlm.html +2 -1
  78. data/test/helper.rb +4 -0
  79. data/test/html/sax/test_parser.rb +2 -2
  80. data/test/html/test_document.rb +47 -11
  81. data/test/html/test_document_encoding.rb +55 -58
  82. data/test/html/test_document_fragment.rb +27 -23
  83. data/test/html/test_node.rb +16 -0
  84. data/test/html/test_node_encoding.rb +71 -13
  85. data/test/namespaces/test_namespaces_in_parsed_doc.rb +14 -0
  86. data/test/test_css_cache.rb +1 -1
  87. data/test/test_encoding_handler.rb +2 -0
  88. data/test/test_xslt_transforms.rb +38 -3
  89. data/test/xml/sax/test_parser.rb +54 -53
  90. data/test/xml/test_document.rb +7 -2
  91. data/test/xml/test_document_encoding.rb +19 -16
  92. data/test/xml/test_document_fragment.rb +12 -0
  93. data/test/xml/test_dtd_encoding.rb +0 -2
  94. data/test/xml/test_namespace.rb +2 -2
  95. data/test/xml/test_node.rb +15 -4
  96. data/test/xml/test_node_attributes.rb +6 -0
  97. data/test/xml/test_node_encoding.rb +49 -87
  98. data/test/xml/test_node_reparenting.rb +193 -18
  99. data/test/xml/test_node_set.rb +1 -1
  100. data/test/xml/test_reader.rb +589 -0
  101. data/test/xml/test_reader_encoding.rb +100 -102
  102. data/test/xml/test_unparented_node.rb +14 -1
  103. data/test/xslt/test_exception_handling.rb +1 -1
  104. data/test_all +47 -33
  105. metadata +38 -36
  106. data/CHANGELOG.ja.rdoc +0 -1057
  107. data/test/test_reader.rb +0 -558
@@ -35,13 +35,13 @@ static void relink_namespace(xmlNodePtr reparented)
35
35
  xmlNsPtr ns;
36
36
 
37
37
  if (reparented->type != XML_ATTRIBUTE_NODE &&
38
- reparented->type != XML_ELEMENT_NODE) return;
38
+ reparented->type != XML_ELEMENT_NODE) { return; }
39
39
 
40
40
  if (reparented->ns == NULL || reparented->ns->prefix == NULL) {
41
41
  name = xmlSplitQName2(reparented->name, &prefix);
42
42
 
43
43
  if(reparented->type == XML_ATTRIBUTE_NODE) {
44
- if (prefix == NULL || strcmp((char*)prefix, XMLNS_PREFIX) == 0) return;
44
+ if (prefix == NULL || strcmp((char*)prefix, XMLNS_PREFIX) == 0) { return; }
45
45
  }
46
46
 
47
47
  ns = xmlSearchNs(reparented->doc, reparented, prefix);
@@ -57,18 +57,19 @@ static void relink_namespace(xmlNodePtr reparented)
57
57
  }
58
58
 
59
59
  /* Avoid segv when relinking against unlinked nodes. */
60
- if (reparented->type != XML_ELEMENT_NODE || !reparented->parent) return;
60
+ if (reparented->type != XML_ELEMENT_NODE || !reparented->parent) { return; }
61
61
 
62
62
  /* Make sure that our reparented node has the correct namespaces */
63
- if(!reparented->ns && reparented->doc != (xmlDocPtr)reparented->parent)
63
+ if (!reparented->ns && reparented->doc != (xmlDocPtr)reparented->parent) {
64
64
  xmlSetNs(reparented, reparented->parent->ns);
65
+ }
65
66
 
66
67
  /* Search our parents for an existing definition */
67
- if(reparented->nsDef) {
68
+ if (reparented->nsDef) {
68
69
  xmlNsPtr curr = reparented->nsDef;
69
70
  xmlNsPtr prev = NULL;
70
71
 
71
- while(curr) {
72
+ while (curr) {
72
73
  xmlNsPtr ns = xmlSearchNsByHref(
73
74
  reparented->doc,
74
75
  reparented->parent,
@@ -76,7 +77,7 @@ static void relink_namespace(xmlNodePtr reparented)
76
77
  );
77
78
  /* If we find the namespace is already declared, remove it from this
78
79
  * definition list. */
79
- if(ns && ns != curr) {
80
+ if (ns && ns != curr && xmlStrEqual(ns->prefix, curr->prefix)) {
80
81
  if (prev) {
81
82
  prev->next = curr->next;
82
83
  } else {
@@ -92,12 +93,12 @@ static void relink_namespace(xmlNodePtr reparented)
92
93
 
93
94
  /* Only walk all children if there actually is a namespace we need to */
94
95
  /* reparent. */
95
- if(NULL == reparented->ns) return;
96
+ if (NULL == reparented->ns) { return; }
96
97
 
97
98
  /* When a node gets reparented, walk it's children to make sure that */
98
99
  /* their namespaces are reparented as well. */
99
100
  child = reparented->children;
100
- while(NULL != child) {
101
+ while (NULL != child) {
101
102
  relink_namespace(child);
102
103
  child = child->next;
103
104
  }
@@ -140,6 +141,7 @@ static VALUE reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_rep
140
141
  {
141
142
  VALUE reparented_obj ;
142
143
  xmlNodePtr reparentee, pivot, reparented, next_text, new_next_text, parent ;
144
+ int original_ns_prefix_is_default = 0 ;
143
145
 
144
146
  if(!rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlNode))
145
147
  rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
@@ -233,18 +235,38 @@ ok:
233
235
  * uninteresting libxml2 implementation detail). as a result, we cannot
234
236
  * reparent the actual reparentee, so we reparent a duplicate.
235
237
  */
236
- nokogiri_root_node(reparentee);
237
-
238
- xmlResetLastError();
239
- xmlSetStructuredErrorFunc((void *)rb_iv_get(DOC_RUBY_OBJECT(pivot->doc), "@errors"), Nokogiri_error_array_pusher);
238
+ if (reparentee->type == XML_TEXT_NODE && reparentee->_private) {
239
+ /*
240
+ * additionally, since we know this C struct isn't going to be related to
241
+ * a Ruby object anymore, let's break the relationship on this end as
242
+ * well.
243
+ *
244
+ * this is not absolutely necessary unless libxml-ruby is also in effect,
245
+ * in which case its global callback `rxml_node_deregisterNode` will try
246
+ * to do things to our data.
247
+ *
248
+ * for more details on this particular (and particularly nasty) edge
249
+ * case, see:
250
+ *
251
+ * https://github.com/sparklemotion/nokogiri/issues/1426
252
+ */
253
+ reparentee->_private = NULL ;
254
+ }
240
255
 
241
- reparentee = xmlDocCopyNode(reparentee, pivot->doc, 1) ;
256
+ if (reparentee->ns != NULL && reparentee->ns->prefix == NULL) {
257
+ original_ns_prefix_is_default = 1;
258
+ }
242
259
 
243
- xmlSetStructuredErrorFunc(NULL, NULL);
260
+ nokogiri_root_node(reparentee);
244
261
 
245
- if (! reparentee) {
262
+ if (!(reparentee = xmlDocCopyNode(reparentee, pivot->doc, 1))) {
246
263
  rb_raise(rb_eRuntimeError, "Could not reparent node (xmlDocCopyNode)");
247
264
  }
265
+
266
+ if (original_ns_prefix_is_default && reparentee->ns != NULL && reparentee->ns->prefix != NULL) {
267
+ /* issue #391, where new node's prefix may become the string "default" */
268
+ reparentee->ns->prefix = NULL;
269
+ }
248
270
  }
249
271
 
250
272
  if (prf != xmlAddPrevSibling && prf != xmlAddNextSibling
@@ -339,7 +361,7 @@ static VALUE encode_special_chars(VALUE self, VALUE string)
339
361
  Data_Get_Struct(self, xmlNode, node);
340
362
  encoded = xmlEncodeSpecialChars(
341
363
  node->doc,
342
- (const xmlChar *)StringValuePtr(string)
364
+ (const xmlChar *)StringValueCStr(string)
343
365
  );
344
366
 
345
367
  encoded_str = NOKOGIRI_STR_NEW2(encoded);
@@ -375,9 +397,9 @@ static VALUE create_internal_subset(VALUE self, VALUE name, VALUE external_id, V
375
397
 
376
398
  dtd = xmlCreateIntSubset(
377
399
  doc,
378
- NIL_P(name) ? NULL : (const xmlChar *)StringValuePtr(name),
379
- NIL_P(external_id) ? NULL : (const xmlChar *)StringValuePtr(external_id),
380
- NIL_P(system_id) ? NULL : (const xmlChar *)StringValuePtr(system_id)
400
+ NIL_P(name) ? NULL : (const xmlChar *)StringValueCStr(name),
401
+ NIL_P(external_id) ? NULL : (const xmlChar *)StringValueCStr(external_id),
402
+ NIL_P(system_id) ? NULL : (const xmlChar *)StringValueCStr(system_id)
381
403
  );
382
404
 
383
405
  if(!dtd) return Qnil;
@@ -406,9 +428,9 @@ static VALUE create_external_subset(VALUE self, VALUE name, VALUE external_id, V
406
428
 
407
429
  dtd = xmlNewDtd(
408
430
  doc,
409
- NIL_P(name) ? NULL : (const xmlChar *)StringValuePtr(name),
410
- NIL_P(external_id) ? NULL : (const xmlChar *)StringValuePtr(external_id),
411
- NIL_P(system_id) ? NULL : (const xmlChar *)StringValuePtr(system_id)
431
+ NIL_P(name) ? NULL : (const xmlChar *)StringValueCStr(name),
432
+ NIL_P(external_id) ? NULL : (const xmlChar *)StringValueCStr(external_id),
433
+ NIL_P(system_id) ? NULL : (const xmlChar *)StringValueCStr(system_id)
412
434
  );
413
435
 
414
436
  if(!dtd) return Qnil;
@@ -481,13 +503,7 @@ static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
481
503
 
482
504
  Data_Get_Struct(self, xmlNode, node);
483
505
 
484
- xmlResetLastError();
485
- xmlSetStructuredErrorFunc(NULL, Nokogiri_error_silencer);
486
-
487
506
  dup = xmlDocCopyNode(node, node->doc, (int)NUM2INT(level));
488
-
489
- xmlSetStructuredErrorFunc(NULL, NULL);
490
-
491
507
  if(dup == NULL) return Qnil;
492
508
 
493
509
  nokogiri_root_node(dup);
@@ -751,7 +767,7 @@ static VALUE key_eh(VALUE self, VALUE attribute)
751
767
  {
752
768
  xmlNodePtr node;
753
769
  Data_Get_Struct(self, xmlNode, node);
754
- if(xmlHasProp(node, (xmlChar *)StringValuePtr(attribute)))
770
+ if(xmlHasProp(node, (xmlChar *)StringValueCStr(attribute)))
755
771
  return Qtrue;
756
772
  return Qfalse;
757
773
  }
@@ -766,8 +782,8 @@ static VALUE namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
766
782
  {
767
783
  xmlNodePtr node;
768
784
  Data_Get_Struct(self, xmlNode, node);
769
- if(xmlHasNsProp(node, (xmlChar *)StringValuePtr(attribute),
770
- NIL_P(namespace) ? NULL : (xmlChar *)StringValuePtr(namespace)))
785
+ if(xmlHasNsProp(node, (xmlChar *)StringValueCStr(attribute),
786
+ NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace)))
771
787
  return Qtrue;
772
788
  return Qfalse;
773
789
  }
@@ -778,19 +794,11 @@ static VALUE namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
778
794
  *
779
795
  * Set the +property+ to +value+
780
796
  */
781
- static VALUE set(VALUE node_rb, VALUE property_name_rb, VALUE property_value_rb)
797
+ static VALUE set(VALUE self, VALUE property, VALUE value)
782
798
  {
783
799
  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);
800
+ xmlAttrPtr prop;
801
+ Data_Get_Struct(self, xmlNode, node);
794
802
 
795
803
  /* If a matching attribute node already exists, then xmlSetProp will destroy
796
804
  * the existing node's children. However, if Nokogiri has a node object
@@ -798,9 +806,11 @@ static VALUE set(VALUE node_rb, VALUE property_name_rb, VALUE property_value_rb)
798
806
  *
799
807
  * We can avoid this by unlinking these nodes first.
800
808
  */
801
- property = xmlHasProp(node, property_name);
802
- if (property && property->children) {
803
- for (cur = property->children; cur; cur = cur->next) {
809
+ if (node->type != XML_ELEMENT_NODE)
810
+ return(Qnil);
811
+ prop = xmlHasProp(node, (xmlChar *)StringValueCStr(property));
812
+ if (prop && prop->children) {
813
+ for (cur = prop->children; cur; cur = cur->next) {
804
814
  if (cur->_private) {
805
815
  nokogiri_root_node(cur);
806
816
  xmlUnlinkNode(cur);
@@ -808,14 +818,10 @@ static VALUE set(VALUE node_rb, VALUE property_name_rb, VALUE property_value_rb)
808
818
  }
809
819
  }
810
820
 
811
- xmlResetLastError();
812
- xmlSetStructuredErrorFunc(NULL, Nokogiri_error_silencer);
813
-
814
- xmlSetProp(node, property_name, (xmlChar *)StringValuePtr(property_value_rb));
815
-
816
- xmlSetStructuredErrorFunc(NULL, NULL);
821
+ xmlSetProp(node, (xmlChar *)StringValueCStr(property),
822
+ (xmlChar *)StringValueCStr(value));
817
823
 
818
- return property_value_rb;
824
+ return value;
819
825
  }
820
826
 
821
827
  /*
@@ -836,7 +842,7 @@ static VALUE get(VALUE self, VALUE rattribute)
836
842
  if (NIL_P(rattribute)) return Qnil;
837
843
 
838
844
  Data_Get_Struct(self, xmlNode, node);
839
- attribute = strdup(StringValuePtr(rattribute));
845
+ attribute = strdup(StringValueCStr(rattribute));
840
846
 
841
847
  colon = strchr(attribute, ':');
842
848
  if (colon) {
@@ -847,7 +853,7 @@ static VALUE get(VALUE self, VALUE rattribute)
847
853
  if (ns) {
848
854
  value = xmlGetNsProp(node, (xmlChar*)(attr_name), ns->href);
849
855
  } else {
850
- value = xmlGetProp(node, (xmlChar*)StringValuePtr(rattribute));
856
+ value = xmlGetProp(node, (xmlChar*)StringValueCStr(rattribute));
851
857
  }
852
858
  } else {
853
859
  value = xmlGetNoNsProp(node, (xmlChar*)attribute);
@@ -894,7 +900,7 @@ static VALUE attr(VALUE self, VALUE name)
894
900
  xmlNodePtr node;
895
901
  xmlAttrPtr prop;
896
902
  Data_Get_Struct(self, xmlNode, node);
897
- prop = xmlHasProp(node, (xmlChar *)StringValuePtr(name));
903
+ prop = xmlHasProp(node, (xmlChar *)StringValueCStr(name));
898
904
 
899
905
  if(! prop) return Qnil;
900
906
  return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)prop);
@@ -911,8 +917,8 @@ static VALUE attribute_with_ns(VALUE self, VALUE name, VALUE namespace)
911
917
  xmlNodePtr node;
912
918
  xmlAttrPtr prop;
913
919
  Data_Get_Struct(self, xmlNode, node);
914
- prop = xmlHasNsProp(node, (xmlChar *)StringValuePtr(name),
915
- NIL_P(namespace) ? NULL : (xmlChar *)StringValuePtr(namespace));
920
+ prop = xmlHasNsProp(node, (xmlChar *)StringValueCStr(name),
921
+ NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace));
916
922
 
917
923
  if(! prop) return Qnil;
918
924
  return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)prop);
@@ -1049,7 +1055,7 @@ static VALUE set_native_content(VALUE self, VALUE content)
1049
1055
  child = next ;
1050
1056
  }
1051
1057
 
1052
- xmlNodeSetContent(node, (xmlChar *)StringValuePtr(content));
1058
+ xmlNodeSetContent(node, (xmlChar *)StringValueCStr(content));
1053
1059
  return content;
1054
1060
  }
1055
1061
 
@@ -1087,7 +1093,7 @@ static VALUE set_lang(VALUE self_rb, VALUE lang_rb)
1087
1093
  xmlChar* lang ;
1088
1094
 
1089
1095
  Data_Get_Struct(self_rb, xmlNode, self);
1090
- lang = (xmlChar*)StringValuePtr(lang_rb);
1096
+ lang = (xmlChar*)StringValueCStr(lang_rb);
1091
1097
 
1092
1098
  xmlNodeSetLang(self, lang);
1093
1099
 
@@ -1152,7 +1158,7 @@ static VALUE set_name(VALUE self, VALUE new_name)
1152
1158
  {
1153
1159
  xmlNodePtr node;
1154
1160
  Data_Get_Struct(self, xmlNode, node);
1155
- xmlNodeSetName(node, (xmlChar*)StringValuePtr(new_name));
1161
+ xmlNodeSetName(node, (xmlChar*)StringValueCStr(new_name));
1156
1162
  return new_name;
1157
1163
  }
1158
1164
 
@@ -1226,13 +1232,13 @@ static VALUE native_write_to(
1226
1232
 
1227
1233
  before_indent = xmlTreeIndentString;
1228
1234
 
1229
- xmlTreeIndentString = StringValuePtr(indent_string);
1235
+ xmlTreeIndentString = StringValueCStr(indent_string);
1230
1236
 
1231
1237
  savectx = xmlSaveToIO(
1232
1238
  (xmlOutputWriteCallback)io_write_callback,
1233
1239
  (xmlOutputCloseCallback)io_close_callback,
1234
1240
  (void *)io,
1235
- RTEST(encoding) ? StringValuePtr(encoding) : NULL,
1241
+ RTEST(encoding) ? StringValueCStr(encoding) : NULL,
1236
1242
  (int)NUM2INT(options)
1237
1243
  );
1238
1244
 
@@ -1279,7 +1285,7 @@ static VALUE add_namespace_definition(VALUE self, VALUE prefix, VALUE href)
1279
1285
  ns = xmlSearchNs(
1280
1286
  node->doc,
1281
1287
  node,
1282
- (const xmlChar *)(NIL_P(prefix) ? NULL : StringValuePtr(prefix))
1288
+ (const xmlChar *)(NIL_P(prefix) ? NULL : StringValueCStr(prefix))
1283
1289
  );
1284
1290
 
1285
1291
  if(!ns) {
@@ -1288,8 +1294,8 @@ static VALUE add_namespace_definition(VALUE self, VALUE prefix, VALUE href)
1288
1294
  }
1289
1295
  ns = xmlNewNs(
1290
1296
  namespacee,
1291
- (const xmlChar *)StringValuePtr(href),
1292
- (const xmlChar *)(NIL_P(prefix) ? NULL : StringValuePtr(prefix))
1297
+ (const xmlChar *)StringValueCStr(href),
1298
+ (const xmlChar *)(NIL_P(prefix) ? NULL : StringValueCStr(prefix))
1293
1299
  );
1294
1300
  }
1295
1301
 
@@ -1319,7 +1325,7 @@ static VALUE new(int argc, VALUE *argv, VALUE klass)
1319
1325
 
1320
1326
  Data_Get_Struct(document, xmlDoc, doc);
1321
1327
 
1322
- node = xmlNewNode(NULL, (xmlChar *)StringValuePtr(name));
1328
+ node = xmlNewNode(NULL, (xmlChar *)StringValueCStr(name));
1323
1329
  node->doc = doc->doc;
1324
1330
  nokogiri_root_node(node);
1325
1331
 
@@ -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,27 +320,18 @@ 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;
360
- VALUE *elts;
323
+ xmlNodeSetPtr node_set ;
361
324
  VALUE list;
362
325
  int i;
363
- nokogiriNodeSetTuple *tuple;
364
326
 
365
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
366
- set = tuple->node_set;
327
+ Data_Get_Struct(self, xmlNodeSet, node_set);
367
328
 
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]);
329
+ list = rb_ary_new2(node_set->nodeNr);
330
+ for(i = 0; i < node_set->nodeNr; i++) {
331
+ VALUE elt = Nokogiri_wrap_xml_node_set_node(node_set->nodeTab[i], self);
332
+ rb_ary_push( list, elt );
374
333
  }
375
334
 
376
- list = rb_ary_new4((long)set->nodeNr, elts);
377
-
378
- /*free(elts); */
379
-
380
335
  return list;
381
336
  }
382
337
 
@@ -390,13 +345,12 @@ static VALUE unlink_nodeset(VALUE self)
390
345
  {
391
346
  xmlNodeSetPtr node_set;
392
347
  int j, nodeNr ;
393
- nokogiriNodeSetTuple *tuple;
394
348
 
395
- Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
396
- node_set = tuple->node_set;
349
+ Data_Get_Struct(self, xmlNodeSet, node_set);
350
+
397
351
  nodeNr = node_set->nodeNr ;
398
352
  for (j = 0 ; j < nodeNr ; j++) {
399
- if (XML_NAMESPACE_DECL != node_set->nodeTab[j]->type) {
353
+ if (! Nokogiri_namespace_eh(node_set->nodeTab[j])) {
400
354
  VALUE node ;
401
355
  xmlNodePtr node_ptr;
402
356
  node = Nokogiri_wrap_xml_node(Qnil, node_set->nodeTab[j]);
@@ -408,39 +362,103 @@ static VALUE unlink_nodeset(VALUE self)
408
362
  return self ;
409
363
  }
410
364
 
365
+
366
+ static void reify_node_set_namespaces(VALUE self)
367
+ {
368
+ /*
369
+ * as mentioned in deallocate() above, xmlNs structs returned in an XPath
370
+ * NodeSet are duplicates, and we don't clean them up at deallocate() time.
371
+ *
372
+ * as a result, we need to make sure the Ruby manages this memory. we do this
373
+ * by forcing the creation of a Ruby object wrapped around the xmlNs.
374
+ *
375
+ * we also have to make sure that the NodeSet has a reference to the
376
+ * Namespace object, otherwise GC will kick in and the Namespace won't be
377
+ * marked.
378
+ *
379
+ * we *could* do this safely with *all* the nodes in the NodeSet, but we only
380
+ * *need* to do it for xmlNs structs, and so you get the code we have here.
381
+ */
382
+ int j ;
383
+ xmlNodeSetPtr node_set ;
384
+ VALUE namespace_cache ;
385
+
386
+ Data_Get_Struct(self, xmlNodeSet, node_set);
387
+
388
+ namespace_cache = rb_iv_get(self, "@namespace_cache");
389
+
390
+ for (j = 0 ; j < node_set->nodeNr ; j++) {
391
+ if (Nokogiri_namespace_eh(node_set->nodeTab[j])) {
392
+ rb_ary_push(namespace_cache, Nokogiri_wrap_xml_node_set_node(node_set->nodeTab[j], self));
393
+ }
394
+ }
395
+ }
396
+
397
+
411
398
  VALUE Nokogiri_wrap_xml_node_set(xmlNodeSetPtr node_set, VALUE document)
412
399
  {
413
400
  VALUE new_set ;
414
- int i;
415
- xmlNodePtr cur;
416
- xmlNsPtr ns;
417
- nokogiriNodeSetTuple *tuple;
418
401
 
419
- new_set = Data_Make_Struct(cNokogiriXmlNodeSet, nokogiriNodeSetTuple, 0,
420
- deallocate, tuple);
402
+ if (node_set == NULL) {
403
+ node_set = xmlXPathNodeSetCreate(NULL);
404
+ }
421
405
 
422
- tuple->node_set = node_set;
423
- tuple->namespaces = st_init_numtable();
406
+ new_set = Data_Wrap_Struct(cNokogiriXmlNodeSet, 0, deallocate, node_set);
424
407
 
425
408
  if (!NIL_P(document)) {
426
409
  rb_iv_set(new_set, "@document", document);
427
410
  rb_funcall(document, decorate, 1, new_set);
428
411
  }
429
412
 
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
- }
413
+ rb_iv_set(new_set, "@namespace_cache", rb_ary_new());
414
+ reify_node_set_namespaces(new_set);
440
415
 
441
416
  return new_set ;
442
417
  }
443
418
 
419
+ VALUE Nokogiri_wrap_xml_node_set_node(xmlNodePtr node, VALUE node_set)
420
+ {
421
+ xmlDocPtr document ;
422
+
423
+ if (Nokogiri_namespace_eh(node)) {
424
+ Data_Get_Struct(rb_iv_get(node_set, "@document"), xmlDoc, document);
425
+ return Nokogiri_wrap_xml_namespace(document, (xmlNsPtr)node);
426
+ } else {
427
+ return Nokogiri_wrap_xml_node(Qnil, node);
428
+ }
429
+ }
430
+
431
+
432
+ static void xpath_node_set_del(xmlNodeSetPtr cur, xmlNodePtr val)
433
+ {
434
+ /*
435
+ * as mentioned a few times above, we do not want to free xmlNs structs
436
+ * outside of the Namespace lifecycle.
437
+ *
438
+ * xmlXPathNodeSetDel() frees xmlNs structs, and so here we reproduce that
439
+ * function with the xmlNs logic.
440
+ */
441
+ int i;
442
+
443
+ if (cur == NULL) return;
444
+ if (val == NULL) return;
445
+
446
+ /*
447
+ * find node in nodeTab
448
+ */
449
+ for (i = 0;i < cur->nodeNr;i++)
450
+ if (cur->nodeTab[i] == val) break;
451
+
452
+ if (i >= cur->nodeNr) { /* not found */
453
+ return;
454
+ }
455
+ cur->nodeNr--;
456
+ for (;i < cur->nodeNr;i++)
457
+ cur->nodeTab[i] = cur->nodeTab[i + 1];
458
+ cur->nodeTab[cur->nodeNr] = NULL;
459
+ }
460
+
461
+
444
462
  VALUE cNokogiriXmlNodeSet ;
445
463
  void init_xml_node_set(void)
446
464
  {