Nokogiri_precompiled_aarch64_dedshit 1.14.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (263) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +44 -0
  3. data/LICENSE-DEPENDENCIES.md +2224 -0
  4. data/LICENSE.md +9 -0
  5. data/README.md +287 -0
  6. data/bin/nokogiri +131 -0
  7. data/dependencies.yml +41 -0
  8. data/ext/java/nokogiri/Html4Document.java +157 -0
  9. data/ext/java/nokogiri/Html4ElementDescription.java +133 -0
  10. data/ext/java/nokogiri/Html4EntityLookup.java +63 -0
  11. data/ext/java/nokogiri/Html4SaxParserContext.java +289 -0
  12. data/ext/java/nokogiri/Html4SaxPushParser.java +213 -0
  13. data/ext/java/nokogiri/NokogiriService.java +613 -0
  14. data/ext/java/nokogiri/XmlAttr.java +154 -0
  15. data/ext/java/nokogiri/XmlAttributeDecl.java +119 -0
  16. data/ext/java/nokogiri/XmlCdata.java +60 -0
  17. data/ext/java/nokogiri/XmlComment.java +77 -0
  18. data/ext/java/nokogiri/XmlDocument.java +705 -0
  19. data/ext/java/nokogiri/XmlDocumentFragment.java +163 -0
  20. data/ext/java/nokogiri/XmlDtd.java +516 -0
  21. data/ext/java/nokogiri/XmlElement.java +44 -0
  22. data/ext/java/nokogiri/XmlElementContent.java +412 -0
  23. data/ext/java/nokogiri/XmlElementDecl.java +148 -0
  24. data/ext/java/nokogiri/XmlEntityDecl.java +151 -0
  25. data/ext/java/nokogiri/XmlEntityReference.java +79 -0
  26. data/ext/java/nokogiri/XmlNamespace.java +193 -0
  27. data/ext/java/nokogiri/XmlNode.java +1938 -0
  28. data/ext/java/nokogiri/XmlNodeSet.java +463 -0
  29. data/ext/java/nokogiri/XmlProcessingInstruction.java +79 -0
  30. data/ext/java/nokogiri/XmlReader.java +615 -0
  31. data/ext/java/nokogiri/XmlRelaxng.java +133 -0
  32. data/ext/java/nokogiri/XmlSaxParserContext.java +329 -0
  33. data/ext/java/nokogiri/XmlSaxPushParser.java +288 -0
  34. data/ext/java/nokogiri/XmlSchema.java +423 -0
  35. data/ext/java/nokogiri/XmlSyntaxError.java +137 -0
  36. data/ext/java/nokogiri/XmlText.java +90 -0
  37. data/ext/java/nokogiri/XmlXpathContext.java +305 -0
  38. data/ext/java/nokogiri/XsltStylesheet.java +368 -0
  39. data/ext/java/nokogiri/internals/ClosedStreamException.java +13 -0
  40. data/ext/java/nokogiri/internals/HtmlDomParserContext.java +252 -0
  41. data/ext/java/nokogiri/internals/IgnoreSchemaErrorsErrorHandler.java +27 -0
  42. data/ext/java/nokogiri/internals/NokogiriBlockingQueueInputStream.java +178 -0
  43. data/ext/java/nokogiri/internals/NokogiriDomParser.java +99 -0
  44. data/ext/java/nokogiri/internals/NokogiriEntityResolver.java +140 -0
  45. data/ext/java/nokogiri/internals/NokogiriErrorHandler.java +65 -0
  46. data/ext/java/nokogiri/internals/NokogiriHandler.java +339 -0
  47. data/ext/java/nokogiri/internals/NokogiriHelpers.java +817 -0
  48. data/ext/java/nokogiri/internals/NokogiriNamespaceCache.java +228 -0
  49. data/ext/java/nokogiri/internals/NokogiriNamespaceContext.java +110 -0
  50. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler.java +86 -0
  51. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler4NekoHtml.java +107 -0
  52. data/ext/java/nokogiri/internals/NokogiriStrictErrorHandler.java +62 -0
  53. data/ext/java/nokogiri/internals/NokogiriXPathFunction.java +165 -0
  54. data/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java +50 -0
  55. data/ext/java/nokogiri/internals/NokogiriXPathVariableResolver.java +37 -0
  56. data/ext/java/nokogiri/internals/NokogiriXsltErrorListener.java +70 -0
  57. data/ext/java/nokogiri/internals/ParserContext.java +262 -0
  58. data/ext/java/nokogiri/internals/ReaderNode.java +564 -0
  59. data/ext/java/nokogiri/internals/SaveContextVisitor.java +865 -0
  60. data/ext/java/nokogiri/internals/SchemaErrorHandler.java +50 -0
  61. data/ext/java/nokogiri/internals/XalanDTMManagerPatch.java +174 -0
  62. data/ext/java/nokogiri/internals/XmlDeclHandler.java +11 -0
  63. data/ext/java/nokogiri/internals/XmlDomParserContext.java +265 -0
  64. data/ext/java/nokogiri/internals/XmlSaxParser.java +40 -0
  65. data/ext/java/nokogiri/internals/c14n/AttrCompare.java +122 -0
  66. data/ext/java/nokogiri/internals/c14n/C14nHelper.java +178 -0
  67. data/ext/java/nokogiri/internals/c14n/CanonicalFilter.java +43 -0
  68. data/ext/java/nokogiri/internals/c14n/CanonicalizationException.java +106 -0
  69. data/ext/java/nokogiri/internals/c14n/Canonicalizer.java +278 -0
  70. data/ext/java/nokogiri/internals/c14n/Canonicalizer11.java +664 -0
  71. data/ext/java/nokogiri/internals/c14n/Canonicalizer11_OmitComments.java +45 -0
  72. data/ext/java/nokogiri/internals/c14n/Canonicalizer11_WithComments.java +45 -0
  73. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315.java +388 -0
  74. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315Excl.java +308 -0
  75. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315ExclOmitComments.java +47 -0
  76. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315ExclWithComments.java +51 -0
  77. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315OmitComments.java +51 -0
  78. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315WithComments.java +50 -0
  79. data/ext/java/nokogiri/internals/c14n/CanonicalizerBase.java +660 -0
  80. data/ext/java/nokogiri/internals/c14n/CanonicalizerPhysical.java +194 -0
  81. data/ext/java/nokogiri/internals/c14n/CanonicalizerSpi.java +77 -0
  82. data/ext/java/nokogiri/internals/c14n/Constants.java +45 -0
  83. data/ext/java/nokogiri/internals/c14n/ElementProxy.java +325 -0
  84. data/ext/java/nokogiri/internals/c14n/HelperNodeList.java +106 -0
  85. data/ext/java/nokogiri/internals/c14n/IgnoreAllErrorHandler.java +86 -0
  86. data/ext/java/nokogiri/internals/c14n/InclusiveNamespaces.java +181 -0
  87. data/ext/java/nokogiri/internals/c14n/InvalidCanonicalizerException.java +87 -0
  88. data/ext/java/nokogiri/internals/c14n/NameSpaceSymbTable.java +452 -0
  89. data/ext/java/nokogiri/internals/c14n/NodeFilter.java +52 -0
  90. data/ext/java/nokogiri/internals/c14n/UtfHelpper.java +190 -0
  91. data/ext/java/nokogiri/internals/c14n/XMLUtils.java +540 -0
  92. data/ext/java/nokogiri/internals/dom2dtm/DOM2DTM.java +1712 -0
  93. data/ext/java/nokogiri/internals/dom2dtm/DOM2DTMdefaultNamespaceDeclarationNode.java +737 -0
  94. data/ext/nokogiri/depend +38 -0
  95. data/ext/nokogiri/extconf.rb +1086 -0
  96. data/ext/nokogiri/gumbo.c +594 -0
  97. data/ext/nokogiri/html4_document.c +167 -0
  98. data/ext/nokogiri/html4_element_description.c +294 -0
  99. data/ext/nokogiri/html4_entity_lookup.c +37 -0
  100. data/ext/nokogiri/html4_sax_parser_context.c +116 -0
  101. data/ext/nokogiri/html4_sax_push_parser.c +95 -0
  102. data/ext/nokogiri/libxml2_backwards_compat.c +121 -0
  103. data/ext/nokogiri/nokogiri.c +265 -0
  104. data/ext/nokogiri/nokogiri.h +235 -0
  105. data/ext/nokogiri/test_global_handlers.c +42 -0
  106. data/ext/nokogiri/xml_attr.c +103 -0
  107. data/ext/nokogiri/xml_attribute_decl.c +70 -0
  108. data/ext/nokogiri/xml_cdata.c +57 -0
  109. data/ext/nokogiri/xml_comment.c +62 -0
  110. data/ext/nokogiri/xml_document.c +689 -0
  111. data/ext/nokogiri/xml_document_fragment.c +44 -0
  112. data/ext/nokogiri/xml_dtd.c +210 -0
  113. data/ext/nokogiri/xml_element_content.c +128 -0
  114. data/ext/nokogiri/xml_element_decl.c +69 -0
  115. data/ext/nokogiri/xml_encoding_handler.c +104 -0
  116. data/ext/nokogiri/xml_entity_decl.c +112 -0
  117. data/ext/nokogiri/xml_entity_reference.c +50 -0
  118. data/ext/nokogiri/xml_namespace.c +186 -0
  119. data/ext/nokogiri/xml_node.c +2426 -0
  120. data/ext/nokogiri/xml_node_set.c +496 -0
  121. data/ext/nokogiri/xml_processing_instruction.c +54 -0
  122. data/ext/nokogiri/xml_reader.c +794 -0
  123. data/ext/nokogiri/xml_relax_ng.c +164 -0
  124. data/ext/nokogiri/xml_sax_parser.c +316 -0
  125. data/ext/nokogiri/xml_sax_parser_context.c +283 -0
  126. data/ext/nokogiri/xml_sax_push_parser.c +166 -0
  127. data/ext/nokogiri/xml_schema.c +260 -0
  128. data/ext/nokogiri/xml_syntax_error.c +85 -0
  129. data/ext/nokogiri/xml_text.c +48 -0
  130. data/ext/nokogiri/xml_xpath_context.c +415 -0
  131. data/ext/nokogiri/xslt_stylesheet.c +363 -0
  132. data/gumbo-parser/CHANGES.md +63 -0
  133. data/gumbo-parser/Makefile +111 -0
  134. data/gumbo-parser/THANKS +27 -0
  135. data/gumbo-parser/src/Makefile +34 -0
  136. data/gumbo-parser/src/README.md +41 -0
  137. data/gumbo-parser/src/ascii.c +75 -0
  138. data/gumbo-parser/src/ascii.h +115 -0
  139. data/gumbo-parser/src/attribute.c +42 -0
  140. data/gumbo-parser/src/attribute.h +17 -0
  141. data/gumbo-parser/src/char_ref.c +22225 -0
  142. data/gumbo-parser/src/char_ref.h +29 -0
  143. data/gumbo-parser/src/char_ref.rl +2154 -0
  144. data/gumbo-parser/src/error.c +626 -0
  145. data/gumbo-parser/src/error.h +148 -0
  146. data/gumbo-parser/src/foreign_attrs.c +104 -0
  147. data/gumbo-parser/src/foreign_attrs.gperf +27 -0
  148. data/gumbo-parser/src/insertion_mode.h +33 -0
  149. data/gumbo-parser/src/macros.h +91 -0
  150. data/gumbo-parser/src/nokogiri_gumbo.h +944 -0
  151. data/gumbo-parser/src/parser.c +4878 -0
  152. data/gumbo-parser/src/parser.h +41 -0
  153. data/gumbo-parser/src/replacement.h +33 -0
  154. data/gumbo-parser/src/string_buffer.c +103 -0
  155. data/gumbo-parser/src/string_buffer.h +68 -0
  156. data/gumbo-parser/src/string_piece.c +48 -0
  157. data/gumbo-parser/src/svg_attrs.c +174 -0
  158. data/gumbo-parser/src/svg_attrs.gperf +77 -0
  159. data/gumbo-parser/src/svg_tags.c +137 -0
  160. data/gumbo-parser/src/svg_tags.gperf +55 -0
  161. data/gumbo-parser/src/tag.c +223 -0
  162. data/gumbo-parser/src/tag_lookup.c +382 -0
  163. data/gumbo-parser/src/tag_lookup.gperf +170 -0
  164. data/gumbo-parser/src/tag_lookup.h +13 -0
  165. data/gumbo-parser/src/token_buffer.c +79 -0
  166. data/gumbo-parser/src/token_buffer.h +71 -0
  167. data/gumbo-parser/src/token_type.h +17 -0
  168. data/gumbo-parser/src/tokenizer.c +3463 -0
  169. data/gumbo-parser/src/tokenizer.h +112 -0
  170. data/gumbo-parser/src/tokenizer_states.h +339 -0
  171. data/gumbo-parser/src/utf8.c +245 -0
  172. data/gumbo-parser/src/utf8.h +164 -0
  173. data/gumbo-parser/src/util.c +66 -0
  174. data/gumbo-parser/src/util.h +34 -0
  175. data/gumbo-parser/src/vector.c +111 -0
  176. data/gumbo-parser/src/vector.h +45 -0
  177. data/lib/nokogiri/class_resolver.rb +67 -0
  178. data/lib/nokogiri/css/node.rb +54 -0
  179. data/lib/nokogiri/css/parser.rb +770 -0
  180. data/lib/nokogiri/css/parser.y +277 -0
  181. data/lib/nokogiri/css/parser_extras.rb +96 -0
  182. data/lib/nokogiri/css/syntax_error.rb +9 -0
  183. data/lib/nokogiri/css/tokenizer.rb +155 -0
  184. data/lib/nokogiri/css/tokenizer.rex +56 -0
  185. data/lib/nokogiri/css/xpath_visitor.rb +359 -0
  186. data/lib/nokogiri/css.rb +66 -0
  187. data/lib/nokogiri/decorators/slop.rb +44 -0
  188. data/lib/nokogiri/encoding_handler.rb +57 -0
  189. data/lib/nokogiri/extension.rb +32 -0
  190. data/lib/nokogiri/gumbo.rb +15 -0
  191. data/lib/nokogiri/html.rb +48 -0
  192. data/lib/nokogiri/html4/builder.rb +37 -0
  193. data/lib/nokogiri/html4/document.rb +214 -0
  194. data/lib/nokogiri/html4/document_fragment.rb +54 -0
  195. data/lib/nokogiri/html4/element_description.rb +25 -0
  196. data/lib/nokogiri/html4/element_description_defaults.rb +572 -0
  197. data/lib/nokogiri/html4/encoding_reader.rb +121 -0
  198. data/lib/nokogiri/html4/entity_lookup.rb +15 -0
  199. data/lib/nokogiri/html4/sax/parser.rb +63 -0
  200. data/lib/nokogiri/html4/sax/parser_context.rb +20 -0
  201. data/lib/nokogiri/html4/sax/push_parser.rb +37 -0
  202. data/lib/nokogiri/html4.rb +47 -0
  203. data/lib/nokogiri/html5/document.rb +168 -0
  204. data/lib/nokogiri/html5/document_fragment.rb +90 -0
  205. data/lib/nokogiri/html5/node.rb +98 -0
  206. data/lib/nokogiri/html5.rb +389 -0
  207. data/lib/nokogiri/jruby/dependencies.rb +3 -0
  208. data/lib/nokogiri/jruby/isorelax/isorelax/20030108/isorelax-20030108.jar +0 -0
  209. data/lib/nokogiri/jruby/net/sf/saxon/Saxon-HE/9.6.0-4/Saxon-HE-9.6.0-4.jar +0 -0
  210. data/lib/nokogiri/jruby/net/sourceforge/htmlunit/neko-htmlunit/2.63.0/neko-htmlunit-2.63.0.jar +0 -0
  211. data/lib/nokogiri/jruby/nokogiri_jars.rb +43 -0
  212. data/lib/nokogiri/jruby/nu/validator/jing/20200702VNU/jing-20200702VNU.jar +0 -0
  213. data/lib/nokogiri/jruby/org/nokogiri/nekodtd/0.1.11.noko2/nekodtd-0.1.11.noko2.jar +0 -0
  214. data/lib/nokogiri/jruby/xalan/serializer/2.7.3/serializer-2.7.3.jar +0 -0
  215. data/lib/nokogiri/jruby/xalan/xalan/2.7.3/xalan-2.7.3.jar +0 -0
  216. data/lib/nokogiri/jruby/xerces/xercesImpl/2.12.2/xercesImpl-2.12.2.jar +0 -0
  217. data/lib/nokogiri/jruby/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar +0 -0
  218. data/lib/nokogiri/syntax_error.rb +6 -0
  219. data/lib/nokogiri/version/constant.rb +6 -0
  220. data/lib/nokogiri/version/info.rb +223 -0
  221. data/lib/nokogiri/version.rb +4 -0
  222. data/lib/nokogiri/xml/attr.rb +66 -0
  223. data/lib/nokogiri/xml/attribute_decl.rb +20 -0
  224. data/lib/nokogiri/xml/builder.rb +487 -0
  225. data/lib/nokogiri/xml/cdata.rb +13 -0
  226. data/lib/nokogiri/xml/character_data.rb +9 -0
  227. data/lib/nokogiri/xml/document.rb +471 -0
  228. data/lib/nokogiri/xml/document_fragment.rb +205 -0
  229. data/lib/nokogiri/xml/dtd.rb +34 -0
  230. data/lib/nokogiri/xml/element_content.rb +38 -0
  231. data/lib/nokogiri/xml/element_decl.rb +15 -0
  232. data/lib/nokogiri/xml/entity_decl.rb +21 -0
  233. data/lib/nokogiri/xml/entity_reference.rb +20 -0
  234. data/lib/nokogiri/xml/namespace.rb +58 -0
  235. data/lib/nokogiri/xml/node/save_options.rb +68 -0
  236. data/lib/nokogiri/xml/node.rb +1563 -0
  237. data/lib/nokogiri/xml/node_set.rb +447 -0
  238. data/lib/nokogiri/xml/notation.rb +19 -0
  239. data/lib/nokogiri/xml/parse_options.rb +213 -0
  240. data/lib/nokogiri/xml/pp/character_data.rb +21 -0
  241. data/lib/nokogiri/xml/pp/node.rb +57 -0
  242. data/lib/nokogiri/xml/pp.rb +4 -0
  243. data/lib/nokogiri/xml/processing_instruction.rb +11 -0
  244. data/lib/nokogiri/xml/reader.rb +105 -0
  245. data/lib/nokogiri/xml/relax_ng.rb +38 -0
  246. data/lib/nokogiri/xml/sax/document.rb +167 -0
  247. data/lib/nokogiri/xml/sax/parser.rb +125 -0
  248. data/lib/nokogiri/xml/sax/parser_context.rb +21 -0
  249. data/lib/nokogiri/xml/sax/push_parser.rb +61 -0
  250. data/lib/nokogiri/xml/sax.rb +6 -0
  251. data/lib/nokogiri/xml/schema.rb +73 -0
  252. data/lib/nokogiri/xml/searchable.rb +270 -0
  253. data/lib/nokogiri/xml/syntax_error.rb +72 -0
  254. data/lib/nokogiri/xml/text.rb +11 -0
  255. data/lib/nokogiri/xml/xpath/syntax_error.rb +13 -0
  256. data/lib/nokogiri/xml/xpath.rb +21 -0
  257. data/lib/nokogiri/xml/xpath_context.rb +16 -0
  258. data/lib/nokogiri/xml.rb +76 -0
  259. data/lib/nokogiri/xslt/stylesheet.rb +27 -0
  260. data/lib/nokogiri/xslt.rb +65 -0
  261. data/lib/nokogiri.rb +120 -0
  262. data/lib/xsd/xmlparser/nokogiri.rb +106 -0
  263. metadata +391 -0
