lib/file/find.rb in file-find-0.3.4 vs lib/file/find.rb in file-find-0.3.5
- old
+ new
@@ -2,503 +2,503 @@
require 'rbconfig'
# For alternate implementations of Ruby, such as JRuby, that cannot
# build C extensions fall back to the Etc module.
begin
- require 'sys/admin'
+ require 'sys/admin'
rescue LoadError
- require 'etc'
+ require 'etc'
end
class File::Find
- # The version of the file-find library
- VERSION = '0.3.4'
+ # The version of the file-find library
+ VERSION = '0.3.5'
- # :stopdoc:
- VALID_OPTIONS = %w/
- atime
- ctime
- follow
- ftype
- inum
- group
- links
- maxdepth
- mindepth
- mount
- mtime
- name
- pattern
- path
- perm
- prune
- size
- user
- /
- # :startdoc:
+ # :stopdoc:
+ VALID_OPTIONS = %w/
+ atime
+ ctime
+ follow
+ ftype
+ inum
+ group
+ links
+ maxdepth
+ mindepth
+ mount
+ mtime
+ name
+ pattern
+ path
+ perm
+ prune
+ size
+ user
+ /
+ # :startdoc:
- # The starting path(s) for the search. The default is the current directory.
- # This can be a single path or an array of paths.
- #
- attr_accessor :path
+ # The starting path(s) for the search. The default is the current directory.
+ # This can be a single path or an array of paths.
+ #
+ attr_accessor :path
- # The list of options passed to the constructor and/or used by the
- # File::Find#find method.
- #
- attr_accessor :options
+ # The list of options passed to the constructor and/or used by the
+ # File::Find#find method.
+ #
+ attr_accessor :options
- # Limits searches by file access time, where the value you supply is the
- # number of days back from the time that the File::Find#find method was
- # called.
- #
- attr_accessor :atime
+ # Limits searches by file access time, where the value you supply is the
+ # number of days back from the time that the File::Find#find method was
+ # called.
+ #
+ attr_accessor :atime
- # Limits searches by file change time, where the value you supply is the
- # number of days back from the time that the File::Find#find method was
- # called.
- #
- attr_accessor :ctime
+ # Limits searches by file change time, where the value you supply is the
+ # number of days back from the time that the File::Find#find method was
+ # called.
+ #
+ attr_accessor :ctime
- # Limits searches to files that belong to a specific group, where the
- # group can be either a group name or ID.
- #
- # Not currently supported on MS Windows.
- #
- attr_accessor :group
+ # Limits searches to files that belong to a specific group, where the
+ # group can be either a group name or ID.
+ #
+ # Not currently supported on MS Windows.
+ #
+ attr_accessor :group
- # An array of two element arrays for storing FileTest methods and their
- # boolean value.
- #
- attr_accessor :filetest
+ # An array of two element arrays for storing FileTest methods and their
+ # boolean value.
+ #
+ attr_accessor :filetest
- # Controls the behavior of how symlinks are followed. If set to true (the
- # default), then follows the file pointed to. If false, it considers the
- # symlink itself.
- #
- attr_accessor :follow
+ # Controls the behavior of how symlinks are followed. If set to true (the
+ # default), then follows the file pointed to. If false, it considers the
+ # symlink itself.
+ #
+ attr_accessor :follow
- # Limits searches to specific types of files. The possible values here are
- # those returned by the File.ftype method.
- #
- attr_accessor :ftype
+ # Limits searches to specific types of files. The possible values here are
+ # those returned by the File.ftype method.
+ #
+ attr_accessor :ftype
- # Limits search to a file with a specific inode number. Ignored on MS
- # Windows.
- #
- attr_accessor :inum
+ # Limits search to a file with a specific inode number. Ignored on MS
+ # Windows.
+ #
+ attr_accessor :inum
- # Limits search to files with the specified number of links.
- #
- attr_accessor :links
+ # Limits search to files with the specified number of links.
+ #
+ attr_accessor :links
- # Limits search to a maximum depth into the tree relative to the starting
- # search directory.
- #
- attr_accessor :maxdepth
+ # Limits search to a maximum depth into the tree relative to the starting
+ # search directory.
+ #
+ attr_accessor :maxdepth
- # Limits searches to a minimum depth into the tree relative to the starting
- # search directory.
- #
- attr_accessor :mindepth
+ # Limits searches to a minimum depth into the tree relative to the starting
+ # search directory.
+ #
+ attr_accessor :mindepth
- # Limits searches to the same filesystem as the specified directory. For
- # Windows users, this refers to the volume.
- #
- attr_reader :mount
+ # Limits searches to the same filesystem as the specified directory. For
+ # Windows users, this refers to the volume.
+ #
+ attr_reader :mount
- # Limits searches by file modification time, where the value you supply is
- # the number of days back from the time that the File::Find#find method was
- # called.
- #
- attr_accessor :mtime
+ # Limits searches by file modification time, where the value you supply is
+ # the number of days back from the time that the File::Find#find method was
+ # called.
+ #
+ attr_accessor :mtime
- # The name pattern used to limit file searches. The patterns that are legal
- # for Dir.glob are legal here. The default is '*', i.e. everything.
- #
- attr_accessor :name
+ # The name pattern used to limit file searches. The patterns that are legal
+ # for Dir.glob are legal here. The default is '*', i.e. everything.
+ #
+ attr_accessor :name
- # Limits searches to files which have permissions that match the octal
- # value that you provide. For purposes of this comparison, only the user,
- # group, and world settings are used. Do not use a leading 0 in the values
- # that you supply, e.g. use 755 not 0755.
- #
- # You may optionally use symbolic permissions, e.g. "g+rw", "u=rwx", etc.
- #
- # Not currently supported on MS Windows.
- #
- attr_accessor :perm
+ # Limits searches to files which have permissions that match the octal
+ # value that you provide. For purposes of this comparison, only the user,
+ # group, and world settings are used. Do not use a leading 0 in the values
+ # that you supply, e.g. use 755 not 0755.
+ #
+ # You may optionally use symbolic permissions, e.g. "g+rw", "u=rwx", etc.
+ #
+ # Not currently supported on MS Windows.
+ #
+ attr_accessor :perm
- # Skips files or directories that match the string provided as an argument.
- #
- attr_accessor :prune
+ # Skips files or directories that match the string provided as an argument.
+ #
+ attr_accessor :prune
- # If the value passed is an integer, this option limits searches to files
- # that match the size, in bytes, exactly. If a string is passed, you can
- # use the standard comparable operators to match files, e.g. ">= 200" would
- # limit searches to files greater than or equal to 200 bytes.
- #
- attr_accessor :size
+ # If the value passed is an integer, this option limits searches to files
+ # that match the size, in bytes, exactly. If a string is passed, you can
+ # use the standard comparable operators to match files, e.g. ">= 200" would
+ # limit searches to files greater than or equal to 200 bytes.
+ #
+ attr_accessor :size
- # Limits searches to files that belong to a specific user, where the user
- # can be either a user name or an ID.
- #
- # Not currently supported on MS Windows.
- #
- attr_accessor :user
+ # Limits searches to files that belong to a specific user, where the user
+ # can be either a user name or an ID.
+ #
+ # Not currently supported on MS Windows.
+ #
+ attr_accessor :user
- # The file that matched previously in the current search.
- #
- attr_reader :previous
+ # The file that matched previously in the current search.
+ #
+ attr_reader :previous
- alias pattern name
- alias pattern= name=
+ alias pattern name
+ alias pattern= name=
- # Creates and returns a new File::Find object. The options set for this
- # object serve as the rules for determining what files the File::Find#find
- # method will search for.
- #
- # In addition to the standard list of valid options, you may also use
- # FileTest methods as options, setting their value to true or false.
- #
- # Example:
- #
- # rule = File::Find.new(
- # :name => "*.rb",
- # :follow => false,
- # :path => ['/usr/local/lib', '/opt/local/lib'],
- # :readable? => true
- # )
- #
- def initialize(options = {})
- @options = options
+ # Creates and returns a new File::Find object. The options set for this
+ # object serve as the rules for determining what files the File::Find#find
+ # method will search for.
+ #
+ # In addition to the standard list of valid options, you may also use
+ # FileTest methods as options, setting their value to true or false.
+ #
+ # Example:
+ #
+ # rule = File::Find.new(
+ # :name => "*.rb",
+ # :follow => false,
+ # :path => ['/usr/local/lib', '/opt/local/lib'],
+ # :readable? => true
+ # )
+ #
+ def initialize(options = {})
+ @options = options
- @atime = nil
- @ctime = nil
- @ftype = nil
- @group = nil
- @follow = true
- @inum = nil
- @links = nil
- @mount = nil
- @mtime = nil
- @perm = nil
- @prune = nil
- @size = nil
- @user = nil
+ @atime = nil
+ @ctime = nil
+ @ftype = nil
+ @group = nil
+ @follow = true
+ @inum = nil
+ @links = nil
+ @mount = nil
+ @mtime = nil
+ @perm = nil
+ @prune = nil
+ @size = nil
+ @user = nil
- @previous = nil
- @maxdepth = nil
- @mindepth = nil
- @filetest = []
+ @previous = nil
+ @maxdepth = nil
+ @mindepth = nil
+ @filetest = []
- validate_and_set_options(options) unless options.empty?
+ validate_and_set_options(options) unless options.empty?
- @filesystem = File.stat(@mount).dev if @mount
+ @filesystem = File.stat(@mount).dev if @mount
- @path ||= Dir.pwd
- @name ||= '*'
- end
+ @path ||= Dir.pwd
+ @name ||= '*'
+ end
- # Executes the find based on the rules you set for the File::Find object.
- # In block form, yields each file in turn that matches the specified rules.
- # In non-block form it will return an array of matches instead.
- #
- # Example:
- #
- # rule = File::Find.new(
- # :name => "*.rb",
- # :follow => false,
- # :path => ['/usr/local/lib', '/opt/local/lib']
- # )
- #
- # rule.find{ |f|
- # puts f
- # }
- #
- def find
- results = [] unless block_given?
- paths = @path.is_a?(String) ? [@path] : @path # Ruby 1.9.x compatibility
+ # Executes the find based on the rules you set for the File::Find object.
+ # In block form, yields each file in turn that matches the specified rules.
+ # In non-block form it will return an array of matches instead.
+ #
+ # Example:
+ #
+ # rule = File::Find.new(
+ # :name => "*.rb",
+ # :follow => false,
+ # :path => ['/usr/local/lib', '/opt/local/lib']
+ # )
+ #
+ # rule.find{ |f|
+ # puts f
+ # }
+ #
+ def find
+ results = [] unless block_given?
+ paths = @path.is_a?(String) ? [@path] : @path # Ruby 1.9.x compatibility
- if @prune
- prune_regex = Regexp.new(@prune)
- else
- prune_regex = nil
- end
+ if @prune
+ prune_regex = Regexp.new(@prune)
+ else
+ prune_regex = nil
+ end
- paths.each{ |path|
- begin
- Dir.foreach(path){ |file|
- next if file == '.'
- next if file == '..'
+ paths.each{ |path|
+ begin
+ Dir.foreach(path){ |file|
+ next if file == '.'
+ next if file == '..'
- if prune_regex
- next if prune_regex.match(file)
- end
+ if prune_regex
+ next if prune_regex.match(file)
+ end
- orig = file.dup
- file = File.join(path, file)
+ orig = file.dup
+ file = File.join(path, file)
- stat_method = @follow ? :lstat : :stat
+ stat_method = @follow ? :stat : :lstat
- # Skip files we cannot access, stale links, etc.
- begin
- stat_info = File.send(stat_method, file)
- rescue Errno::ENOENT, Errno::EACCES
- next
- rescue Errno::ELOOP
- stat_method = :lstat # Handle recursive symlinks
- retry if stat_method.to_s != 'lstat'
- end
+ # Skip files we cannot access, stale links, etc.
+ begin
+ stat_info = File.send(stat_method, file)
+ rescue Errno::ENOENT, Errno::EACCES
+ next
+ rescue Errno::ELOOP
+ stat_method = :lstat # Handle recursive symlinks
+ retry if stat_method.to_s != 'lstat'
+ end
- glob = File.join(File.dirname(file), @name)
+ glob = File.join(File.dirname(file), @name)
- # Dir[] doesn't like backslashes
- if File::ALT_SEPARATOR
- file.tr!(File::ALT_SEPARATOR, File::SEPARATOR)
- glob.tr!(File::ALT_SEPARATOR, File::SEPARATOR)
- end
+ # Dir[] doesn't like backslashes
+ if File::ALT_SEPARATOR
+ file.tr!(File::ALT_SEPARATOR, File::SEPARATOR)
+ glob.tr!(File::ALT_SEPARATOR, File::SEPARATOR)
+ end
- if @mount
- next unless stat_info.dev == @filesystem
- end
+ if @mount
+ next unless stat_info.dev == @filesystem
+ end
- if @links
- next unless stat_info.nlink == @links
- end
+ if @links
+ next unless stat_info.nlink == @links
+ end
- if @maxdepth || @mindepth
- file_depth = file.split(File::SEPARATOR).length
- path_depth = @path.split(File::SEPARATOR).length
- depth = file_depth - path_depth
+ if @maxdepth || @mindepth
+ file_depth = file.split(File::SEPARATOR).length
+ path_depth = @path.split(File::SEPARATOR).length
+ depth = file_depth - path_depth
- if @maxdepth && (depth > @maxdepth)
- if File.directory?(file)
- unless paths.include?(file) && depth > @maxdepth
- paths << file
- end
- end
+ if @maxdepth && (depth > @maxdepth)
+ if File.directory?(file)
+ unless paths.include?(file) && depth > @maxdepth
+ paths << file
+ end
+ end
- next
- end
+ next
+ end
- if @mindepth && (depth < @mindepth)
- if File.directory?(file)
- unless paths.include?(file) && depth < @mindepth
- paths << file
- end
- end
+ if @mindepth && (depth < @mindepth)
+ if File.directory?(file)
+ unless paths.include?(file) && depth < @mindepth
+ paths << file
+ end
+ end
- next
- end
- end
+ next
+ end
+ end
- # Add directories back onto the list of paths to search unless
- # they've already been added
- #
- if stat_info.directory?
- paths << file unless paths.include?(file)
- end
+ # Add directories back onto the list of paths to search unless
+ # they've already been added
+ #
+ if stat_info.directory?
+ paths << file unless paths.include?(file)
+ end
- next unless Dir[glob].include?(file)
+ next unless Dir[glob].include?(file)
- unless @filetest.empty?
- file_test = true
+ unless @filetest.empty?
+ file_test = true
- @filetest.each{ |array|
- meth = array[0]
- bool = array[1]
+ @filetest.each{ |array|
+ meth = array[0]
+ bool = array[1]
- unless File.send(meth, file) == bool
- file_test = false
- break
- end
- }
+ unless File.send(meth, file) == bool
+ file_test = false
+ break
+ end
+ }
- next unless file_test
- end
+ next unless file_test
+ end
- if @atime || @ctime || @mtime
- date1 = Date.parse(Time.now.to_s)
+ if @atime || @ctime || @mtime
+ date1 = Date.parse(Time.now.to_s)
- if @atime
- date2 = Date.parse(stat_info.atime.to_s)
- next unless (date1 - date2).numerator == @atime
- end
+ if @atime
+ date2 = Date.parse(stat_info.atime.to_s)
+ next unless (date1 - date2).numerator == @atime
+ end
- if @ctime
- date2 = Date.parse(stat_info.ctime.to_s)
- next unless (date1 - date2).numerator == @ctime
- end
+ if @ctime
+ date2 = Date.parse(stat_info.ctime.to_s)
+ next unless (date1 - date2).numerator == @ctime
+ end
- if @mtime
- date2 = Date.parse(stat_info.mtime.to_s)
- next unless (date1 - date2).numerator == @mtime
- end
- end
+ if @mtime
+ date2 = Date.parse(stat_info.mtime.to_s)
+ next unless (date1 - date2).numerator == @mtime
+ end
+ end
- if @ftype
- next unless File.ftype(file) == @ftype
- end
+ if @ftype
+ next unless File.ftype(file) == @ftype
+ end
- if @group
- if @group.is_a?(String)
- next unless get_group(stat_info.gid).name == @group
- else
- next unless stat_info.gid == @group
- end
- end
+ if @group
+ if @group.is_a?(String)
+ next unless get_group(stat_info.gid).name == @group
+ else
+ next unless stat_info.gid == @group
+ end
+ end
- unless Config::CONFIG['host_os'] =~ /windows|mswin/i
- if @inum
- next unless stat_info.ino == @inum
- end
- end
+ unless Config::CONFIG['host_os'] =~ /windows|mswin/i
+ if @inum
+ next unless stat_info.ino == @inum
+ end
+ end
- # This currently doesn't work on MS Windows, even in limited
- # fashion for 0666 and 0664, because File.stat.mode doesn't
- # return the proper value.
- #
- if @perm
- if @perm.is_a?(String)
- octal_perm = sym2oct(@perm)
- next unless stat_info.mode & octal_perm == octal_perm
- else
- next unless sprintf("%o", stat_info.mode & 07777) == @perm.to_s
- end
- end
+ # This currently doesn't work on MS Windows, even in limited
+ # fashion for 0666 and 0664, because File.stat.mode doesn't
+ # return the proper value.
+ #
+ if @perm
+ if @perm.is_a?(String)
+ octal_perm = sym2oct(@perm)
+ next unless stat_info.mode & octal_perm == octal_perm
+ else
+ next unless sprintf("%o", stat_info.mode & 07777) == @perm.to_s
+ end
+ end
- # Allow plain numbers, or strings for comparison operators.
- if @size
- if @size.is_a?(String)
- regex = /^([><=]+)\s*?(\d+)$/
- match = regex.match(@size)
+ # Allow plain numbers, or strings for comparison operators.
+ if @size
+ if @size.is_a?(String)
+ regex = /^([><=]+)\s*?(\d+)$/
+ match = regex.match(@size)
- if match.nil? || match.captures.include?(nil)
- raise ArgumentError, "invalid size string: '#{@size}'"
- end
+ if match.nil? || match.captures.include?(nil)
+ raise ArgumentError, "invalid size string: '#{@size}'"
+ end
- operator = match.captures.first.strip
- number = match.captures.last.strip.to_i
+ operator = match.captures.first.strip
+ number = match.captures.last.strip.to_i
- next unless stat_info.size.send(operator, number)
- else
- next unless stat_info.size == @size
- end
- end
+ next unless stat_info.size.send(operator, number)
+ else
+ next unless stat_info.size == @size
+ end
+ end
- if @user
- if @user.is_a?(String)
- next unless get_user(stat_info.uid).name == @user
- else
- next unless stat_info.uid == @user
- end
- end
+ if @user
+ if @user.is_a?(String)
+ next unless get_user(stat_info.uid).name == @user
+ else
+ next unless stat_info.uid == @user
+ end
+ end
- if block_given?
- yield file
- else
- results << file
- end
+ if block_given?
+ yield file
+ else
+ results << file
+ end
- @previous = file unless @previous == file
- }
- rescue Errno::EACCES
- next # Skip inaccessible directories
- end
- }
+ @previous = file unless @previous == file
+ }
+ rescue Errno::EACCES
+ next # Skip inaccessible directories
+ end
+ }
- block_given? ? nil : results
- end
-
- # Limits searches to the same file system as the specified +mount_point+.
- #
- def mount=(mount_point)
- @mount = mount_point
- @filesystem = File.stat(mount_point).dev
- end
+ block_given? ? nil : results
+ end
- private
+ # Limits searches to the same file system as the specified +mount_point+.
+ #
+ def mount=(mount_point)
+ @mount = mount_point
+ @filesystem = File.stat(mount_point).dev
+ end
- # This validates that the keys are valid. If they are, it sets the value
- # of that key's corresponding method to the given value. If a key ends
- # with a '?', it's validated as a File method.
- #
- def validate_and_set_options(options)
- options.each do |key, value|
- key = key.to_s.downcase
+ private
- if key[-1].chr == '?'
- sym = key.to_sym
+ # This validates that the keys are valid. If they are, it sets the value
+ # of that key's corresponding method to the given value. If a key ends
+ # with a '?', it's validated as a File method.
+ #
+ def validate_and_set_options(options)
+ options.each do |key, value|
+ key = key.to_s.downcase
- unless File.respond_to?(sym)
- raise ArgumentError, "invalid option '#{key}'"
- end
+ if key[-1].chr == '?'
+ sym = key.to_sym
- @filetest << [sym, value]
- else
- unless VALID_OPTIONS.include?(key)
- raise ArgumentError, "invalid option '#{key}'"
- end
+ unless File.respond_to?(sym)
+ raise ArgumentError, "invalid option '#{key}'"
+ end
- send("#{key}=", value)
- end
+ @filetest << [sym, value]
+ else
+ unless VALID_OPTIONS.include?(key)
+ raise ArgumentError, "invalid option '#{key}'"
+ end
+
+ send("#{key}=", value)
end
- end
+ end
+ end
- # Converts a symoblic permissions mode into its octal equivalent.
- #--
- # Taken almost entirely from ruby-talk: 96956 (Hal Fulton).
- #
- def sym2oct(str)
- left = {'u' => 0700, 'g' => 0070, 'o' => 0007, 'a' => 0777}
- right = {'r' => 0444, 'w' => 0222, 'x' => 0111}
- regex = /([ugoa]+)([+-=])([rwx]+)/
+ # Converts a symoblic permissions mode into its octal equivalent.
+ #--
+ # Taken almost entirely from ruby-talk: 96956 (Hal Fulton).
+ #
+ def sym2oct(str)
+ left = {'u' => 0700, 'g' => 0070, 'o' => 0007, 'a' => 0777}
+ right = {'r' => 0444, 'w' => 0222, 'x' => 0111}
+ regex = /([ugoa]+)([+-=])([rwx]+)/
- cmds = str.split(',')
+ cmds = str.split(',')
- perm = 0
+ perm = 0
- cmds.each do |cmd|
- match = cmd.match(regex)
- raise "Invalid symbolic permissions: '#{str}'" if match.nil?
+ cmds.each do |cmd|
+ match = cmd.match(regex)
+ raise "Invalid symbolic permissions: '#{str}'" if match.nil?
- junk, who, what, how = match.to_a
+ junk, who, what, how = match.to_a
- who = who.split(//).inject(who_num=0){ |num,b| num |= left[b]; num }
- how = how.split(//).inject(how_num=0){ |num,b| num |= right[b]; num }
- mask = who & how
+ who = who.split(//).inject(who_num=0){ |num,b| num |= left[b]; num }
+ how = how.split(//).inject(how_num=0){ |num,b| num |= right[b]; num }
+ mask = who & how
- case what
- when '+'
- perm = perm | mask
- when '-'
- perm = perm & ~mask
- when '='
- perm = mask
- end
+ case what
+ when '+'
+ perm = perm | mask
+ when '-'
+ perm = perm & ~mask
+ when '='
+ perm = mask
end
+ end
- perm
- end
+ perm
+ end
- # Returns the group object based on the group id. Implemented for the
- # sake of platforms that cannot build extensions, such as JRuby.
- #
- def get_group(gid)
- if defined? Sys::Admin
- Sys::Admin.get_group(gid)
- else
- Etc.getgrgid(gid)
- end
- end
+ # Returns the group object based on the group id. Implemented for the
+ # sake of platforms that cannot build extensions, such as JRuby.
+ #
+ def get_group(gid)
+ if defined? Sys::Admin
+ Sys::Admin.get_group(gid)
+ else
+ Etc.getgrgid(gid)
+ end
+ end
- # Returns the user object based on the group id. Implemented for the
- # sake of platforms that cannot build extensions, such as JRuby.
- #
- def get_user(uid)
- if defined? Sys::Admin
- Sys::Admin.get_user(uid)
- else
- Etc.getpwuid(uid)
- end
- end
+ # Returns the user object based on the group id. Implemented for the
+ # sake of platforms that cannot build extensions, such as JRuby.
+ #
+ def get_user(uid)
+ if defined? Sys::Admin
+ Sys::Admin.get_user(uid)
+ else
+ Etc.getpwuid(uid)
+ end
+ end
end