manticore-smash 3.1.0

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.
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: false
2
+
3
+ # Copyright (C) 2024 Manticore Authors
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published
7
+ # by the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ module XmlUtils
19
+ class Formatters
20
+ class Default
21
+ attr_accessor :compact
22
+
23
+ def initialize(compact = false)
24
+ @compact = compact
25
+ end
26
+
27
+ def write(node, output = $stdout)
28
+ case node
29
+ when Document
30
+ node.children.each_with_index do |child, idx|
31
+ write(child, output)
32
+ output << "\n" unless idx == node.children.length - 1
33
+ end
34
+ when Element
35
+ write_element(node, output, 0)
36
+ when Text
37
+ output << node.to_s
38
+ when CData
39
+ output << node.to_s
40
+ when Comment
41
+ output << node.to_s
42
+ when ProcessingInstruction
43
+ output << node.to_s
44
+ when XMLDecl
45
+ output << node.to_s
46
+ when DocType
47
+ output << node.to_s
48
+ end
49
+ end
50
+
51
+ def write_element(node, output, indent = 0)
52
+ output << ' ' * indent
53
+ output << "<#{node.name}"
54
+ node.attributes.each_value do |attr|
55
+ output << " #{attr.to_string}"
56
+ end
57
+
58
+ if node.children.empty?
59
+ output << " />"
60
+ else
61
+ output << ">"
62
+ has_only_text = node.children.all? { |c| c.is_a?(Text) || c.is_a?(CData) }
63
+ if has_only_text
64
+ node.children.each { |c| write(c, output) }
65
+ else
66
+ output << "\n"
67
+ node.children.each do |c|
68
+ write(c, output) if c.is_a?(Text) && c.to_s.strip.empty?
69
+ next if c.is_a?(Text) && c.to_s.strip.empty?
70
+ if c.is_a?(Element)
71
+ write_element(c, output, indent + 1)
72
+ else
73
+ output << ' ' * (indent + 1)
74
+ write(c, output)
75
+ end
76
+ output << "\n"
77
+ end
78
+ output << ' ' * indent
79
+ end
80
+ output << "</#{node.name}>"
81
+ end
82
+ end
83
+ end
84
+
85
+ class Pretty < Default
86
+ def initialize
87
+ super(false)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,585 @@
1
+ # frozen_string_literal: false
2
+
3
+ # Copyright (C) 2024 Manticore Authors
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published
7
+ # by the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ module XmlUtils
19
+ class ParseException < RuntimeError
20
+ attr_reader :line, :position
21
+
22
+ def initialize(message, line = nil, position = nil)
23
+ @line = line
24
+ @position = position
25
+ super(message)
26
+ end
27
+
28
+ def to_s
29
+ base = super
30
+ @line ? "#{base} (line #{@line}, pos #{@position})" : base
31
+ end
32
+ end
33
+
34
+ class IllegalArgumentError < ArgumentError; end
35
+ class UndefinedNamespaceException < ParseException; end
36
+
37
+ class Node
38
+ include Enumerable
39
+
40
+ attr_accessor :parent
41
+
42
+ def initialize
43
+ @parent = nil
44
+ end
45
+
46
+ def each(&block)
47
+ return to_enum unless block_given?
48
+ end
49
+
50
+ def to_s
51
+ output = ""
52
+ write(output)
53
+ output
54
+ end
55
+
56
+ def node_type
57
+ self.class.name.split('::').last.downcase.to_sym
58
+ end
59
+
60
+ def root
61
+ node = self
62
+ node = node.parent while node.parent
63
+ node
64
+ end
65
+
66
+ def document
67
+ r = root
68
+ r.is_a?(Document) ? r : nil
69
+ end
70
+
71
+ def next_sibling
72
+ return nil unless @parent
73
+ siblings = @parent.children
74
+ idx = siblings.index(self)
75
+ idx ? siblings[idx + 1] : nil
76
+ end
77
+
78
+ def previous_sibling
79
+ return nil unless @parent
80
+ siblings = @parent.children
81
+ idx = siblings.index(self)
82
+ idx && idx > 0 ? siblings[idx - 1] : nil
83
+ end
84
+
85
+ def remove
86
+ @parent.delete(self) if @parent
87
+ self
88
+ end
89
+
90
+ def deep_clone
91
+ Marshal.load(Marshal.dump(self))
92
+ end
93
+ end
94
+
95
+ class ChildNode < Node
96
+ def write(output, indent = 0)
97
+ output << ' ' * indent
98
+ write_content(output, indent)
99
+ end
100
+
101
+ def write_content(output, indent)
102
+ raise NotImplementedError
103
+ end
104
+ end
105
+
106
+ class Text < ChildNode
107
+ attr_accessor :value, :raw, :unnormalized
108
+
109
+ def initialize(value, respect_whitespace = false, parent = nil)
110
+ super()
111
+ @parent = parent
112
+ @raw = false
113
+ @unnormalized = nil
114
+ @value = respect_whitespace ? value : normalize(value)
115
+ end
116
+
117
+ def node_type
118
+ :text
119
+ end
120
+
121
+ def clone
122
+ Text.new(@value, true)
123
+ end
124
+
125
+ def empty?
126
+ @value.nil? || @value.empty?
127
+ end
128
+
129
+ def <=>(other)
130
+ other <=> @value
131
+ end
132
+
133
+ def to_s
134
+ @value.to_s
135
+ end
136
+
137
+ def value
138
+ @unnormalized || unnormalize(@value)
139
+ end
140
+
141
+ def value=(val)
142
+ @value = normalize(val)
143
+ @unnormalized = nil
144
+ end
145
+
146
+ def write_content(output, indent = 0)
147
+ if @raw
148
+ output << @value
149
+ else
150
+ output << escape(@value)
151
+ end
152
+ end
153
+
154
+ private
155
+
156
+ def normalize(input)
157
+ return '' if input.nil?
158
+ input.gsub(/\r\n?/, "\n")
159
+ end
160
+
161
+ def unnormalize(input)
162
+ input.gsub(/&(amp|lt|gt|quot|apos);/) do |match|
163
+ case $1
164
+ when 'amp' then '&'
165
+ when 'lt' then '<'
166
+ when 'gt' then '>'
167
+ when 'quot' then '"'
168
+ when 'apos' then "'"
169
+ else match
170
+ end
171
+ end
172
+ end
173
+
174
+ def escape(input)
175
+ input.gsub('&', '&amp;')
176
+ .gsub('<', '&lt;')
177
+ .gsub('>', '&gt;')
178
+ .gsub('"', '&quot;')
179
+ end
180
+ end
181
+
182
+ class CData < Text
183
+ def initialize(value, respect_whitespace = false, parent = nil)
184
+ super(value, respect_whitespace, parent)
185
+ @raw = true
186
+ end
187
+
188
+ def node_type
189
+ :cdata
190
+ end
191
+
192
+ def clone
193
+ CData.new(@value, true)
194
+ end
195
+
196
+ def write_content(output, indent = 0)
197
+ output << "<![CDATA[#{@value}]]>"
198
+ end
199
+ end
200
+
201
+ class Comment < ChildNode
202
+ attr_accessor :string
203
+
204
+ def initialize(string, parent = nil)
205
+ super()
206
+ @parent = parent
207
+ @string = string.to_s
208
+ end
209
+
210
+ def node_type
211
+ :comment
212
+ end
213
+
214
+ def clone
215
+ Comment.new(@string)
216
+ end
217
+
218
+ def write_content(output, indent = 0)
219
+ output << "<!--#{@string}-->"
220
+ end
221
+ end
222
+
223
+ class ProcessingInstruction < ChildNode
224
+ attr_accessor :target, :content
225
+
226
+ def initialize(target, content = nil, parent = nil)
227
+ super()
228
+ @parent = parent
229
+ @target = target
230
+ @content = content
231
+ end
232
+
233
+ def node_type
234
+ :processing_instruction
235
+ end
236
+
237
+ def clone
238
+ ProcessingInstruction.new(@target, @content)
239
+ end
240
+
241
+ def write_content(output, indent = 0)
242
+ if @content && !@content.empty?
243
+ output << "<?#{@target} #{@content}?>"
244
+ else
245
+ output << "<?#{@target}?>"
246
+ end
247
+ end
248
+ end
249
+
250
+ class DocType < ChildNode
251
+ attr_accessor :name, :external_id, :system_id, :public_id
252
+
253
+ def initialize(name, external_id = nil, system_id = nil, public_id = nil, parent = nil)
254
+ super()
255
+ @parent = parent
256
+ @name = name
257
+ @external_id = external_id
258
+ @system_id = system_id
259
+ @public_id = public_id
260
+ end
261
+
262
+ def node_type
263
+ :doctype
264
+ end
265
+
266
+ def write_content(output, indent = 0)
267
+ if @external_id
268
+ if @public_id
269
+ output << "<!DOCTYPE #{@name} PUBLIC \"#{@public_id}\" \"#{@system_id}\">"
270
+ else
271
+ output << "<!DOCTYPE #{@name} SYSTEM \"#{@system_id}\">"
272
+ end
273
+ else
274
+ output << "<!DOCTYPE #{@name}>"
275
+ end
276
+ end
277
+ end
278
+
279
+ class XMLDecl < ProcessingInstruction
280
+ attr_accessor :version, :encoding, :standalone
281
+
282
+ def initialize(version = "1.0", encoding = nil, standalone = nil, parent = nil)
283
+ super('xml', nil, parent)
284
+ @version = version
285
+ @encoding = encoding
286
+ @standalone = standalone
287
+ end
288
+
289
+ def node_type
290
+ :xmldecl
291
+ end
292
+
293
+ def write_content(output, indent = 0)
294
+ attrs = ["version=\"#{@version}\""]
295
+ attrs << "encoding=\"#{@encoding}\"" if @encoding
296
+ attrs << "standalone=\"#{@standalone}\"" if @standalone
297
+ output << "<?xml #{attrs.join(' ')}?>"
298
+ end
299
+ end
300
+
301
+ class Attribute
302
+ attr_accessor :name, :value, :normalized, :element
303
+
304
+ def initialize(name, value, normalized = true)
305
+ @name = name.to_s
306
+ @value = normalized ? value.to_s : normalize(value.to_s)
307
+ @normalized = normalized
308
+ @element = nil
309
+ end
310
+
311
+ def node_type
312
+ :attribute
313
+ end
314
+
315
+ def clone
316
+ Attribute.new(@name, @value, true)
317
+ end
318
+
319
+ def to_s
320
+ @value
321
+ end
322
+
323
+ def to_string
324
+ "#{@name}=\"#{escape(@value)}\""
325
+ end
326
+
327
+ def namespace
328
+ prefix = @name.include?(':') ? @name.split(':').first : ''
329
+ prefix == 'xmlns' ? '' : prefix
330
+ end
331
+
332
+ def prefix
333
+ @name.include?(':') ? @name.split(':').first : ''
334
+ end
335
+
336
+ private
337
+
338
+ def normalize(input)
339
+ input.gsub(/\r\n?/, "\n").gsub(/\n/, '&#10;')
340
+ end
341
+
342
+ def escape(input)
343
+ input.gsub('&', '&amp;')
344
+ .gsub('<', '&lt;')
345
+ .gsub('>', '&gt;')
346
+ .gsub('"', '&quot;')
347
+ .gsub("'", '&apos;')
348
+ end
349
+ end
350
+
351
+ class Element < ChildNode
352
+ attr_accessor :name, :attributes, :children, :prefixes
353
+
354
+ def initialize(name, parent = nil)
355
+ super()
356
+ @parent = parent
357
+ @name = name.to_s
358
+ @attributes = {}
359
+ @children = []
360
+ @prefixes = {}
361
+ @parent.add(self) if @parent && @parent.respond_to?(:add)
362
+ end
363
+
364
+ def node_type
365
+ :element
366
+ end
367
+
368
+ def clone
369
+ cloned = Element.new(@name)
370
+ @attributes.each { |k, v| cloned.add_attribute(k, v.clone) }
371
+ @children.each { |c| cloned.add(c.clone) }
372
+ cloned
373
+ end
374
+
375
+ def add(element)
376
+ element.parent = self
377
+ @children << element
378
+ element
379
+ end
380
+ alias << add
381
+
382
+ def delete(element)
383
+ @children.delete(element)
384
+ element.parent = nil if element.respond_to?(:parent=)
385
+ end
386
+
387
+ def delete_at(index)
388
+ @children.delete_at(index)
389
+ end
390
+
391
+ def [](name_or_index)
392
+ if name_or_index.is_a?(Integer)
393
+ @children[name_or_index]
394
+ else
395
+ attr = attribute(name_or_index.to_s)
396
+ attr ? attr.value : nil
397
+ end
398
+ end
399
+
400
+ def []=(name_or_index, value)
401
+ if name_or_index.is_a?(Integer)
402
+ @children[name_or_index] = value
403
+ else
404
+ add_attribute(name_or_index.to_s, value.to_s)
405
+ end
406
+ end
407
+
408
+ def add_attribute(name, value)
409
+ attr = value.is_a?(Attribute) ? value : Attribute.new(name, value)
410
+ attr.element = self
411
+ @attributes[name.to_s] = attr
412
+ attr
413
+ end
414
+ alias set_attribute add_attribute
415
+
416
+ def delete_attribute(name)
417
+ @attributes.delete(name.to_s)
418
+ end
419
+
420
+ def attribute(name)
421
+ @attributes[name.to_s]
422
+ end
423
+
424
+ def has_attribute?(name)
425
+ @attributes.key?(name.to_s)
426
+ end
427
+
428
+ def each_element(&block)
429
+ return to_enum(:each_element) unless block_given?
430
+ @children.select { |c| c.is_a?(Element) }.each(&block)
431
+ end
432
+
433
+ def elements
434
+ @children.select { |c| c.is_a?(Element) }
435
+ end
436
+
437
+ def text(path = nil)
438
+ return XPath.match(self, path).first.text if path
439
+ txt = @children.select { |c| c.is_a?(Text) || c.is_a?(CData) }
440
+ txt.map(&:to_s).join('')
441
+ end
442
+
443
+ def get_text(path = nil)
444
+ return XPath.match(self, path).first if path
445
+ @children.find { |c| c.is_a?(Text) || c.is_a?(CData) }
446
+ end
447
+
448
+ def add_text(text)
449
+ t = text.is_a?(Text) ? text : Text.new(text)
450
+ add(t)
451
+ t
452
+ end
453
+
454
+ def get_elements(name)
455
+ @children.select { |c| c.is_a?(Element) && c.name == name }
456
+ end
457
+
458
+ def each_recursive(&block)
459
+ return to_enum(:each_recursive) unless block_given?
460
+ @children.each do |child|
461
+ block.call(child)
462
+ child.each_recursive(&block) if child.is_a?(Element)
463
+ end
464
+ end
465
+
466
+ def namespaces
467
+ ns = @prefixes.dup
468
+ @attributes.each do |k, v|
469
+ if k == 'xmlns'
470
+ ns[''] = v.value
471
+ elsif k.start_with?('xmlns:')
472
+ ns[k.sub('xmlns:', '')] = v.value
473
+ end
474
+ end
475
+ ns
476
+ end
477
+
478
+ def namespace(prefix = nil)
479
+ prefix ||= self.prefix
480
+ ns = namespaces
481
+ return ns[prefix] if ns.key?(prefix)
482
+ @parent.respond_to?(:namespace) ? @parent.namespace(prefix) : nil
483
+ end
484
+
485
+ def prefix
486
+ @name.include?(':') ? @name.split(':').first : ''
487
+ end
488
+
489
+ def expand(name)
490
+ return name unless name.include?(':')
491
+ p, local = name.split(':')
492
+ ns = namespace(p)
493
+ ns ? "{#{ns}}#{local}" : name
494
+ end
495
+
496
+ def write_content(output, indent = 0)
497
+ output << "<#{@name}"
498
+ @attributes.each_value { |attr| output << " #{attr.to_string}" }
499
+ if @children.empty?
500
+ output << " />"
501
+ else
502
+ output << ">"
503
+ has_only_text = @children.all? { |c| c.is_a?(Text) || c.is_a?(CData) }
504
+ if has_only_text
505
+ @children.each { |c| c.write(output, 0) }
506
+ else
507
+ output << "\n"
508
+ @children.each { |c| c.write(output, indent + 1); output << "\n" }
509
+ output << ' ' * indent
510
+ end
511
+ output << "</#{@name}>"
512
+ end
513
+ end
514
+
515
+ def inspect
516
+ attrs = @attributes.map { |k, v| "#{k}=#{v.to_s.inspect}" }.join(' ')
517
+ attrs = " #{attrs}" unless attrs.empty?
518
+ "<#{@name}#{attrs}>"
519
+ end
520
+ end
521
+
522
+ class Document < Node
523
+ attr_accessor :children
524
+
525
+ def initialize(standalone = nil)
526
+ super()
527
+ @children = []
528
+ @standalone = standalone
529
+ end
530
+
531
+ def node_type
532
+ :document
533
+ end
534
+
535
+ def add(element)
536
+ element.parent = self
537
+ @children << element
538
+ element
539
+ end
540
+ alias << add
541
+
542
+ def delete(element)
543
+ @children.delete(element)
544
+ element.parent = nil if element.respond_to?(:parent=)
545
+ end
546
+
547
+ def root
548
+ @children.find { |c| c.is_a?(Element) }
549
+ end
550
+
551
+ def write(output = $stdout, indent = 0)
552
+ @children.each_with_index do |child, idx|
553
+ child.write(output, indent)
554
+ output << "\n" unless idx == @children.length - 1
555
+ end
556
+ end
557
+
558
+ def to_s
559
+ output = ""
560
+ write(output)
561
+ output
562
+ end
563
+
564
+ def xml_decl
565
+ @children.find { |c| c.is_a?(XMLDecl) || (c.is_a?(ProcessingInstruction) && c.respond_to?(:target) && c.target == 'xml') }
566
+ end
567
+
568
+ def doctype
569
+ @children.find { |c| c.is_a?(DocType) }
570
+ end
571
+
572
+ def each_element(&block)
573
+ return to_enum(:each_element) unless block_given?
574
+ @children.select { |c| c.is_a?(Element) }.each(&block)
575
+ end
576
+
577
+ def elements
578
+ @children.select { |c| c.is_a?(Element) }
579
+ end
580
+
581
+ def context
582
+ {}
583
+ end
584
+ end
585
+ end