This commit is contained in:
Burdette Lamar 2023-07-05 08:45:54 -05:00 коммит произвёл GitHub
Родитель 1f9618fc95
Коммит 00f9231534
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
1 изменённых файлов: 226 добавлений и 170 удалений

396
dir.c
Просмотреть файл

@ -590,23 +590,24 @@ dir_s_close(rb_execution_context_t *ec, VALUE klass, VALUE dir)
# if defined(HAVE_FDOPENDIR) && defined(HAVE_DIRFD)
/*
* call-seq:
* Dir.for_fd(integer) -> aDir
* call-seq:
* Dir.for_fd(fd) -> dir
*
* Returns a Dir representing the directory specified by the given
* directory file descriptor. Note that the returned Dir will not
* have an associated path.
* Returns a new \Dir object representing the directory specified by the given
* integer directory file descriptor +fd+:
*
* d1 = Dir.new('..')
* d2 = Dir.for_fd(d1.fileno)
* d1.path # => '..'
* d2.path # => nil
* d1.chdir{Dir.pwd} == d2.chdir{Dir.pwd} # => true
* d0 = Dir.new('..')
* d1 = Dir.for_fd(d0.fileno)
*
* This method uses fdopendir() function defined by POSIX 2008.
* NotImplementedError is raised on other platforms, such as Windows,
* which doesn't provide the function.
* Note that the returned +d1+ does not have an associated path:
*
* d0.path # => '..'
* d1.path # => nil
*
* This method uses the
* {fdopendir()}[https://www.man7.org/linux/man-pages/man3/fdopendir.3p.html]
* function defined by POSIX 2008;
* the method is not implemented on non-POSIX platforms (raises NotImplementedError).
*/
static VALUE
dir_s_for_fd(VALUE klass, VALUE fd)
@ -653,10 +654,13 @@ dir_check(VALUE dir)
/*
* call-seq:
* dir.inspect -> string
* call-seq:
* inspect -> string
*
* Returns a string description of +self+:
*
* Dir.new('example').inspect # => "#<Dir:example>"
*
* Return a string describing this Dir object.
*/
static VALUE
dir_inspect(VALUE dir)
@ -690,18 +694,18 @@ dir_inspect(VALUE dir)
#ifdef HAVE_DIRFD
/*
* call-seq:
* dir.fileno -> integer
* call-seq:
* fileno -> integer
*
* Returns the file descriptor used in <em>dir</em>.
* Returns the file descriptor used in <em>dir</em>.
*
* d = Dir.new("..")
* d.fileno #=> 8
*
* This method uses dirfd() function defined by POSIX 2008.
* NotImplementedError is raised on other platforms, such as Windows,
* which doesn't provide the function.
* d = Dir.new('..')
* d.fileno # => 8
*
* This method uses the
* {dirfd()}[https://www.man7.org/linux/man-pages/man3/dirfd.3.html]
* function defined by POSIX 2008;
* the method is not implemented on non-POSIX platforms (raises NotImplementedError).
*/
static VALUE
dir_fileno(VALUE dir)
@ -720,14 +724,14 @@ dir_fileno(VALUE dir)
#endif
/*
* call-seq:
* dir.path -> string or nil
* dir.to_path -> string or nil
* call-seq:
* path -> string or nil
*
* Returns the path parameter passed to <em>dir</em>'s constructor.
* Returns the +dirpath+ string that was used to create +self+
* (or +nil+ if created by method Dir.for_fd):
*
* Dir.new('example').path # => "example"
*
* d = Dir.new("..")
* d.path #=> ".."
*/
static VALUE
dir_path(VALUE dir)
@ -781,16 +785,18 @@ to_be_skipped(const struct dirent *dp)
}
/*
* call-seq:
* dir.read -> string or nil
* call-seq:
* read -> string or nil
*
* Reads the next entry from <em>dir</em> and returns it as a string.
* Returns <code>nil</code> at the end of the stream.
* Reads and returns the next entry name from +self+;
* returns +nil+ if at end-of-stream;
* see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
*
* dir = Dir.new('example')
* dir.read # => "."
* dir.read # => ".."
* dir.read # => "config.h"
*
* d = Dir.new("testdir")
* d.read #=> "."
* d.read #=> ".."
* d.read #=> "config.h"
*/
static VALUE
dir_read(VALUE dir)
@ -819,24 +825,23 @@ dir_yield(VALUE arg, VALUE path)
}
/*
* call-seq:
* dir.each { |filename| block } -> dir
* dir.each -> an_enumerator
* call-seq:
* each {|entry_name| ... } -> self
*
* Calls the block once for each entry in this directory, passing the
* filename of each entry as a parameter to the block.
* Calls the block with each entry name in +self+:
*
* If no block is given, an enumerator is returned instead.
* Dir.new('example').each {|entry_name| p entry_name }
*
* d = Dir.new("testdir")
* d.each {|x| puts "Got #{x}" }
* Output:
* "."
* ".."
* "config.h"
* "lib"
* "main.rb"
*
* <em>produces:</em>
* With no block given, returns an Enumerator.
*
* Got .
* Got ..
* Got config.h
* Got main.rb
*/
static VALUE
dir_each(VALUE dir)
@ -879,16 +884,17 @@ dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_o
#ifdef HAVE_TELLDIR
/*
* call-seq:
* dir.pos -> integer
* dir.tell -> integer
* call-seq:
* tell -> integer
*
* Returns the current position in <em>dir</em>. See also Dir#seek.
* Returns the current position of +self+;
* see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
*
* dir = Dir.new('example')
* dir.tell # => 0
* dir.read # => "."
* dir.tell # => 1
*
* d = Dir.new("testdir")
* d.tell #=> 0
* d.read #=> "."
* d.tell #=> 12
*/
static VALUE
dir_tell(VALUE dir)
@ -906,18 +912,24 @@ dir_tell(VALUE dir)
#ifdef HAVE_SEEKDIR
/*
* call-seq:
* dir.seek( integer ) -> dir
* call-seq:
* seek(position) -> self
*
* Seeks to a particular location in <em>dir</em>. <i>integer</i>
* must be a value returned by Dir#tell.
* Sets the position in +self+ and returns +self+.
* The value of +position+ should have been returned from an earlier call to #tell;
* if not, the return values from subsequent calls to #read are unspecified.
*
* See {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like].
*
* Examples:
*
* dir = Dir.new('example')
* dir.pos # => 0
* dir.seek(3) # => #<Dir:example>
* dir.pos # => 3
* dir.seek(30) # => #<Dir:example>
* dir.pos # => 5
*
* d = Dir.new("testdir") #=> #<Dir:0x401b3c40>
* d.read #=> "."
* i = d.tell #=> 12
* d.read #=> ".."
* d.seek(i) #=> #<Dir:0x401b3c40>
* d.read #=> ".."
*/
static VALUE
dir_seek(VALUE dir, VALUE pos)
@ -935,17 +947,24 @@ dir_seek(VALUE dir, VALUE pos)
#ifdef HAVE_SEEKDIR
/*
* call-seq:
* dir.pos = integer -> integer
* call-seq:
* pos = position -> integer
*
* Synonym for Dir#seek, but returns the position parameter.
* Sets the position in +self+ and returns +position+.
* The value of +position+ should have been returned from an earlier call to #tell;
* if not, the return values from subsequent calls to #read are unspecified.
*
* See {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like].
*
* Examples:
*
* dir = Dir.new('example')
* dir.pos # => 0
* dir.pos = 3 # => 3
* dir.pos # => 3
* dir.pos = 30 # => 30
* dir.pos # => 5
*
* d = Dir.new("testdir") #=> #<Dir:0x401b3c40>
* d.read #=> "."
* i = d.pos #=> 12
* d.read #=> ".."
* d.pos = i #=> 12
* d.read #=> ".."
*/
static VALUE
dir_set_pos(VALUE dir, VALUE pos)
@ -958,15 +977,19 @@ dir_set_pos(VALUE dir, VALUE pos)
#endif
/*
* call-seq:
* dir.rewind -> dir
* call-seq:
* rewind -> self
*
* Repositions <em>dir</em> to the first entry.
* Sets the position in +self+ to zero;
* see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
*
* dir = Dir.new('example')
* dir.read # => "."
* dir.read # => ".."
* dir.pos # => 2
* dir.rewind # => #<Dir:example>
* dir.pos # => 0
*
* d = Dir.new("testdir")
* d.read #=> "."
* d.rewind #=> #<Dir:0x401b3fb0>
* d.read #=> "."
*/
static VALUE
dir_rewind(VALUE dir)
@ -979,14 +1002,18 @@ dir_rewind(VALUE dir)
}
/*
* call-seq:
* dir.close -> nil
* call-seq:
* close -> nil
*
* Closes the directory stream.
* Calling this method on closed Dir object is ignored since Ruby 2.3.
* Closes the stream in +self+, if it is open, and returns +nil+;
* ignored if +self+ is already closed:
*
* dir = Dir.new('example')
* dir.read # => "."
* dir.close # => nil
* dir.close # => nil
* dir.read # Raises IOError.
*
* d = Dir.new("testdir")
* d.close #=> nil
*/
static VALUE
dir_close(VALUE dir)
@ -1050,44 +1077,67 @@ chdir_restore(VALUE v)
}
/*
* call-seq:
* Dir.chdir( [ string] ) -> 0
* Dir.chdir( [ string] ) {| path | block } -> anObject
* call-seq:
* Dir.chdir(new_dirpath) -> 0
* Dir.chdir -> 0
* Dir.chdir(new_dirpath) {|new_dirpath| ... } -> object
* Dir.chdir {|cur_dirpath| ... } -> object
*
* Changes the current working directory of the process to the given
* string. When called without an argument, changes the directory to
* the value of the environment variable <code>HOME</code>, or
* <code>LOGDIR</code>. SystemCallError (probably Errno::ENOENT) if
* the target directory does not exist.
* Changes the current working directory.
*
* If a block is given, it is passed the name of the new current
* directory, and the block is executed with that as the current
* directory. The original working directory is restored when the block
* exits. The return value of <code>chdir</code> is the value of the
* block. <code>chdir</code> blocks can be nested, but in a
* multi-threaded program an error will be raised if a thread attempts
* to open a <code>chdir</code> block while another thread has one
* open or a call to <code>chdir</code> without a block occurs inside
* a block passed to <code>chdir</code> (even in the same thread).
* With argument +new_dirpath+ and no block,
* changes to the given +dirpath+:
*
* Dir.chdir("/var/spool/mail")
* puts Dir.pwd
* Dir.chdir("/tmp") do
* puts Dir.pwd
* Dir.chdir("/usr") do
* puts Dir.pwd
* end
* puts Dir.pwd
* Dir.pwd # => "/example"
* Dir.chdir('..') # => 0
* Dir.pwd # => "/"
*
* With no argument and no block:
*
* - Changes to the value of environment variable +HOME+ if defined.
* - Otherwise changes to the value of environment variable +LOGDIR+ if defined.
* - Otherwise makes no change.
*
* With argument +new_dirpath+ and a block, temporarily changes the working directory:
*
* - Calls the block with the argument.
* - Changes to the given directory.
* - Executes the block
* - Restores the previous working directory.
* - Returns the block's return value.
*
* Example:
*
* Dir.chdir('/var/spool/mail')
* Dir.pwd # => "/var/spool/mail"
* Dir.chdir('/tmp') do
* Dir.pwd # => "/tmp"
* end
* Dir.pwd # => "/var/spool/mail"
*
* With no argument and a block,
* calls the block with the current working directory (string)
* and returns the block's return value.
*
* Calls to \Dir.chdir with blocks may be nested:
*
* Dir.chdir('/var/spool/mail')
* Dir.pwd # => "/var/spool/mail"
* Dir.chdir('/tmp') do
* Dir.pwd # => "/tmp"
* Dir.chdir('/usr') do
* Dir.pwd # => "/usr"
* end
* puts Dir.pwd
* Dir.pwd # => "/tmp"
* end
* Dir.pwd # => "/var/spool/mail"
*
* <em>produces:</em>
* In a multi-threaded program an error is raised if a thread attempts
* to open a +chdir+ block while another thread has one open,
* or a call to +chdir+ without a block occurs inside
* a block passed to +chdir+ (even in the same thread).
*
* /var/spool/mail
* /tmp
* /usr
* /tmp
* /var/spool/mail
* Raises an exception if the target directory does not exist.
*/
static VALUE
dir_s_chdir(int argc, VALUE *argv, VALUE obj)
@ -1181,52 +1231,56 @@ fchdir_restore(VALUE v)
}
/*
* call-seq:
* Dir.fchdir( integer ) -> 0
* Dir.fchdir( integer ) { block } -> anObject
* call-seq:
* Dir.fchdir(fd) -> 0
* Dir.fchdir(fd) { ... } -> object
*
* Changes the current working directory of the process to the directory
* specified by the given file descriptor integer. If the file descriptor
* is not valid, raises SystemCallError. One reason to use
* <code>fchdir</code> instead of <code>chdir</code> is when passing
* directory file descriptors over a UNIX socket or to child processes,
* to avoid TOCTOU (time-of-check to time-of-use) vulnerabilities.
* Changes the current working directory to the directory
* specified by the integer file descriptor +fd+.
*
* If a block is given, the current working directory is changed for the
* duration of the block, and the original working directory is restored
* when the block exits. The return value of <code>fchdir</code> is the
* value of the block. <code>fchdir</code> and <code>chdir</code> blocks
* can be nested, but in a multi-threaded program an error will be raised
* if a thread attempts to open a <code>fchdir</code> or <code>chdir</code>
* block while another thread has one open or a call to <code>fchdir</code>
* or <code>chdir</code> without a block occurs inside a block passed to
* <code>fchdir</code> or <code>chdir</code> (even in the same thread).
* When passing a file descriptor over a UNIX socket or to a child process,
* using +fchdir+ instead of +chdir+ avoids the
* {time-of-check to time-of-use vulnerability}[https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use]
*
* When generating directory file descriptors from a +Dir+ instance,
* make sure the +Dir+ instance is not garbage collected before the
* directory file descriptor is passed to another process. Otherwise,
* the directory file descriptor will be closed before it is passed.
* With no block, changes to the directory given by +fd+:
*
* dir = Dir.new("/var/spool/mail")
* dir2 = Dir.new("/usr")
* fd = dir.fileno
* fd2 = dir2.fileno
* Dir.fchdir(fd) do
* puts Dir.pwd
* Dir.fchdir(fd2) do
* puts Dir.pwd
* end
* puts Dir.pwd
* end
* puts Dir.pwd
* Dir.chdir('/var/spool/mail')
* Dir.pwd # => "/var/spool/mail"
* dir = Dir.new('/usr')
* fd = dir.fileno
* Dir.fchdir(fd) do
* Dir.pwd # => "/usr"
* end
* Dir.pwd # => "/var/spool/mail"
*
* <em>produces:</em>
* With a block, temporarily changes the working directory:
*
* /var/spool/mail
* /tmp
* /usr
* /tmp
* /var/spool/mail
* - Calls the block with the argument.
* - Changes to the given directory.
* - Executes the block
* - Restores the previous working directory.
* - Returns the block's return value.
*
* Example:
*
* Dir.chdir('/var/spool/mail')
* Dir.pwd # => "/var/spool/mail"
* Dir.chdir('/tmp') do
* Dir.pwd # => "/tmp"
* end
* Dir.pwd # => "/var/spool/mail"
*
* This method uses the
* {fchdir()}[https://www.man7.org/linux/man-pages/man3/fchdir.3p.html]
* function defined by POSIX 2008;
* the method is not implemented on non-POSIX platforms (raises NotImplementedError).
*
* Raises an exception if the file descriptor is not valid.
*
* In a multi-threaded program an error is raised if a thread attempts
* to open a +chdir+ block while another thread has one open,
* or a call to +chdir+ without a block occurs inside
* a block passed to +chdir+ (even in the same thread).
*/
static VALUE
dir_s_fchdir(VALUE klass, VALUE fd_value)
@ -1262,14 +1316,16 @@ dir_s_fchdir(VALUE klass, VALUE fd_value)
#endif
/*
* call-seq:
* dir.chdir -> nil
* call-seq:
* chdir -> nil
*
* Changes the current working directory to the receiver.
* Changes the current working directory to the path of +self+:
*
* Dir.pwd # => "/"
* dir = Dir.new('example')
* dir.chdir
* dir.pwd # => "/example"
*
* # Assume current directory is /path
* Dir.new("testdir").chdir
* Dir.pwd # => '/path/testdir'
*/
static VALUE
dir_chdir(VALUE dir)