mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
7b9d1da3fc
Added complete call-seq documentation for the entire mruby-io gem across both Ruby and C implementations: ## Ruby Methods (mrblib/) - 50 methods documented: ### Kernel Module (kernel.rb): - Backtick operator: shell command execution with output capture - open: unified file/subprocess opening with pipe support - p: debug output with inspect formatting and multiple argument handling - print/puts/printf: output methods with proper formatting and separators - gets/readline/readlines: input methods with various line handling options ### File Constants (file_constants.rb): - FNM_* constants: file name matching flags for glob and fnmatch operations with detailed explanations of case sensitivity, escaping, and pattern behavior ### IO Class (io.rb): Class methods: - IO.open: creates IO objects with automatic resource management - IO.popen: subprocess communication with pipe handling - IO.pipe: creates connected pipe endpoints for IPC - IO.read: convenience method for reading entire files Instance methods: - Stream positioning: pos=, rewind, tell with proper seeking behavior - Iteration: each, each_byte, each_char with enumerator support - Output: puts, print, printf with formatting and newline handling - Utility: hash, <<, ungetbyte with proper stream manipulation - Global streams: STDIN/STDOUT/STDERR and $stdin/$stdout/$stderr ### File Class (file.rb): Instance methods: - Constructor: handles both file paths and file descriptors - Timestamps: atime, ctime, mtime with proper Time object conversion - Inspection: inspect method for debugging file objects Class methods: - Path utilities: join with cross-platform separator handling - File iteration: foreach with block and enumerator support - FileTest delegation: complete set of file type and existence checks (directory?, exist?, file?, pipe?, size, socket?, symlink?, zero?) - Path manipulation: extname for extension extraction, path for conversion ## C Methods (src/) - 25 methods documented: ### Core IO Operations (io.c): - File descriptor management: fileno with proper error handling - Stream state: closed?, eof?, sync/sync= for buffering control - Process management: pid for pipe process tracking - Resource management: close_on_exec?/close_on_exec= for FD_CLOEXEC handling ### Reading Operations: - Character reading: getc, readchar with EOF handling differences - Byte reading: getbyte, readbyte with integer conversion - Buffer reading: read with length and output buffer support - Stream manipulation: ungetc for character pushback ### System Operations: - IO multiplexing: IO.select for monitoring multiple streams - Constructor: IO.new for creating IO objects from file descriptors - Stream flushing: flush for forcing output to OS Co-authored-by: Atlassian Rovo Dev
399 lines
9.6 KiB
Ruby
399 lines
9.6 KiB
Ruby
##
|
|
# 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
|
|
|
|
#
|
|
# call-seq:
|
|
# ios << obj -> ios
|
|
#
|
|
# String Output - Writes obj to ios. obj will be converted to a string using
|
|
# to_s.
|
|
#
|
|
# $stdout << "Hello " << "world!\n"
|
|
# Hello world!
|
|
#
|
|
def <<(str)
|
|
write(str)
|
|
self
|
|
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.ungetbyte(string) -> nil
|
|
# ios.ungetbyte(integer) -> nil
|
|
#
|
|
# Pushes back bytes (passed as a parameter) onto ios, such that a subsequent
|
|
# buffered character read will return it. Only one byte may be pushed back
|
|
# before a subsequent read operation (that is, you will be able to read only
|
|
# the last of several bytes that have been pushed back). Has no effect with
|
|
# unbuffered reads (such as IO#sysread).
|
|
#
|
|
# f = File.new("testfile") #=> #<File:testfile>
|
|
# b = f.getbyte #=> 0x38
|
|
# f.ungetbyte(b) #=> nil
|
|
# f.getbyte #=> 0x38
|
|
#
|
|
def ungetbyte(c)
|
|
if c.is_a? String
|
|
c = c.getbyte(0)
|
|
else
|
|
c &= 0xff
|
|
end
|
|
s = " "
|
|
s.setbyte(0,c)
|
|
ungetc s
|
|
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 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.puts(obj, ...) -> nil
|
|
#
|
|
# Writes the given objects to ios as strings, each followed by a newline
|
|
# character unless the string already ends with a newline. If called with
|
|
# an array argument, writes each element on a new line. If called without
|
|
# arguments, outputs a single newline.
|
|
#
|
|
# $stdout.puts("this", "is", "a", "test")
|
|
# this
|
|
# is
|
|
# a
|
|
# test
|
|
#
|
|
def puts(*args)
|
|
i = 0
|
|
len = args.size
|
|
if len == 0
|
|
write "\n"
|
|
return
|
|
end
|
|
while i < len
|
|
s = args[i]
|
|
if s.kind_of?(Array)
|
|
puts(*s) if s.size > 0
|
|
else
|
|
s = s.to_s
|
|
write s
|
|
write "\n" if (s[-1] != "\n")
|
|
end
|
|
i += 1
|
|
end
|
|
nil
|
|
end
|
|
|
|
#
|
|
# call-seq:
|
|
# ios.print() -> nil
|
|
# ios.print(obj, ...) -> nil
|
|
#
|
|
# Writes the given object(s) to ios. Objects that aren't strings will be
|
|
# converted by calling their to_s method. With no argument, prints the
|
|
# contents of the variable $_.
|
|
#
|
|
# $stdout.print("This is ", 100, " percent.\n")
|
|
# This is 100 percent.
|
|
#
|
|
def print(*args)
|
|
i = 0
|
|
len = args.size
|
|
while i < len
|
|
write args[i].to_s
|
|
i += 1
|
|
end
|
|
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
|