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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +751 -0
  3. data/LICENSE +21 -0
  4. data/README.md +351 -0
  5. data/ext/ox/attr.h +78 -0
  6. data/ext/ox/base64.c +105 -0
  7. data/ext/ox/base64.h +18 -0
  8. data/ext/ox/buf.h +162 -0
  9. data/ext/ox/builder.c +948 -0
  10. data/ext/ox/cache.c +351 -0
  11. data/ext/ox/cache.h +21 -0
  12. data/ext/ox/cache8.c +106 -0
  13. data/ext/ox/cache8.h +23 -0
  14. data/ext/ox/dump.c +1260 -0
  15. data/ext/ox/err.c +46 -0
  16. data/ext/ox/err.h +36 -0
  17. data/ext/ox/extconf.rb +47 -0
  18. data/ext/ox/gen_load.c +342 -0
  19. data/ext/ox/hash_load.c +309 -0
  20. data/ext/ox/helper.h +84 -0
  21. data/ext/ox/intern.c +157 -0
  22. data/ext/ox/intern.h +25 -0
  23. data/ext/ox/obj_load.c +809 -0
  24. data/ext/ox/ox.c +1649 -0
  25. data/ext/ox/ox.h +245 -0
  26. data/ext/ox/parse.c +1197 -0
  27. data/ext/ox/sax.c +1570 -0
  28. data/ext/ox/sax.h +69 -0
  29. data/ext/ox/sax_as.c +270 -0
  30. data/ext/ox/sax_buf.c +209 -0
  31. data/ext/ox/sax_buf.h +204 -0
  32. data/ext/ox/sax_hint.c +207 -0
  33. data/ext/ox/sax_hint.h +40 -0
  34. data/ext/ox/sax_stack.h +113 -0
  35. data/ext/ox/slotcache.c +158 -0
  36. data/ext/ox/slotcache.h +19 -0
  37. data/ext/ox/special.c +390 -0
  38. data/ext/ox/special.h +14 -0
  39. data/ext/ox/type.h +39 -0
  40. data/lib/ox/bag.rb +103 -0
  41. data/lib/ox/cdata.rb +10 -0
  42. data/lib/ox/comment.rb +11 -0
  43. data/lib/ox/doctype.rb +11 -0
  44. data/lib/ox/document.rb +28 -0
  45. data/lib/ox/element.rb +464 -0
  46. data/lib/ox/error.rb +25 -0
  47. data/lib/ox/hasattrs.rb +54 -0
  48. data/lib/ox/instruct.rb +34 -0
  49. data/lib/ox/node.rb +23 -0
  50. data/lib/ox/raw.rb +12 -0
  51. data/lib/ox/sax.rb +97 -0
  52. data/lib/ox/version.rb +4 -0
  53. data/lib/ox/xmlrpc_adapter.rb +33 -0
  54. data/lib/ox.rb +79 -0
  55. 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 '&#x10FFFF;'. 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, "&quot;", 6); break;
167
+ case '&': buf_append_string(&b->buf, "&amp;", 5); break;
168
+ case '\'': buf_append_string(&b->buf, "&apos;", 6); break;
169
+ case '<': buf_append_string(&b->buf, "&lt;", 4); break;
170
+ case '>': buf_append_string(&b->buf, "&gt;", 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
+ }