Changeset 9074
- Timestamp:
- 04/07/08 16:46:43 (5 years ago)
- Location:
- lang/ruby/mdmaildir
- Files:
-
- 3 modified
Legend:
- Unmodified
- Added
- Removed
-
lang/ruby/mdmaildir/README
r5682 r9074 1 Maildir man upiration scripts for migration and maintenance.1 Maildir manipuration scripts for migration and maintenance. 2 2 3 3 - mdmerge.rb: Merge different two Maildir into one Maildir. -
lang/ruby/mdmaildir/mdd.rb
r5682 r9074 1 1 #!/usr/local/bin/ruby 2 2 # $Id$ 3 require 'gdbm'4 require 'digest/sha1'5 require 'pp'6 3 4 # 5 # = Manipulates message-digest file for whole of messages in the Maildir. 6 # 7 # Authors:: $Author: genta $ 8 # Revision:: $Revision: 77 $ 9 # 10 class MDDigest 11 require 'gdbm' 12 require 'digest/sha1' 13 require 'pp' 7 14 8 # Maildir digest file class9 class MDDigest10 15 attr_reader :path, :basedir, :db 16 17 # Create MDDigest instance. MDDigest opens a message-digest file in 18 # the specified Maildir. The message-digest file is stored into the 19 # top-level directory as filename like "_maildir_/mddigest". 20 # 21 # _maildir_:: Path of Maildir. 22 # returns:: An instance of MDDigest class 11 23 def initialize(maildir) 12 24 @basedir = maildir.dup … … 16 28 17 29 def open; @db = GDBM.open(@path); end 18 def close; @db.reorganize.close; end 19 def clear; @db.clear; end 30 private :open 20 31 32 # Close the backend DBM. Call this method at the end of your program. 33 def close; 34 @db.reorganize.close 35 end 36 37 # Truncate message-digest file. 38 def clear 39 @db.clear 40 end 41 42 # Caliculates a message-digest for specified message, and records it 43 # into message-digest file. 44 # 45 # _path_:: Relative path of a message from Maildir directory. 46 # returns:: void 21 47 def add(path) 22 48 apath = abspath(path) … … 33 59 end 34 60 61 # Check specified key and file are already records in message-digest 62 # file, or not. 63 # 64 # _key_:: Key of file-hash. It typically message-digest + size of 65 # message. 66 # _file_:: Relative path of a message from Maildir directory. 67 # returns:: *true* if it already exists. 68 # *false* for not exists. 35 69 def has_file?(key, file) 36 70 return false if @db[key].nil? … … 41 75 end 42 76 77 # Wrapper of GDBM#each. It is for traversal all of messages in Maildir 78 # by using a message-digest file. 43 79 def each(&block) 44 80 @db.each(&block) … … 46 82 47 83 84 private 85 86 # Caliculates a Message digest for specified message. 87 # 88 # _file_:: Path for a message. 89 # returns:: Instance of Digest::SHA1. 48 90 def hash(file) 49 91 md = Digest::SHA1.new … … 57 99 end 58 100 59 def abspath(file) 60 File.expand_path(file, @basedir) 61 end 62 101 # _key_:: 102 # _dest_:: 63 103 def merge(key, dest) 64 104 dfolder = folder(abspath(dest)) … … 77 117 end 78 118 119 # Determines a folder name for a specified message. 120 # 121 # _path_:: Relative path of a message from Maildir directory 122 # returns:: A folder name in Maildir for a message that directed by path. 79 123 def folder(path) 80 124 f, = path.split('/') … … 82 126 return f 83 127 end 128 129 # Convert relative path to the absolute path. The root of absolute path is 130 # Maildir directory. 131 # 132 # _file_:: Relative path from Maildir directory. 133 # returns:: Absolute path that converted from _file_. 134 def abspath(file) 135 File.expand_path(file, @basedir) 136 end 84 137 end -
lang/ruby/mdmaildir/mdmerge.rb
r5682 r9074 5 5 $LOAD_PATH << File.dirname($0) 6 6 require 'mdd' # MDDigest class 7 require 'optparse' 7 8 require 'pp' 8 9 10 # store command-line configuration params. 11 $config = Hash.new 12 9 13 10 14 11 15 class CMD 12 attr_reader :fileq, :dirq, :skipdir, :basedir, :fromdir, :config 13 def initialize(basedir, fromdir, config = {}) 14 @fileq, @dirq = [], [] 15 @skipdir = [] 16 require 'tempfile' 17 18 attr_reader :basedir, :fromdir 19 def initialize(basedir, fromdir, config = Hash.new) 20 @config = config.dup 16 21 @basedir = basedir.dup 17 22 @fromdir = fromdir.dup 18 @config = config.dup 23 @logger = nil 24 @tempfile = Tempfile.new(File.basename($0)) 25 end 26 27 def openlog(file) 28 @logger = open(file, 'w') 29 end 30 31 def closelog 32 @logger.close 33 @logger = nil 34 end 35 36 def logging? 37 @logger.nil? ? false : true 38 end 39 40 def fd 41 logging? ? @logger : @tempfile 42 end 43 protected :fd 44 45 def rewind 46 return self if logging? 47 @tempfile.rewind 48 end 49 protected :rewind 50 51 def load(file) 52 @tempfile = open(file) 19 53 end 20 54 21 55 def add(file) 22 mkdir(file) 23 cpfile(file) 24 end 25 26 def cpfile(rpath) 27 return if @fileq.include?(rpath) 28 @fileq << rpath 29 end 30 31 def mkdir(rpath) 32 rdir = File.dirname(rpath) 33 path = rdir.split(File::SEPARATOR) 34 path.pop if path[-1] =~ /^(cur|new|tmp)$/ 35 rdir = File.join(path) 36 adir = abspath(rdir) 37 38 return if @dirq.include?(rdir) or @skipdir.include?(rdir) 39 if test(?d, adir) then 40 puts "mkdir: already exists: #{adir}" 41 @skipdir << rdir 42 return 43 end 44 @dirq << rdir 45 end 46 47 def commit_check 48 @dirq.each do |dir| 49 puts "mkdir: #{dir}" 50 end 51 52 @fileq.each do |file| 53 puts "cp: #{file}" 54 end 56 fd.puts(file) 57 return self 55 58 end 56 59 57 60 def commit 58 return if immediate? 59 do_mkdir 60 do_cpfile 61 end 62 63 64 def do_mkdir 65 @dirq.each do |rpath| 66 folder = rpath.split(File::SEPARATOR)[0].sub(/^\./, '') 67 #puts "maildirmake: #{folder} (from: #{rpath}, basedir: #{@basedir})" 68 system('maildirmake', '-f', folder, @basedir) or 69 raise "maildirmake: #{$?}" 70 end 71 end 72 73 def do_cpfile 74 @fileq.each do |rpath| 75 tofile = File.expand_path(rpath, @basedir) 61 rewind 62 dircache = Hash.new(false) 63 while rpath = fd.gets 64 rpath.chomp! 65 folder = rpath_to_folder(rpath) 66 if dircache[folder] == false then 67 do_maildirmake(folder) 68 dircache[folder] = true 69 end 70 71 to_rpath = rpath.dup 72 unless imap_prefix.empty? then 73 if to_rpath =~ /^\./ then 74 to_rpath = '.' + imap_prefix + to_rpath 75 else 76 unless to_rpath =~ /^(cur|tmp|new)/ then 77 raise "Internal Error: rpath contains illegal path: #{to_rpath}" 78 end 79 to_rpath = '.' + imap_prefix + '/' + to_rpath 80 end 81 end 82 tofile = File.expand_path(to_rpath, @basedir) 76 83 frfile = File.expand_path(rpath, @fromdir) 77 78 system('cp', '-p', frfile, tofile) or raise "cp: #{$?}" 79 end 84 do_cpfile(frfile, tofile) 85 end 86 end 87 88 89 private 90 91 def do_maildirmake(folder) 92 path = folder.split('.') 93 path.unshift(imap_prefix) unless imap_prefix.empty? 94 path.size.times do |i| 95 folder = path[0..i].join('.') 96 next if test(?e, abspath('.' + folder)) 97 98 args = ['maildirmake', '-f', folder, @basedir] 99 args.unshift('echo') if dry_run? 100 system(*args) or 101 raise("maildirmake: returned in status #{$?} for " + 102 "'maildirmake -f #{folder} #{@basedir}'") 103 end 104 return self 105 end 106 107 def do_cpfile(src, dest) 108 args = link? ? ['ln', '-f', src, dest] : ['cp', '-p', src, dest] 109 args.unshift('echo') if dry_run? 110 system(*args) or 111 raise("#{args[0]}: returned in status #{$?} for " + args.join(' ')) 112 return self 113 end 114 115 def rpath_to_folder(msg_rpath) 116 folder, = File.dirname(msg_rpath).split(File::SEPARATOR) 117 return '' unless folder.sub!(/^\./, '') 118 return folder 80 119 end 81 120 … … 84 123 end 85 124 86 def immediate? 87 @config.key?(:immediate) 88 end 89 end 125 def dry_run?; @config[:dry_run]; end 126 def link?; @config[:link]; end 127 def imap_prefix; @config.key?(:prefix) ? @config[:prefix] : ''; end 128 end 129 130 131 class CMDProcessor 132 require 'forwardable' 133 extend Forwardable 134 def_delegators(:@cmd, :openlog, :closelog, :load, :commit) 135 protected :openlog, :closelog, :load, :commit 136 137 def initialize(updating_maildir, mergefrom_maildir, config) 138 @config = config 139 @mode = @config[:mode] 140 @cmdfile = @config[:cmdfile] 141 142 @updating = MDDigest.new(updating_maildir) 143 @mergefrom = MDDigest.new(mergefrom_maildir) 144 145 @cmd = CMD.new(@updating.basedir, @mergefrom.basedir, @config) 146 end 147 148 def process 149 raise "Please inhelit this class" 150 end 151 152 def close 153 $stderr.print 'Closing DBMs...' 154 @updating.close 155 @mergefrom.close 156 $stderr.puts ' done.' 157 return nil 158 end 159 160 protected 161 def check_maildir 162 @mergefrom.each do |key, file| 163 # 'files' are mail. that have a same hash digest. 164 files = file.split('\0') 165 files.each do |file| 166 @cmd.add(file) unless @updating.has_file?(key, file) 167 end 168 end 169 return self 170 end 171 end 172 173 class OnTheFly < CMDProcessor 174 def process 175 $stderr.puts 'Processing in on-the-fly mode.' 176 177 $stderr.print "Checking maildir... " 178 check_maildir 179 $stderr.puts "done." 180 181 $stderr.print "Commit... " 182 commit 183 $stderr.puts "done." 184 185 return self 186 end 187 end 188 189 class MakeCMDFile < CMDProcessor 190 def process 191 $stderr.puts 'Processing in make-cmdfile mode.' 192 193 openlog(@cmdfile) 194 $stderr.print "Checking maildir... " 195 check_maildir 196 $stderr.puts "done." 197 closelog 198 199 return self 200 end 201 end 202 203 class CommitFromFile < CMDProcessor 204 def process 205 $stderr.puts 'Processing in commit-from-file mode.' 206 self.load(@cmdfile) 207 208 $stderr.print "Commit... " 209 commit 210 $stderr.puts "done." 211 212 return self 213 end 214 end 215 90 216 91 217 def usage 92 218 puts <<-_EOD_ 93 #{File.basename($0)} [ base-maildir] [merge-from-maildir]219 #{File.basename($0)} [options]... [base-maildir] [merge-from-maildir] 94 220 Note: base-maildir will be overwritten. Please backup first. 221 222 Options: 223 -f COMMAND_FILE 実際に処理を行わず,COMMAND_FILEに処理の 224 内容を書き出します. 225 -c -f COMMAND_FILE COMMAND_FILEから処理の内容を読み込み,実 226 行します. 227 -p IMAP_FOLDER_PREFIX base-maildirにコピーする際,指定されたフ 228 ォルダ以下にコピーを行います. 229 --link base-maildirにコピーする際,cp(1) の代わ 230 りにln(1)を用います. 231 -n For debug. 232 -h Show usage. 95 233 _EOD_ 96 234 end … … 98 236 def getargs 99 237 $home = ENV['HOME'] or raise "HOME environment undefined" 238 239 commit_from_file = false 240 OptionParser.new do |opt| 241 opt.on('-f MANDATORY') {|val| $config[:cmdfile] = val } 242 opt.on('-n') {|v| $config[:dry_run] = true } 243 opt.on('-c') {|v| commit_from_file = true } 244 opt.on('-p MANDATORY') {|val| $config[:prefix] = val } 245 opt.on('--link') {|v| $config[:link] = true } 246 opt.on('-h') {|v| usage(); exit } 247 opt.parse!(ARGV) 248 end 249 100 250 if ARGV.size != 2 then 101 251 usage() … … 103 253 end 104 254 $src, $dest = ARGV[0..1] 105 end 106 255 256 # sanity check for options 257 $config[:mode] = :on_the_fly 258 if $config.key?(:cmdfile) then 259 $config[:mode] = :make_cmdfile 260 end 261 if (commit_from_file == true) then 262 $config[:mode] = :commit_from_file 263 unless test(?e, $config[:cmdfile]) then 264 raise "#{$config[:cmdfile]}: Not exists" 265 end 266 end 267 end 107 268 108 269 109 270 getargs() 110 smd = MDDigest.new($src) 111 dmd = MDDigest.new($dest) 112 cmd = CMD.new(smd.basedir, dmd.basedir, {:immediate => true}) 113 114 dmd.each do |key, file| 115 files = file.split('\0') # 'files' are mail. that have a same hash digest. 116 files.each do |file| 117 cmd.add(file) unless smd.has_file?(key, file) 118 end 119 end 120 cmd.commit 121 122 smd.close 123 dmd.close 271 { :on_the_fly => OnTheFly, 272 :make_cmdfile => MakeCMDFile, 273 :commit_from_file => CommitFromFile }[$config[:mode]]. 274 new($src, $dest, $config).process.close 124 275 __END__
![(please configure the [header_logo] section in trac.ini)](/share/chrome/site/your_project_logo.png)