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) # if defined(HAVE_FDOPENDIR) && defined(HAVE_DIRFD)
/* /*
* call-seq: * call-seq:
* Dir.for_fd(integer) -> aDir * Dir.for_fd(fd) -> dir
* *
* Returns a Dir representing the directory specified by the given * Returns a new \Dir object representing the directory specified by the given
* directory file descriptor. Note that the returned Dir will not * integer directory file descriptor +fd+:
* have an associated path.
* *
* d1 = Dir.new('..') * d0 = Dir.new('..')
* d2 = Dir.for_fd(d1.fileno) * d1 = Dir.for_fd(d0.fileno)
* d1.path # => '..'
* d2.path # => nil
* d1.chdir{Dir.pwd} == d2.chdir{Dir.pwd} # => true
* *
* This method uses fdopendir() function defined by POSIX 2008. * Note that the returned +d1+ does not have an associated path:
* NotImplementedError is raised on other platforms, such as Windows,
* which doesn't provide the function.
* *
* 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 static VALUE
dir_s_for_fd(VALUE klass, VALUE fd) dir_s_for_fd(VALUE klass, VALUE fd)
@ -653,10 +654,13 @@ dir_check(VALUE dir)
/* /*
* call-seq: * call-seq:
* dir.inspect -> string * inspect -> string
*
* Returns a string description of +self+:
*
* Dir.new('example').inspect # => "#<Dir:example>"
* *
* Return a string describing this Dir object.
*/ */
static VALUE static VALUE
dir_inspect(VALUE dir) dir_inspect(VALUE dir)
@ -690,18 +694,18 @@ dir_inspect(VALUE dir)
#ifdef HAVE_DIRFD #ifdef HAVE_DIRFD
/* /*
* call-seq: * call-seq:
* dir.fileno -> integer * fileno -> integer
* *
* Returns the file descriptor used in <em>dir</em>. * Returns the file descriptor used in <em>dir</em>.
* *
* d = Dir.new("..") * d = Dir.new('..')
* d.fileno #=> 8 * 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.
* *
* 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 static VALUE
dir_fileno(VALUE dir) dir_fileno(VALUE dir)
@ -720,14 +724,14 @@ dir_fileno(VALUE dir)
#endif #endif
/* /*
* call-seq: * call-seq:
* dir.path -> string or nil * path -> string or nil
* dir.to_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 static VALUE
dir_path(VALUE dir) dir_path(VALUE dir)
@ -781,16 +785,18 @@ to_be_skipped(const struct dirent *dp)
} }
/* /*
* call-seq: * call-seq:
* dir.read -> string or nil * read -> string or nil
* *
* Reads the next entry from <em>dir</em> and returns it as a string. * Reads and returns the next entry name from +self+;
* Returns <code>nil</code> at the end of the stream. * 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 static VALUE
dir_read(VALUE dir) dir_read(VALUE dir)
@ -819,24 +825,23 @@ dir_yield(VALUE arg, VALUE path)
} }
/* /*
* call-seq: * call-seq:
* dir.each { |filename| block } -> dir * each {|entry_name| ... } -> self
* dir.each -> an_enumerator
* *
* Calls the block once for each entry in this directory, passing the * Calls the block with each entry name in +self+:
* filename of each entry as a parameter to the block.
* *
* If no block is given, an enumerator is returned instead. * Dir.new('example').each {|entry_name| p entry_name }
* *
* d = Dir.new("testdir") * Output:
* d.each {|x| puts "Got #{x}" }
* "."
* ".."
* "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 static VALUE
dir_each(VALUE dir) 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 #ifdef HAVE_TELLDIR
/* /*
* call-seq: * call-seq:
* dir.pos -> integer * tell -> integer
* dir.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 static VALUE
dir_tell(VALUE dir) dir_tell(VALUE dir)
@ -906,18 +912,24 @@ dir_tell(VALUE dir)
#ifdef HAVE_SEEKDIR #ifdef HAVE_SEEKDIR
/* /*
* call-seq: * call-seq:
* dir.seek( integer ) -> dir * seek(position) -> self
* *
* Seeks to a particular location in <em>dir</em>. <i>integer</i> * Sets the position in +self+ and returns +self+.
* must be a value returned by Dir#tell. * 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 static VALUE
dir_seek(VALUE dir, VALUE pos) dir_seek(VALUE dir, VALUE pos)
@ -935,17 +947,24 @@ dir_seek(VALUE dir, VALUE pos)
#ifdef HAVE_SEEKDIR #ifdef HAVE_SEEKDIR
/* /*
* call-seq: * call-seq:
* dir.pos = integer -> integer * 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 static VALUE
dir_set_pos(VALUE dir, VALUE pos) dir_set_pos(VALUE dir, VALUE pos)
@ -958,15 +977,19 @@ dir_set_pos(VALUE dir, VALUE pos)
#endif #endif
/* /*
* call-seq: * call-seq:
* dir.rewind -> dir * 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 static VALUE
dir_rewind(VALUE dir) dir_rewind(VALUE dir)
@ -979,14 +1002,18 @@ dir_rewind(VALUE dir)
} }
/* /*
* call-seq: * call-seq:
* dir.close -> nil * close -> nil
* *
* Closes the directory stream. * Closes the stream in +self+, if it is open, and returns +nil+;
* Calling this method on closed Dir object is ignored since Ruby 2.3. * 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 static VALUE
dir_close(VALUE dir) dir_close(VALUE dir)
@ -1050,44 +1077,67 @@ chdir_restore(VALUE v)
} }
/* /*
* call-seq: * call-seq:
* Dir.chdir( [ string] ) -> 0 * Dir.chdir(new_dirpath) -> 0
* Dir.chdir( [ string] ) {| path | block } -> anObject * 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 * Changes the current working directory.
* 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.
* *
* If a block is given, it is passed the name of the new current * With argument +new_dirpath+ and no block,
* directory, and the block is executed with that as the current * changes to the given +dirpath+:
* 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).
* *
* Dir.chdir("/var/spool/mail") * Dir.pwd # => "/example"
* puts Dir.pwd * Dir.chdir('..') # => 0
* Dir.chdir("/tmp") do * Dir.pwd # => "/"
* puts Dir.pwd *
* Dir.chdir("/usr") do * With no argument and no block:
* puts Dir.pwd *
* end * - Changes to the value of environment variable +HOME+ if defined.
* puts Dir.pwd * - 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 * 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 * Raises an exception if the target directory does not exist.
* /tmp
* /usr
* /tmp
* /var/spool/mail
*/ */
static VALUE static VALUE
dir_s_chdir(int argc, VALUE *argv, VALUE obj) dir_s_chdir(int argc, VALUE *argv, VALUE obj)
@ -1181,52 +1231,56 @@ fchdir_restore(VALUE v)
} }
/* /*
* call-seq: * call-seq:
* Dir.fchdir( integer ) -> 0 * Dir.fchdir(fd) -> 0
* Dir.fchdir( integer ) { block } -> anObject * Dir.fchdir(fd) { ... } -> object
* *
* Changes the current working directory of the process to the directory * Changes the current working directory to the directory
* specified by the given file descriptor integer. If the file descriptor * specified by the integer file descriptor +fd+.
* 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.
* *
* If a block is given, the current working directory is changed for the * When passing a file descriptor over a UNIX socket or to a child process,
* duration of the block, and the original working directory is restored * using +fchdir+ instead of +chdir+ avoids the
* when the block exits. The return value of <code>fchdir</code> is the * {time-of-check to time-of-use vulnerability}[https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use]
* 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 generating directory file descriptors from a +Dir+ instance, * With no block, changes to the directory given by +fd+:
* 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.
* *
* dir = Dir.new("/var/spool/mail") * Dir.chdir('/var/spool/mail')
* dir2 = Dir.new("/usr") * Dir.pwd # => "/var/spool/mail"
* fd = dir.fileno * dir = Dir.new('/usr')
* fd2 = dir2.fileno * fd = dir.fileno
* Dir.fchdir(fd) do * Dir.fchdir(fd) do
* puts Dir.pwd * Dir.pwd # => "/usr"
* Dir.fchdir(fd2) do * end
* puts Dir.pwd * Dir.pwd # => "/var/spool/mail"
* end
* puts Dir.pwd
* end
* puts Dir.pwd
* *
* <em>produces:</em> * With a block, temporarily changes the working directory:
* *
* /var/spool/mail * - Calls the block with the argument.
* /tmp * - Changes to the given directory.
* /usr * - Executes the block
* /tmp * - Restores the previous working directory.
* /var/spool/mail * - 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 static VALUE
dir_s_fchdir(VALUE klass, VALUE fd_value) dir_s_fchdir(VALUE klass, VALUE fd_value)
@ -1262,14 +1316,16 @@ dir_s_fchdir(VALUE klass, VALUE fd_value)
#endif #endif
/* /*
* call-seq: * call-seq:
* dir.chdir -> nil * 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 static VALUE
dir_chdir(VALUE dir) dir_chdir(VALUE dir)