ox 2.14.28 → 2.14.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +103 -0
- data/ext/ox/base64.c +24 -17
- data/ext/ox/base64.h +2 -1
- data/ext/ox/buf.h +4 -7
- data/ext/ox/builder.c +277 -122
- data/ext/ox/cache.c +48 -13
- data/ext/ox/cache.h +2 -0
- data/ext/ox/dump.c +374 -221
- data/ext/ox/extconf.rb +3 -0
- data/ext/ox/gen_load.c +10 -8
- data/ext/ox/hash_load.c +7 -2
- data/ext/ox/helper.h +10 -6
- data/ext/ox/intern.c +6 -1
- data/ext/ox/obj_load.c +207 -47
- data/ext/ox/ox.c +179 -38
- data/ext/ox/ox.h +11 -1
- data/ext/ox/parse.c +249 -106
- data/ext/ox/sax.c +22 -18
- data/ext/ox/sax_as.c +42 -10
- data/ext/ox/sax_buf.c +23 -8
- data/ext/ox/sax_stack.h +13 -0
- data/ext/ox/time_conv.h +62 -0
- data/ext/ox/xml_check.h +58 -0
- data/ext/ox/xml_str.h +247 -0
- data/lib/ox/version.rb +1 -1
- metadata +5 -2
data/ext/ox/extconf.rb
CHANGED
|
@@ -46,6 +46,9 @@ have_func('rb_ext_ractor_safe', 'ruby.h')
|
|
|
46
46
|
have_func('pthread_mutex_init')
|
|
47
47
|
have_func('rb_enc_interned_str')
|
|
48
48
|
have_func('index')
|
|
49
|
+
# Seeds the name cache hash. Not on every implementation, so cache.c falls back
|
|
50
|
+
# to String#hash, which is per process randomized everywhere it matters.
|
|
51
|
+
have_func('rb_hash_start', 'ruby.h')
|
|
49
52
|
|
|
50
53
|
have_header('ruby/st.h')
|
|
51
54
|
have_header('sys/uio.h')
|
data/ext/ox/gen_load.c
CHANGED
|
@@ -21,13 +21,11 @@ static void nomode_instruct(PInfo pi, const char *target, Attr attrs, const char
|
|
|
21
21
|
static void add_doctype(PInfo pi, const char *docType);
|
|
22
22
|
static void add_comment(PInfo pi, const char *comment);
|
|
23
23
|
static void add_cdata(PInfo pi, const char *cdata, size_t len);
|
|
24
|
-
static void add_text(PInfo pi, char *text, int closed);
|
|
24
|
+
static void add_text(PInfo pi, char *text, size_t len, int closed);
|
|
25
25
|
static void add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren);
|
|
26
26
|
static void end_element(PInfo pi, const char *ename);
|
|
27
27
|
static void add_instruct(PInfo pi, const char *name, Attr attrs, const char *content);
|
|
28
28
|
|
|
29
|
-
extern ParseCallbacks ox_obj_callbacks;
|
|
30
|
-
|
|
31
29
|
struct _parseCallbacks _ox_gen_callbacks = {
|
|
32
30
|
instruct, /* instruct, */
|
|
33
31
|
add_doctype,
|
|
@@ -163,14 +161,18 @@ static void nomode_instruct(PInfo pi, const char *target, Attr attrs, const char
|
|
|
163
161
|
}
|
|
164
162
|
} else if (0 == strcmp("mode", attrs->name)) {
|
|
165
163
|
if (0 == strcmp("object", attrs->value)) {
|
|
166
|
-
|
|
167
|
-
pi->
|
|
168
|
-
|
|
164
|
+
// Only the caller can select object mode.
|
|
165
|
+
if (TRACE <= pi->options->trace) {
|
|
166
|
+
printf("Object mode processing instruction ignored.\n");
|
|
167
|
+
}
|
|
169
168
|
} else if (0 == strcmp("generic", attrs->value)) {
|
|
170
169
|
pi->pcb = ox_gen_callbacks;
|
|
171
170
|
} else if (0 == strcmp("limited", attrs->value)) {
|
|
172
171
|
pi->pcb = ox_limited_callbacks;
|
|
173
172
|
pi->obj = Qnil;
|
|
173
|
+
// The instruction can appear after the stack has grown, and
|
|
174
|
+
// helper_stack_init() only moves head back to base.
|
|
175
|
+
helper_stack_cleanup(&pi->helpers);
|
|
174
176
|
helper_stack_init(&pi->helpers);
|
|
175
177
|
} else {
|
|
176
178
|
ox_err_set(&pi->err,
|
|
@@ -230,8 +232,8 @@ static void add_cdata(PInfo pi, const char *cdata, size_t len) {
|
|
|
230
232
|
rb_ary_push(helper_stack_peek(&pi->helpers)->obj, n);
|
|
231
233
|
}
|
|
232
234
|
|
|
233
|
-
static void add_text(PInfo pi, char *text, int closed) {
|
|
234
|
-
VALUE s =
|
|
235
|
+
static void add_text(PInfo pi, char *text, size_t len, int closed) {
|
|
236
|
+
VALUE s = rb_str_new(text, len);
|
|
235
237
|
|
|
236
238
|
if (0 != pi->options->rb_enc) {
|
|
237
239
|
rb_enc_associate(s, pi->options->rb_enc);
|
data/ext/ox/hash_load.c
CHANGED
|
@@ -92,8 +92,8 @@ static void add_str(PInfo pi, VALUE s) {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
static void add_text(PInfo pi, char *text, int closed) {
|
|
96
|
-
VALUE s =
|
|
95
|
+
static void add_text(PInfo pi, char *text, size_t len, int closed) {
|
|
96
|
+
VALUE s = rb_str_new(text, len);
|
|
97
97
|
if (0 != pi->options->rb_enc) {
|
|
98
98
|
rb_enc_associate(s, pi->options->rb_enc);
|
|
99
99
|
}
|
|
@@ -239,7 +239,12 @@ static void end_element_no_attrs(PInfo pi, const char *ename) {
|
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
static void finish(PInfo pi) {
|
|
242
|
+
// Called once per yielded entity and once after the loop, so the pointer
|
|
243
|
+
// and the count have to go with the block.
|
|
242
244
|
xfree(pi->marked);
|
|
245
|
+
pi->marked = NULL;
|
|
246
|
+
pi->mark_size = 0;
|
|
247
|
+
pi->mark_cnt = 0;
|
|
243
248
|
}
|
|
244
249
|
|
|
245
250
|
static void set_encoding_from_instruct(PInfo pi, Attr attrs) {
|
data/ext/ox/helper.h
CHANGED
|
@@ -6,14 +6,17 @@
|
|
|
6
6
|
#ifndef OX_HELPER_H
|
|
7
7
|
#define OX_HELPER_H
|
|
8
8
|
|
|
9
|
+
#include <stdbool.h>
|
|
10
|
+
|
|
9
11
|
#include "type.h"
|
|
10
12
|
|
|
11
13
|
#define HELPER_STACK_INC 16
|
|
12
14
|
|
|
13
15
|
typedef struct _helper {
|
|
14
|
-
ID var;
|
|
15
|
-
VALUE obj;
|
|
16
|
-
Type type;
|
|
16
|
+
ID var; /* Object var ID, or a Struct member index when index is set */
|
|
17
|
+
VALUE obj; /* object created or Qundef if not appropriate */
|
|
18
|
+
Type type; /* type of object in obj */
|
|
19
|
+
bool index; /* var is a member index and not an ID */
|
|
17
20
|
} *Helper;
|
|
18
21
|
|
|
19
22
|
typedef struct _helperStack {
|
|
@@ -58,9 +61,10 @@ inline static Helper helper_stack_push(HelperStack stack, ID var, VALUE obj, Typ
|
|
|
58
61
|
stack->tail = stack->head + toff;
|
|
59
62
|
stack->end = stack->head + len + HELPER_STACK_INC;
|
|
60
63
|
}
|
|
61
|
-
stack->tail->var
|
|
62
|
-
stack->tail->obj
|
|
63
|
-
stack->tail->type
|
|
64
|
+
stack->tail->var = var;
|
|
65
|
+
stack->tail->obj = obj;
|
|
66
|
+
stack->tail->type = type;
|
|
67
|
+
stack->tail->index = false; // obj_load.c sets its own
|
|
64
68
|
stack->tail++;
|
|
65
69
|
|
|
66
70
|
return stack->tail - 1;
|
data/ext/ox/intern.c
CHANGED
|
@@ -68,7 +68,12 @@ static VALUE form_id(const char *str, size_t len) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
void ox_hash_init(void) {
|
|
71
|
-
VALUE cache_class
|
|
71
|
+
VALUE cache_class;
|
|
72
|
+
|
|
73
|
+
// Before the first cache exists, so no name is ever hashed unseeded.
|
|
74
|
+
ox_hash_seed_init();
|
|
75
|
+
|
|
76
|
+
cache_class = rb_define_class_under(Ox, "Cache", rb_cObject);
|
|
72
77
|
#if RUBY_API_VERSION_CODE >= 30200
|
|
73
78
|
rb_undef_alloc_func(cache_class);
|
|
74
79
|
#endif
|
data/ext/ox/obj_load.c
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
#include <errno.h>
|
|
7
7
|
#include <stdarg.h>
|
|
8
|
+
#include <stdbool.h>
|
|
8
9
|
#include <stdio.h>
|
|
9
10
|
#include <stdlib.h>
|
|
10
11
|
#include <string.h>
|
|
@@ -15,24 +16,27 @@
|
|
|
15
16
|
#include "ox.h"
|
|
16
17
|
#include "ruby.h"
|
|
17
18
|
#include "ruby/encoding.h"
|
|
19
|
+
#include "time_conv.h"
|
|
20
|
+
|
|
21
|
+
// No Struct has this many members, so a larger index is always out of range.
|
|
22
|
+
#define MAX_STRUCT_INDEX (1 << 24)
|
|
18
23
|
|
|
19
24
|
static void instruct(PInfo pi, const char *target, Attr attrs, const char *content);
|
|
20
|
-
static void add_text(PInfo pi, char *text, int closed);
|
|
25
|
+
static void add_text(PInfo pi, char *text, size_t len, int closed);
|
|
21
26
|
static void add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren);
|
|
22
27
|
static void end_element(PInfo pi, const char *ename);
|
|
23
28
|
|
|
24
29
|
static VALUE parse_time(const char *text, VALUE clas);
|
|
25
30
|
static VALUE parse_xsd_time(const char *text, VALUE clas);
|
|
26
31
|
static VALUE parse_double_time(const char *text, VALUE clas);
|
|
27
|
-
static VALUE parse_regexp(const char *text);
|
|
32
|
+
static VALUE parse_regexp(const char *text, PInfo pi);
|
|
28
33
|
|
|
29
|
-
static ID get_var_sym_from_attrs(Attr a, void *encoding);
|
|
34
|
+
static ID get_var_sym_from_attrs(Attr a, void *encoding, bool *indexp);
|
|
30
35
|
static VALUE get_obj_from_attrs(Attr a, PInfo pi, VALUE base_class);
|
|
31
36
|
static VALUE get_class_from_attrs(Attr a, PInfo pi, VALUE base_class);
|
|
32
37
|
static VALUE classname2class(const char *name, PInfo pi, VALUE base_class);
|
|
33
38
|
static unsigned long get_id_from_attrs(PInfo pi, Attr a);
|
|
34
39
|
static CircArray circ_array_new(void);
|
|
35
|
-
static void circ_array_free(CircArray ca);
|
|
36
40
|
static void circ_array_set(CircArray ca, VALUE obj, unsigned long id);
|
|
37
41
|
static VALUE circ_array_get(CircArray ca, unsigned long id);
|
|
38
42
|
|
|
@@ -143,7 +147,8 @@ static VALUE classname2class(const char *name, PInfo pi, VALUE base_class) {
|
|
|
143
147
|
if (Qundef == (clas = slot_cache_get(ox_class_cache, name, &slot, 0))) {
|
|
144
148
|
char class_name[1024];
|
|
145
149
|
char *s;
|
|
146
|
-
|
|
150
|
+
char *class_name_end = class_name + sizeof(class_name) - 1;
|
|
151
|
+
const char *n = name;
|
|
147
152
|
|
|
148
153
|
clas = rb_cObject;
|
|
149
154
|
for (s = class_name; '\0' != *n; n++) {
|
|
@@ -159,6 +164,13 @@ static VALUE classname2class(const char *name, PInfo pi, VALUE base_class) {
|
|
|
159
164
|
}
|
|
160
165
|
s = class_name;
|
|
161
166
|
} else {
|
|
167
|
+
if (class_name_end <= s) {
|
|
168
|
+
// Guard the fixed-size stack buffer: an over-long class name
|
|
169
|
+
// (from the attacker-controlled `c` attribute in object mode)
|
|
170
|
+
// would otherwise overflow class_name[].
|
|
171
|
+
set_error(&pi->err, "Invalid classname, too long", pi->str, pi->s);
|
|
172
|
+
return Qundef;
|
|
173
|
+
}
|
|
162
174
|
*s++ = *n;
|
|
163
175
|
}
|
|
164
176
|
}
|
|
@@ -171,13 +183,30 @@ static VALUE classname2class(const char *name, PInfo pi, VALUE base_class) {
|
|
|
171
183
|
return clas;
|
|
172
184
|
}
|
|
173
185
|
|
|
174
|
-
|
|
186
|
+
// Returns a Struct member index as a Fixnum or an instance variable ID. An ID
|
|
187
|
+
// is odd, so it satisfies FIXNUM_P too; indexp says which one it is.
|
|
188
|
+
static ID get_var_sym_from_attrs(Attr a, void *encoding, bool *indexp) {
|
|
189
|
+
*indexp = false;
|
|
175
190
|
for (; 0 != a->name; a++) {
|
|
176
191
|
if ('a' == *a->name && '\0' == *(a->name + 1)) {
|
|
177
192
|
const char *val = a->value;
|
|
178
193
|
|
|
179
194
|
if ('0' <= *val && *val <= '9') {
|
|
180
|
-
|
|
195
|
+
// atoi() wrapped past INT_MAX into a negative index, which Ruby
|
|
196
|
+
// counts from the end. Stopping at INT_MAX is no good either:
|
|
197
|
+
// FIXNUM_MAX is 2**30 - 1 where long is 32 bits, and INT2NUM of
|
|
198
|
+
// INT_MAX came back as -1 there.
|
|
199
|
+
int i = 0;
|
|
200
|
+
|
|
201
|
+
for (; '0' <= *val && *val <= '9'; val++) {
|
|
202
|
+
i = i * 10 + (*val - '0');
|
|
203
|
+
if (MAX_STRUCT_INDEX < i) {
|
|
204
|
+
i = MAX_STRUCT_INDEX;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
*indexp = true;
|
|
209
|
+
return (ID)INT2NUM(i);
|
|
181
210
|
}
|
|
182
211
|
return ox_id_intern(val, strlen(val));
|
|
183
212
|
}
|
|
@@ -212,16 +241,25 @@ static VALUE get_class_from_attrs(Attr a, PInfo pi, VALUE base_class) {
|
|
|
212
241
|
return Qundef;
|
|
213
242
|
}
|
|
214
243
|
|
|
244
|
+
// The id indexes the circular reference table, which is grown to fit it, so an
|
|
245
|
+
// unbounded id is an unbounded heap write. Cap it at the document length: every
|
|
246
|
+
// referenced object needs at least one element, so no valid id can exceed it.
|
|
215
247
|
static unsigned long get_id_from_attrs(PInfo pi, Attr a) {
|
|
216
248
|
for (; 0 != a->name; a++) {
|
|
217
249
|
if ('i' == *a->name && '\0' == *(a->name + 1)) {
|
|
218
|
-
unsigned long
|
|
219
|
-
const
|
|
220
|
-
char
|
|
250
|
+
unsigned long id = 0;
|
|
251
|
+
const unsigned long limit = (unsigned long)(pi->end - pi->str);
|
|
252
|
+
const char *text = a->value;
|
|
253
|
+
char c;
|
|
221
254
|
|
|
222
255
|
for (; '\0' != *text; text++) {
|
|
223
256
|
c = *text;
|
|
224
257
|
if ('0' <= c && c <= '9') {
|
|
258
|
+
// Checked before the multiply so id can not overflow.
|
|
259
|
+
if (limit / 10 < id || limit < id * 10 + (unsigned long)(c - '0')) {
|
|
260
|
+
set_error(&pi->err, "circular reference id out of range", pi->str, pi->s);
|
|
261
|
+
return 0;
|
|
262
|
+
}
|
|
225
263
|
id = id * 10 + (c - '0');
|
|
226
264
|
} else {
|
|
227
265
|
set_error(&pi->err, "bad number format", pi->str, pi->s);
|
|
@@ -245,7 +283,8 @@ static CircArray circ_array_new(void) {
|
|
|
245
283
|
return ca;
|
|
246
284
|
}
|
|
247
285
|
|
|
248
|
-
|
|
286
|
+
// Also called from ox_parse_ensure() when a parse ends before end_element().
|
|
287
|
+
void ox_circ_array_free(CircArray ca) {
|
|
249
288
|
if (ca->objs != ca->obj_array) {
|
|
250
289
|
xfree(ca->objs);
|
|
251
290
|
}
|
|
@@ -281,17 +320,30 @@ static void circ_array_set(CircArray ca, VALUE obj, unsigned long id) {
|
|
|
281
320
|
static VALUE circ_array_get(CircArray ca, unsigned long id) {
|
|
282
321
|
VALUE obj = Qundef;
|
|
283
322
|
|
|
284
|
-
|
|
323
|
+
// id is 0 when the `i` attribute is missing or invalid. Without the lower
|
|
324
|
+
// bound that reads objs[-1] and hands the word to Ruby as an object.
|
|
325
|
+
if (0 < id && id <= ca->cnt) {
|
|
285
326
|
obj = ca->objs[id - 1];
|
|
286
327
|
}
|
|
287
328
|
return obj;
|
|
288
329
|
}
|
|
289
330
|
|
|
290
|
-
static VALUE parse_regexp(const char *text) {
|
|
331
|
+
static VALUE parse_regexp(const char *text, PInfo pi) {
|
|
291
332
|
const char *te;
|
|
333
|
+
const char *s;
|
|
334
|
+
char *b;
|
|
335
|
+
VALUE src;
|
|
336
|
+
size_t len = strlen(text);
|
|
292
337
|
int options = 0;
|
|
293
338
|
|
|
294
|
-
|
|
339
|
+
// The text is Regexp#inspect output, so the shortest it can be is "//".
|
|
340
|
+
// Below that te would be built from text - 1, which is not a pointer the
|
|
341
|
+
// string owns.
|
|
342
|
+
if (len < 2 || '/' != *text) {
|
|
343
|
+
set_error(&pi->err, "Invalid regexp format", pi->str, pi->s);
|
|
344
|
+
return Qundef;
|
|
345
|
+
}
|
|
346
|
+
te = text + len - 1;
|
|
295
347
|
#ifdef ONIG_OPTION_IGNORECASE
|
|
296
348
|
for (; text < te && '/' != *te; te--) {
|
|
297
349
|
switch (*te) {
|
|
@@ -302,7 +354,41 @@ static VALUE parse_regexp(const char *text) {
|
|
|
302
354
|
}
|
|
303
355
|
}
|
|
304
356
|
#endif
|
|
305
|
-
|
|
357
|
+
// The scan above stops on the opening / when there is no closing one, and
|
|
358
|
+
// without ONIG_OPTION_IGNORECASE it never runs at all. Either way the
|
|
359
|
+
// length below goes negative if this is not checked.
|
|
360
|
+
if (te <= text || '/' != *te) {
|
|
361
|
+
set_error(&pi->err, "Invalid regexp format", pi->str, pi->s);
|
|
362
|
+
return Qundef;
|
|
363
|
+
}
|
|
364
|
+
// rb_reg_new() takes bytes, so it can not be told the document's encoding
|
|
365
|
+
// and leaves the source ASCII-8BIT. Going through the String also gets
|
|
366
|
+
// Ruby's own rule, which promotes an ASCII only source to US-ASCII the way
|
|
367
|
+
// a literal is.
|
|
368
|
+
src = rb_str_new(0, te - text - 1);
|
|
369
|
+
b = RSTRING_PTR(src);
|
|
370
|
+
// The dumper writes Regexp#inspect, which escapes a / in the source as \/,
|
|
371
|
+
// so take that back off. A backslash pair is stepped over whole or the /
|
|
372
|
+
// in "\\/" would be read as the escaped one.
|
|
373
|
+
s = text + 1;
|
|
374
|
+
while (s < te) {
|
|
375
|
+
if ('\\' == *s && s + 1 < te) {
|
|
376
|
+
if ('/' == s[1]) {
|
|
377
|
+
*b++ = '/';
|
|
378
|
+
} else {
|
|
379
|
+
*b++ = *s;
|
|
380
|
+
*b++ = s[1];
|
|
381
|
+
}
|
|
382
|
+
s += 2;
|
|
383
|
+
} else {
|
|
384
|
+
*b++ = *s++;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
rb_str_set_len(src, b - RSTRING_PTR(src));
|
|
388
|
+
if (0 != pi->options->rb_enc) {
|
|
389
|
+
rb_enc_associate(src, pi->options->rb_enc);
|
|
390
|
+
}
|
|
391
|
+
return rb_reg_new_str(src, options);
|
|
306
392
|
}
|
|
307
393
|
|
|
308
394
|
static void instruct(PInfo pi, const char *target, Attr attrs, const char *content) {
|
|
@@ -315,7 +401,7 @@ static void instruct(PInfo pi, const char *target, Attr attrs, const char *conte
|
|
|
315
401
|
}
|
|
316
402
|
}
|
|
317
403
|
|
|
318
|
-
static void add_text(PInfo pi, char *text, int closed) {
|
|
404
|
+
static void add_text(PInfo pi, char *text, size_t len, int closed) {
|
|
319
405
|
Helper h = helper_stack_peek(&pi->helpers);
|
|
320
406
|
|
|
321
407
|
if (!closed) {
|
|
@@ -335,7 +421,7 @@ static void add_text(PInfo pi, char *text, int closed) {
|
|
|
335
421
|
switch (h->type) {
|
|
336
422
|
case NoCode:
|
|
337
423
|
case StringCode:
|
|
338
|
-
h->obj =
|
|
424
|
+
h->obj = rb_str_new(text, len);
|
|
339
425
|
if (0 != pi->options->rb_enc) {
|
|
340
426
|
rb_enc_associate(h->obj, pi->options->rb_enc);
|
|
341
427
|
}
|
|
@@ -381,42 +467,68 @@ static void add_text(PInfo pi, char *text, int closed) {
|
|
|
381
467
|
case TimeCode: h->obj = parse_time(text, ox_time_class); break;
|
|
382
468
|
case String64Code: {
|
|
383
469
|
unsigned long str_size = b64_orig_size(text);
|
|
384
|
-
|
|
385
|
-
|
|
470
|
+
// Decode straight into the String. The size comes from the document, so
|
|
471
|
+
// an ALLOCA_N of it is an unbounded stack allocation, and the heap the
|
|
472
|
+
// String already needs costs nothing extra.
|
|
473
|
+
VALUE v = rb_str_new(0, (long)str_size);
|
|
386
474
|
|
|
387
|
-
from_base64(text, (uchar *)
|
|
388
|
-
v = rb_str_new(str, str_size);
|
|
475
|
+
from_base64(text, (uchar *)RSTRING_PTR(v), str_size + 1);
|
|
389
476
|
if (0 != pi->options->rb_enc) {
|
|
390
477
|
rb_enc_associate(v, pi->options->rb_enc);
|
|
391
478
|
}
|
|
392
479
|
if (0 != pi->circ_array) {
|
|
393
|
-
|
|
480
|
+
// h->obj is still Qundef; add_element() parked the id in pi->id.
|
|
481
|
+
circ_array_set(pi->circ_array, v, pi->id);
|
|
394
482
|
}
|
|
395
483
|
h->obj = v;
|
|
396
484
|
break;
|
|
397
485
|
}
|
|
398
486
|
case Symbol64Code: {
|
|
399
487
|
unsigned long str_size = b64_orig_size(text);
|
|
400
|
-
|
|
488
|
+
VALUE v = rb_str_new(0, (long)str_size);
|
|
489
|
+
char *str = RSTRING_PTR(v);
|
|
401
490
|
|
|
402
|
-
from_base64(text, (uchar *)str);
|
|
491
|
+
from_base64(text, (uchar *)str, str_size + 1);
|
|
403
492
|
h->obj = ox_sym_intern(str, strlen(str), NULL);
|
|
493
|
+
RB_GC_GUARD(v);
|
|
404
494
|
break;
|
|
405
495
|
}
|
|
406
|
-
case RegexpCode:
|
|
496
|
+
case RegexpCode: {
|
|
497
|
+
VALUE re;
|
|
498
|
+
|
|
407
499
|
if ('/' == *text) {
|
|
408
|
-
|
|
500
|
+
re = parse_regexp(text, pi);
|
|
409
501
|
} else {
|
|
410
502
|
unsigned long str_size = b64_orig_size(text);
|
|
411
|
-
|
|
503
|
+
VALUE v = rb_str_new(0, (long)str_size);
|
|
504
|
+
char *str = RSTRING_PTR(v);
|
|
412
505
|
|
|
413
|
-
from_base64(text, (uchar *)str);
|
|
414
|
-
|
|
506
|
+
from_base64(text, (uchar *)str, str_size + 1);
|
|
507
|
+
re = parse_regexp(str, pi);
|
|
508
|
+
RB_GC_GUARD(v);
|
|
509
|
+
}
|
|
510
|
+
if (Qundef == re) {
|
|
511
|
+
return;
|
|
415
512
|
}
|
|
513
|
+
h->obj = re;
|
|
416
514
|
break;
|
|
515
|
+
}
|
|
417
516
|
case BignumCode: h->obj = rb_cstr_to_inum(text, 10, 1); break;
|
|
418
517
|
case BigDecimalCode: h->obj = rb_funcall(rb_cObject, ox_bigdecimal_id, 1, rb_str_new2(text)); break;
|
|
419
|
-
default:
|
|
518
|
+
default: {
|
|
519
|
+
// The rest are containers add_element() built. Replacing one with Qnil
|
|
520
|
+
// left end_element() reading the nil back as an Array or a Struct.
|
|
521
|
+
// skip_off delivers the indentation between children here.
|
|
522
|
+
size_t i;
|
|
523
|
+
|
|
524
|
+
for (i = 0; i < len; i++) {
|
|
525
|
+
if (' ' != text[i] && '\t' != text[i] && '\n' != text[i] && '\r' != text[i]) {
|
|
526
|
+
set_error(&pi->err, "Unexpected text", pi->str, pi->s);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
420
532
|
}
|
|
421
533
|
}
|
|
422
534
|
|
|
@@ -424,6 +536,8 @@ static void add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren
|
|
|
424
536
|
Attr a;
|
|
425
537
|
Helper h;
|
|
426
538
|
unsigned long id;
|
|
539
|
+
bool index;
|
|
540
|
+
ID var;
|
|
427
541
|
|
|
428
542
|
if (TRACE <= pi->options->trace) {
|
|
429
543
|
char buf[1024];
|
|
@@ -454,7 +568,9 @@ static void add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren
|
|
|
454
568
|
set_error(&pi->err, "Invalid element name", pi->str, pi->s);
|
|
455
569
|
return;
|
|
456
570
|
}
|
|
457
|
-
|
|
571
|
+
var = get_var_sym_from_attrs(attrs, (void *)pi->options->rb_enc, &index);
|
|
572
|
+
h = helper_stack_push(&pi->helpers, var, Qundef, *ename);
|
|
573
|
+
h->index = index;
|
|
458
574
|
switch (h->type) {
|
|
459
575
|
case NilClassCode: h->obj = Qnil; break;
|
|
460
576
|
case TrueClassCode: h->obj = Qtrue; break;
|
|
@@ -527,7 +643,12 @@ static void add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren
|
|
|
527
643
|
}
|
|
528
644
|
break;
|
|
529
645
|
case StructCode:
|
|
530
|
-
|
|
646
|
+
// Returns Qundef with no c attribute and, unlike the helpers above, does
|
|
647
|
+
// not set the error itself. The Qundef reached rb_struct_aset().
|
|
648
|
+
if (Qundef == (h->obj = get_struct_from_attrs(attrs))) {
|
|
649
|
+
set_error(&pi->err, "Invalid element for object mode", pi->str, pi->s);
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
531
652
|
if (0 != pi->circ_array) {
|
|
532
653
|
circ_array_set(pi->circ_array, h->obj, get_id_from_attrs(pi, attrs));
|
|
533
654
|
}
|
|
@@ -594,7 +715,9 @@ static void end_element(PInfo pi, const char *ename) {
|
|
|
594
715
|
case ExceptionCode:
|
|
595
716
|
case ObjectCode:
|
|
596
717
|
if (Qnil != ph->obj) {
|
|
597
|
-
|
|
718
|
+
// An index is a valid ID too, so a numeric a attribute set an
|
|
719
|
+
// instance variable named whatever was interned in that slot.
|
|
720
|
+
if (0 == h->var || h->index || NULL == rb_id2name(h->var)) {
|
|
598
721
|
set_error(&pi->err, "Invalid element for object mode", pi->str, pi->s);
|
|
599
722
|
return;
|
|
600
723
|
}
|
|
@@ -606,7 +729,9 @@ static void end_element(PInfo pi, const char *ename) {
|
|
|
606
729
|
}
|
|
607
730
|
break;
|
|
608
731
|
case StructCode:
|
|
609
|
-
|
|
732
|
+
// An ID landed in rb_struct_aset() as a Fixnum, so a name picked
|
|
733
|
+
// whichever member that number happened to be.
|
|
734
|
+
if (0 == h->var || !h->index) {
|
|
610
735
|
set_error(&pi->err, "Invalid element for object mode", pi->str, pi->s);
|
|
611
736
|
return;
|
|
612
737
|
}
|
|
@@ -614,9 +739,14 @@ static void end_element(PInfo pi, const char *ename) {
|
|
|
614
739
|
break;
|
|
615
740
|
case HashCode:
|
|
616
741
|
// put back h
|
|
617
|
-
helper_stack_push(&pi->helpers, h->var, h->obj, KeyCode);
|
|
742
|
+
helper_stack_push(&pi->helpers, h->var, h->obj, KeyCode)->index = h->index;
|
|
618
743
|
break;
|
|
619
744
|
case RangeCode:
|
|
745
|
+
// An index can equal one of these IDs, so turn it away first.
|
|
746
|
+
if (h->index) {
|
|
747
|
+
set_error(&pi->err, "Invalid range attribute", pi->str, pi->s);
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
620
750
|
if (ox_beg_id == h->var) {
|
|
621
751
|
rb_ary_store(ph->obj, 0, h->obj);
|
|
622
752
|
} else if (ox_end_id == h->var) {
|
|
@@ -673,7 +803,7 @@ static void end_element(PInfo pi, const char *ename) {
|
|
|
673
803
|
}
|
|
674
804
|
}
|
|
675
805
|
if (0 != pi->circ_array && helper_stack_empty(&pi->helpers)) {
|
|
676
|
-
|
|
806
|
+
ox_circ_array_free(pi->circ_array);
|
|
677
807
|
pi->circ_array = 0;
|
|
678
808
|
}
|
|
679
809
|
if (DEBUG <= pi->options->trace) {
|
|
@@ -682,17 +812,23 @@ static void end_element(PInfo pi, const char *ename) {
|
|
|
682
812
|
}
|
|
683
813
|
|
|
684
814
|
static VALUE parse_double_time(const char *text, VALUE clas) {
|
|
685
|
-
|
|
815
|
+
time_t v = 0;
|
|
686
816
|
long v2 = 0;
|
|
687
817
|
const char *dot = 0;
|
|
688
818
|
char c;
|
|
819
|
+
bool neg = false;
|
|
689
820
|
|
|
821
|
+
// A time before the epoch is written as a sign followed by the magnitude.
|
|
822
|
+
if ('-' == *text) {
|
|
823
|
+
neg = true;
|
|
824
|
+
text++;
|
|
825
|
+
}
|
|
690
826
|
for (; '.' != *text; text++) {
|
|
691
827
|
c = *text;
|
|
692
828
|
if (c < '0' || '9' < c) {
|
|
693
829
|
return Qnil;
|
|
694
830
|
}
|
|
695
|
-
v = 10 * v + (
|
|
831
|
+
v = 10 * v + (time_t)(c - '0');
|
|
696
832
|
}
|
|
697
833
|
dot = text++;
|
|
698
834
|
for (; '\0' != *text && text - dot <= 6; text++) {
|
|
@@ -705,6 +841,17 @@ static VALUE parse_double_time(const char *text, VALUE clas) {
|
|
|
705
841
|
for (; text - dot <= 9; text++) {
|
|
706
842
|
v2 *= 10;
|
|
707
843
|
}
|
|
844
|
+
if (neg) {
|
|
845
|
+
// rb_time_nano_new() wants a non negative nanosecond count, so the
|
|
846
|
+
// fraction goes back into the second count the way dump_time_thin()
|
|
847
|
+
// took it out.
|
|
848
|
+
if (0 == v2) {
|
|
849
|
+
v = -v;
|
|
850
|
+
} else {
|
|
851
|
+
v = -v - 1;
|
|
852
|
+
v2 = 1000000000L - v2;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
708
855
|
return rb_time_nano_new(v, v2);
|
|
709
856
|
}
|
|
710
857
|
|
|
@@ -731,7 +878,9 @@ static VALUE parse_xsd_time(const char *text, VALUE clas) {
|
|
|
731
878
|
{2, '\0', '\0'},
|
|
732
879
|
{0, '\0', '\0'}};
|
|
733
880
|
Tp tp = tpa;
|
|
734
|
-
|
|
881
|
+
long nsec = 0;
|
|
882
|
+
long offset;
|
|
883
|
+
bool neg = false;
|
|
735
884
|
|
|
736
885
|
for (; 0 != tp->cnt; tp++) {
|
|
737
886
|
for (i = tp->cnt, v = 0; 0 < i; text++, i--) {
|
|
@@ -744,19 +893,30 @@ static VALUE parse_xsd_time(const char *text, VALUE clas) {
|
|
|
744
893
|
}
|
|
745
894
|
v = 10 * v + (long)(c - '0');
|
|
746
895
|
}
|
|
896
|
+
// The fraction is the only field terminated by the offset sign, and the
|
|
897
|
+
// sign is the only place the offset's direction is written down.
|
|
898
|
+
if ('+' == tp->end) {
|
|
899
|
+
nsec = v;
|
|
900
|
+
for (; 0 < i; i--) {
|
|
901
|
+
nsec *= 10;
|
|
902
|
+
}
|
|
903
|
+
neg = ('-' == *text);
|
|
904
|
+
}
|
|
747
905
|
c = *text++;
|
|
748
906
|
if (tp->end != c && tp->alt != c) {
|
|
749
907
|
return Qnil;
|
|
750
908
|
}
|
|
751
909
|
*cp++ = v;
|
|
752
910
|
}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
return rb_time_nano_new(
|
|
911
|
+
offset = cargs[7] * 3600 + cargs[8] * 60;
|
|
912
|
+
if (neg) {
|
|
913
|
+
offset = -offset;
|
|
914
|
+
}
|
|
915
|
+
// mktime() would read the wall clock as local time and throw the offset
|
|
916
|
+
// away, and returns -1 for everything before the epoch on Windows.
|
|
917
|
+
return rb_time_nano_new(
|
|
918
|
+
(time_t)(ox_epoch_from_civil(cargs[0], cargs[1], cargs[2], cargs[3], cargs[4], cargs[5]) - offset),
|
|
919
|
+
nsec);
|
|
760
920
|
}
|
|
761
921
|
|
|
762
922
|
// debug functions
|
|
@@ -791,7 +951,7 @@ static void debug_stack(PInfo pi, const char *comment) {
|
|
|
791
951
|
clas = rb_class2name(c);
|
|
792
952
|
}
|
|
793
953
|
if (0 != h->var) {
|
|
794
|
-
if (
|
|
954
|
+
if (h->index) {
|
|
795
955
|
VALUE v;
|
|
796
956
|
|
|
797
957
|
v = rb_String(h->var);
|