|
| 1 | +diff -ruN a/Gemfile b/Gemfile |
| 2 | +--- a/Gemfile 2021-04-05 04:43:38.000000000 -0700 |
| 3 | ++++ b/Gemfile 2024-05-29 00:06:13.851182285 -0700 |
| 4 | +@@ -4,3 +4,7 @@ |
| 5 | + |
| 6 | + # Specify your gem's dependencies in rexml.gemspec |
| 7 | + gemspec |
| 8 | ++ |
| 9 | ++group :development do |
| 10 | ++ gem "test-unit-ruby-core" |
| 11 | ++end |
| 12 | +diff -ruN a/lib/rexml/parsers/baseparser.rb b/lib/rexml/parsers/baseparser.rb |
| 13 | +--- a/lib/rexml/parsers/baseparser.rb 2021-04-05 04:43:38.000000000 -0700 |
| 14 | ++++ b/lib/rexml/parsers/baseparser.rb 2024-05-28 18:53:32.656078157 -0700 |
| 15 | +@@ -589,60 +589,41 @@ |
| 16 | + def parse_attributes(prefixes, curr_ns) |
| 17 | + attributes = {} |
| 18 | + closed = false |
| 19 | +- match_data = @source.match(/^(.*?)(\/)?>/um, true) |
| 20 | +- if match_data.nil? |
| 21 | +- message = "Start tag isn't ended" |
| 22 | +- raise REXML::ParseException.new(message, @source) |
| 23 | +- end |
| 24 | +- |
| 25 | +- raw_attributes = match_data[1] |
| 26 | +- closed = !match_data[2].nil? |
| 27 | +- return attributes, closed if raw_attributes.nil? |
| 28 | +- return attributes, closed if raw_attributes.empty? |
| 29 | +- |
| 30 | +- scanner = StringScanner.new(raw_attributes) |
| 31 | +- until scanner.eos? |
| 32 | +- if scanner.scan(/\s+/) |
| 33 | +- break if scanner.eos? |
| 34 | +- end |
| 35 | +- |
| 36 | +- pos = scanner.pos |
| 37 | +- loop do |
| 38 | +- break if scanner.scan(ATTRIBUTE_PATTERN) |
| 39 | +- unless scanner.scan(QNAME) |
| 40 | +- message = "Invalid attribute name: <#{scanner.rest}>" |
| 41 | +- raise REXML::ParseException.new(message, @source) |
| 42 | +- end |
| 43 | +- name = scanner[0] |
| 44 | +- unless scanner.scan(/\s*=\s*/um) |
| 45 | ++ while true |
| 46 | ++ if @source.match(">", true) |
| 47 | ++ return attributes, closed |
| 48 | ++ elsif @source.match("/>", true) |
| 49 | ++ closed = true |
| 50 | ++ return attributes, closed |
| 51 | ++ elsif match = @source.match(QNAME, true) |
| 52 | ++ name = match[1] |
| 53 | ++ prefix = match[2] |
| 54 | ++ local_part = match[3] |
| 55 | ++ unless @source.match(/\s*=\s*/um, true) |
| 56 | + message = "Missing attribute equal: <#{name}>" |
| 57 | + raise REXML::ParseException.new(message, @source) |
| 58 | + end |
| 59 | +- quote = scanner.scan(/['"]/) |
| 60 | +- unless quote |
| 61 | ++ unless match = @source.match(/(['"])(.*?)\1\s*/um, true) |
| 62 | ++ if match = @source.match(/(['"])/, true) |
| 63 | ++ message = |
| 64 | ++ "Missing attribute value end quote: <#{name}>: <#{match[1]}>" |
| 65 | ++ raise REXML::ParseException.new(message, @source) |
| 66 | ++ else |
| 67 | ++ message = "Missing attribute value start quote: <#{name}>" |
| 68 | ++ raise REXML::ParseException.new(message, @source) |
| 69 | ++ end |
| 70 | ++ unless match = @source.match(/(['"])/, true) |
| 71 | + message = "Missing attribute value start quote: <#{name}>" |
| 72 | + raise REXML::ParseException.new(message, @source) |
| 73 | + end |
| 74 | +- unless scanner.scan(/.*#{Regexp.escape(quote)}/um) |
| 75 | +- match_data = @source.match(/^(.*?)(\/)?>/um, true) |
| 76 | +- if match_data |
| 77 | +- scanner << "/" if closed |
| 78 | +- scanner << ">" |
| 79 | +- scanner << match_data[1] |
| 80 | +- scanner.pos = pos |
| 81 | +- closed = !match_data[2].nil? |
| 82 | +- next |
| 83 | +- end |
| 84 | +- message = |
| 85 | +- "Missing attribute value end quote: <#{name}>: <#{quote}>" |
| 86 | ++ quote = match[1] |
| 87 | ++ value = @source.read_until(quote) |
| 88 | ++ unless value.chomp!(quote) |
| 89 | ++ message = "Missing attribute value end quote: <#{name}>: <#{quote}>" |
| 90 | + raise REXML::ParseException.new(message, @source) |
| 91 | + end |
| 92 | +- end |
| 93 | +- name = scanner[1] |
| 94 | +- prefix = scanner[2] |
| 95 | +- local_part = scanner[3] |
| 96 | +- # quote = scanner[4] |
| 97 | +- value = scanner[5] |
| 98 | ++ value = match[2] |
| 99 | ++ @source.match(/\s*/um, true) |
| 100 | + if prefix == "xmlns" |
| 101 | + if local_part == "xml" |
| 102 | + if value != "http://www.w3.org/XML/1998/namespace" |
| 103 | +diff -ruN a/lib/rexml/source.rb b/lib/rexml/source.rb |
| 104 | +--- a/lib/rexml/source.rb 2021-04-05 04:43:38.000000000 -0700 |
| 105 | ++++ b/lib/rexml/source.rb 2024-05-28 17:10:36.356913505 -0700 |
| 106 | +@@ -81,7 +81,11 @@ |
| 107 | + rv |
| 108 | + end |
| 109 | + |
| 110 | +- def read |
| 111 | ++ def read(term = nil) |
| 112 | ++ end |
| 113 | ++ |
| 114 | ++ def read_until(term) |
| 115 | ++ @scanner.scan_until(Regexp.union(term)) or @scanner.rest |
| 116 | + end |
| 117 | + |
| 118 | + def consume( pattern ) |
| 119 | +@@ -204,11 +208,28 @@ |
| 120 | + rv |
| 121 | + end |
| 122 | + |
| 123 | +- def read |
| 124 | ++ def read(term = nil) |
| 125 | + begin |
| 126 | +- @buffer << readline |
| 127 | ++ @scanner << readline(term) |
| 128 | ++ true |
| 129 | + rescue Exception, NameError |
| 130 | + @source = nil |
| 131 | ++ false |
| 132 | ++ end |
| 133 | ++ end |
| 134 | ++ |
| 135 | ++ def read_until(term) |
| 136 | ++ pattern = Regexp.union(term) |
| 137 | ++ data = [] |
| 138 | ++ begin |
| 139 | ++ until str = @scanner.scan_until(pattern) |
| 140 | ++ @scanner << readline(term) |
| 141 | ++ end |
| 142 | ++ rescue EOFError |
| 143 | ++ @scanner.rest |
| 144 | ++ else |
| 145 | ++ read if @scanner.eos? and !@source.eof? |
| 146 | ++ str |
| 147 | + end |
| 148 | + end |
| 149 | + |
| 150 | +@@ -263,8 +284,8 @@ |
| 151 | + end |
| 152 | + |
| 153 | + private |
| 154 | +- def readline |
| 155 | +- str = @source.readline(@line_break) |
| 156 | ++ def readline(term = nil) |
| 157 | ++ str = @source.readline(term || @line_break) |
| 158 | + if @pending_buffer |
| 159 | + if str.nil? |
| 160 | + str = @pending_buffer |
| 161 | +diff -ruN a/test/test_document.rb b/test/test_document.rb |
| 162 | +--- a/test/test_document.rb 2021-04-05 04:43:38.000000000 -0700 |
| 163 | ++++ b/test/test_document.rb 2024-05-29 00:08:01.164345808 -0700 |
| 164 | +@@ -1,8 +1,12 @@ |
| 165 | + # -*- coding: utf-8 -*- |
| 166 | + # frozen_string_literal: false |
| 167 | + |
| 168 | ++require 'core_assertions' |
| 169 | ++ |
| 170 | + module REXMLTests |
| 171 | + class TestDocument < Test::Unit::TestCase |
| 172 | ++ include Test::Unit::CoreAssertions |
| 173 | ++ |
| 174 | + def test_version_attributes_to_s |
| 175 | + doc = REXML::Document.new(<<-eoxml) |
| 176 | + <?xml version="1.0" encoding="UTF-8" standalone="no"?> |
| 177 | +@@ -200,6 +204,13 @@ |
| 178 | + assert_equal('no', doc.stand_alone?, bug2539) |
| 179 | + end |
| 180 | + |
| 181 | ++ def test_gt_linear_performance |
| 182 | ++ seq = [10000, 50000, 100000, 150000, 200000] |
| 183 | ++ assert_linear_performance(seq) do |n| |
| 184 | ++ REXML::Document.new('<test testing="' + ">" * n + '"></test>') |
| 185 | ++ end |
| 186 | ++ end |
| 187 | ++ |
| 188 | + class WriteTest < Test::Unit::TestCase |
| 189 | + def setup |
| 190 | + @document = REXML::Document.new(<<-EOX) |
0 commit comments