ox 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ox might be problematic. Click here for more details.

@@ -29,6 +29,7 @@
29
29
  */
30
30
 
31
31
  #include <stdlib.h>
32
+ #include <stdio.h>
32
33
 
33
34
  #include "base64.h"
34
35
 
@@ -64,10 +65,10 @@ to_base64(const u_char *src, int len, char *b64) {
64
65
  b1 = *src++;
65
66
  b2 = *src++;
66
67
  b3 = *src++;
67
- *b64++ = digits[b1 >> 2];
68
- *b64++ = digits[((b1 & 0x03) << 4) | (b2 >> 4)];
69
- *b64++ = digits[((b2 & 0x0F) << 2) | (b3 >> 6)];
70
- *b64++ = digits[b3 & 0x3F];
68
+ *b64++ = digits[(u_char)(b1 >> 2)];
69
+ *b64++ = digits[(u_char)(((b1 & 0x03) << 4) | (b2 >> 4))];
70
+ *b64++ = digits[(u_char)(((b2 & 0x0F) << 2) | (b3 >> 6))];
71
+ *b64++ = digits[(u_char)(b3 & 0x3F)];
71
72
  }
72
73
  if (1 == len3) {
73
74
  b1 = *src++;
@@ -90,7 +91,7 @@ unsigned long
90
91
  b64_orig_size(const char *text) {
91
92
  const char *start = text;
92
93
  unsigned long size = 0;
93
-
94
+
94
95
  if ('\0' != *text) {
95
96
  for (; 0 != *text; text++) { }
96
97
  size = (text - start) * 3 / 4;
@@ -67,10 +67,11 @@ typedef struct _Out {
67
67
  int depth; // used by dumpHash
68
68
  } *Out;
69
69
 
70
- static void dump_obj_to_xml(VALUE obj, int indent, int xsd_date, int circular, Out out);
70
+ static void dump_obj_to_xml(VALUE obj, Options copts, Out out);
71
71
 
72
+ static void dump_first_obj(VALUE obj, Options copts, Out out);
72
73
  static void dump_obj(ID aid, VALUE obj, unsigned int depth, Out out);
73
- static void dump_gen_doc(VALUE obj, unsigned int depth, Out out);
74
+ static void dump_gen_doc(VALUE obj, unsigned int depth, Options copts, Out out);
74
75
  static void dump_gen_element(VALUE obj, unsigned int depth, Out out);
75
76
  static int dump_gen_attr(VALUE key, VALUE value, Out out);
76
77
  static int dump_gen_nodes(VALUE obj, unsigned int depth, Out out);
@@ -113,6 +114,30 @@ is_xml_friendly(const u_char *str, int len) {
113
114
  return 1;
114
115
  }
115
116
 
117
+ inline static Type
118
+ obj_class_code(VALUE obj) {
119
+ switch (rb_type(obj)) {
120
+ case RUBY_T_NIL: return NilClassCode;
121
+ case RUBY_T_ARRAY: return ArrayCode;
122
+ case RUBY_T_HASH: return HashCode;
123
+ case RUBY_T_TRUE: return TrueClassCode;
124
+ case RUBY_T_FALSE: return FalseClassCode;
125
+ case RUBY_T_FIXNUM: return FixnumCode;
126
+ case RUBY_T_FLOAT: return FloatCode;
127
+ case RUBY_T_STRING: return (is_xml_friendly((u_char*)StringValuePtr(obj), (int)RSTRING_LEN(obj))) ? StringCode : Base64Code;
128
+ case RUBY_T_SYMBOL: return SymbolCode;
129
+ case RUBY_T_DATA: return (rb_cTime == rb_obj_class(obj)) ? TimeCode : 0;
130
+ case RUBY_T_STRUCT: return (rb_cRange == rb_obj_class(obj)) ? RangeCode : StructCode;
131
+ case RUBY_T_OBJECT: return (ox_document_clas == rb_obj_class(obj) || ox_element_clas == rb_obj_class(obj)) ? RawCode : ObjectCode;
132
+ case RUBY_T_REGEXP: return RegexpCode;
133
+ case RUBY_T_BIGNUM: return BignumCode;
134
+ case RUBY_T_COMPLEX: return ComplexCode;
135
+ case RUBY_T_RATIONAL: return RationalCode;
136
+ case RUBY_T_CLASS: return ClassCode;
137
+ default: return 0;
138
+ }
139
+ }
140
+
116
141
  inline static void
117
142
  fill_indent(Out out, int cnt) {
118
143
  if (0 <= cnt) {
@@ -221,7 +246,9 @@ dump_start(Out out, Element e) {
221
246
  if (out->end - out->cur < (long)size) {
222
247
  grow(out, size);
223
248
  }
224
- fill_indent(out, e->indent);
249
+ if (out->buf < out->cur) {
250
+ fill_indent(out, e->indent);
251
+ }
225
252
  *out->cur++ = '<';
226
253
  *out->cur++ = e->type;
227
254
  if (0 < e->attr.len) {
@@ -361,6 +388,33 @@ dump_time_xsd(Out out, VALUE obj) {
361
388
  tzsign, tzhour, tzmin);
362
389
  }
363
390
 
391
+ static void
392
+ dump_first_obj(VALUE obj, Options copts, Out out) {
393
+ char buf[128];
394
+ int cnt;
395
+
396
+ if (Yes == copts->with_xml) {
397
+ if ('\0' == *copts->encoding) {
398
+ dump_value(out, "<?xml version=\"1.0\"?>", 21);
399
+ } else {
400
+ cnt = sprintf(buf, "<?xml version=\"1.0\" encoding=\"%s\"?>", copts->encoding);
401
+ dump_value(out, buf, cnt);
402
+ }
403
+ }
404
+ if (Yes == copts->with_instruct) {
405
+ cnt = sprintf(buf, "%s<?ox version=\"1.0\" mode=\"object\"%s%s?>",
406
+ (out->buf < out->cur) ? "\n" : "",
407
+ (Yes == copts->circular) ? " circular=\"yes\"" : ((No == copts->circular) ? " circular=\"no\"" : ""),
408
+ (Yes == copts->xsd_date) ? " xsd_date=\"yes\"" : ((No == copts->xsd_date) ? " xsd_date=\"no\"" : ""));
409
+ dump_value(out, buf, cnt);
410
+ }
411
+ if (Yes == copts->with_dtd) {
412
+ cnt = sprintf(buf, "%s<!DOCTYPE %c SYSTEM \"ox.dtd\">", (out->buf < out->cur) ? "\n" : "", obj_class_code(obj));
413
+ dump_value(out, buf, cnt);
414
+ }
415
+ dump_obj(0, obj, 0, out);
416
+ }
417
+
364
418
  static void
365
419
  dump_obj(ID aid, VALUE obj, unsigned int depth, Out out) {
366
420
  struct _Element e;
@@ -376,12 +430,7 @@ dump_obj(ID aid, VALUE obj, unsigned int depth, Out out) {
376
430
  }
377
431
  e.closed = 0;
378
432
  if (0 == depth) {
379
- if (0 <= out->indent) {
380
- e.indent = 0;
381
- } else {
382
- e.indent = -1;
383
- }
384
- dump_value(out, "<?xml version=\"1.0\"?>", 21);
433
+ e.indent = (0 <= out->indent) ? 0 : -1;
385
434
  } else if (0 > out->indent) {
386
435
  e.indent = -1;
387
436
  } else if (0 == out->indent) {
@@ -573,7 +622,7 @@ dump_obj(ID aid, VALUE obj, unsigned int depth, Out out) {
573
622
  if (ox_document_clas == clas) {
574
623
  e.type = RawCode;
575
624
  out->w_start(out, &e);
576
- dump_gen_doc(obj, depth + 1, out);
625
+ dump_gen_doc(obj, depth + 1, 0, out);
577
626
  out->w_end(out, &e);
578
627
  } else if (ox_element_clas == clas) {
579
628
  e.type = RawCode;
@@ -702,15 +751,24 @@ dump_hash(VALUE key, VALUE value, Out out) {
702
751
  }
703
752
 
704
753
  static void
705
- dump_gen_doc(VALUE obj, unsigned int depth, Out out) {
754
+ dump_gen_doc(VALUE obj, unsigned int depth, Options copts, Out out) {
706
755
  VALUE attrs = rb_attr_get(obj, attributes_id);
707
756
  VALUE nodes = rb_attr_get(obj, nodes_id);
708
757
 
709
- dump_value(out, "<?xml", 5);
710
- if (Qnil != attrs) {
711
- rb_hash_foreach(attrs, dump_gen_attr, (VALUE)out);
758
+ if (Yes == copts->with_xml) {
759
+ dump_value(out, "<?xml", 5);
760
+ if (Qnil != attrs) {
761
+ rb_hash_foreach(attrs, dump_gen_attr, (VALUE)out);
762
+ }
763
+ dump_value(out, "?>", 2);
764
+ }
765
+ if (Yes == copts->with_instruct) {
766
+ if (out->buf < out->cur) {
767
+ dump_value(out, "\n<?ox version=\"1.0\" mode=\"generic\"?>", 36);
768
+ } else {
769
+ dump_value(out, "<?ox version=\"1.0\" mode=\"generic\"?>", 35);
770
+ }
712
771
  }
713
- dump_value(out, "?>", 2);
714
772
  if (Qnil != nodes) {
715
773
  dump_gen_nodes(nodes, depth, out);
716
774
  }
@@ -848,47 +906,47 @@ dump_gen_val_node(VALUE obj, unsigned int depth,
848
906
  }
849
907
 
850
908
  static void
851
- dump_obj_to_xml(VALUE obj, int indent, int xsd_date, int circular, Out out) {
909
+ dump_obj_to_xml(VALUE obj, Options copts, Out out) {
852
910
  VALUE clas = rb_obj_class(obj);
853
911
 
854
- out->w_time = (xsd_date) ? dump_time_xsd : dump_time_thin;
912
+ out->w_time = (Yes == copts->xsd_date) ? dump_time_xsd : dump_time_thin;
855
913
  out->buf = (char*)malloc(65336);
856
914
  out->end = out->buf + 65336;
857
915
  out->cur = out->buf;
858
916
  out->circ_cache = 0;
859
917
  out->circ_cnt = 0;
860
- if (circular) {
918
+ if (Yes == copts->circular) {
861
919
  ox_cache8_new(&out->circ_cache);
862
920
  }
863
- out->indent = indent;
921
+ out->indent = copts->indent;
864
922
  if (ox_document_clas == clas) {
865
- dump_gen_doc(obj, -1, out);
923
+ dump_gen_doc(obj, -1, copts, out);
866
924
  } else if (ox_element_clas == clas) {
867
925
  dump_gen_element(obj, 0, out);
868
926
  } else {
869
927
  out->w_start = dump_start;
870
928
  out->w_end = dump_end;
871
- dump_obj(0, obj, 0, out);
929
+ dump_first_obj(obj, copts, out);
872
930
  }
873
931
  dump_value(out, "\n", 1);
874
932
  }
875
933
 
876
934
  char*
877
- write_obj_to_str(VALUE obj, int indent, int xsd_date, int circular) {
935
+ write_obj_to_str(VALUE obj, Options copts) {
878
936
  struct _Out out;
879
937
 
880
- dump_obj_to_xml(obj, indent, xsd_date, circular, &out);
938
+ dump_obj_to_xml(obj, copts, &out);
881
939
 
882
940
  return out.buf;
883
941
  }
884
942
 
885
943
  void
886
- write_obj_to_file(VALUE obj, const char *path, int indent, int xsd_date, int circular) {
944
+ write_obj_to_file(VALUE obj, const char *path, Options copts) {
887
945
  struct _Out out;
888
946
  size_t size;
889
947
  FILE *f;
890
948
 
891
- dump_obj_to_xml(obj, indent, xsd_date, circular, &out);
949
+ dump_obj_to_xml(obj, copts, &out);
892
950
  size = out.cur - out.buf;
893
951
  if (0 == (f = fopen(path, "w"))) {
894
952
  rb_raise(rb_eIOError, "%s\n", strerror(errno));
@@ -37,7 +37,8 @@
37
37
  #include "ruby.h"
38
38
  #include "ox.h"
39
39
 
40
- static void add_prolog(PInfo pi, const char *version, const char *encoding, const char *standalone);
40
+ static void instruct(PInfo pi, const char *target, Attr attrs);
41
+ static void nomode_instruct(PInfo pi, const char *target, Attr attrs);
41
42
  static void add_doctype(PInfo pi, const char *docType);
42
43
  static void add_comment(PInfo pi, const char *comment);
43
44
  static void add_cdata(PInfo pi, const char *cdata, size_t len);
@@ -45,8 +46,10 @@ static void add_text(PInfo pi, char *text, int closed);
45
46
  static void add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren);
46
47
  static void end_element(PInfo pi, const char *ename);
47
48
 
49
+ extern ParseCallbacks ox_obj_callbacks;
50
+
48
51
  struct _ParseCallbacks _ox_gen_callbacks = {
49
- add_prolog,
52
+ instruct, // instruct,
50
53
  add_doctype,
51
54
  add_comment,
52
55
  add_cdata,
@@ -69,33 +72,109 @@ struct _ParseCallbacks _ox_limited_callbacks = {
69
72
 
70
73
  ParseCallbacks ox_limited_callbacks = &_ox_limited_callbacks;
71
74
 
75
+ struct _ParseCallbacks _ox_nomode_callbacks = {
76
+ nomode_instruct,
77
+ add_doctype,
78
+ add_comment,
79
+ add_cdata,
80
+ add_text,
81
+ add_element,
82
+ end_element,
83
+ };
84
+
85
+ ParseCallbacks ox_nomode_callbacks = &_ox_nomode_callbacks;
86
+
72
87
  static void
73
- add_prolog(PInfo pi, const char *version, const char *encoding, const char *standalone) {
74
- VALUE doc;
75
- VALUE ah;
76
- VALUE nodes;
77
-
78
- if (0 != pi->h) { // top level object
79
- rb_raise(rb_eEncodingError, "Prolog must be the first element in an XML document.\n");
80
- }
81
- pi->h = pi->helpers;
82
- doc = rb_obj_alloc(ox_document_clas);
83
- ah = rb_hash_new();
84
- if (0 != version) {
85
- rb_hash_aset(ah, version_sym, rb_str_new2(version));
86
- }
87
- if (0 != encoding) {
88
- rb_hash_aset(ah, encoding_sym, rb_str_new2(encoding));
89
- pi->encoding = rb_enc_find(encoding);
88
+ instruct(PInfo pi, const char *target, Attr attrs) {
89
+ if (0 == strcmp("xml", target)) {
90
+ VALUE doc;
91
+ VALUE ah;
92
+ VALUE nodes;
93
+
94
+ if (0 != pi->h) { // top level object
95
+ rb_raise(rb_eEncodingError, "Prolog must be the first element in an XML document.\n");
96
+ }
97
+ pi->h = pi->helpers;
98
+ doc = rb_obj_alloc(ox_document_clas);
99
+ ah = rb_hash_new();
100
+ for (; 0 != attrs->name; attrs++) {
101
+ rb_hash_aset(ah, ID2SYM(rb_intern(attrs->name)), rb_str_new2(attrs->value));
102
+ if (0 == strcmp("encoding", attrs->name)) {
103
+ pi->encoding = rb_enc_find(attrs->value);
104
+ }
105
+ }
106
+ nodes = rb_ary_new();
107
+ rb_ivar_set(doc, attributes_id, ah);
108
+ rb_ivar_set(doc, nodes_id, nodes);
109
+ pi->h->obj = nodes;
110
+ pi->obj = doc;
111
+ } else if (0 == strcmp("ox", target)) {
112
+ for (; 0 != attrs->name; attrs++) {
113
+ if (0 == strcmp("version", attrs->name)) {
114
+ if (0 != strcmp("1.0", attrs->value)) {
115
+ rb_raise(rb_eEncodingError, "Only Ox XML Object version 1.0 supported, not %s.\n", attrs->value);
116
+ }
117
+ }
118
+ // ignore other instructions
119
+ }
120
+ } else {
121
+ if (TRACE <= pi->trace) {
122
+ printf("Processing instruction %s ignored.\n", target);
123
+ }
90
124
  }
91
- if (0 != standalone) {
92
- rb_hash_aset(ah, standalone_sym, rb_str_new2(standalone));
125
+ }
126
+
127
+ static void
128
+ nomode_instruct(PInfo pi, const char *target, Attr attrs) {
129
+ if (0 == strcmp("xml", target)) {
130
+ VALUE doc;
131
+ VALUE ah;
132
+ VALUE nodes;
133
+
134
+ if (0 != pi->h) { // top level object
135
+ rb_raise(rb_eEncodingError, "Prolog must be the first element in an XML document.\n");
136
+ }
137
+ pi->h = pi->helpers;
138
+ doc = rb_obj_alloc(ox_document_clas);
139
+ ah = rb_hash_new();
140
+ for (; 0 != attrs->name; attrs++) {
141
+ rb_hash_aset(ah, ID2SYM(rb_intern(attrs->name)), rb_str_new2(attrs->value));
142
+ if (0 == strcmp("encoding", attrs->name)) {
143
+ pi->encoding = rb_enc_find(attrs->value);
144
+ }
145
+ }
146
+ nodes = rb_ary_new();
147
+ rb_ivar_set(doc, attributes_id, ah);
148
+ rb_ivar_set(doc, nodes_id, nodes);
149
+ pi->h->obj = nodes;
150
+ pi->obj = doc;
151
+ } else if (0 == strcmp("ox", target)) {
152
+ for (; 0 != attrs->name; attrs++) {
153
+ if (0 == strcmp("version", attrs->name)) {
154
+ if (0 != strcmp("1.0", attrs->value)) {
155
+ rb_raise(rb_eEncodingError, "Only Ox XML Object version 1.0 supported, not %s.\n", attrs->value);
156
+ }
157
+ } else if (0 == strcmp("mode", attrs->name)) {
158
+ if (0 == strcmp("object", attrs->value)) {
159
+ pi->pcb = ox_obj_callbacks;
160
+ pi->obj = Qnil;
161
+ pi->h = 0;
162
+ } else if (0 == strcmp("generic", attrs->value)) {
163
+ pi->pcb = ox_gen_callbacks;
164
+ } else if (0 == strcmp("limited", attrs->value)) {
165
+ pi->pcb = ox_limited_callbacks;
166
+ pi->obj = Qnil;
167
+ pi->h = 0;
168
+ } else {
169
+ rb_raise(rb_eEncodingError, "%s is not a valid processing instruction mode.\n", attrs->value);
170
+ }
171
+ }
172
+ }
173
+ } else {
174
+ if (TRACE <= pi->trace) {
175
+ printf("Processing instruction %s ignored.\n", target);
176
+ }
93
177
  }
94
- nodes = rb_ary_new();
95
- rb_ivar_set(doc, attributes_id, ah);
96
- rb_ivar_set(doc, nodes_id, nodes);
97
- pi->h->obj = nodes;
98
- pi->obj = doc;
99
178
  }
100
179
 
101
180
  static void
@@ -40,7 +40,7 @@
40
40
  #include "base64.h"
41
41
  #include "ox.h"
42
42
 
43
- static void add_prolog(PInfo pi, const char *version, const char *encoding, const char *standalone);
43
+ static void instruct(PInfo pi, const char *target, Attr attrs);
44
44
  static void add_text(PInfo pi, char *text, int closed);
45
45
  static void add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren);
46
46
  static void end_element(PInfo pi, const char *ename);
@@ -65,7 +65,7 @@ static void fill_indent(PInfo pi, char *buf, size_t size);
65
65
 
66
66
 
67
67
  struct _ParseCallbacks _ox_obj_callbacks = {
68
- add_prolog,
68
+ instruct, // instruct,
69
69
  0, // add_doctype,
70
70
  0, // add_comment,
71
71
  0, // add_cdata,
@@ -342,9 +342,13 @@ parse_regexp(const char *text) {
342
342
  }
343
343
 
344
344
  static void
345
- add_prolog(PInfo pi, const char *version, const char *encoding, const char *standalone) {
346
- if (0 != encoding) {
347
- pi->encoding = rb_enc_find(encoding);
345
+ instruct(PInfo pi, const char *target, Attr attrs) {
346
+ if (0 == strcmp("xml", target)) {
347
+ for (; 0 != attrs->name; attrs++) {
348
+ if (0 == strcmp("encoding", attrs->name)) {
349
+ pi->encoding = rb_enc_find(attrs->value);
350
+ }
351
+ }
348
352
  }
349
353
  }
350
354
 
@@ -36,6 +36,11 @@
36
36
  #include "ruby.h"
37
37
  #include "ox.h"
38
38
 
39
+ typedef struct _YesNoOpt {
40
+ VALUE sym;
41
+ char *attr;
42
+ } *YesNoOpt;
43
+
39
44
  void Init_ox();
40
45
 
41
46
  VALUE Ox = Qnil;
@@ -76,6 +81,10 @@ VALUE tolerant_sym;
76
81
  VALUE effort_sym;
77
82
  VALUE auto_define_sym;
78
83
  VALUE trace_sym;
84
+ VALUE strict_sym;
85
+ VALUE with_dtd_sym;
86
+ VALUE with_instruct_sym;
87
+ VALUE with_xml_sym;
79
88
  VALUE empty_string;
80
89
  VALUE zero_fixnum;
81
90
 
@@ -92,11 +101,153 @@ Cache symbol_cache = 0;
92
101
  Cache class_cache = 0;
93
102
  Cache attr_cache = 0;
94
103
 
104
+ static struct _Options default_options = {
105
+ { '\0' }, // encoding
106
+ 2, // indent
107
+ 0, // trace
108
+ No, // with_dtd
109
+ No, // with_xml
110
+ No, // with_instruct
111
+ No, // circular
112
+ No, // xsd_date
113
+ NoMode, // mode
114
+ StrictEffort, // effort
115
+ };
116
+
95
117
  extern ParseCallbacks ox_obj_callbacks;
96
118
  extern ParseCallbacks ox_gen_callbacks;
97
119
  extern ParseCallbacks ox_limited_callbacks;
120
+ extern ParseCallbacks ox_nomode_callbacks;
98
121
 
99
- static void parse_dump_options(VALUE options, int *indent, int *xsd_date, int *circular);
122
+ static void parse_dump_options(VALUE ropts, Options copts);
123
+
124
+ /* call-seq: default_options() => Hash
125
+ *
126
+ * Returns the default load and dump options as a Hash. The options are
127
+ * - indent: [Fixnum] number of spaces to indent each element in an XML document
128
+ * - trace: [Fixnum] trace level where 0 is silent
129
+ * - encoding: [String] character encoding for the XML file
130
+ * - with_dtd: [true|false|nil] include DTD in the dump
131
+ * - with_instruct: [true|false|nil] include instructions in the dump
132
+ * - with_xml: [true|false|nil] include XML prolog in the dump
133
+ * - circular: [true|false|nil] support circular references while dumping
134
+ * - xsd_date: [true|false|nil] use XSD date format instead of decimal format
135
+ * - mode: [:object|:generic|:limited|nil] load method to use for XML
136
+ * - effort: [:strict|:tolerant|:auto_define] set the tolerance level for loading
137
+ */
138
+ static VALUE
139
+ get_def_opts(VALUE self) {
140
+ VALUE opts = rb_hash_new();
141
+ int elen = (int)strlen(default_options.encoding);
142
+
143
+ rb_hash_aset(opts, encoding_sym, (0 == elen) ? Qnil : rb_str_new(default_options.encoding, elen));
144
+ rb_hash_aset(opts, indent_sym, INT2FIX(default_options.indent));
145
+ rb_hash_aset(opts, trace_sym, INT2FIX(default_options.trace));
146
+ rb_hash_aset(opts, with_dtd_sym, (Yes == default_options.with_dtd) ? Qtrue : ((No == default_options.with_dtd) ? Qfalse : Qnil));
147
+ rb_hash_aset(opts, with_xml_sym, (Yes == default_options.with_xml) ? Qtrue : ((No == default_options.with_xml) ? Qfalse : Qnil));
148
+ rb_hash_aset(opts, with_instruct_sym, (Yes == default_options.with_instruct) ? Qtrue : ((No == default_options.with_instruct) ? Qfalse : Qnil));
149
+ rb_hash_aset(opts, circular_sym, (Yes == default_options.circular) ? Qtrue : ((No == default_options.circular) ? Qfalse : Qnil));
150
+ rb_hash_aset(opts, xsd_date_sym, (Yes == default_options.xsd_date) ? Qtrue : ((No == default_options.xsd_date) ? Qfalse : Qnil));
151
+ switch (default_options.mode) {
152
+ case ObjMode: rb_hash_aset(opts, mode_sym, object_sym); break;
153
+ case GenMode: rb_hash_aset(opts, mode_sym, generic_sym); break;
154
+ case LimMode: rb_hash_aset(opts, mode_sym, limited_sym); break;
155
+ case NoMode:
156
+ default: rb_hash_aset(opts, mode_sym, Qnil); break;
157
+ }
158
+ switch (default_options.effort) {
159
+ case StrictEffort: rb_hash_aset(opts, effort_sym, strict_sym); break;
160
+ case TolerantEffort: rb_hash_aset(opts, effort_sym, tolerant_sym); break;
161
+ case AutoEffort: rb_hash_aset(opts, effort_sym, auto_define_sym); break;
162
+ case NoEffort:
163
+ default: rb_hash_aset(opts, effort_sym, Qnil); break;
164
+ }
165
+ return opts;
166
+ }
167
+
168
+ /* call-seq: default_options=(Hash)
169
+ *
170
+ * Sets the default options for load and dump. Options are:
171
+ * - indent: [Fixnum] number of spaces to indent each element in an XML document
172
+ * - trace: [Fixnum] trace level where 0 is silent
173
+ * - encoding: [String] character encoding for the XML file
174
+ * - with_dtd: [true|false|nil] include DTD in the dump
175
+ * - with_instruct: [true|false|nil] include instructions in the dump
176
+ * - with_xml: [true|false|nil] include XML prolog in the dump
177
+ * - circular: [true|false|nil] support circular references while dumping
178
+ * - xsd_date: [true|false|nil] use XSD date format instead of decimal format
179
+ * - mode: [:object|:generic|:limited|nil] load method to use for XML
180
+ * - effort: [:strict|:tolerant|:auto_define] set the tolerance level for loading
181
+ */
182
+ static VALUE
183
+ set_def_opts(VALUE self, VALUE opts) {
184
+ struct _YesNoOpt ynos[] = {
185
+ { with_xml_sym, &default_options.with_xml },
186
+ { with_dtd_sym, &default_options.with_dtd },
187
+ { with_instruct_sym, &default_options.with_instruct },
188
+ { xsd_date_sym, &default_options.xsd_date },
189
+ { circular_sym, &default_options.circular },
190
+ { Qnil, 0 }
191
+ };
192
+ YesNoOpt o;
193
+ VALUE v;
194
+
195
+ Check_Type(opts, T_HASH);
196
+
197
+ v = rb_hash_aref(opts, encoding_sym);
198
+ if (Qnil == v) {
199
+ *default_options.encoding = '\0';
200
+ } else {
201
+ Check_Type(v, T_STRING);
202
+ strncpy(default_options.encoding, StringValuePtr(v), sizeof(default_options.encoding) - 1);
203
+ }
204
+ v = rb_hash_aref(opts, indent_sym);
205
+ Check_Type(v, T_FIXNUM);
206
+ default_options.indent = FIX2INT(v);
207
+
208
+ v = rb_hash_aref(opts, trace_sym);
209
+ Check_Type(v, T_FIXNUM);
210
+ default_options.trace = FIX2INT(v);
211
+
212
+ v = rb_hash_aref(opts, mode_sym);
213
+ if (Qnil == v) {
214
+ default_options.mode = NoMode;
215
+ } else if (object_sym == v) {
216
+ default_options.mode = ObjMode;
217
+ } else if (generic_sym == v) {
218
+ default_options.mode = GenMode;
219
+ } else if (limited_sym == v) {
220
+ default_options.mode = LimMode;
221
+ } else {
222
+ rb_raise(rb_eArgError, ":mode must be :object, :generic, :limited, or nil.\n");
223
+ }
224
+
225
+ v = rb_hash_aref(opts, effort_sym);
226
+ if (Qnil == v) {
227
+ default_options.effort = NoEffort;
228
+ } else if (strict_sym == v) {
229
+ default_options.effort = StrictEffort;
230
+ } else if (tolerant_sym == v) {
231
+ default_options.effort = TolerantEffort;
232
+ } else if (auto_define_sym == v) {
233
+ default_options.effort = AutoEffort;
234
+ } else {
235
+ rb_raise(rb_eArgError, ":effort must be :strict, :tolerant, :auto_define, or nil.\n");
236
+ }
237
+ for (o = ynos; 0 != o->attr; o++) {
238
+ v = rb_hash_lookup(opts, o->sym);
239
+ if (Qnil == v) {
240
+ *o->attr = NotSet;
241
+ } else if (Qtrue == v) {
242
+ *o->attr = Yes;
243
+ } else if (Qfalse == v) {
244
+ *o->attr = No;
245
+ } else {
246
+ rb_raise(rb_eArgError, "%s must be true, false, or nil.\n", StringValuePtr(o->sym));
247
+ }
248
+ }
249
+ return Qnil;
250
+ }
100
251
 
101
252
  /* call-seq: parse_obj(xml) => Object
102
253
  *
@@ -139,68 +290,59 @@ to_gen(VALUE self, VALUE ruby_xml) {
139
290
  return obj;
140
291
  }
141
292
 
142
- typedef enum {
143
- AutoMode = 0, // not supported
144
- ObjMode = 1,
145
- GenMode = 2,
146
- LimMode = 3,
147
- } LoadMode;
148
-
149
293
  static VALUE
150
294
  load(char *xml, int argc, VALUE *argv, VALUE self) {
151
- VALUE obj;
152
- int mode = AutoMode;
153
- int effort = StrictEffort;
154
- int trace = 0;
295
+ VALUE obj;
296
+ struct _Options options = default_options;
155
297
 
156
298
  if (1 == argc && rb_cHash == rb_obj_class(*argv)) {
157
299
  VALUE h = *argv;
158
300
  VALUE v;
159
301
 
160
302
  if (Qnil != (v = rb_hash_lookup(h, mode_sym))) {
161
- if (auto_sym == v) {
162
- mode = AutoMode;
163
- } else if (object_sym == v) {
164
- mode = ObjMode;
303
+ if (object_sym == v) {
304
+ options.mode = ObjMode;
165
305
  } else if (optimized_sym == v) {
166
- mode = ObjMode;
306
+ options.mode = ObjMode;
167
307
  } else if (generic_sym == v) {
168
- mode = GenMode;
308
+ options.mode = GenMode;
169
309
  } else if (limited_sym == v) {
170
- mode = LimMode;
310
+ options.mode = LimMode;
171
311
  } else {
172
312
  rb_raise(rb_eArgError, ":mode must be :generic, :object, or :limited.\n");
173
313
  }
174
314
  }
175
315
  if (Qnil != (v = rb_hash_lookup(h, effort_sym))) {
176
316
  if (auto_define_sym == v) {
177
- effort = AutoEffort;
317
+ options.effort = AutoEffort;
178
318
  } else if (tolerant_sym == v) {
179
- effort = TolerantEffort;
319
+ options.effort = TolerantEffort;
180
320
  } else if (strict_sym == v) {
181
- effort = StrictEffort;
321
+ options.effort = StrictEffort;
182
322
  } else {
183
323
  rb_raise(rb_eArgError, ":effort must be :strict, :tolerant, or :auto_define.\n");
184
324
  }
185
325
  }
186
326
  if (Qnil != (v = rb_hash_lookup(h, trace_sym))) {
187
327
  Check_Type(v, T_FIXNUM);
188
- trace = FIX2INT(v);
328
+ options.trace = FIX2INT(v);
189
329
  }
190
330
  }
191
- switch (mode) {
331
+ switch (options.mode) {
192
332
  case ObjMode:
193
- obj = parse(xml, ox_obj_callbacks, 0, trace, effort);
333
+ obj = parse(xml, ox_obj_callbacks, 0, options.trace, options.effort);
194
334
  break;
195
335
  case GenMode:
196
- obj = parse(xml, ox_gen_callbacks, 0, trace, StrictEffort);
336
+ obj = parse(xml, ox_gen_callbacks, 0, options.trace, StrictEffort);
197
337
  break;
198
338
  case LimMode:
199
- obj = parse(xml, ox_limited_callbacks, 0, trace, effort);
339
+ obj = parse(xml, ox_limited_callbacks, 0, options.trace, StrictEffort);
340
+ break;
341
+ case NoMode:
342
+ obj = parse(xml, ox_nomode_callbacks, 0, options.trace, options.effort);
200
343
  break;
201
- case AutoMode:
202
344
  default:
203
- obj = parse(xml, ox_gen_callbacks, 0, trace, StrictEffort);
345
+ obj = parse(xml, ox_gen_callbacks, 0, options.trace, StrictEffort);
204
346
  break;
205
347
  }
206
348
  free(xml);
@@ -283,36 +425,49 @@ load_file(int argc, VALUE *argv, VALUE self) {
283
425
  }
284
426
 
285
427
  static void
286
- parse_dump_options(VALUE options, int *indent, int *xsd_date, int *circular) {
287
- if (rb_cHash == rb_obj_class(options)) {
428
+ parse_dump_options(VALUE ropts, Options copts) {
429
+ struct _YesNoOpt ynos[] = {
430
+ { with_xml_sym, &copts->with_xml },
431
+ { with_dtd_sym, &copts->with_dtd },
432
+ { with_instruct_sym, &copts->with_instruct },
433
+ { xsd_date_sym, &copts->xsd_date },
434
+ { circular_sym, &copts->circular },
435
+ { Qnil, 0 }
436
+ };
437
+ YesNoOpt o;
438
+
439
+ if (rb_cHash == rb_obj_class(ropts)) {
288
440
  VALUE v;
289
441
 
290
- if (Qnil != (v = rb_hash_lookup(options, indent_sym))) {
442
+ if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) {
291
443
  if (rb_cFixnum != rb_obj_class(v)) {
292
444
  rb_raise(rb_eArgError, ":indent must be a Fixnum.\n");
293
445
  }
294
- *indent = NUM2INT(v);
446
+ copts->indent = NUM2INT(v);
295
447
  }
296
- if (Qnil != (v = rb_hash_lookup(options, xsd_date_sym))) {
297
- VALUE c = rb_obj_class(v);
298
-
299
- if (rb_cTrueClass == c) {
300
- *xsd_date = 1;
301
- } else if (rb_cFalseClass == c) {
302
- *xsd_date = 0;
303
- } else {
304
- rb_raise(rb_eArgError, ":xsd_date must be true or false.\n");
448
+ if (Qnil != (v = rb_hash_lookup(ropts, trace_sym))) {
449
+ if (rb_cFixnum != rb_obj_class(v)) {
450
+ rb_raise(rb_eArgError, ":trace must be a Fixnum.\n");
305
451
  }
452
+ copts->trace = NUM2INT(v);
306
453
  }
307
- if (Qnil != (v = rb_hash_lookup(options, circular_sym))) {
308
- VALUE c = rb_obj_class(v);
309
-
310
- if (rb_cTrueClass == c) {
311
- *circular = 1;
312
- } else if (rb_cFalseClass == c) {
313
- *circular = 0;
314
- } else {
315
- rb_raise(rb_eArgError, ":circular must be true or false.\n");
454
+ if (Qnil != (v = rb_hash_lookup(ropts, encoding_sym))) {
455
+ if (rb_cString != rb_obj_class(v)) {
456
+ rb_raise(rb_eArgError, ":encoding must be a String.\n");
457
+ }
458
+ strncpy(copts->encoding, StringValuePtr(v), sizeof(copts->encoding) - 1);
459
+ }
460
+ for (o = ynos; 0 != o->attr; o++) {
461
+ if (Qnil != (v = rb_hash_lookup(ropts, o->sym))) {
462
+ VALUE c = rb_obj_class(v);
463
+
464
+ if (rb_cTrueClass == c) {
465
+ *o->attr = Yes;
466
+ } else if (rb_cFalseClass == c) {
467
+ *o->attr = No;
468
+ } else {
469
+ rb_raise(rb_eArgError, "%s must be true or false.\n", StringValuePtr(o->sym));
470
+ }
316
471
  }
317
472
  }
318
473
  }
@@ -329,16 +484,14 @@ parse_dump_options(VALUE options, int *indent, int *xsd_date, int *circular) {
329
484
  */
330
485
  static VALUE
331
486
  dump(int argc, VALUE *argv, VALUE self) {
332
- char *xml;
333
- int indent = 2;
334
- int xsd_date = 0;
335
- int circular = 0;
336
- VALUE rstr;
487
+ char *xml;
488
+ struct _Options copts;
489
+ VALUE rstr;
337
490
 
338
491
  if (2 == argc) {
339
- parse_dump_options(argv[1], &indent, &xsd_date, &circular);
492
+ parse_dump_options(argv[1], &copts);
340
493
  }
341
- if (0 == (xml = write_obj_to_str(*argv, indent, xsd_date, circular))) {
494
+ if (0 == (xml = write_obj_to_str(*argv, &copts))) {
342
495
  rb_raise(rb_eNoMemError, "Not enough memory.\n");
343
496
  }
344
497
  rstr = rb_str_new2(xml);
@@ -359,15 +512,13 @@ dump(int argc, VALUE *argv, VALUE self) {
359
512
  */
360
513
  static VALUE
361
514
  to_file(int argc, VALUE *argv, VALUE self) {
362
- int indent = 2;
363
- int xsd_date = 0;
364
- int circular = 0;
515
+ struct _Options copts;
365
516
 
366
517
  if (3 == argc) {
367
- parse_dump_options(argv[2], &indent, &xsd_date, &circular);
518
+ parse_dump_options(argv[2], &copts);
368
519
  }
369
520
  Check_Type(*argv, T_STRING);
370
- write_obj_to_file(argv[1], StringValuePtr(*argv), indent, xsd_date, circular);
521
+ write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
371
522
 
372
523
  return Qnil;
373
524
  }
@@ -389,7 +540,14 @@ cache8_test(VALUE self) {
389
540
  }
390
541
 
391
542
  void Init_ox() {
543
+ VALUE keep = Qnil;
544
+
392
545
  Ox = rb_define_module("Ox");
546
+ keep = rb_cv_get(Ox, "@@keep"); // needed to stop GC from deleting and reusing VALUEs
547
+
548
+ rb_define_module_function(Ox, "default_options", get_def_opts, 0);
549
+ rb_define_module_function(Ox, "default_options=", set_def_opts, 1);
550
+
393
551
  rb_define_module_function(Ox, "parse_obj", to_obj, 1);
394
552
  rb_define_module_function(Ox, "parse", to_gen, 1);
395
553
  rb_define_module_function(Ox, "load", load_str, -1);
@@ -422,26 +580,31 @@ void Init_ox() {
422
580
  time_class = rb_const_get(rb_cObject, rb_intern("Time"));
423
581
  struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
424
582
 
425
- version_sym = ID2SYM(rb_intern("version"));
426
- standalone_sym = ID2SYM(rb_intern("standalone"));
427
- encoding_sym = ID2SYM(rb_intern("encoding"));
428
- indent_sym = ID2SYM(rb_intern("indent"));
429
- xsd_date_sym = ID2SYM(rb_intern("xsd_date"));
430
- opt_format_sym = ID2SYM(rb_intern("opt_format"));
431
- mode_sym = ID2SYM(rb_intern("mode"));
432
- auto_sym = ID2SYM(rb_intern("auto"));
433
- optimized_sym = ID2SYM(rb_intern("optimized"));
434
- object_sym = ID2SYM(rb_intern("object"));
435
- circular_sym = ID2SYM(rb_intern("circular"));
436
- generic_sym = ID2SYM(rb_intern("generic"));
437
- limited_sym = ID2SYM(rb_intern("limited"));
438
- trace_sym = ID2SYM(rb_intern("trace"));
439
- effort_sym = ID2SYM(rb_intern("effort"));
440
- strict_sym = ID2SYM(rb_intern("strict"));
441
- tolerant_sym = ID2SYM(rb_intern("tolerant"));
442
- auto_define_sym = ID2SYM(rb_intern("auto_define"));
443
- empty_string = rb_str_new2("");
444
- zero_fixnum = INT2NUM(0);
583
+ version_sym = ID2SYM(rb_intern("version")); rb_ary_push(keep, version_sym);
584
+ standalone_sym = ID2SYM(rb_intern("standalone")); rb_ary_push(keep, standalone_sym);
585
+ encoding_sym = ID2SYM(rb_intern("encoding")); rb_ary_push(keep, encoding_sym);
586
+ indent_sym = ID2SYM(rb_intern("indent")); rb_ary_push(keep, indent_sym);
587
+ xsd_date_sym = ID2SYM(rb_intern("xsd_date")); rb_ary_push(keep, xsd_date_sym);
588
+ opt_format_sym = ID2SYM(rb_intern("opt_format")); rb_ary_push(keep, opt_format_sym);
589
+ mode_sym = ID2SYM(rb_intern("mode")); rb_ary_push(keep, mode_sym);
590
+ auto_sym = ID2SYM(rb_intern("auto")); rb_ary_push(keep, auto_sym);
591
+ optimized_sym = ID2SYM(rb_intern("optimized")); rb_ary_push(keep, optimized_sym);
592
+ object_sym = ID2SYM(rb_intern("object")); rb_ary_push(keep, object_sym);
593
+ circular_sym = ID2SYM(rb_intern("circular")); rb_ary_push(keep, circular_sym);
594
+ generic_sym = ID2SYM(rb_intern("generic")); rb_ary_push(keep, generic_sym);
595
+ limited_sym = ID2SYM(rb_intern("limited")); rb_ary_push(keep, limited_sym);
596
+ trace_sym = ID2SYM(rb_intern("trace")); rb_ary_push(keep, trace_sym);
597
+ effort_sym = ID2SYM(rb_intern("effort")); rb_ary_push(keep, effort_sym);
598
+ strict_sym = ID2SYM(rb_intern("strict")); rb_ary_push(keep, strict_sym);
599
+ tolerant_sym = ID2SYM(rb_intern("tolerant")); rb_ary_push(keep, tolerant_sym);
600
+ auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_ary_push(keep, auto_define_sym);
601
+ with_dtd_sym = ID2SYM(rb_intern("with_dtd")); rb_ary_push(keep, with_dtd_sym);
602
+ with_instruct_sym = ID2SYM(rb_intern("with_instructions")); rb_ary_push(keep, with_instruct_sym);
603
+ with_xml_sym = ID2SYM(rb_intern("with_xml")); rb_ary_push(keep, with_xml_sym);
604
+
605
+ empty_string = rb_str_new2(""); rb_ary_push(keep, empty_string);
606
+ zero_fixnum = INT2NUM(0); rb_ary_push(keep, zero_fixnum);
607
+
445
608
 
446
609
  //rb_require("node"); // generic xml node classes
447
610
  ox_document_clas = rb_const_get(Ox, rb_intern("Document"));