nokogiri 1.19.4 → 1.19.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE-DEPENDENCIES.md +29 -0
- data/ext/nokogiri/extconf.rb +1 -0
- data/ext/nokogiri/gumbo.c +83 -6
- data/ext/nokogiri/html4_document.c +10 -3
- data/ext/nokogiri/libxml2_polyfill.c +12 -0
- data/ext/nokogiri/nokogiri.c +84 -2
- data/ext/nokogiri/nokogiri.h +14 -1
- data/ext/nokogiri/xml_document.c +42 -16
- data/ext/nokogiri/xml_node.c +73 -38
- data/ext/nokogiri/xml_node_set.c +9 -9
- data/ext/nokogiri/xml_reader.c +198 -107
- data/ext/nokogiri/xml_sax_parser.c +3 -3
- data/ext/nokogiri/xml_sax_parser_context.c +38 -7
- data/ext/nokogiri/xml_sax_push_parser.c +12 -0
- data/ext/nokogiri/xml_xpath_context.c +124 -26
- data/ext/nokogiri/xslt_stylesheet.c +123 -27
- data/gumbo-parser/src/Makefile +2 -0
- data/gumbo-parser/src/hashmap.c +1154 -0
- data/gumbo-parser/src/hashmap.h +62 -0
- data/gumbo-parser/src/parser.c +49 -13
- data/gumbo-parser/src/string_set.c +41 -0
- data/gumbo-parser/src/string_set.h +21 -0
- data/gumbo-parser/src/tokenizer.c +49 -19
- data/lib/nokogiri/css/parser.rb +35 -18
- data/lib/nokogiri/css/parser.y +35 -17
- data/lib/nokogiri/css/xpath_visitor.rb +71 -28
- data/lib/nokogiri/html5/document.rb +9 -0
- data/lib/nokogiri/html5/document_fragment.rb +16 -1
- data/lib/nokogiri/html5/node.rb +1 -1
- data/lib/nokogiri/html5.rb +16 -0
- data/lib/nokogiri/version/constant.rb +1 -1
- data/lib/nokogiri/xml/attr.rb +2 -0
- data/lib/nokogiri/xml/attribute_decl.rb +2 -1
- data/lib/nokogiri/xml/builder.rb +24 -32
- data/lib/nokogiri/xml/document.rb +1 -0
- data/lib/nokogiri/xml/dtd.rb +2 -1
- data/lib/nokogiri/xml/element_decl.rb +2 -1
- data/lib/nokogiri/xml/entity_decl.rb +2 -1
- data/lib/nokogiri/xml/node.rb +8 -6
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 30ead164124146485c9fb1f09b065cd2fdbf2cfa70e37c2752e1bf01ef1515d2
|
|
4
|
+
data.tar.gz: 926476a04dc45804be15b5869b71be848d8bb250dc58b5d40bde5822351172cb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: de09c938ce5be1f9b507a5169b68617847ebb005163430c7978978693b4a9055e76324d1bc3ef428f23526ab82ad9a23597a29e2a289975fff9a02558d97139c
|
|
7
|
+
data.tar.gz: 3a8397e8d45312a2db1237e24a4a7d64d777fba54a9c9030f0c5895fc3145f4fcd7d471b1aa4cc9c8d3e5187bf41f078120f6f50028a8e51e280d667976c9612
|
data/LICENSE-DEPENDENCIES.md
CHANGED
|
@@ -15,6 +15,7 @@ Note that this document is broken into multiple sections, each of which describe
|
|
|
15
15
|
* [Native WindowsⓇ platform releases ("x64-mingw-ucrt")](#native-windows%E2%93%A1-platform-releases-x64-mingw-ucrt)
|
|
16
16
|
* [JavaⓇ (JRuby) platform release ("java")](#java%E2%93%A1-jruby-platform-release-java)
|
|
17
17
|
- [Appendix: Dependencies' License Texts](#appendix-dependencies-license-texts)
|
|
18
|
+
* [hashmap.c](#hashmapc)
|
|
18
19
|
* [libgumbo](#libgumbo)
|
|
19
20
|
* [libxml2](#libxml2)
|
|
20
21
|
* [libxslt](#libxslt)
|
|
@@ -114,6 +115,34 @@ This section contains a subsection for each potentially-distributed dependency,
|
|
|
114
115
|
Please see previous sections to understand which of these potential dependencies is actually distributed in the gem file you're downloading and using.
|
|
115
116
|
|
|
116
117
|
|
|
118
|
+
### hashmap.c
|
|
119
|
+
|
|
120
|
+
MIT
|
|
121
|
+
|
|
122
|
+
https://github.com/tidwall/hashmap.c/blob/master/LICENSE
|
|
123
|
+
|
|
124
|
+
The MIT License (MIT)
|
|
125
|
+
|
|
126
|
+
Copyright (c) 2020 Joshua J Baker
|
|
127
|
+
|
|
128
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
129
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
130
|
+
the Software without restriction, including without limitation the rights to
|
|
131
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
132
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
133
|
+
subject to the following conditions:
|
|
134
|
+
|
|
135
|
+
The above copyright notice and this permission notice shall be included in all
|
|
136
|
+
copies or substantial portions of the Software.
|
|
137
|
+
|
|
138
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
139
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
140
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
141
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
142
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
143
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
144
|
+
|
|
145
|
+
|
|
117
146
|
### libgumbo
|
|
118
147
|
|
|
119
148
|
Apache 2.0
|
data/ext/nokogiri/extconf.rb
CHANGED
|
@@ -1125,6 +1125,7 @@ end
|
|
|
1125
1125
|
have_func("xmlCtxtSetOptions") # introduced in libxml2 2.13.0
|
|
1126
1126
|
have_func("xmlCtxtGetOptions") # introduced in libxml2 2.14.0
|
|
1127
1127
|
have_func("xmlSwitchEncodingName") # introduced in libxml2 2.13.0
|
|
1128
|
+
have_func("xmlAddIDSafe") # introduced in libxml2 2.13.0
|
|
1128
1129
|
have_func("rb_category_warning") # introduced in Ruby 3.0 but had trouble resolving this symbol in truffleruby
|
|
1129
1130
|
|
|
1130
1131
|
other_library_versions_string = OTHER_LIBRARY_VERSIONS.map { |k, v| [k, v].join(":") }.join(",")
|
data/ext/nokogiri/gumbo.c
CHANGED
|
@@ -113,6 +113,71 @@ set_line(xmlNodePtr node, size_t line)
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// This function is essentially xmlNewNsProp, but we skip the full list traversal to append by
|
|
117
|
+
// providing the last property in the linked list as a parameter.
|
|
118
|
+
static xmlAttrPtr
|
|
119
|
+
append_property(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name, const xmlChar *value, xmlAttrPtr last_prop)
|
|
120
|
+
{
|
|
121
|
+
xmlAttrPtr cur = (xmlAttrPtr) xmlMalloc(sizeof(xmlAttr));
|
|
122
|
+
if (cur == NULL) {
|
|
123
|
+
return NULL;
|
|
124
|
+
}
|
|
125
|
+
memset(cur, 0, sizeof(xmlAttr));
|
|
126
|
+
cur->type = XML_ATTRIBUTE_NODE;
|
|
127
|
+
cur->parent = node;
|
|
128
|
+
xmlDocPtr doc = node->doc;
|
|
129
|
+
cur->doc = doc;
|
|
130
|
+
cur->ns = ns;
|
|
131
|
+
|
|
132
|
+
if ((doc != NULL) && (doc->dict != NULL)) {
|
|
133
|
+
cur->name = (xmlChar *) xmlDictLookup(doc->dict, name, -1);
|
|
134
|
+
} else {
|
|
135
|
+
cur->name = xmlStrdup(name);
|
|
136
|
+
}
|
|
137
|
+
if (cur->name == NULL) {
|
|
138
|
+
goto error;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (value != NULL) {
|
|
142
|
+
cur->children = xmlNewDocText(doc, value);
|
|
143
|
+
if (cur->children == NULL) {
|
|
144
|
+
goto error;
|
|
145
|
+
}
|
|
146
|
+
cur->last = NULL;
|
|
147
|
+
xmlNodePtr tmp = cur->children;
|
|
148
|
+
while (tmp != NULL) {
|
|
149
|
+
tmp->parent = (xmlNodePtr) cur;
|
|
150
|
+
if (tmp->next == NULL) {
|
|
151
|
+
cur->last = tmp;
|
|
152
|
+
}
|
|
153
|
+
tmp = tmp->next;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (doc != NULL) {
|
|
157
|
+
int res = xmlIsID(doc, node, cur);
|
|
158
|
+
if (res < 0) {
|
|
159
|
+
goto error;
|
|
160
|
+
}
|
|
161
|
+
if ((res == 1) && (xmlAddIDSafe(cur, value) < 0)) {
|
|
162
|
+
goto error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (node->properties == NULL) {
|
|
168
|
+
node->properties = cur;
|
|
169
|
+
} else {
|
|
170
|
+
last_prop->next = cur;
|
|
171
|
+
cur->prev = last_prop;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return cur;
|
|
175
|
+
|
|
176
|
+
error:
|
|
177
|
+
xmlFreeProp(cur);
|
|
178
|
+
return (NULL);
|
|
179
|
+
}
|
|
180
|
+
|
|
116
181
|
// Construct an XML tree rooted at xml_output_node from the Gumbo tree rooted
|
|
117
182
|
// at gumbo_node.
|
|
118
183
|
static void
|
|
@@ -200,6 +265,7 @@ build_tree(
|
|
|
200
265
|
xmlAddChild(xml_node, xml_child);
|
|
201
266
|
|
|
202
267
|
// Add the attributes.
|
|
268
|
+
xmlAttrPtr last_prop = NULL;
|
|
203
269
|
const GumboVector *attrs = &gumbo_child->v.element.attributes;
|
|
204
270
|
for (size_t i = 0; i < attrs->length; i++) {
|
|
205
271
|
const GumboAttribute *attr = attrs->data[i];
|
|
@@ -220,7 +286,9 @@ build_tree(
|
|
|
220
286
|
default:
|
|
221
287
|
ns = NULL;
|
|
222
288
|
}
|
|
223
|
-
|
|
289
|
+
|
|
290
|
+
// We micromanage the attribute list for performance reasons.
|
|
291
|
+
last_prop = append_property(xml_child, ns, (const xmlChar *)attr->name, (const xmlChar *)attr->value, last_prop);
|
|
224
292
|
}
|
|
225
293
|
|
|
226
294
|
// Add children for this element.
|
|
@@ -373,12 +441,20 @@ parse_continue(VALUE parse_args)
|
|
|
373
441
|
} else {
|
|
374
442
|
doc = new_html_doc(NULL, NULL, NULL);
|
|
375
443
|
}
|
|
376
|
-
|
|
444
|
+
|
|
445
|
+
// We're about to build the libxml2 tree structure from the Gumbo tree
|
|
446
|
+
// structure. If an exception is raised during tree building, control passes
|
|
447
|
+
// to `parse_cleanup()` which needs to free the newly allocated `xmlDoc`,
|
|
448
|
+
// `doc`. If the tree is successfully constructed, then
|
|
449
|
+
// `noko_xml_document_wrap()` will create a new Ruby object which will own
|
|
450
|
+
// `doc`. In that case, `parse_cleanup()` should not free `doc`.
|
|
451
|
+
args->doc = doc;
|
|
377
452
|
build_tree(doc, (xmlNodePtr)doc, output->document);
|
|
453
|
+
args->doc = NULL;
|
|
378
454
|
VALUE rdoc = noko_xml_document_wrap(args->klass, doc);
|
|
455
|
+
|
|
379
456
|
rb_iv_set(rdoc, "@url", args->url_or_frag);
|
|
380
457
|
rb_iv_set(rdoc, "@quirks_mode", INT2NUM(output->document->v.document.doc_type_quirks_mode));
|
|
381
|
-
args->doc = NULL; // The Ruby runtime now owns doc so don't delete it.
|
|
382
458
|
add_errors(output, rdoc, args->input, args->url_or_frag);
|
|
383
459
|
return rdoc;
|
|
384
460
|
}
|
|
@@ -512,6 +588,7 @@ error:
|
|
|
512
588
|
continue;
|
|
513
589
|
}
|
|
514
590
|
VALUE element_name = rb_funcall(node, name, 0);
|
|
591
|
+
Check_Type(element_name, T_STRING);
|
|
515
592
|
if (RSTRING_LEN(element_name) == 4
|
|
516
593
|
&& !st_strcasecmp(RSTRING_PTR(element_name), "form")
|
|
517
594
|
&& lookup_namespace(node, false) == GUMBO_NAMESPACE_HTML) {
|
|
@@ -568,7 +645,7 @@ error:
|
|
|
568
645
|
.output = output,
|
|
569
646
|
.input = tags,
|
|
570
647
|
.url_or_frag = doc_fragment,
|
|
571
|
-
.doc =
|
|
648
|
+
.doc = NULL,
|
|
572
649
|
};
|
|
573
650
|
rb_ensure(fragment_continue, (VALUE)(&args), parse_cleanup, (VALUE)(&args));
|
|
574
651
|
return Qnil;
|
|
@@ -580,9 +657,9 @@ fragment_continue(VALUE parse_args)
|
|
|
580
657
|
ParseArgs *args = (ParseArgs *)parse_args;
|
|
581
658
|
GumboOutput *output = args->output;
|
|
582
659
|
VALUE doc_fragment = args->url_or_frag;
|
|
583
|
-
|
|
660
|
+
VALUE doc = rb_funcall(doc_fragment, rb_intern_const("document"), 0);
|
|
661
|
+
xmlDocPtr xml_doc = (xmlDocPtr)extract_xml_node(doc);
|
|
584
662
|
|
|
585
|
-
args->doc = NULL; // The Ruby runtime owns doc so make sure we don't delete it.
|
|
586
663
|
xmlNodePtr xml_frag = extract_xml_node(doc_fragment);
|
|
587
664
|
build_tree(xml_doc, xml_frag, output->root);
|
|
588
665
|
rb_iv_set(doc_fragment, "@quirks_mode", INT2NUM(output->document->v.document.doc_type_quirks_mode));
|
|
@@ -18,8 +18,8 @@ rb_html_document_s_new(int argc, VALUE *argv, VALUE klass)
|
|
|
18
18
|
htmlDocPtr doc;
|
|
19
19
|
|
|
20
20
|
rb_scan_args(argc, argv, "0*", &rest);
|
|
21
|
-
uri = rb_ary_entry(rest, (
|
|
22
|
-
external_id = rb_ary_entry(rest, (
|
|
21
|
+
uri = rb_ary_entry(rest, (rb_len_t)0);
|
|
22
|
+
external_id = rb_ary_entry(rest, (rb_len_t)1);
|
|
23
23
|
|
|
24
24
|
doc = htmlNewDoc(
|
|
25
25
|
RTEST(uri) ? (const xmlChar *)StringValueCStr(uri) : NULL,
|
|
@@ -48,10 +48,17 @@ rb_html_document_s_read_io(VALUE klass, VALUE rb_io, VALUE rb_url, VALUE rb_enco
|
|
|
48
48
|
|
|
49
49
|
xmlSetStructuredErrorFunc((void *)rb_error_list, noko__error_array_pusher);
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
noko_io_read_reset_exception();
|
|
52
|
+
c_doc = htmlReadIO(noko_io_read_protected, noko_io_close, (void *)rb_io, c_url, c_encoding, options);
|
|
53
|
+
VALUE rb_io_read_exception = noko_io_read_take_exception();
|
|
52
54
|
|
|
53
55
|
xmlSetStructuredErrorFunc(NULL, NULL);
|
|
54
56
|
|
|
57
|
+
if (!NIL_P(rb_io_read_exception)) {
|
|
58
|
+
xmlFreeDoc(c_doc);
|
|
59
|
+
noko_io_read_raise_exception(rb_io_read_exception);
|
|
60
|
+
}
|
|
61
|
+
|
|
55
62
|
/*
|
|
56
63
|
* If EncodingFound has occurred in EncodingReader, make sure to do
|
|
57
64
|
* a cleanup and propagate the error.
|
|
@@ -112,3 +112,15 @@ xmlSwitchEncodingName(xmlParserCtxtPtr ctxt, const char *encoding)
|
|
|
112
112
|
return (xmlSwitchToEncoding(ctxt, handler));
|
|
113
113
|
}
|
|
114
114
|
#endif
|
|
115
|
+
|
|
116
|
+
#ifndef HAVE_XMLADDIDSAFE
|
|
117
|
+
int
|
|
118
|
+
xmlAddIDSafe(xmlAttrPtr attr, const xmlChar *value)
|
|
119
|
+
{
|
|
120
|
+
xmlIDPtr id = xmlAddID(NULL, attr->doc, value, attr);
|
|
121
|
+
if (id) {
|
|
122
|
+
return 1;
|
|
123
|
+
}
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
#endif
|
data/ext/nokogiri/nokogiri.c
CHANGED
|
@@ -50,7 +50,7 @@ void noko_init_html4_sax_parser(void);
|
|
|
50
50
|
void noko_init_gumbo(void);
|
|
51
51
|
void noko_init_test_global_handlers(void);
|
|
52
52
|
|
|
53
|
-
static ID id_read, id_write, id_external_encoding;
|
|
53
|
+
static ID id_read, id_write, id_external_encoding, id_io_read_exception;
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
static VALUE
|
|
@@ -92,6 +92,87 @@ noko_io_read(void *io, char *c_buffer, int c_buffer_len)
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
typedef struct {
|
|
96
|
+
void *io;
|
|
97
|
+
char *c_buffer;
|
|
98
|
+
int c_buffer_len;
|
|
99
|
+
int n_bytes_read;
|
|
100
|
+
} noko_io_read_args_t;
|
|
101
|
+
|
|
102
|
+
static VALUE
|
|
103
|
+
noko_io_read_call(VALUE data)
|
|
104
|
+
{
|
|
105
|
+
noko_io_read_args_t *args = (noko_io_read_args_t *)data;
|
|
106
|
+
args->n_bytes_read = noko_io_read(args->io, args->c_buffer, args->c_buffer_len);
|
|
107
|
+
return Qnil;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
/*
|
|
112
|
+
* Like noko_io_read, a StandardError raised by IO#read is swallowed, and libxml2 sees a read
|
|
113
|
+
* error. Any other exception (e.g., Interrupt or Timeout::ExitException) must not unwind through
|
|
114
|
+
* libxml2's stack frames, because that skips libxml2's cleanup and leaks memory, and skips the
|
|
115
|
+
* reset of the error handlers. So it is saved in a fiber-local variable, and $! is cleared until
|
|
116
|
+
* it is re-raised.
|
|
117
|
+
*
|
|
118
|
+
* A caller of libxml2 that passes this callback must call noko_io_read_reset_exception before
|
|
119
|
+
* calling libxml2, and noko_io_read_take_exception after libxml2 returns, and must re-raise a
|
|
120
|
+
* non-nil result with noko_io_read_raise_exception.
|
|
121
|
+
*/
|
|
122
|
+
int
|
|
123
|
+
noko_io_read_protected(void *io, char *c_buffer, int c_buffer_len)
|
|
124
|
+
{
|
|
125
|
+
noko_io_read_args_t args = { io, c_buffer, c_buffer_len, -1 };
|
|
126
|
+
int state = 0;
|
|
127
|
+
|
|
128
|
+
rb_protect(noko_io_read_call, (VALUE)&args, &state);
|
|
129
|
+
|
|
130
|
+
if (state) {
|
|
131
|
+
VALUE rb_errinfo_value = rb_errinfo();
|
|
132
|
+
if (RB_TYPE_P(rb_errinfo_value, T_OBJECT) && rb_obj_is_kind_of(rb_errinfo_value, rb_eException)) {
|
|
133
|
+
rb_set_errinfo(Qnil);
|
|
134
|
+
} else {
|
|
135
|
+
rb_errinfo_value = Qnil;
|
|
136
|
+
}
|
|
137
|
+
rb_thread_local_aset(rb_thread_current(), id_io_read_exception,
|
|
138
|
+
rb_assoc_new(INT2FIX(state), rb_errinfo_value));
|
|
139
|
+
return -1;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return args.n_bytes_read;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
void
|
|
147
|
+
noko_io_read_reset_exception(void)
|
|
148
|
+
{
|
|
149
|
+
rb_thread_local_aset(rb_thread_current(), id_io_read_exception, Qnil);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
VALUE
|
|
154
|
+
noko_io_read_take_exception(void)
|
|
155
|
+
{
|
|
156
|
+
VALUE rb_thread = rb_thread_current();
|
|
157
|
+
VALUE rb_exception = rb_thread_local_aref(rb_thread, id_io_read_exception);
|
|
158
|
+
|
|
159
|
+
rb_thread_local_aset(rb_thread, id_io_read_exception, Qnil);
|
|
160
|
+
return rb_exception;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
void
|
|
165
|
+
noko_io_read_raise_exception(VALUE rb_exception)
|
|
166
|
+
{
|
|
167
|
+
VALUE rb_errinfo_value = rb_ary_entry(rb_exception, 1);
|
|
168
|
+
|
|
169
|
+
if (!NIL_P(rb_errinfo_value)) {
|
|
170
|
+
rb_set_errinfo(rb_errinfo_value);
|
|
171
|
+
}
|
|
172
|
+
rb_jump_tag(FIX2INT(rb_ary_entry(rb_exception, 0)));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
95
176
|
static VALUE
|
|
96
177
|
noko_io_write_check(VALUE rb_args)
|
|
97
178
|
{
|
|
@@ -122,7 +203,7 @@ noko_io_write(void *io, char *c_buffer, int c_buffer_len)
|
|
|
122
203
|
io_encoding = RB_NIL_P(rb_enc) ? rb_ascii8bit_encoding() : rb_to_encoding(rb_enc);
|
|
123
204
|
|
|
124
205
|
rb_args[0] = rb_io;
|
|
125
|
-
rb_args[1] = rb_enc_str_new(c_buffer, (
|
|
206
|
+
rb_args[1] = rb_enc_str_new(c_buffer, (rb_len_t)c_buffer_len, io_encoding);
|
|
126
207
|
|
|
127
208
|
rb_n_bytes_written = rb_rescue(noko_io_write_check, (VALUE)rb_args, noko_io_write_failed, 0);
|
|
128
209
|
if (rb_n_bytes_written == Qundef) { return -1; }
|
|
@@ -292,6 +373,7 @@ Init_nokogiri(void)
|
|
|
292
373
|
noko_init_test_global_handlers();
|
|
293
374
|
|
|
294
375
|
id_read = rb_intern("read");
|
|
376
|
+
id_io_read_exception = rb_intern("__nokogiri_io_read_exception");
|
|
295
377
|
id_write = rb_intern("write");
|
|
296
378
|
id_external_encoding = rb_intern("external_encoding");
|
|
297
379
|
}
|
data/ext/nokogiri/nokogiri.h
CHANGED
|
@@ -66,6 +66,9 @@ int xmlCtxtGetOptions(xmlParserCtxtPtr ctxt);
|
|
|
66
66
|
#ifndef HAVE_XMLSWITCHENCODINGNAME
|
|
67
67
|
int xmlSwitchEncodingName(xmlParserCtxtPtr ctxt, const char *encoding);
|
|
68
68
|
#endif
|
|
69
|
+
#ifndef HAVE_XMLADDIDSAFE
|
|
70
|
+
int xmlAddIDSafe(xmlAttrPtr attr, const xmlChar *value);
|
|
71
|
+
#endif
|
|
69
72
|
|
|
70
73
|
#define XMLNS_PREFIX "xmlns"
|
|
71
74
|
#define XMLNS_PREFIX_LEN 6 /* including either colon or \0 */
|
|
@@ -85,7 +88,7 @@ int xmlSwitchEncodingName(xmlParserCtxtPtr ctxt, const char *encoding);
|
|
|
85
88
|
#include <ruby/version.h>
|
|
86
89
|
|
|
87
90
|
#define NOKOGIRI_STR_NEW2(str) NOKOGIRI_STR_NEW(str, strlen((const char *)(str)))
|
|
88
|
-
#define NOKOGIRI_STR_NEW(str, len) rb_external_str_new_with_enc((const char *)(str), (
|
|
91
|
+
#define NOKOGIRI_STR_NEW(str, len) rb_external_str_new_with_enc((const char *)(str), (rb_len_t)(len), rb_utf8_encoding())
|
|
89
92
|
#define RBSTR_OR_QNIL(_str) (_str ? NOKOGIRI_STR_NEW2(_str) : Qnil)
|
|
90
93
|
|
|
91
94
|
#ifndef NORETURN_DECL
|
|
@@ -178,6 +181,10 @@ void noko_xml_document_pin_namespace(xmlNsPtr, xmlDocPtr);
|
|
|
178
181
|
int noko_xml_document_has_wrapped_blank_nodes_p(xmlDocPtr c_document);
|
|
179
182
|
|
|
180
183
|
int noko_io_read(void *ctx, char *buffer, int len);
|
|
184
|
+
int noko_io_read_protected(void *ctx, char *buffer, int len);
|
|
185
|
+
void noko_io_read_reset_exception(void);
|
|
186
|
+
VALUE noko_io_read_take_exception(void);
|
|
187
|
+
NORETURN_DECL void noko_io_read_raise_exception(VALUE rb_exception);
|
|
181
188
|
int noko_io_write(void *ctx, char *buffer, int len);
|
|
182
189
|
int noko_io_close(void *ctx);
|
|
183
190
|
|
|
@@ -234,6 +241,12 @@ void noko_xml_sax_parser_context_set_encoding(xmlParserCtxtPtr c_context, VALUE
|
|
|
234
241
|
# define NOKO_WARN_DEPRECATION(message...) rb_warning(message)
|
|
235
242
|
#endif
|
|
236
243
|
|
|
244
|
+
#ifndef HAVE_RB_LEN_T
|
|
245
|
+
typedef long rb_len_t;
|
|
246
|
+
#define RB_NUM2LEN RB_NUM2LONG
|
|
247
|
+
#define RB_FIX2LEN RB_FIX2LONG
|
|
248
|
+
#endif
|
|
249
|
+
|
|
237
250
|
void noko__structured_error_func_save(libxmlStructuredErrorHandlerState *handler_state);
|
|
238
251
|
void noko__structured_error_func_save_and_set(libxmlStructuredErrorHandlerState *handler_state, void *user_data,
|
|
239
252
|
xmlStructuredErrorFunc handler);
|
data/ext/nokogiri/xml_document.c
CHANGED
|
@@ -384,17 +384,24 @@ noko_xml_document_s_read_io(VALUE rb_class,
|
|
|
384
384
|
|
|
385
385
|
const char *c_url = NIL_P(rb_url) ? NULL : StringValueCStr(rb_url);
|
|
386
386
|
const char *c_enc = NIL_P(rb_encoding) ? NULL : StringValueCStr(rb_encoding);
|
|
387
|
+
noko_io_read_reset_exception();
|
|
387
388
|
xmlDocPtr c_document = xmlReadIO(
|
|
388
|
-
(xmlInputReadCallback)
|
|
389
|
+
(xmlInputReadCallback)noko_io_read_protected,
|
|
389
390
|
(xmlInputCloseCallback)noko_io_close,
|
|
390
391
|
(void *)rb_io,
|
|
391
392
|
c_url,
|
|
392
393
|
c_enc,
|
|
393
394
|
(int)NUM2INT(rb_options)
|
|
394
395
|
);
|
|
396
|
+
VALUE rb_io_read_exception = noko_io_read_take_exception();
|
|
395
397
|
|
|
396
398
|
noko__structured_error_func_restore(&handler_state);
|
|
397
399
|
|
|
400
|
+
if (!NIL_P(rb_io_read_exception)) {
|
|
401
|
+
xmlFreeDoc(c_document);
|
|
402
|
+
noko_io_read_raise_exception(rb_io_read_exception);
|
|
403
|
+
}
|
|
404
|
+
|
|
398
405
|
if (c_document == NULL) {
|
|
399
406
|
xmlFreeDoc(c_document);
|
|
400
407
|
|
|
@@ -464,7 +471,7 @@ new (int argc, VALUE *argv, VALUE klass)
|
|
|
464
471
|
VALUE version, rest, rb_doc ;
|
|
465
472
|
|
|
466
473
|
rb_scan_args(argc, argv, "0*", &rest);
|
|
467
|
-
version = rb_ary_entry(rest, (
|
|
474
|
+
version = rb_ary_entry(rest, (rb_len_t)0);
|
|
468
475
|
if (NIL_P(version)) { version = rb_str_new2("1.0"); }
|
|
469
476
|
|
|
470
477
|
doc = xmlNewDoc((xmlChar *)StringValueCStr(version));
|
|
@@ -609,7 +616,8 @@ rb_xml_document_canonicalize(int argc, VALUE *argv, VALUE self)
|
|
|
609
616
|
VALUE rb_namespaces;
|
|
610
617
|
VALUE rb_comments_p;
|
|
611
618
|
int c_mode = 0;
|
|
612
|
-
xmlChar **c_namespaces;
|
|
619
|
+
xmlChar **c_namespaces = NULL;
|
|
620
|
+
VALUE rb_namespaces_buffer = 0;
|
|
613
621
|
|
|
614
622
|
xmlDocPtr c_doc;
|
|
615
623
|
xmlOutputBufferPtr c_obuf;
|
|
@@ -629,6 +637,35 @@ rb_xml_document_canonicalize(int argc, VALUE *argv, VALUE self)
|
|
|
629
637
|
if (c_mode == XML_C14N_1_0 || c_mode == XML_C14N_1_1) {
|
|
630
638
|
rb_raise(rb_eRuntimeError, "This canonicalizer does not support this operation");
|
|
631
639
|
}
|
|
640
|
+
|
|
641
|
+
// Copy every prefix into one temporary buffer before libxml2 sees it. Converting first means a
|
|
642
|
+
// raise from #to_str or a null byte leaks nothing. Copying means GC compaction, which moves the
|
|
643
|
+
// bytes of embedded strings, cannot move a prefix while libxml2 reads it. No allocation may
|
|
644
|
+
// happen between reading a prefix's pointer and copying its bytes.
|
|
645
|
+
rb_len_t ns_len = RARRAY_LEN(rb_namespaces);
|
|
646
|
+
VALUE rb_prefixes = rb_ary_new_capa(ns_len);
|
|
647
|
+
for (rb_len_t j = 0; j < ns_len; j++) {
|
|
648
|
+
VALUE entry = rb_ary_entry(rb_namespaces, j);
|
|
649
|
+
StringValueCStr(entry);
|
|
650
|
+
rb_ary_push(rb_prefixes, entry);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
size_t size = ((size_t)ns_len + 1) * sizeof(xmlChar *);
|
|
654
|
+
for (rb_len_t j = 0; j < ns_len; j++) {
|
|
655
|
+
size += (size_t)RSTRING_LEN(RARRAY_AREF(rb_prefixes, j)) + 1;
|
|
656
|
+
}
|
|
657
|
+
c_namespaces = ALLOCV(rb_namespaces_buffer, size);
|
|
658
|
+
|
|
659
|
+
xmlChar *c_prefix = (xmlChar *)(c_namespaces + ns_len + 1);
|
|
660
|
+
for (rb_len_t j = 0; j < ns_len; j++) {
|
|
661
|
+
VALUE prefix = RARRAY_AREF(rb_prefixes, j);
|
|
662
|
+
size_t prefix_len = (size_t)RSTRING_LEN(prefix);
|
|
663
|
+
memcpy(c_prefix, RSTRING_PTR(prefix), prefix_len);
|
|
664
|
+
c_prefix[prefix_len] = '\0';
|
|
665
|
+
c_namespaces[j] = c_prefix;
|
|
666
|
+
c_prefix += prefix_len + 1;
|
|
667
|
+
}
|
|
668
|
+
c_namespaces[ns_len] = NULL;
|
|
632
669
|
}
|
|
633
670
|
|
|
634
671
|
c_doc = noko_xml_document_unwrap(self);
|
|
@@ -646,24 +683,13 @@ rb_xml_document_canonicalize(int argc, VALUE *argv, VALUE self)
|
|
|
646
683
|
rb_callback = (void *)rb_block_proc();
|
|
647
684
|
}
|
|
648
685
|
|
|
649
|
-
if (NIL_P(rb_namespaces)) {
|
|
650
|
-
c_namespaces = NULL;
|
|
651
|
-
} else {
|
|
652
|
-
long ns_len = RARRAY_LEN(rb_namespaces);
|
|
653
|
-
c_namespaces = ruby_xcalloc((size_t)ns_len + 1, sizeof(xmlChar *));
|
|
654
|
-
for (int j = 0 ; j < ns_len ; j++) {
|
|
655
|
-
VALUE entry = rb_ary_entry(rb_namespaces, j);
|
|
656
|
-
c_namespaces[j] = (xmlChar *)StringValueCStr(entry);
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
686
|
int ret = xmlC14NExecute(c_doc, c_callback_wrapper, rb_callback,
|
|
661
687
|
c_mode,
|
|
662
688
|
c_namespaces,
|
|
663
689
|
(int)RTEST(rb_comments_p),
|
|
664
690
|
c_obuf);
|
|
665
691
|
|
|
666
|
-
|
|
692
|
+
ALLOCV_END(rb_namespaces_buffer);
|
|
667
693
|
xmlOutputBufferClose(c_obuf);
|
|
668
694
|
|
|
669
695
|
if (ret < 0) {
|
|
@@ -733,7 +759,7 @@ noko_xml_document_has_wrapped_blank_nodes_p(xmlDocPtr c_document)
|
|
|
733
759
|
return 0;
|
|
734
760
|
}
|
|
735
761
|
|
|
736
|
-
for (
|
|
762
|
+
for (rb_len_t jnode = 0; jnode < RARRAY_LEN(cache); jnode++) {
|
|
737
763
|
xmlNodePtr node;
|
|
738
764
|
VALUE element = rb_ary_entry(cache, jnode);
|
|
739
765
|
|