ox 1.1.1 → 1.2.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.
- data/ext/ox/base64.c +6 -5
- data/ext/ox/dump.c +83 -25
- data/ext/ox/gen_load.c +105 -26
- data/ext/ox/obj_load.c +9 -5
- data/ext/ox/ox.c +246 -83
- data/ext/ox/ox.h +45 -6
- data/ext/ox/parse.c +46 -72
- data/lib/ox.rb +3 -2
- data/lib/ox/bag.rb +25 -25
- data/lib/ox/version.rb +5 -0
- data/test/bug1.rb +24 -0
- data/test/bug2.rb +38 -0
- data/test/func.rb +79 -2
- metadata +8 -3
data/ext/ox/base64.c
CHANGED
@@ -29,6 +29,7 @@
|
|
29
29
|
*/
|
30
30
|
|
31
31
|
#include <stdlib.h>
|
32
|
+
#include <stdio.h>
|
32
33
|
|
33
34
|
#include "base64.h"
|
34
35
|
|
@@ -64,10 +65,10 @@ to_base64(const u_char *src, int len, char *b64) {
|
|
64
65
|
b1 = *src++;
|
65
66
|
b2 = *src++;
|
66
67
|
b3 = *src++;
|
67
|
-
*b64++ = digits[b1 >> 2];
|
68
|
-
*b64++ = digits[((b1 & 0x03) << 4) | (b2 >> 4)];
|
69
|
-
*b64++ = digits[((b2 & 0x0F) << 2) | (b3 >> 6)];
|
70
|
-
*b64++ = digits[b3 & 0x3F];
|
68
|
+
*b64++ = digits[(u_char)(b1 >> 2)];
|
69
|
+
*b64++ = digits[(u_char)(((b1 & 0x03) << 4) | (b2 >> 4))];
|
70
|
+
*b64++ = digits[(u_char)(((b2 & 0x0F) << 2) | (b3 >> 6))];
|
71
|
+
*b64++ = digits[(u_char)(b3 & 0x3F)];
|
71
72
|
}
|
72
73
|
if (1 == len3) {
|
73
74
|
b1 = *src++;
|
@@ -90,7 +91,7 @@ unsigned long
|
|
90
91
|
b64_orig_size(const char *text) {
|
91
92
|
const char *start = text;
|
92
93
|
unsigned long size = 0;
|
93
|
-
|
94
|
+
|
94
95
|
if ('\0' != *text) {
|
95
96
|
for (; 0 != *text; text++) { }
|
96
97
|
size = (text - start) * 3 / 4;
|
data/ext/ox/dump.c
CHANGED
@@ -67,10 +67,11 @@ typedef struct _Out {
|
|
67
67
|
int depth; // used by dumpHash
|
68
68
|
} *Out;
|
69
69
|
|
70
|
-
static void dump_obj_to_xml(VALUE obj,
|
70
|
+
static void dump_obj_to_xml(VALUE obj, Options copts, Out out);
|
71
71
|
|
72
|
+
static void dump_first_obj(VALUE obj, Options copts, Out out);
|
72
73
|
static void dump_obj(ID aid, VALUE obj, unsigned int depth, Out out);
|
73
|
-
static void dump_gen_doc(VALUE obj, unsigned int depth, Out out);
|
74
|
+
static void dump_gen_doc(VALUE obj, unsigned int depth, Options copts, Out out);
|
74
75
|
static void dump_gen_element(VALUE obj, unsigned int depth, Out out);
|
75
76
|
static int dump_gen_attr(VALUE key, VALUE value, Out out);
|
76
77
|
static int dump_gen_nodes(VALUE obj, unsigned int depth, Out out);
|
@@ -113,6 +114,30 @@ is_xml_friendly(const u_char *str, int len) {
|
|
113
114
|
return 1;
|
114
115
|
}
|
115
116
|
|
117
|
+
inline static Type
|
118
|
+
obj_class_code(VALUE obj) {
|
119
|
+
switch (rb_type(obj)) {
|
120
|
+
case RUBY_T_NIL: return NilClassCode;
|
121
|
+
case RUBY_T_ARRAY: return ArrayCode;
|
122
|
+
case RUBY_T_HASH: return HashCode;
|
123
|
+
case RUBY_T_TRUE: return TrueClassCode;
|
124
|
+
case RUBY_T_FALSE: return FalseClassCode;
|
125
|
+
case RUBY_T_FIXNUM: return FixnumCode;
|
126
|
+
case RUBY_T_FLOAT: return FloatCode;
|
127
|
+
case RUBY_T_STRING: return (is_xml_friendly((u_char*)StringValuePtr(obj), (int)RSTRING_LEN(obj))) ? StringCode : Base64Code;
|
128
|
+
case RUBY_T_SYMBOL: return SymbolCode;
|
129
|
+
case RUBY_T_DATA: return (rb_cTime == rb_obj_class(obj)) ? TimeCode : 0;
|
130
|
+
case RUBY_T_STRUCT: return (rb_cRange == rb_obj_class(obj)) ? RangeCode : StructCode;
|
131
|
+
case RUBY_T_OBJECT: return (ox_document_clas == rb_obj_class(obj) || ox_element_clas == rb_obj_class(obj)) ? RawCode : ObjectCode;
|
132
|
+
case RUBY_T_REGEXP: return RegexpCode;
|
133
|
+
case RUBY_T_BIGNUM: return BignumCode;
|
134
|
+
case RUBY_T_COMPLEX: return ComplexCode;
|
135
|
+
case RUBY_T_RATIONAL: return RationalCode;
|
136
|
+
case RUBY_T_CLASS: return ClassCode;
|
137
|
+
default: return 0;
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
116
141
|
inline static void
|
117
142
|
fill_indent(Out out, int cnt) {
|
118
143
|
if (0 <= cnt) {
|
@@ -221,7 +246,9 @@ dump_start(Out out, Element e) {
|
|
221
246
|
if (out->end - out->cur < (long)size) {
|
222
247
|
grow(out, size);
|
223
248
|
}
|
224
|
-
|
249
|
+
if (out->buf < out->cur) {
|
250
|
+
fill_indent(out, e->indent);
|
251
|
+
}
|
225
252
|
*out->cur++ = '<';
|
226
253
|
*out->cur++ = e->type;
|
227
254
|
if (0 < e->attr.len) {
|
@@ -361,6 +388,33 @@ dump_time_xsd(Out out, VALUE obj) {
|
|
361
388
|
tzsign, tzhour, tzmin);
|
362
389
|
}
|
363
390
|
|
391
|
+
static void
|
392
|
+
dump_first_obj(VALUE obj, Options copts, Out out) {
|
393
|
+
char buf[128];
|
394
|
+
int cnt;
|
395
|
+
|
396
|
+
if (Yes == copts->with_xml) {
|
397
|
+
if ('\0' == *copts->encoding) {
|
398
|
+
dump_value(out, "<?xml version=\"1.0\"?>", 21);
|
399
|
+
} else {
|
400
|
+
cnt = sprintf(buf, "<?xml version=\"1.0\" encoding=\"%s\"?>", copts->encoding);
|
401
|
+
dump_value(out, buf, cnt);
|
402
|
+
}
|
403
|
+
}
|
404
|
+
if (Yes == copts->with_instruct) {
|
405
|
+
cnt = sprintf(buf, "%s<?ox version=\"1.0\" mode=\"object\"%s%s?>",
|
406
|
+
(out->buf < out->cur) ? "\n" : "",
|
407
|
+
(Yes == copts->circular) ? " circular=\"yes\"" : ((No == copts->circular) ? " circular=\"no\"" : ""),
|
408
|
+
(Yes == copts->xsd_date) ? " xsd_date=\"yes\"" : ((No == copts->xsd_date) ? " xsd_date=\"no\"" : ""));
|
409
|
+
dump_value(out, buf, cnt);
|
410
|
+
}
|
411
|
+
if (Yes == copts->with_dtd) {
|
412
|
+
cnt = sprintf(buf, "%s<!DOCTYPE %c SYSTEM \"ox.dtd\">", (out->buf < out->cur) ? "\n" : "", obj_class_code(obj));
|
413
|
+
dump_value(out, buf, cnt);
|
414
|
+
}
|
415
|
+
dump_obj(0, obj, 0, out);
|
416
|
+
}
|
417
|
+
|
364
418
|
static void
|
365
419
|
dump_obj(ID aid, VALUE obj, unsigned int depth, Out out) {
|
366
420
|
struct _Element e;
|
@@ -376,12 +430,7 @@ dump_obj(ID aid, VALUE obj, unsigned int depth, Out out) {
|
|
376
430
|
}
|
377
431
|
e.closed = 0;
|
378
432
|
if (0 == depth) {
|
379
|
-
|
380
|
-
e.indent = 0;
|
381
|
-
} else {
|
382
|
-
e.indent = -1;
|
383
|
-
}
|
384
|
-
dump_value(out, "<?xml version=\"1.0\"?>", 21);
|
433
|
+
e.indent = (0 <= out->indent) ? 0 : -1;
|
385
434
|
} else if (0 > out->indent) {
|
386
435
|
e.indent = -1;
|
387
436
|
} else if (0 == out->indent) {
|
@@ -573,7 +622,7 @@ dump_obj(ID aid, VALUE obj, unsigned int depth, Out out) {
|
|
573
622
|
if (ox_document_clas == clas) {
|
574
623
|
e.type = RawCode;
|
575
624
|
out->w_start(out, &e);
|
576
|
-
dump_gen_doc(obj, depth + 1, out);
|
625
|
+
dump_gen_doc(obj, depth + 1, 0, out);
|
577
626
|
out->w_end(out, &e);
|
578
627
|
} else if (ox_element_clas == clas) {
|
579
628
|
e.type = RawCode;
|
@@ -702,15 +751,24 @@ dump_hash(VALUE key, VALUE value, Out out) {
|
|
702
751
|
}
|
703
752
|
|
704
753
|
static void
|
705
|
-
dump_gen_doc(VALUE obj, unsigned int depth, Out out) {
|
754
|
+
dump_gen_doc(VALUE obj, unsigned int depth, Options copts, Out out) {
|
706
755
|
VALUE attrs = rb_attr_get(obj, attributes_id);
|
707
756
|
VALUE nodes = rb_attr_get(obj, nodes_id);
|
708
757
|
|
709
|
-
|
710
|
-
|
711
|
-
|
758
|
+
if (Yes == copts->with_xml) {
|
759
|
+
dump_value(out, "<?xml", 5);
|
760
|
+
if (Qnil != attrs) {
|
761
|
+
rb_hash_foreach(attrs, dump_gen_attr, (VALUE)out);
|
762
|
+
}
|
763
|
+
dump_value(out, "?>", 2);
|
764
|
+
}
|
765
|
+
if (Yes == copts->with_instruct) {
|
766
|
+
if (out->buf < out->cur) {
|
767
|
+
dump_value(out, "\n<?ox version=\"1.0\" mode=\"generic\"?>", 36);
|
768
|
+
} else {
|
769
|
+
dump_value(out, "<?ox version=\"1.0\" mode=\"generic\"?>", 35);
|
770
|
+
}
|
712
771
|
}
|
713
|
-
dump_value(out, "?>", 2);
|
714
772
|
if (Qnil != nodes) {
|
715
773
|
dump_gen_nodes(nodes, depth, out);
|
716
774
|
}
|
@@ -848,47 +906,47 @@ dump_gen_val_node(VALUE obj, unsigned int depth,
|
|
848
906
|
}
|
849
907
|
|
850
908
|
static void
|
851
|
-
dump_obj_to_xml(VALUE obj,
|
909
|
+
dump_obj_to_xml(VALUE obj, Options copts, Out out) {
|
852
910
|
VALUE clas = rb_obj_class(obj);
|
853
911
|
|
854
|
-
out->w_time = (xsd_date) ? dump_time_xsd : dump_time_thin;
|
912
|
+
out->w_time = (Yes == copts->xsd_date) ? dump_time_xsd : dump_time_thin;
|
855
913
|
out->buf = (char*)malloc(65336);
|
856
914
|
out->end = out->buf + 65336;
|
857
915
|
out->cur = out->buf;
|
858
916
|
out->circ_cache = 0;
|
859
917
|
out->circ_cnt = 0;
|
860
|
-
if (circular) {
|
918
|
+
if (Yes == copts->circular) {
|
861
919
|
ox_cache8_new(&out->circ_cache);
|
862
920
|
}
|
863
|
-
out->indent = indent;
|
921
|
+
out->indent = copts->indent;
|
864
922
|
if (ox_document_clas == clas) {
|
865
|
-
dump_gen_doc(obj, -1, out);
|
923
|
+
dump_gen_doc(obj, -1, copts, out);
|
866
924
|
} else if (ox_element_clas == clas) {
|
867
925
|
dump_gen_element(obj, 0, out);
|
868
926
|
} else {
|
869
927
|
out->w_start = dump_start;
|
870
928
|
out->w_end = dump_end;
|
871
|
-
|
929
|
+
dump_first_obj(obj, copts, out);
|
872
930
|
}
|
873
931
|
dump_value(out, "\n", 1);
|
874
932
|
}
|
875
933
|
|
876
934
|
char*
|
877
|
-
write_obj_to_str(VALUE obj,
|
935
|
+
write_obj_to_str(VALUE obj, Options copts) {
|
878
936
|
struct _Out out;
|
879
937
|
|
880
|
-
dump_obj_to_xml(obj,
|
938
|
+
dump_obj_to_xml(obj, copts, &out);
|
881
939
|
|
882
940
|
return out.buf;
|
883
941
|
}
|
884
942
|
|
885
943
|
void
|
886
|
-
write_obj_to_file(VALUE obj, const char *path,
|
944
|
+
write_obj_to_file(VALUE obj, const char *path, Options copts) {
|
887
945
|
struct _Out out;
|
888
946
|
size_t size;
|
889
947
|
FILE *f;
|
890
948
|
|
891
|
-
dump_obj_to_xml(obj,
|
949
|
+
dump_obj_to_xml(obj, copts, &out);
|
892
950
|
size = out.cur - out.buf;
|
893
951
|
if (0 == (f = fopen(path, "w"))) {
|
894
952
|
rb_raise(rb_eIOError, "%s\n", strerror(errno));
|
data/ext/ox/gen_load.c
CHANGED
@@ -37,7 +37,8 @@
|
|
37
37
|
#include "ruby.h"
|
38
38
|
#include "ox.h"
|
39
39
|
|
40
|
-
static void
|
40
|
+
static void instruct(PInfo pi, const char *target, Attr attrs);
|
41
|
+
static void nomode_instruct(PInfo pi, const char *target, Attr attrs);
|
41
42
|
static void add_doctype(PInfo pi, const char *docType);
|
42
43
|
static void add_comment(PInfo pi, const char *comment);
|
43
44
|
static void add_cdata(PInfo pi, const char *cdata, size_t len);
|
@@ -45,8 +46,10 @@ static void add_text(PInfo pi, char *text, int closed);
|
|
45
46
|
static void add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren);
|
46
47
|
static void end_element(PInfo pi, const char *ename);
|
47
48
|
|
49
|
+
extern ParseCallbacks ox_obj_callbacks;
|
50
|
+
|
48
51
|
struct _ParseCallbacks _ox_gen_callbacks = {
|
49
|
-
|
52
|
+
instruct, // instruct,
|
50
53
|
add_doctype,
|
51
54
|
add_comment,
|
52
55
|
add_cdata,
|
@@ -69,33 +72,109 @@ struct _ParseCallbacks _ox_limited_callbacks = {
|
|
69
72
|
|
70
73
|
ParseCallbacks ox_limited_callbacks = &_ox_limited_callbacks;
|
71
74
|
|
75
|
+
struct _ParseCallbacks _ox_nomode_callbacks = {
|
76
|
+
nomode_instruct,
|
77
|
+
add_doctype,
|
78
|
+
add_comment,
|
79
|
+
add_cdata,
|
80
|
+
add_text,
|
81
|
+
add_element,
|
82
|
+
end_element,
|
83
|
+
};
|
84
|
+
|
85
|
+
ParseCallbacks ox_nomode_callbacks = &_ox_nomode_callbacks;
|
86
|
+
|
72
87
|
static void
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
88
|
+
instruct(PInfo pi, const char *target, Attr attrs) {
|
89
|
+
if (0 == strcmp("xml", target)) {
|
90
|
+
VALUE doc;
|
91
|
+
VALUE ah;
|
92
|
+
VALUE nodes;
|
93
|
+
|
94
|
+
if (0 != pi->h) { // top level object
|
95
|
+
rb_raise(rb_eEncodingError, "Prolog must be the first element in an XML document.\n");
|
96
|
+
}
|
97
|
+
pi->h = pi->helpers;
|
98
|
+
doc = rb_obj_alloc(ox_document_clas);
|
99
|
+
ah = rb_hash_new();
|
100
|
+
for (; 0 != attrs->name; attrs++) {
|
101
|
+
rb_hash_aset(ah, ID2SYM(rb_intern(attrs->name)), rb_str_new2(attrs->value));
|
102
|
+
if (0 == strcmp("encoding", attrs->name)) {
|
103
|
+
pi->encoding = rb_enc_find(attrs->value);
|
104
|
+
}
|
105
|
+
}
|
106
|
+
nodes = rb_ary_new();
|
107
|
+
rb_ivar_set(doc, attributes_id, ah);
|
108
|
+
rb_ivar_set(doc, nodes_id, nodes);
|
109
|
+
pi->h->obj = nodes;
|
110
|
+
pi->obj = doc;
|
111
|
+
} else if (0 == strcmp("ox", target)) {
|
112
|
+
for (; 0 != attrs->name; attrs++) {
|
113
|
+
if (0 == strcmp("version", attrs->name)) {
|
114
|
+
if (0 != strcmp("1.0", attrs->value)) {
|
115
|
+
rb_raise(rb_eEncodingError, "Only Ox XML Object version 1.0 supported, not %s.\n", attrs->value);
|
116
|
+
}
|
117
|
+
}
|
118
|
+
// ignore other instructions
|
119
|
+
}
|
120
|
+
} else {
|
121
|
+
if (TRACE <= pi->trace) {
|
122
|
+
printf("Processing instruction %s ignored.\n", target);
|
123
|
+
}
|
90
124
|
}
|
91
|
-
|
92
|
-
|
125
|
+
}
|
126
|
+
|
127
|
+
static void
|
128
|
+
nomode_instruct(PInfo pi, const char *target, Attr attrs) {
|
129
|
+
if (0 == strcmp("xml", target)) {
|
130
|
+
VALUE doc;
|
131
|
+
VALUE ah;
|
132
|
+
VALUE nodes;
|
133
|
+
|
134
|
+
if (0 != pi->h) { // top level object
|
135
|
+
rb_raise(rb_eEncodingError, "Prolog must be the first element in an XML document.\n");
|
136
|
+
}
|
137
|
+
pi->h = pi->helpers;
|
138
|
+
doc = rb_obj_alloc(ox_document_clas);
|
139
|
+
ah = rb_hash_new();
|
140
|
+
for (; 0 != attrs->name; attrs++) {
|
141
|
+
rb_hash_aset(ah, ID2SYM(rb_intern(attrs->name)), rb_str_new2(attrs->value));
|
142
|
+
if (0 == strcmp("encoding", attrs->name)) {
|
143
|
+
pi->encoding = rb_enc_find(attrs->value);
|
144
|
+
}
|
145
|
+
}
|
146
|
+
nodes = rb_ary_new();
|
147
|
+
rb_ivar_set(doc, attributes_id, ah);
|
148
|
+
rb_ivar_set(doc, nodes_id, nodes);
|
149
|
+
pi->h->obj = nodes;
|
150
|
+
pi->obj = doc;
|
151
|
+
} else if (0 == strcmp("ox", target)) {
|
152
|
+
for (; 0 != attrs->name; attrs++) {
|
153
|
+
if (0 == strcmp("version", attrs->name)) {
|
154
|
+
if (0 != strcmp("1.0", attrs->value)) {
|
155
|
+
rb_raise(rb_eEncodingError, "Only Ox XML Object version 1.0 supported, not %s.\n", attrs->value);
|
156
|
+
}
|
157
|
+
} else if (0 == strcmp("mode", attrs->name)) {
|
158
|
+
if (0 == strcmp("object", attrs->value)) {
|
159
|
+
pi->pcb = ox_obj_callbacks;
|
160
|
+
pi->obj = Qnil;
|
161
|
+
pi->h = 0;
|
162
|
+
} else if (0 == strcmp("generic", attrs->value)) {
|
163
|
+
pi->pcb = ox_gen_callbacks;
|
164
|
+
} else if (0 == strcmp("limited", attrs->value)) {
|
165
|
+
pi->pcb = ox_limited_callbacks;
|
166
|
+
pi->obj = Qnil;
|
167
|
+
pi->h = 0;
|
168
|
+
} else {
|
169
|
+
rb_raise(rb_eEncodingError, "%s is not a valid processing instruction mode.\n", attrs->value);
|
170
|
+
}
|
171
|
+
}
|
172
|
+
}
|
173
|
+
} else {
|
174
|
+
if (TRACE <= pi->trace) {
|
175
|
+
printf("Processing instruction %s ignored.\n", target);
|
176
|
+
}
|
93
177
|
}
|
94
|
-
nodes = rb_ary_new();
|
95
|
-
rb_ivar_set(doc, attributes_id, ah);
|
96
|
-
rb_ivar_set(doc, nodes_id, nodes);
|
97
|
-
pi->h->obj = nodes;
|
98
|
-
pi->obj = doc;
|
99
178
|
}
|
100
179
|
|
101
180
|
static void
|
data/ext/ox/obj_load.c
CHANGED
@@ -40,7 +40,7 @@
|
|
40
40
|
#include "base64.h"
|
41
41
|
#include "ox.h"
|
42
42
|
|
43
|
-
static void
|
43
|
+
static void instruct(PInfo pi, const char *target, Attr attrs);
|
44
44
|
static void add_text(PInfo pi, char *text, int closed);
|
45
45
|
static void add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren);
|
46
46
|
static void end_element(PInfo pi, const char *ename);
|
@@ -65,7 +65,7 @@ static void fill_indent(PInfo pi, char *buf, size_t size);
|
|
65
65
|
|
66
66
|
|
67
67
|
struct _ParseCallbacks _ox_obj_callbacks = {
|
68
|
-
|
68
|
+
instruct, // instruct,
|
69
69
|
0, // add_doctype,
|
70
70
|
0, // add_comment,
|
71
71
|
0, // add_cdata,
|
@@ -342,9 +342,13 @@ parse_regexp(const char *text) {
|
|
342
342
|
}
|
343
343
|
|
344
344
|
static void
|
345
|
-
|
346
|
-
if (0
|
347
|
-
|
345
|
+
instruct(PInfo pi, const char *target, Attr attrs) {
|
346
|
+
if (0 == strcmp("xml", target)) {
|
347
|
+
for (; 0 != attrs->name; attrs++) {
|
348
|
+
if (0 == strcmp("encoding", attrs->name)) {
|
349
|
+
pi->encoding = rb_enc_find(attrs->value);
|
350
|
+
}
|
351
|
+
}
|
348
352
|
}
|
349
353
|
}
|
350
354
|
|
data/ext/ox/ox.c
CHANGED
@@ -36,6 +36,11 @@
|
|
36
36
|
#include "ruby.h"
|
37
37
|
#include "ox.h"
|
38
38
|
|
39
|
+
typedef struct _YesNoOpt {
|
40
|
+
VALUE sym;
|
41
|
+
char *attr;
|
42
|
+
} *YesNoOpt;
|
43
|
+
|
39
44
|
void Init_ox();
|
40
45
|
|
41
46
|
VALUE Ox = Qnil;
|
@@ -76,6 +81,10 @@ VALUE tolerant_sym;
|
|
76
81
|
VALUE effort_sym;
|
77
82
|
VALUE auto_define_sym;
|
78
83
|
VALUE trace_sym;
|
84
|
+
VALUE strict_sym;
|
85
|
+
VALUE with_dtd_sym;
|
86
|
+
VALUE with_instruct_sym;
|
87
|
+
VALUE with_xml_sym;
|
79
88
|
VALUE empty_string;
|
80
89
|
VALUE zero_fixnum;
|
81
90
|
|
@@ -92,11 +101,153 @@ Cache symbol_cache = 0;
|
|
92
101
|
Cache class_cache = 0;
|
93
102
|
Cache attr_cache = 0;
|
94
103
|
|
104
|
+
static struct _Options default_options = {
|
105
|
+
{ '\0' }, // encoding
|
106
|
+
2, // indent
|
107
|
+
0, // trace
|
108
|
+
No, // with_dtd
|
109
|
+
No, // with_xml
|
110
|
+
No, // with_instruct
|
111
|
+
No, // circular
|
112
|
+
No, // xsd_date
|
113
|
+
NoMode, // mode
|
114
|
+
StrictEffort, // effort
|
115
|
+
};
|
116
|
+
|
95
117
|
extern ParseCallbacks ox_obj_callbacks;
|
96
118
|
extern ParseCallbacks ox_gen_callbacks;
|
97
119
|
extern ParseCallbacks ox_limited_callbacks;
|
120
|
+
extern ParseCallbacks ox_nomode_callbacks;
|
98
121
|
|
99
|
-
static void parse_dump_options(VALUE
|
122
|
+
static void parse_dump_options(VALUE ropts, Options copts);
|
123
|
+
|
124
|
+
/* call-seq: default_options() => Hash
|
125
|
+
*
|
126
|
+
* Returns the default load and dump options as a Hash. The options are
|
127
|
+
* - indent: [Fixnum] number of spaces to indent each element in an XML document
|
128
|
+
* - trace: [Fixnum] trace level where 0 is silent
|
129
|
+
* - encoding: [String] character encoding for the XML file
|
130
|
+
* - with_dtd: [true|false|nil] include DTD in the dump
|
131
|
+
* - with_instruct: [true|false|nil] include instructions in the dump
|
132
|
+
* - with_xml: [true|false|nil] include XML prolog in the dump
|
133
|
+
* - circular: [true|false|nil] support circular references while dumping
|
134
|
+
* - xsd_date: [true|false|nil] use XSD date format instead of decimal format
|
135
|
+
* - mode: [:object|:generic|:limited|nil] load method to use for XML
|
136
|
+
* - effort: [:strict|:tolerant|:auto_define] set the tolerance level for loading
|
137
|
+
*/
|
138
|
+
static VALUE
|
139
|
+
get_def_opts(VALUE self) {
|
140
|
+
VALUE opts = rb_hash_new();
|
141
|
+
int elen = (int)strlen(default_options.encoding);
|
142
|
+
|
143
|
+
rb_hash_aset(opts, encoding_sym, (0 == elen) ? Qnil : rb_str_new(default_options.encoding, elen));
|
144
|
+
rb_hash_aset(opts, indent_sym, INT2FIX(default_options.indent));
|
145
|
+
rb_hash_aset(opts, trace_sym, INT2FIX(default_options.trace));
|
146
|
+
rb_hash_aset(opts, with_dtd_sym, (Yes == default_options.with_dtd) ? Qtrue : ((No == default_options.with_dtd) ? Qfalse : Qnil));
|
147
|
+
rb_hash_aset(opts, with_xml_sym, (Yes == default_options.with_xml) ? Qtrue : ((No == default_options.with_xml) ? Qfalse : Qnil));
|
148
|
+
rb_hash_aset(opts, with_instruct_sym, (Yes == default_options.with_instruct) ? Qtrue : ((No == default_options.with_instruct) ? Qfalse : Qnil));
|
149
|
+
rb_hash_aset(opts, circular_sym, (Yes == default_options.circular) ? Qtrue : ((No == default_options.circular) ? Qfalse : Qnil));
|
150
|
+
rb_hash_aset(opts, xsd_date_sym, (Yes == default_options.xsd_date) ? Qtrue : ((No == default_options.xsd_date) ? Qfalse : Qnil));
|
151
|
+
switch (default_options.mode) {
|
152
|
+
case ObjMode: rb_hash_aset(opts, mode_sym, object_sym); break;
|
153
|
+
case GenMode: rb_hash_aset(opts, mode_sym, generic_sym); break;
|
154
|
+
case LimMode: rb_hash_aset(opts, mode_sym, limited_sym); break;
|
155
|
+
case NoMode:
|
156
|
+
default: rb_hash_aset(opts, mode_sym, Qnil); break;
|
157
|
+
}
|
158
|
+
switch (default_options.effort) {
|
159
|
+
case StrictEffort: rb_hash_aset(opts, effort_sym, strict_sym); break;
|
160
|
+
case TolerantEffort: rb_hash_aset(opts, effort_sym, tolerant_sym); break;
|
161
|
+
case AutoEffort: rb_hash_aset(opts, effort_sym, auto_define_sym); break;
|
162
|
+
case NoEffort:
|
163
|
+
default: rb_hash_aset(opts, effort_sym, Qnil); break;
|
164
|
+
}
|
165
|
+
return opts;
|
166
|
+
}
|
167
|
+
|
168
|
+
/* call-seq: default_options=(Hash)
|
169
|
+
*
|
170
|
+
* Sets the default options for load and dump. Options are:
|
171
|
+
* - indent: [Fixnum] number of spaces to indent each element in an XML document
|
172
|
+
* - trace: [Fixnum] trace level where 0 is silent
|
173
|
+
* - encoding: [String] character encoding for the XML file
|
174
|
+
* - with_dtd: [true|false|nil] include DTD in the dump
|
175
|
+
* - with_instruct: [true|false|nil] include instructions in the dump
|
176
|
+
* - with_xml: [true|false|nil] include XML prolog in the dump
|
177
|
+
* - circular: [true|false|nil] support circular references while dumping
|
178
|
+
* - xsd_date: [true|false|nil] use XSD date format instead of decimal format
|
179
|
+
* - mode: [:object|:generic|:limited|nil] load method to use for XML
|
180
|
+
* - effort: [:strict|:tolerant|:auto_define] set the tolerance level for loading
|
181
|
+
*/
|
182
|
+
static VALUE
|
183
|
+
set_def_opts(VALUE self, VALUE opts) {
|
184
|
+
struct _YesNoOpt ynos[] = {
|
185
|
+
{ with_xml_sym, &default_options.with_xml },
|
186
|
+
{ with_dtd_sym, &default_options.with_dtd },
|
187
|
+
{ with_instruct_sym, &default_options.with_instruct },
|
188
|
+
{ xsd_date_sym, &default_options.xsd_date },
|
189
|
+
{ circular_sym, &default_options.circular },
|
190
|
+
{ Qnil, 0 }
|
191
|
+
};
|
192
|
+
YesNoOpt o;
|
193
|
+
VALUE v;
|
194
|
+
|
195
|
+
Check_Type(opts, T_HASH);
|
196
|
+
|
197
|
+
v = rb_hash_aref(opts, encoding_sym);
|
198
|
+
if (Qnil == v) {
|
199
|
+
*default_options.encoding = '\0';
|
200
|
+
} else {
|
201
|
+
Check_Type(v, T_STRING);
|
202
|
+
strncpy(default_options.encoding, StringValuePtr(v), sizeof(default_options.encoding) - 1);
|
203
|
+
}
|
204
|
+
v = rb_hash_aref(opts, indent_sym);
|
205
|
+
Check_Type(v, T_FIXNUM);
|
206
|
+
default_options.indent = FIX2INT(v);
|
207
|
+
|
208
|
+
v = rb_hash_aref(opts, trace_sym);
|
209
|
+
Check_Type(v, T_FIXNUM);
|
210
|
+
default_options.trace = FIX2INT(v);
|
211
|
+
|
212
|
+
v = rb_hash_aref(opts, mode_sym);
|
213
|
+
if (Qnil == v) {
|
214
|
+
default_options.mode = NoMode;
|
215
|
+
} else if (object_sym == v) {
|
216
|
+
default_options.mode = ObjMode;
|
217
|
+
} else if (generic_sym == v) {
|
218
|
+
default_options.mode = GenMode;
|
219
|
+
} else if (limited_sym == v) {
|
220
|
+
default_options.mode = LimMode;
|
221
|
+
} else {
|
222
|
+
rb_raise(rb_eArgError, ":mode must be :object, :generic, :limited, or nil.\n");
|
223
|
+
}
|
224
|
+
|
225
|
+
v = rb_hash_aref(opts, effort_sym);
|
226
|
+
if (Qnil == v) {
|
227
|
+
default_options.effort = NoEffort;
|
228
|
+
} else if (strict_sym == v) {
|
229
|
+
default_options.effort = StrictEffort;
|
230
|
+
} else if (tolerant_sym == v) {
|
231
|
+
default_options.effort = TolerantEffort;
|
232
|
+
} else if (auto_define_sym == v) {
|
233
|
+
default_options.effort = AutoEffort;
|
234
|
+
} else {
|
235
|
+
rb_raise(rb_eArgError, ":effort must be :strict, :tolerant, :auto_define, or nil.\n");
|
236
|
+
}
|
237
|
+
for (o = ynos; 0 != o->attr; o++) {
|
238
|
+
v = rb_hash_lookup(opts, o->sym);
|
239
|
+
if (Qnil == v) {
|
240
|
+
*o->attr = NotSet;
|
241
|
+
} else if (Qtrue == v) {
|
242
|
+
*o->attr = Yes;
|
243
|
+
} else if (Qfalse == v) {
|
244
|
+
*o->attr = No;
|
245
|
+
} else {
|
246
|
+
rb_raise(rb_eArgError, "%s must be true, false, or nil.\n", StringValuePtr(o->sym));
|
247
|
+
}
|
248
|
+
}
|
249
|
+
return Qnil;
|
250
|
+
}
|
100
251
|
|
101
252
|
/* call-seq: parse_obj(xml) => Object
|
102
253
|
*
|
@@ -139,68 +290,59 @@ to_gen(VALUE self, VALUE ruby_xml) {
|
|
139
290
|
return obj;
|
140
291
|
}
|
141
292
|
|
142
|
-
typedef enum {
|
143
|
-
AutoMode = 0, // not supported
|
144
|
-
ObjMode = 1,
|
145
|
-
GenMode = 2,
|
146
|
-
LimMode = 3,
|
147
|
-
} LoadMode;
|
148
|
-
|
149
293
|
static VALUE
|
150
294
|
load(char *xml, int argc, VALUE *argv, VALUE self) {
|
151
|
-
VALUE
|
152
|
-
|
153
|
-
int effort = StrictEffort;
|
154
|
-
int trace = 0;
|
295
|
+
VALUE obj;
|
296
|
+
struct _Options options = default_options;
|
155
297
|
|
156
298
|
if (1 == argc && rb_cHash == rb_obj_class(*argv)) {
|
157
299
|
VALUE h = *argv;
|
158
300
|
VALUE v;
|
159
301
|
|
160
302
|
if (Qnil != (v = rb_hash_lookup(h, mode_sym))) {
|
161
|
-
if (
|
162
|
-
mode =
|
163
|
-
} else if (object_sym == v) {
|
164
|
-
mode = ObjMode;
|
303
|
+
if (object_sym == v) {
|
304
|
+
options.mode = ObjMode;
|
165
305
|
} else if (optimized_sym == v) {
|
166
|
-
mode = ObjMode;
|
306
|
+
options.mode = ObjMode;
|
167
307
|
} else if (generic_sym == v) {
|
168
|
-
mode = GenMode;
|
308
|
+
options.mode = GenMode;
|
169
309
|
} else if (limited_sym == v) {
|
170
|
-
mode = LimMode;
|
310
|
+
options.mode = LimMode;
|
171
311
|
} else {
|
172
312
|
rb_raise(rb_eArgError, ":mode must be :generic, :object, or :limited.\n");
|
173
313
|
}
|
174
314
|
}
|
175
315
|
if (Qnil != (v = rb_hash_lookup(h, effort_sym))) {
|
176
316
|
if (auto_define_sym == v) {
|
177
|
-
effort = AutoEffort;
|
317
|
+
options.effort = AutoEffort;
|
178
318
|
} else if (tolerant_sym == v) {
|
179
|
-
effort = TolerantEffort;
|
319
|
+
options.effort = TolerantEffort;
|
180
320
|
} else if (strict_sym == v) {
|
181
|
-
effort = StrictEffort;
|
321
|
+
options.effort = StrictEffort;
|
182
322
|
} else {
|
183
323
|
rb_raise(rb_eArgError, ":effort must be :strict, :tolerant, or :auto_define.\n");
|
184
324
|
}
|
185
325
|
}
|
186
326
|
if (Qnil != (v = rb_hash_lookup(h, trace_sym))) {
|
187
327
|
Check_Type(v, T_FIXNUM);
|
188
|
-
trace = FIX2INT(v);
|
328
|
+
options.trace = FIX2INT(v);
|
189
329
|
}
|
190
330
|
}
|
191
|
-
switch (mode) {
|
331
|
+
switch (options.mode) {
|
192
332
|
case ObjMode:
|
193
|
-
obj = parse(xml, ox_obj_callbacks, 0, trace, effort);
|
333
|
+
obj = parse(xml, ox_obj_callbacks, 0, options.trace, options.effort);
|
194
334
|
break;
|
195
335
|
case GenMode:
|
196
|
-
obj = parse(xml, ox_gen_callbacks, 0, trace, StrictEffort);
|
336
|
+
obj = parse(xml, ox_gen_callbacks, 0, options.trace, StrictEffort);
|
197
337
|
break;
|
198
338
|
case LimMode:
|
199
|
-
obj = parse(xml, ox_limited_callbacks, 0, trace,
|
339
|
+
obj = parse(xml, ox_limited_callbacks, 0, options.trace, StrictEffort);
|
340
|
+
break;
|
341
|
+
case NoMode:
|
342
|
+
obj = parse(xml, ox_nomode_callbacks, 0, options.trace, options.effort);
|
200
343
|
break;
|
201
|
-
case AutoMode:
|
202
344
|
default:
|
203
|
-
obj = parse(xml, ox_gen_callbacks, 0, trace, StrictEffort);
|
345
|
+
obj = parse(xml, ox_gen_callbacks, 0, options.trace, StrictEffort);
|
204
346
|
break;
|
205
347
|
}
|
206
348
|
free(xml);
|
@@ -283,36 +425,49 @@ load_file(int argc, VALUE *argv, VALUE self) {
|
|
283
425
|
}
|
284
426
|
|
285
427
|
static void
|
286
|
-
parse_dump_options(VALUE
|
287
|
-
|
428
|
+
parse_dump_options(VALUE ropts, Options copts) {
|
429
|
+
struct _YesNoOpt ynos[] = {
|
430
|
+
{ with_xml_sym, &copts->with_xml },
|
431
|
+
{ with_dtd_sym, &copts->with_dtd },
|
432
|
+
{ with_instruct_sym, &copts->with_instruct },
|
433
|
+
{ xsd_date_sym, &copts->xsd_date },
|
434
|
+
{ circular_sym, &copts->circular },
|
435
|
+
{ Qnil, 0 }
|
436
|
+
};
|
437
|
+
YesNoOpt o;
|
438
|
+
|
439
|
+
if (rb_cHash == rb_obj_class(ropts)) {
|
288
440
|
VALUE v;
|
289
441
|
|
290
|
-
if (Qnil != (v = rb_hash_lookup(
|
442
|
+
if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) {
|
291
443
|
if (rb_cFixnum != rb_obj_class(v)) {
|
292
444
|
rb_raise(rb_eArgError, ":indent must be a Fixnum.\n");
|
293
445
|
}
|
294
|
-
|
446
|
+
copts->indent = NUM2INT(v);
|
295
447
|
}
|
296
|
-
if (Qnil != (v = rb_hash_lookup(
|
297
|
-
|
298
|
-
|
299
|
-
if (rb_cTrueClass == c) {
|
300
|
-
*xsd_date = 1;
|
301
|
-
} else if (rb_cFalseClass == c) {
|
302
|
-
*xsd_date = 0;
|
303
|
-
} else {
|
304
|
-
rb_raise(rb_eArgError, ":xsd_date must be true or false.\n");
|
448
|
+
if (Qnil != (v = rb_hash_lookup(ropts, trace_sym))) {
|
449
|
+
if (rb_cFixnum != rb_obj_class(v)) {
|
450
|
+
rb_raise(rb_eArgError, ":trace must be a Fixnum.\n");
|
305
451
|
}
|
452
|
+
copts->trace = NUM2INT(v);
|
306
453
|
}
|
307
|
-
if (Qnil != (v = rb_hash_lookup(
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
454
|
+
if (Qnil != (v = rb_hash_lookup(ropts, encoding_sym))) {
|
455
|
+
if (rb_cString != rb_obj_class(v)) {
|
456
|
+
rb_raise(rb_eArgError, ":encoding must be a String.\n");
|
457
|
+
}
|
458
|
+
strncpy(copts->encoding, StringValuePtr(v), sizeof(copts->encoding) - 1);
|
459
|
+
}
|
460
|
+
for (o = ynos; 0 != o->attr; o++) {
|
461
|
+
if (Qnil != (v = rb_hash_lookup(ropts, o->sym))) {
|
462
|
+
VALUE c = rb_obj_class(v);
|
463
|
+
|
464
|
+
if (rb_cTrueClass == c) {
|
465
|
+
*o->attr = Yes;
|
466
|
+
} else if (rb_cFalseClass == c) {
|
467
|
+
*o->attr = No;
|
468
|
+
} else {
|
469
|
+
rb_raise(rb_eArgError, "%s must be true or false.\n", StringValuePtr(o->sym));
|
470
|
+
}
|
316
471
|
}
|
317
472
|
}
|
318
473
|
}
|
@@ -329,16 +484,14 @@ parse_dump_options(VALUE options, int *indent, int *xsd_date, int *circular) {
|
|
329
484
|
*/
|
330
485
|
static VALUE
|
331
486
|
dump(int argc, VALUE *argv, VALUE self) {
|
332
|
-
char
|
333
|
-
|
334
|
-
|
335
|
-
int circular = 0;
|
336
|
-
VALUE rstr;
|
487
|
+
char *xml;
|
488
|
+
struct _Options copts;
|
489
|
+
VALUE rstr;
|
337
490
|
|
338
491
|
if (2 == argc) {
|
339
|
-
parse_dump_options(argv[1], &
|
492
|
+
parse_dump_options(argv[1], &copts);
|
340
493
|
}
|
341
|
-
if (0 == (xml = write_obj_to_str(*argv,
|
494
|
+
if (0 == (xml = write_obj_to_str(*argv, &copts))) {
|
342
495
|
rb_raise(rb_eNoMemError, "Not enough memory.\n");
|
343
496
|
}
|
344
497
|
rstr = rb_str_new2(xml);
|
@@ -359,15 +512,13 @@ dump(int argc, VALUE *argv, VALUE self) {
|
|
359
512
|
*/
|
360
513
|
static VALUE
|
361
514
|
to_file(int argc, VALUE *argv, VALUE self) {
|
362
|
-
|
363
|
-
int xsd_date = 0;
|
364
|
-
int circular = 0;
|
515
|
+
struct _Options copts;
|
365
516
|
|
366
517
|
if (3 == argc) {
|
367
|
-
parse_dump_options(argv[2], &
|
518
|
+
parse_dump_options(argv[2], &copts);
|
368
519
|
}
|
369
520
|
Check_Type(*argv, T_STRING);
|
370
|
-
write_obj_to_file(argv[1], StringValuePtr(*argv),
|
521
|
+
write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
|
371
522
|
|
372
523
|
return Qnil;
|
373
524
|
}
|
@@ -389,7 +540,14 @@ cache8_test(VALUE self) {
|
|
389
540
|
}
|
390
541
|
|
391
542
|
void Init_ox() {
|
543
|
+
VALUE keep = Qnil;
|
544
|
+
|
392
545
|
Ox = rb_define_module("Ox");
|
546
|
+
keep = rb_cv_get(Ox, "@@keep"); // needed to stop GC from deleting and reusing VALUEs
|
547
|
+
|
548
|
+
rb_define_module_function(Ox, "default_options", get_def_opts, 0);
|
549
|
+
rb_define_module_function(Ox, "default_options=", set_def_opts, 1);
|
550
|
+
|
393
551
|
rb_define_module_function(Ox, "parse_obj", to_obj, 1);
|
394
552
|
rb_define_module_function(Ox, "parse", to_gen, 1);
|
395
553
|
rb_define_module_function(Ox, "load", load_str, -1);
|
@@ -422,26 +580,31 @@ void Init_ox() {
|
|
422
580
|
time_class = rb_const_get(rb_cObject, rb_intern("Time"));
|
423
581
|
struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
|
424
582
|
|
425
|
-
version_sym = ID2SYM(rb_intern("version"));
|
426
|
-
standalone_sym = ID2SYM(rb_intern("standalone"));
|
427
|
-
encoding_sym = ID2SYM(rb_intern("encoding"));
|
428
|
-
indent_sym = ID2SYM(rb_intern("indent"));
|
429
|
-
xsd_date_sym = ID2SYM(rb_intern("xsd_date"));
|
430
|
-
opt_format_sym = ID2SYM(rb_intern("opt_format"));
|
431
|
-
mode_sym = ID2SYM(rb_intern("mode"));
|
432
|
-
auto_sym = ID2SYM(rb_intern("auto"));
|
433
|
-
optimized_sym = ID2SYM(rb_intern("optimized"));
|
434
|
-
object_sym = ID2SYM(rb_intern("object"));
|
435
|
-
circular_sym = ID2SYM(rb_intern("circular"));
|
436
|
-
generic_sym = ID2SYM(rb_intern("generic"));
|
437
|
-
limited_sym = ID2SYM(rb_intern("limited"));
|
438
|
-
trace_sym = ID2SYM(rb_intern("trace"));
|
439
|
-
effort_sym = ID2SYM(rb_intern("effort"));
|
440
|
-
strict_sym = ID2SYM(rb_intern("strict"));
|
441
|
-
tolerant_sym = ID2SYM(rb_intern("tolerant"));
|
442
|
-
auto_define_sym = ID2SYM(rb_intern("auto_define"));
|
443
|
-
|
444
|
-
|
583
|
+
version_sym = ID2SYM(rb_intern("version")); rb_ary_push(keep, version_sym);
|
584
|
+
standalone_sym = ID2SYM(rb_intern("standalone")); rb_ary_push(keep, standalone_sym);
|
585
|
+
encoding_sym = ID2SYM(rb_intern("encoding")); rb_ary_push(keep, encoding_sym);
|
586
|
+
indent_sym = ID2SYM(rb_intern("indent")); rb_ary_push(keep, indent_sym);
|
587
|
+
xsd_date_sym = ID2SYM(rb_intern("xsd_date")); rb_ary_push(keep, xsd_date_sym);
|
588
|
+
opt_format_sym = ID2SYM(rb_intern("opt_format")); rb_ary_push(keep, opt_format_sym);
|
589
|
+
mode_sym = ID2SYM(rb_intern("mode")); rb_ary_push(keep, mode_sym);
|
590
|
+
auto_sym = ID2SYM(rb_intern("auto")); rb_ary_push(keep, auto_sym);
|
591
|
+
optimized_sym = ID2SYM(rb_intern("optimized")); rb_ary_push(keep, optimized_sym);
|
592
|
+
object_sym = ID2SYM(rb_intern("object")); rb_ary_push(keep, object_sym);
|
593
|
+
circular_sym = ID2SYM(rb_intern("circular")); rb_ary_push(keep, circular_sym);
|
594
|
+
generic_sym = ID2SYM(rb_intern("generic")); rb_ary_push(keep, generic_sym);
|
595
|
+
limited_sym = ID2SYM(rb_intern("limited")); rb_ary_push(keep, limited_sym);
|
596
|
+
trace_sym = ID2SYM(rb_intern("trace")); rb_ary_push(keep, trace_sym);
|
597
|
+
effort_sym = ID2SYM(rb_intern("effort")); rb_ary_push(keep, effort_sym);
|
598
|
+
strict_sym = ID2SYM(rb_intern("strict")); rb_ary_push(keep, strict_sym);
|
599
|
+
tolerant_sym = ID2SYM(rb_intern("tolerant")); rb_ary_push(keep, tolerant_sym);
|
600
|
+
auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_ary_push(keep, auto_define_sym);
|
601
|
+
with_dtd_sym = ID2SYM(rb_intern("with_dtd")); rb_ary_push(keep, with_dtd_sym);
|
602
|
+
with_instruct_sym = ID2SYM(rb_intern("with_instructions")); rb_ary_push(keep, with_instruct_sym);
|
603
|
+
with_xml_sym = ID2SYM(rb_intern("with_xml")); rb_ary_push(keep, with_xml_sym);
|
604
|
+
|
605
|
+
empty_string = rb_str_new2(""); rb_ary_push(keep, empty_string);
|
606
|
+
zero_fixnum = INT2NUM(0); rb_ary_push(keep, zero_fixnum);
|
607
|
+
|
445
608
|
|
446
609
|
//rb_require("node"); // generic xml node classes
|
447
610
|
ox_document_clas = rb_const_get(Ox, rb_intern("Document"));
|