ox-bundlecachetest 2.14.23
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 +7 -0
- data/CHANGELOG.md +751 -0
- data/LICENSE +21 -0
- data/README.md +351 -0
- data/ext/ox/attr.h +78 -0
- data/ext/ox/base64.c +105 -0
- data/ext/ox/base64.h +18 -0
- data/ext/ox/buf.h +162 -0
- data/ext/ox/builder.c +948 -0
- data/ext/ox/cache.c +351 -0
- data/ext/ox/cache.h +21 -0
- data/ext/ox/cache8.c +106 -0
- data/ext/ox/cache8.h +23 -0
- data/ext/ox/dump.c +1260 -0
- data/ext/ox/err.c +46 -0
- data/ext/ox/err.h +36 -0
- data/ext/ox/extconf.rb +47 -0
- data/ext/ox/gen_load.c +342 -0
- data/ext/ox/hash_load.c +309 -0
- data/ext/ox/helper.h +84 -0
- data/ext/ox/intern.c +157 -0
- data/ext/ox/intern.h +25 -0
- data/ext/ox/obj_load.c +809 -0
- data/ext/ox/ox.c +1649 -0
- data/ext/ox/ox.h +245 -0
- data/ext/ox/parse.c +1197 -0
- data/ext/ox/sax.c +1570 -0
- data/ext/ox/sax.h +69 -0
- data/ext/ox/sax_as.c +270 -0
- data/ext/ox/sax_buf.c +209 -0
- data/ext/ox/sax_buf.h +204 -0
- data/ext/ox/sax_hint.c +207 -0
- data/ext/ox/sax_hint.h +40 -0
- data/ext/ox/sax_stack.h +113 -0
- data/ext/ox/slotcache.c +158 -0
- data/ext/ox/slotcache.h +19 -0
- data/ext/ox/special.c +390 -0
- data/ext/ox/special.h +14 -0
- data/ext/ox/type.h +39 -0
- data/lib/ox/bag.rb +103 -0
- data/lib/ox/cdata.rb +10 -0
- data/lib/ox/comment.rb +11 -0
- data/lib/ox/doctype.rb +11 -0
- data/lib/ox/document.rb +28 -0
- data/lib/ox/element.rb +464 -0
- data/lib/ox/error.rb +25 -0
- data/lib/ox/hasattrs.rb +54 -0
- data/lib/ox/instruct.rb +34 -0
- data/lib/ox/node.rb +23 -0
- data/lib/ox/raw.rb +12 -0
- data/lib/ox/sax.rb +97 -0
- data/lib/ox/version.rb +4 -0
- data/lib/ox/xmlrpc_adapter.rb +33 -0
- data/lib/ox.rb +79 -0
- metadata +128 -0
data/ext/ox/dump.c
ADDED
|
@@ -0,0 +1,1260 @@
|
|
|
1
|
+
/* dump.c
|
|
2
|
+
* Copyright (c) 2011, Peter Ohler
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#include <errno.h>
|
|
7
|
+
#include <stdio.h>
|
|
8
|
+
#include <stdlib.h>
|
|
9
|
+
#include <string.h>
|
|
10
|
+
#include <time.h>
|
|
11
|
+
|
|
12
|
+
#include "base64.h"
|
|
13
|
+
#include "cache8.h"
|
|
14
|
+
#include "ox.h"
|
|
15
|
+
|
|
16
|
+
#define USE_B64 0
|
|
17
|
+
#define MAX_DEPTH 1000
|
|
18
|
+
|
|
19
|
+
typedef unsigned long ulong;
|
|
20
|
+
|
|
21
|
+
typedef struct _str {
|
|
22
|
+
const char *str;
|
|
23
|
+
size_t len;
|
|
24
|
+
} *Str;
|
|
25
|
+
|
|
26
|
+
typedef struct _element {
|
|
27
|
+
struct _str clas;
|
|
28
|
+
struct _str attr;
|
|
29
|
+
unsigned long id;
|
|
30
|
+
int indent; /* < 0 indicates no \n */
|
|
31
|
+
int closed;
|
|
32
|
+
char type;
|
|
33
|
+
} *Element;
|
|
34
|
+
|
|
35
|
+
typedef struct _out {
|
|
36
|
+
void (*w_start)(struct _out *out, Element e);
|
|
37
|
+
void (*w_end)(struct _out *out, Element e);
|
|
38
|
+
void (*w_time)(struct _out *out, VALUE obj);
|
|
39
|
+
char *buf;
|
|
40
|
+
char *end;
|
|
41
|
+
char *cur;
|
|
42
|
+
Cache8 circ_cache;
|
|
43
|
+
unsigned long circ_cnt;
|
|
44
|
+
int indent;
|
|
45
|
+
int depth; /* used by dumpHash */
|
|
46
|
+
Options opts;
|
|
47
|
+
VALUE obj;
|
|
48
|
+
} *Out;
|
|
49
|
+
|
|
50
|
+
static void dump_obj_to_xml(VALUE obj, Options copts, Out out);
|
|
51
|
+
|
|
52
|
+
static void dump_first_obj(VALUE obj, Out out);
|
|
53
|
+
static void dump_obj(ID aid, VALUE obj, int depth, Out out);
|
|
54
|
+
static void dump_gen_doc(VALUE obj, int depth, Out out);
|
|
55
|
+
static void dump_gen_element(VALUE obj, int depth, Out out);
|
|
56
|
+
static void dump_gen_instruct(VALUE obj, int depth, Out out);
|
|
57
|
+
static int dump_gen_attr(VALUE key, VALUE value, VALUE ov);
|
|
58
|
+
static int dump_gen_nodes(VALUE obj, int depth, Out out);
|
|
59
|
+
static void
|
|
60
|
+
dump_gen_val_node(VALUE obj, int depth, const char *pre, size_t plen, const char *suf, size_t slen, Out out);
|
|
61
|
+
|
|
62
|
+
static void dump_start(Out out, Element e);
|
|
63
|
+
static void dump_end(Out out, Element e);
|
|
64
|
+
|
|
65
|
+
static void grow(Out out, size_t len);
|
|
66
|
+
|
|
67
|
+
static void dump_value(Out out, const char *value, size_t size);
|
|
68
|
+
static void dump_str_value(Out out, const char *value, size_t size, const char *table);
|
|
69
|
+
static int dump_var(ID key, VALUE value, VALUE ov);
|
|
70
|
+
static void dump_num(Out out, VALUE obj);
|
|
71
|
+
static void dump_date(Out out, VALUE obj);
|
|
72
|
+
static void dump_time_thin(Out out, VALUE obj);
|
|
73
|
+
static void dump_time_xsd(Out out, VALUE obj);
|
|
74
|
+
static int dump_hash(VALUE key, VALUE value, VALUE ov);
|
|
75
|
+
|
|
76
|
+
static int is_xml_friendly(const uchar *str, int len, const char *table);
|
|
77
|
+
|
|
78
|
+
static const char hex_chars[17] = "0123456789abcdef";
|
|
79
|
+
|
|
80
|
+
// The : character is equivalent to 10. Used for replacement characters up to 10
|
|
81
|
+
// characters long such as ''.
|
|
82
|
+
static const char xml_friendly_chars[257] = "\
|
|
83
|
+
:::::::::11::1::::::::::::::::::\
|
|
84
|
+
11611156111111111111111111114141\
|
|
85
|
+
11111111111111111111111111111111\
|
|
86
|
+
11111111111111111111111111111111\
|
|
87
|
+
11111111111111111111111111111111\
|
|
88
|
+
11111111111111111111111111111111\
|
|
89
|
+
11111111111111111111111111111111\
|
|
90
|
+
11111111111111111111111111111111";
|
|
91
|
+
|
|
92
|
+
static const char xml_quote_chars[257] = "\
|
|
93
|
+
:::::::::11::1::::::::::::::::::\
|
|
94
|
+
11611151111111111111111111114141\
|
|
95
|
+
11111111111111111111111111111111\
|
|
96
|
+
11111111111111111111111111111111\
|
|
97
|
+
11111111111111111111111111111111\
|
|
98
|
+
11111111111111111111111111111111\
|
|
99
|
+
11111111111111111111111111111111\
|
|
100
|
+
11111111111111111111111111111111";
|
|
101
|
+
|
|
102
|
+
static const char xml_element_chars[257] = "\
|
|
103
|
+
:::::::::11::1::::::::::::::::::\
|
|
104
|
+
11111151111111111111111111114141\
|
|
105
|
+
11111111111111111111111111111111\
|
|
106
|
+
11111111111111111111111111111111\
|
|
107
|
+
11111111111111111111111111111111\
|
|
108
|
+
11111111111111111111111111111111\
|
|
109
|
+
11111111111111111111111111111111\
|
|
110
|
+
11111111111111111111111111111111";
|
|
111
|
+
|
|
112
|
+
inline static int is_xml_friendly(const uchar *str, int len, const char *table) {
|
|
113
|
+
for (; 0 < len; str++, len--) {
|
|
114
|
+
if ('1' != table[*str]) {
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return 1;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
inline static size_t xml_str_len(const uchar *str, size_t len, const char *table) {
|
|
122
|
+
size_t size = 0;
|
|
123
|
+
|
|
124
|
+
for (; 0 < len; str++, len--) {
|
|
125
|
+
size += xml_friendly_chars[*str];
|
|
126
|
+
}
|
|
127
|
+
return size - len * (size_t)'0';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
inline static void dump_hex(uchar c, Out out) {
|
|
131
|
+
uchar d = (c >> 4) & 0x0F;
|
|
132
|
+
|
|
133
|
+
*out->cur++ = hex_chars[d];
|
|
134
|
+
d = c & 0x0F;
|
|
135
|
+
*out->cur++ = hex_chars[d];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
static Type obj_class_code(VALUE obj) {
|
|
139
|
+
VALUE clas = rb_obj_class(obj);
|
|
140
|
+
|
|
141
|
+
switch (rb_type(obj)) {
|
|
142
|
+
case T_NIL: return NilClassCode;
|
|
143
|
+
case T_ARRAY: return ArrayCode;
|
|
144
|
+
case T_HASH: return HashCode;
|
|
145
|
+
case T_TRUE: return TrueClassCode;
|
|
146
|
+
case T_FALSE: return FalseClassCode;
|
|
147
|
+
case T_FIXNUM: return FixnumCode;
|
|
148
|
+
case T_FLOAT: return FloatCode;
|
|
149
|
+
case T_STRING:
|
|
150
|
+
return (is_xml_friendly((uchar *)StringValuePtr(obj), (int)RSTRING_LEN(obj), xml_element_chars)) ? StringCode
|
|
151
|
+
: String64Code;
|
|
152
|
+
case T_SYMBOL: {
|
|
153
|
+
const char *sym = rb_id2name(SYM2ID(obj));
|
|
154
|
+
|
|
155
|
+
return (is_xml_friendly((uchar *)sym, (int)strlen(sym), xml_element_chars)) ? SymbolCode : Symbol64Code;
|
|
156
|
+
}
|
|
157
|
+
case T_DATA: return (rb_cTime == clas) ? TimeCode : ((ox_date_class == clas) ? DateCode : 0);
|
|
158
|
+
case T_STRUCT: return (rb_cRange == clas) ? RangeCode : StructCode;
|
|
159
|
+
case T_OBJECT: return (ox_document_clas == clas || ox_element_clas == clas) ? RawCode : ObjectCode;
|
|
160
|
+
case T_REGEXP: return RegexpCode;
|
|
161
|
+
case T_BIGNUM: return BignumCode;
|
|
162
|
+
case T_COMPLEX: return ComplexCode;
|
|
163
|
+
case T_RATIONAL: return RationalCode;
|
|
164
|
+
case T_CLASS: return ClassCode;
|
|
165
|
+
default: return 0;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
inline static void fill_indent(Out out, int cnt) {
|
|
170
|
+
if (0 <= cnt) {
|
|
171
|
+
*out->cur++ = '\n';
|
|
172
|
+
if (0 < out->opts->margin_len) {
|
|
173
|
+
memcpy(out->cur, out->opts->margin, out->opts->margin_len);
|
|
174
|
+
out->cur += out->opts->margin_len;
|
|
175
|
+
}
|
|
176
|
+
for (; 0 < cnt; cnt--) {
|
|
177
|
+
*out->cur++ = ' ';
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
inline static void fill_value(Out out, const char *value, size_t len) {
|
|
183
|
+
if (6 < len) {
|
|
184
|
+
memcpy(out->cur, value, len);
|
|
185
|
+
out->cur += len;
|
|
186
|
+
} else {
|
|
187
|
+
for (; 0 < len; len--, value++) {
|
|
188
|
+
*out->cur++ = *value;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
inline static void fill_attr(Out out, char name, const char *value, size_t len) {
|
|
194
|
+
*out->cur++ = ' ';
|
|
195
|
+
*out->cur++ = name;
|
|
196
|
+
*out->cur++ = '=';
|
|
197
|
+
*out->cur++ = '"';
|
|
198
|
+
if (6 < len) {
|
|
199
|
+
memcpy(out->cur, value, len);
|
|
200
|
+
out->cur += len;
|
|
201
|
+
} else {
|
|
202
|
+
for (; 0 < len; len--, value++) {
|
|
203
|
+
*out->cur++ = *value;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
*out->cur++ = '"';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
inline static const char *ulong2str(ulong num, char *end) {
|
|
210
|
+
char *b;
|
|
211
|
+
|
|
212
|
+
*end-- = '\0';
|
|
213
|
+
for (b = end; 0 < num || b == end; num /= 10, b--) {
|
|
214
|
+
*b = (num % 10) + '0';
|
|
215
|
+
}
|
|
216
|
+
b++;
|
|
217
|
+
|
|
218
|
+
return b;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
static int check_circular(Out out, VALUE obj, Element e) {
|
|
222
|
+
slot_t *slot;
|
|
223
|
+
slot_t id;
|
|
224
|
+
int result;
|
|
225
|
+
|
|
226
|
+
if (0 == (id = ox_cache8_get(out->circ_cache, obj, &slot))) {
|
|
227
|
+
out->circ_cnt++;
|
|
228
|
+
id = out->circ_cnt;
|
|
229
|
+
*slot = id;
|
|
230
|
+
e->id = id;
|
|
231
|
+
result = 0;
|
|
232
|
+
} else {
|
|
233
|
+
e->type = RefCode;
|
|
234
|
+
e->clas.len = 0;
|
|
235
|
+
e->clas.str = 0;
|
|
236
|
+
e->closed = 1;
|
|
237
|
+
e->id = id;
|
|
238
|
+
out->w_start(out, e);
|
|
239
|
+
result = 1;
|
|
240
|
+
}
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
static void grow(Out out, size_t len) {
|
|
245
|
+
size_t size = out->end - out->buf;
|
|
246
|
+
long pos = out->cur - out->buf;
|
|
247
|
+
|
|
248
|
+
size *= 2;
|
|
249
|
+
if (size <= len * 2 + pos) {
|
|
250
|
+
size += len;
|
|
251
|
+
}
|
|
252
|
+
REALLOC_N(out->buf, char, size + 10); /* 10 extra for terminator character plus extra (paranoid) */
|
|
253
|
+
out->end = out->buf + size;
|
|
254
|
+
out->cur = out->buf + pos;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
static void dump_start(Out out, Element e) {
|
|
258
|
+
size_t size = e->indent + 4 + out->opts->margin_len;
|
|
259
|
+
|
|
260
|
+
if (0 < e->attr.len) { /* a="attr" */
|
|
261
|
+
size += e->attr.len + 5;
|
|
262
|
+
}
|
|
263
|
+
if (0 < e->clas.len) { /* c="class" */
|
|
264
|
+
size += e->clas.len + 5;
|
|
265
|
+
}
|
|
266
|
+
if (0 < e->id) { /* i="id" */
|
|
267
|
+
size += 24; /* over estimate, 19 digits */
|
|
268
|
+
}
|
|
269
|
+
if (out->end - out->cur <= (long)size) {
|
|
270
|
+
grow(out, size);
|
|
271
|
+
}
|
|
272
|
+
if (out->buf + out->opts->margin_len < out->cur) {
|
|
273
|
+
fill_indent(out, e->indent);
|
|
274
|
+
}
|
|
275
|
+
*out->cur++ = '<';
|
|
276
|
+
*out->cur++ = e->type;
|
|
277
|
+
if (0 < e->attr.len) {
|
|
278
|
+
fill_attr(out, 'a', e->attr.str, e->attr.len);
|
|
279
|
+
}
|
|
280
|
+
if ((ObjectCode == e->type || ExceptionCode == e->type || StructCode == e->type || ClassCode == e->type) &&
|
|
281
|
+
0 < e->clas.len) {
|
|
282
|
+
fill_attr(out, 'c', e->clas.str, e->clas.len);
|
|
283
|
+
}
|
|
284
|
+
if (0 < e->id) {
|
|
285
|
+
char buf[32];
|
|
286
|
+
char *end = buf + sizeof(buf) - 1;
|
|
287
|
+
const char *s = ulong2str(e->id, end);
|
|
288
|
+
|
|
289
|
+
fill_attr(out, 'i', s, end - s);
|
|
290
|
+
}
|
|
291
|
+
if (e->closed) {
|
|
292
|
+
if (out->opts->no_empty) {
|
|
293
|
+
*out->cur++ = '>';
|
|
294
|
+
*out->cur++ = '<';
|
|
295
|
+
*out->cur++ = '/';
|
|
296
|
+
*out->cur++ = e->type;
|
|
297
|
+
} else {
|
|
298
|
+
*out->cur++ = '/';
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
*out->cur++ = '>';
|
|
302
|
+
*out->cur = '\0';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
static void dump_end(Out out, Element e) {
|
|
306
|
+
size_t size = e->indent + 5 + out->opts->margin_len;
|
|
307
|
+
|
|
308
|
+
if (out->end - out->cur <= (long)size) {
|
|
309
|
+
grow(out, size);
|
|
310
|
+
}
|
|
311
|
+
fill_indent(out, e->indent);
|
|
312
|
+
*out->cur++ = '<';
|
|
313
|
+
*out->cur++ = '/';
|
|
314
|
+
*out->cur++ = e->type;
|
|
315
|
+
*out->cur++ = '>';
|
|
316
|
+
*out->cur = '\0';
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
inline static void dump_value(Out out, const char *value, size_t size) {
|
|
320
|
+
if (out->end - out->cur <= (long)size) {
|
|
321
|
+
grow(out, size);
|
|
322
|
+
}
|
|
323
|
+
if (6 < size) {
|
|
324
|
+
memcpy(out->cur, value, size);
|
|
325
|
+
out->cur += size;
|
|
326
|
+
} else {
|
|
327
|
+
for (; 0 < size; size--, value++) {
|
|
328
|
+
*out->cur++ = *value;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
*out->cur = '\0';
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
inline static void dump_str_value(Out out, const char *value, size_t size, const char *table) {
|
|
335
|
+
size_t xsize = xml_str_len((const uchar *)value, size, table);
|
|
336
|
+
|
|
337
|
+
if (out->end - out->cur <= (long)xsize) {
|
|
338
|
+
grow(out, xsize);
|
|
339
|
+
}
|
|
340
|
+
for (; 0 < size; size--, value++) {
|
|
341
|
+
if ('1' == table[(uchar)*value]) {
|
|
342
|
+
*out->cur++ = *value;
|
|
343
|
+
} else {
|
|
344
|
+
switch (*value) {
|
|
345
|
+
case '"':
|
|
346
|
+
*out->cur++ = '&';
|
|
347
|
+
*out->cur++ = 'q';
|
|
348
|
+
*out->cur++ = 'u';
|
|
349
|
+
*out->cur++ = 'o';
|
|
350
|
+
*out->cur++ = 't';
|
|
351
|
+
*out->cur++ = ';';
|
|
352
|
+
break;
|
|
353
|
+
case '&':
|
|
354
|
+
*out->cur++ = '&';
|
|
355
|
+
*out->cur++ = 'a';
|
|
356
|
+
*out->cur++ = 'm';
|
|
357
|
+
*out->cur++ = 'p';
|
|
358
|
+
*out->cur++ = ';';
|
|
359
|
+
break;
|
|
360
|
+
case '\'':
|
|
361
|
+
*out->cur++ = '&';
|
|
362
|
+
*out->cur++ = 'a';
|
|
363
|
+
*out->cur++ = 'p';
|
|
364
|
+
*out->cur++ = 'o';
|
|
365
|
+
*out->cur++ = 's';
|
|
366
|
+
*out->cur++ = ';';
|
|
367
|
+
break;
|
|
368
|
+
case '<':
|
|
369
|
+
*out->cur++ = '&';
|
|
370
|
+
*out->cur++ = 'l';
|
|
371
|
+
*out->cur++ = 't';
|
|
372
|
+
*out->cur++ = ';';
|
|
373
|
+
break;
|
|
374
|
+
case '>':
|
|
375
|
+
*out->cur++ = '&';
|
|
376
|
+
*out->cur++ = 'g';
|
|
377
|
+
*out->cur++ = 't';
|
|
378
|
+
*out->cur++ = ';';
|
|
379
|
+
break;
|
|
380
|
+
default:
|
|
381
|
+
// Must be one of the invalid characters.
|
|
382
|
+
if (StrictEffort == out->opts->effort) {
|
|
383
|
+
rb_raise(ox_syntax_error_class, "'\\#x%02x' is not a valid XML character.", *value);
|
|
384
|
+
}
|
|
385
|
+
if (Yes == out->opts->allow_invalid) {
|
|
386
|
+
*out->cur++ = '&';
|
|
387
|
+
*out->cur++ = '#';
|
|
388
|
+
*out->cur++ = 'x';
|
|
389
|
+
*out->cur++ = '0';
|
|
390
|
+
*out->cur++ = '0';
|
|
391
|
+
dump_hex(*value, out);
|
|
392
|
+
*out->cur++ = ';';
|
|
393
|
+
} else if ('\0' != *out->opts->inv_repl) {
|
|
394
|
+
// If the empty string then ignore. The first character of
|
|
395
|
+
// the replacement is the length.
|
|
396
|
+
memcpy(out->cur, out->opts->inv_repl + 1, (size_t)*out->opts->inv_repl);
|
|
397
|
+
out->cur += *out->opts->inv_repl;
|
|
398
|
+
}
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
*out->cur = '\0';
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
inline static void dump_num(Out out, VALUE obj) {
|
|
407
|
+
char buf[32];
|
|
408
|
+
char *b = buf + sizeof(buf) - 1;
|
|
409
|
+
long num = NUM2LONG(obj);
|
|
410
|
+
int neg = 0;
|
|
411
|
+
|
|
412
|
+
if (0 > num) {
|
|
413
|
+
neg = 1;
|
|
414
|
+
num = -num;
|
|
415
|
+
}
|
|
416
|
+
*b-- = '\0';
|
|
417
|
+
if (0 < num) {
|
|
418
|
+
for (; 0 < num; num /= 10, b--) {
|
|
419
|
+
*b = (num % 10) + '0';
|
|
420
|
+
}
|
|
421
|
+
if (neg) {
|
|
422
|
+
*b = '-';
|
|
423
|
+
} else {
|
|
424
|
+
b++;
|
|
425
|
+
}
|
|
426
|
+
} else {
|
|
427
|
+
*b = '0';
|
|
428
|
+
}
|
|
429
|
+
if (out->end - out->cur <= (long)(sizeof(buf) - (b - buf))) {
|
|
430
|
+
grow(out, sizeof(buf) - (b - buf));
|
|
431
|
+
}
|
|
432
|
+
for (; '\0' != *b; b++) {
|
|
433
|
+
*out->cur++ = *b;
|
|
434
|
+
}
|
|
435
|
+
*out->cur = '\0';
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
static void dump_time_thin(Out out, VALUE obj) {
|
|
439
|
+
char buf[64];
|
|
440
|
+
char *b = buf + sizeof(buf) - 1;
|
|
441
|
+
struct timespec ts = rb_time_timespec(obj);
|
|
442
|
+
time_t sec = ts.tv_sec;
|
|
443
|
+
long nsec = ts.tv_nsec;
|
|
444
|
+
char *dot = b - 10;
|
|
445
|
+
long size;
|
|
446
|
+
|
|
447
|
+
*b-- = '\0';
|
|
448
|
+
for (; dot < b; b--, nsec /= 10) {
|
|
449
|
+
*b = '0' + (nsec % 10);
|
|
450
|
+
}
|
|
451
|
+
*b-- = '.';
|
|
452
|
+
for (; 0 < sec; b--, sec /= 10) {
|
|
453
|
+
*b = '0' + (sec % 10);
|
|
454
|
+
}
|
|
455
|
+
b++;
|
|
456
|
+
size = sizeof(buf) - (b - buf) - 1;
|
|
457
|
+
if (out->end - out->cur <= size) {
|
|
458
|
+
grow(out, size);
|
|
459
|
+
}
|
|
460
|
+
memcpy(out->cur, b, size);
|
|
461
|
+
out->cur += size;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
static void dump_date(Out out, VALUE obj) {
|
|
465
|
+
char buf[64];
|
|
466
|
+
char *b = buf + sizeof(buf) - 1;
|
|
467
|
+
long jd = NUM2LONG(rb_funcall2(obj, ox_jd_id, 0, 0));
|
|
468
|
+
long size;
|
|
469
|
+
|
|
470
|
+
*b-- = '\0';
|
|
471
|
+
for (; 0 < jd; b--, jd /= 10) {
|
|
472
|
+
*b = '0' + (jd % 10);
|
|
473
|
+
}
|
|
474
|
+
b++;
|
|
475
|
+
if ('\0' == *b) {
|
|
476
|
+
b--;
|
|
477
|
+
*b = '0';
|
|
478
|
+
}
|
|
479
|
+
size = sizeof(buf) - (b - buf) - 1;
|
|
480
|
+
if (out->end - out->cur <= size) {
|
|
481
|
+
grow(out, size);
|
|
482
|
+
}
|
|
483
|
+
memcpy(out->cur, b, size);
|
|
484
|
+
out->cur += size;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
static void dump_time_xsd(Out out, VALUE obj) {
|
|
488
|
+
struct tm *tm;
|
|
489
|
+
struct timespec ts = rb_time_timespec(obj);
|
|
490
|
+
time_t sec = ts.tv_sec;
|
|
491
|
+
long nsec = ts.tv_nsec;
|
|
492
|
+
int tzhour, tzmin;
|
|
493
|
+
char tzsign = '+';
|
|
494
|
+
|
|
495
|
+
if (out->end - out->cur <= 33) {
|
|
496
|
+
grow(out, 33);
|
|
497
|
+
}
|
|
498
|
+
/* 2010-07-09T10:47:45.895826+09:00 */
|
|
499
|
+
tm = localtime(&sec);
|
|
500
|
+
#if HAVE_ST_TM_GMTOFF
|
|
501
|
+
if (0 > tm->tm_gmtoff) {
|
|
502
|
+
tzsign = '-';
|
|
503
|
+
tzhour = (int)(tm->tm_gmtoff / -3600);
|
|
504
|
+
tzmin = (int)(tm->tm_gmtoff / -60) - (tzhour * 60);
|
|
505
|
+
} else {
|
|
506
|
+
tzhour = (int)(tm->tm_gmtoff / 3600);
|
|
507
|
+
tzmin = (int)(tm->tm_gmtoff / 60) - (tzhour * 60);
|
|
508
|
+
}
|
|
509
|
+
#else
|
|
510
|
+
tzhour = 0;
|
|
511
|
+
tzmin = 0;
|
|
512
|
+
#endif
|
|
513
|
+
/* TBD replace with more efficient printer */
|
|
514
|
+
out->cur += sprintf(out->cur,
|
|
515
|
+
"%04d-%02d-%02dT%02d:%02d:%02d.%06ld%c%02d:%02d",
|
|
516
|
+
tm->tm_year + 1900,
|
|
517
|
+
tm->tm_mon + 1,
|
|
518
|
+
tm->tm_mday,
|
|
519
|
+
tm->tm_hour,
|
|
520
|
+
tm->tm_min,
|
|
521
|
+
tm->tm_sec,
|
|
522
|
+
nsec / 1000,
|
|
523
|
+
tzsign,
|
|
524
|
+
tzhour,
|
|
525
|
+
tzmin);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
static void dump_first_obj(VALUE obj, Out out) {
|
|
529
|
+
char buf[128];
|
|
530
|
+
Options copts = out->opts;
|
|
531
|
+
int cnt;
|
|
532
|
+
|
|
533
|
+
if (Yes == copts->with_xml) {
|
|
534
|
+
if (0 < copts->margin_len) {
|
|
535
|
+
dump_value(out, copts->margin, copts->margin_len);
|
|
536
|
+
}
|
|
537
|
+
if ('\0' == *copts->encoding) {
|
|
538
|
+
dump_value(out, "<?xml version=\"1.0\"?>", 21);
|
|
539
|
+
} else {
|
|
540
|
+
cnt = snprintf(buf, sizeof(buf), "<?xml version=\"1.0\" encoding=\"%s\"?>", copts->encoding);
|
|
541
|
+
dump_value(out, buf, cnt);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (Yes == copts->with_instruct) {
|
|
545
|
+
if (out->buf < out->cur) {
|
|
546
|
+
dump_value(out, "\n", 1);
|
|
547
|
+
}
|
|
548
|
+
if (0 < copts->margin_len) {
|
|
549
|
+
dump_value(out, copts->margin, copts->margin_len);
|
|
550
|
+
}
|
|
551
|
+
cnt = snprintf(
|
|
552
|
+
buf,
|
|
553
|
+
sizeof(buf),
|
|
554
|
+
"<?ox version=\"1.0\" mode=\"object\"%s%s?>",
|
|
555
|
+
(Yes == copts->circular) ? " circular=\"yes\"" : ((No == copts->circular) ? " circular=\"no\"" : ""),
|
|
556
|
+
(Yes == copts->xsd_date) ? " xsd_date=\"yes\"" : ((No == copts->xsd_date) ? " xsd_date=\"no\"" : ""));
|
|
557
|
+
dump_value(out, buf, cnt);
|
|
558
|
+
}
|
|
559
|
+
if (Yes == copts->with_dtd) {
|
|
560
|
+
if (0 < copts->margin_len) {
|
|
561
|
+
dump_value(out, copts->margin, copts->margin_len);
|
|
562
|
+
}
|
|
563
|
+
cnt = snprintf(buf,
|
|
564
|
+
sizeof(buf),
|
|
565
|
+
"%s<!DOCTYPE %c SYSTEM \"ox.dtd\">",
|
|
566
|
+
(out->buf < out->cur) ? "\n" : "",
|
|
567
|
+
obj_class_code(obj));
|
|
568
|
+
dump_value(out, buf, cnt);
|
|
569
|
+
}
|
|
570
|
+
if (0 < copts->margin_len) {
|
|
571
|
+
dump_value(out, copts->margin, copts->margin_len);
|
|
572
|
+
}
|
|
573
|
+
dump_obj(0, obj, 0, out);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
static void dump_obj(ID aid, VALUE obj, int depth, Out out) {
|
|
577
|
+
struct _element e;
|
|
578
|
+
VALUE prev_obj = out->obj;
|
|
579
|
+
char value_buf[64];
|
|
580
|
+
int cnt;
|
|
581
|
+
|
|
582
|
+
if (MAX_DEPTH < depth) {
|
|
583
|
+
rb_raise(rb_eSysStackError, "maximum depth exceeded");
|
|
584
|
+
}
|
|
585
|
+
out->obj = obj;
|
|
586
|
+
if (0 == aid) {
|
|
587
|
+
e.attr.str = 0;
|
|
588
|
+
e.attr.len = 0;
|
|
589
|
+
} else {
|
|
590
|
+
e.attr.str = rb_id2name(aid);
|
|
591
|
+
// Ruby 2.3 started to return NULL for some IDs so check for
|
|
592
|
+
// NULL. Ignore if NULL aid.
|
|
593
|
+
if (NULL == e.attr.str) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
e.attr.len = strlen(e.attr.str);
|
|
597
|
+
}
|
|
598
|
+
e.closed = 0;
|
|
599
|
+
if (0 == depth) {
|
|
600
|
+
e.indent = (0 <= out->indent) ? 0 : -1;
|
|
601
|
+
} else if (0 > out->indent) {
|
|
602
|
+
e.indent = -1;
|
|
603
|
+
} else if (0 == out->indent) {
|
|
604
|
+
e.indent = 0;
|
|
605
|
+
} else {
|
|
606
|
+
e.indent = depth * out->indent;
|
|
607
|
+
}
|
|
608
|
+
e.id = 0;
|
|
609
|
+
e.clas.len = 0;
|
|
610
|
+
e.clas.str = 0;
|
|
611
|
+
switch (rb_type(obj)) {
|
|
612
|
+
case T_NIL:
|
|
613
|
+
e.type = NilClassCode;
|
|
614
|
+
e.closed = 1;
|
|
615
|
+
out->w_start(out, &e);
|
|
616
|
+
break;
|
|
617
|
+
case T_ARRAY:
|
|
618
|
+
if (0 != out->circ_cache && check_circular(out, obj, &e)) {
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
cnt = (int)RARRAY_LEN(obj);
|
|
622
|
+
e.type = ArrayCode;
|
|
623
|
+
e.closed = (0 >= cnt);
|
|
624
|
+
out->w_start(out, &e);
|
|
625
|
+
if (!e.closed) {
|
|
626
|
+
const VALUE *np = RARRAY_PTR(obj);
|
|
627
|
+
int i;
|
|
628
|
+
int d2 = depth + 1;
|
|
629
|
+
|
|
630
|
+
for (i = cnt; 0 < i; i--, np++) {
|
|
631
|
+
dump_obj(0, *np, d2, out);
|
|
632
|
+
}
|
|
633
|
+
out->w_end(out, &e);
|
|
634
|
+
}
|
|
635
|
+
break;
|
|
636
|
+
case T_HASH:
|
|
637
|
+
if (0 != out->circ_cache && check_circular(out, obj, &e)) {
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
cnt = (int)RHASH_SIZE(obj);
|
|
641
|
+
e.type = HashCode;
|
|
642
|
+
e.closed = (0 >= cnt);
|
|
643
|
+
out->w_start(out, &e);
|
|
644
|
+
if (0 < cnt) {
|
|
645
|
+
unsigned int od = out->depth;
|
|
646
|
+
|
|
647
|
+
out->depth = depth + 1;
|
|
648
|
+
rb_hash_foreach(obj, dump_hash, (VALUE)out);
|
|
649
|
+
out->depth = od;
|
|
650
|
+
out->w_end(out, &e);
|
|
651
|
+
}
|
|
652
|
+
break;
|
|
653
|
+
case T_TRUE:
|
|
654
|
+
e.type = TrueClassCode;
|
|
655
|
+
e.closed = 1;
|
|
656
|
+
out->w_start(out, &e);
|
|
657
|
+
break;
|
|
658
|
+
case T_FALSE:
|
|
659
|
+
e.type = FalseClassCode;
|
|
660
|
+
e.closed = 1;
|
|
661
|
+
out->w_start(out, &e);
|
|
662
|
+
break;
|
|
663
|
+
case T_FIXNUM:
|
|
664
|
+
e.type = FixnumCode;
|
|
665
|
+
out->w_start(out, &e);
|
|
666
|
+
dump_num(out, obj);
|
|
667
|
+
e.indent = -1;
|
|
668
|
+
out->w_end(out, &e);
|
|
669
|
+
break;
|
|
670
|
+
case T_FLOAT:
|
|
671
|
+
e.type = FloatCode;
|
|
672
|
+
cnt = snprintf(value_buf, sizeof(value_buf), "%0.16g", rb_num2dbl(obj));
|
|
673
|
+
out->w_start(out, &e);
|
|
674
|
+
dump_value(out, value_buf, cnt);
|
|
675
|
+
e.indent = -1;
|
|
676
|
+
out->w_end(out, &e);
|
|
677
|
+
break;
|
|
678
|
+
case T_STRING: {
|
|
679
|
+
const char *str;
|
|
680
|
+
|
|
681
|
+
if (0 != out->circ_cache && check_circular(out, obj, &e)) {
|
|
682
|
+
break;
|
|
683
|
+
}
|
|
684
|
+
str = StringValuePtr(obj);
|
|
685
|
+
cnt = (int)RSTRING_LEN(obj);
|
|
686
|
+
#if USE_B64
|
|
687
|
+
if (is_xml_friendly((uchar *)str, cnt)) {
|
|
688
|
+
e.type = StringCode;
|
|
689
|
+
out->w_start(out, &e);
|
|
690
|
+
dump_str_value(out, str, cnt, '<');
|
|
691
|
+
e.indent = -1;
|
|
692
|
+
out->w_end(out, &e);
|
|
693
|
+
} else {
|
|
694
|
+
ulong size = b64_size(cnt);
|
|
695
|
+
char *b64 = ALLOCA_N(char, size + 1);
|
|
696
|
+
|
|
697
|
+
e.type = String64Code;
|
|
698
|
+
to_base64((uchar *)str, cnt, b64);
|
|
699
|
+
out->w_start(out, &e);
|
|
700
|
+
dump_value(out, b64, size);
|
|
701
|
+
e.indent = -1;
|
|
702
|
+
out->w_end(out, &e);
|
|
703
|
+
}
|
|
704
|
+
#else
|
|
705
|
+
e.type = StringCode;
|
|
706
|
+
out->w_start(out, &e);
|
|
707
|
+
dump_str_value(out, str, cnt, xml_element_chars);
|
|
708
|
+
e.indent = -1;
|
|
709
|
+
out->w_end(out, &e);
|
|
710
|
+
#endif
|
|
711
|
+
break;
|
|
712
|
+
}
|
|
713
|
+
case T_SYMBOL: {
|
|
714
|
+
const char *sym = rb_id2name(SYM2ID(obj));
|
|
715
|
+
|
|
716
|
+
cnt = (int)strlen(sym);
|
|
717
|
+
#if USE_B64
|
|
718
|
+
if (is_xml_friendly((uchar *)sym, cnt)) {
|
|
719
|
+
e.type = SymbolCode;
|
|
720
|
+
out->w_start(out, &e);
|
|
721
|
+
dump_str_value(out, sym, cnt, '<');
|
|
722
|
+
e.indent = -1;
|
|
723
|
+
out->w_end(out, &e);
|
|
724
|
+
} else {
|
|
725
|
+
ulong size = b64_size(cnt);
|
|
726
|
+
char *b64 = ALLOCA_N(char, size + 1);
|
|
727
|
+
|
|
728
|
+
e.type = Symbol64Code;
|
|
729
|
+
to_base64((uchar *)sym, cnt, b64);
|
|
730
|
+
out->w_start(out, &e);
|
|
731
|
+
dump_value(out, b64, size);
|
|
732
|
+
e.indent = -1;
|
|
733
|
+
out->w_end(out, &e);
|
|
734
|
+
}
|
|
735
|
+
#else
|
|
736
|
+
e.type = SymbolCode;
|
|
737
|
+
out->w_start(out, &e);
|
|
738
|
+
dump_str_value(out, sym, cnt, xml_element_chars);
|
|
739
|
+
e.indent = -1;
|
|
740
|
+
out->w_end(out, &e);
|
|
741
|
+
#endif
|
|
742
|
+
break;
|
|
743
|
+
}
|
|
744
|
+
case T_DATA: {
|
|
745
|
+
VALUE clas;
|
|
746
|
+
|
|
747
|
+
clas = rb_obj_class(obj);
|
|
748
|
+
if (rb_cTime == clas) {
|
|
749
|
+
e.type = TimeCode;
|
|
750
|
+
out->w_start(out, &e);
|
|
751
|
+
out->w_time(out, obj);
|
|
752
|
+
e.indent = -1;
|
|
753
|
+
out->w_end(out, &e);
|
|
754
|
+
} else {
|
|
755
|
+
const char *classname = rb_class2name(clas);
|
|
756
|
+
|
|
757
|
+
if (0 == strcmp("Date", classname)) {
|
|
758
|
+
e.type = DateCode;
|
|
759
|
+
out->w_start(out, &e);
|
|
760
|
+
dump_date(out, obj);
|
|
761
|
+
e.indent = -1;
|
|
762
|
+
out->w_end(out, &e);
|
|
763
|
+
} else if (0 == strcmp("BigDecimal", classname)) {
|
|
764
|
+
volatile VALUE rs = rb_String(obj);
|
|
765
|
+
|
|
766
|
+
e.type = BigDecimalCode;
|
|
767
|
+
out->w_start(out, &e);
|
|
768
|
+
dump_value(out, StringValuePtr(rs), RSTRING_LEN(rs));
|
|
769
|
+
e.indent = -1;
|
|
770
|
+
out->w_end(out, &e);
|
|
771
|
+
} else {
|
|
772
|
+
if (StrictEffort == out->opts->effort) {
|
|
773
|
+
rb_raise(rb_eNotImpError, "Failed to dump T_DATA %s\n", classname);
|
|
774
|
+
} else {
|
|
775
|
+
e.type = NilClassCode;
|
|
776
|
+
e.closed = 1;
|
|
777
|
+
out->w_start(out, &e);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
break;
|
|
782
|
+
}
|
|
783
|
+
case T_STRUCT: {
|
|
784
|
+
#ifdef RSTRUCT_GET
|
|
785
|
+
VALUE clas;
|
|
786
|
+
|
|
787
|
+
if (0 != out->circ_cache && check_circular(out, obj, &e)) {
|
|
788
|
+
break;
|
|
789
|
+
}
|
|
790
|
+
clas = rb_obj_class(obj);
|
|
791
|
+
if (rb_cRange == clas) {
|
|
792
|
+
VALUE beg = RSTRUCT_GET(obj, 0);
|
|
793
|
+
VALUE end = RSTRUCT_GET(obj, 1);
|
|
794
|
+
VALUE excl = RSTRUCT_GET(obj, 2);
|
|
795
|
+
int d2 = depth + 1;
|
|
796
|
+
|
|
797
|
+
e.type = RangeCode;
|
|
798
|
+
e.clas.len = 5;
|
|
799
|
+
e.clas.str = "Range";
|
|
800
|
+
out->w_start(out, &e);
|
|
801
|
+
dump_obj(ox_beg_id, beg, d2, out);
|
|
802
|
+
dump_obj(ox_end_id, end, d2, out);
|
|
803
|
+
dump_obj(ox_excl_id, excl, d2, out);
|
|
804
|
+
out->w_end(out, &e);
|
|
805
|
+
} else {
|
|
806
|
+
char num_buf[16];
|
|
807
|
+
int d2 = depth + 1;
|
|
808
|
+
long i;
|
|
809
|
+
long cnt = NUM2LONG(rb_struct_size(obj));
|
|
810
|
+
e.type = StructCode;
|
|
811
|
+
e.clas.str = rb_class2name(clas);
|
|
812
|
+
e.clas.len = strlen(e.clas.str);
|
|
813
|
+
out->w_start(out, &e);
|
|
814
|
+
|
|
815
|
+
for (i = 0; i < cnt; i++) {
|
|
816
|
+
VALUE v = RSTRUCT_GET(obj, (int)(i));
|
|
817
|
+
dump_obj(rb_intern(ulong2str(i, num_buf + sizeof(num_buf) - 1)), v, d2, out);
|
|
818
|
+
}
|
|
819
|
+
out->w_end(out, &e);
|
|
820
|
+
}
|
|
821
|
+
#else
|
|
822
|
+
e.type = NilClassCode;
|
|
823
|
+
e.closed = 1;
|
|
824
|
+
out->w_start(out, &e);
|
|
825
|
+
#endif
|
|
826
|
+
break;
|
|
827
|
+
}
|
|
828
|
+
case T_OBJECT: {
|
|
829
|
+
VALUE clas;
|
|
830
|
+
|
|
831
|
+
if (0 != out->circ_cache && check_circular(out, obj, &e)) {
|
|
832
|
+
break;
|
|
833
|
+
}
|
|
834
|
+
clas = rb_obj_class(obj);
|
|
835
|
+
e.clas.str = rb_class2name(clas);
|
|
836
|
+
e.clas.len = strlen(e.clas.str);
|
|
837
|
+
if (ox_document_clas == clas) {
|
|
838
|
+
e.type = RawCode;
|
|
839
|
+
out->w_start(out, &e);
|
|
840
|
+
dump_gen_doc(obj, depth + 1, out);
|
|
841
|
+
out->w_end(out, &e);
|
|
842
|
+
} else if (ox_element_clas == clas) {
|
|
843
|
+
e.type = RawCode;
|
|
844
|
+
out->w_start(out, &e);
|
|
845
|
+
dump_gen_element(obj, depth + 1, out);
|
|
846
|
+
out->w_end(out, &e);
|
|
847
|
+
} else { /* Object */
|
|
848
|
+
e.type = (Qtrue == rb_obj_is_kind_of(obj, rb_eException)) ? ExceptionCode : ObjectCode;
|
|
849
|
+
cnt = (int)rb_ivar_count(obj);
|
|
850
|
+
e.closed = (0 >= cnt);
|
|
851
|
+
out->w_start(out, &e);
|
|
852
|
+
if (0 < cnt) {
|
|
853
|
+
unsigned int od = out->depth;
|
|
854
|
+
|
|
855
|
+
out->depth = depth + 1;
|
|
856
|
+
rb_ivar_foreach(obj, dump_var, (VALUE)out);
|
|
857
|
+
out->depth = od;
|
|
858
|
+
out->w_end(out, &e);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
break;
|
|
862
|
+
}
|
|
863
|
+
case T_REGEXP: {
|
|
864
|
+
volatile VALUE rs = rb_funcall2(obj, ox_inspect_id, 0, 0);
|
|
865
|
+
const char *s = StringValuePtr(rs);
|
|
866
|
+
|
|
867
|
+
cnt = (int)RSTRING_LEN(rs);
|
|
868
|
+
e.type = RegexpCode;
|
|
869
|
+
out->w_start(out, &e);
|
|
870
|
+
#if USE_B64
|
|
871
|
+
if (is_xml_friendly((uchar *)s, cnt)) {
|
|
872
|
+
/*dump_value(out, "/", 1); */
|
|
873
|
+
dump_str_value(out, s, cnt, '<');
|
|
874
|
+
} else {
|
|
875
|
+
ulong size = b64_size(cnt);
|
|
876
|
+
char *b64 = ALLOCA_N(char, size + 1);
|
|
877
|
+
|
|
878
|
+
to_base64((uchar *)s, cnt, b64);
|
|
879
|
+
dump_value(out, b64, size);
|
|
880
|
+
}
|
|
881
|
+
#else
|
|
882
|
+
dump_str_value(out, s, cnt, xml_element_chars);
|
|
883
|
+
#endif
|
|
884
|
+
e.indent = -1;
|
|
885
|
+
out->w_end(out, &e);
|
|
886
|
+
break;
|
|
887
|
+
}
|
|
888
|
+
case T_BIGNUM: {
|
|
889
|
+
volatile VALUE rs = rb_big2str(obj, 10);
|
|
890
|
+
|
|
891
|
+
e.type = BignumCode;
|
|
892
|
+
out->w_start(out, &e);
|
|
893
|
+
dump_value(out, StringValuePtr(rs), RSTRING_LEN(rs));
|
|
894
|
+
e.indent = -1;
|
|
895
|
+
out->w_end(out, &e);
|
|
896
|
+
break;
|
|
897
|
+
}
|
|
898
|
+
case T_COMPLEX: e.type = ComplexCode; out->w_start(out, &e);
|
|
899
|
+
#ifdef RCOMPLEX
|
|
900
|
+
dump_obj(0, RCOMPLEX(obj)->real, depth + 1, out);
|
|
901
|
+
dump_obj(0, RCOMPLEX(obj)->imag, depth + 1, out);
|
|
902
|
+
#else
|
|
903
|
+
dump_obj(0, rb_funcall2(obj, rb_intern("real"), 0, 0), depth + 1, out);
|
|
904
|
+
dump_obj(0, rb_funcall2(obj, rb_intern("imag"), 0, 0), depth + 1, out);
|
|
905
|
+
#endif
|
|
906
|
+
out->w_end(out, &e);
|
|
907
|
+
break;
|
|
908
|
+
case T_RATIONAL: e.type = RationalCode; out->w_start(out, &e);
|
|
909
|
+
#ifdef RRATIONAL
|
|
910
|
+
dump_obj(0, RRATIONAL(obj)->num, depth + 1, out);
|
|
911
|
+
dump_obj(0, RRATIONAL(obj)->den, depth + 1, out);
|
|
912
|
+
#else
|
|
913
|
+
dump_obj(0, rb_funcall2(obj, rb_intern("numerator"), 0, 0), depth + 1, out);
|
|
914
|
+
dump_obj(0, rb_funcall2(obj, rb_intern("denominator"), 0, 0), depth + 1, out);
|
|
915
|
+
#endif
|
|
916
|
+
out->w_end(out, &e);
|
|
917
|
+
break;
|
|
918
|
+
case T_CLASS: {
|
|
919
|
+
e.type = ClassCode;
|
|
920
|
+
e.clas.str = rb_class2name(obj);
|
|
921
|
+
e.clas.len = strlen(e.clas.str);
|
|
922
|
+
e.closed = 1;
|
|
923
|
+
out->w_start(out, &e);
|
|
924
|
+
break;
|
|
925
|
+
}
|
|
926
|
+
default:
|
|
927
|
+
if (StrictEffort == out->opts->effort) {
|
|
928
|
+
rb_raise(rb_eNotImpError, "Failed to dump %s Object (%02x)\n", rb_obj_classname(obj), rb_type(obj));
|
|
929
|
+
} else {
|
|
930
|
+
e.type = NilClassCode;
|
|
931
|
+
e.closed = 1;
|
|
932
|
+
out->w_start(out, &e);
|
|
933
|
+
}
|
|
934
|
+
break;
|
|
935
|
+
}
|
|
936
|
+
out->obj = prev_obj;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
static int dump_var(ID key, VALUE value, VALUE ov) {
|
|
940
|
+
Out out = (Out)ov;
|
|
941
|
+
|
|
942
|
+
if (T_DATA == rb_type(value) && key == ox_mesg_id) {
|
|
943
|
+
/* There is a secret recipe that keeps Exception mesg attributes as a
|
|
944
|
+
* T_DATA until it is needed. The safe way around this hack is to call
|
|
945
|
+
* the message() method and use the returned string as the
|
|
946
|
+
* message. Not pretty but it solves the most common use of this
|
|
947
|
+
* hack. If there are others they will have to be handled one at a
|
|
948
|
+
* time.
|
|
949
|
+
*/
|
|
950
|
+
value = rb_funcall(out->obj, ox_message_id, 0);
|
|
951
|
+
}
|
|
952
|
+
dump_obj(key, value, out->depth, out);
|
|
953
|
+
|
|
954
|
+
return ST_CONTINUE;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
static int dump_hash(VALUE key, VALUE value, VALUE ov) {
|
|
958
|
+
Out out = (Out)ov;
|
|
959
|
+
|
|
960
|
+
dump_obj(0, key, out->depth, out);
|
|
961
|
+
dump_obj(0, value, out->depth, out);
|
|
962
|
+
|
|
963
|
+
return ST_CONTINUE;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
static void dump_gen_doc(VALUE obj, int depth, Out out) {
|
|
967
|
+
volatile VALUE attrs = rb_attr_get(obj, ox_attributes_id);
|
|
968
|
+
volatile VALUE nodes = rb_attr_get(obj, ox_nodes_id);
|
|
969
|
+
|
|
970
|
+
if ('\0' == *out->opts->encoding && Qnil != attrs) {
|
|
971
|
+
volatile VALUE renc = rb_hash_lookup(attrs, ox_encoding_sym);
|
|
972
|
+
|
|
973
|
+
if (Qnil != renc) {
|
|
974
|
+
const char *enc = StringValuePtr(renc);
|
|
975
|
+
|
|
976
|
+
strncpy(out->opts->encoding, enc, sizeof(out->opts->encoding) - 1);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
if (Yes == out->opts->with_xml) {
|
|
980
|
+
if (0 < out->opts->margin_len) {
|
|
981
|
+
dump_value(out, out->opts->margin, out->opts->margin_len);
|
|
982
|
+
}
|
|
983
|
+
dump_value(out, "<?xml", 5);
|
|
984
|
+
if (Qnil != attrs) {
|
|
985
|
+
rb_hash_foreach(attrs, dump_gen_attr, (VALUE)out);
|
|
986
|
+
}
|
|
987
|
+
dump_value(out, "?>", 2);
|
|
988
|
+
}
|
|
989
|
+
if (Yes == out->opts->with_instruct) {
|
|
990
|
+
if (out->buf < out->cur) {
|
|
991
|
+
dump_value(out, "\n", 1);
|
|
992
|
+
}
|
|
993
|
+
if (0 < out->opts->margin_len) {
|
|
994
|
+
dump_value(out, out->opts->margin, out->opts->margin_len);
|
|
995
|
+
}
|
|
996
|
+
dump_value(out, "<?ox version=\"1.0\" mode=\"generic\"?>", 35);
|
|
997
|
+
}
|
|
998
|
+
if (Qnil != nodes) {
|
|
999
|
+
dump_gen_nodes(nodes, depth, out);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
static void dump_gen_element(VALUE obj, int depth, Out out) {
|
|
1004
|
+
volatile VALUE rname = rb_attr_get(obj, ox_at_value_id);
|
|
1005
|
+
volatile VALUE attrs = rb_attr_get(obj, ox_attributes_id);
|
|
1006
|
+
volatile VALUE nodes = rb_attr_get(obj, ox_nodes_id);
|
|
1007
|
+
const char *name = StringValuePtr(rname);
|
|
1008
|
+
long nlen = RSTRING_LEN(rname);
|
|
1009
|
+
size_t size;
|
|
1010
|
+
int indent;
|
|
1011
|
+
|
|
1012
|
+
if (0 > out->indent) {
|
|
1013
|
+
indent = -1;
|
|
1014
|
+
} else if (0 == out->indent) {
|
|
1015
|
+
indent = 0;
|
|
1016
|
+
} else {
|
|
1017
|
+
indent = depth * out->indent;
|
|
1018
|
+
}
|
|
1019
|
+
size = indent + 4 + nlen + out->opts->margin_len;
|
|
1020
|
+
if (out->end - out->cur <= (long)size) {
|
|
1021
|
+
grow(out, size);
|
|
1022
|
+
}
|
|
1023
|
+
if (0 == depth && 0 < out->opts->margin_len && 0 < out->indent) {
|
|
1024
|
+
memcpy(out->cur, out->opts->margin, out->opts->margin_len);
|
|
1025
|
+
out->cur += out->opts->margin_len;
|
|
1026
|
+
}
|
|
1027
|
+
fill_indent(out, indent);
|
|
1028
|
+
*out->cur++ = '<';
|
|
1029
|
+
fill_value(out, name, nlen);
|
|
1030
|
+
if (Qnil != attrs) {
|
|
1031
|
+
rb_hash_foreach(attrs, dump_gen_attr, (VALUE)out);
|
|
1032
|
+
}
|
|
1033
|
+
if (Qnil != nodes && 0 < RARRAY_LEN(nodes)) {
|
|
1034
|
+
int do_indent;
|
|
1035
|
+
|
|
1036
|
+
*out->cur++ = '>';
|
|
1037
|
+
do_indent = dump_gen_nodes(nodes, depth, out);
|
|
1038
|
+
if (out->end - out->cur <= (long)size) {
|
|
1039
|
+
grow(out, size);
|
|
1040
|
+
}
|
|
1041
|
+
if (do_indent) {
|
|
1042
|
+
fill_indent(out, indent);
|
|
1043
|
+
}
|
|
1044
|
+
*out->cur++ = '<';
|
|
1045
|
+
*out->cur++ = '/';
|
|
1046
|
+
fill_value(out, name, nlen);
|
|
1047
|
+
} else if (out->opts->no_empty) {
|
|
1048
|
+
*out->cur++ = '>';
|
|
1049
|
+
*out->cur++ = '<';
|
|
1050
|
+
*out->cur++ = '/';
|
|
1051
|
+
fill_value(out, name, nlen);
|
|
1052
|
+
} else {
|
|
1053
|
+
*out->cur++ = '/';
|
|
1054
|
+
}
|
|
1055
|
+
*out->cur++ = '>';
|
|
1056
|
+
*out->cur = '\0';
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
static void dump_gen_instruct(VALUE obj, int depth, Out out) {
|
|
1060
|
+
volatile VALUE rname = rb_attr_get(obj, ox_at_value_id);
|
|
1061
|
+
volatile VALUE attrs = rb_attr_get(obj, ox_attributes_id);
|
|
1062
|
+
volatile VALUE rcontent = rb_attr_get(obj, ox_at_content_id);
|
|
1063
|
+
const char *name = StringValuePtr(rname);
|
|
1064
|
+
const char *content = 0;
|
|
1065
|
+
long nlen = RSTRING_LEN(rname);
|
|
1066
|
+
long clen = 0;
|
|
1067
|
+
size_t size;
|
|
1068
|
+
|
|
1069
|
+
if (T_STRING == rb_type(rcontent)) {
|
|
1070
|
+
content = StringValuePtr(rcontent);
|
|
1071
|
+
clen = RSTRING_LEN(rcontent);
|
|
1072
|
+
size = 4 + nlen + clen;
|
|
1073
|
+
} else {
|
|
1074
|
+
size = 4 + nlen;
|
|
1075
|
+
}
|
|
1076
|
+
if (out->end - out->cur <= (long)size) {
|
|
1077
|
+
grow(out, size);
|
|
1078
|
+
}
|
|
1079
|
+
*out->cur++ = '<';
|
|
1080
|
+
*out->cur++ = '?';
|
|
1081
|
+
fill_value(out, name, nlen);
|
|
1082
|
+
if (0 != content) {
|
|
1083
|
+
if (' ' != *content) {
|
|
1084
|
+
dump_value(out, " ", 1);
|
|
1085
|
+
}
|
|
1086
|
+
fill_value(out, content, clen);
|
|
1087
|
+
} else if (Qnil != attrs) {
|
|
1088
|
+
rb_hash_foreach(attrs, dump_gen_attr, (VALUE)out);
|
|
1089
|
+
}
|
|
1090
|
+
*out->cur++ = '?';
|
|
1091
|
+
*out->cur++ = '>';
|
|
1092
|
+
*out->cur = '\0';
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
static int dump_gen_nodes(VALUE obj, int depth, Out out) {
|
|
1096
|
+
long cnt = RARRAY_LEN(obj);
|
|
1097
|
+
int indent_needed = 1;
|
|
1098
|
+
|
|
1099
|
+
if (0 < cnt) {
|
|
1100
|
+
const VALUE *np = RARRAY_PTR(obj);
|
|
1101
|
+
VALUE clas;
|
|
1102
|
+
int d2 = depth + 1;
|
|
1103
|
+
|
|
1104
|
+
if (MAX_DEPTH < depth) {
|
|
1105
|
+
rb_raise(rb_eSysStackError, "maximum depth exceeded");
|
|
1106
|
+
}
|
|
1107
|
+
for (; 0 < cnt; cnt--, np++) {
|
|
1108
|
+
clas = rb_obj_class(*np);
|
|
1109
|
+
if (ox_element_clas == clas) {
|
|
1110
|
+
dump_gen_element(*np, d2, out);
|
|
1111
|
+
} else if (ox_instruct_clas == clas) {
|
|
1112
|
+
dump_gen_instruct(*np, d2, out);
|
|
1113
|
+
indent_needed = (1 == cnt) ? 0 : 1;
|
|
1114
|
+
} else if (rb_cString == clas) {
|
|
1115
|
+
dump_str_value(out, StringValuePtr(*(VALUE *)np), RSTRING_LEN(*np), xml_element_chars);
|
|
1116
|
+
indent_needed = (1 == cnt) ? 0 : 1;
|
|
1117
|
+
} else if (ox_comment_clas == clas) {
|
|
1118
|
+
dump_gen_val_node(*np, d2, "<!--", 4, "-->", 3, out);
|
|
1119
|
+
} else if (ox_raw_clas == clas) {
|
|
1120
|
+
dump_gen_val_node(*np, d2, "", 0, "", 0, out);
|
|
1121
|
+
} else if (ox_cdata_clas == clas) {
|
|
1122
|
+
dump_gen_val_node(*np, d2, "<![CDATA[", 9, "]]>", 3, out);
|
|
1123
|
+
} else if (ox_doctype_clas == clas) {
|
|
1124
|
+
dump_gen_val_node(*np, d2, "<!DOCTYPE ", 10, ">", 1, out);
|
|
1125
|
+
} else {
|
|
1126
|
+
rb_raise(rb_eTypeError, "Unexpected class, %s, while dumping generic XML\n", rb_class2name(clas));
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
return indent_needed;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
static int dump_gen_attr(VALUE key, VALUE value, VALUE ov) {
|
|
1134
|
+
Out out = (Out)ov;
|
|
1135
|
+
|
|
1136
|
+
const char *ks;
|
|
1137
|
+
size_t klen;
|
|
1138
|
+
size_t size;
|
|
1139
|
+
|
|
1140
|
+
switch (rb_type(key)) {
|
|
1141
|
+
case T_SYMBOL: ks = rb_id2name(SYM2ID(key)); break;
|
|
1142
|
+
case T_STRING: ks = StringValuePtr(key); break;
|
|
1143
|
+
default:
|
|
1144
|
+
key = rb_String(key);
|
|
1145
|
+
ks = StringValuePtr(key);
|
|
1146
|
+
break;
|
|
1147
|
+
}
|
|
1148
|
+
klen = strlen(ks);
|
|
1149
|
+
value = rb_String(value);
|
|
1150
|
+
size = 4 + klen + RSTRING_LEN(value);
|
|
1151
|
+
if (out->end - out->cur <= (long)size) {
|
|
1152
|
+
grow(out, size);
|
|
1153
|
+
}
|
|
1154
|
+
*out->cur++ = ' ';
|
|
1155
|
+
fill_value(out, ks, klen);
|
|
1156
|
+
*out->cur++ = '=';
|
|
1157
|
+
*out->cur++ = '"';
|
|
1158
|
+
dump_str_value(out, StringValuePtr(value), RSTRING_LEN(value), xml_quote_chars);
|
|
1159
|
+
*out->cur++ = '"';
|
|
1160
|
+
|
|
1161
|
+
return ST_CONTINUE;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
static void
|
|
1165
|
+
dump_gen_val_node(VALUE obj, int depth, const char *pre, size_t plen, const char *suf, size_t slen, Out out) {
|
|
1166
|
+
volatile VALUE v = rb_attr_get(obj, ox_at_value_id);
|
|
1167
|
+
const char *val;
|
|
1168
|
+
size_t vlen;
|
|
1169
|
+
size_t size;
|
|
1170
|
+
int indent;
|
|
1171
|
+
|
|
1172
|
+
if (T_STRING != rb_type(v)) {
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
val = StringValuePtr(v);
|
|
1176
|
+
vlen = RSTRING_LEN(v);
|
|
1177
|
+
if (0 > out->indent) {
|
|
1178
|
+
indent = -1;
|
|
1179
|
+
} else if (0 == out->indent) {
|
|
1180
|
+
indent = 0;
|
|
1181
|
+
} else {
|
|
1182
|
+
indent = depth * out->indent;
|
|
1183
|
+
}
|
|
1184
|
+
size = indent + plen + slen + vlen + out->opts->margin_len;
|
|
1185
|
+
if (out->end - out->cur <= (long)size) {
|
|
1186
|
+
grow(out, size);
|
|
1187
|
+
}
|
|
1188
|
+
fill_indent(out, indent);
|
|
1189
|
+
fill_value(out, pre, plen);
|
|
1190
|
+
fill_value(out, val, vlen);
|
|
1191
|
+
fill_value(out, suf, slen);
|
|
1192
|
+
*out->cur = '\0';
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
static void dump_obj_to_xml(VALUE obj, Options copts, Out out) {
|
|
1196
|
+
VALUE clas = rb_obj_class(obj);
|
|
1197
|
+
|
|
1198
|
+
out->w_time = (Yes == copts->xsd_date) ? dump_time_xsd : dump_time_thin;
|
|
1199
|
+
out->buf = ALLOC_N(char, 65336);
|
|
1200
|
+
out->end = out->buf + 65325; /* 10 less than end plus extra for possible errors */
|
|
1201
|
+
out->cur = out->buf;
|
|
1202
|
+
out->circ_cache = 0;
|
|
1203
|
+
out->circ_cnt = 0;
|
|
1204
|
+
out->opts = copts;
|
|
1205
|
+
out->obj = obj;
|
|
1206
|
+
*out->cur = '\0';
|
|
1207
|
+
if (Yes == copts->circular) {
|
|
1208
|
+
ox_cache8_new(&out->circ_cache);
|
|
1209
|
+
}
|
|
1210
|
+
out->indent = copts->indent;
|
|
1211
|
+
|
|
1212
|
+
if (ox_document_clas == clas) {
|
|
1213
|
+
dump_gen_doc(obj, -1, out);
|
|
1214
|
+
} else if (ox_element_clas == clas) {
|
|
1215
|
+
dump_gen_element(obj, 0, out);
|
|
1216
|
+
} else if (ox_cdata_clas == clas) {
|
|
1217
|
+
dump_gen_val_node(obj, 0, "<![CDATA[", 9, "]]>", 3, out);
|
|
1218
|
+
} else if (ox_instruct_clas == clas) {
|
|
1219
|
+
dump_gen_instruct(obj, 0, out);
|
|
1220
|
+
} else if (ox_comment_clas == clas) {
|
|
1221
|
+
dump_gen_val_node(obj, 0, "<!--", 4, "-->", 3, out);
|
|
1222
|
+
} else if (ox_doctype_clas == clas) {
|
|
1223
|
+
dump_gen_val_node(obj, 0, "<!DOCTYPE ", 10, ">", 1, out);
|
|
1224
|
+
} else {
|
|
1225
|
+
out->w_start = dump_start;
|
|
1226
|
+
out->w_end = dump_end;
|
|
1227
|
+
dump_first_obj(obj, out);
|
|
1228
|
+
}
|
|
1229
|
+
if (0 <= out->indent) {
|
|
1230
|
+
dump_value(out, "\n", 1);
|
|
1231
|
+
}
|
|
1232
|
+
if (Yes == copts->circular) {
|
|
1233
|
+
ox_cache8_delete(out->circ_cache);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
char *ox_write_obj_to_str(VALUE obj, Options copts) {
|
|
1238
|
+
struct _out out;
|
|
1239
|
+
|
|
1240
|
+
dump_obj_to_xml(obj, copts, &out);
|
|
1241
|
+
return out.buf;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
void ox_write_obj_to_file(VALUE obj, const char *path, Options copts) {
|
|
1245
|
+
struct _out out;
|
|
1246
|
+
size_t size;
|
|
1247
|
+
FILE *f;
|
|
1248
|
+
|
|
1249
|
+
dump_obj_to_xml(obj, copts, &out);
|
|
1250
|
+
size = out.cur - out.buf;
|
|
1251
|
+
if (0 == (f = fopen(path, "w"))) {
|
|
1252
|
+
rb_raise(rb_eIOError, "%s\n", strerror(errno));
|
|
1253
|
+
}
|
|
1254
|
+
if (size != fwrite(out.buf, 1, size, f)) {
|
|
1255
|
+
int err = ferror(f);
|
|
1256
|
+
rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", err, strerror(err));
|
|
1257
|
+
}
|
|
1258
|
+
xfree(out.buf);
|
|
1259
|
+
fclose(f);
|
|
1260
|
+
}
|