mirror of
https://github.com/mistergibson/gxg-web-client.git
synced 2026-08-15 11:34:29 -07:00
573 lines
12 KiB
Ruby
573 lines
12 KiB
Ruby
module Rubinius
|
|
|
|
# :internal:
|
|
#
|
|
# The virtual concatenation file of the files given on command line (or
|
|
# from $stdin if no files were given.)
|
|
#
|
|
# The only instance hereof is the object referred to by ARGF.
|
|
#
|
|
# @see ARGF
|
|
#
|
|
class ARGFClass
|
|
include Enumerable
|
|
|
|
# :internal:
|
|
#
|
|
# Create a stateless ARGF.
|
|
#
|
|
# The actual setup is done on the fly:
|
|
#
|
|
# @see #advance!
|
|
#
|
|
def initialize
|
|
@lineno = 0
|
|
@advance = true
|
|
@init = false
|
|
@use_stdin_only = false
|
|
@encoding_args = nil
|
|
end
|
|
|
|
private :initialize
|
|
|
|
#
|
|
# Set stream into binary mode.
|
|
#
|
|
# Stream is set into binary mode, i.e. 8-bit ASCII.
|
|
# Once set, the binary mode cannot be undone. Returns
|
|
# self.
|
|
#
|
|
def binmode
|
|
@binmode = true
|
|
@external = Encoding::ASCII_8BIT
|
|
self
|
|
end
|
|
|
|
def binmode?
|
|
@binmode
|
|
end
|
|
|
|
#
|
|
# Close stream.
|
|
#
|
|
def close
|
|
advance!
|
|
@stream.close
|
|
@advance = true unless @use_stdin_only
|
|
@lineno = 0
|
|
@binmode = false
|
|
@external = nil
|
|
self
|
|
end
|
|
|
|
#
|
|
# True if the stream is closed.
|
|
#
|
|
def closed?
|
|
advance!
|
|
@stream.closed?
|
|
end
|
|
|
|
def default_value
|
|
"".encode(encoding)
|
|
end
|
|
|
|
#
|
|
# Linewise iteration.
|
|
#
|
|
# Yields one line from stream at a time, as given by
|
|
# #gets. An Enumerator is returned if no block is
|
|
# provided. Returns nil if no content, self otherwise.
|
|
#
|
|
# @see #gets.
|
|
#
|
|
def each_line(sep=$/)
|
|
return to_enum :each_line, sep unless block_given?
|
|
return nil unless advance!
|
|
|
|
while line = gets(sep)
|
|
yield line
|
|
end
|
|
self
|
|
end
|
|
alias_method :lines, :each_line
|
|
alias_method :each, :each_line
|
|
|
|
#
|
|
# Bytewise iteration.
|
|
#
|
|
# Yields one byte at a time from stream, an Integer
|
|
# as given by #getc. An Enumerator is returned if no
|
|
# block is provided. Returns self.
|
|
#
|
|
# @see #getc
|
|
#
|
|
def each_byte
|
|
return to_enum :each_byte unless block_given?
|
|
while ch = getbyte()
|
|
yield ch
|
|
end
|
|
self
|
|
end
|
|
alias_method :bytes, :each_byte
|
|
|
|
#
|
|
# Character-wise iteration.
|
|
#
|
|
# Yields one character at a time from stream. An
|
|
# Enumerator is returned if no block is provided.
|
|
# Returns self.
|
|
#
|
|
# The characters yielded are gotten from #getc.
|
|
#
|
|
# @see #getc
|
|
#
|
|
def each_char
|
|
return to_enum :each_char unless block_given?
|
|
while c = getc()
|
|
yield c.chr
|
|
end
|
|
self
|
|
end
|
|
alias_method :chars, :each_char
|
|
|
|
def each_codepoint
|
|
return to_enum :each_codepoint unless block_given?
|
|
|
|
while c = getc
|
|
yield c.ord
|
|
end
|
|
|
|
self
|
|
end
|
|
alias_method :codepoints, :each_codepoint
|
|
|
|
def encoding
|
|
@external || Encoding.default_external
|
|
end
|
|
|
|
#
|
|
# Query whether stream is at end-of-file.
|
|
#
|
|
# True if there is a stream and it is in EOF
|
|
# status.
|
|
#
|
|
def eof?
|
|
@stream and @stream.eof?
|
|
end
|
|
alias_method :eof, :eof?
|
|
|
|
#
|
|
# File descriptor number for stream.
|
|
#
|
|
# Returns a file descriptor number for the stream being
|
|
# read out of.
|
|
#
|
|
# @todo Check correctness, does this imply there may be
|
|
# multiple FDs and if so, is this correct? --rue
|
|
#
|
|
def fileno
|
|
raise ArgumentError, "No stream" unless advance!
|
|
@stream.fileno
|
|
end
|
|
alias_method :to_i, :fileno
|
|
|
|
#
|
|
# File path currently in use.
|
|
#
|
|
# Path to file from which read currently is
|
|
# occurring, or an indication that the stream
|
|
# is STDIN.
|
|
#
|
|
def filename
|
|
advance!
|
|
@filename
|
|
end
|
|
alias_method :path, :filename
|
|
|
|
#
|
|
# Current stream object.
|
|
#
|
|
# This may change during the course of execution,
|
|
# but is the current one!
|
|
#
|
|
def file
|
|
advance!
|
|
@stream
|
|
end
|
|
|
|
def getbyte
|
|
while true
|
|
return nil unless advance!
|
|
if val = @stream.getbyte
|
|
return val
|
|
end
|
|
|
|
return nil if @use_stdin_only
|
|
@stream.close unless @stream.closed?
|
|
@advance = true
|
|
end
|
|
end
|
|
|
|
#
|
|
# Return one character from stream.
|
|
#
|
|
# If a character cannot be returned and we are
|
|
# reading from a file, the stream is closed.
|
|
#
|
|
def getc
|
|
while true
|
|
return nil unless advance!
|
|
if val = @stream.getc
|
|
return val
|
|
end
|
|
|
|
return nil if @use_stdin_only
|
|
@stream.close unless @stream.closed?
|
|
@advance = true
|
|
end
|
|
end
|
|
|
|
#
|
|
# Return next line of text from stream.
|
|
#
|
|
# If a line cannot be returned and we are
|
|
# reading from a file, the stream is closed.
|
|
#
|
|
# The mechanism does track the line numbers,
|
|
# and updates $. accordingly.
|
|
#
|
|
def gets(sep=$/)
|
|
while true
|
|
return nil unless advance!
|
|
line = @stream.gets(sep)
|
|
|
|
unless line
|
|
return nil if @use_stdin_only
|
|
@stream.close unless @stream.closed?
|
|
@advance = true
|
|
next
|
|
end
|
|
|
|
@lineno += 1
|
|
$. = @lineno
|
|
return line
|
|
end
|
|
end
|
|
|
|
#
|
|
# Return current line number.
|
|
#
|
|
# Line numbers are maintained when using the linewise
|
|
# access methods.
|
|
#
|
|
# @see #gets
|
|
# @see #each_line
|
|
#
|
|
attr_reader :lineno
|
|
|
|
#
|
|
# Set current line number.
|
|
#
|
|
# Also sets $. accordingly.
|
|
#
|
|
# @todo Should this be public? --rue
|
|
#
|
|
def lineno=(val)
|
|
$. = @lineno = val
|
|
end
|
|
|
|
#
|
|
# Return stream position for seeking etc.
|
|
#
|
|
# @see IO#pos.
|
|
#
|
|
def pos
|
|
raise ArgumentError, "no stream" unless advance!
|
|
@stream.tell
|
|
end
|
|
alias_method :tell, :pos
|
|
|
|
#
|
|
# Set stream position to a previously obtained position.
|
|
#
|
|
# @see IO#pos=
|
|
#
|
|
def pos=(position)
|
|
raise ArgumentError, "no stream" unless advance!
|
|
@stream.pos = position
|
|
end
|
|
|
|
#
|
|
# Read a byte from stream.
|
|
#
|
|
# Similar to #getc, but raises an EOFError if
|
|
# EOF has been reached.
|
|
#
|
|
# @see #getc
|
|
#
|
|
def readbyte
|
|
advance!
|
|
|
|
if val = getc()
|
|
return val
|
|
end
|
|
|
|
raise EOFError, "ARGF at end"
|
|
end
|
|
alias_method :readchar, :readbyte
|
|
|
|
#
|
|
# Read number of bytes or all, optionally into buffer.
|
|
#
|
|
# If number of bytes is not given or is nil, tries to read
|
|
# all of the stream, which is then closed. If the number is
|
|
# specified, then at most that many bytes will be read.
|
|
#
|
|
# A buffer responding to #<< may be provided as the second
|
|
# argument. The data read is pushed into it. If no buffer
|
|
# is provided, as by default, a String with the data is
|
|
# returned instead.
|
|
#
|
|
def read(bytes=nil, output=nil)
|
|
# The user might try to pass in nil, so we have to check here
|
|
output ||= default_value
|
|
output.clear
|
|
|
|
if bytes
|
|
bytes_left = bytes
|
|
|
|
until bytes_left == 0
|
|
return output unless advance!
|
|
|
|
if res = @stream.read(bytes_left)
|
|
output << res
|
|
bytes_left -= res.size
|
|
else
|
|
break if @use_stdin_only
|
|
@stream.close unless @stream.closed?
|
|
@advance = true
|
|
end
|
|
|
|
end
|
|
|
|
return output
|
|
end
|
|
|
|
while advance!
|
|
output << @stream.read
|
|
|
|
break if @use_stdin_only
|
|
@stream.close unless @stream.closed?
|
|
@advance = true
|
|
end
|
|
|
|
output
|
|
end
|
|
|
|
def read_nonblock(maxlen, output = nil, exception: true)
|
|
output ||= default_value
|
|
|
|
unless advance!
|
|
output.clear
|
|
raise EOFError, "ARGF at end"
|
|
end
|
|
|
|
begin
|
|
out = @stream.read_nonblock(maxlen, output, exception: exception)
|
|
|
|
return out if out == :wait_readable
|
|
rescue EOFError => e
|
|
raise e if @use_stdin_only
|
|
|
|
@stream.close
|
|
@advance = true
|
|
advance! or raise e
|
|
end
|
|
|
|
return output
|
|
end
|
|
|
|
#
|
|
# Read next line of text.
|
|
#
|
|
# As #gets, but an EOFError is raised if the stream
|
|
# is at EOF.
|
|
#
|
|
# @see #gets
|
|
#
|
|
def readline(sep=$/)
|
|
raise EOFError, "ARGF at end" unless advance!
|
|
|
|
if line = gets(sep)
|
|
return line
|
|
end
|
|
|
|
raise EOFError, "ARGF at end"
|
|
end
|
|
|
|
#
|
|
# Read all lines from stream.
|
|
#
|
|
# Reads all lines into an Array using #gets and
|
|
# returns the Array.
|
|
#
|
|
# @see #gets
|
|
#
|
|
def readlines(sep=$/)
|
|
return [] unless advance!
|
|
|
|
lines = []
|
|
while line = gets(sep)
|
|
lines << line
|
|
end
|
|
|
|
lines
|
|
end
|
|
|
|
alias_method :to_a, :readlines
|
|
|
|
def readpartial(maxlen, output=nil)
|
|
output ||= default_value
|
|
|
|
unless advance!
|
|
output.clear
|
|
raise EOFError, "ARGF at end"
|
|
end
|
|
|
|
begin
|
|
@stream.readpartial(maxlen, output)
|
|
rescue EOFError => e
|
|
raise e if @use_stdin_only
|
|
|
|
@stream.close
|
|
@advance = true
|
|
advance! or raise e
|
|
end
|
|
|
|
return output
|
|
end
|
|
|
|
#
|
|
# Rewind the stream to its beginning.
|
|
#
|
|
# Line number is updated accordingly.
|
|
#
|
|
# @todo Is this correct, only current stream is rewound? --rue
|
|
#
|
|
def rewind
|
|
raise ArgumentError, "no stream to rewind" unless advance!
|
|
@lineno -= @stream.lineno
|
|
@stream.rewind
|
|
end
|
|
|
|
#
|
|
# Seek into a previous position in the stream.
|
|
#
|
|
# @see IO#seek.
|
|
#
|
|
def seek(*args)
|
|
raise ArgumentError, "no stream" unless advance!
|
|
@stream.seek(*args)
|
|
end
|
|
|
|
def set_encoding(*args)
|
|
@encoding_args = args
|
|
if @stream and !@stream.closed?
|
|
@stream.set_encoding *args
|
|
end
|
|
end
|
|
|
|
#
|
|
# Close file stream and return self.
|
|
#
|
|
# STDIN is not closed if being used, otherwise the
|
|
# stream gets closed. Returns self.
|
|
#
|
|
def skip
|
|
return self if @use_stdin_only
|
|
@stream.close unless @stream.closed?
|
|
@advance = true
|
|
self
|
|
end
|
|
|
|
def stream(file)
|
|
stream = file == "-" ? STDIN : File.open(file, "r", :external_encoding => encoding)
|
|
|
|
if @encoding_args
|
|
stream.set_encoding *@encoding_args
|
|
elsif encoding
|
|
stream.set_encoding encoding
|
|
end
|
|
|
|
stream
|
|
end
|
|
|
|
#
|
|
# Return IO object for current stream.
|
|
#
|
|
# @see IO#to_io
|
|
#
|
|
def to_io
|
|
advance!
|
|
@stream.to_io
|
|
end
|
|
|
|
#
|
|
# Returns "ARGF" as the string representation of this object.
|
|
#
|
|
def to_s
|
|
"ARGF"
|
|
end
|
|
|
|
|
|
# Internals
|
|
|
|
#
|
|
# Main processing.
|
|
#
|
|
# If not initialised yet, sets the object up on either
|
|
# first of provided file names or STDIN.
|
|
#
|
|
# Does nothing further or later if using STDIN, but if
|
|
# there are further file names in ARGV, tries to open
|
|
# the next one as the current stream.
|
|
#
|
|
def advance!
|
|
return true unless @advance
|
|
|
|
unless @init
|
|
|
|
if ARGV.empty?
|
|
@advance = false
|
|
@stream = STDIN
|
|
@filename = "-"
|
|
@use_stdin_only = true
|
|
return true
|
|
end
|
|
@init = true
|
|
end
|
|
|
|
File.unlink(@backup_filename) if @backup_filename && $-i == ""
|
|
|
|
return false if @use_stdin_only || ARGV.empty?
|
|
|
|
@advance = false
|
|
|
|
file = ARGV.shift
|
|
@stream = stream(file)
|
|
@filename = file
|
|
|
|
if $-i && @stream != STDIN
|
|
backup_extension = $-i == "" ? ".bak" : $-i
|
|
@backup_filename = "#{@filename}#{backup_extension}"
|
|
File.rename(@filename, @backup_filename)
|
|
@stream = File.open(@backup_filename, "r")
|
|
$stdout = File.open(@filename, "w")
|
|
end
|
|
|
|
return true
|
|
end
|
|
private :advance!
|
|
end
|
|
end
|