Mini Shell
# coding: US-ASCII
# frozen_string_literal: false
require 'rexml/encoding'
module REXML
# Generates Source-s. USE THIS CLASS.
class SourceFactory
# Generates a Source object
# @param arg Either a String, or an IO
# @return a Source, or nil if a bad argument was given
def SourceFactory::create_from(arg)
if arg.respond_to? :read and
arg.respond_to? :readline and
arg.respond_to? :nil? and
arg.respond_to? :eof?
IOSource.new(arg)
elsif arg.respond_to? :to_str
require 'stringio'
IOSource.new(StringIO.new(arg))
elsif arg.kind_of? Source
arg
else
raise "#{arg.class} is not a valid input stream. It must walk \n"+
"like either a String, an IO, or a Source."
end
end
end
# A Source can be searched for patterns, and wraps buffers and other
# objects and provides consumption of text
class Source
include Encoding
# The line number of the last consumed text
attr_reader :line
attr_reader :encoding
# Constructor
# @param arg must be a String, and should be a valid XML document
# @param encoding if non-null, sets the encoding of the source to this
# value, overriding all encoding detection
def initialize(arg, encoding=nil)
@orig = arg
@scanner = StringScanner.new(@orig)
if encoding
self.encoding = encoding
else
detect_encoding
end
@line = 0
end
# The current buffer (what we're going to read next)
def buffer
@scanner.rest
end
def buffer_encoding=(encoding)
@scanner.string.force_encoding(encoding)
end
# Inherited from Encoding
# Overridden to support optimized en/decoding
def encoding=(enc)
return unless super
encoding_updated
end
def read(term = nil)
end
def read_until(term)
@scanner.scan_until(Regexp.union(term)) or @scanner.rest
end
def match(pattern, cons=false)
if cons
@scanner.scan(pattern).nil? ? nil : @scanner
else
@scanner.check(pattern).nil? ? nil : @scanner
end
end
def string=(string)
@scanner.string = string
end
# @return true if the Source is exhausted
def empty?
@scanner.eos?
end
# @return the current line in the source
def current_line
lines = @orig.split
res = lines.grep @scanner.rest[0..30]
res = res[-1] if res.kind_of? Array
lines.index( res ) if res
end
private
def detect_encoding
scanner_encoding = @scanner.rest.encoding
detected_encoding = "UTF-8"
begin
@scanner.string.force_encoding("ASCII-8BIT")
if @scanner.scan(/\xfe\xff/n)
detected_encoding = "UTF-16BE"
elsif @scanner.scan(/\xff\xfe/n)
detected_encoding = "UTF-16LE"
elsif @scanner.scan(/\xef\xbb\xbf/n)
detected_encoding = "UTF-8"
end
ensure
@scanner.string.force_encoding(scanner_encoding)
end
self.encoding = detected_encoding
end
def encoding_updated
if @encoding != 'UTF-8'
@scanner.string = decode(@scanner.rest)
@to_utf = true
else
@to_utf = false
@scanner.string.force_encoding(::Encoding::UTF_8)
end
end
end
# A Source that wraps an IO. See the Source class for method
# documentation
class IOSource < Source
#attr_reader :block_size
# block_size has been deprecated
def initialize(arg, block_size=500, encoding=nil)
@er_source = @source = arg
@to_utf = false
@pending_buffer = nil
if encoding
super("", encoding)
else
super(@source.read(3) || "")
end
if !@to_utf and
@orig.respond_to?(:force_encoding) and
@source.respond_to?(:external_encoding) and
@source.external_encoding != ::Encoding::UTF_8
@force_utf8 = true
else
@force_utf8 = false
end
end
def read(term = nil)
begin
@scanner << readline(term)
true
rescue Exception, NameError
@source = nil
false
end
end
def read_until(term)
pattern = Regexp.union(term)
data = []
begin
until str = @scanner.scan_until(pattern)
@scanner << readline(term)
end
rescue EOFError
@scanner.rest
else
read if @scanner.eos? and !@source.eof?
str
end
end
def match( pattern, cons=false )
read if @scanner.eos? && @source
while true
if cons
md = @scanner.scan(pattern)
else
md = @scanner.check(pattern)
end
break if md
return nil if pattern.is_a?(String) && pattern.bytesize <= @scanner.rest_size
return nil if @source.nil?
return nil unless read
end
md.nil? ? nil : @scanner
end
def empty?
super and ( @source.nil? || @source.eof? )
end
# @return the current line in the source
def current_line
begin
pos = @er_source.pos # The byte position in the source
lineno = @er_source.lineno # The XML < position in the source
@er_source.rewind
line = 0 # The \r\n position in the source
begin
while @er_source.pos < pos
@er_source.readline
line += 1
end
rescue
end
rescue IOError
pos = -1
line = -1
end
[pos, lineno, line]
end
private
def readline(term = nil)
str = @source.readline(term || @line_break)
if @pending_buffer
if str.nil?
str = @pending_buffer
else
str = @pending_buffer + str
end
@pending_buffer = nil
end
return nil if str.nil?
if @to_utf
decode(str)
else
str.force_encoding(::Encoding::UTF_8) if @force_utf8
str
end
end
def encoding_updated
case @encoding
when "UTF-16BE", "UTF-16LE"
@source.binmode
@source.set_encoding(@encoding, @encoding)
end
@line_break = encode(">")
@pending_buffer, @scanner.string = @scanner.rest, ""
@pending_buffer.force_encoding(@encoding)
super
end
end
end
Zerion Mini Shell 1.0