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/ox.h
CHANGED
@@ -51,6 +51,18 @@ extern "C" {
|
|
51
51
|
#define TRACE 1
|
52
52
|
#define DEBUG 2
|
53
53
|
|
54
|
+
#define XSD_DATE 0x0001
|
55
|
+
#define WITH_XML 0x0002
|
56
|
+
#define WITH_INST 0x0004
|
57
|
+
#define WITH_DTD 0x0008
|
58
|
+
#define CIRCULAR 0x0010
|
59
|
+
|
60
|
+
#define XSD_DATE_SET 0x0100
|
61
|
+
#define WITH_XML_SET 0x0200
|
62
|
+
#define WITH_INST_SET 0x0400
|
63
|
+
#define WITH_DTD_SET 0x0800
|
64
|
+
#define CIRCULAR_SET 0x1000
|
65
|
+
|
54
66
|
typedef enum {
|
55
67
|
UseObj = 1,
|
56
68
|
UseAttr = 2,
|
@@ -66,11 +78,25 @@ typedef enum {
|
|
66
78
|
} Use;
|
67
79
|
|
68
80
|
typedef enum {
|
69
|
-
StrictEffort =
|
70
|
-
TolerantEffort =
|
71
|
-
AutoEffort =
|
81
|
+
StrictEffort = 's',
|
82
|
+
TolerantEffort = 't',
|
83
|
+
AutoEffort = 'a',
|
84
|
+
NoEffort = 0,
|
72
85
|
} Effort;
|
73
86
|
|
87
|
+
typedef enum {
|
88
|
+
Yes = 'y',
|
89
|
+
No = 'n',
|
90
|
+
NotSet = 0
|
91
|
+
} YesNo;
|
92
|
+
|
93
|
+
typedef enum {
|
94
|
+
ObjMode = 'o',
|
95
|
+
GenMode = 'g',
|
96
|
+
LimMode = 'l',
|
97
|
+
NoMode = 0
|
98
|
+
} LoadMode;
|
99
|
+
|
74
100
|
typedef enum {
|
75
101
|
NoCode = 0,
|
76
102
|
ArrayCode = 'a',
|
@@ -111,7 +137,7 @@ typedef struct _Helper {
|
|
111
137
|
typedef struct _PInfo *PInfo;
|
112
138
|
|
113
139
|
typedef struct _ParseCallbacks {
|
114
|
-
void (*
|
140
|
+
void (*instruct)(PInfo pi, const char *target, Attr attrs);
|
115
141
|
void (*add_doctype)(PInfo pi, const char *docType);
|
116
142
|
void (*add_comment)(PInfo pi, const char *comment);
|
117
143
|
void (*add_cdata)(PInfo pi, const char *cdata, size_t len);
|
@@ -142,11 +168,24 @@ struct _PInfo {
|
|
142
168
|
Effort effort;
|
143
169
|
};
|
144
170
|
|
171
|
+
typedef struct _Options {
|
172
|
+
char encoding[64]; // encoding, stored in the option to avoid GC invalidation in default values
|
173
|
+
int indent; // indention for dump, default 2
|
174
|
+
int trace; // trace level
|
175
|
+
char with_dtd; // YesNo
|
176
|
+
char with_xml; // YesNo
|
177
|
+
char with_instruct; // YesNo
|
178
|
+
char circular; // YesNo
|
179
|
+
char xsd_date; // YesNo
|
180
|
+
char mode; // LoadMode
|
181
|
+
char effort; // Effort
|
182
|
+
} *Options;
|
183
|
+
|
145
184
|
extern VALUE parse(char *xml, ParseCallbacks pcb, char **endp, int trace, Effort effort);
|
146
185
|
extern void _raise_error(const char *msg, const char *xml, const char *current, const char* file, int line);
|
147
186
|
|
148
|
-
extern char* write_obj_to_str(VALUE obj,
|
149
|
-
extern void write_obj_to_file(VALUE obj, const char *path,
|
187
|
+
extern char* write_obj_to_str(VALUE obj, Options copts);
|
188
|
+
extern void write_obj_to_file(VALUE obj, const char *path, Options copts);
|
150
189
|
|
151
190
|
extern VALUE Ox;
|
152
191
|
|
data/ext/ox/parse.c
CHANGED
@@ -36,7 +36,7 @@
|
|
36
36
|
#include "ruby.h"
|
37
37
|
#include "ox.h"
|
38
38
|
|
39
|
-
static void
|
39
|
+
static void read_instruction(PInfo pi);
|
40
40
|
static void read_doctype(PInfo pi);
|
41
41
|
static void read_comment(PInfo pi);
|
42
42
|
static void read_element(PInfo pi);
|
@@ -115,7 +115,7 @@ parse(char *xml, ParseCallbacks pcb, char **endp, int trace, Effort effort) {
|
|
115
115
|
switch (*pi.s) {
|
116
116
|
case '?': // prolog
|
117
117
|
pi.s++;
|
118
|
-
|
118
|
+
read_instruction(&pi);
|
119
119
|
break;
|
120
120
|
case '!': /* comment or doctype */
|
121
121
|
pi.s++;
|
@@ -150,77 +150,49 @@ parse(char *xml, ParseCallbacks pcb, char **endp, int trace, Effort effort) {
|
|
150
150
|
/* Entered after the "<?" sequence. Ready to read the rest.
|
151
151
|
*/
|
152
152
|
static void
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
char
|
157
|
-
char
|
158
|
-
char
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
/*
|
191
|
-
if (validateProlog && 0 != strcasecmp("UTF-8", encoding)) {
|
192
|
-
raise_error("invalid format, only UTF-8 supported", pi->str, pi->s);
|
193
|
-
}
|
194
|
-
*/
|
195
|
-
} else if (0 == strcasecmp("standalone", name)) {
|
196
|
-
standalone = read_quoted_value(pi);
|
197
|
-
if (validateProlog && 0 != strcmp("yes", standalone)) {
|
198
|
-
raise_error("invalid format, only standalone XML supported", pi->str, pi->s);
|
199
|
-
}
|
200
|
-
} else {
|
201
|
-
raise_error("invalid format, unknown prolog attribute", pi->str, pi->s);
|
202
|
-
}
|
203
|
-
} else if ('?' == c) {
|
204
|
-
pi->s++;
|
205
|
-
if ('>' != *pi->s++) {
|
206
|
-
raise_error("invalid format, prolog not terminated", pi->str, pi->s);
|
207
|
-
}
|
208
|
-
return;
|
209
|
-
} else {
|
210
|
-
raise_error("invalid format, prolog format error", pi->str, pi->s);
|
211
|
-
}
|
212
|
-
}
|
213
|
-
if ('\0' == pi->s) {
|
214
|
-
raise_error("invalid format, prolog not terminated", pi->str, pi->s);
|
215
|
-
}
|
216
|
-
if ('?' == *pi->s) {
|
217
|
-
pi->s++;
|
153
|
+
read_instruction(PInfo pi) {
|
154
|
+
struct _Attr attrs[MAX_ATTRS + 1];
|
155
|
+
Attr a = attrs;
|
156
|
+
char *target;
|
157
|
+
char *end;
|
158
|
+
char c;
|
159
|
+
|
160
|
+
memset(attrs, 0, sizeof(attrs));
|
161
|
+
target = read_name_token(pi);
|
162
|
+
end = pi->s;
|
163
|
+
next_non_white(pi);
|
164
|
+
c = *pi->s;
|
165
|
+
*end = '\0'; // terminate name
|
166
|
+
if ('?' != c) {
|
167
|
+
while ('?' != *pi->s) {
|
168
|
+
if ('\0' == *pi->s) {
|
169
|
+
raise_error("invalid format, processing instruction not terminated", pi->str, pi->s);
|
170
|
+
}
|
171
|
+
next_non_white(pi);
|
172
|
+
a->name = read_name_token(pi);
|
173
|
+
end = pi->s;
|
174
|
+
next_non_white(pi);
|
175
|
+
if ('=' != *pi->s++) {
|
176
|
+
raise_error("invalid format, no attribute value", pi->str, pi->s);
|
177
|
+
}
|
178
|
+
*end = '\0'; // terminate name
|
179
|
+
// read value
|
180
|
+
next_non_white(pi);
|
181
|
+
a->value = read_quoted_value(pi);
|
182
|
+
a++;
|
183
|
+
if (MAX_ATTRS <= (a - attrs)) {
|
184
|
+
raise_error("too many attributes", pi->str, pi->s);
|
185
|
+
}
|
186
|
+
}
|
187
|
+
if ('?' == *pi->s) {
|
188
|
+
pi->s++;
|
189
|
+
}
|
218
190
|
}
|
219
191
|
if ('>' != *pi->s++) {
|
220
|
-
|
192
|
+
raise_error("invalid format, processing instruction not terminated", pi->str, pi->s);
|
221
193
|
}
|
222
|
-
if (0 != pi->pcb->
|
223
|
-
pi->pcb->
|
194
|
+
if (0 != pi->pcb->instruct) {
|
195
|
+
pi->pcb->instruct(pi, target, attrs);
|
224
196
|
}
|
225
197
|
}
|
226
198
|
|
@@ -492,7 +464,7 @@ read_text(PInfo pi) {
|
|
492
464
|
}
|
493
465
|
b = alloc_buf + pos;
|
494
466
|
}
|
495
|
-
end = alloc_buf + size;
|
467
|
+
end = alloc_buf + size - 2;
|
496
468
|
}
|
497
469
|
if (spc) {
|
498
470
|
*b++ = ' ';
|
@@ -503,9 +475,11 @@ read_text(PInfo pi) {
|
|
503
475
|
}
|
504
476
|
}
|
505
477
|
*b = '\0';
|
506
|
-
pi->pcb->add_text(pi, buf, ('/' == *(pi->s + 1)));
|
507
478
|
if (0 != alloc_buf) {
|
479
|
+
pi->pcb->add_text(pi, alloc_buf, ('/' == *(pi->s + 1)));
|
508
480
|
free(alloc_buf);
|
481
|
+
} else {
|
482
|
+
pi->pcb->add_text(pi, buf, ('/' == *(pi->s + 1)));
|
509
483
|
}
|
510
484
|
}
|
511
485
|
|
data/lib/ox.rb
CHANGED
@@ -83,10 +83,11 @@
|
|
83
83
|
# doc2 = Ox.parse(xml)
|
84
84
|
# puts "Same? #{doc == doc2}"
|
85
85
|
module Ox
|
86
|
-
|
87
|
-
|
86
|
+
private
|
87
|
+
@@keep = []
|
88
88
|
end
|
89
89
|
|
90
|
+
require 'ox/version'
|
90
91
|
require 'ox/node'
|
91
92
|
require 'ox/comment'
|
92
93
|
require 'ox/cdata'
|
data/lib/ox/bag.rb
CHANGED
@@ -54,35 +54,35 @@ module Ox
|
|
54
54
|
end
|
55
55
|
alias == eql?
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
57
|
+
# Define a new class based on the Ox::Bag class. This is used internally in
|
58
|
+
# the Ox module and is available to service wrappers that receive XML
|
59
|
+
# requests that include Objects of Classes not defined in the storage
|
60
|
+
# process.
|
61
|
+
# [classname] Class name or symbol that includes Module names
|
62
|
+
def self.define_class(classname)
|
63
|
+
classname = classname.to_s unless classname.is_a?(String)
|
64
|
+
tokens = classname.split('::').map { |n| n.to_sym }
|
65
|
+
raise "Invalid classname '#{classname}" if tokens.empty?
|
66
|
+
m = Object
|
67
|
+
tokens[0..-2].each do |sym|
|
68
|
+
if m.const_defined?(sym)
|
69
|
+
m = m.const_get(sym)
|
70
|
+
else
|
71
|
+
c = Module.new
|
72
|
+
m.const_set(sym, c)
|
73
|
+
m = c
|
74
|
+
end
|
75
|
+
end
|
76
|
+
sym = tokens[-1]
|
70
77
|
if m.const_defined?(sym)
|
71
|
-
|
78
|
+
c = m.const_get(sym)
|
72
79
|
else
|
73
|
-
c =
|
80
|
+
c = Class.new(Ox::Bag)
|
74
81
|
m.const_set(sym, c)
|
75
|
-
m = c
|
76
82
|
end
|
83
|
+
c
|
77
84
|
end
|
78
|
-
|
79
|
-
|
80
|
-
c = m.const_get(sym)
|
81
|
-
else
|
82
|
-
c = Class.new(Ox::Bag)
|
83
|
-
m.const_set(sym, c)
|
84
|
-
end
|
85
|
-
c
|
86
|
-
end
|
85
|
+
|
86
|
+
end # Bag
|
87
87
|
|
88
88
|
end # Ox
|
data/lib/ox/version.rb
ADDED
data/test/bug1.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby -wW1
|
2
|
+
|
3
|
+
$: << '../lib'
|
4
|
+
$: << '../ext'
|
5
|
+
|
6
|
+
if __FILE__ == $0
|
7
|
+
if (i = ARGV.index('-I'))
|
8
|
+
x,path = ARGV.slice!(i, 2)
|
9
|
+
$: << path
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'ox'
|
14
|
+
|
15
|
+
def oxpoo(cnt = 100000)
|
16
|
+
xml = "<?xml version=\"1.0\"?>\n<a>\n <m>inc</m>\n <i>1</i>\n</a>\n"
|
17
|
+
cnt.times do |i|
|
18
|
+
obj = Ox.load(xml, :mode => :object)
|
19
|
+
#puts "#{obj} (#{obj.class})"
|
20
|
+
raise "decode ##{i} not equal; #{obj.inspect} != '#{[:inc, 1] }'" unless [:inc, 1] == obj
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
oxpoo()
|
data/test/bug2.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby -wW1
|
2
|
+
|
3
|
+
$: << '../lib'
|
4
|
+
$: << '../ext'
|
5
|
+
|
6
|
+
if __FILE__ == $0
|
7
|
+
if (i = ARGV.index('-I'))
|
8
|
+
x,path = ARGV.slice!(i, 2)
|
9
|
+
$: << path
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'ox'
|
14
|
+
|
15
|
+
def bug2()
|
16
|
+
s = File.read('long.pdf')
|
17
|
+
puts "size before: #{s.size}"
|
18
|
+
|
19
|
+
# s = "Hello\x00\x00\x00there."
|
20
|
+
xml = Ox.dump(s, mode: :object)
|
21
|
+
#puts "xml size: #{xml.size}"
|
22
|
+
s2 = Ox.load(xml, mode: :object)
|
23
|
+
puts "size after: #{s2.size}"
|
24
|
+
#puts s2
|
25
|
+
b1 = s.bytes
|
26
|
+
b2 = s2.bytes
|
27
|
+
(0..s2.bytesize).each do |i|
|
28
|
+
puts "#{s.getbyte(i)} #{s2.getbyte(i)}"
|
29
|
+
if s.getbyte(i) != s2.getbyte(i)
|
30
|
+
puts "stopped at #{i}"
|
31
|
+
break
|
32
|
+
end
|
33
|
+
end
|
34
|
+
puts "byte sizes #{s.bytesize} #{s2.bytesize}"
|
35
|
+
puts "char sizes #{s.size} #{s2.size}"
|
36
|
+
end
|
37
|
+
|
38
|
+
bug2()
|
data/test/func.rb
CHANGED
@@ -22,7 +22,51 @@ opts.on("-h", "--help", "Show this display") { puts opts; Process
|
|
22
22
|
files = opts.parse(ARGV)
|
23
23
|
|
24
24
|
class Func < ::Test::Unit::TestCase
|
25
|
-
|
25
|
+
|
26
|
+
def test_get_options
|
27
|
+
opts = Ox.default_options()
|
28
|
+
assert_equal(opts, {
|
29
|
+
:encoding=>nil,
|
30
|
+
:indent=>2,
|
31
|
+
:trace=>0,
|
32
|
+
:with_dtd=>false,
|
33
|
+
:with_xml=>false,
|
34
|
+
:with_instructions=>false,
|
35
|
+
:circular=>false,
|
36
|
+
:xsd_date=>false,
|
37
|
+
:mode=>nil,
|
38
|
+
:effort=>:strict})
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_set_options
|
42
|
+
orig = {
|
43
|
+
:encoding=>nil,
|
44
|
+
:indent=>2,
|
45
|
+
:trace=>0,
|
46
|
+
:with_dtd=>false,
|
47
|
+
:with_xml=>true,
|
48
|
+
:with_instructions=>false,
|
49
|
+
:circular=>false,
|
50
|
+
:xsd_date=>false,
|
51
|
+
:mode=>nil,
|
52
|
+
:effort=>:strict}
|
53
|
+
o2 = {
|
54
|
+
:encoding=>"UTF-8",
|
55
|
+
:indent=>4,
|
56
|
+
:trace=>1,
|
57
|
+
:with_dtd=>true,
|
58
|
+
:with_xml=>false,
|
59
|
+
:with_instructions=>true,
|
60
|
+
:circular=>true,
|
61
|
+
:xsd_date=>true,
|
62
|
+
:mode=>:object,
|
63
|
+
:effort=>:tolerant }
|
64
|
+
Ox.default_options = o2
|
65
|
+
opts = Ox.default_options()
|
66
|
+
assert_equal(opts, o2);
|
67
|
+
Ox.default_options = orig # return to original
|
68
|
+
end
|
69
|
+
|
26
70
|
def test_nil
|
27
71
|
dump_and_load(nil, false)
|
28
72
|
end
|
@@ -133,7 +177,32 @@ class Func < ::Test::Unit::TestCase
|
|
133
177
|
assert_equal(loaded.class.to_s, 'Bad')
|
134
178
|
assert_equal(loaded.class.superclass.to_s, 'Ox::Bag')
|
135
179
|
end
|
136
|
-
|
180
|
+
|
181
|
+
def test_xml_instruction
|
182
|
+
xml = Ox.dump("test", mode: :object, with_xml: false)
|
183
|
+
assert_equal(xml, "<s>test</s>\n")
|
184
|
+
xml = Ox.dump("test", mode: :object, with_xml: true)
|
185
|
+
assert_equal(xml, "<?xml version=\"1.0\"?>\n<s>test</s>\n")
|
186
|
+
xml = Ox.dump("test", mode: :object, with_xml: true, encoding: 'UTF-8')
|
187
|
+
assert_equal(xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<s>test</s>\n")
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_ox_instruction
|
191
|
+
xml = Ox.dump("test", mode: :object, with_xml: true, with_instructions: true)
|
192
|
+
assert_equal(xml, "<?xml version=\"1.0\"?>\n<?ox version=\"1.0\" mode=\"object\"?>\n<s>test</s>\n")
|
193
|
+
xml = Ox.dump("test", mode: :object, with_instructions: true)
|
194
|
+
assert_equal(xml, "<?ox version=\"1.0\" mode=\"object\"?>\n<s>test</s>\n")
|
195
|
+
xml = Ox.dump("test", mode: :object, with_instructions: true, circular: true, xsd_date: true)
|
196
|
+
assert_equal(xml, "<?ox version=\"1.0\" mode=\"object\" circular=\"yes\" xsd_date=\"yes\"?>\n<s i=\"1\">test</s>\n")
|
197
|
+
xml = Ox.dump("test", mode: :object, with_instructions: true, circular: false, xsd_date: false)
|
198
|
+
assert_equal(xml, "<?ox version=\"1.0\" mode=\"object\" circular=\"no\" xsd_date=\"no\"?>\n<s>test</s>\n")
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_dtd
|
202
|
+
xml = Ox.dump("test", mode: :object, with_dtd: true)
|
203
|
+
assert_equal(xml, "<!DOCTYPE s SYSTEM \"ox.dtd\">\n<s>test</s>\n")
|
204
|
+
end
|
205
|
+
|
137
206
|
def test_class
|
138
207
|
dump_and_load(Bag, false)
|
139
208
|
end
|
@@ -215,6 +284,14 @@ class Func < ::Test::Unit::TestCase
|
|
215
284
|
loaded
|
216
285
|
end
|
217
286
|
|
287
|
+
def test_instructions
|
288
|
+
xml = Ox.dump("test", with_instructions: true)
|
289
|
+
puts xml
|
290
|
+
obj = Ox.load(xml) # should convert it to an object
|
291
|
+
assert_equal("test", obj)
|
292
|
+
end
|
293
|
+
|
294
|
+
|
218
295
|
end
|
219
296
|
|
220
297
|
class Bag
|