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/builder.c
ADDED
|
@@ -0,0 +1,948 @@
|
|
|
1
|
+
/* builder.c
|
|
2
|
+
* Copyright (c) 2011, 2016 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
|
+
|
|
11
|
+
#include "buf.h"
|
|
12
|
+
#include "err.h"
|
|
13
|
+
#include "ox.h"
|
|
14
|
+
#include "ruby.h"
|
|
15
|
+
#include "ruby/encoding.h"
|
|
16
|
+
#include "ruby/version.h"
|
|
17
|
+
|
|
18
|
+
#define MAX_DEPTH 128
|
|
19
|
+
|
|
20
|
+
static void builder_free(void *ptr);
|
|
21
|
+
|
|
22
|
+
static const rb_data_type_t ox_builder_type = {
|
|
23
|
+
"Ox/builder",
|
|
24
|
+
{
|
|
25
|
+
NULL,
|
|
26
|
+
builder_free,
|
|
27
|
+
NULL,
|
|
28
|
+
},
|
|
29
|
+
0,
|
|
30
|
+
0,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
typedef struct _element {
|
|
34
|
+
char *name;
|
|
35
|
+
char buf[64];
|
|
36
|
+
long len;
|
|
37
|
+
bool has_child;
|
|
38
|
+
bool non_text_child;
|
|
39
|
+
} *Element;
|
|
40
|
+
|
|
41
|
+
typedef struct _builder {
|
|
42
|
+
struct _buf buf;
|
|
43
|
+
int indent;
|
|
44
|
+
char encoding[64];
|
|
45
|
+
int depth;
|
|
46
|
+
FILE *file;
|
|
47
|
+
struct _element stack[MAX_DEPTH];
|
|
48
|
+
long line;
|
|
49
|
+
long col;
|
|
50
|
+
long pos;
|
|
51
|
+
} *Builder;
|
|
52
|
+
|
|
53
|
+
static VALUE builder_class = Qundef;
|
|
54
|
+
static const char indent_spaces[] = "\n "
|
|
55
|
+
" "; // 128 spaces
|
|
56
|
+
|
|
57
|
+
// The : character is equivalent to 10. Used for replacement characters up to
|
|
58
|
+
// 10 characters long such as ''. From
|
|
59
|
+
// https://www.w3.org/TR/2006/REC-xml11-20060816
|
|
60
|
+
#if 0
|
|
61
|
+
static const char xml_friendly_chars[257] = "\
|
|
62
|
+
:::::::::11::1::::::::::::::::::\
|
|
63
|
+
11611156111111111111111111114141\
|
|
64
|
+
11111111111111111111111111111111\
|
|
65
|
+
11111111111111111111111111111111\
|
|
66
|
+
11111111111111111111111111111111\
|
|
67
|
+
11111111111111111111111111111111\
|
|
68
|
+
11111111111111111111111111111111\
|
|
69
|
+
11111111111111111111111111111111";
|
|
70
|
+
#endif
|
|
71
|
+
|
|
72
|
+
// From 2.3 of the XML 1.1 spec. All over 0x20 except <&", > also. Builder
|
|
73
|
+
// uses double quotes for attributes.
|
|
74
|
+
static const char xml_attr_chars[257] = "\
|
|
75
|
+
:::::::::11::1::::::::::::::::::\
|
|
76
|
+
11611151111111111111111111114141\
|
|
77
|
+
11111111111111111111111111111111\
|
|
78
|
+
11111111111111111111111111111111\
|
|
79
|
+
11111111111111111111111111111111\
|
|
80
|
+
11111111111111111111111111111111\
|
|
81
|
+
11111111111111111111111111111111\
|
|
82
|
+
11111111111111111111111111111111";
|
|
83
|
+
|
|
84
|
+
// From 3.1 of the XML 1.1 spec. All over 0x20 except <&, > also.
|
|
85
|
+
static const char xml_element_chars[257] = "\
|
|
86
|
+
:::::::::11::1::::::::::::::::::\
|
|
87
|
+
11111151111111111111111111114141\
|
|
88
|
+
11111111111111111111111111111111\
|
|
89
|
+
11111111111111111111111111111111\
|
|
90
|
+
11111111111111111111111111111111\
|
|
91
|
+
11111111111111111111111111111111\
|
|
92
|
+
11111111111111111111111111111111\
|
|
93
|
+
11111111111111111111111111111111";
|
|
94
|
+
|
|
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
|
+
static void append_indent(Builder b) {
|
|
105
|
+
if (0 >= b->indent) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (b->buf.head < b->buf.tail) {
|
|
109
|
+
int cnt = (b->indent * (b->depth + 1)) + 1;
|
|
110
|
+
|
|
111
|
+
if (sizeof(indent_spaces) <= (size_t)cnt) {
|
|
112
|
+
cnt = sizeof(indent_spaces) - 1;
|
|
113
|
+
}
|
|
114
|
+
buf_append_string(&b->buf, indent_spaces, cnt);
|
|
115
|
+
b->line++;
|
|
116
|
+
b->col = cnt - 1;
|
|
117
|
+
b->pos += cnt;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
static void append_string(Builder b, const char *str, size_t size, const char *table, bool strip_invalid_chars) {
|
|
122
|
+
size_t xsize = xml_str_len((const unsigned char *)str, size, table);
|
|
123
|
+
|
|
124
|
+
if (size == xsize) {
|
|
125
|
+
const char *s = str;
|
|
126
|
+
const char *end = str + size;
|
|
127
|
+
|
|
128
|
+
buf_append_string(&b->buf, str, size);
|
|
129
|
+
b->col += size;
|
|
130
|
+
s = strchr(s, '\n');
|
|
131
|
+
while (NULL != s) {
|
|
132
|
+
b->line++;
|
|
133
|
+
b->col = end - s;
|
|
134
|
+
s = strchr(s + 1, '\n');
|
|
135
|
+
}
|
|
136
|
+
b->pos += size;
|
|
137
|
+
} else {
|
|
138
|
+
char buf[256];
|
|
139
|
+
char *end = buf + sizeof(buf) - 1;
|
|
140
|
+
char *bp = buf;
|
|
141
|
+
size_t i = size;
|
|
142
|
+
int fcnt;
|
|
143
|
+
|
|
144
|
+
for (; '\0' != *str && 0 < i; i--, str++) {
|
|
145
|
+
if ('1' == (fcnt = table[(unsigned char)*str])) {
|
|
146
|
+
if (end <= bp) {
|
|
147
|
+
buf_append_string(&b->buf, buf, bp - buf);
|
|
148
|
+
bp = buf;
|
|
149
|
+
}
|
|
150
|
+
if ('\n' == *str) {
|
|
151
|
+
b->line++;
|
|
152
|
+
b->col = 1;
|
|
153
|
+
} else {
|
|
154
|
+
b->col++;
|
|
155
|
+
}
|
|
156
|
+
b->pos++;
|
|
157
|
+
*bp++ = *str;
|
|
158
|
+
} else {
|
|
159
|
+
b->pos += fcnt - '0';
|
|
160
|
+
b->col += fcnt - '0';
|
|
161
|
+
if (buf < bp) {
|
|
162
|
+
buf_append_string(&b->buf, buf, bp - buf);
|
|
163
|
+
bp = buf;
|
|
164
|
+
}
|
|
165
|
+
switch (*str) {
|
|
166
|
+
case '"': buf_append_string(&b->buf, """, 6); break;
|
|
167
|
+
case '&': buf_append_string(&b->buf, "&", 5); break;
|
|
168
|
+
case '\'': buf_append_string(&b->buf, "'", 6); break;
|
|
169
|
+
case '<': buf_append_string(&b->buf, "<", 4); break;
|
|
170
|
+
case '>': buf_append_string(&b->buf, ">", 4); break;
|
|
171
|
+
default:
|
|
172
|
+
// Must be one of the invalid characters.
|
|
173
|
+
if (!strip_invalid_chars) {
|
|
174
|
+
rb_raise(ox_syntax_error_class, "'\\#x%02x' is not a valid XML character.", *str);
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (buf < bp) {
|
|
181
|
+
buf_append_string(&b->buf, buf, bp - buf);
|
|
182
|
+
bp = buf;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
static void append_sym_str(Builder b, VALUE v) {
|
|
188
|
+
const char *s;
|
|
189
|
+
long len;
|
|
190
|
+
|
|
191
|
+
switch (rb_type(v)) {
|
|
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);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
static void i_am_a_child(Builder b, bool is_text) {
|
|
206
|
+
if (0 <= b->depth) {
|
|
207
|
+
Element e = &b->stack[b->depth];
|
|
208
|
+
|
|
209
|
+
if (!e->has_child) {
|
|
210
|
+
e->has_child = true;
|
|
211
|
+
buf_append(&b->buf, '>');
|
|
212
|
+
b->col++;
|
|
213
|
+
b->pos++;
|
|
214
|
+
}
|
|
215
|
+
if (!is_text) {
|
|
216
|
+
e->non_text_child = true;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
static int append_attr(VALUE key, VALUE value, VALUE bv) {
|
|
222
|
+
Builder b = (Builder)bv;
|
|
223
|
+
|
|
224
|
+
buf_append(&b->buf, ' ');
|
|
225
|
+
b->col++;
|
|
226
|
+
b->pos++;
|
|
227
|
+
append_sym_str(b, key);
|
|
228
|
+
buf_append_string(&b->buf, "=\"", 2);
|
|
229
|
+
b->col += 2;
|
|
230
|
+
b->pos += 2;
|
|
231
|
+
Check_Type(value, T_STRING);
|
|
232
|
+
append_string(b, StringValuePtr(value), (int)RSTRING_LEN(value), xml_attr_chars, false);
|
|
233
|
+
buf_append(&b->buf, '"');
|
|
234
|
+
b->col++;
|
|
235
|
+
b->pos++;
|
|
236
|
+
|
|
237
|
+
return ST_CONTINUE;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
static void init(Builder b, int fd, int indent, long initial_size) {
|
|
241
|
+
buf_init(&b->buf, fd, initial_size);
|
|
242
|
+
b->indent = indent;
|
|
243
|
+
*b->encoding = '\0';
|
|
244
|
+
b->depth = -1;
|
|
245
|
+
b->line = 1;
|
|
246
|
+
b->col = 1;
|
|
247
|
+
b->pos = 0;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static void builder_free(void *ptr) {
|
|
251
|
+
Builder b;
|
|
252
|
+
Element e;
|
|
253
|
+
int d;
|
|
254
|
+
|
|
255
|
+
if (0 == ptr) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
b = (Builder)ptr;
|
|
259
|
+
buf_cleanup(&b->buf);
|
|
260
|
+
for (e = b->stack, d = b->depth; 0 < d; d--, e++) {
|
|
261
|
+
if (e->name != e->buf) {
|
|
262
|
+
free(e->name);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
xfree(ptr);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
static void pop(Builder b) {
|
|
269
|
+
Element e;
|
|
270
|
+
|
|
271
|
+
if (0 > b->depth) {
|
|
272
|
+
rb_raise(ox_arg_error_class, "closed too many elements");
|
|
273
|
+
}
|
|
274
|
+
e = &b->stack[b->depth];
|
|
275
|
+
b->depth--;
|
|
276
|
+
if (e->has_child) {
|
|
277
|
+
if (e->non_text_child) {
|
|
278
|
+
append_indent(b);
|
|
279
|
+
}
|
|
280
|
+
buf_append_string(&b->buf, "</", 2);
|
|
281
|
+
append_string(b, e->name, e->len, xml_element_chars, false);
|
|
282
|
+
buf_append(&b->buf, '>');
|
|
283
|
+
b->col += e->len + 3;
|
|
284
|
+
b->pos += e->len + 3;
|
|
285
|
+
if (e->buf != e->name) {
|
|
286
|
+
free(e->name);
|
|
287
|
+
e->name = 0;
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
buf_append_string(&b->buf, "/>", 2);
|
|
291
|
+
b->col += 2;
|
|
292
|
+
b->pos += 2;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
static void bclose(Builder b) {
|
|
297
|
+
while (0 <= b->depth) {
|
|
298
|
+
pop(b);
|
|
299
|
+
}
|
|
300
|
+
if (0 <= b->indent) {
|
|
301
|
+
buf_append(&b->buf, '\n');
|
|
302
|
+
}
|
|
303
|
+
b->line++;
|
|
304
|
+
b->col = 1;
|
|
305
|
+
b->pos++;
|
|
306
|
+
buf_finish(&b->buf);
|
|
307
|
+
if (NULL != b->file) {
|
|
308
|
+
fclose(b->file);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
static VALUE to_s(Builder b) {
|
|
313
|
+
volatile VALUE rstr;
|
|
314
|
+
|
|
315
|
+
if (0 != b->buf.fd) {
|
|
316
|
+
rb_raise(ox_arg_error_class, "can not create a String with a stream or file builder.");
|
|
317
|
+
}
|
|
318
|
+
if (0 <= b->indent && '\n' != *(b->buf.tail - 1)) {
|
|
319
|
+
buf_append(&b->buf, '\n');
|
|
320
|
+
b->line++;
|
|
321
|
+
b->col = 1;
|
|
322
|
+
b->pos++;
|
|
323
|
+
}
|
|
324
|
+
*b->buf.tail = '\0'; // for debugging
|
|
325
|
+
rstr = rb_str_new(b->buf.head, buf_len(&b->buf));
|
|
326
|
+
|
|
327
|
+
if ('\0' != *b->encoding) {
|
|
328
|
+
rb_enc_associate(rstr, rb_enc_find(b->encoding));
|
|
329
|
+
}
|
|
330
|
+
return rstr;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* call-seq: new(options)
|
|
334
|
+
*
|
|
335
|
+
* Creates a new Builder that will write to a string that can be retrieved with
|
|
336
|
+
* the to_s() method. If a block is given it is executed with a single parameter
|
|
337
|
+
* which is the builder instance. The return value is then the generated string.
|
|
338
|
+
*
|
|
339
|
+
* - +options+ - (Hash) formating options
|
|
340
|
+
* - +:indent+ (Fixnum) indentaion level, negative values excludes terminating newline
|
|
341
|
+
* - +:size+ (Fixnum) the initial size of the string buffer
|
|
342
|
+
*/
|
|
343
|
+
static VALUE builder_new(int argc, VALUE *argv, VALUE self) {
|
|
344
|
+
Builder b = ALLOC(struct _builder);
|
|
345
|
+
int indent = ox_default_options.indent;
|
|
346
|
+
long buf_size = 0;
|
|
347
|
+
|
|
348
|
+
if (1 == argc) {
|
|
349
|
+
volatile VALUE v;
|
|
350
|
+
|
|
351
|
+
rb_check_type(*argv, T_HASH);
|
|
352
|
+
if (Qnil != (v = rb_hash_lookup(*argv, ox_indent_sym))) {
|
|
353
|
+
if (rb_cInteger != rb_obj_class(v)) {
|
|
354
|
+
rb_raise(ox_parse_error_class, ":indent must be a fixnum.\n");
|
|
355
|
+
}
|
|
356
|
+
indent = NUM2INT(v);
|
|
357
|
+
}
|
|
358
|
+
if (Qnil != (v = rb_hash_lookup(*argv, ox_size_sym))) {
|
|
359
|
+
if (rb_cInteger != rb_obj_class(v)) {
|
|
360
|
+
rb_raise(ox_parse_error_class, ":size must be a fixnum.\n");
|
|
361
|
+
}
|
|
362
|
+
buf_size = NUM2LONG(v);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
b->file = NULL;
|
|
366
|
+
init(b, 0, indent, buf_size);
|
|
367
|
+
|
|
368
|
+
if (rb_block_given_p()) {
|
|
369
|
+
volatile VALUE rb = TypedData_Wrap_Struct(builder_class, &ox_builder_type, b);
|
|
370
|
+
|
|
371
|
+
rb_yield(rb);
|
|
372
|
+
bclose(b);
|
|
373
|
+
|
|
374
|
+
return to_s(b);
|
|
375
|
+
} else {
|
|
376
|
+
return TypedData_Wrap_Struct(builder_class, &ox_builder_type, b);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* call-seq: file(filename, options)
|
|
381
|
+
*
|
|
382
|
+
* Creates a new Builder that will write to a file.
|
|
383
|
+
*
|
|
384
|
+
* - +filename+ (String) filename to write to
|
|
385
|
+
* - +options+ - (Hash) formating options
|
|
386
|
+
* - +:indent+ (Fixnum) indentaion level, negative values excludes terminating newline
|
|
387
|
+
* - +:size+ (Fixnum) the initial size of the string buffer
|
|
388
|
+
*/
|
|
389
|
+
static VALUE builder_file(int argc, VALUE *argv, VALUE self) {
|
|
390
|
+
Builder b = ALLOC(struct _builder);
|
|
391
|
+
int indent = ox_default_options.indent;
|
|
392
|
+
long buf_size = 0;
|
|
393
|
+
FILE *f;
|
|
394
|
+
|
|
395
|
+
if (1 > argc) {
|
|
396
|
+
rb_raise(ox_arg_error_class, "missing filename");
|
|
397
|
+
}
|
|
398
|
+
Check_Type(*argv, T_STRING);
|
|
399
|
+
if (NULL == (f = fopen(StringValuePtr(*argv), "w"))) {
|
|
400
|
+
xfree(b);
|
|
401
|
+
rb_raise(rb_eIOError, "%s\n", strerror(errno));
|
|
402
|
+
}
|
|
403
|
+
if (2 == argc) {
|
|
404
|
+
volatile VALUE v;
|
|
405
|
+
|
|
406
|
+
rb_check_type(argv[1], T_HASH);
|
|
407
|
+
if (Qnil != (v = rb_hash_lookup(argv[1], ox_indent_sym))) {
|
|
408
|
+
if (rb_cInteger != rb_obj_class(v)) {
|
|
409
|
+
rb_raise(ox_parse_error_class, ":indent must be a fixnum.\n");
|
|
410
|
+
}
|
|
411
|
+
indent = NUM2INT(v);
|
|
412
|
+
}
|
|
413
|
+
if (Qnil != (v = rb_hash_lookup(argv[1], ox_size_sym))) {
|
|
414
|
+
if (rb_cInteger != rb_obj_class(v)) {
|
|
415
|
+
rb_raise(ox_parse_error_class, ":size must be a fixnum.\n");
|
|
416
|
+
}
|
|
417
|
+
buf_size = NUM2LONG(v);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
b->file = f;
|
|
421
|
+
init(b, fileno(f), indent, buf_size);
|
|
422
|
+
|
|
423
|
+
if (rb_block_given_p()) {
|
|
424
|
+
volatile VALUE rb = TypedData_Wrap_Struct(builder_class, &ox_builder_type, b);
|
|
425
|
+
rb_yield(rb);
|
|
426
|
+
bclose(b);
|
|
427
|
+
return Qnil;
|
|
428
|
+
} else {
|
|
429
|
+
return TypedData_Wrap_Struct(builder_class, &ox_builder_type, b);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/* call-seq: io(io, options)
|
|
434
|
+
*
|
|
435
|
+
* Creates a new Builder that will write to an IO instance.
|
|
436
|
+
*
|
|
437
|
+
* - +io+ (String) IO to write to
|
|
438
|
+
* - +options+ - (Hash) formating options
|
|
439
|
+
* - +:indent+ (Fixnum) indentaion level, negative values excludes terminating newline
|
|
440
|
+
* - +:size+ (Fixnum) the initial size of the string buffer
|
|
441
|
+
*/
|
|
442
|
+
static VALUE builder_io(int argc, VALUE *argv, VALUE self) {
|
|
443
|
+
Builder b = ALLOC(struct _builder);
|
|
444
|
+
int indent = ox_default_options.indent;
|
|
445
|
+
long buf_size = 0;
|
|
446
|
+
int fd;
|
|
447
|
+
volatile VALUE v;
|
|
448
|
+
|
|
449
|
+
if (1 > argc) {
|
|
450
|
+
rb_raise(ox_arg_error_class, "missing IO object");
|
|
451
|
+
}
|
|
452
|
+
if (!rb_respond_to(*argv, ox_fileno_id) || Qnil == (v = rb_funcall(*argv, ox_fileno_id, 0)) ||
|
|
453
|
+
0 == (fd = FIX2INT(v))) {
|
|
454
|
+
rb_raise(rb_eIOError, "expected an IO that has a fileno.");
|
|
455
|
+
}
|
|
456
|
+
if (2 == argc) {
|
|
457
|
+
volatile VALUE v;
|
|
458
|
+
|
|
459
|
+
rb_check_type(argv[1], T_HASH);
|
|
460
|
+
if (Qnil != (v = rb_hash_lookup(argv[1], ox_indent_sym))) {
|
|
461
|
+
if (rb_cInteger != rb_obj_class(v)) {
|
|
462
|
+
rb_raise(ox_parse_error_class, ":indent must be a fixnum.\n");
|
|
463
|
+
}
|
|
464
|
+
indent = NUM2INT(v);
|
|
465
|
+
}
|
|
466
|
+
if (Qnil != (v = rb_hash_lookup(argv[1], ox_size_sym))) {
|
|
467
|
+
if (rb_cInteger != rb_obj_class(v)) {
|
|
468
|
+
rb_raise(ox_parse_error_class, ":size must be a fixnum.\n");
|
|
469
|
+
}
|
|
470
|
+
buf_size = NUM2LONG(v);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
b->file = NULL;
|
|
474
|
+
init(b, fd, indent, buf_size);
|
|
475
|
+
|
|
476
|
+
if (rb_block_given_p()) {
|
|
477
|
+
volatile VALUE rb = TypedData_Wrap_Struct(builder_class, &ox_builder_type, b);
|
|
478
|
+
rb_yield(rb);
|
|
479
|
+
bclose(b);
|
|
480
|
+
return Qnil;
|
|
481
|
+
} else {
|
|
482
|
+
return TypedData_Wrap_Struct(builder_class, &ox_builder_type, b);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* call-seq: instruct(decl,options)
|
|
487
|
+
*
|
|
488
|
+
* Adds the top level <?xml?> element.
|
|
489
|
+
*
|
|
490
|
+
* - +decl+ - (String) 'xml' expected
|
|
491
|
+
* - +options+ - (Hash) version or encoding
|
|
492
|
+
*/
|
|
493
|
+
static VALUE builder_instruct(int argc, VALUE *argv, VALUE self) {
|
|
494
|
+
Builder b;
|
|
495
|
+
|
|
496
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
497
|
+
i_am_a_child(b, false);
|
|
498
|
+
append_indent(b);
|
|
499
|
+
if (0 == argc) {
|
|
500
|
+
buf_append_string(&b->buf, "<?xml?>", 7);
|
|
501
|
+
b->col += 7;
|
|
502
|
+
b->pos += 7;
|
|
503
|
+
} else {
|
|
504
|
+
volatile VALUE v;
|
|
505
|
+
|
|
506
|
+
buf_append_string(&b->buf, "<?", 2);
|
|
507
|
+
b->col += 2;
|
|
508
|
+
b->pos += 2;
|
|
509
|
+
append_sym_str(b, *argv);
|
|
510
|
+
if (1 < argc && rb_cHash == rb_obj_class(argv[1])) {
|
|
511
|
+
int len;
|
|
512
|
+
|
|
513
|
+
if (Qnil != (v = rb_hash_lookup(argv[1], ox_version_sym))) {
|
|
514
|
+
if (rb_cString != rb_obj_class(v)) {
|
|
515
|
+
rb_raise(ox_parse_error_class, ":version must be a Symbol.\n");
|
|
516
|
+
}
|
|
517
|
+
len = (int)RSTRING_LEN(v);
|
|
518
|
+
buf_append_string(&b->buf, " version=\"", 10);
|
|
519
|
+
buf_append_string(&b->buf, StringValuePtr(v), len);
|
|
520
|
+
buf_append(&b->buf, '"');
|
|
521
|
+
b->col += len + 11;
|
|
522
|
+
b->pos += len + 11;
|
|
523
|
+
}
|
|
524
|
+
if (Qnil != (v = rb_hash_lookup(argv[1], ox_encoding_sym))) {
|
|
525
|
+
if (rb_cString != rb_obj_class(v)) {
|
|
526
|
+
rb_raise(ox_parse_error_class, ":encoding must be a Symbol.\n");
|
|
527
|
+
}
|
|
528
|
+
len = (int)RSTRING_LEN(v);
|
|
529
|
+
buf_append_string(&b->buf, " encoding=\"", 11);
|
|
530
|
+
buf_append_string(&b->buf, StringValuePtr(v), len);
|
|
531
|
+
buf_append(&b->buf, '"');
|
|
532
|
+
b->col += len + 12;
|
|
533
|
+
b->pos += len + 12;
|
|
534
|
+
strncpy(b->encoding, StringValuePtr(v), sizeof(b->encoding));
|
|
535
|
+
b->encoding[sizeof(b->encoding) - 1] = '\0';
|
|
536
|
+
}
|
|
537
|
+
if (Qnil != (v = rb_hash_lookup(argv[1], ox_standalone_sym))) {
|
|
538
|
+
if (rb_cString != rb_obj_class(v)) {
|
|
539
|
+
rb_raise(ox_parse_error_class, ":standalone must be a Symbol.\n");
|
|
540
|
+
}
|
|
541
|
+
len = (int)RSTRING_LEN(v);
|
|
542
|
+
buf_append_string(&b->buf, " standalone=\"", 13);
|
|
543
|
+
buf_append_string(&b->buf, StringValuePtr(v), len);
|
|
544
|
+
buf_append(&b->buf, '"');
|
|
545
|
+
b->col += len + 14;
|
|
546
|
+
b->pos += len + 14;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
buf_append_string(&b->buf, "?>", 2);
|
|
550
|
+
b->col += 2;
|
|
551
|
+
b->pos += 2;
|
|
552
|
+
}
|
|
553
|
+
return Qnil;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/* call-seq: element(name,attributes)
|
|
557
|
+
*
|
|
558
|
+
* Adds an element with the name and attributes provided. If a block is given
|
|
559
|
+
* then on closing of the block a pop() is called.
|
|
560
|
+
*
|
|
561
|
+
* - +name+ - (String) name of the element
|
|
562
|
+
* - +attributes+ - (Hash) of the element
|
|
563
|
+
*/
|
|
564
|
+
static VALUE builder_element(int argc, VALUE *argv, VALUE self) {
|
|
565
|
+
Builder b;
|
|
566
|
+
Element e;
|
|
567
|
+
const char *name;
|
|
568
|
+
long len;
|
|
569
|
+
|
|
570
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
571
|
+
|
|
572
|
+
if (1 > argc) {
|
|
573
|
+
rb_raise(ox_arg_error_class, "missing element name");
|
|
574
|
+
}
|
|
575
|
+
i_am_a_child(b, false);
|
|
576
|
+
append_indent(b);
|
|
577
|
+
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
|
+
e = &b->stack[b->depth];
|
|
593
|
+
if (sizeof(e->buf) <= (size_t)len) {
|
|
594
|
+
e->name = strdup(name);
|
|
595
|
+
*e->buf = '\0';
|
|
596
|
+
} else {
|
|
597
|
+
strcpy(e->buf, name);
|
|
598
|
+
e->name = e->buf;
|
|
599
|
+
}
|
|
600
|
+
e->len = len;
|
|
601
|
+
e->has_child = false;
|
|
602
|
+
e->non_text_child = false;
|
|
603
|
+
|
|
604
|
+
buf_append(&b->buf, '<');
|
|
605
|
+
b->col++;
|
|
606
|
+
b->pos++;
|
|
607
|
+
append_string(b, e->name, len, xml_element_chars, false);
|
|
608
|
+
if (1 < argc && T_HASH == rb_type(argv[1])) {
|
|
609
|
+
rb_hash_foreach(argv[1], append_attr, (VALUE)b);
|
|
610
|
+
}
|
|
611
|
+
// Do not close with > or /> yet. That is done with i_am_a_child() or pop().
|
|
612
|
+
if (rb_block_given_p()) {
|
|
613
|
+
rb_yield(self);
|
|
614
|
+
pop(b);
|
|
615
|
+
}
|
|
616
|
+
return Qnil;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/* call-seq: void_element(name,attributes)
|
|
620
|
+
*
|
|
621
|
+
* Adds an void element with the name and attributes provided.
|
|
622
|
+
*
|
|
623
|
+
* - +name+ - (String) name of the element
|
|
624
|
+
* - +attributes+ - (Hash) of the element
|
|
625
|
+
*/
|
|
626
|
+
static VALUE builder_void_element(int argc, VALUE *argv, VALUE self) {
|
|
627
|
+
Builder b;
|
|
628
|
+
const char *name;
|
|
629
|
+
long len;
|
|
630
|
+
|
|
631
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
632
|
+
|
|
633
|
+
if (1 > argc) {
|
|
634
|
+
rb_raise(ox_arg_error_class, "missing element name");
|
|
635
|
+
}
|
|
636
|
+
i_am_a_child(b, false);
|
|
637
|
+
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
|
+
buf_append(&b->buf, '<');
|
|
650
|
+
b->col++;
|
|
651
|
+
b->pos++;
|
|
652
|
+
append_string(b, name, len, xml_element_chars, false);
|
|
653
|
+
if (1 < argc && T_HASH == rb_type(argv[1])) {
|
|
654
|
+
rb_hash_foreach(argv[1], append_attr, (VALUE)b);
|
|
655
|
+
}
|
|
656
|
+
buf_append_string(&b->buf, ">", 1);
|
|
657
|
+
b->col++;
|
|
658
|
+
;
|
|
659
|
+
b->pos++;
|
|
660
|
+
|
|
661
|
+
return Qnil;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/* call-seq: comment(text)
|
|
665
|
+
*
|
|
666
|
+
* Adds a comment element to the XML string being formed.
|
|
667
|
+
* - +text+ - (String) contents of the comment
|
|
668
|
+
*/
|
|
669
|
+
static VALUE builder_comment(VALUE self, VALUE text) {
|
|
670
|
+
Builder b;
|
|
671
|
+
|
|
672
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
673
|
+
rb_check_type(text, T_STRING);
|
|
674
|
+
i_am_a_child(b, false);
|
|
675
|
+
append_indent(b);
|
|
676
|
+
buf_append_string(&b->buf, "<!--", 4);
|
|
677
|
+
b->col += 5;
|
|
678
|
+
b->pos += 5;
|
|
679
|
+
append_string(b, StringValuePtr(text), RSTRING_LEN(text), xml_element_chars, false);
|
|
680
|
+
buf_append_string(&b->buf, "-->", 3);
|
|
681
|
+
b->col += 5;
|
|
682
|
+
b->pos += 5;
|
|
683
|
+
|
|
684
|
+
return Qnil;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/* call-seq: doctype(text)
|
|
688
|
+
*
|
|
689
|
+
* Adds a DOCTYPE element to the XML string being formed.
|
|
690
|
+
* - +text+ - (String) contents of the doctype
|
|
691
|
+
*/
|
|
692
|
+
static VALUE builder_doctype(VALUE self, VALUE text) {
|
|
693
|
+
Builder b;
|
|
694
|
+
|
|
695
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
696
|
+
rb_check_type(text, T_STRING);
|
|
697
|
+
i_am_a_child(b, false);
|
|
698
|
+
append_indent(b);
|
|
699
|
+
buf_append_string(&b->buf, "<!DOCTYPE ", 10);
|
|
700
|
+
b->col += 10;
|
|
701
|
+
b->pos += 10;
|
|
702
|
+
append_string(b, StringValuePtr(text), RSTRING_LEN(text), xml_element_chars, false);
|
|
703
|
+
buf_append(&b->buf, '>');
|
|
704
|
+
b->col++;
|
|
705
|
+
b->pos++;
|
|
706
|
+
|
|
707
|
+
return Qnil;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/* call-seq: text(text)
|
|
711
|
+
*
|
|
712
|
+
* Adds a text element to the XML string being formed.
|
|
713
|
+
* - +text+ - (String) contents of the text field
|
|
714
|
+
* - +strip_invalid_chars+ - [true|false] strips any characters invalid for XML, defaults to false
|
|
715
|
+
*/
|
|
716
|
+
static VALUE builder_text(int argc, VALUE *argv, VALUE self) {
|
|
717
|
+
Builder b;
|
|
718
|
+
volatile VALUE v;
|
|
719
|
+
volatile VALUE strip_invalid_chars;
|
|
720
|
+
|
|
721
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
722
|
+
|
|
723
|
+
if ((0 == argc) || (argc > 2)) {
|
|
724
|
+
rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1..2)", argc);
|
|
725
|
+
}
|
|
726
|
+
v = argv[0];
|
|
727
|
+
if (2 == argc) {
|
|
728
|
+
strip_invalid_chars = argv[1];
|
|
729
|
+
} else {
|
|
730
|
+
strip_invalid_chars = Qfalse;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
v = rb_String(v);
|
|
734
|
+
i_am_a_child(b, true);
|
|
735
|
+
append_string(b, StringValuePtr(v), RSTRING_LEN(v), xml_element_chars, RTEST(strip_invalid_chars));
|
|
736
|
+
|
|
737
|
+
return Qnil;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/* call-seq: cdata(data)
|
|
741
|
+
*
|
|
742
|
+
* Adds a CDATA element to the XML string being formed.
|
|
743
|
+
* - +data+ - (String) contents of the CDATA element
|
|
744
|
+
*/
|
|
745
|
+
static VALUE builder_cdata(VALUE self, VALUE data) {
|
|
746
|
+
Builder b;
|
|
747
|
+
volatile VALUE v = data;
|
|
748
|
+
const char *str;
|
|
749
|
+
const char *s;
|
|
750
|
+
const char *end;
|
|
751
|
+
int len;
|
|
752
|
+
|
|
753
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
754
|
+
|
|
755
|
+
v = rb_String(v);
|
|
756
|
+
str = StringValuePtr(v);
|
|
757
|
+
len = (int)RSTRING_LEN(v);
|
|
758
|
+
s = str;
|
|
759
|
+
end = str + len;
|
|
760
|
+
i_am_a_child(b, false);
|
|
761
|
+
append_indent(b);
|
|
762
|
+
buf_append_string(&b->buf, "<![CDATA[", 9);
|
|
763
|
+
b->col += 9;
|
|
764
|
+
b->pos += 9;
|
|
765
|
+
buf_append_string(&b->buf, str, len);
|
|
766
|
+
b->col += len;
|
|
767
|
+
s = strchr(s, '\n');
|
|
768
|
+
while (NULL != s) {
|
|
769
|
+
b->line++;
|
|
770
|
+
b->col = end - s;
|
|
771
|
+
s = strchr(s + 1, '\n');
|
|
772
|
+
}
|
|
773
|
+
b->pos += len;
|
|
774
|
+
buf_append_string(&b->buf, "]]>", 3);
|
|
775
|
+
b->col += 3;
|
|
776
|
+
b->pos += 3;
|
|
777
|
+
|
|
778
|
+
return Qnil;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/* call-seq: raw(text)
|
|
782
|
+
*
|
|
783
|
+
* Adds the provided string directly to the XML without formatting or modifications.
|
|
784
|
+
*
|
|
785
|
+
* - +text+ - (String) contents to be added
|
|
786
|
+
*/
|
|
787
|
+
static VALUE builder_raw(VALUE self, VALUE text) {
|
|
788
|
+
Builder b;
|
|
789
|
+
volatile VALUE v = text;
|
|
790
|
+
const char *str;
|
|
791
|
+
const char *s;
|
|
792
|
+
const char *end;
|
|
793
|
+
int len;
|
|
794
|
+
|
|
795
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
796
|
+
v = rb_String(v);
|
|
797
|
+
str = StringValuePtr(v);
|
|
798
|
+
len = (int)RSTRING_LEN(v);
|
|
799
|
+
s = str;
|
|
800
|
+
end = str + len;
|
|
801
|
+
i_am_a_child(b, true);
|
|
802
|
+
buf_append_string(&b->buf, str, len);
|
|
803
|
+
b->col += len;
|
|
804
|
+
s = strchr(s, '\n');
|
|
805
|
+
while (NULL != s) {
|
|
806
|
+
b->line++;
|
|
807
|
+
b->col = end - s;
|
|
808
|
+
s = strchr(s + 1, '\n');
|
|
809
|
+
}
|
|
810
|
+
b->pos += len;
|
|
811
|
+
|
|
812
|
+
return Qnil;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/* call-seq: to_s()
|
|
816
|
+
*
|
|
817
|
+
* Returns the JSON document string in what ever state the construction is at.
|
|
818
|
+
*/
|
|
819
|
+
static VALUE builder_to_s(VALUE self) {
|
|
820
|
+
Builder b;
|
|
821
|
+
|
|
822
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
823
|
+
return to_s(b);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/* call-seq: line()
|
|
827
|
+
*
|
|
828
|
+
* Returns the current line in the output. The first line is line 1.
|
|
829
|
+
*/
|
|
830
|
+
static VALUE builder_line(VALUE self) {
|
|
831
|
+
Builder b;
|
|
832
|
+
|
|
833
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
834
|
+
return LONG2NUM(b->line);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/* call-seq: column()
|
|
838
|
+
*
|
|
839
|
+
* Returns the current column in the output. The first character in a line is at
|
|
840
|
+
* column 1.
|
|
841
|
+
*/
|
|
842
|
+
static VALUE builder_column(VALUE self) {
|
|
843
|
+
Builder b;
|
|
844
|
+
|
|
845
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
846
|
+
return LONG2NUM(b->col);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/* call-seq: indent()
|
|
850
|
+
*
|
|
851
|
+
* Returns the indentation level
|
|
852
|
+
*/
|
|
853
|
+
static VALUE builder_get_indent(VALUE self) {
|
|
854
|
+
Builder b;
|
|
855
|
+
|
|
856
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
857
|
+
return INT2NUM(b->indent);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/* call-seq: indent=(indent)
|
|
861
|
+
*
|
|
862
|
+
* Sets the indentation level
|
|
863
|
+
*
|
|
864
|
+
* - +indent+ (Fixnum) indentaion level, negative values excludes terminating newline
|
|
865
|
+
*/
|
|
866
|
+
static VALUE builder_set_indent(VALUE self, VALUE indent) {
|
|
867
|
+
Builder b;
|
|
868
|
+
|
|
869
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
870
|
+
|
|
871
|
+
if (rb_cInteger != rb_obj_class(indent)) {
|
|
872
|
+
rb_raise(ox_parse_error_class, "indent must be a fixnum.\n");
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
b->indent = NUM2INT(indent);
|
|
876
|
+
return Qnil;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/* call-seq: pos()
|
|
880
|
+
*
|
|
881
|
+
* Returns the number of bytes written.
|
|
882
|
+
*/
|
|
883
|
+
static VALUE builder_pos(VALUE self) {
|
|
884
|
+
Builder b;
|
|
885
|
+
|
|
886
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
887
|
+
return LONG2NUM(b->pos);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/* call-seq: pop()
|
|
891
|
+
*
|
|
892
|
+
* Closes the current element.
|
|
893
|
+
*/
|
|
894
|
+
static VALUE builder_pop(VALUE self) {
|
|
895
|
+
Builder b;
|
|
896
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
897
|
+
pop(b);
|
|
898
|
+
|
|
899
|
+
return Qnil;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/* call-seq: close()
|
|
903
|
+
*
|
|
904
|
+
* Closes the all elements and the document.
|
|
905
|
+
*/
|
|
906
|
+
static VALUE builder_close(VALUE self) {
|
|
907
|
+
Builder b;
|
|
908
|
+
|
|
909
|
+
TypedData_Get_Struct(self, struct _builder, &ox_builder_type, b);
|
|
910
|
+
bclose(b);
|
|
911
|
+
|
|
912
|
+
return Qnil;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/*
|
|
916
|
+
* Document-class: Ox::Builder
|
|
917
|
+
*
|
|
918
|
+
* An XML builder.
|
|
919
|
+
*/
|
|
920
|
+
void ox_init_builder(VALUE ox) {
|
|
921
|
+
#if 0
|
|
922
|
+
// Just for rdoc.
|
|
923
|
+
ox = rb_define_module("Ox");
|
|
924
|
+
#endif
|
|
925
|
+
builder_class = rb_define_class_under(ox, "Builder", rb_cObject);
|
|
926
|
+
#if RUBY_API_VERSION_CODE >= 30200
|
|
927
|
+
rb_undef_alloc_func(builder_class);
|
|
928
|
+
#endif
|
|
929
|
+
rb_define_module_function(builder_class, "new", builder_new, -1);
|
|
930
|
+
rb_define_module_function(builder_class, "file", builder_file, -1);
|
|
931
|
+
rb_define_module_function(builder_class, "io", builder_io, -1);
|
|
932
|
+
rb_define_method(builder_class, "instruct", builder_instruct, -1);
|
|
933
|
+
rb_define_method(builder_class, "comment", builder_comment, 1);
|
|
934
|
+
rb_define_method(builder_class, "doctype", builder_doctype, 1);
|
|
935
|
+
rb_define_method(builder_class, "element", builder_element, -1);
|
|
936
|
+
rb_define_method(builder_class, "void_element", builder_void_element, -1);
|
|
937
|
+
rb_define_method(builder_class, "text", builder_text, -1);
|
|
938
|
+
rb_define_method(builder_class, "cdata", builder_cdata, 1);
|
|
939
|
+
rb_define_method(builder_class, "raw", builder_raw, 1);
|
|
940
|
+
rb_define_method(builder_class, "pop", builder_pop, 0);
|
|
941
|
+
rb_define_method(builder_class, "close", builder_close, 0);
|
|
942
|
+
rb_define_method(builder_class, "to_s", builder_to_s, 0);
|
|
943
|
+
rb_define_method(builder_class, "line", builder_line, 0);
|
|
944
|
+
rb_define_method(builder_class, "column", builder_column, 0);
|
|
945
|
+
rb_define_method(builder_class, "pos", builder_pos, 0);
|
|
946
|
+
rb_define_method(builder_class, "indent", builder_get_indent, 0);
|
|
947
|
+
rb_define_method(builder_class, "indent=", builder_set_indent, 1);
|
|
948
|
+
}
|