#!/usr/bin/env ruby # # $Id: cvschk.rb,v 1.2 2005/12/31 07:23:17 aamine Exp $ # # Lists filenames which are not under CVS control. # require 'optparse' USE_ALL_CVSIGNORE = 2 USE_LOCAL_CVSIGNORE = 1 USE_NO_CVSIGNORE = 0 def main ign_level = USE_ALL_CVSIGNORE local = false parser = OptionParser.new parser.banner = "Usage: #{File.basename($0)} [-aAl] []" parser.on('-a', '--almost', 'Turn off ~/.cvsignore, $CVSIGNORE, and builtin entries.') { ign_level = USE_LOCAL_CVSIGNORE } parser.on('-A', '--all', "Turn off all cvsignores.") { ign_level = USE_NO_CVSIGNORE } parser.on('-l', '--local', 'Process only local directory.') { local = true } parser.on('--help', 'Prints this message and quit.') { puts parser.help exit 0 } begin parser.parse! rescue OptionParser::ParseError => err $stderr.puts err.message $stderr.puts parser.help exit 1 end if ARGV.size > 1 $stderr.puts 'too many arguments' $stderr.puts parser.help exit 1 end check((ARGV[0] || '.'), local, ign_level) end def check(dir, no_recursive, ignlevel) unknown_files(dir, ignlevel).sort.each do |i| puts i.sub(/\A\.\//, '') end return if no_recursive cvsed_files(dir).each do |path| next unless File.directory?(path) next if File.symlink?(path) check path, false, ignlevel end end def unknown_files(dir, ignlevel) reject_ignorefiles(all_entries(dir) - cvsed_files(dir), ignlevel, dir) end def reject_ignorefiles(pathes, level, dir) ign_patterns = ignore_patterns(level, dir) pathes.reject {|path| ign_patterns.any? {|pat| File.fnmatch?(pat, File.basename(path)) } } end def ignore_patterns(level, cwd) case level when USE_NO_CVSIGNORE ['CVS'] when USE_LOCAL_CVSIGNORE ['CVS'] + parse_cvsignore("#{cwd}/.cvsignore") when USE_ALL_CVSIGNORE ['CVS'] + parse_cvsignore("#{cwd}/.cvsignore") + global_cvsignore() else raise 'must not happen' end end BUILTIN_IGNORE = %w( RCS SCCS CVS CVS.adm RCSLOG cvslog.* tags TAGS .make.state .nse_depinfo *~ #* .#* ,* _$* *$ *.old *.bak *.BAK *.orig *.rej .del-* *.a *.olb *.o *.obj *.so *.exe *.Z *.elc *.ln core ) def global_cvsignore BUILTIN_IGNORE + env_CVSIGNORE() + parse_cvsignore(File.expand_path('~/.cvsignore')) end def env_CVSIGNORE return [] unless ENV['CVSIGNORE'] ENV['CVSIGNORE'].split end def parse_cvsignore(path) return [] unless File.file?(path) File.read(path).split end def cvsed_files(dir) read_CVS_Entries(dir) | cvsed_directories(dir) end def read_CVS_Entries(dir) File.readlines(dir + '/CVS/Entries')\ .map {|line| line.slice(%r_\AD?/([^/]+)_, 1) }.compact\ .map {|ent| "#{dir}/#{ent}" } rescue Errno::ENOENT $stderr.puts "no CVS/: #{dir}" exit 1 end def cvsed_directories(dir) Dir.glob(dir + '/*')\ .select {|path| File.directory?(path) }\ .select {|path| File.directory?(path + '/CVS') } end def all_entries(dir) (Dir.entries(dir) - %w(. ..)).map {|ent| "#{dir}/#{ent}" } end main