ox 2.6.0 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ox might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/ext/ox/builder.c +39 -25
- data/ext/ox/dump.c +1 -2
- data/ext/ox/err.c +1 -1
- data/ext/ox/gen_load.c +3 -0
- data/ext/ox/hash_load.c +224 -0
- data/ext/ox/obj_load.c +1 -0
- data/ext/ox/ox.c +36 -10
- data/ext/ox/ox.h +7 -4
- data/ext/ox/parse.c +26 -1
- data/lib/ox/version.rb +1 -1
- metadata +3 -3
- data/ext/ox/encode.h +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75309ac947bc1f273ca4f6f65096f8fd114bf4ab
|
4
|
+
data.tar.gz: 481e35498618a38fde11809e04abf896024ab43f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fa20f3ffd22f25b93984ac9f89948a76f90b6398e085980b7295ed634e0e1baf4399c43e2612821da8f47dd2ea8c0da01ae9712923f7042ab646ac7c463c406
|
7
|
+
data.tar.gz: d2769754eea05431e2e0b6be87bc0bed35992d28ab0b159d967a2198dcaa44489d538f48d96de1a4d8152f2a72ad2d5563b580a2f9064281ef876eb55d9fb5d3
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
|
2
|
+
## 2.7.0 - August 18, 2017
|
3
|
+
|
4
|
+
- Two new load modes added, :hash and :hash_no_attrs. Both load an XML
|
5
|
+
document to create a Hash populated with core Ruby objects.
|
6
|
+
|
7
|
+
- Worked around Ruby API change for RSTRUCT_LEN so Ruby 2.4.2 does not crash.
|
8
|
+
|
2
9
|
## 2.6.0 - August 9, 2017
|
3
10
|
|
4
11
|
- The Element#each() method was added to allow iteration over Element nodes conditionally.
|
data/ext/ox/builder.c
CHANGED
@@ -104,13 +104,13 @@ append_indent(Builder b) {
|
|
104
104
|
}
|
105
105
|
|
106
106
|
static void
|
107
|
-
append_string(Builder b, const char *str, size_t size, const char *table) {
|
107
|
+
append_string(Builder b, const char *str, size_t size, const char *table, bool strip_invalid_chars) {
|
108
108
|
size_t xsize = xml_str_len((const unsigned char*)str, size, table);
|
109
109
|
|
110
110
|
if (size == xsize) {
|
111
111
|
const char *s = str;
|
112
112
|
const char *end = str + size;
|
113
|
-
|
113
|
+
|
114
114
|
buf_append_string(&b->buf, str, size);
|
115
115
|
b->col += size;
|
116
116
|
s = strchr(s, '\n');
|
@@ -126,7 +126,7 @@ append_string(Builder b, const char *str, size_t size, const char *table) {
|
|
126
126
|
char *bp = buf;
|
127
127
|
int i = size;
|
128
128
|
int fcnt;
|
129
|
-
|
129
|
+
|
130
130
|
for (; '\0' != *str && 0 < i; i--, str++) {
|
131
131
|
if ('1' == (fcnt = table[(unsigned char)*str])) {
|
132
132
|
if (end <= bp) {
|
@@ -166,7 +166,9 @@ append_string(Builder b, const char *str, size_t size, const char *table) {
|
|
166
166
|
break;
|
167
167
|
default:
|
168
168
|
// Must be one of the invalid characters.
|
169
|
-
|
169
|
+
if (!strip_invalid_chars) {
|
170
|
+
rb_raise(rb_eSyntaxError, "'\\#x%02x' is not a valid XML character.", *str);
|
171
|
+
}
|
170
172
|
break;
|
171
173
|
}
|
172
174
|
}
|
@@ -182,7 +184,7 @@ static void
|
|
182
184
|
append_sym_str(Builder b, VALUE v) {
|
183
185
|
const char *s;
|
184
186
|
int len;
|
185
|
-
|
187
|
+
|
186
188
|
switch (rb_type(v)) {
|
187
189
|
case T_STRING:
|
188
190
|
s = StringValuePtr(v);
|
@@ -196,14 +198,14 @@ append_sym_str(Builder b, VALUE v) {
|
|
196
198
|
rb_raise(ox_arg_error_class, "expected a Symbol or String");
|
197
199
|
break;
|
198
200
|
}
|
199
|
-
append_string(b, s, len, xml_element_chars);
|
201
|
+
append_string(b, s, len, xml_element_chars, false);
|
200
202
|
}
|
201
203
|
|
202
204
|
static void
|
203
205
|
i_am_a_child(Builder b, bool is_text) {
|
204
206
|
if (0 <= b->depth) {
|
205
207
|
Element e = &b->stack[b->depth];
|
206
|
-
|
208
|
+
|
207
209
|
if (!e->has_child) {
|
208
210
|
e->has_child = true;
|
209
211
|
buf_append(&b->buf, '>');
|
@@ -226,11 +228,11 @@ append_attr(VALUE key, VALUE value, Builder b) {
|
|
226
228
|
b->col += 2;
|
227
229
|
b->pos += 2;
|
228
230
|
Check_Type(value, T_STRING);
|
229
|
-
append_string(b, StringValuePtr(value), (int)RSTRING_LEN(value), xml_attr_chars);
|
231
|
+
append_string(b, StringValuePtr(value), (int)RSTRING_LEN(value), xml_attr_chars, false);
|
230
232
|
buf_append(&b->buf, '"');
|
231
233
|
b->col++;
|
232
234
|
b->pos++;
|
233
|
-
|
235
|
+
|
234
236
|
return ST_CONTINUE;
|
235
237
|
}
|
236
238
|
|
@@ -250,7 +252,7 @@ builder_free(void *ptr) {
|
|
250
252
|
Builder b;
|
251
253
|
Element e;
|
252
254
|
int d;
|
253
|
-
|
255
|
+
|
254
256
|
if (0 == ptr) {
|
255
257
|
return;
|
256
258
|
}
|
@@ -349,7 +351,7 @@ builder_new(int argc, VALUE *argv, VALUE self) {
|
|
349
351
|
Builder b = ALLOC(struct _Builder);
|
350
352
|
int indent = ox_default_options.indent;
|
351
353
|
long buf_size = 0;
|
352
|
-
|
354
|
+
|
353
355
|
if (1 == argc) {
|
354
356
|
volatile VALUE v;
|
355
357
|
|
@@ -380,7 +382,7 @@ builder_new(int argc, VALUE *argv, VALUE self) {
|
|
380
382
|
|
381
383
|
if (rb_block_given_p()) {
|
382
384
|
volatile VALUE rb = Data_Wrap_Struct(builder_class, NULL, builder_free, b);
|
383
|
-
|
385
|
+
|
384
386
|
rb_yield(rb);
|
385
387
|
bclose(b);
|
386
388
|
|
@@ -405,7 +407,7 @@ builder_file(int argc, VALUE *argv, VALUE self) {
|
|
405
407
|
int indent = ox_default_options.indent;
|
406
408
|
long buf_size = 0;
|
407
409
|
FILE *f;
|
408
|
-
|
410
|
+
|
409
411
|
if (1 > argc) {
|
410
412
|
rb_raise(ox_arg_error_class, "missing filename");
|
411
413
|
}
|
@@ -468,7 +470,7 @@ builder_io(int argc, VALUE *argv, VALUE self) {
|
|
468
470
|
long buf_size = 0;
|
469
471
|
int fd;
|
470
472
|
volatile VALUE v;
|
471
|
-
|
473
|
+
|
472
474
|
if (1 > argc) {
|
473
475
|
rb_raise(ox_arg_error_class, "missing IO object");
|
474
476
|
}
|
@@ -534,14 +536,14 @@ builder_instruct(int argc, VALUE *argv, VALUE self) {
|
|
534
536
|
b->pos += 7;
|
535
537
|
} else {
|
536
538
|
volatile VALUE v;
|
537
|
-
|
539
|
+
|
538
540
|
buf_append_string(&b->buf, "<?", 2);
|
539
541
|
b->col += 2;
|
540
542
|
b->pos += 2;
|
541
543
|
append_sym_str(b, *argv);
|
542
544
|
if (1 < argc && rb_cHash == rb_obj_class(argv[1])) {
|
543
545
|
int len;
|
544
|
-
|
546
|
+
|
545
547
|
if (Qnil != (v = rb_hash_lookup(argv[1], ox_version_sym))) {
|
546
548
|
if (rb_cString != rb_obj_class(v)) {
|
547
549
|
rb_raise(ox_parse_error_class, ":version must be a Symbol.\n");
|
@@ -637,7 +639,7 @@ builder_element(int argc, VALUE *argv, VALUE self) {
|
|
637
639
|
buf_append(&b->buf, '<');
|
638
640
|
b->col++;
|
639
641
|
b->pos++;
|
640
|
-
append_string(b, e->name, len, xml_element_chars);
|
642
|
+
append_string(b, e->name, len, xml_element_chars, false);
|
641
643
|
if (1 < argc) {
|
642
644
|
rb_hash_foreach(argv[1], append_attr, (VALUE)b);
|
643
645
|
}
|
@@ -657,18 +659,18 @@ builder_element(int argc, VALUE *argv, VALUE self) {
|
|
657
659
|
static VALUE
|
658
660
|
builder_comment(VALUE self, VALUE text) {
|
659
661
|
Builder b = (Builder)DATA_PTR(self);
|
660
|
-
|
662
|
+
|
661
663
|
rb_check_type(text, T_STRING);
|
662
664
|
i_am_a_child(b, false);
|
663
665
|
append_indent(b);
|
664
666
|
buf_append_string(&b->buf, "<!-- ", 5);
|
665
667
|
b->col += 5;
|
666
668
|
b->pos += 5;
|
667
|
-
append_string(b, StringValuePtr(text), RSTRING_LEN(text), xml_element_chars);
|
669
|
+
append_string(b, StringValuePtr(text), RSTRING_LEN(text), xml_element_chars, false);
|
668
670
|
buf_append_string(&b->buf, " --/> ", 5);
|
669
671
|
b->col += 5;
|
670
672
|
b->pos += 5;
|
671
|
-
|
673
|
+
|
672
674
|
return Qnil;
|
673
675
|
}
|
674
676
|
|
@@ -687,7 +689,7 @@ builder_doctype(VALUE self, VALUE text) {
|
|
687
689
|
buf_append_string(&b->buf, "<!DOCTYPE ", 10);
|
688
690
|
b->col += 10;
|
689
691
|
b->pos += 10;
|
690
|
-
append_string(b, StringValuePtr(text), RSTRING_LEN(text), xml_element_chars);
|
692
|
+
append_string(b, StringValuePtr(text), RSTRING_LEN(text), xml_element_chars, false);
|
691
693
|
buf_append(&b->buf, '>');
|
692
694
|
b->col++;
|
693
695
|
b->pos++;
|
@@ -699,17 +701,29 @@ builder_doctype(VALUE self, VALUE text) {
|
|
699
701
|
*
|
700
702
|
* Adds a text element to the XML string being formed.
|
701
703
|
* - +text+ - (String) contents of the text field
|
704
|
+
* - +strip_invalid_chars+ - [true|false] strips any characters invalid for XML, defaults to false
|
702
705
|
*/
|
703
706
|
static VALUE
|
704
|
-
builder_text(VALUE
|
707
|
+
builder_text(int argc, VALUE *argv, VALUE self) {
|
705
708
|
Builder b = (Builder)DATA_PTR(self);
|
706
|
-
volatile VALUE v
|
709
|
+
volatile VALUE v;
|
710
|
+
volatile VALUE strip_invalid_chars;
|
711
|
+
|
712
|
+
if ((0 == argc) || (argc > 2)) {
|
713
|
+
rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1..2)", argc);
|
714
|
+
}
|
715
|
+
v = argv[0];
|
716
|
+
if (2 == argc) {
|
717
|
+
strip_invalid_chars = argv[1];
|
718
|
+
} else {
|
719
|
+
strip_invalid_chars = Qfalse;
|
720
|
+
}
|
707
721
|
|
708
722
|
if (T_STRING != rb_type(v)) {
|
709
723
|
v = rb_funcall(v, ox_to_s_id, 0);
|
710
724
|
}
|
711
725
|
i_am_a_child(b, true);
|
712
|
-
append_string(b, StringValuePtr(v), RSTRING_LEN(v), xml_element_chars);
|
726
|
+
append_string(b, StringValuePtr(v), RSTRING_LEN(v), xml_element_chars, RTEST(strip_invalid_chars));
|
713
727
|
|
714
728
|
return Qnil;
|
715
729
|
}
|
@@ -865,7 +879,7 @@ void ox_init_builder(VALUE ox) {
|
|
865
879
|
rb_define_method(builder_class, "comment", builder_comment, 1);
|
866
880
|
rb_define_method(builder_class, "doctype", builder_doctype, 1);
|
867
881
|
rb_define_method(builder_class, "element", builder_element, -1);
|
868
|
-
rb_define_method(builder_class, "text", builder_text, 1);
|
882
|
+
rb_define_method(builder_class, "text", builder_text, -1);
|
869
883
|
rb_define_method(builder_class, "cdata", builder_cdata, 1);
|
870
884
|
rb_define_method(builder_class, "raw", builder_raw, 1);
|
871
885
|
rb_define_method(builder_class, "pop", builder_pop, 0);
|
data/ext/ox/dump.c
CHANGED
@@ -826,12 +826,11 @@ dump_obj(ID aid, VALUE obj, int depth, Out out) {
|
|
826
826
|
int d2 = depth + 1;
|
827
827
|
#if UNIFY_FIXNUM_AND_BIGNUM
|
828
828
|
long i;
|
829
|
-
long cnt = NUM2LONG(
|
829
|
+
long cnt = NUM2LONG(rb_struct_size(obj));
|
830
830
|
#else // UNIFY_FIXNUM_AND_INTEGER
|
831
831
|
int i;
|
832
832
|
int cnt = (int)RSTRUCT_LEN(obj);
|
833
833
|
#endif // UNIFY_FIXNUM_AND_INTEGER
|
834
|
-
|
835
834
|
e.type = StructCode;
|
836
835
|
e.clas.str = rb_class2name(clas);
|
837
836
|
e.clas.len = strlen(e.clas.str);
|
data/ext/ox/err.c
CHANGED
data/ext/ox/gen_load.c
CHANGED
@@ -34,6 +34,7 @@ struct _ParseCallbacks _ox_gen_callbacks = {
|
|
34
34
|
add_text,
|
35
35
|
add_element,
|
36
36
|
end_element,
|
37
|
+
NULL,
|
37
38
|
};
|
38
39
|
|
39
40
|
ParseCallbacks ox_gen_callbacks = &_ox_gen_callbacks;
|
@@ -46,6 +47,7 @@ struct _ParseCallbacks _ox_limited_callbacks = {
|
|
46
47
|
add_text,
|
47
48
|
add_element,
|
48
49
|
end_element,
|
50
|
+
NULL,
|
49
51
|
};
|
50
52
|
|
51
53
|
ParseCallbacks ox_limited_callbacks = &_ox_limited_callbacks;
|
@@ -58,6 +60,7 @@ struct _ParseCallbacks _ox_nomode_callbacks = {
|
|
58
60
|
add_text,
|
59
61
|
add_element,
|
60
62
|
end_element,
|
63
|
+
NULL,
|
61
64
|
};
|
62
65
|
|
63
66
|
ParseCallbacks ox_nomode_callbacks = &_ox_nomode_callbacks;
|
data/ext/ox/hash_load.c
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
/* hash_load.c
|
2
|
+
* Copyright (c) 2011, Peter Ohler
|
3
|
+
* All rights reserved.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#include <stdbool.h>
|
7
|
+
#include <stdlib.h>
|
8
|
+
#include <errno.h>
|
9
|
+
#include <stdio.h>
|
10
|
+
#include <string.h>
|
11
|
+
#include <stdarg.h>
|
12
|
+
|
13
|
+
#include "ruby.h"
|
14
|
+
#include "ox.h"
|
15
|
+
|
16
|
+
// The approach taken for the hash and has_no_attrs parsing is to push just
|
17
|
+
// the key on to the stack and then decide what to do on the way up/out.
|
18
|
+
|
19
|
+
static VALUE
|
20
|
+
create_top(PInfo pi) {
|
21
|
+
volatile VALUE top = rb_hash_new();;
|
22
|
+
|
23
|
+
helper_stack_push(&pi->helpers, 0, top, HashCode);
|
24
|
+
pi->obj = top;
|
25
|
+
|
26
|
+
return top;
|
27
|
+
}
|
28
|
+
|
29
|
+
static void
|
30
|
+
add_text(PInfo pi, char *text, int closed) {
|
31
|
+
Helper parent = helper_stack_peek(&pi->helpers);
|
32
|
+
volatile VALUE s = rb_str_new2(text);
|
33
|
+
volatile VALUE a;
|
34
|
+
|
35
|
+
#if HAS_ENCODING_SUPPORT
|
36
|
+
if (0 != pi->options->rb_enc) {
|
37
|
+
rb_enc_associate(s, pi->options->rb_enc);
|
38
|
+
}
|
39
|
+
#elif HAS_PRIVATE_ENCODING
|
40
|
+
if (Qnil != pi->options->rb_enc) {
|
41
|
+
rb_funcall(s, ox_force_encoding_id, 1, pi->options->rb_enc);
|
42
|
+
}
|
43
|
+
#endif
|
44
|
+
switch (parent->type) {
|
45
|
+
case NoCode:
|
46
|
+
parent->obj = s;
|
47
|
+
parent->type = StringCode;
|
48
|
+
break;
|
49
|
+
case ArrayCode:
|
50
|
+
rb_ary_push(parent->obj, s);
|
51
|
+
break;
|
52
|
+
default:
|
53
|
+
a = rb_ary_new();
|
54
|
+
rb_ary_push(a, parent->obj);
|
55
|
+
rb_ary_push(a, s);
|
56
|
+
parent->obj = a;
|
57
|
+
parent->type = ArrayCode;
|
58
|
+
break;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
static void
|
63
|
+
add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren) {
|
64
|
+
if (helper_stack_empty(&pi->helpers)) {
|
65
|
+
create_top(pi);
|
66
|
+
}
|
67
|
+
if (NULL != attrs && NULL != attrs->name) {
|
68
|
+
volatile VALUE h = rb_hash_new();
|
69
|
+
volatile VALUE key;
|
70
|
+
volatile VALUE val;
|
71
|
+
volatile VALUE a;
|
72
|
+
|
73
|
+
for (; 0 != attrs->name; attrs++) {
|
74
|
+
if (Yes == pi->options->sym_keys) {
|
75
|
+
key = rb_id2sym(rb_intern(attrs->name));
|
76
|
+
} else {
|
77
|
+
key = rb_str_new2(attrs->name);
|
78
|
+
}
|
79
|
+
val = rb_str_new2(attrs->value);
|
80
|
+
#if HAS_ENCODING_SUPPORT
|
81
|
+
if (0 != pi->options->rb_enc) {
|
82
|
+
rb_enc_associate(val, pi->options->rb_enc);
|
83
|
+
}
|
84
|
+
#elif HAS_PRIVATE_ENCODING
|
85
|
+
if (Qnil != pi->options->rb_enc) {
|
86
|
+
rb_funcall(val, ox_force_encoding_id, 1, pi->options->rb_enc);
|
87
|
+
}
|
88
|
+
#endif
|
89
|
+
rb_hash_aset(h, key, val);
|
90
|
+
}
|
91
|
+
a = rb_ary_new();
|
92
|
+
rb_ary_push(a, h);
|
93
|
+
rb_obj_taint(a); // flag indicating it is a unit, kind of a hack but it works
|
94
|
+
helper_stack_push(&pi->helpers, rb_intern(ename), a, ArrayCode);
|
95
|
+
} else {
|
96
|
+
helper_stack_push(&pi->helpers, rb_intern(ename), Qnil, NoCode);
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
static void
|
101
|
+
add_element_no_attrs(PInfo pi, const char *ename, Attr attrs, int hasChildren) {
|
102
|
+
if (helper_stack_empty(&pi->helpers)) {
|
103
|
+
create_top(pi);
|
104
|
+
}
|
105
|
+
helper_stack_push(&pi->helpers, rb_intern(ename), Qnil, NoCode);
|
106
|
+
}
|
107
|
+
|
108
|
+
static int
|
109
|
+
untaint_hash_cb(VALUE key, VALUE value, VALUE x) {
|
110
|
+
if (Qtrue == rb_obj_tainted(value)) {
|
111
|
+
rb_obj_untaint(value);
|
112
|
+
}
|
113
|
+
return ST_CONTINUE;
|
114
|
+
}
|
115
|
+
|
116
|
+
static void
|
117
|
+
end_element_core(PInfo pi, const char *ename, bool check_taint) {
|
118
|
+
Helper e = helper_stack_pop(&pi->helpers);
|
119
|
+
Helper parent = helper_stack_peek(&pi->helpers);
|
120
|
+
volatile VALUE pobj = parent->obj;
|
121
|
+
volatile VALUE found = Qundef;
|
122
|
+
volatile VALUE key;
|
123
|
+
volatile VALUE a;
|
124
|
+
|
125
|
+
if (NoCode == e->type) {
|
126
|
+
e->obj = Qnil;
|
127
|
+
}
|
128
|
+
if (Yes == pi->options->sym_keys) {
|
129
|
+
key = rb_id2sym(e->var);
|
130
|
+
} else {
|
131
|
+
key = rb_id2str(e->var);
|
132
|
+
}
|
133
|
+
// Make sure the parent is a Hash. If not set then make a Hash. If an
|
134
|
+
// Array or non-Hash then append to array or create and append.
|
135
|
+
switch (parent->type) {
|
136
|
+
case NoCode:
|
137
|
+
pobj = rb_hash_new();
|
138
|
+
parent->obj = pobj;
|
139
|
+
parent->type = HashCode;
|
140
|
+
break;
|
141
|
+
case ArrayCode:
|
142
|
+
pobj = rb_hash_new();
|
143
|
+
rb_ary_push(parent->obj, pobj);
|
144
|
+
break;
|
145
|
+
case HashCode:
|
146
|
+
found = rb_hash_lookup2(parent->obj, key, Qundef);
|
147
|
+
break;
|
148
|
+
default:
|
149
|
+
a = rb_ary_new();
|
150
|
+
rb_ary_push(a, parent->obj);
|
151
|
+
pobj = rb_hash_new();
|
152
|
+
rb_ary_push(a, pobj);
|
153
|
+
parent->obj = a;
|
154
|
+
parent->type = ArrayCode;
|
155
|
+
break;
|
156
|
+
}
|
157
|
+
if (Qundef == found) {
|
158
|
+
rb_hash_aset(pobj, key, e->obj);
|
159
|
+
} else if (RUBY_T_ARRAY == rb_type(found)) {
|
160
|
+
if (check_taint && Qtrue == rb_obj_tainted(found)) {
|
161
|
+
rb_obj_untaint(found);
|
162
|
+
a = rb_ary_new();
|
163
|
+
rb_ary_push(a, found);
|
164
|
+
rb_ary_push(a, e->obj);
|
165
|
+
rb_hash_aset(pobj, key, a);
|
166
|
+
} else {
|
167
|
+
rb_ary_push(found, e->obj);
|
168
|
+
}
|
169
|
+
} else { // something there other than an array
|
170
|
+
if (check_taint && Qtrue == rb_obj_tainted(e->obj)) {
|
171
|
+
rb_obj_untaint(e->obj);
|
172
|
+
}
|
173
|
+
a = rb_ary_new();
|
174
|
+
rb_ary_push(a, found);
|
175
|
+
rb_ary_push(a, e->obj);
|
176
|
+
rb_hash_aset(pobj, key, a);
|
177
|
+
}
|
178
|
+
if (check_taint && RUBY_T_HASH == rb_type(e->obj)) {
|
179
|
+
rb_hash_foreach(e->obj, untaint_hash_cb, Qnil);
|
180
|
+
}
|
181
|
+
}
|
182
|
+
|
183
|
+
static void
|
184
|
+
end_element(PInfo pi, const char *ename) {
|
185
|
+
end_element_core(pi, ename, true);
|
186
|
+
}
|
187
|
+
|
188
|
+
static void
|
189
|
+
end_element_no_attrs(PInfo pi, const char *ename) {
|
190
|
+
end_element_core(pi, ename, false);
|
191
|
+
}
|
192
|
+
|
193
|
+
static void
|
194
|
+
finish(PInfo pi) {
|
195
|
+
if (Qnil != pi->obj && RUBY_T_HASH == rb_type(pi->obj)) {
|
196
|
+
rb_hash_foreach(pi->obj, untaint_hash_cb, Qnil);
|
197
|
+
}
|
198
|
+
}
|
199
|
+
|
200
|
+
struct _ParseCallbacks _ox_hash_callbacks = {
|
201
|
+
NULL,
|
202
|
+
NULL,
|
203
|
+
NULL,
|
204
|
+
NULL,
|
205
|
+
add_text,
|
206
|
+
add_element,
|
207
|
+
end_element,
|
208
|
+
finish,
|
209
|
+
};
|
210
|
+
|
211
|
+
ParseCallbacks ox_hash_callbacks = &_ox_hash_callbacks;
|
212
|
+
|
213
|
+
struct _ParseCallbacks _ox_hash_no_attrs_callbacks = {
|
214
|
+
NULL,
|
215
|
+
NULL,
|
216
|
+
NULL,
|
217
|
+
NULL,
|
218
|
+
add_text,
|
219
|
+
add_element_no_attrs,
|
220
|
+
end_element_no_attrs,
|
221
|
+
NULL,
|
222
|
+
};
|
223
|
+
|
224
|
+
ParseCallbacks ox_hash_no_attrs_callbacks = &_ox_hash_no_attrs_callbacks;
|
data/ext/ox/obj_load.c
CHANGED
data/ext/ox/ox.c
CHANGED
@@ -115,6 +115,8 @@ static VALUE circular_sym;
|
|
115
115
|
static VALUE convert_special_sym;
|
116
116
|
static VALUE effort_sym;
|
117
117
|
static VALUE generic_sym;
|
118
|
+
static VALUE hash_no_attrs_sym;
|
119
|
+
static VALUE hash_sym;
|
118
120
|
static VALUE inactive_sym;
|
119
121
|
static VALUE invalid_replace_sym;
|
120
122
|
static VALUE limited_sym;
|
@@ -185,6 +187,8 @@ extern ParseCallbacks ox_obj_callbacks;
|
|
185
187
|
extern ParseCallbacks ox_gen_callbacks;
|
186
188
|
extern ParseCallbacks ox_limited_callbacks;
|
187
189
|
extern ParseCallbacks ox_nomode_callbacks;
|
190
|
+
extern ParseCallbacks ox_hash_callbacks;
|
191
|
+
extern ParseCallbacks ox_hash_no_attrs_callbacks;
|
188
192
|
|
189
193
|
static void parse_dump_options(VALUE ropts, Options copts);
|
190
194
|
|
@@ -271,7 +275,7 @@ hints_to_overlay(Hints hints) {
|
|
271
275
|
* - _:with_xml_ [true|false|nil] include XML prolog in the dump
|
272
276
|
* - _:circular_ [true|false|nil] support circular references while dumping
|
273
277
|
* - _:xsd_date_ [true|false|nil] use XSD date format instead of decimal format
|
274
|
-
* - _:mode_ [:object|:generic|:limited|nil] load method to use for XML
|
278
|
+
* - _:mode_ [:object|:generic|:limited|:hash|:hash_no_attrs|nil] load method to use for XML
|
275
279
|
* - _:effort_ [:strict|:tolerant|:auto_define] set the tolerance level for loading
|
276
280
|
* - _:symbolize_keys_ [true|false|nil] symbolize element attribute keys or leave as Strings
|
277
281
|
* - _:skip_ [:skip_none|:skip_return|:skip_white] determines how to handle white space in text
|
@@ -310,11 +314,13 @@ get_def_opts(VALUE self) {
|
|
310
314
|
rb_hash_aset(opts, smart_sym, (Yes == ox_default_options.smart) ? Qtrue : ((No == ox_default_options.smart) ? Qfalse : Qnil));
|
311
315
|
rb_hash_aset(opts, convert_special_sym, (ox_default_options.convert_special) ? Qtrue : Qfalse);
|
312
316
|
switch (ox_default_options.mode) {
|
313
|
-
case ObjMode:
|
314
|
-
case GenMode:
|
315
|
-
case LimMode:
|
317
|
+
case ObjMode: rb_hash_aset(opts, mode_sym, object_sym); break;
|
318
|
+
case GenMode: rb_hash_aset(opts, mode_sym, generic_sym); break;
|
319
|
+
case LimMode: rb_hash_aset(opts, mode_sym, limited_sym); break;
|
320
|
+
case HashMode: rb_hash_aset(opts, mode_sym, hash_sym); break;
|
321
|
+
case HashNoAttrMode: rb_hash_aset(opts, mode_sym, hash_no_attrs_sym); break;
|
316
322
|
case NoMode:
|
317
|
-
default:
|
323
|
+
default: rb_hash_aset(opts, mode_sym, Qnil); break;
|
318
324
|
}
|
319
325
|
switch (ox_default_options.effort) {
|
320
326
|
case StrictEffort: rb_hash_aset(opts, effort_sym, strict_sym); break;
|
@@ -404,7 +410,7 @@ sax_html_overlay(VALUE self) {
|
|
404
410
|
* - _:with_xml_ [true|false|nil] include XML prolog in the dump
|
405
411
|
* - _:circular_ [true|false|nil] support circular references while dumping
|
406
412
|
* - _:xsd_date_ [true|false|nil] use XSD date format instead of decimal format
|
407
|
-
* - _:mode_ [:object|:generic|:limited|nil] load method to use for XML
|
413
|
+
* - _:mode_ [:object|:generic|:limited|:hash|:hash_no_attrs|nil] load method to use for XML
|
408
414
|
* - _:effort_ [:strict|:tolerant|:auto_define] set the tolerance level for loading
|
409
415
|
* - _:symbolize_keys_ [true|false|nil] symbolize element attribute keys or leave as Strings
|
410
416
|
* - _:skip_ [:skip_none|:skip_return|:skip_white] determines how to handle white space in text
|
@@ -473,8 +479,12 @@ set_def_opts(VALUE self, VALUE opts) {
|
|
473
479
|
ox_default_options.mode = GenMode;
|
474
480
|
} else if (limited_sym == v) {
|
475
481
|
ox_default_options.mode = LimMode;
|
482
|
+
} else if (hash_sym == v) {
|
483
|
+
ox_default_options.mode = HashMode;
|
484
|
+
} else if (hash_no_attrs_sym == v) {
|
485
|
+
ox_default_options.mode = HashNoAttrMode;
|
476
486
|
} else {
|
477
|
-
rb_raise(ox_parse_error_class, ":mode must be :object, :generic, :limited, or nil.\n");
|
487
|
+
rb_raise(ox_parse_error_class, ":mode must be :object, :generic, :limited, :hash, :hash_no_attrs, or nil.\n");
|
478
488
|
}
|
479
489
|
|
480
490
|
v = rb_hash_aref(opts, effort_sym);
|
@@ -700,8 +710,12 @@ load(char *xml, int argc, VALUE *argv, VALUE self, VALUE encoding, Err err) {
|
|
700
710
|
options.mode = GenMode;
|
701
711
|
} else if (limited_sym == v) {
|
702
712
|
options.mode = LimMode;
|
713
|
+
} else if (hash_sym == v) {
|
714
|
+
options.mode = HashMode;
|
715
|
+
} else if (hash_no_attrs_sym == v) {
|
716
|
+
options.mode = HashNoAttrMode;
|
703
717
|
} else {
|
704
|
-
rb_raise(ox_parse_error_class, ":mode must be :generic, :object,
|
718
|
+
rb_raise(ox_parse_error_class, ":mode must be :generic, :object, :limited, :hash, :hash_no_attrs.\n");
|
705
719
|
}
|
706
720
|
}
|
707
721
|
if (Qnil != (v = rb_hash_lookup(h, effort_sym))) {
|
@@ -830,6 +844,12 @@ load(char *xml, int argc, VALUE *argv, VALUE self, VALUE encoding, Err err) {
|
|
830
844
|
case LimMode:
|
831
845
|
obj = ox_parse(xml, ox_limited_callbacks, 0, &options, err);
|
832
846
|
break;
|
847
|
+
case HashMode:
|
848
|
+
obj = ox_parse(xml, ox_hash_callbacks, 0, &options, err);
|
849
|
+
break;
|
850
|
+
case HashNoAttrMode:
|
851
|
+
obj = ox_parse(xml, ox_hash_no_attrs_callbacks, 0, &options, err);
|
852
|
+
break;
|
833
853
|
case NoMode:
|
834
854
|
obj = ox_parse(xml, ox_nomode_callbacks, 0, &options, err);
|
835
855
|
break;
|
@@ -854,6 +874,8 @@ load(char *xml, int argc, VALUE *argv, VALUE self, VALUE encoding, Err err) {
|
|
854
874
|
* - _:object_ - object format
|
855
875
|
* - _:generic_ - read as a generic XML file
|
856
876
|
* - _:limited_ - read as a generic XML file but with callbacks on text and elements events only
|
877
|
+
* - _:hash_ - read and convert to a Hash and core class objects only
|
878
|
+
* - _:hash_no_attrs_ - read and convert to a Hash and core class objects only without capturing attributes
|
857
879
|
* - *:effort* [:strict|:tolerant|:auto_define] effort to use when an undefined class is encountered, default: :strict
|
858
880
|
* - _:strict_ - raise an NameError for missing classes and modules
|
859
881
|
* - _:tolerant_ - return nil for missing classes and modules
|
@@ -913,6 +935,8 @@ load_str(int argc, VALUE *argv, VALUE self) {
|
|
913
935
|
* - _:object_ - object format
|
914
936
|
* - _:generic_ - read as a generic XML file
|
915
937
|
* - _:limited_ - read as a generic XML file but with callbacks on text and elements events only
|
938
|
+
* - _:hash_ - read and convert to a Hash and core class objects only
|
939
|
+
* - _:hash_no_attrs_ - read and convert to a Hash and core class objects only without capturing attributes
|
916
940
|
* - *:effort* [:strict|:tolerant|:auto_define] effort to use when an undefined class is encountered, default: :strict
|
917
941
|
* - _:strict_ - raise an NameError for missing classes and modules
|
918
942
|
* - _:tolerant_ - return nil for missing classes and modules
|
@@ -1412,9 +1436,12 @@ void Init_ox() {
|
|
1412
1436
|
convert_special_sym = ID2SYM(rb_intern("convert_special")); rb_gc_register_address(&convert_special_sym);
|
1413
1437
|
effort_sym = ID2SYM(rb_intern("effort")); rb_gc_register_address(&effort_sym);
|
1414
1438
|
generic_sym = ID2SYM(rb_intern("generic")); rb_gc_register_address(&generic_sym);
|
1439
|
+
hash_no_attrs_sym = ID2SYM(rb_intern("hash_no_attrs")); rb_gc_register_address(&hash_no_attrs_sym);
|
1440
|
+
hash_sym = ID2SYM(rb_intern("hash")); rb_gc_register_address(&hash_sym);
|
1415
1441
|
inactive_sym = ID2SYM(rb_intern("inactive")); rb_gc_register_address(&inactive_sym);
|
1416
1442
|
invalid_replace_sym = ID2SYM(rb_intern("invalid_replace")); rb_gc_register_address(&invalid_replace_sym);
|
1417
1443
|
limited_sym = ID2SYM(rb_intern("limited")); rb_gc_register_address(&limited_sym);
|
1444
|
+
margin_sym = ID2SYM(rb_intern("margin")); rb_gc_register_address(&margin_sym);
|
1418
1445
|
mode_sym = ID2SYM(rb_intern("mode")); rb_gc_register_address(&mode_sym);
|
1419
1446
|
nest_ok_sym = ID2SYM(rb_intern("nest_ok")); rb_gc_register_address(&nest_ok_sym);
|
1420
1447
|
object_sym = ID2SYM(rb_intern("object")); rb_gc_register_address(&object_sym);
|
@@ -1434,7 +1461,6 @@ void Init_ox() {
|
|
1434
1461
|
smart_sym = ID2SYM(rb_intern("smart")); rb_gc_register_address(&smart_sym);
|
1435
1462
|
strict_sym = ID2SYM(rb_intern("strict")); rb_gc_register_address(&strict_sym);
|
1436
1463
|
strip_namespace_sym = ID2SYM(rb_intern("strip_namespace")); rb_gc_register_address(&strip_namespace_sym);
|
1437
|
-
margin_sym = ID2SYM(rb_intern("margin")); rb_gc_register_address(&margin_sym);
|
1438
1464
|
symbolize_keys_sym = ID2SYM(rb_intern("symbolize_keys")); rb_gc_register_address(&symbolize_keys_sym);
|
1439
1465
|
symbolize_sym = ID2SYM(rb_intern("symbolize")); rb_gc_register_address(&symbolize_sym);
|
1440
1466
|
tolerant_sym = ID2SYM(rb_intern("tolerant")); rb_gc_register_address(&tolerant_sym);
|
@@ -1477,7 +1503,7 @@ void Init_ox() {
|
|
1477
1503
|
#endif
|
1478
1504
|
}
|
1479
1505
|
|
1480
|
-
void
|
1506
|
+
_Noreturn void
|
1481
1507
|
_ox_raise_error(const char *msg, const char *xml, const char *current, const char* file, int line) {
|
1482
1508
|
int xline = 1;
|
1483
1509
|
int col = 1;
|
data/ext/ox/ox.h
CHANGED
@@ -87,10 +87,12 @@ typedef enum {
|
|
87
87
|
} YesNo;
|
88
88
|
|
89
89
|
typedef enum {
|
90
|
-
ObjMode
|
91
|
-
GenMode
|
92
|
-
LimMode
|
93
|
-
|
90
|
+
ObjMode = 'o',
|
91
|
+
GenMode = 'g',
|
92
|
+
LimMode = 'l',
|
93
|
+
HashMode = 'h',
|
94
|
+
HashNoAttrMode = 'n',
|
95
|
+
NoMode = 0
|
94
96
|
} LoadMode;
|
95
97
|
|
96
98
|
typedef enum {
|
@@ -109,6 +111,7 @@ typedef struct _ParseCallbacks {
|
|
109
111
|
void (*add_text)(PInfo pi, char *text, int closed);
|
110
112
|
void (*add_element)(PInfo pi, const char *ename, Attr attrs, int hasChildren);
|
111
113
|
void (*end_element)(PInfo pi, const char *ename);
|
114
|
+
void (*finish)(PInfo pi);
|
112
115
|
} *ParseCallbacks;
|
113
116
|
|
114
117
|
typedef struct _CircArray {
|
data/ext/ox/parse.c
CHANGED
@@ -93,12 +93,27 @@ next_white(PInfo pi) {
|
|
93
93
|
}
|
94
94
|
}
|
95
95
|
|
96
|
+
static void
|
97
|
+
mark_pi_cb(void *ptr) {
|
98
|
+
if (NULL != ptr) {
|
99
|
+
HelperStack stack = &((PInfo)ptr)->helpers;
|
100
|
+
Helper h;
|
101
|
+
|
102
|
+
for (h = stack->head; h < stack->tail; h++) {
|
103
|
+
if (NoCode != h->type) {
|
104
|
+
rb_gc_mark(h->obj);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
96
110
|
VALUE
|
97
111
|
ox_parse(char *xml, ParseCallbacks pcb, char **endp, Options options, Err err) {
|
98
112
|
struct _PInfo pi;
|
99
113
|
int body_read = 0;
|
100
114
|
int block_given = rb_block_given_p();
|
101
|
-
|
115
|
+
volatile VALUE wrap;
|
116
|
+
|
102
117
|
if (0 == xml) {
|
103
118
|
set_error(err, "Invalid arg, xml string can not be null", xml, 0);
|
104
119
|
return Qnil;
|
@@ -108,6 +123,9 @@ ox_parse(char *xml, ParseCallbacks pcb, char **endp, Options options, Err err) {
|
|
108
123
|
}
|
109
124
|
/* initialize parse info */
|
110
125
|
helper_stack_init(&pi.helpers);
|
126
|
+
// Protect against GC
|
127
|
+
wrap = rb_data_object_wrap(rb_cObject, &pi, mark_pi_cb, NULL);
|
128
|
+
|
111
129
|
err_init(&pi.err);
|
112
130
|
pi.str = xml;
|
113
131
|
pi.s = xml;
|
@@ -175,10 +193,17 @@ ox_parse(char *xml, ParseCallbacks pcb, char **endp, Options options, Err err) {
|
|
175
193
|
return Qnil;
|
176
194
|
}
|
177
195
|
if (block_given && Qnil != pi.obj && Qundef != pi.obj) {
|
196
|
+
if (NULL != pcb->finish) {
|
197
|
+
pcb->finish(&pi);
|
198
|
+
}
|
178
199
|
rb_yield(pi.obj);
|
179
200
|
}
|
180
201
|
}
|
202
|
+
DATA_PTR(wrap) = NULL;
|
181
203
|
helper_stack_cleanup(&pi.helpers);
|
204
|
+
if (NULL != pcb->finish) {
|
205
|
+
pcb->finish(&pi);
|
206
|
+
}
|
182
207
|
return pi.obj;
|
183
208
|
}
|
184
209
|
|
data/lib/ox/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Ohler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: "A fast XML parser and object serializer that uses only standard C lib.\n
|
14
14
|
\ \nOptimized XML (Ox), as the name implies was written to provide speed
|
@@ -35,11 +35,11 @@ files:
|
|
35
35
|
- ext/ox/cache8.c
|
36
36
|
- ext/ox/cache8.h
|
37
37
|
- ext/ox/dump.c
|
38
|
-
- ext/ox/encode.h
|
39
38
|
- ext/ox/err.c
|
40
39
|
- ext/ox/err.h
|
41
40
|
- ext/ox/extconf.rb
|
42
41
|
- ext/ox/gen_load.c
|
42
|
+
- ext/ox/hash_load.c
|
43
43
|
- ext/ox/helper.h
|
44
44
|
- ext/ox/obj_load.c
|
45
45
|
- ext/ox/ox.c
|
data/ext/ox/encode.h
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
/* encode.h
|
2
|
-
* Copyright (c) 2011, Peter Ohler
|
3
|
-
* All rights reserved.
|
4
|
-
*/
|
5
|
-
|
6
|
-
#ifndef __OX_ENCODE_H__
|
7
|
-
#define __OX_ENCODE_H__
|
8
|
-
|
9
|
-
#include "ruby.h"
|
10
|
-
#if HAS_ENCODING_SUPPORT
|
11
|
-
#include "ruby/encoding.h"
|
12
|
-
#endif
|
13
|
-
|
14
|
-
static inline VALUE
|
15
|
-
ox_encode(VALUE rstr) {
|
16
|
-
#if HAS_ENCODING_SUPPORT
|
17
|
-
rb_enc_associate(rstr, ox_utf8_encoding);
|
18
|
-
#else
|
19
|
-
if (Qnil != ox_utf8_encoding) {
|
20
|
-
rstr = rb_funcall(ox_utf8_encoding, ox_iconv_id, 1, rstr);
|
21
|
-
}
|
22
|
-
#endif
|
23
|
-
return rstr;
|
24
|
-
}
|
25
|
-
|
26
|
-
#endif /* __OX_ENCODE_H__ */
|