@@ -0,0 +1,2426 @@
1
+ #include <nokogiri.h>
2
+
3
+ #include <stdbool.h>
4
+
5
+ // :stopdoc:
6
+ void xmlSetStructuredErrorFunc(void *qqg, void *shs);
7
+
8
+ VALUE cNokogiriXmlNode ;
9
+ static ID id_decorate, id_decorate_bang;
10
+
11
+ typedef xmlNodePtr(*pivot_reparentee_func)(xmlNodePtr, xmlNodePtr);
12
+
13
+ static void
14
+ _xml_node_mark(void *ptr)
15
+ {
16
+ xmlNodePtr node = ptr;
17
+
18
+ if (!DOC_RUBY_OBJECT_TEST(node->doc)) {
19
+ return;
20
+ }
21
+
22
+ xmlDocPtr doc = node->doc;
23
+ if (doc->type == XML_DOCUMENT_NODE || doc->type == XML_HTML_DOCUMENT_NODE) {
24
+ if (DOC_RUBY_OBJECT_TEST(doc)) {
25
+ rb_gc_mark(DOC_RUBY_OBJECT(doc));
26
+ }
27
+ } else if (node->doc->_private) {
28
+ rb_gc_mark((VALUE)doc->_private);
29
+ }
30
+ }
31
+
32
+ #ifdef HAVE_RB_GC_LOCATION
33
+ static void
34
+ _xml_node_update_references(void *ptr)
35
+ {
36
+ xmlNodePtr node = ptr;
37
+
38
+ if (node->_private) {
39
+ node->_private = (void *)rb_gc_location((VALUE)node->_private);
40
+ }
41
+ }
42
+ #else
43
+ # define _xml_node_update_references 0
44
+ #endif
45
+
46
+ static const rb_data_type_t nokogiri_node_type = {
47
+ "Nokogiri/XMLNode",
48
+ {_xml_node_mark, 0, 0, _xml_node_update_references},
49
+ 0, 0,
50
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
51
+ RUBY_TYPED_FREE_IMMEDIATELY,
52
+ #endif
53
+ };
54
+
55
+ static void
56
+ relink_namespace(xmlNodePtr reparented)
57
+ {
58
+ xmlNodePtr child;
59
+ xmlAttrPtr attr;
60
+
61
+ if (reparented->type != XML_ATTRIBUTE_NODE &&
62
+ reparented->type != XML_ELEMENT_NODE) { return; }
63
+
64
+ if (reparented->ns == NULL || reparented->ns->prefix == NULL) {
65
+ xmlNsPtr ns = NULL;
66
+ xmlChar *name = NULL, *prefix = NULL;
67
+
68
+ name = xmlSplitQName2(reparented->name, &prefix);
69
+
70
+ if (reparented->type == XML_ATTRIBUTE_NODE) {
71
+ if (prefix == NULL || strcmp((char *)prefix, XMLNS_PREFIX) == 0) {
72
+ xmlFree(name);
73
+ xmlFree(prefix);
74
+ return;
75
+ }
76
+ }
77
+
78
+ ns = xmlSearchNs(reparented->doc, reparented, prefix);
79
+
80
+ if (ns != NULL) {
81
+ xmlNodeSetName(reparented, name);
82
+ xmlSetNs(reparented, ns);
83
+ }
84
+
85
+ xmlFree(name);
86
+ xmlFree(prefix);
87
+ }
88
+
89
+ /* Avoid segv when relinking against unlinked nodes. */
90
+ if (reparented->type != XML_ELEMENT_NODE || !reparented->parent) { return; }
91
+
92
+ /* Make sure that our reparented node has the correct namespaces */
93
+ if (!reparented->ns &&
94
+ (reparented->doc != (xmlDocPtr)reparented->parent) &&
95
+ (rb_iv_get(DOC_RUBY_OBJECT(reparented->doc), "@namespace_inheritance") == Qtrue)) {
96
+ xmlSetNs(reparented, reparented->parent->ns);
97
+ }
98
+
99
+ /* Search our parents for an existing definition */
100
+ if (reparented->nsDef) {
101
+ xmlNsPtr curr = reparented->nsDef;
102
+ xmlNsPtr prev = NULL;
103
+
104
+ while (curr) {
105
+ xmlNsPtr ns = xmlSearchNsByHref(
106
+ reparented->doc,
107
+ reparented->parent,
108
+ curr->href
109
+ );
110
+ /* If we find the namespace is already declared, remove it from this
111
+ * definition list. */
112
+ if (ns && ns != curr && xmlStrEqual(ns->prefix, curr->prefix)) {
113
+ if (prev) {
114
+ prev->next = curr->next;
115
+ } else {
116
+ reparented->nsDef = curr->next;
117
+ }
118
+ noko_xml_document_pin_namespace(curr, reparented->doc);
119
+ } else {
120
+ prev = curr;
121
+ }
122
+ curr = curr->next;
123
+ }
124
+ }
125
+
126
+ /*
127
+ * Search our parents for an existing definition of current namespace,
128
+ * because the definition it's pointing to may have just been removed nsDef.
129
+ *
130
+ * And although that would technically probably be OK, I'd feel better if we
131
+ * referred to a namespace that's still present in a node's nsDef somewhere
132
+ * in the doc.
133
+ */
134
+ if (reparented->ns) {
135
+ xmlNsPtr ns = xmlSearchNs(reparented->doc, reparented, reparented->ns->prefix);
136
+ if (ns
137
+ && ns != reparented->ns
138
+ && xmlStrEqual(ns->prefix, reparented->ns->prefix)
139
+ && xmlStrEqual(ns->href, reparented->ns->href)
140
+ ) {
141
+ xmlSetNs(reparented, ns);
142
+ }
143
+ }
144
+
145
+ /* Only walk all children if there actually is a namespace we need to */
146
+ /* reparent. */
147
+ if (NULL == reparented->ns) { return; }
148
+
149
+ /* When a node gets reparented, walk it's children to make sure that */
150
+ /* their namespaces are reparented as well. */
151
+ child = reparented->children;
152
+ while (NULL != child) {
153
+ relink_namespace(child);
154
+ child = child->next;
155
+ }
156
+
157
+ if (reparented->type == XML_ELEMENT_NODE) {
158
+ attr = reparented->properties;
159
+ while (NULL != attr) {
160
+ relink_namespace((xmlNodePtr)attr);
161
+ attr = attr->next;
162
+ }
163
+ }
164
+ }
165
+
166
+
167
+ /* internal function meant to wrap xmlReplaceNode
168
+ and fix some issues we have with libxml2 merging nodes */
169
+ static xmlNodePtr
170
+ xmlReplaceNodeWrapper(xmlNodePtr pivot, xmlNodePtr new_node)
171
+ {
172
+ xmlNodePtr retval ;
173
+
174
+ retval = xmlReplaceNode(pivot, new_node) ;
175
+
176
+ if (retval == pivot) {
177
+ retval = new_node ; /* return semantics for reparent_node_with */
178
+ }
179
+
180
+ /* work around libxml2 issue: https://bugzilla.gnome.org/show_bug.cgi?id=615612 */
181
+ if (retval && retval->type == XML_TEXT_NODE) {
182
+ if (retval->prev && retval->prev->type == XML_TEXT_NODE) {
183
+ retval = xmlTextMerge(retval->prev, retval);
184
+ }
185
+ if (retval->next && retval->next->type == XML_TEXT_NODE) {
186
+ retval = xmlTextMerge(retval, retval->next);
187
+ }
188
+ }
189
+
190
+ return retval ;
191
+ }
192
+
193
+
194
+ static void
195
+ raise_if_ancestor_of_self(xmlNodePtr self)
196
+ {
197
+ for (xmlNodePtr ancestor = self->parent ; ancestor ; ancestor = ancestor->parent) {
198
+ if (self == ancestor) {
199
+ rb_raise(rb_eRuntimeError, "cycle detected: node '%s' is an ancestor of itself", self->name);
200
+ }
201
+ }
202
+ }
203
+
204
+
205
+ static VALUE
206
+ reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_reparentee_func prf)
207
+ {
208
+ VALUE reparented_obj ;
209
+ xmlNodePtr reparentee, original_reparentee, pivot, reparented, next_text, new_next_text, parent ;
210
+ int original_ns_prefix_is_default = 0 ;
211
+
212
+ if (!rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlNode)) {
213
+ rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
214
+ }
215
+ if (rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlDocument)) {
216
+ rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
217
+ }
218
+
219
+ Noko_Node_Get_Struct(reparentee_obj, xmlNode, reparentee);
220
+ Noko_Node_Get_Struct(pivot_obj, xmlNode, pivot);
221
+
222
+ /*
223
+ * Check if nodes given are appropriate to have a parent-child
224
+ * relationship, based on the DOM specification.
225
+ *
226
+ * cf. http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-1590626202
227
+ */
228
+ if (prf == xmlAddChild) {
229
+ parent = pivot;
230
+ } else {
231
+ parent = pivot->parent;
232
+ }
233
+
234
+ if (parent) {
235
+ switch (parent->type) {
236
+ case XML_DOCUMENT_NODE:
237
+ case XML_HTML_DOCUMENT_NODE:
238
+ switch (reparentee->type) {
239
+ case XML_ELEMENT_NODE:
240
+ case XML_PI_NODE:
241
+ case XML_COMMENT_NODE:
242
+ case XML_DOCUMENT_TYPE_NODE:
243
+ /*
244
+ * The DOM specification says no to adding text-like nodes
245
+ * directly to a document, but we allow it for compatibility.
246
+ */
247
+ case XML_TEXT_NODE:
248
+ case XML_CDATA_SECTION_NODE:
249
+ case XML_ENTITY_REF_NODE:
250
+ goto ok;
251
+ default:
252
+ break;
253
+ }
254
+ break;
255
+ case XML_DOCUMENT_FRAG_NODE:
256
+ case XML_ENTITY_REF_NODE:
257
+ case XML_ELEMENT_NODE:
258
+ switch (reparentee->type) {
259
+ case XML_ELEMENT_NODE:
260
+ case XML_PI_NODE:
261
+ case XML_COMMENT_NODE:
262
+ case XML_TEXT_NODE:
263
+ case XML_CDATA_SECTION_NODE:
264
+ case XML_ENTITY_REF_NODE:
265
+ goto ok;
266
+ default:
267
+ break;
268
+ }
269
+ break;
270
+ case XML_ATTRIBUTE_NODE:
271
+ switch (reparentee->type) {
272
+ case XML_TEXT_NODE:
273
+ case XML_ENTITY_REF_NODE:
274
+ goto ok;
275
+ default:
276
+ break;
277
+ }
278
+ break;
279
+ case XML_TEXT_NODE:
280
+ /*
281
+ * xmlAddChild() breaks the DOM specification in that it allows
282
+ * adding a text node to another, in which case text nodes are
283
+ * coalesced, but since our JRuby version does not support such
284
+ * operation, we should inhibit it.
285
+ */
286
+ break;
287
+ default:
288
+ break;
289
+ }
290
+
291
+ rb_raise(rb_eArgError, "cannot reparent %s there", rb_obj_classname(reparentee_obj));
292
+ }
293
+
294
+ ok:
295
+ original_reparentee = reparentee;
296
+
297
+ if (reparentee->doc != pivot->doc || reparentee->type == XML_TEXT_NODE) {
298
+ /*
299
+ * if the reparentee is a text node, there's a very good chance it will be
300
+ * merged with an adjacent text node after being reparented, and in that case
301
+ * libxml will free the underlying C struct.
302
+ *
303
+ * since we clearly have a ruby object which references the underlying
304
+ * memory, we can't let the C struct get freed. let's pickle the original
305
+ * reparentee by rooting it; and then we'll reparent a duplicate of the
306
+ * node that we don't care about preserving.
307
+ *
308
+ * alternatively, if the reparentee is from a different document than the
309
+ * pivot node, libxml2 is going to get confused about which document's
310
+ * "dictionary" the node's strings belong to (this is an otherwise
311
+ * uninteresting libxml2 implementation detail). as a result, we cannot
312
+ * reparent the actual reparentee, so we reparent a duplicate.
313
+ */
314
+ if (reparentee->type == XML_TEXT_NODE && reparentee->_private) {
315
+ /*
316
+ * additionally, since we know this C struct isn't going to be related to
317
+ * a Ruby object anymore, let's break the relationship on this end as
318
+ * well.
319
+ *
320
+ * this is not absolutely necessary unless libxml-ruby is also in effect,
321
+ * in which case its global callback `rxml_node_deregisterNode` will try
322
+ * to do things to our data.
323
+ *
324
+ * for more details on this particular (and particularly nasty) edge
325
+ * case, see:
326
+ *
327
+ * https://github.com/sparklemotion/nokogiri/issues/1426
328
+ */
329
+ reparentee->_private = NULL ;
330
+ }
331
+
332
+ if (reparentee->ns != NULL && reparentee->ns->prefix == NULL) {
333
+ original_ns_prefix_is_default = 1;
334
+ }
335
+
336
+ noko_xml_document_pin_node(reparentee);
337
+
338
+ if (!(reparentee = xmlDocCopyNode(reparentee, pivot->doc, 1))) {
339
+ rb_raise(rb_eRuntimeError, "Could not reparent node (xmlDocCopyNode)");
340
+ }
341
+
342
+ if (original_ns_prefix_is_default && reparentee->ns != NULL && reparentee->ns->prefix != NULL) {
343
+ /*
344
+ * issue #391, where new node's prefix may become the string "default"
345
+ * see libxml2 tree.c xmlNewReconciliedNs which implements this behavior.
346
+ */
347
+ xmlFree(DISCARD_CONST_QUAL_XMLCHAR(reparentee->ns->prefix));
348
+ reparentee->ns->prefix = NULL;
349
+ }
350
+ }
351
+
352
+ xmlUnlinkNode(original_reparentee);
353
+
354
+ if (prf != xmlAddPrevSibling && prf != xmlAddNextSibling
355
+ && reparentee->type == XML_TEXT_NODE && pivot->next && pivot->next->type == XML_TEXT_NODE) {
356
+ /*
357
+ * libxml merges text nodes in a right-to-left fashion, meaning that if
358
+ * there are two text nodes who would be adjacent, the right (or following,
359
+ * or next) node will be merged into the left (or preceding, or previous)
360
+ * node.
361
+ *
362
+ * and by "merged" I mean the string contents will be concatenated onto the
363
+ * left node's contents, and then the node will be freed.
364
+ *
365
+ * which means that if we have a ruby object wrapped around the right node,
366
+ * its memory would be freed out from under it.
367
+ *
368
+ * so, we detect this edge case and unlink-and-root the text node before it gets
369
+ * merged. then we dup the node and insert that duplicate back into the
370
+ * document where the real node was.
371
+ *
372
+ * yes, this is totally lame.
373
+ */
374
+ next_text = pivot->next ;
375
+ new_next_text = xmlDocCopyNode(next_text, pivot->doc, 1) ;
376
+
377
+ xmlUnlinkNode(next_text);
378
+ noko_xml_document_pin_node(next_text);
379
+
380
+ xmlAddNextSibling(pivot, new_next_text);
381
+ }
382
+
383
+ if (!(reparented = (*prf)(pivot, reparentee))) {
384
+ rb_raise(rb_eRuntimeError, "Could not reparent node");
385
+ }
386
+
387
+ /*
388
+ * make sure the ruby object is pointed at the just-reparented node, which
389
+ * might be a duplicate (see above) or might be the result of merging
390
+ * adjacent text nodes.
391
+ */
392
+ DATA_PTR(reparentee_obj) = reparented ;
393
+ reparented_obj = noko_xml_node_wrap(Qnil, reparented);
394
+
395
+ rb_funcall(reparented_obj, id_decorate_bang, 0);
396
+
397
+ /* if we've created a cycle, raise an exception */
398
+ raise_if_ancestor_of_self(reparented);
399
+
400
+ relink_namespace(reparented);
401
+
402
+ return reparented_obj ;
403
+ }
404
+
405
+ // :startdoc:
406
+
407
+ /*
408
+ * :call-seq:
409
+ * add_namespace_definition(prefix, href) → Nokogiri::XML::Namespace
410
+ * add_namespace(prefix, href) → Nokogiri::XML::Namespace
411
+ *
412
+ * :category: Manipulating Document Structure
413
+ *
414
+ * Adds a namespace definition to this node with +prefix+ using +href+ value, as if this node had
415
+ * included an attribute "xmlns:prefix=href".
416
+ *
417
+ * A default namespace definition for this node can be added by passing +nil+ for +prefix+.
418
+ *
419
+ * [Parameters]
420
+ * - +prefix+ (String, +nil+) An {XML Name}[https://www.w3.org/TR/xml-names/#ns-decl]
421
+ * - +href+ (String) The {URI reference}[https://www.w3.org/TR/xml-names/#sec-namespaces]
422
+ *
423
+ * [Returns] The new Nokogiri::XML::Namespace
424
+ *
425
+ * *Example:* adding a non-default namespace definition
426
+ *
427
+ * doc = Nokogiri::XML("<store><inventory></inventory></store>")
428
+ * inventory = doc.at_css("inventory")
429
+ * inventory.add_namespace_definition("automobile", "http://alices-autos.com/")
430
+ * inventory.add_namespace_definition("bicycle", "http://bobs-bikes.com/")
431
+ * inventory.add_child("<automobile:tire>Michelin model XGV, size 75R</automobile:tire>")
432
+ * doc.to_xml
433
+ * # => "<?xml version=\"1.0\"?>\n" +
434
+ * # "<store>\n" +
435
+ * # " <inventory xmlns:automobile=\"http://alices-autos.com/\" xmlns:bicycle=\"http://bobs-bikes.com/\">\n" +
436
+ * # " <automobile:tire>Michelin model XGV, size 75R</automobile:tire>\n" +
437
+ * # " </inventory>\n" +
438
+ * # "</store>\n"
439
+ *
440
+ * *Example:* adding a default namespace definition
441
+ *
442
+ * doc = Nokogiri::XML("<store><inventory><tire>Michelin model XGV, size 75R</tire></inventory></store>")
443
+ * doc.at_css("tire").add_namespace_definition(nil, "http://bobs-bikes.com/")
444
+ * doc.to_xml
445
+ * # => "<?xml version=\"1.0\"?>\n" +
446
+ * # "<store>\n" +
447
+ * # " <inventory>\n" +
448
+ * # " <tire xmlns=\"http://bobs-bikes.com/\">Michelin model XGV, size 75R</tire>\n" +
449
+ * # " </inventory>\n" +
450
+ * # "</store>\n"
451
+ *
452
+ */
453
+ static VALUE
454
+ rb_xml_node_add_namespace_definition(VALUE rb_node, VALUE rb_prefix, VALUE rb_href)
455
+ {
456
+ xmlNodePtr c_node, element;
457
+ xmlNsPtr c_namespace;
458
+ const xmlChar *c_prefix = (const xmlChar *)(NIL_P(rb_prefix) ? NULL : StringValueCStr(rb_prefix));
459
+
460
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
461
+ element = c_node ;
462
+
463
+ c_namespace = xmlSearchNs(c_node->doc, c_node, c_prefix);
464
+
465
+ if (!c_namespace) {
466
+ if (c_node->type != XML_ELEMENT_NODE) {
467
+ element = c_node->parent;
468
+ }
469
+ c_namespace = xmlNewNs(element, (const xmlChar *)StringValueCStr(rb_href), c_prefix);
470
+ }
471
+
472
+ if (!c_namespace) {
473
+ return Qnil ;
474
+ }
475
+
476
+ if (NIL_P(rb_prefix) || c_node != element) {
477
+ xmlSetNs(c_node, c_namespace);
478
+ }
479
+
480
+ return noko_xml_namespace_wrap(c_namespace, c_node->doc);
481
+ }
482
+
483
+
484
+ /*
485
+ * :call-seq: attribute(name) → Nokogiri::XML::Attr
486
+ *
487
+ * :category: Working With Node Attributes
488
+ *
489
+ * [Returns] Attribute (Nokogiri::XML::Attr) belonging to this node with name +name+.
490
+ *
491
+ * ⚠ Note that attribute namespaces are ignored and only the simple (non-namespace-prefixed) name is
492
+ * used to find a matching attribute. In case of a simple name collision, only one of the matching
493
+ * attributes will be returned. In this case, you will need to use #attribute_with_ns.
494
+ *
495
+ * *Example:*
496
+ *
497
+ * doc = Nokogiri::XML("<root><child size='large' class='big wide tall'/></root>")
498
+ * child = doc.at_css("child")
499
+ * child.attribute("size") # => #<Nokogiri::XML::Attr:0x550 name="size" value="large">
500
+ * child.attribute("class") # => #<Nokogiri::XML::Attr:0x564 name="class" value="big wide tall">
501
+ *
502
+ * *Example* showing that namespaced attributes will not be returned:
503
+ *
504
+ * ⚠ Note that only one of the two matching attributes is returned.
505
+ *
506
+ * doc = Nokogiri::XML(<<~EOF)
507
+ * <root xmlns:width='http://example.com/widths'
508
+ * xmlns:height='http://example.com/heights'>
509
+ * <child width:size='broad' height:size='tall'/>
510
+ * </root>
511
+ * EOF
512
+ * doc.at_css("child").attribute("size")
513
+ * # => #(Attr:0x550 {
514
+ * # name = "size",
515
+ * # namespace = #(Namespace:0x564 {
516
+ * # prefix = "width",
517
+ * # href = "http://example.com/widths"
518
+ * # }),
519
+ * # value = "broad"
520
+ * # })
521
+ */
522
+ static VALUE
523
+ rb_xml_node_attribute(VALUE self, VALUE name)
524
+ {
525
+ xmlNodePtr node;
526
+ xmlAttrPtr prop;
527
+ Noko_Node_Get_Struct(self, xmlNode, node);
528
+ prop = xmlHasProp(node, (xmlChar *)StringValueCStr(name));
529
+
530
+ if (! prop) { return Qnil; }
531
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)prop);
532
+ }
533
+
534
+
535
+ /*
536
+ * :call-seq: attribute_nodes() → Array<Nokogiri::XML::Attr>
537
+ *
538
+ * :category: Working With Node Attributes
539
+ *
540
+ * [Returns] Attributes (an Array of Nokogiri::XML::Attr) belonging to this node.
541
+ *
542
+ * Note that this is the preferred alternative to #attributes when the simple
543
+ * (non-namespace-prefixed) attribute names may collide.
544
+ *
545
+ * *Example:*
546
+ *
547
+ * Contrast this with the colliding-name example from #attributes.
548
+ *
549
+ * doc = Nokogiri::XML(<<~EOF)
550
+ * <root xmlns:width='http://example.com/widths'
551
+ * xmlns:height='http://example.com/heights'>
552
+ * <child width:size='broad' height:size='tall'/>
553
+ * </root>
554
+ * EOF
555
+ * doc.at_css("child").attribute_nodes
556
+ * # => [#(Attr:0x550 {
557
+ * # name = "size",
558
+ * # namespace = #(Namespace:0x564 {
559
+ * # prefix = "width",
560
+ * # href = "http://example.com/widths"
561
+ * # }),
562
+ * # value = "broad"
563
+ * # }),
564
+ * # #(Attr:0x578 {
565
+ * # name = "size",
566
+ * # namespace = #(Namespace:0x58c {
567
+ * # prefix = "height",
568
+ * # href = "http://example.com/heights"
569
+ * # }),
570
+ * # value = "tall"
571
+ * # })]
572
+ */
573
+ static VALUE
574
+ rb_xml_node_attribute_nodes(VALUE rb_node)
575
+ {
576
+ xmlNodePtr c_node;
577
+
578
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
579
+
580
+ return noko_xml_node_attrs(c_node);
581
+ }
582
+
583
+
584
+ /*
585
+ * :call-seq: attribute_with_ns(name, namespace) → Nokogiri::XML::Attr
586
+ *
587
+ * :category: Working With Node Attributes
588
+ *
589
+ * [Returns]
590
+ * Attribute (Nokogiri::XML::Attr) belonging to this node with matching +name+ and +namespace+.
591
+ *
592
+ * [Parameters]
593
+ * - +name+ (String): the simple (non-namespace-prefixed) name of the attribute
594
+ * - +namespace+ (String): the URI of the attribute's namespace
595
+ *
596
+ * See related: #attribute
597
+ *
598
+ * *Example:*
599
+ *
600
+ * doc = Nokogiri::XML(<<~EOF)
601
+ * <root xmlns:width='http://example.com/widths'
602
+ * xmlns:height='http://example.com/heights'>
603
+ * <child width:size='broad' height:size='tall'/>
604
+ * </root>
605
+ * EOF
606
+ * doc.at_css("child").attribute_with_ns("size", "http://example.com/widths")
607
+ * # => #(Attr:0x550 {
608
+ * # name = "size",
609
+ * # namespace = #(Namespace:0x564 {
610
+ * # prefix = "width",
611
+ * # href = "http://example.com/widths"
612
+ * # }),
613
+ * # value = "broad"
614
+ * # })
615
+ * doc.at_css("child").attribute_with_ns("size", "http://example.com/heights")
616
+ * # => #(Attr:0x578 {
617
+ * # name = "size",
618
+ * # namespace = #(Namespace:0x58c {
619
+ * # prefix = "height",
620
+ * # href = "http://example.com/heights"
621
+ * # }),
622
+ * # value = "tall"
623
+ * # })
624
+ */
625
+ static VALUE
626
+ rb_xml_node_attribute_with_ns(VALUE self, VALUE name, VALUE namespace)
627
+ {
628
+ xmlNodePtr node;
629
+ xmlAttrPtr prop;
630
+ Noko_Node_Get_Struct(self, xmlNode, node);
631
+ prop = xmlHasNsProp(node, (xmlChar *)StringValueCStr(name),
632
+ NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace));
633
+
634
+ if (! prop) { return Qnil; }
635
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)prop);
636
+ }
637
+
638
+
639
+
640
+ /*
641
+ * call-seq: blank? → Boolean
642
+ *
643
+ * [Returns] +true+ if the node is an empty or whitespace-only text or cdata node, else +false+.
644
+ *
645
+ * *Example:*
646
+ *
647
+ * Nokogiri("<root><child/></root>").root.child.blank? # => false
648
+ * Nokogiri("<root>\t \n</root>").root.child.blank? # => true
649
+ * Nokogiri("<root><![CDATA[\t \n]]></root>").root.child.blank? # => true
650
+ * Nokogiri("<root>not-blank</root>").root.child
651
+ * .tap { |n| n.content = "" }.blank # => true
652
+ */
653
+ static VALUE
654
+ rb_xml_node_blank_eh(VALUE self)
655
+ {
656
+ xmlNodePtr node;
657
+ Noko_Node_Get_Struct(self, xmlNode, node);
658
+ return (1 == xmlIsBlankNode(node)) ? Qtrue : Qfalse ;
659
+ }
660
+
661
+
662
+ /*
663
+ * :call-seq: child() → Nokogiri::XML::Node
664
+ *
665
+ * :category: Traversing Document Structure
666
+ *
667
+ * [Returns] First of this node's children, or +nil+ if there are no children
668
+ *
669
+ * This is a convenience method and is equivalent to:
670
+ *
671
+ * node.children.first
672
+ *
673
+ * See related: #children
674
+ */
675
+ static VALUE
676
+ rb_xml_node_child(VALUE self)
677
+ {
678
+ xmlNodePtr node, child;
679
+ Noko_Node_Get_Struct(self, xmlNode, node);
680
+
681
+ child = node->children;
682
+ if (!child) { return Qnil; }
683
+
684
+ return noko_xml_node_wrap(Qnil, child);
685
+ }
686
+
687
+
688
+ /*
689
+ * :call-seq: children() → Nokogiri::XML::NodeSet
690
+ *
691
+ * :category: Traversing Document Structure
692
+ *
693
+ * [Returns] Nokogiri::XML::NodeSet containing this node's children.
694
+ */
695
+ static VALUE
696
+ rb_xml_node_children(VALUE self)
697
+ {
698
+ xmlNodePtr node;
699
+ xmlNodePtr child;
700
+ xmlNodeSetPtr set;
701
+ VALUE document;
702
+ VALUE node_set;
703
+
704
+ Noko_Node_Get_Struct(self, xmlNode, node);
705
+
706
+ child = node->children;
707
+ set = xmlXPathNodeSetCreate(child);
708
+
709
+ document = DOC_RUBY_OBJECT(node->doc);
710
+
711
+ if (!child) { return noko_xml_node_set_wrap(set, document); }
712
+
713
+ child = child->next;
714
+ while (NULL != child) {
715
+ xmlXPathNodeSetAddUnique(set, child);
716
+ child = child->next;
717
+ }
718
+
719
+ node_set = noko_xml_node_set_wrap(set, document);
720
+
721
+ return node_set;
722
+ }
723
+
724
+
725
+ /*
726
+ * :call-seq:
727
+ * content() → String
728
+ * inner_text() → String
729
+ * text() → String
730
+ * to_str() → String
731
+ *
732
+ * [Returns]
733
+ * Contents of all the text nodes in this node's subtree, concatenated together into a single
734
+ * String.
735
+ *
736
+ * ⚠ Note that entities will _always_ be expanded in the returned String.
737
+ *
738
+ * See related: #inner_html
739
+ *
740
+ * *Example* of how entities are handled:
741
+ *
742
+ * Note that <tt>&lt;</tt> becomes <tt><</tt> in the returned String.
743
+ *
744
+ * doc = Nokogiri::XML.fragment("<child>a &lt; b</child>")
745
+ * doc.at_css("child").content
746
+ * # => "a < b"
747
+ *
748
+ * *Example* of how a subtree is handled:
749
+ *
750
+ * Note that the <tt><span></tt> tags are omitted and only the text node contents are returned,
751
+ * concatenated into a single string.
752
+ *
753
+ * doc = Nokogiri::XML.fragment("<child><span>first</span> <span>second</span></child>")
754
+ * doc.at_css("child").content
755
+ * # => "first second"
756
+ */
757
+ static VALUE
758
+ rb_xml_node_content(VALUE self)
759
+ {
760
+ xmlNodePtr node;
761
+ xmlChar *content;
762
+
763
+ Noko_Node_Get_Struct(self, xmlNode, node);
764
+
765
+ content = xmlNodeGetContent(node);
766
+ if (content) {
767
+ VALUE rval = NOKOGIRI_STR_NEW2(content);
768
+ xmlFree(content);
769
+ return rval;
770
+ }
771
+ return Qnil;
772
+ }
773
+
774
+
775
+ /*
776
+ * :call-seq: document() → Nokogiri::XML::Document
777
+ *
778
+ * :category: Traversing Document Structure
779
+ *
780
+ * [Returns] Parent Nokogiri::XML::Document for this node
781
+ */
782
+ static VALUE
783
+ rb_xml_node_document(VALUE self)
784
+ {
785
+ xmlNodePtr node;
786
+ Noko_Node_Get_Struct(self, xmlNode, node);
787
+ return DOC_RUBY_OBJECT(node->doc);
788
+ }
789
+
790
+ /*
791
+ * :call-seq: pointer_id() → Integer
792
+ *
793
+ * [Returns]
794
+ * A unique id for this node based on the internal memory structures. This method is used by #==
795
+ * to determine node identity.
796
+ */
797
+ static VALUE
798
+ rb_xml_node_pointer_id(VALUE self)
799
+ {
800
+ xmlNodePtr node;
801
+ Noko_Node_Get_Struct(self, xmlNode, node);
802
+
803
+ return rb_uint2inum((uintptr_t)(node));
804
+ }
805
+
806
+ /*
807
+ * :call-seq: encode_special_chars(string) → String
808
+ *
809
+ * Encode any special characters in +string+
810
+ */
811
+ static VALUE
812
+ encode_special_chars(VALUE self, VALUE string)
813
+ {
814
+ xmlNodePtr node;
815
+ xmlChar *encoded;
816
+ VALUE encoded_str;
817
+
818
+ Noko_Node_Get_Struct(self, xmlNode, node);
819
+ encoded = xmlEncodeSpecialChars(
820
+ node->doc,
821
+ (const xmlChar *)StringValueCStr(string)
822
+ );
823
+
824
+ encoded_str = NOKOGIRI_STR_NEW2(encoded);
825
+ xmlFree(encoded);
826
+
827
+ return encoded_str;
828
+ }
829
+
830
+ /*
831
+ * :call-seq:
832
+ * create_internal_subset(name, external_id, system_id)
833
+ *
834
+ * Create the internal subset of a document.
835
+ *
836
+ * doc.create_internal_subset("chapter", "-//OASIS//DTD DocBook XML//EN", "chapter.dtd")
837
+ * # => <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML//EN" "chapter.dtd">
838
+ *
839
+ * doc.create_internal_subset("chapter", nil, "chapter.dtd")
840
+ * # => <!DOCTYPE chapter SYSTEM "chapter.dtd">
841
+ */
842
+ static VALUE
843
+ create_internal_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
844
+ {
845
+ xmlNodePtr node;
846
+ xmlDocPtr doc;
847
+ xmlDtdPtr dtd;
848
+
849
+ Noko_Node_Get_Struct(self, xmlNode, node);
850
+
851
+ doc = node->doc;
852
+
853
+ if (xmlGetIntSubset(doc)) {
854
+ rb_raise(rb_eRuntimeError, "Document already has an internal subset");
855
+ }
856
+
857
+ dtd = xmlCreateIntSubset(
858
+ doc,
859
+ NIL_P(name) ? NULL : (const xmlChar *)StringValueCStr(name),
860
+ NIL_P(external_id) ? NULL : (const xmlChar *)StringValueCStr(external_id),
861
+ NIL_P(system_id) ? NULL : (const xmlChar *)StringValueCStr(system_id)
862
+ );
863
+
864
+ if (!dtd) { return Qnil; }
865
+
866
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
867
+ }
868
+
869
+ /*
870
+ * :call-seq:
871
+ * create_external_subset(name, external_id, system_id)
872
+ *
873
+ * Create an external subset
874
+ */
875
+ static VALUE
876
+ create_external_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
877
+ {
878
+ xmlNodePtr node;
879
+ xmlDocPtr doc;
880
+ xmlDtdPtr dtd;
881
+
882
+ Noko_Node_Get_Struct(self, xmlNode, node);
883
+
884
+ doc = node->doc;
885
+
886
+ if (doc->extSubset) {
887
+ rb_raise(rb_eRuntimeError, "Document already has an external subset");
888
+ }
889
+
890
+ dtd = xmlNewDtd(
891
+ doc,
892
+ NIL_P(name) ? NULL : (const xmlChar *)StringValueCStr(name),
893
+ NIL_P(external_id) ? NULL : (const xmlChar *)StringValueCStr(external_id),
894
+ NIL_P(system_id) ? NULL : (const xmlChar *)StringValueCStr(system_id)
895
+ );
896
+
897
+ if (!dtd) { return Qnil; }
898
+
899
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
900
+ }
901
+
902
+ /*
903
+ * :call-seq:
904
+ * external_subset()
905
+ *
906
+ * Get the external subset
907
+ */
908
+ static VALUE
909
+ external_subset(VALUE self)
910
+ {
911
+ xmlNodePtr node;
912
+ xmlDocPtr doc;
913
+ xmlDtdPtr dtd;
914
+
915
+ Noko_Node_Get_Struct(self, xmlNode, node);
916
+
917
+ if (!node->doc) { return Qnil; }
918
+
919
+ doc = node->doc;
920
+ dtd = doc->extSubset;
921
+
922
+ if (!dtd) { return Qnil; }
923
+
924
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
925
+ }
926
+
927
+ /*
928
+ * :call-seq:
929
+ * internal_subset()
930
+ *
931
+ * Get the internal subset
932
+ */
933
+ static VALUE
934
+ internal_subset(VALUE self)
935
+ {
936
+ xmlNodePtr node;
937
+ xmlDocPtr doc;
938
+ xmlDtdPtr dtd;
939
+
940
+ Noko_Node_Get_Struct(self, xmlNode, node);
941
+
942
+ if (!node->doc) { return Qnil; }
943
+
944
+ doc = node->doc;
945
+ dtd = xmlGetIntSubset(doc);
946
+
947
+ if (!dtd) { return Qnil; }
948
+
949
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
950
+ }
951
+
952
+ /*
953
+ * :call-seq:
954
+ * dup → Nokogiri::XML::Node
955
+ * dup(depth) → Nokogiri::XML::Node
956
+ * dup(depth, new_parent_doc) → Nokogiri::XML::Node
957
+ *
958
+ * Copy this node.
959
+ *
960
+ * [Parameters]
961
+ * - +depth+ 0 is a shallow copy, 1 (the default) is a deep copy.
962
+ * - +new_parent_doc+
963
+ * The new node's parent Document. Defaults to the this node's document.
964
+ *
965
+ * [Returns] The new Nokgiri::XML::Node
966
+ */
967
+ static VALUE
968
+ duplicate_node(int argc, VALUE *argv, VALUE self)
969
+ {
970
+ VALUE r_level, r_new_parent_doc;
971
+ int level;
972
+ int n_args;
973
+ xmlDocPtr new_parent_doc;
974
+ xmlNodePtr node, dup;
975
+
976
+ Noko_Node_Get_Struct(self, xmlNode, node);
977
+
978
+ n_args = rb_scan_args(argc, argv, "02", &r_level, &r_new_parent_doc);
979
+
980
+ if (n_args < 1) {
981
+ r_level = INT2NUM((long)1);
982
+ }
983
+ level = (int)NUM2INT(r_level);
984
+
985
+ if (n_args < 2) {
986
+ new_parent_doc = node->doc;
987
+ } else {
988
+ Data_Get_Struct(r_new_parent_doc, xmlDoc, new_parent_doc);
989
+ }
990
+
991
+ dup = xmlDocCopyNode(node, new_parent_doc, level);
992
+ if (dup == NULL) { return Qnil; }
993
+
994
+ noko_xml_document_pin_node(dup);
995
+
996
+ return noko_xml_node_wrap(rb_obj_class(self), dup);
997
+ }
998
+
999
+ /*
1000
+ * :call-seq:
1001
+ * unlink() → self
1002
+ *
1003
+ * Unlink this node from its current context.
1004
+ */
1005
+ static VALUE
1006
+ unlink_node(VALUE self)
1007
+ {
1008
+ xmlNodePtr node;
1009
+ Noko_Node_Get_Struct(self, xmlNode, node);
1010
+ xmlUnlinkNode(node);
1011
+ noko_xml_document_pin_node(node);
1012
+ return self;
1013
+ }
1014
+
1015
+
1016
+ /*
1017
+ * call-seq:
1018
+ * next_sibling
1019
+ *
1020
+ * Returns the next sibling node
1021
+ */
1022
+ static VALUE
1023
+ next_sibling(VALUE self)
1024
+ {
1025
+ xmlNodePtr node, sibling;
1026
+ Noko_Node_Get_Struct(self, xmlNode, node);
1027
+
1028
+ sibling = node->next;
1029
+ if (!sibling) { return Qnil; }
1030
+
1031
+ return noko_xml_node_wrap(Qnil, sibling) ;
1032
+ }
1033
+
1034
+ /*
1035
+ * call-seq:
1036
+ * previous_sibling
1037
+ *
1038
+ * Returns the previous sibling node
1039
+ */
1040
+ static VALUE
1041
+ previous_sibling(VALUE self)
1042
+ {
1043
+ xmlNodePtr node, sibling;
1044
+ Noko_Node_Get_Struct(self, xmlNode, node);
1045
+
1046
+ sibling = node->prev;
1047
+ if (!sibling) { return Qnil; }
1048
+
1049
+ return noko_xml_node_wrap(Qnil, sibling);
1050
+ }
1051
+
1052
+ /*
1053
+ * call-seq:
1054
+ * next_element
1055
+ *
1056
+ * Returns the next Nokogiri::XML::Element type sibling node.
1057
+ */
1058
+ static VALUE
1059
+ next_element(VALUE self)
1060
+ {
1061
+ xmlNodePtr node, sibling;
1062
+ Noko_Node_Get_Struct(self, xmlNode, node);
1063
+
1064
+ sibling = xmlNextElementSibling(node);
1065
+ if (!sibling) { return Qnil; }
1066
+
1067
+ return noko_xml_node_wrap(Qnil, sibling);
1068
+ }
1069
+
1070
+ /*
1071
+ * call-seq:
1072
+ * previous_element
1073
+ *
1074
+ * Returns the previous Nokogiri::XML::Element type sibling node.
1075
+ */
1076
+ static VALUE
1077
+ previous_element(VALUE self)
1078
+ {
1079
+ xmlNodePtr node, sibling;
1080
+ Noko_Node_Get_Struct(self, xmlNode, node);
1081
+
1082
+ /*
1083
+ * note that we don't use xmlPreviousElementSibling here because it's buggy pre-2.7.7.
1084
+ */
1085
+ sibling = node->prev;
1086
+ if (!sibling) { return Qnil; }
1087
+
1088
+ while (sibling && sibling->type != XML_ELEMENT_NODE) {
1089
+ sibling = sibling->prev;
1090
+ }
1091
+
1092
+ return sibling ? noko_xml_node_wrap(Qnil, sibling) : Qnil ;
1093
+ }
1094
+
1095
+ /* :nodoc: */
1096
+ static VALUE
1097
+ replace(VALUE self, VALUE new_node)
1098
+ {
1099
+ VALUE reparent = reparent_node_with(self, new_node, xmlReplaceNodeWrapper);
1100
+
1101
+ xmlNodePtr pivot;
1102
+ Noko_Node_Get_Struct(self, xmlNode, pivot);
1103
+ noko_xml_document_pin_node(pivot);
1104
+
1105
+ return reparent;
1106
+ }
1107
+
1108
+ /*
1109
+ * :call-seq:
1110
+ * element_children() → NodeSet
1111
+ * elements() → NodeSet
1112
+ *
1113
+ * [Returns]
1114
+ * The node's child elements as a NodeSet. Only children that are elements will be returned, which
1115
+ * notably excludes Text nodes.
1116
+ *
1117
+ * *Example:*
1118
+ *
1119
+ * Note that #children returns the Text node "hello" while #element_children does not.
1120
+ *
1121
+ * div = Nokogiri::HTML5("<div>hello<span>world</span>").at_css("div")
1122
+ * div.element_children
1123
+ * # => [#<Nokogiri::XML::Element:0x50 name="span" children=[#<Nokogiri::XML::Text:0x3c "world">]>]
1124
+ * div.children
1125
+ * # => [#<Nokogiri::XML::Text:0x64 "hello">,
1126
+ * # #<Nokogiri::XML::Element:0x50 name="span" children=[#<Nokogiri::XML::Text:0x3c "world">]>]
1127
+ */
1128
+ static VALUE
1129
+ rb_xml_node_element_children(VALUE self)
1130
+ {
1131
+ xmlNodePtr node;
1132
+ xmlNodePtr child;
1133
+ xmlNodeSetPtr set;
1134
+ VALUE document;
1135
+ VALUE node_set;
1136
+
1137
+ Noko_Node_Get_Struct(self, xmlNode, node);
1138
+
1139
+ child = xmlFirstElementChild(node);
1140
+ set = xmlXPathNodeSetCreate(child);
1141
+
1142
+ document = DOC_RUBY_OBJECT(node->doc);
1143
+
1144
+ if (!child) { return noko_xml_node_set_wrap(set, document); }
1145
+
1146
+ child = xmlNextElementSibling(child);
1147
+ while (NULL != child) {
1148
+ xmlXPathNodeSetAddUnique(set, child);
1149
+ child = xmlNextElementSibling(child);
1150
+ }
1151
+
1152
+ node_set = noko_xml_node_set_wrap(set, document);
1153
+
1154
+ return node_set;
1155
+ }
1156
+
1157
+ /*
1158
+ * :call-seq:
1159
+ * first_element_child() → Node
1160
+ *
1161
+ * [Returns] The first child Node that is an element.
1162
+ *
1163
+ * *Example:*
1164
+ *
1165
+ * Note that the "hello" child, which is a Text node, is skipped and the <tt><span></tt> element is
1166
+ * returned.
1167
+ *
1168
+ * div = Nokogiri::HTML5("<div>hello<span>world</span>").at_css("div")
1169
+ * div.first_element_child
1170
+ * # => #(Element:0x3c { name = "span", children = [ #(Text "world")] })
1171
+ */
1172
+ static VALUE
1173
+ rb_xml_node_first_element_child(VALUE self)
1174
+ {
1175
+ xmlNodePtr node, child;
1176
+ Noko_Node_Get_Struct(self, xmlNode, node);
1177
+
1178
+ child = xmlFirstElementChild(node);
1179
+ if (!child) { return Qnil; }
1180
+
1181
+ return noko_xml_node_wrap(Qnil, child);
1182
+ }
1183
+
1184
+ /*
1185
+ * :call-seq:
1186
+ * last_element_child() → Node
1187
+ *
1188
+ * [Returns] The last child Node that is an element.
1189
+ *
1190
+ * *Example:*
1191
+ *
1192
+ * Note that the "hello" child, which is a Text node, is skipped and the <tt><span>yes</span></tt>
1193
+ * element is returned.
1194
+ *
1195
+ * div = Nokogiri::HTML5("<div><span>no</span><span>yes</span>skip</div>").at_css("div")
1196
+ * div.last_element_child
1197
+ * # => #(Element:0x3c { name = "span", children = [ #(Text "yes")] })
1198
+ */
1199
+ static VALUE
1200
+ rb_xml_node_last_element_child(VALUE self)
1201
+ {
1202
+ xmlNodePtr node, child;
1203
+ Noko_Node_Get_Struct(self, xmlNode, node);
1204
+
1205
+ child = xmlLastElementChild(node);
1206
+ if (!child) { return Qnil; }
1207
+
1208
+ return noko_xml_node_wrap(Qnil, child);
1209
+ }
1210
+
1211
+ /*
1212
+ * call-seq:
1213
+ * key?(attribute)
1214
+ *
1215
+ * Returns true if +attribute+ is set
1216
+ */
1217
+ static VALUE
1218
+ key_eh(VALUE self, VALUE attribute)
1219
+ {
1220
+ xmlNodePtr node;
1221
+ Noko_Node_Get_Struct(self, xmlNode, node);
1222
+ if (xmlHasProp(node, (xmlChar *)StringValueCStr(attribute))) {
1223
+ return Qtrue;
1224
+ }
1225
+ return Qfalse;
1226
+ }
1227
+
1228
+ /*
1229
+ * call-seq:
1230
+ * namespaced_key?(attribute, namespace)
1231
+ *
1232
+ * Returns true if +attribute+ is set with +namespace+
1233
+ */
1234
+ static VALUE
1235
+ namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
1236
+ {
1237
+ xmlNodePtr node;
1238
+ Noko_Node_Get_Struct(self, xmlNode, node);
1239
+ if (xmlHasNsProp(node, (xmlChar *)StringValueCStr(attribute),
1240
+ NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace))) {
1241
+ return Qtrue;
1242
+ }
1243
+ return Qfalse;
1244
+ }
1245
+
1246
+ /*
1247
+ * call-seq:
1248
+ * []=(property, value)
1249
+ *
1250
+ * Set the +property+ to +value+
1251
+ */
1252
+ static VALUE
1253
+ set(VALUE self, VALUE property, VALUE value)
1254
+ {
1255
+ xmlNodePtr node, cur;
1256
+ xmlAttrPtr prop;
1257
+ Noko_Node_Get_Struct(self, xmlNode, node);
1258
+
1259
+ /* If a matching attribute node already exists, then xmlSetProp will destroy
1260
+ * the existing node's children. However, if Nokogiri has a node object
1261
+ * pointing to one of those children, we are left with a broken reference.
1262
+ *
1263
+ * We can avoid this by unlinking these nodes first.
1264
+ */
1265
+ if (node->type != XML_ELEMENT_NODE) {
1266
+ return (Qnil);
1267
+ }
1268
+ prop = xmlHasProp(node, (xmlChar *)StringValueCStr(property));
1269
+ if (prop && prop->children) {
1270
+ for (cur = prop->children; cur; cur = cur->next) {
1271
+ if (cur->_private) {
1272
+ noko_xml_document_pin_node(cur);
1273
+ xmlUnlinkNode(cur);
1274
+ }
1275
+ }
1276
+ }
1277
+
1278
+ xmlSetProp(node, (xmlChar *)StringValueCStr(property),
1279
+ (xmlChar *)StringValueCStr(value));
1280
+
1281
+ return value;
1282
+ }
1283
+
1284
+ /*
1285
+ * call-seq:
1286
+ * get(attribute)
1287
+ *
1288
+ * Get the value for +attribute+
1289
+ */
1290
+ static VALUE
1291
+ get(VALUE self, VALUE rattribute)
1292
+ {
1293
+ xmlNodePtr node;
1294
+ xmlChar *value = 0;
1295
+ VALUE rvalue;
1296
+ xmlChar *colon;
1297
+ xmlChar *attribute, *attr_name, *prefix;
1298
+ xmlNsPtr ns;
1299
+
1300
+ if (NIL_P(rattribute)) { return Qnil; }
1301
+
1302
+ Noko_Node_Get_Struct(self, xmlNode, node);
1303
+ attribute = xmlCharStrdup(StringValueCStr(rattribute));
1304
+
1305
+ colon = DISCARD_CONST_QUAL_XMLCHAR(xmlStrchr(attribute, (const xmlChar)':'));
1306
+ if (colon) {
1307
+ /* split the attribute string into separate prefix and name by
1308
+ * null-terminating the prefix at the colon */
1309
+ prefix = attribute;
1310
+ attr_name = colon + 1;
1311
+ (*colon) = 0;
1312
+
1313
+ ns = xmlSearchNs(node->doc, node, prefix);
1314
+ if (ns) {
1315
+ value = xmlGetNsProp(node, attr_name, ns->href);
1316
+ } else {
1317
+ value = xmlGetProp(node, (xmlChar *)StringValueCStr(rattribute));
1318
+ }
1319
+ } else {
1320
+ value = xmlGetNoNsProp(node, attribute);
1321
+ }
1322
+
1323
+ xmlFree((void *)attribute);
1324
+ if (!value) { return Qnil; }
1325
+
1326
+ rvalue = NOKOGIRI_STR_NEW2(value);
1327
+ xmlFree((void *)value);
1328
+
1329
+ return rvalue ;
1330
+ }
1331
+
1332
+ /*
1333
+ * call-seq:
1334
+ * set_namespace(namespace)
1335
+ *
1336
+ * Set the namespace to +namespace+
1337
+ */
1338
+ static VALUE
1339
+ set_namespace(VALUE self, VALUE namespace)
1340
+ {
1341
+ xmlNodePtr node;
1342
+ xmlNsPtr ns = NULL;
1343
+
1344
+ Noko_Node_Get_Struct(self, xmlNode, node);
1345
+
1346
+ if (!NIL_P(namespace)) {
1347
+ Noko_Namespace_Get_Struct(namespace, xmlNs, ns);
1348
+ }
1349
+
1350
+ xmlSetNs(node, ns);
1351
+
1352
+ return self;
1353
+ }
1354
+
1355
+ /*
1356
+ * :call-seq:
1357
+ * namespace() → Namespace
1358
+ *
1359
+ * [Returns] The Namespace of the element or attribute node, or +nil+ if there is no namespace.
1360
+ *
1361
+ * *Example:*
1362
+ *
1363
+ * doc = Nokogiri::XML(<<~EOF)
1364
+ * <root>
1365
+ * <first/>
1366
+ * <second xmlns="http://example.com/child"/>
1367
+ * <foo:third xmlns:foo="http://example.com/foo"/>
1368
+ * </root>
1369
+ * EOF
1370
+ * doc.at_xpath("//first").namespace
1371
+ * # => nil
1372
+ * doc.at_xpath("//xmlns:second", "xmlns" => "http://example.com/child").namespace
1373
+ * # => #(Namespace:0x3c { href = "http://example.com/child" })
1374
+ * doc.at_xpath("//foo:third", "foo" => "http://example.com/foo").namespace
1375
+ * # => #(Namespace:0x50 { prefix = "foo", href = "http://example.com/foo" })
1376
+ */
1377
+ static VALUE
1378
+ rb_xml_node_namespace(VALUE rb_node)
1379
+ {
1380
+ xmlNodePtr c_node ;
1381
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1382
+
1383
+ if (c_node->ns) {
1384
+ return noko_xml_namespace_wrap(c_node->ns, c_node->doc);
1385
+ }
1386
+
1387
+ return Qnil ;
1388
+ }
1389
+
1390
+ /*
1391
+ * :call-seq:
1392
+ * namespace_definitions() → Array<Nokogiri::XML::Namespace>
1393
+ *
1394
+ * [Returns]
1395
+ * Namespaces that are defined directly on this node, as an Array of Namespace objects. The array
1396
+ * will be empty if no namespaces are defined on this node.
1397
+ *
1398
+ * *Example:*
1399
+ *
1400
+ * doc = Nokogiri::XML(<<~EOF)
1401
+ * <root xmlns="http://example.com/root">
1402
+ * <first/>
1403
+ * <second xmlns="http://example.com/child" xmlns:unused="http://example.com/unused"/>
1404
+ * <foo:third xmlns:foo="http://example.com/foo"/>
1405
+ * </root>
1406
+ * EOF
1407
+ * doc.at_xpath("//root:first", "root" => "http://example.com/root").namespace_definitions
1408
+ * # => []
1409
+ * doc.at_xpath("//xmlns:second", "xmlns" => "http://example.com/child").namespace_definitions
1410
+ * # => [#(Namespace:0x3c { href = "http://example.com/child" }),
1411
+ * # #(Namespace:0x50 {
1412
+ * # prefix = "unused",
1413
+ * # href = "http://example.com/unused"
1414
+ * # })]
1415
+ * doc.at_xpath("//foo:third", "foo" => "http://example.com/foo").namespace_definitions
1416
+ * # => [#(Namespace:0x64 { prefix = "foo", href = "http://example.com/foo" })]
1417
+ */
1418
+ static VALUE
1419
+ namespace_definitions(VALUE rb_node)
1420
+ {
1421
+ /* this code in the mode of xmlHasProp() */
1422
+ xmlNodePtr c_node ;
1423
+ xmlNsPtr c_namespace;
1424
+ VALUE definitions = rb_ary_new();
1425
+
1426
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1427
+
1428
+ c_namespace = c_node->nsDef;
1429
+ if (!c_namespace) {
1430
+ return definitions;
1431
+ }
1432
+
1433
+ while (c_namespace != NULL) {
1434
+ rb_ary_push(definitions, noko_xml_namespace_wrap(c_namespace, c_node->doc));
1435
+ c_namespace = c_namespace->next;
1436
+ }
1437
+
1438
+ return definitions;
1439
+ }
1440
+
1441
+ /*
1442
+ * :call-seq:
1443
+ * namespace_scopes() → Array<Nokogiri::XML::Namespace>
1444
+ *
1445
+ * [Returns] Array of all the Namespaces on this node and its ancestors.
1446
+ *
1447
+ * See also #namespaces
1448
+ *
1449
+ * *Example:*
1450
+ *
1451
+ * doc = Nokogiri::XML(<<~EOF)
1452
+ * <root xmlns="http://example.com/root" xmlns:bar="http://example.com/bar">
1453
+ * <first/>
1454
+ * <second xmlns="http://example.com/child"/>
1455
+ * <third xmlns:foo="http://example.com/foo"/>
1456
+ * </root>
1457
+ * EOF
1458
+ * doc.at_xpath("//root:first", "root" => "http://example.com/root").namespace_scopes
1459
+ * # => [#(Namespace:0x3c { href = "http://example.com/root" }),
1460
+ * # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
1461
+ * doc.at_xpath("//child:second", "child" => "http://example.com/child").namespace_scopes
1462
+ * # => [#(Namespace:0x64 { href = "http://example.com/child" }),
1463
+ * # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
1464
+ * doc.at_xpath("//root:third", "root" => "http://example.com/root").namespace_scopes
1465
+ * # => [#(Namespace:0x78 { prefix = "foo", href = "http://example.com/foo" }),
1466
+ * # #(Namespace:0x3c { href = "http://example.com/root" }),
1467
+ * # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
1468
+ */
1469
+ static VALUE
1470
+ rb_xml_node_namespace_scopes(VALUE rb_node)
1471
+ {
1472
+ xmlNodePtr c_node ;
1473
+ xmlNsPtr *namespaces;
1474
+ VALUE scopes = rb_ary_new();
1475
+ int j;
1476
+
1477
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1478
+
1479
+ namespaces = xmlGetNsList(c_node->doc, c_node);
1480
+ if (!namespaces) {
1481
+ return scopes;
1482
+ }
1483
+
1484
+ for (j = 0 ; namespaces[j] != NULL ; ++j) {
1485
+ rb_ary_push(scopes, noko_xml_namespace_wrap(namespaces[j], c_node->doc));
1486
+ }
1487
+
1488
+ xmlFree(namespaces);
1489
+ return scopes;
1490
+ }
1491
+
1492
+ /*
1493
+ * call-seq:
1494
+ * node_type
1495
+ *
1496
+ * Get the type for this Node
1497
+ */
1498
+ static VALUE
1499
+ node_type(VALUE self)
1500
+ {
1501
+ xmlNodePtr node;
1502
+ Noko_Node_Get_Struct(self, xmlNode, node);
1503
+ return INT2NUM(node->type);
1504
+ }
1505
+
1506
+ /*
1507
+ * call-seq:
1508
+ * content=
1509
+ *
1510
+ * Set the content for this Node
1511
+ */
1512
+ static VALUE
1513
+ set_native_content(VALUE self, VALUE content)
1514
+ {
1515
+ xmlNodePtr node, child, next ;
1516
+ Noko_Node_Get_Struct(self, xmlNode, node);
1517
+
1518
+ child = node->children;
1519
+ while (NULL != child) {
1520
+ next = child->next ;
1521
+ xmlUnlinkNode(child) ;
1522
+ noko_xml_document_pin_node(child);
1523
+ child = next ;
1524
+ }
1525
+
1526
+ xmlNodeSetContent(node, (xmlChar *)StringValueCStr(content));
1527
+ return content;
1528
+ }
1529
+
1530
+ /*
1531
+ * call-seq:
1532
+ * lang=
1533
+ *
1534
+ * Set the language of a node, i.e. the values of the xml:lang attribute.
1535
+ */
1536
+ static VALUE
1537
+ set_lang(VALUE self_rb, VALUE lang_rb)
1538
+ {
1539
+ xmlNodePtr self ;
1540
+ xmlChar *lang ;
1541
+
1542
+ Noko_Node_Get_Struct(self_rb, xmlNode, self);
1543
+ lang = (xmlChar *)StringValueCStr(lang_rb);
1544
+
1545
+ xmlNodeSetLang(self, lang);
1546
+
1547
+ return Qnil ;
1548
+ }
1549
+
1550
+ /*
1551
+ * call-seq:
1552
+ * lang
1553
+ *
1554
+ * Searches the language of a node, i.e. the values of the xml:lang attribute or
1555
+ * the one carried by the nearest ancestor.
1556
+ */
1557
+ static VALUE
1558
+ get_lang(VALUE self_rb)
1559
+ {
1560
+ xmlNodePtr self ;
1561
+ xmlChar *lang ;
1562
+ VALUE lang_rb ;
1563
+
1564
+ Noko_Node_Get_Struct(self_rb, xmlNode, self);
1565
+
1566
+ lang = xmlNodeGetLang(self);
1567
+ if (lang) {
1568
+ lang_rb = NOKOGIRI_STR_NEW2(lang);
1569
+ xmlFree(lang);
1570
+ return lang_rb ;
1571
+ }
1572
+
1573
+ return Qnil ;
1574
+ }
1575
+
1576
+ /* :nodoc: */
1577
+ static VALUE
1578
+ add_child(VALUE self, VALUE new_child)
1579
+ {
1580
+ return reparent_node_with(self, new_child, xmlAddChild);
1581
+ }
1582
+
1583
+ /*
1584
+ * call-seq:
1585
+ * parent
1586
+ *
1587
+ * Get the parent Node for this Node
1588
+ */
1589
+ static VALUE
1590
+ get_parent(VALUE self)
1591
+ {
1592
+ xmlNodePtr node, parent;
1593
+ Noko_Node_Get_Struct(self, xmlNode, node);
1594
+
1595
+ parent = node->parent;
1596
+ if (!parent) { return Qnil; }
1597
+
1598
+ return noko_xml_node_wrap(Qnil, parent) ;
1599
+ }
1600
+
1601
+ /*
1602
+ * call-seq:
1603
+ * name=(new_name)
1604
+ *
1605
+ * Set the name for this Node
1606
+ */
1607
+ static VALUE
1608
+ set_name(VALUE self, VALUE new_name)
1609
+ {
1610
+ xmlNodePtr node;
1611
+ Noko_Node_Get_Struct(self, xmlNode, node);
1612
+ xmlNodeSetName(node, (xmlChar *)StringValueCStr(new_name));
1613
+ return new_name;
1614
+ }
1615
+
1616
+ /*
1617
+ * call-seq:
1618
+ * name
1619
+ *
1620
+ * Returns the name for this Node
1621
+ */
1622
+ static VALUE
1623
+ get_name(VALUE self)
1624
+ {
1625
+ xmlNodePtr node;
1626
+ Noko_Node_Get_Struct(self, xmlNode, node);
1627
+ if (node->name) {
1628
+ return NOKOGIRI_STR_NEW2(node->name);
1629
+ }
1630
+ return Qnil;
1631
+ }
1632
+
1633
+ /*
1634
+ * call-seq:
1635
+ * path
1636
+ *
1637
+ * Returns the path associated with this Node
1638
+ */
1639
+ static VALUE
1640
+ rb_xml_node_path(VALUE rb_node)
1641
+ {
1642
+ xmlNodePtr c_node;
1643
+ xmlChar *c_path ;
1644
+ VALUE rval;
1645
+
1646
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1647
+
1648
+ c_path = xmlGetNodePath(c_node);
1649
+ if (c_path == NULL) {
1650
+ // see https://github.com/sparklemotion/nokogiri/issues/2250
1651
+ // this behavior is clearly undesirable, but is what libxml <= 2.9.10 returned, and so we
1652
+ // do this for now to preserve the behavior across libxml2 versions.
1653
+ rval = NOKOGIRI_STR_NEW2("?");
1654
+ } else {
1655
+ rval = NOKOGIRI_STR_NEW2(c_path);
1656
+ xmlFree(c_path);
1657
+ }
1658
+
1659
+ return rval ;
1660
+ }
1661
+
1662
+ /* :nodoc: */
1663
+ static VALUE
1664
+ add_next_sibling(VALUE self, VALUE new_sibling)
1665
+ {
1666
+ return reparent_node_with(self, new_sibling, xmlAddNextSibling) ;
1667
+ }
1668
+
1669
+ /* :nodoc: */
1670
+ static VALUE
1671
+ add_previous_sibling(VALUE self, VALUE new_sibling)
1672
+ {
1673
+ return reparent_node_with(self, new_sibling, xmlAddPrevSibling) ;
1674
+ }
1675
+
1676
+ /*
1677
+ * call-seq:
1678
+ * native_write_to(io, encoding, options)
1679
+ *
1680
+ * Write this Node to +io+ with +encoding+ and +options+
1681
+ */
1682
+ static VALUE
1683
+ native_write_to(
1684
+ VALUE self,
1685
+ VALUE io,
1686
+ VALUE encoding,
1687
+ VALUE indent_string,
1688
+ VALUE options
1689
+ )
1690
+ {
1691
+ xmlNodePtr node;
1692
+ const char *before_indent;
1693
+ xmlSaveCtxtPtr savectx;
1694
+
1695
+ Noko_Node_Get_Struct(self, xmlNode, node);
1696
+
1697
+ xmlIndentTreeOutput = 1;
1698
+
1699
+ before_indent = xmlTreeIndentString;
1700
+
1701
+ xmlTreeIndentString = StringValueCStr(indent_string);
1702
+
1703
+ savectx = xmlSaveToIO(
1704
+ (xmlOutputWriteCallback)noko_io_write,
1705
+ (xmlOutputCloseCallback)noko_io_close,
1706
+ (void *)io,
1707
+ RTEST(encoding) ? StringValueCStr(encoding) : NULL,
1708
+ (int)NUM2INT(options)
1709
+ );
1710
+
1711
+ xmlSaveTree(savectx, node);
1712
+ xmlSaveClose(savectx);
1713
+
1714
+ xmlTreeIndentString = before_indent;
1715
+ return io;
1716
+ }
1717
+
1718
+
1719
+ static inline void
1720
+ output_partial_string(VALUE out, char const *str, size_t length)
1721
+ {
1722
+ if (length) {
1723
+ rb_enc_str_buf_cat(out, str, (long)length, rb_utf8_encoding());
1724
+ }
1725
+ }
1726
+
1727
+ static inline void
1728
+ output_char(VALUE out, char ch)
1729
+ {
1730
+ output_partial_string(out, &ch, 1);
1731
+ }
1732
+
1733
+ static inline void
1734
+ output_string(VALUE out, char const *str)
1735
+ {
1736
+ output_partial_string(out, str, strlen(str));
1737
+ }
1738
+
1739
+ static inline void
1740
+ output_tagname(VALUE out, xmlNodePtr elem)
1741
+ {
1742
+ // Elements in the HTML, MathML, and SVG namespaces do not use a namespace
1743
+ // prefix in the HTML syntax.
1744
+ char const *name = (char const *)elem->name;
1745
+ xmlNsPtr ns = elem->ns;
1746
+ if (ns && ns->href && ns->prefix
1747
+ && strcmp((char const *)ns->href, "http://www.w3.org/1999/xhtml")
1748
+ && strcmp((char const *)ns->href, "http://www.w3.org/1998/Math/MathML")
1749
+ && strcmp((char const *)ns->href, "http://www.w3.org/2000/svg")) {
1750
+ output_string(out, (char const *)elem->ns->prefix);
1751
+ output_char(out, ':');
1752
+ char const *colon = strchr(name, ':');
1753
+ if (colon) {
1754
+ name = colon + 1;
1755
+ }
1756
+ }
1757
+ output_string(out, name);
1758
+ }
1759
+
1760
+ static inline void
1761
+ output_attr_name(VALUE out, xmlAttrPtr attr)
1762
+ {
1763
+ xmlNsPtr ns = attr->ns;
1764
+ char const *name = (char const *)attr->name;
1765
+ if (ns && ns->href) {
1766
+ char const *uri = (char const *)ns->href;
1767
+ char const *localname = strchr(name, ':');
1768
+ if (localname) {
1769
+ ++localname;
1770
+ } else {
1771
+ localname = name;
1772
+ }
1773
+
1774
+ if (!strcmp(uri, "http://www.w3.org/XML/1998/namespace")) {
1775
+ output_string(out, "xml:");
1776
+ name = localname;
1777
+ } else if (!strcmp(uri, "http://www.w3.org/2000/xmlns/")) {
1778
+ // xmlns:xmlns -> xmlns
1779
+ // xmlns:foo -> xmlns:foo
1780
+ if (strcmp(localname, "xmlns")) {
1781
+ output_string(out, "xmlns:");
1782
+ }
1783
+ name = localname;
1784
+ } else if (!strcmp(uri, "http://www.w3.org/1999/xlink")) {
1785
+ output_string(out, "xlink:");
1786
+ name = localname;
1787
+ } else if (ns->prefix) {
1788
+ output_string(out, (char const *)ns->prefix);
1789
+ output_char(out, ':');
1790
+ name = localname;
1791
+ }
1792
+ }
1793
+ output_string(out, name);
1794
+ }
1795
+
1796
+ static void
1797
+ output_escaped_string(VALUE out, xmlChar const *start, bool attr)
1798
+ {
1799
+ xmlChar const *next = start;
1800
+ int ch;
1801
+
1802
+ while ((ch = *next) != 0) {
1803
+ char const *replacement = NULL;
1804
+ size_t replaced_bytes = 1;
1805
+ if (ch == '&') {
1806
+ replacement = "&amp;";
1807
+ } else if (ch == 0xC2 && next[1] == 0xA0) {
1808
+ // U+00A0 NO-BREAK SPACE has the UTF-8 encoding C2 A0.
1809
+ replacement = "&nbsp;";
1810
+ replaced_bytes = 2;
1811
+ } else if (attr && ch == '"') {
1812
+ replacement = "&quot;";
1813
+ } else if (!attr && ch == '<') {
1814
+ replacement = "&lt;";
1815
+ } else if (!attr && ch == '>') {
1816
+ replacement = "&gt;";
1817
+ } else {
1818
+ ++next;
1819
+ continue;
1820
+ }
1821
+ output_partial_string(out, (char const *)start, next - start);
1822
+ output_string(out, replacement);
1823
+ next += replaced_bytes;
1824
+ start = next;
1825
+ }
1826
+ output_partial_string(out, (char const *)start, next - start);
1827
+ }
1828
+
1829
+ static bool
1830
+ should_prepend_newline(xmlNodePtr node)
1831
+ {
1832
+ char const *name = (char const *)node->name;
1833
+ xmlNodePtr child = node->children;
1834
+
1835
+ if (!name || !child || (strcmp(name, "pre") && strcmp(name, "textarea") && strcmp(name, "listing"))) {
1836
+ return false;
1837
+ }
1838
+
1839
+ return child->type == XML_TEXT_NODE && child->content && child->content[0] == '\n';
1840
+ }
1841
+
1842
+ static VALUE
1843
+ rb_prepend_newline(VALUE self)
1844
+ {
1845
+ xmlNodePtr node;
1846
+ Noko_Node_Get_Struct(self, xmlNode, node);
1847
+ return should_prepend_newline(node) ? Qtrue : Qfalse;
1848
+ }
1849
+
1850
+ static bool
1851
+ is_one_of(xmlNodePtr node, char const *const *tagnames, size_t num_tagnames)
1852
+ {
1853
+ char const *name = (char const *)node->name;
1854
+ if (name == NULL) { // fragments don't have a name
1855
+ return false;
1856
+ }
1857
+ for (size_t idx = 0; idx < num_tagnames; ++idx) {
1858
+ if (!strcmp(name, tagnames[idx])) {
1859
+ return true;
1860
+ }
1861
+ }
1862
+ return false;
1863
+
1864
+ }
1865
+
1866
+ static void
1867
+ output_node(
1868
+ VALUE out,
1869
+ xmlNodePtr node,
1870
+ bool preserve_newline
1871
+ )
1872
+ {
1873
+ static char const *const VOID_ELEMENTS[] = {
1874
+ "area", "base", "basefont", "bgsound", "br", "col", "embed", "frame", "hr",
1875
+ "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr",
1876
+ };
1877
+
1878
+ static char const *const UNESCAPED_TEXT_ELEMENTS[] = {
1879
+ "style", "script", "xmp", "iframe", "noembed", "noframes", "plaintext", "noscript",
1880
+ };
1881
+
1882
+ switch (node->type) {
1883
+ case XML_ELEMENT_NODE:
1884
+ // Serialize the start tag.
1885
+ output_char(out, '<');
1886
+ output_tagname(out, node);
1887
+
1888
+ // Add attributes.
1889
+ for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) {
1890
+ output_char(out, ' ');
1891
+ output_attr_name(out, attr);
1892
+ if (attr->children) {
1893
+ output_string(out, "=\"");
1894
+ xmlChar *value = xmlNodeListGetString(attr->doc, attr->children, 1);
1895
+ output_escaped_string(out, value, true);
1896
+ xmlFree(value);
1897
+ output_char(out, '"');
1898
+ } else {
1899
+ // Output name=""
1900
+ output_string(out, "=\"\"");
1901
+ }
1902
+ }
1903
+ output_char(out, '>');
1904
+
1905
+ // Add children and end tag if element is not void.
1906
+ if (!is_one_of(node, VOID_ELEMENTS, sizeof VOID_ELEMENTS / sizeof VOID_ELEMENTS[0])) {
1907
+ if (preserve_newline && should_prepend_newline(node)) {
1908
+ output_char(out, '\n');
1909
+ }
1910
+ for (xmlNodePtr child = node->children; child; child = child->next) {
1911
+ output_node(out, child, preserve_newline);
1912
+ }
1913
+ output_string(out, "</");
1914
+ output_tagname(out, node);
1915
+ output_char(out, '>');
1916
+ }
1917
+ break;
1918
+
1919
+ case XML_TEXT_NODE:
1920
+ if (node->parent
1921
+ && is_one_of(node->parent, UNESCAPED_TEXT_ELEMENTS,
1922
+ sizeof UNESCAPED_TEXT_ELEMENTS / sizeof UNESCAPED_TEXT_ELEMENTS[0])) {
1923
+ output_string(out, (char const *)node->content);
1924
+ } else {
1925
+ output_escaped_string(out, node->content, false);
1926
+ }
1927
+ break;
1928
+
1929
+ case XML_CDATA_SECTION_NODE:
1930
+ output_string(out, "<![CDATA[");
1931
+ output_string(out, (char const *)node->content);
1932
+ output_string(out, "]]>");
1933
+ break;
1934
+
1935
+ case XML_COMMENT_NODE:
1936
+ output_string(out, "<!--");
1937
+ output_string(out, (char const *)node->content);
1938
+ output_string(out, "-->");
1939
+ break;
1940
+
1941
+ case XML_PI_NODE:
1942
+ output_string(out, "<?");
1943
+ output_string(out, (char const *)node->content);
1944
+ output_char(out, '>');
1945
+ break;
1946
+
1947
+ case XML_DOCUMENT_TYPE_NODE:
1948
+ case XML_DTD_NODE:
1949
+ output_string(out, "<!DOCTYPE ");
1950
+ output_string(out, (char const *)node->name);
1951
+ output_string(out, ">");
1952
+ break;
1953
+
1954
+ case XML_DOCUMENT_NODE:
1955
+ case XML_DOCUMENT_FRAG_NODE:
1956
+ case XML_HTML_DOCUMENT_NODE:
1957
+ for (xmlNodePtr child = node->children; child; child = child->next) {
1958
+ output_node(out, child, preserve_newline);
1959
+ }
1960
+ break;
1961
+
1962
+ default:
1963
+ rb_raise(rb_eRuntimeError, "Unsupported document node (%d); this is a bug in Nokogiri", node->type);
1964
+ break;
1965
+ }
1966
+ }
1967
+
1968
+ static VALUE
1969
+ html_standard_serialize(
1970
+ VALUE self,
1971
+ VALUE preserve_newline
1972
+ )
1973
+ {
1974
+ xmlNodePtr node;
1975
+ Noko_Node_Get_Struct(self, xmlNode, node);
1976
+ VALUE output = rb_str_buf_new(4096);
1977
+ output_node(output, node, RTEST(preserve_newline));
1978
+ return output;
1979
+ }
1980
+
1981
+ /*
1982
+ * :call-seq:
1983
+ * line() → Integer
1984
+ *
1985
+ * [Returns] The line number of this Node.
1986
+ *
1987
+ * ---
1988
+ *
1989
+ * <b> ⚠ The CRuby and JRuby implementations differ in important ways! </b>
1990
+ *
1991
+ * Semantic differences:
1992
+ * - The CRuby method reflects the node's line number <i>in the parsed string</i>
1993
+ * - The JRuby method reflects the node's line number <i>in the final DOM structure</i> after
1994
+ * corrections have been applied
1995
+ *
1996
+ * Performance differences:
1997
+ * - The CRuby method is {O(1)}[https://en.wikipedia.org/wiki/Time_complexity#Constant_time]
1998
+ * (constant time)
1999
+ * - The JRuby method is {O(n)}[https://en.wikipedia.org/wiki/Time_complexity#Linear_time] (linear
2000
+ * time, where n is the number of nodes before/above the element in the DOM)
2001
+ *
2002
+ * If you'd like to help improve the JRuby implementation, please review these issues and reach out
2003
+ * to the maintainers:
2004
+ * - https://github.com/sparklemotion/nokogiri/issues/1223
2005
+ * - https://github.com/sparklemotion/nokogiri/pull/2177
2006
+ * - https://github.com/sparklemotion/nokogiri/issues/2380
2007
+ */
2008
+ static VALUE
2009
+ rb_xml_node_line(VALUE rb_node)
2010
+ {
2011
+ xmlNodePtr c_node;
2012
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
2013
+
2014
+ return LONG2NUM(xmlGetLineNo(c_node));
2015
+ }
2016
+
2017
+ /*
2018
+ * call-seq:
2019
+ * line=(num)
2020
+ *
2021
+ * Sets the line for this Node. num must be less than 65535.
2022
+ */
2023
+ static VALUE
2024
+ rb_xml_node_line_set(VALUE rb_node, VALUE rb_line_number)
2025
+ {
2026
+ xmlNodePtr c_node;
2027
+ int line_number = NUM2INT(rb_line_number);
2028
+
2029
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
2030
+
2031
+ // libxml2 optionally uses xmlNode.psvi to store longer line numbers, but only for text nodes.
2032
+ // search for "psvi" in SAX2.c and tree.c to learn more.
2033
+ if (line_number < 65535) {
2034
+ c_node->line = (short) line_number;
2035
+ } else {
2036
+ c_node->line = 65535;
2037
+ if (c_node->type == XML_TEXT_NODE) {
2038
+ c_node->psvi = (void *)(ptrdiff_t) line_number;
2039
+ }
2040
+ }
2041
+
2042
+ return rb_line_number;
2043
+ }
2044
+
2045
+ /* :nodoc: documented in lib/nokogiri/xml/node.rb */
2046
+ static VALUE
2047
+ rb_xml_node_new(int argc, VALUE *argv, VALUE klass)
2048
+ {
2049
+ xmlNodePtr c_document_node;
2050
+ xmlNodePtr c_node;
2051
+ VALUE rb_name;
2052
+ VALUE rb_document_node;
2053
+ VALUE rest;
2054
+ VALUE rb_node;
2055
+
2056
+ rb_scan_args(argc, argv, "2*", &rb_name, &rb_document_node, &rest);
2057
+
2058
+ if (!rb_obj_is_kind_of(rb_document_node, cNokogiriXmlNode)) {
2059
+ rb_raise(rb_eArgError, "document must be a Nokogiri::XML::Node");
2060
+ }
2061
+ if (!rb_obj_is_kind_of(rb_document_node, cNokogiriXmlDocument)) {
2062
+ // TODO: deprecate allowing Node
2063
+ NOKO_WARN_DEPRECATION("Passing a Node as the second parameter to Node.new is deprecated. Please pass a Document instead, or prefer an alternative constructor like Node#add_child. This will become an error in a future release of Nokogiri.");
2064
+ }
2065
+ Noko_Node_Get_Struct(rb_document_node, xmlNode, c_document_node);
2066
+
2067
+ c_node = xmlNewNode(NULL, (xmlChar *)StringValueCStr(rb_name));
2068
+ c_node->doc = c_document_node->doc;
2069
+ noko_xml_document_pin_node(c_node);
2070
+
2071
+ rb_node = noko_xml_node_wrap(
2072
+ klass == cNokogiriXmlNode ? (VALUE)NULL : klass,
2073
+ c_node
2074
+ );
2075
+ rb_obj_call_init(rb_node, argc, argv);
2076
+
2077
+ if (rb_block_given_p()) { rb_yield(rb_node); }
2078
+
2079
+ return rb_node;
2080
+ }
2081
+
2082
+ /*
2083
+ * call-seq:
2084
+ * dump_html
2085
+ *
2086
+ * Returns the Node as html.
2087
+ */
2088
+ static VALUE
2089
+ dump_html(VALUE self)
2090
+ {
2091
+ xmlBufferPtr buf ;
2092
+ xmlNodePtr node ;
2093
+ VALUE html;
2094
+
2095
+ Noko_Node_Get_Struct(self, xmlNode, node);
2096
+
2097
+ buf = xmlBufferCreate() ;
2098
+ htmlNodeDump(buf, node->doc, node);
2099
+ html = NOKOGIRI_STR_NEW2(buf->content);
2100
+ xmlBufferFree(buf);
2101
+ return html ;
2102
+ }
2103
+
2104
+ /*
2105
+ * call-seq:
2106
+ * compare(other)
2107
+ *
2108
+ * Compare this Node to +other+ with respect to their Document
2109
+ */
2110
+ static VALUE
2111
+ compare(VALUE self, VALUE _other)
2112
+ {
2113
+ xmlNodePtr node, other;
2114
+ Noko_Node_Get_Struct(self, xmlNode, node);
2115
+ Noko_Node_Get_Struct(_other, xmlNode, other);
2116
+
2117
+ return INT2NUM(xmlXPathCmpNodes(other, node));
2118
+ }
2119
+
2120
+
2121
+ /*
2122
+ * call-seq:
2123
+ * process_xincludes(options)
2124
+ *
2125
+ * Loads and substitutes all xinclude elements below the node. The
2126
+ * parser context will be initialized with +options+.
2127
+ */
2128
+ static VALUE
2129
+ process_xincludes(VALUE self, VALUE options)
2130
+ {
2131
+ int rcode ;
2132
+ xmlNodePtr node;
2133
+ VALUE error_list = rb_ary_new();
2134
+
2135
+ Noko_Node_Get_Struct(self, xmlNode, node);
2136
+
2137
+ xmlSetStructuredErrorFunc((void *)error_list, Nokogiri_error_array_pusher);
2138
+ rcode = xmlXIncludeProcessTreeFlags(node, (int)NUM2INT(options));
2139
+ xmlSetStructuredErrorFunc(NULL, NULL);
2140
+
2141
+ if (rcode < 0) {
2142
+ xmlErrorPtr error;
2143
+
2144
+ error = xmlGetLastError();
2145
+ if (error) {
2146
+ rb_exc_raise(Nokogiri_wrap_xml_syntax_error(error));
2147
+ } else {
2148
+ rb_raise(rb_eRuntimeError, "Could not perform xinclude substitution");
2149
+ }
2150
+ }
2151
+
2152
+ return self;
2153
+ }
2154
+
2155
+
2156
+ /* TODO: DOCUMENT ME */
2157
+ static VALUE
2158
+ in_context(VALUE self, VALUE _str, VALUE _options)
2159
+ {
2160
+ xmlNodePtr node, list = 0, tmp, child_iter, node_children, doc_children;
2161
+ xmlNodeSetPtr set;
2162
+ xmlParserErrors error;
2163
+ VALUE doc, err;
2164
+ int doc_is_empty;
2165
+
2166
+ Noko_Node_Get_Struct(self, xmlNode, node);
2167
+
2168
+ doc = DOC_RUBY_OBJECT(node->doc);
2169
+ err = rb_iv_get(doc, "@errors");
2170
+ doc_is_empty = (node->doc->children == NULL) ? 1 : 0;
2171
+ node_children = node->children;
2172
+ doc_children = node->doc->children;
2173
+
2174
+ xmlSetStructuredErrorFunc((void *)err, Nokogiri_error_array_pusher);
2175
+
2176
+ /* Twiddle global variable because of a bug in libxml2.
2177
+ * http://git.gnome.org/browse/libxml2/commit/?id=e20fb5a72c83cbfc8e4a8aa3943c6be8febadab7
2178
+ */
2179
+ #ifndef HTML_PARSE_NOIMPLIED
2180
+ htmlHandleOmittedElem(0);
2181
+ #endif
2182
+
2183
+ /* This function adds a fake node to the child of +node+. If the parser
2184
+ * does not exit cleanly with XML_ERR_OK, the list is freed. This can
2185
+ * leave the child pointers in a bad state if they were originally empty.
2186
+ *
2187
+ * http://git.gnome.org/browse/libxml2/tree/parser.c#n13177
2188
+ * */
2189
+ error = xmlParseInNodeContext(node, StringValuePtr(_str),
2190
+ (int)RSTRING_LEN(_str),
2191
+ (int)NUM2INT(_options), &list);
2192
+
2193
+ /* xmlParseInNodeContext should not mutate the original document or node,
2194
+ * so reassigning these pointers should be OK. The reason we're reassigning
2195
+ * is because if there were errors, it's possible for the child pointers
2196
+ * to be manipulated. */
2197
+ if (error != XML_ERR_OK) {
2198
+ node->doc->children = doc_children;
2199
+ node->children = node_children;
2200
+ }
2201
+
2202
+ /* make sure parent/child pointers are coherent so an unlink will work
2203
+ * properly (#331)
2204
+ */
2205
+ child_iter = node->doc->children ;
2206
+ while (child_iter) {
2207
+ child_iter->parent = (xmlNodePtr)node->doc;
2208
+ child_iter = child_iter->next;
2209
+ }
2210
+
2211
+ #ifndef HTML_PARSE_NOIMPLIED
2212
+ htmlHandleOmittedElem(1);
2213
+ #endif
2214
+
2215
+ xmlSetStructuredErrorFunc(NULL, NULL);
2216
+
2217
+ /*
2218
+ * Workaround for a libxml2 bug where a parsing error may leave a broken
2219
+ * node reference in node->doc->children.
2220
+ *
2221
+ * https://bugzilla.gnome.org/show_bug.cgi?id=668155
2222
+ *
2223
+ * This workaround is limited to when a parse error occurs, the document
2224
+ * went from having no children to having children, and the context node is
2225
+ * part of a document fragment.
2226
+ *
2227
+ * TODO: This was fixed in libxml 2.8.0 by 71a243d
2228
+ */
2229
+ if (error != XML_ERR_OK && doc_is_empty && node->doc->children != NULL) {
2230
+ child_iter = node;
2231
+ while (child_iter->parent) {
2232
+ child_iter = child_iter->parent;
2233
+ }
2234
+
2235
+ if (child_iter->type == XML_DOCUMENT_FRAG_NODE) {
2236
+ node->doc->children = NULL;
2237
+ }
2238
+ }
2239
+
2240
+ /* FIXME: This probably needs to handle more constants... */
2241
+ switch (error) {
2242
+ case XML_ERR_INTERNAL_ERROR:
2243
+ case XML_ERR_NO_MEMORY:
2244
+ rb_raise(rb_eRuntimeError, "error parsing fragment (%d)", error);
2245
+ break;
2246
+ default:
2247
+ break;
2248
+ }
2249
+
2250
+ set = xmlXPathNodeSetCreate(NULL);
2251
+
2252
+ while (list) {
2253
+ tmp = list->next;
2254
+ list->next = NULL;
2255
+ xmlXPathNodeSetAddUnique(set, list);
2256
+ noko_xml_document_pin_node(list);
2257
+ list = tmp;
2258
+ }
2259
+
2260
+ return noko_xml_node_set_wrap(set, doc);
2261
+ }
2262
+
2263
+ VALUE
2264
+ noko_xml_node_wrap(VALUE rb_class, xmlNodePtr c_node)
2265
+ {
2266
+ VALUE rb_document, rb_node_cache, rb_node;
2267
+ nokogiriTuplePtr node_has_a_document;
2268
+ xmlDocPtr c_doc;
2269
+
2270
+ assert(c_node);
2271
+
2272
+ if (c_node->type == XML_DOCUMENT_NODE || c_node->type == XML_HTML_DOCUMENT_NODE) {
2273
+ return DOC_RUBY_OBJECT(c_node->doc);
2274
+ }
2275
+
2276
+ c_doc = c_node->doc;
2277
+
2278
+ // Nodes yielded from XML::Reader don't have a fully-realized Document
2279
+ node_has_a_document = DOC_RUBY_OBJECT_TEST(c_doc);
2280
+
2281
+ if (c_node->_private && node_has_a_document) {
2282
+ return (VALUE)c_node->_private;
2283
+ }
2284
+
2285
+ if (!RTEST(rb_class)) {
2286
+ switch (c_node->type) {
2287
+ case XML_ELEMENT_NODE:
2288
+ rb_class = cNokogiriXmlElement;
2289
+ break;
2290
+ case XML_TEXT_NODE:
2291
+ rb_class = cNokogiriXmlText;
2292
+ break;
2293
+ case XML_ATTRIBUTE_NODE:
2294
+ rb_class = cNokogiriXmlAttr;
2295
+ break;
2296
+ case XML_ENTITY_REF_NODE:
2297
+ rb_class = cNokogiriXmlEntityReference;
2298
+ break;
2299
+ case XML_COMMENT_NODE:
2300
+ rb_class = cNokogiriXmlComment;
2301
+ break;
2302
+ case XML_DOCUMENT_FRAG_NODE:
2303
+ rb_class = cNokogiriXmlDocumentFragment;
2304
+ break;
2305
+ case XML_PI_NODE:
2306
+ rb_class = cNokogiriXmlProcessingInstruction;
2307
+ break;
2308
+ case XML_ENTITY_DECL:
2309
+ rb_class = cNokogiriXmlEntityDecl;
2310
+ break;
2311
+ case XML_CDATA_SECTION_NODE:
2312
+ rb_class = cNokogiriXmlCData;
2313
+ break;
2314
+ case XML_DTD_NODE:
2315
+ rb_class = cNokogiriXmlDtd;
2316
+ break;
2317
+ case XML_ATTRIBUTE_DECL:
2318
+ rb_class = cNokogiriXmlAttributeDecl;
2319
+ break;
2320
+ case XML_ELEMENT_DECL:
2321
+ rb_class = cNokogiriXmlElementDecl;
2322
+ break;
2323
+ default:
2324
+ rb_class = cNokogiriXmlNode;
2325
+ }
2326
+ }
2327
+
2328
+ rb_node = TypedData_Wrap_Struct(rb_class, &nokogiri_node_type, c_node) ;
2329
+ c_node->_private = (void *)rb_node;
2330
+
2331
+ if (node_has_a_document) {
2332
+ rb_document = DOC_RUBY_OBJECT(c_doc);
2333
+ rb_node_cache = DOC_NODE_CACHE(c_doc);
2334
+ rb_ary_push(rb_node_cache, rb_node);
2335
+ rb_funcall(rb_document, id_decorate, 1, rb_node);
2336
+ }
2337
+
2338
+ return rb_node ;
2339
+ }
2340
+
2341
+
2342
+ /*
2343
+ * return Array<Nokogiri::XML::Attr> containing the node's attributes
2344
+ */
2345
+ VALUE
2346
+ noko_xml_node_attrs(xmlNodePtr c_node)
2347
+ {
2348
+ VALUE rb_properties = rb_ary_new();
2349
+ xmlAttrPtr c_property;
2350
+
2351
+ c_property = c_node->properties ;
2352
+ while (c_property != NULL) {
2353
+ rb_ary_push(rb_properties, noko_xml_node_wrap(Qnil, (xmlNodePtr)c_property));
2354
+ c_property = c_property->next ;
2355
+ }
2356
+
2357
+ return rb_properties;
2358
+ }
2359
+
2360
+ void
2361
+ noko_init_xml_node(void)
2362
+ {
2363
+ cNokogiriXmlNode = rb_define_class_under(mNokogiriXml, "Node", rb_cObject);
2364
+
2365
+ rb_undef_alloc_func(cNokogiriXmlNode);
2366
+
2367
+ rb_define_singleton_method(cNokogiriXmlNode, "new", rb_xml_node_new, -1);
2368
+
2369
+ rb_define_method(cNokogiriXmlNode, "add_namespace_definition", rb_xml_node_add_namespace_definition, 2);
2370
+ rb_define_method(cNokogiriXmlNode, "attribute", rb_xml_node_attribute, 1);
2371
+ rb_define_method(cNokogiriXmlNode, "attribute_nodes", rb_xml_node_attribute_nodes, 0);
2372
+ rb_define_method(cNokogiriXmlNode, "attribute_with_ns", rb_xml_node_attribute_with_ns, 2);
2373
+ rb_define_method(cNokogiriXmlNode, "blank?", rb_xml_node_blank_eh, 0);
2374
+ rb_define_method(cNokogiriXmlNode, "child", rb_xml_node_child, 0);
2375
+ rb_define_method(cNokogiriXmlNode, "children", rb_xml_node_children, 0);
2376
+ rb_define_method(cNokogiriXmlNode, "content", rb_xml_node_content, 0);
2377
+ rb_define_method(cNokogiriXmlNode, "create_external_subset", create_external_subset, 3);
2378
+ rb_define_method(cNokogiriXmlNode, "create_internal_subset", create_internal_subset, 3);
2379
+ rb_define_method(cNokogiriXmlNode, "document", rb_xml_node_document, 0);
2380
+ rb_define_method(cNokogiriXmlNode, "dup", duplicate_node, -1);
2381
+ rb_define_method(cNokogiriXmlNode, "element_children", rb_xml_node_element_children, 0);
2382
+ rb_define_method(cNokogiriXmlNode, "encode_special_chars", encode_special_chars, 1);
2383
+ rb_define_method(cNokogiriXmlNode, "external_subset", external_subset, 0);
2384
+ rb_define_method(cNokogiriXmlNode, "first_element_child", rb_xml_node_first_element_child, 0);
2385
+ rb_define_method(cNokogiriXmlNode, "internal_subset", internal_subset, 0);
2386
+ rb_define_method(cNokogiriXmlNode, "key?", key_eh, 1);
2387
+ rb_define_method(cNokogiriXmlNode, "lang", get_lang, 0);
2388
+ rb_define_method(cNokogiriXmlNode, "lang=", set_lang, 1);
2389
+ rb_define_method(cNokogiriXmlNode, "last_element_child", rb_xml_node_last_element_child, 0);
2390
+ rb_define_method(cNokogiriXmlNode, "line", rb_xml_node_line, 0);
2391
+ rb_define_method(cNokogiriXmlNode, "line=", rb_xml_node_line_set, 1);
2392
+ rb_define_method(cNokogiriXmlNode, "namespace", rb_xml_node_namespace, 0);
2393
+ rb_define_method(cNokogiriXmlNode, "namespace_definitions", namespace_definitions, 0);
2394
+ rb_define_method(cNokogiriXmlNode, "namespace_scopes", rb_xml_node_namespace_scopes, 0);
2395
+ rb_define_method(cNokogiriXmlNode, "namespaced_key?", namespaced_key_eh, 2);
2396
+ rb_define_method(cNokogiriXmlNode, "native_content=", set_native_content, 1);
2397
+ rb_define_method(cNokogiriXmlNode, "next_element", next_element, 0);
2398
+ rb_define_method(cNokogiriXmlNode, "next_sibling", next_sibling, 0);
2399
+ rb_define_method(cNokogiriXmlNode, "node_name", get_name, 0);
2400
+ rb_define_method(cNokogiriXmlNode, "node_name=", set_name, 1);
2401
+ rb_define_method(cNokogiriXmlNode, "node_type", node_type, 0);
2402
+ rb_define_method(cNokogiriXmlNode, "parent", get_parent, 0);
2403
+ rb_define_method(cNokogiriXmlNode, "path", rb_xml_node_path, 0);
2404
+ rb_define_method(cNokogiriXmlNode, "pointer_id", rb_xml_node_pointer_id, 0);
2405
+ rb_define_method(cNokogiriXmlNode, "previous_element", previous_element, 0);
2406
+ rb_define_method(cNokogiriXmlNode, "previous_sibling", previous_sibling, 0);
2407
+ rb_define_method(cNokogiriXmlNode, "unlink", unlink_node, 0);
2408
+
2409
+ rb_define_private_method(cNokogiriXmlNode, "add_child_node", add_child, 1);
2410
+ rb_define_private_method(cNokogiriXmlNode, "add_next_sibling_node", add_next_sibling, 1);
2411
+ rb_define_private_method(cNokogiriXmlNode, "add_previous_sibling_node", add_previous_sibling, 1);
2412
+ rb_define_private_method(cNokogiriXmlNode, "compare", compare, 1);
2413
+ rb_define_private_method(cNokogiriXmlNode, "dump_html", dump_html, 0);
2414
+ rb_define_private_method(cNokogiriXmlNode, "get", get, 1);
2415
+ rb_define_private_method(cNokogiriXmlNode, "in_context", in_context, 2);
2416
+ rb_define_private_method(cNokogiriXmlNode, "native_write_to", native_write_to, 4);
2417
+ rb_define_private_method(cNokogiriXmlNode, "prepend_newline?", rb_prepend_newline, 0);
2418
+ rb_define_private_method(cNokogiriXmlNode, "html_standard_serialize", html_standard_serialize, 1);
2419
+ rb_define_private_method(cNokogiriXmlNode, "process_xincludes", process_xincludes, 1);
2420
+ rb_define_private_method(cNokogiriXmlNode, "replace_node", replace, 1);
2421
+ rb_define_private_method(cNokogiriXmlNode, "set", set, 2);
2422
+ rb_define_private_method(cNokogiriXmlNode, "set_namespace", set_namespace, 1);
2423
+
2424
+ id_decorate = rb_intern("decorate");
2425
+ id_decorate_bang = rb_intern("decorate!");
2426
+ }