calog/vendor/mruby/mrbgems/mruby-io/mrblib/io.rb
2026-07-04 20:25:00 -05:00

303 lines
7.4 KiB
Ruby
Vendored

##
# IO
#
# ISO 15.2.20
class IOError < StandardError; end
class EOFError < IOError; end
class IO
#
# call-seq:
# IO.open(fd, mode="r" [, opt]) -> io
# IO.open(fd, mode="r" [, opt]) {|io| block } -> obj
#
# With no associated block, IO.open is a synonym for IO.new. If the optional
# code block is given, it will be passed io as an argument, and the IO object
# will automatically be closed when the block terminates. In this instance,
# IO.open returns the value of the block.
#
# fd = IO.sysopen("/dev/tty", "w")
# a = IO.open(fd,"w")
# $stderr.puts "Hello"
# a.close
#
def self.open(*args, &block)
io = self.new(*args)
return io unless block
begin
yield io
ensure
begin
io.close unless io.closed?
rescue StandardError
end
end
end
#
# call-seq:
# IO.popen(cmd, mode="r" [, opt]) -> io
# IO.popen(cmd, mode="r" [, opt]) {|io| block } -> obj
#
# Runs the specified command as a subprocess; the subprocess's standard input
# and output will be connected to the returned IO object.
#
# p IO.popen("date").read #=> "Wed Apr 9 08:56:30 CDT 2003\n"
# IO.popen("dc", "r+") {|f|
# f.puts "5 2 *"
# f.close_write
# puts f.read
# }
#
def self.popen(command, mode = 'r', **opts, &block)
if !self.respond_to?(:_popen)
raise NotImplementedError, "popen is not supported on this platform"
end
io = self._popen(command, mode, **opts)
return io unless block
begin
yield io
ensure
begin
io.close unless io.closed?
rescue IOError
# nothing
end
end
end
#
# call-seq:
# IO.pipe -> [read_io, write_io]
# IO.pipe {|read_io, write_io| ... } -> obj
#
# Creates a pair of pipe endpoints (connected to each other) and returns
# them as a two-element array of IO objects: [read_io, write_io].
#
# rd, wr = IO.pipe
# if fork
# wr.close
# puts rd.read
# rd.close
# Process.wait
# else
# rd.close
# wr.write "Hello, parent!"
# wr.close
# exit
# end
#
def self.pipe(&block)
if !self.respond_to?(:_pipe)
raise NotImplementedError, "pipe is not supported on this platform"
end
if block
begin
r, w = IO._pipe
yield r, w
ensure
r.close unless r.closed?
w.close unless w.closed?
end
else
IO._pipe
end
end
#
# call-seq:
# IO.read(name, [length [, offset]] ) -> string
# IO.read(name, [length [, offset]], mode: mode) -> string
#
# Opens the file, optionally seeks to the given offset, then returns length
# bytes (defaulting to the rest of the file). read ensures the file is
# closed before returning.
#
# IO.read("testfile") #=> "This is line one\nThis is line two\n"
# IO.read("testfile", 20) #=> "This is line one\nTh"
# IO.read("testfile", 20, 10) #=> "ne one\nThis is line "
#
def self.read(path, length=nil, offset=0, mode: "r")
str = ""
fd = -1
io = nil
begin
fd = IO.sysopen(path, mode)
io = IO.open(fd, mode)
io.seek(offset) if offset > 0
str = io.read(length)
ensure
if io
io.close
elsif fd != -1
IO._sysclose(fd)
end
end
str
end
#
# call-seq:
# ios.hash -> integer
#
# Compute a hash based on the IO object. Two IO objects with the same
# content will have the same hash code (and will compare using eql?).
# We must define IO#hash here because IO includes Enumerable and
# Enumerable#hash will call IO#read() otherwise.
#
def hash
# We must define IO#hash here because IO includes Enumerable and
# Enumerable#hash will call IO#read() otherwise
self.__id__
end
# Alias for eof?
alias_method :eof, :eof?
# Alias for pos
alias_method :tell, :pos
#
# call-seq:
# ios.pos = integer -> integer
#
# Seeks to the given position (in bytes) in ios. It is not guaranteed that
# seeking to the right position when ios is textmode.
#
# f = File.new("testfile")
# f.pos = 17
# f.gets #=> "This is line two\n"
#
def pos=(i)
seek(i, SEEK_SET)
end
#
# call-seq:
# ios.rewind -> 0
#
# Positions ios to the beginning of input, resetting lineno to zero.
#
# f = File.new("testfile")
# f.readline #=> "This is line one\n"
# f.rewind #=> 0
# f.lineno #=> 0
# f.readline #=> "This is line one\n"
#
def rewind
seek(0, SEEK_SET)
end
#
# call-seq:
# ios.each(sep=$/) {|line| block } -> ios
# ios.each(limit) {|line| block } -> ios
# ios.each(sep,limit) {|line| block } -> ios
# ios.each(...) -> an_enumerator
#
# Executes the block for every line in ios, where lines are separated by sep.
# ios must be opened for reading. If no block is given, an enumerator is returned instead.
#
# f = File.new("testfile")
# f.each {|line| puts "#{f.lineno}: #{line}" }
#
# 15.2.20.5.3
def each(&block)
return to_enum(:each) unless block
while line = self.gets
block.call(line)
end
self
end
#
# call-seq:
# ios.each_byte {|byte| block } -> ios
# ios.each_byte -> an_enumerator
#
# Calls the given block once for each byte (0..255) in ios, passing the byte
# as an argument. The stream must be opened for reading or an IOError will be raised.
#
# f = File.new("testfile")
# checksum = 0
# f.each_byte {|x| checksum ^= x } #=> #<File:testfile>
# checksum #=> 12
#
# 15.2.20.5.4
def each_byte(&block)
return to_enum(:each_byte) unless block
while byte = self.getbyte
block.call(byte)
end
self
end
# Alias for each - 15.2.20.5.5
alias each_line each
#
# call-seq:
# ios.each_char {|c| block } -> ios
# ios.each_char -> an_enumerator
#
# Calls the given block once for each character in ios, passing the character
# as an argument. The stream must be opened for reading or an IOError will be raised.
#
# f = File.new("testfile")
# ios.each_char {|c| print c, ' ' } #=> #<File:testfile>
#
def each_char(&block)
return to_enum(:each_char) unless block
while char = self.getc
block.call(char)
end
self
end
#
# call-seq:
# ios.printf(format_string [, obj, ...]) -> nil
#
# Formats and writes to ios, converting parameters under control of the format string.
# See sprintf for details of the format string.
#
# $stdout.printf "Number: %5.2f,\nString: %s\n", 1.23, "hello"
# Number: 1.23,
# String: hello
#
def printf(*args)
write sprintf(*args)
nil
end
# Alias for fileno - returns the integer file descriptor for ios
alias_method :to_i, :fileno
# Alias for isatty - returns true if ios is associated with a terminal device
alias_method :tty?, :isatty
end
# Standard input stream - connected to file descriptor 0
STDIN = IO.open(0, "r")
# Standard output stream - connected to file descriptor 1
STDOUT = IO.open(1, "w")
# Standard error stream - connected to file descriptor 2
STDERR = IO.open(2, "w")
# Global variable for standard input
$stdin = STDIN
# Global variable for standard output
$stdout = STDOUT
# Global variable for standard error
$stderr = STDERR