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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a200a69cf6325bb5b0609452e4075b30ad3c432b
4
- data.tar.gz: 96f8028076776533ae56dd098b970fbbd9937a6e
3
+ metadata.gz: 75309ac947bc1f273ca4f6f65096f8fd114bf4ab
4
+ data.tar.gz: 481e35498618a38fde11809e04abf896024ab43f
5
5
  SHA512:
6
- metadata.gz: cc2eb452d9d428d943c46ab4a412374f1ca97f127684c654008880ca7bc9415df11551c199a030953b23f4e957a3b1a81750207f4aeb5814da10d836b1f399d2
7
- data.tar.gz: bb36e32b2dc96a17b1c56f96ade6eab40ad349e0e14ffff0726f5e36992ac4df3c1eeaf4df2b52eb513a13c4bac42bed34e12657b206e707f9121aeb48e17354
6
+ metadata.gz: 3fa20f3ffd22f25b93984ac9f89948a76f90b6398e085980b7295ed634e0e1baf4399c43e2612821da8f47dd2ea8c0da01ae9712923f7042ab646ac7c463c406
7
+ data.tar.gz: d2769754eea05431e2e0b6be87bc0bed35992d28ab0b159d967a2198dcaa44489d538f48d96de1a4d8152f2a72ad2d5563b580a2f9064281ef876eb55d9fb5d3
@@ -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.
@@ -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
- rb_raise(rb_eSyntaxError, "'\\#x%02x' is not a valid XML character.", *str);
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 self, VALUE text) {
707
+ builder_text(int argc, VALUE *argv, VALUE self) {
705
708
  Builder b = (Builder)DATA_PTR(self);
706
- volatile VALUE v = text;
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);
@@ -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(RSTRUCT_LEN(obj));
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);
@@ -17,7 +17,7 @@ ox_err_set(Err e, VALUE clas, const char *format, ...) {
17
17
  va_end(ap);
18
18
  }
19
19
 
20
- void
20
+ _Noreturn void
21
21
  ox_err_raise(Err e) {
22
22
  rb_raise(e->clas, "%s", e->msg);
23
23
  }
@@ -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;
@@ -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;
@@ -46,6 +46,7 @@ struct _ParseCallbacks _ox_obj_callbacks = {
46
46
  add_text,
47
47
  add_element,
48
48
  end_element,
49
+ NULL,
49
50
  };
50
51
 
51
52
  ParseCallbacks ox_obj_callbacks = &_ox_obj_callbacks;
@@ -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: rb_hash_aset(opts, mode_sym, object_sym); break;
314
- case GenMode: rb_hash_aset(opts, mode_sym, generic_sym); break;
315
- case LimMode: rb_hash_aset(opts, mode_sym, limited_sym); break;
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: rb_hash_aset(opts, mode_sym, Qnil); break;
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, or :limited.\n");
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;
@@ -87,10 +87,12 @@ typedef enum {
87
87
  } YesNo;
88
88
 
89
89
  typedef enum {
90
- ObjMode = 'o',
91
- GenMode = 'g',
92
- LimMode = 'l',
93
- NoMode = 0
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 {
@@ -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
 
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Ox
3
3
  # Current version of the module.
4
- VERSION = '2.6.0'
4
+ VERSION = '2.7.0'
5
5
  end
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.6.0
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-08-09 00:00:00.000000000 Z
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
@@ -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__ */