ox 2.14.28 → 2.14.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/ox/ox.c CHANGED
@@ -11,6 +11,13 @@
11
11
  #include <stdio.h>
12
12
  #include <stdlib.h>
13
13
  #include <string.h>
14
+ #include <sys/stat.h>
15
+ #include <sys/types.h>
16
+
17
+ // Not defined by every C library, and it is only these three bits.
18
+ #ifndef S_ISREG
19
+ #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
20
+ #endif
14
21
 
15
22
  #include "intern.h"
16
23
  #include "ruby.h"
@@ -73,6 +80,7 @@ ID ox_start_element_id;
73
80
  ID ox_string_id;
74
81
  ID ox_text_id;
75
82
  ID ox_to_c_id;
83
+ ID ox_utc_offset_id;
76
84
  ID ox_value_id;
77
85
 
78
86
  VALUE ox_encoding_sym;
@@ -168,7 +176,7 @@ struct _options ox_default_options = {
168
176
  SpcSkip, // skip
169
177
  No, // smart
170
178
  true, // convert_special
171
- No, // allow_invalid
179
+ NotSet, // allow_invalid
172
180
  false, // no_empty
173
181
  false, // with_cdata
174
182
  {'\0'}, // inv_repl
@@ -282,8 +290,11 @@ static VALUE hints_to_overlay(Hints hints) {
282
290
  * - _:skip_ [:skip_none|:skip_return|:skip_white|:skip_off] determines how to handle white space in text
283
291
  * - _:smart_ [true|false|nil] flag indicating the SAX parser uses hints if available (use with html)
284
292
  * - _:convert_special_ [true|false|nil] flag indicating special characters like &lt; are converted with the SAX parser
285
- * - _:invalid_replace_ [nil|String] replacement string for invalid XML characters on dump. nil indicates include anyway
286
- * as hex. A string, limited to 10 characters will replace the invalid character with the replace.
293
+ * - _:invalid_replace_ [nil|false|String] what to do with a character XML can not hold on dump. false, the default,
294
+ * raises an Ox::SyntaxError. nil writes it as a hex character reference, other than a NUL which is dropped since
295
+ * &#x0000; can not be read back. A string, limited to 10 characters, replaces it and the empty string drops it. This
296
+ * covers text and attribute values. A comment, CDATA section, DOCTYPE or processing instruction expands no character
297
+ * reference, so a character XML can not hold always raises there.
287
298
  * - _:no_empty_ [true|false|nil] flag indicating there should be no empty elements in a dump
288
299
  * - _:with_cdata_ [true|false] includes cdata in hash_load results
289
300
  * - _:strip_namespace_ [String|true|false] false or "" results in no namespace stripping. A string of "*" or true will
@@ -359,12 +370,14 @@ static VALUE get_def_opts(VALUE self) {
359
370
  case SpcSkip: rb_hash_aset(opts, skip_sym, skip_white_sym); break;
360
371
  default: rb_hash_aset(opts, skip_sym, Qnil); break;
361
372
  }
362
- if (Yes == ox_default_options.allow_invalid) {
363
- rb_hash_aset(opts, invalid_replace_sym, Qnil);
364
- } else {
373
+ switch (ox_default_options.allow_invalid) {
374
+ case Yes: rb_hash_aset(opts, invalid_replace_sym, Qnil); break;
375
+ case No:
365
376
  rb_hash_aset(opts,
366
377
  invalid_replace_sym,
367
378
  rb_str_new(ox_default_options.inv_repl + 1, (int)*ox_default_options.inv_repl));
379
+ break;
380
+ default: rb_hash_aset(opts, invalid_replace_sym, Qfalse); break;
368
381
  }
369
382
  if ('\0' == *ox_default_options.strip_ns) {
370
383
  rb_hash_aset(opts, strip_namespace_sym, Qfalse);
@@ -443,8 +456,11 @@ static VALUE sax_html_overlay(VALUE self) {
443
456
  * - _:attr_key_mod_ [Proc|nil] converts attribute keys on parse if not nil
444
457
  * - _:skip_ [:skip_none|:skip_return|:skip_white|:skip_off] determines how to handle white space in text
445
458
  * - _:smart_ [true|false|nil] flag indicating the SAX parser uses hints if available (use with html)
446
- * - _:invalid_replace_ [nil|String] replacement string for invalid XML characters on dump. nil indicates include
447
- * anyway as hex. A string, limited to 10 characters will replace the invalid character with the replace.
459
+ * - _:invalid_replace_ [nil|false|String] what to do with a character XML can not hold on dump. false, the default,
460
+ * raises an Ox::SyntaxError. nil writes it as a hex character reference, other than a NUL which is dropped since
461
+ * &#x0000; can not be read back. A string, limited to 10 characters, replaces it and the empty string drops it. This
462
+ * covers text and attribute values. A comment, CDATA section, DOCTYPE or processing instruction expands no character
463
+ * reference, so a character XML can not hold always raises there.
448
464
  * - _:strip_namespace_ [nil|String|true|false] "" or false result in no namespace stripping. A string of "*" or true
449
465
  * will strip all namespaces. Any other non-empty string indicates that matching namespaces will be stripped.
450
466
  * - _:with_cdata_ [true|false] includes cdata in hash_load results
@@ -474,7 +490,10 @@ static VALUE set_def_opts(VALUE self, VALUE opts) {
474
490
 
475
491
  v = rb_hash_aref(opts, ox_encoding_sym);
476
492
  if (Qnil == v) {
493
+ // Ox.parse_obj and Ox.parse read rb_enc and never re-derive it from the
494
+ // name, so clearing only the name leaves them on the old encoding.
477
495
  *ox_default_options.encoding = '\0';
496
+ ox_default_options.rb_enc = 0;
478
497
  } else {
479
498
  Check_Type(v, T_STRING);
480
499
  strncpy(ox_default_options.encoding, StringValuePtr(v), sizeof(ox_default_options.encoding) - 1);
@@ -483,8 +502,19 @@ static VALUE set_def_opts(VALUE self, VALUE opts) {
483
502
 
484
503
  v = rb_hash_aref(opts, ox_indent_sym);
485
504
  if (Qnil != v) {
505
+ int indent;
506
+
486
507
  Check_Type(v, T_FIXNUM);
487
- ox_default_options.indent = FIX2INT(v);
508
+ indent = FIX2INT(v);
509
+ // Bound the indent so that depth * indent (depth up to MAX_DEPTH) cannot
510
+ // overflow the int arithmetic in dump.c and smash the output buffer.
511
+ // Any negative value already means "tight, no newlines"; normalize it.
512
+ if (indent < 0) {
513
+ indent = -1;
514
+ } else if (OX_MAX_INDENT < indent) {
515
+ rb_raise(ox_parse_error_class, ":indent can be no larger than %d.\n", OX_MAX_INDENT);
516
+ }
517
+ ox_default_options.indent = indent;
488
518
  }
489
519
 
490
520
  v = rb_hash_aref(opts, trace_sym);
@@ -560,9 +590,19 @@ static VALUE set_def_opts(VALUE self, VALUE opts) {
560
590
  rb_raise(ox_parse_error_class, ":no_empty must be true or false.\n");
561
591
  }
562
592
 
563
- v = rb_hash_aref(opts, invalid_replace_sym);
593
+ v = rb_hash_lookup(opts, invalid_replace_sym);
564
594
  if (Qnil == v) {
565
- ox_default_options.allow_invalid = Yes;
595
+ // A key that is not there asks for the default, an explicit nil asks for
596
+ // the character to be written out, so the two cannot share a branch.
597
+ if (Qtrue == rb_funcall(opts, has_key_id, 1, invalid_replace_sym)) {
598
+ ox_default_options.allow_invalid = Yes;
599
+ } else {
600
+ ox_default_options.allow_invalid = NotSet;
601
+ *ox_default_options.inv_repl = '\0';
602
+ }
603
+ } else if (Qfalse == v) {
604
+ ox_default_options.allow_invalid = NotSet;
605
+ *ox_default_options.inv_repl = '\0';
566
606
  } else {
567
607
  long slen;
568
608
 
@@ -665,31 +705,63 @@ static VALUE set_def_opts(VALUE self, VALUE opts) {
665
705
  * - +xml+ [String] XML String in optimized Object format.
666
706
  * *return* [Object] deserialized Object.
667
707
  */
708
+ // Arguments for an object-mode parse that runs with GC disabled.
709
+ struct _objParse {
710
+ char *xml;
711
+ size_t len;
712
+ Options options;
713
+ Err err;
714
+ };
715
+
716
+ static VALUE obj_parse_body(VALUE argsv) {
717
+ struct _objParse *args = (struct _objParse *)argsv;
718
+
719
+ return ox_parse(args->xml, args->len, ox_obj_callbacks, 0, args->options, args->err);
720
+ }
721
+
722
+ // rb_gc_disable() returns Qtrue when GC was already disabled. Only re-enable
723
+ // when this call is the one that disabled it. Runs even if the parse raised.
724
+ static VALUE obj_parse_reenable(VALUE was_disabled) {
725
+ if (Qtrue != was_disabled) {
726
+ rb_gc_enable();
727
+ }
728
+ return Qnil;
729
+ }
730
+
668
731
  static VALUE to_obj(VALUE self, VALUE ruby_xml) {
669
- char *xml, *x;
670
- size_t len;
671
- VALUE obj;
672
- struct _options options = ox_default_options;
673
- struct _err err;
732
+ char *xml, *x, *str;
733
+ size_t len;
734
+ VALUE obj;
735
+ VALUE was_disabled;
736
+ struct _options options = ox_default_options;
737
+ struct _err err;
738
+ struct _objParse args;
674
739
 
675
740
  err_init(&err);
676
741
  Check_Type(ruby_xml, T_STRING);
677
742
  /* the xml string gets modified so make a copy of it */
743
+ str = StringValuePtr(ruby_xml);
678
744
  len = RSTRING_LEN(ruby_xml) + 1;
679
- x = defuse_bom(StringValuePtr(ruby_xml), &options);
745
+ x = defuse_bom(str, &options);
746
+ // Drop the BOM from len as well, otherwise the copy below reads past the
747
+ // end of the Ruby string and xml is left without a terminating '\0'.
748
+ len -= (size_t)(x - str);
680
749
  if (SMALL_XML < len) {
681
750
  xml = ALLOC_N(char, len);
682
751
  } else {
683
752
  xml = ALLOCA_N(char, len);
684
753
  }
685
754
  memcpy(xml, x, len);
686
- rb_gc_disable();
687
- obj = ox_parse(xml, len - 1, ox_obj_callbacks, 0, &options, &err);
755
+ args.xml = xml;
756
+ args.len = len - 1;
757
+ args.options = &options;
758
+ args.err = &err;
759
+ was_disabled = rb_gc_disable();
760
+ obj = rb_ensure(obj_parse_body, (VALUE)&args, obj_parse_reenable, was_disabled);
688
761
  if (SMALL_XML < len) {
689
762
  xfree(xml);
690
763
  }
691
764
  RB_GC_GUARD(obj);
692
- rb_gc_enable();
693
765
  if (err_has(&err)) {
694
766
  ox_err_raise(&err);
695
767
  }
@@ -705,7 +777,7 @@ static VALUE to_obj(VALUE self, VALUE ruby_xml) {
705
777
  * _raise_ [Exception] if the XML is malformed.
706
778
  */
707
779
  static VALUE to_gen(VALUE self, VALUE ruby_xml) {
708
- char *xml, *x;
780
+ char *xml, *x, *str;
709
781
  size_t len;
710
782
  VALUE obj;
711
783
  struct _options options = ox_default_options;
@@ -714,8 +786,12 @@ static VALUE to_gen(VALUE self, VALUE ruby_xml) {
714
786
  err_init(&err);
715
787
  Check_Type(ruby_xml, T_STRING);
716
788
  /* the xml string gets modified so make a copy of it */
789
+ str = StringValuePtr(ruby_xml);
717
790
  len = RSTRING_LEN(ruby_xml) + 1;
718
- x = defuse_bom(StringValuePtr(ruby_xml), &options);
791
+ x = defuse_bom(str, &options);
792
+ // Drop the BOM from len as well, otherwise the copy below reads past the
793
+ // end of the Ruby string and xml is left without a terminating '\0'.
794
+ len -= (size_t)(x - str);
719
795
  if (SMALL_XML < len) {
720
796
  xml = ALLOC_N(char, len);
721
797
  } else {
@@ -789,6 +865,9 @@ static int load_options_cb(VALUE k, VALUE v, VALUE opts) {
789
865
  } else if (invalid_replace_sym == k) {
790
866
  if (Qnil == v) {
791
867
  copts->allow_invalid = Yes;
868
+ } else if (Qfalse == v) {
869
+ copts->allow_invalid = NotSet;
870
+ *copts->inv_repl = '\0';
792
871
  } else {
793
872
  long slen;
794
873
 
@@ -845,6 +924,7 @@ static int load_options_cb(VALUE k, VALUE v, VALUE opts) {
845
924
 
846
925
  static VALUE load(char *xml, size_t len, int argc, VALUE *argv, VALUE self, VALUE encoding, Err err) {
847
926
  VALUE obj;
927
+ char *body;
848
928
  struct _options options = ox_default_options;
849
929
 
850
930
  if (1 == argc && rb_cHash == rb_obj_class(*argv)) {
@@ -859,14 +939,21 @@ static VALUE load(char *xml, size_t len, int argc, VALUE *argv, VALUE self, VALU
859
939
  } else if (0 == options.rb_enc) {
860
940
  options.rb_enc = rb_enc_find(options.encoding);
861
941
  }
862
- xml = defuse_bom(xml, &options);
942
+ // Shorten len by the size of the BOM so that xml + len stays the address
943
+ // of the terminating '\0' rather than running past the end of the buffer.
944
+ body = defuse_bom(xml, &options);
945
+ len -= (size_t)(body - xml);
946
+ xml = body;
863
947
  switch (options.mode) {
864
- case ObjMode:
865
- rb_gc_disable();
866
- obj = ox_parse(xml, len, ox_obj_callbacks, 0, &options, err);
948
+ case ObjMode: {
949
+ struct _objParse args = {xml, len, &options, err};
950
+ VALUE was_disabled;
951
+
952
+ was_disabled = rb_gc_disable();
953
+ obj = rb_ensure(obj_parse_body, (VALUE)&args, obj_parse_reenable, was_disabled);
867
954
  RB_GC_GUARD(obj);
868
- rb_gc_enable();
869
955
  break;
956
+ }
870
957
  case GenMode: obj = ox_parse(xml, len, ox_gen_callbacks, 0, &options, err); break;
871
958
  case LimMode: obj = ox_parse(xml, len, ox_limited_callbacks, 0, &options, err); break;
872
959
  case HashMode:
@@ -905,14 +992,24 @@ static VALUE load(char *xml, size_t len, int argc, VALUE *argv, VALUE self, VALU
905
992
  * - _:limited_ - read as a generic XML file but with callbacks on text and elements events only
906
993
  * - _:hash_ - read and convert to a Hash and core class objects only
907
994
  * - _:hash_no_attrs_ - read and convert to a Hash and core class objects only without capturing attributes
995
+ *
996
+ * If neither this option nor Ox.default_options[:mode] is set the document
997
+ * may pick the mode itself with an <?ox mode="generic"?> or an
998
+ * <?ox mode="limited"?> processing instruction. A document can never pick
999
+ * object mode. Only the caller can, with mode: :object, since object mode
1000
+ * allocates the classes the document names and sets their instance
1001
+ * variables without calling initialize.
908
1002
  * - *:effort* [:strict|:tolerant|:auto_define] effort to use when an undefined class is encountered, default: :strict
909
1003
  * - _:strict_ - raise an NameError for missing classes and modules
910
1004
  * - _:tolerant_ - return nil for missing classes and modules
911
1005
  * - _:auto_define_ - auto define missing classes and modules
912
1006
  * - *:trace* [Fixnum] trace level as a Fixnum, default: 0 (silent)
913
1007
  * - *:symbolize_keys* [true|false|nil] symbolize element attribute keys or leave as Strings
914
- * - *:invalid_replace* [nil|String] replacement string for invalid XML characters on dump. nil indicates include
915
- * anyway as hex. A string, limited to 10 characters will replace the invalid character with the replace.
1008
+ * - *:invalid_replace* [nil|false|String] what to do with a character XML can not hold on dump. false, the default,
1009
+ * raises an Ox::SyntaxError. nil writes it as a hex character reference, other than a NUL which is dropped since
1010
+ * &#x0000; can not be read back. A string, limited to 10 characters, replaces it and the empty string drops it. This
1011
+ * covers text and attribute values. A comment, CDATA section, DOCTYPE or processing instruction expands no character
1012
+ * reference, so a character XML can not hold always raises there.
916
1013
  * - *:strip_namespace* [String|true|false] "" or false result in no namespace stripping. A string of "*" or true will
917
1014
  * strip all namespaces. Any other non-empty string indicates that matching namespaces will be stripped.
918
1015
  * - *:with_cdata* [true|false] if true cdata is included in hash_load output otherwise it is not.
@@ -924,6 +1021,9 @@ static VALUE load_str(int argc, VALUE *argv, VALUE self) {
924
1021
  VALUE encoding;
925
1022
  struct _err err;
926
1023
 
1024
+ if (1 > argc) {
1025
+ rb_raise(ox_arg_error_class, "missing XML string");
1026
+ }
927
1027
  err_init(&err);
928
1028
  Check_Type(*argv, T_STRING);
929
1029
  /* the xml string gets modified so make a copy of it */
@@ -959,14 +1059,24 @@ static VALUE load_str(int argc, VALUE *argv, VALUE self) {
959
1059
  * - _:limited_ - read as a generic XML file but with callbacks on text and elements events only
960
1060
  * - _:hash_ - read and convert to a Hash and core class objects only
961
1061
  * - _:hash_no_attrs_ - read and convert to a Hash and core class objects only without capturing attributes
1062
+ *
1063
+ * If neither this option nor Ox.default_options[:mode] is set the document
1064
+ * may pick the mode itself with an <?ox mode="generic"?> or an
1065
+ * <?ox mode="limited"?> processing instruction. A document can never pick
1066
+ * object mode. Only the caller can, with mode: :object, since object mode
1067
+ * allocates the classes the document names and sets their instance
1068
+ * variables without calling initialize.
962
1069
  * - *:effort* [:strict|:tolerant|:auto_define] effort to use when an undefined class is encountered, default: :strict
963
1070
  * - _:strict_ - raise an NameError for missing classes and modules
964
1071
  * - _:tolerant_ - return nil for missing classes and modules
965
1072
  * - _:auto_define_ - auto define missing classes and modules
966
1073
  * - *:trace* [Fixnum] trace level as a Fixnum, default: 0 (silent)
967
1074
  * - *:symbolize_keys* [true|false|nil] symbolize element attribute keys or leave as Strings
968
- * - *:invalid_replace* [nil|String] replacement string for invalid XML characters on dump. nil indicates include
969
- * anyway as hex. A string, limited to 10 characters will replace the invalid character with the replace.
1075
+ * - *:invalid_replace* [nil|false|String] what to do with a character XML can not hold on dump. false, the default,
1076
+ * raises an Ox::SyntaxError. nil writes it as a hex character reference, other than a NUL which is dropped since
1077
+ * &#x0000; can not be read back. A string, limited to 10 characters, replaces it and the empty string drops it. This
1078
+ * covers text and attribute values. A comment, CDATA section, DOCTYPE or processing instruction expands no character
1079
+ * reference, so a character XML can not hold always raises there.
970
1080
  * - *:strip_namespace* [String|true|false] "" or false result in no namespace stripping. A string of "*" or true will
971
1081
  * strip all namespaces. Any other non-empty string indicates that matching namespaces will be stripped.
972
1082
  */
@@ -975,23 +1085,34 @@ static VALUE load_file(int argc, VALUE *argv, VALUE self) {
975
1085
  char *xml;
976
1086
  FILE *f;
977
1087
  off_t len;
1088
+ struct stat st;
978
1089
  VALUE obj;
979
1090
  struct _err err;
980
1091
 
1092
+ if (1 > argc) {
1093
+ rb_raise(ox_arg_error_class, "missing file path");
1094
+ }
981
1095
  err_init(&err);
982
1096
  Check_Type(*argv, T_STRING);
983
1097
  path = StringValuePtr(*argv);
984
- if (0 == (f = fopen(path, "r"))) {
1098
+ if (0 == (f = fopen(path, "rb"))) {
985
1099
  rb_raise(rb_eIOError, "%s\n", strerror(errno));
986
1100
  }
987
- fseek(f, 0, SEEK_END);
988
- len = ftello(f);
1101
+ // A stream that can not seek leaves len at -1, which allocates nothing and
1102
+ // then asks fread for SIZE_MAX bytes.
1103
+ // Only a regular file has a size worth sizing a read from. A FIFO reports
1104
+ // -1, and a directory can report anything at all, including off_t's
1105
+ // maximum.
1106
+ if (0 != fstat(fileno(f), &st) || !S_ISREG(st.st_mode)) {
1107
+ fclose(f);
1108
+ rb_raise(rb_eIOError, "%s is not a regular file.\n", path);
1109
+ }
1110
+ len = st.st_size;
989
1111
  if (SMALL_XML < len) {
990
- xml = ALLOC_N(char, len + 1);
1112
+ xml = ALLOC_N(char, (size_t)len + 1);
991
1113
  } else {
992
- xml = ALLOCA_N(char, len + 1);
1114
+ xml = ALLOCA_N(char, (size_t)len + 1);
993
1115
  }
994
- fseek(f, 0, SEEK_SET);
995
1116
  if ((size_t)len != fread(xml, 1, len, f)) {
996
1117
  ox_err_set(&err, rb_eLoadError, "Failed to read %ld bytes from %s.\n", (long)len, path);
997
1118
  obj = Qnil;
@@ -1178,10 +1299,20 @@ static void parse_dump_options(VALUE ropts, Options copts) {
1178
1299
  VALUE v;
1179
1300
 
1180
1301
  if (Qnil != (v = rb_hash_lookup(ropts, ox_indent_sym))) {
1302
+ int indent;
1303
+
1181
1304
  if (rb_cInteger != rb_obj_class(v) && T_FIXNUM != rb_type(v)) {
1182
1305
  rb_raise(ox_parse_error_class, ":indent must be a Fixnum.\n");
1183
1306
  }
1184
- copts->indent = NUM2INT(v);
1307
+ indent = NUM2INT(v);
1308
+ // See load_default_options(): bound indent to avoid int overflow in
1309
+ // the dump.c indent arithmetic (oj CVE-2026-54502 class of bug).
1310
+ if (indent < 0) {
1311
+ indent = -1;
1312
+ } else if (OX_MAX_INDENT < indent) {
1313
+ rb_raise(ox_parse_error_class, ":indent can be no larger than %d.\n", OX_MAX_INDENT);
1314
+ }
1315
+ copts->indent = indent;
1185
1316
  }
1186
1317
  if (Qnil != (v = rb_hash_lookup(ropts, trace_sym))) {
1187
1318
  if (rb_cInteger != rb_obj_class(v) && T_FIXNUM != rb_type(v)) {
@@ -1214,6 +1345,9 @@ static void parse_dump_options(VALUE ropts, Options copts) {
1214
1345
  if (Qtrue == rb_funcall(ropts, has_key_id, 1, invalid_replace_sym)) {
1215
1346
  copts->allow_invalid = Yes;
1216
1347
  }
1348
+ } else if (Qfalse == v) {
1349
+ copts->allow_invalid = NotSet;
1350
+ *copts->inv_repl = '\0';
1217
1351
  } else {
1218
1352
  long slen;
1219
1353
 
@@ -1286,6 +1420,9 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
1286
1420
  struct _options copts = ox_default_options;
1287
1421
  VALUE rstr;
1288
1422
 
1423
+ if (1 > argc) {
1424
+ rb_raise(ox_arg_error_class, "missing object to dump");
1425
+ }
1289
1426
  if (2 == argc) {
1290
1427
  parse_dump_options(argv[1], &copts);
1291
1428
  }
@@ -1348,6 +1485,9 @@ static VALUE to_xml(int argc, VALUE *argv, VALUE self) {
1348
1485
  static VALUE to_file(int argc, VALUE *argv, VALUE self) {
1349
1486
  struct _options copts = ox_default_options;
1350
1487
 
1488
+ if (2 > argc) {
1489
+ rb_raise(ox_arg_error_class, "missing file path or object to write");
1490
+ }
1351
1491
  if (3 == argc) {
1352
1492
  parse_dump_options(argv[2], &copts);
1353
1493
  }
@@ -1447,6 +1587,7 @@ void Init_ox(void) {
1447
1587
  ox_string_id = rb_intern("string");
1448
1588
  ox_text_id = rb_intern("text");
1449
1589
  ox_to_c_id = rb_intern("to_c");
1590
+ ox_utc_offset_id = rb_intern("utc_offset");
1450
1591
  ox_value_id = rb_intern("value");
1451
1592
 
1452
1593
  encoding_id = rb_intern("encoding");
data/ext/ox/ox.h CHANGED
@@ -34,6 +34,14 @@ extern "C" {
34
34
  #define raise_error(msg, xml, current) _ox_raise_error(msg, xml, current, __FILE__, __LINE__)
35
35
 
36
36
  #define MAX_TEXT_LEN 4096
37
+ // Anything even close to the max prolog length is most likely someone trying
38
+ // to break something.
39
+ #define MAX_PROLOG 32767
40
+
41
+ /* Upper bound for the :indent dump option. Keeps depth * indent (depth is
42
+ * capped at the dump MAX_DEPTH of 1000) far below INT_MAX so the indent size
43
+ * arithmetic in dump.c cannot overflow and under-grow the output buffer. */
44
+ #define OX_MAX_INDENT 16384
37
45
 
38
46
  #define SILENT 0
39
47
  #define TRACE 1
@@ -90,7 +98,7 @@ typedef struct _parseCallbacks {
90
98
  void (*add_doctype)(PInfo pi, const char *docType);
91
99
  void (*add_comment)(PInfo pi, const char *comment);
92
100
  void (*add_cdata)(PInfo pi, const char *cdata, size_t len);
93
- void (*add_text)(PInfo pi, char *text, int closed);
101
+ void (*add_text)(PInfo pi, char *text, size_t len, int closed);
94
102
  void (*add_element)(PInfo pi, const char *ename, Attr attrs, int hasChildren);
95
103
  void (*end_element)(PInfo pi, const char *ename);
96
104
  void (*finish)(PInfo pi);
@@ -151,6 +159,7 @@ struct _pInfo {
151
159
 
152
160
  extern VALUE ox_parse(char *xml, size_t len, ParseCallbacks pcb, char **endp, Options options, Err err);
153
161
  extern void _ox_raise_error(const char *msg, const char *xml, const char *current, const char *file, int line);
162
+ extern void ox_circ_array_free(CircArray ca);
154
163
 
155
164
  extern void ox_sax_define(void);
156
165
 
@@ -205,6 +214,7 @@ extern ID ox_start_element_id;
205
214
  extern ID ox_string_id;
206
215
  extern ID ox_text_id;
207
216
  extern ID ox_to_c_id;
217
+ extern ID ox_utc_offset_id;
208
218
  extern ID ox_value_id;
209
219
 
210
220
  extern rb_encoding *ox_utf8_encoding;