ox 2.14.27 → 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 +110 -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 +254 -103
- 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/builder.c
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
#include "ruby.h"
|
|
15
15
|
#include "ruby/encoding.h"
|
|
16
16
|
#include "ruby/version.h"
|
|
17
|
+
#include "xml_check.h"
|
|
18
|
+
#include "xml_str.h"
|
|
17
19
|
|
|
18
20
|
#define MAX_DEPTH 128
|
|
19
21
|
|
|
@@ -92,20 +94,13 @@ static const char xml_element_chars[257] = "\
|
|
|
92
94
|
11111111111111111111111111111111\
|
|
93
95
|
11111111111111111111111111111111";
|
|
94
96
|
|
|
95
|
-
inline static size_t xml_str_len(const unsigned char *str, size_t len, const char *table) {
|
|
96
|
-
size_t size = 0;
|
|
97
|
-
|
|
98
|
-
for (; 0 < len; str++, len--) {
|
|
99
|
-
size += table[*str];
|
|
100
|
-
}
|
|
101
|
-
return size - len * (size_t)'0';
|
|
102
|
-
}
|
|
103
|
-
|
|
104
97
|
static void append_indent(Builder b) {
|
|
105
98
|
if (0 >= b->indent) {
|
|
106
99
|
return;
|
|
107
100
|
}
|
|
108
|
-
|
|
101
|
+
// pos and not the buffer: a file builder empties the buffer on every flush,
|
|
102
|
+
// which would read as the start of the document again.
|
|
103
|
+
if (0 < b->pos) {
|
|
109
104
|
int cnt = (b->indent * (b->depth + 1)) + 1;
|
|
110
105
|
|
|
111
106
|
if (sizeof(indent_spaces) <= (size_t)cnt) {
|
|
@@ -118,9 +113,62 @@ static void append_indent(Builder b) {
|
|
|
118
113
|
}
|
|
119
114
|
}
|
|
120
115
|
|
|
121
|
-
|
|
116
|
+
// Returns the escaped size append_string() needs, and raises first if the value
|
|
117
|
+
// holds a character XML has no way to write. append_string() raises on the same
|
|
118
|
+
// byte, but only once the delimiters around the value are already in the buffer
|
|
119
|
+
// and, for an element, once the element is on the stack, and nothing takes those
|
|
120
|
+
// back. So the writers ask here before they write anything.
|
|
121
|
+
//
|
|
122
|
+
// Only a value whose escaped form is longer can hold one, since an invalid byte
|
|
123
|
+
// counts 10, so the common case is the one walk append_string() always did.
|
|
124
|
+
inline static size_t check_string(const char *str, size_t size, const char *table) {
|
|
122
125
|
size_t xsize = xml_str_len((const unsigned char *)str, size, table);
|
|
123
126
|
|
|
127
|
+
if (xsize != size) {
|
|
128
|
+
const unsigned char *bad = xml_first_invalid((const unsigned char *)str, size);
|
|
129
|
+
|
|
130
|
+
if (NULL != bad) {
|
|
131
|
+
rb_raise(ox_syntax_error_class, "'\\#x%02x' is not a valid XML character.", *bad);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return xsize;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// One of the instruct() attributes, which are written from the Hash rather than
|
|
138
|
+
// passed in. A value of the wrong type is left to the type check that already
|
|
139
|
+
// refuses it further down.
|
|
140
|
+
inline static void check_instruct_value(VALUE attrs, VALUE key) {
|
|
141
|
+
volatile VALUE v = rb_hash_lookup(attrs, key);
|
|
142
|
+
|
|
143
|
+
if (Qnil != v && rb_cString == rb_obj_class(v)) {
|
|
144
|
+
check_unescaped(StringValuePtr(v), (size_t)RSTRING_LEN(v));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Resolves a Symbol or String name to bytes and checks it, for the writers that
|
|
149
|
+
// take either. sym holds the Symbol's name String for as long as *strp is used.
|
|
150
|
+
inline static long check_name(VALUE v, volatile VALUE *sym, const char **strp, size_t *xsizep, const char *msg) {
|
|
151
|
+
long len;
|
|
152
|
+
|
|
153
|
+
switch (rb_type(v)) {
|
|
154
|
+
case T_STRING:
|
|
155
|
+
*strp = StringValuePtr(v);
|
|
156
|
+
len = RSTRING_LEN(v);
|
|
157
|
+
break;
|
|
158
|
+
case T_SYMBOL:
|
|
159
|
+
*sym = rb_sym2str(v);
|
|
160
|
+
*strp = RSTRING_PTR(*sym);
|
|
161
|
+
len = RSTRING_LEN(*sym);
|
|
162
|
+
break;
|
|
163
|
+
default: rb_raise(ox_arg_error_class, "%s", msg); break;
|
|
164
|
+
}
|
|
165
|
+
*xsizep = check_string(*strp, (size_t)len, xml_element_chars);
|
|
166
|
+
|
|
167
|
+
return len;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
static void
|
|
171
|
+
append_string(Builder b, const char *str, size_t size, const char *table, size_t xsize, bool strip_invalid_chars) {
|
|
124
172
|
if (size == xsize) {
|
|
125
173
|
const char *s = str;
|
|
126
174
|
const char *end = str + size;
|
|
@@ -135,45 +183,107 @@ static void append_string(Builder b, const char *str, size_t size, const char *t
|
|
|
135
183
|
}
|
|
136
184
|
b->pos += size;
|
|
137
185
|
} else {
|
|
138
|
-
char
|
|
139
|
-
char
|
|
140
|
-
char
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
186
|
+
char buf[256];
|
|
187
|
+
char *bp = buf;
|
|
188
|
+
char *bend = buf + sizeof(buf) - 1;
|
|
189
|
+
const char *send = str + size;
|
|
190
|
+
|
|
191
|
+
while (str < send) {
|
|
192
|
+
/* Copy runs of pass-through bytes a word at a time into the staging
|
|
193
|
+
* buffer. A clean word has no byte below 0x20, so it can hold no
|
|
194
|
+
* newline and no '\0': col and pos advance by the whole word and the
|
|
195
|
+
* line is unchanged. On a hit the store is kept up to the first
|
|
196
|
+
* flagged byte and the rest is redone by the loops below.
|
|
197
|
+
*/
|
|
198
|
+
while (str + 8 <= send) {
|
|
199
|
+
uint64_t v;
|
|
200
|
+
uint64_t mask;
|
|
201
|
+
|
|
202
|
+
memcpy(&v, str, 8);
|
|
203
|
+
mask = xml_bytes_of_interest(v);
|
|
204
|
+
if (bend < bp + 8) {
|
|
147
205
|
buf_append_string(&b->buf, buf, bp - buf);
|
|
148
206
|
bp = buf;
|
|
149
207
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
208
|
+
memcpy(bp, str, 8);
|
|
209
|
+
if (0 != mask) {
|
|
210
|
+
int n = xml_first_of_interest((const unsigned char *)str, mask);
|
|
211
|
+
|
|
212
|
+
bp += n;
|
|
213
|
+
str += n;
|
|
214
|
+
b->col += n;
|
|
215
|
+
b->pos += n;
|
|
216
|
+
break;
|
|
155
217
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
b->pos +=
|
|
160
|
-
|
|
161
|
-
|
|
218
|
+
bp += 8;
|
|
219
|
+
str += 8;
|
|
220
|
+
b->col += 8;
|
|
221
|
+
b->pos += 8;
|
|
222
|
+
}
|
|
223
|
+
/* Pass-through bytes the word loop left: the tail shorter than a
|
|
224
|
+
* word. These are all >= 0x20 and not escape characters, so no
|
|
225
|
+
* newline check is needed.
|
|
226
|
+
*/
|
|
227
|
+
while (str < send) {
|
|
228
|
+
unsigned char c = (unsigned char)*str;
|
|
229
|
+
|
|
230
|
+
if (c < 0x20 || '"' == c || '\'' == c || '&' == c || '<' == c || '>' == c) {
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
if (bend <= bp) {
|
|
162
234
|
buf_append_string(&b->buf, buf, bp - buf);
|
|
163
235
|
bp = buf;
|
|
164
236
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
237
|
+
*bp++ = *str++;
|
|
238
|
+
b->col++;
|
|
239
|
+
b->pos++;
|
|
240
|
+
}
|
|
241
|
+
/* One byte the predicate flagged: an escape, an invalid character,
|
|
242
|
+
* or a byte the table keeps unchanged ('"' and '\'' in the element
|
|
243
|
+
* table, and 0x09/0x0a/0x0d in every table).
|
|
244
|
+
*/
|
|
245
|
+
if (str < send) {
|
|
246
|
+
int fcnt = table[(unsigned char)*str];
|
|
247
|
+
|
|
248
|
+
if ('1' == fcnt) {
|
|
249
|
+
if (bend <= bp) {
|
|
250
|
+
buf_append_string(&b->buf, buf, bp - buf);
|
|
251
|
+
bp = buf;
|
|
175
252
|
}
|
|
176
|
-
|
|
253
|
+
if ('\n' == *str) {
|
|
254
|
+
b->line++;
|
|
255
|
+
b->col = 1;
|
|
256
|
+
} else {
|
|
257
|
+
b->col++;
|
|
258
|
+
}
|
|
259
|
+
b->pos++;
|
|
260
|
+
*bp++ = *str++;
|
|
261
|
+
} else {
|
|
262
|
+
size_t written = (size_t)(fcnt - '0');
|
|
263
|
+
|
|
264
|
+
if (buf < bp) {
|
|
265
|
+
buf_append_string(&b->buf, buf, bp - buf);
|
|
266
|
+
bp = buf;
|
|
267
|
+
}
|
|
268
|
+
switch (*str) {
|
|
269
|
+
case '"': buf_append_string(&b->buf, """, 6); break;
|
|
270
|
+
case '&': buf_append_string(&b->buf, "&", 5); break;
|
|
271
|
+
case '\'': buf_append_string(&b->buf, "'", 6); break;
|
|
272
|
+
case '<': buf_append_string(&b->buf, "<", 4); break;
|
|
273
|
+
case '>': buf_append_string(&b->buf, ">", 4); break;
|
|
274
|
+
default:
|
|
275
|
+
// Must be one of the invalid characters. The table holds
|
|
276
|
+
// 10 for those, the longest escape, but nothing is
|
|
277
|
+
// written for them here.
|
|
278
|
+
if (!strip_invalid_chars) {
|
|
279
|
+
rb_raise(ox_syntax_error_class, "'\\#x%02x' is not a valid XML character.", *str);
|
|
280
|
+
}
|
|
281
|
+
written = 0;
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
b->pos += written;
|
|
285
|
+
b->col += written;
|
|
286
|
+
str++;
|
|
177
287
|
}
|
|
178
288
|
}
|
|
179
289
|
}
|
|
@@ -185,21 +295,12 @@ static void append_string(Builder b, const char *str, size_t size, const char *t
|
|
|
185
295
|
}
|
|
186
296
|
|
|
187
297
|
static void append_sym_str(Builder b, VALUE v) {
|
|
188
|
-
|
|
189
|
-
|
|
298
|
+
volatile VALUE sym = Qnil;
|
|
299
|
+
const char *s;
|
|
300
|
+
size_t xsize = 0;
|
|
301
|
+
long len = check_name(v, &sym, &s, &xsize, "expected a Symbol or String");
|
|
190
302
|
|
|
191
|
-
|
|
192
|
-
case T_STRING:
|
|
193
|
-
s = StringValuePtr(v);
|
|
194
|
-
len = RSTRING_LEN(v);
|
|
195
|
-
break;
|
|
196
|
-
case T_SYMBOL:
|
|
197
|
-
s = rb_id2name(SYM2ID(v));
|
|
198
|
-
len = strlen(s);
|
|
199
|
-
break;
|
|
200
|
-
default: rb_raise(ox_arg_error_class, "expected a Symbol or String"); break;
|
|
201
|
-
}
|
|
202
|
-
append_string(b, s, len, xml_element_chars, false);
|
|
303
|
+
append_string(b, s, len, xml_element_chars, xsize, false);
|
|
203
304
|
}
|
|
204
305
|
|
|
205
306
|
static void i_am_a_child(Builder b, bool is_text) {
|
|
@@ -219,17 +320,30 @@ static void i_am_a_child(Builder b, bool is_text) {
|
|
|
219
320
|
}
|
|
220
321
|
|
|
221
322
|
static int append_attr(VALUE key, VALUE value, VALUE bv) {
|
|
222
|
-
Builder
|
|
323
|
+
Builder b = (Builder)bv;
|
|
324
|
+
volatile VALUE sym = Qnil;
|
|
325
|
+
const char *ks;
|
|
326
|
+
long klen;
|
|
327
|
+
size_t kx;
|
|
328
|
+
size_t vsize;
|
|
329
|
+
size_t vx;
|
|
330
|
+
|
|
331
|
+
// Both halves before the space: an attribute is either written whole or not
|
|
332
|
+
// at all, and the element it belongs to is left as it was.
|
|
333
|
+
Check_Type(value, T_STRING);
|
|
334
|
+
klen = check_name(key, &sym, &ks, &kx, "expected a Symbol or String");
|
|
335
|
+
check_name_chars(ks, (size_t)klen, true);
|
|
336
|
+
vsize = (size_t)RSTRING_LEN(value);
|
|
337
|
+
vx = check_string(StringValuePtr(value), vsize, xml_attr_chars);
|
|
223
338
|
|
|
224
339
|
buf_append(&b->buf, ' ');
|
|
225
340
|
b->col++;
|
|
226
341
|
b->pos++;
|
|
227
|
-
|
|
342
|
+
append_string(b, ks, klen, xml_element_chars, kx, false);
|
|
228
343
|
buf_append_string(&b->buf, "=\"", 2);
|
|
229
344
|
b->col += 2;
|
|
230
345
|
b->pos += 2;
|
|
231
|
-
|
|
232
|
-
append_string(b, StringValuePtr(value), (int)RSTRING_LEN(value), xml_attr_chars, false);
|
|
346
|
+
append_string(b, StringValuePtr(value), vsize, xml_attr_chars, vx, false);
|
|
233
347
|
buf_append(&b->buf, '"');
|
|
234
348
|
b->col++;
|
|
235
349
|
b->pos++;
|
|
@@ -257,7 +371,7 @@ static void builder_free(void *ptr) {
|
|
|
257
371
|
}
|
|
258
372
|
b = (Builder)ptr;
|
|
259
373
|
buf_cleanup(&b->buf);
|
|
260
|
-
for (e = b->stack, d = b->depth; 0
|
|
374
|
+
for (e = b->stack, d = b->depth; 0 <= d; d--, e++) {
|
|
261
375
|
if (e->name != e->buf) {
|
|
262
376
|
free(e->name);
|
|
263
377
|
}
|
|
@@ -278,10 +392,17 @@ static void pop(Builder b) {
|
|
|
278
392
|
append_indent(b);
|
|
279
393
|
}
|
|
280
394
|
buf_append_string(&b->buf, "</", 2);
|
|
281
|
-
append_string(b,
|
|
395
|
+
append_string(b,
|
|
396
|
+
e->name,
|
|
397
|
+
e->len,
|
|
398
|
+
xml_element_chars,
|
|
399
|
+
xml_str_len((const unsigned char *)e->name, e->len, xml_element_chars),
|
|
400
|
+
false);
|
|
282
401
|
buf_append(&b->buf, '>');
|
|
283
|
-
|
|
284
|
-
|
|
402
|
+
// append_string() already counted the name, so this is only the "</"
|
|
403
|
+
// and the ">".
|
|
404
|
+
b->col += 3;
|
|
405
|
+
b->pos += 3;
|
|
285
406
|
if (e->buf != e->name) {
|
|
286
407
|
free(e->name);
|
|
287
408
|
e->name = 0;
|
|
@@ -311,18 +432,21 @@ static void bclose(Builder b) {
|
|
|
311
432
|
|
|
312
433
|
static VALUE to_s(Builder b) {
|
|
313
434
|
volatile VALUE rstr;
|
|
435
|
+
size_t len;
|
|
314
436
|
|
|
315
437
|
if (0 != b->buf.fd) {
|
|
316
438
|
rb_raise(ox_arg_error_class, "can not create a String with a stream or file builder.");
|
|
317
439
|
}
|
|
318
|
-
|
|
319
|
-
buf_append(&b->buf, '\n');
|
|
320
|
-
b->line++;
|
|
321
|
-
b->col = 1;
|
|
322
|
-
b->pos++;
|
|
323
|
-
}
|
|
440
|
+
len = buf_len(&b->buf);
|
|
324
441
|
*b->buf.tail = '\0'; // for debugging
|
|
325
|
-
rstr = rb_str_new(b->buf.head,
|
|
442
|
+
rstr = rb_str_new(b->buf.head, len);
|
|
443
|
+
// The closing newline goes on the String and not the buffer. Appended to the
|
|
444
|
+
// buffer it stays there, so a to_s taken before the document is finished
|
|
445
|
+
// leaves a newline in the middle of it, and in the middle of a value if that
|
|
446
|
+
// is where the builder was.
|
|
447
|
+
if (0 <= b->indent && (0 == len || '\n' != b->buf.head[len - 1])) {
|
|
448
|
+
rb_str_cat(rstr, "\n", 1);
|
|
449
|
+
}
|
|
326
450
|
|
|
327
451
|
if ('\0' != *b->encoding) {
|
|
328
452
|
rb_enc_associate(rstr, rb_enc_find(b->encoding));
|
|
@@ -396,7 +520,7 @@ static VALUE builder_file(int argc, VALUE *argv, VALUE self) {
|
|
|
396
520
|
rb_raise(ox_arg_error_class, "missing filename");
|
|
397
521
|
}
|
|
398
522
|
Check_Type(*argv, T_STRING);
|
|
399
|
-
if (NULL == (f = fopen(StringValuePtr(*argv), "
|
|
523
|
+
if (NULL == (f = fopen(StringValuePtr(*argv), "wb"))) {
|
|
400
524
|
xfree(b);
|
|
401
525
|
rb_raise(rb_eIOError, "%s\n", strerror(errno));
|
|
402
526
|
}
|
|
@@ -494,6 +618,20 @@ static VALUE builder_instruct(int argc, VALUE *argv, VALUE self) {
|
|
|
494
618
|
Builder b;
|
|
495
619
|
|
|
496
620
|
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
621
|
+
if (0 < argc) {
|
|
622
|
+
volatile VALUE nsym = Qnil;
|
|
623
|
+
const char *nstr;
|
|
624
|
+
size_t nx;
|
|
625
|
+
|
|
626
|
+
check_name(*argv, &nsym, &nstr, &nx, "expected a Symbol or String");
|
|
627
|
+
if (1 < argc && rb_cHash == rb_obj_class(argv[1])) {
|
|
628
|
+
// The "<?" and the target are out by the time the values are
|
|
629
|
+
// written, so a raise down there would leave them in the buffer.
|
|
630
|
+
check_instruct_value(argv[1], ox_version_sym);
|
|
631
|
+
check_instruct_value(argv[1], ox_encoding_sym);
|
|
632
|
+
check_instruct_value(argv[1], ox_standalone_sym);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
497
635
|
i_am_a_child(b, false);
|
|
498
636
|
append_indent(b);
|
|
499
637
|
if (0 == argc) {
|
|
@@ -562,33 +700,29 @@ static VALUE builder_instruct(int argc, VALUE *argv, VALUE self) {
|
|
|
562
700
|
* - +attributes+ - (Hash) of the element
|
|
563
701
|
*/
|
|
564
702
|
static VALUE builder_element(int argc, VALUE *argv, VALUE self) {
|
|
565
|
-
Builder
|
|
566
|
-
Element
|
|
567
|
-
|
|
568
|
-
|
|
703
|
+
Builder b;
|
|
704
|
+
Element e;
|
|
705
|
+
volatile VALUE sym = Qnil;
|
|
706
|
+
size_t xsize = 0;
|
|
707
|
+
const char *name;
|
|
708
|
+
long len;
|
|
569
709
|
|
|
570
710
|
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
571
711
|
|
|
572
712
|
if (1 > argc) {
|
|
573
713
|
rb_raise(ox_arg_error_class, "missing element name");
|
|
574
714
|
}
|
|
715
|
+
// Everything that can raise has to run before the first write, or the raise
|
|
716
|
+
// leaves an element started that no later call can finish or take back. It
|
|
717
|
+
// also has to run before b->depth++, or the stack slot is never filled in.
|
|
718
|
+
len = check_name(*argv, &sym, &name, &xsize, "expected a Symbol or String for an element name");
|
|
719
|
+
check_name_chars(name, (size_t)len, false);
|
|
720
|
+
if (MAX_DEPTH <= b->depth + 1) {
|
|
721
|
+
rb_raise(ox_arg_error_class, "XML too deeply nested");
|
|
722
|
+
}
|
|
575
723
|
i_am_a_child(b, false);
|
|
576
724
|
append_indent(b);
|
|
577
725
|
b->depth++;
|
|
578
|
-
if (MAX_DEPTH <= b->depth) {
|
|
579
|
-
rb_raise(ox_arg_error_class, "XML too deeply nested");
|
|
580
|
-
}
|
|
581
|
-
switch (rb_type(*argv)) {
|
|
582
|
-
case T_STRING:
|
|
583
|
-
name = StringValuePtr(*argv);
|
|
584
|
-
len = RSTRING_LEN(*argv);
|
|
585
|
-
break;
|
|
586
|
-
case T_SYMBOL:
|
|
587
|
-
name = rb_id2name(SYM2ID(*argv));
|
|
588
|
-
len = strlen(name);
|
|
589
|
-
break;
|
|
590
|
-
default: rb_raise(ox_arg_error_class, "expected a Symbol or String for an element name"); break;
|
|
591
|
-
}
|
|
592
726
|
e = &b->stack[b->depth];
|
|
593
727
|
if (sizeof(e->buf) <= (size_t)len) {
|
|
594
728
|
e->name = strdup(name);
|
|
@@ -604,7 +738,7 @@ static VALUE builder_element(int argc, VALUE *argv, VALUE self) {
|
|
|
604
738
|
buf_append(&b->buf, '<');
|
|
605
739
|
b->col++;
|
|
606
740
|
b->pos++;
|
|
607
|
-
append_string(b, e->name, len, xml_element_chars, false);
|
|
741
|
+
append_string(b, e->name, len, xml_element_chars, xsize, false);
|
|
608
742
|
if (1 < argc && T_HASH == rb_type(argv[1])) {
|
|
609
743
|
rb_hash_foreach(argv[1], append_attr, (VALUE)b);
|
|
610
744
|
}
|
|
@@ -624,32 +758,25 @@ static VALUE builder_element(int argc, VALUE *argv, VALUE self) {
|
|
|
624
758
|
* - +attributes+ - (Hash) of the element
|
|
625
759
|
*/
|
|
626
760
|
static VALUE builder_void_element(int argc, VALUE *argv, VALUE self) {
|
|
627
|
-
Builder
|
|
628
|
-
|
|
629
|
-
|
|
761
|
+
Builder b;
|
|
762
|
+
volatile VALUE sym = Qnil;
|
|
763
|
+
size_t xsize = 0;
|
|
764
|
+
const char *name;
|
|
765
|
+
long len;
|
|
630
766
|
|
|
631
767
|
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
632
768
|
|
|
633
769
|
if (1 > argc) {
|
|
634
770
|
rb_raise(ox_arg_error_class, "missing element name");
|
|
635
771
|
}
|
|
772
|
+
len = check_name(*argv, &sym, &name, &xsize, "expected a Symbol or String for an element name");
|
|
773
|
+
check_name_chars(name, (size_t)len, false);
|
|
636
774
|
i_am_a_child(b, false);
|
|
637
775
|
append_indent(b);
|
|
638
|
-
switch (rb_type(*argv)) {
|
|
639
|
-
case T_STRING:
|
|
640
|
-
name = StringValuePtr(*argv);
|
|
641
|
-
len = RSTRING_LEN(*argv);
|
|
642
|
-
break;
|
|
643
|
-
case T_SYMBOL:
|
|
644
|
-
name = rb_id2name(SYM2ID(*argv));
|
|
645
|
-
len = strlen(name);
|
|
646
|
-
break;
|
|
647
|
-
default: rb_raise(ox_arg_error_class, "expected a Symbol or String for an element name"); break;
|
|
648
|
-
}
|
|
649
776
|
buf_append(&b->buf, '<');
|
|
650
777
|
b->col++;
|
|
651
778
|
b->pos++;
|
|
652
|
-
append_string(b, name, len, xml_element_chars, false);
|
|
779
|
+
append_string(b, name, len, xml_element_chars, xsize, false);
|
|
653
780
|
if (1 < argc && T_HASH == rb_type(argv[1])) {
|
|
654
781
|
rb_hash_foreach(argv[1], append_attr, (VALUE)b);
|
|
655
782
|
}
|
|
@@ -668,18 +795,22 @@ static VALUE builder_void_element(int argc, VALUE *argv, VALUE self) {
|
|
|
668
795
|
*/
|
|
669
796
|
static VALUE builder_comment(VALUE self, VALUE text) {
|
|
670
797
|
Builder b;
|
|
798
|
+
size_t size;
|
|
799
|
+
size_t xsize;
|
|
671
800
|
|
|
672
801
|
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
673
802
|
rb_check_type(text, T_STRING);
|
|
803
|
+
size = (size_t)RSTRING_LEN(text);
|
|
804
|
+
xsize = check_string(StringValuePtr(text), size, xml_element_chars);
|
|
674
805
|
i_am_a_child(b, false);
|
|
675
806
|
append_indent(b);
|
|
676
807
|
buf_append_string(&b->buf, "<!--", 4);
|
|
677
|
-
b->col +=
|
|
678
|
-
b->pos +=
|
|
679
|
-
append_string(b, StringValuePtr(text),
|
|
808
|
+
b->col += 4;
|
|
809
|
+
b->pos += 4;
|
|
810
|
+
append_string(b, StringValuePtr(text), size, xml_element_chars, xsize, false);
|
|
680
811
|
buf_append_string(&b->buf, "-->", 3);
|
|
681
|
-
b->col +=
|
|
682
|
-
b->pos +=
|
|
812
|
+
b->col += 3;
|
|
813
|
+
b->pos += 3;
|
|
683
814
|
|
|
684
815
|
return Qnil;
|
|
685
816
|
}
|
|
@@ -691,15 +822,19 @@ static VALUE builder_comment(VALUE self, VALUE text) {
|
|
|
691
822
|
*/
|
|
692
823
|
static VALUE builder_doctype(VALUE self, VALUE text) {
|
|
693
824
|
Builder b;
|
|
825
|
+
size_t size;
|
|
826
|
+
size_t xsize;
|
|
694
827
|
|
|
695
828
|
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
696
829
|
rb_check_type(text, T_STRING);
|
|
830
|
+
size = (size_t)RSTRING_LEN(text);
|
|
831
|
+
xsize = check_string(StringValuePtr(text), size, xml_element_chars);
|
|
697
832
|
i_am_a_child(b, false);
|
|
698
833
|
append_indent(b);
|
|
699
834
|
buf_append_string(&b->buf, "<!DOCTYPE ", 10);
|
|
700
835
|
b->col += 10;
|
|
701
836
|
b->pos += 10;
|
|
702
|
-
append_string(b, StringValuePtr(text),
|
|
837
|
+
append_string(b, StringValuePtr(text), size, xml_element_chars, xsize, false);
|
|
703
838
|
buf_append(&b->buf, '>');
|
|
704
839
|
b->col++;
|
|
705
840
|
b->pos++;
|
|
@@ -717,6 +852,8 @@ static VALUE builder_text(int argc, VALUE *argv, VALUE self) {
|
|
|
717
852
|
Builder b;
|
|
718
853
|
volatile VALUE v;
|
|
719
854
|
volatile VALUE strip_invalid_chars;
|
|
855
|
+
size_t size;
|
|
856
|
+
size_t xsize;
|
|
720
857
|
|
|
721
858
|
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
722
859
|
|
|
@@ -730,9 +867,14 @@ static VALUE builder_text(int argc, VALUE *argv, VALUE self) {
|
|
|
730
867
|
strip_invalid_chars = Qfalse;
|
|
731
868
|
}
|
|
732
869
|
|
|
733
|
-
v
|
|
870
|
+
v = rb_String(v);
|
|
871
|
+
size = (size_t)RSTRING_LEN(v);
|
|
872
|
+
// Stripping is allowed to drop the byte, so it is the one writer that does
|
|
873
|
+
// not have to refuse the value up front.
|
|
874
|
+
xsize = RTEST(strip_invalid_chars) ? xml_str_len((const unsigned char *)StringValuePtr(v), size, xml_element_chars)
|
|
875
|
+
: check_string(StringValuePtr(v), size, xml_element_chars);
|
|
734
876
|
i_am_a_child(b, true);
|
|
735
|
-
append_string(b, StringValuePtr(v),
|
|
877
|
+
append_string(b, StringValuePtr(v), size, xml_element_chars, xsize, RTEST(strip_invalid_chars));
|
|
736
878
|
|
|
737
879
|
return Qnil;
|
|
738
880
|
}
|
|
@@ -748,29 +890,42 @@ static VALUE builder_cdata(VALUE self, VALUE data) {
|
|
|
748
890
|
const char *str;
|
|
749
891
|
const char *s;
|
|
750
892
|
const char *end;
|
|
893
|
+
const char *from;
|
|
894
|
+
const char *split;
|
|
895
|
+
size_t extra = 0;
|
|
751
896
|
int len;
|
|
752
897
|
|
|
753
898
|
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
754
899
|
|
|
755
|
-
v
|
|
756
|
-
str
|
|
757
|
-
len
|
|
758
|
-
s
|
|
759
|
-
end
|
|
900
|
+
v = rb_String(v);
|
|
901
|
+
str = StringValuePtr(v);
|
|
902
|
+
len = (int)RSTRING_LEN(v);
|
|
903
|
+
s = str;
|
|
904
|
+
end = str + len;
|
|
905
|
+
from = str;
|
|
906
|
+
check_unescaped(str, (size_t)len);
|
|
760
907
|
i_am_a_child(b, false);
|
|
761
908
|
append_indent(b);
|
|
762
909
|
buf_append_string(&b->buf, "<![CDATA[", 9);
|
|
763
910
|
b->col += 9;
|
|
764
911
|
b->pos += 9;
|
|
765
|
-
|
|
766
|
-
|
|
912
|
+
// See xml_cdata_end(). The ']]' stays in this section and the '>' opens the
|
|
913
|
+
// next one, so from lands on the '>' each time round.
|
|
914
|
+
while (NULL != (split = xml_cdata_end(from, end))) {
|
|
915
|
+
buf_append_string(&b->buf, from, (split + 2) - from);
|
|
916
|
+
buf_append_string(&b->buf, "]]><![CDATA[", CDATA_SPLIT_EXTRA);
|
|
917
|
+
extra += CDATA_SPLIT_EXTRA;
|
|
918
|
+
from = split + 2;
|
|
919
|
+
}
|
|
920
|
+
buf_append_string(&b->buf, from, end - from);
|
|
921
|
+
b->col += len + extra;
|
|
767
922
|
s = strchr(s, '\n');
|
|
768
923
|
while (NULL != s) {
|
|
769
924
|
b->line++;
|
|
770
925
|
b->col = end - s;
|
|
771
926
|
s = strchr(s + 1, '\n');
|
|
772
927
|
}
|
|
773
|
-
b->pos += len;
|
|
928
|
+
b->pos += len + extra;
|
|
774
929
|
buf_append_string(&b->buf, "]]>", 3);
|
|
775
930
|
b->col += 3;
|
|
776
931
|
b->pos += 3;
|