なひです.

ちょっとした必要があって,ftpupの一部機能を変更して,
localとremoteのsyncをするようなモノを作ってみました.
ftpupの機能を向上したり,不具合を修正するものではないので,
取り込む必要はありません(つか,できません ^^;).

判断基準は以下のとおりです.

1. localとremoteのどちらかにファイル/ディレクトリがなければ,
  反対側のファイルを取ってきて作成する.

2. localとremoteの両方にファイルがあり,サイズが同じ場合は,
  タイムスタンプが異なっていても放置(何もしない).

3. 上記サイズが異なる場合,タイムスタンプが新しいほうで,
  古いほうを上書きする(古いほうは消えますのでご注意).

今日作ったばかりで,なひもまだろくに運用してないので,
ご利用の際は充分注意してください.local/remote両サイドのバックアップが
必須です.なひは今後あちこちに使う予定なので,
不具合にぶちあたったらまたpatchを出すでしょう.^^;

ftpup-1.3.3へのpatchです.

--- ftpup	Tue May 16 17:04:38 2000
+++ ftpsync	Sun Jun 25 00:00:25 2000
@@ -103,4 +103,5 @@ EOF
 	  update(".")
 	else
+	  raise ArgumentError.new("Argument not allowed.")
 	  path.each do |p|
 	    update(p)
@@ -136,42 +137,63 @@ EOF
   def updatedir(directory)
     print directory.sub(/^./, Config::REMOTE_DIR), ":\n"
+    local_dirs = []
+    local_filetimes = {}
+    local_filesizes = {}
+    check_local_files(directory, local_dirs, local_filetimes, local_filesizes)
     remote_dirs = [];
     remote_filetimes = {}
     remote_filesizes = {}
     check_remote_files(directory, remote_dirs, remote_filetimes, remote_filesizes)
-    Dir.foreach(directory) do |file|
-      longname = directory + "/" + file
-      if file != "." && file != ".."
-	if File.ftype(longname) == "directory"
-	  if longname =~ @ignore_file_regexp
-	    next
-	  end
-	  unless remote_dirs.include?(file)
-	    makedir(longname)
-	  end
-	  updatedir(longname) unless @not_recursive
-	else
-	  if not longname =~ @ignore_file_regexp and
-             ((not remote_filesizes.has_key?(file) or
-              remote_filesizes[file] != File.size(longname)) or
-	      (not remote_filetimes.has_key?(file) or
-              remote_filetimes[file] < File.mtime(longname).gmtime))
-	    updatefile(longname)
-	  end
-	  remote_filetimes.delete(file)
+    (local_filetimes.keys + remote_filetimes.keys).uniq.each do |file|
+      longname = getlongname(directory, file)
+      if longname =~ @ignore_file_regexp
+	# Nothing to do.
+      elsif not local_filesizes.has_key?(file)
+	getfile(longname)
+      elsif not remote_filesizes.has_key?(file)
+	putfile(longname)
+      elsif local_filesizes[file] != remote_filesizes[file]
+	if local_filetimes[file] < remote_filetimes[file]
+	  getfile(longname)
+	elsif local_filetimes[file] > remote_filetimes[file]
+	  putfile(longname)
 	end
       end
     end
-    if @remove_files
-      remote_filetimes.each_key do |file|
-	longname = directory + "/" + file
-	unless @leave_file_regexp and longname =~ @leave_file_regexp
-	  print "delete ", File.basename(longname), "\n"
-	  @ftp.delete(longname)
+    (local_dirs + remote_dirs).uniq.each do |dir|
+      longname = getlongname(directory, dir)
+      if longname =~ @ignore_file_regexp
+	next
+      end
+      if local_dirs.include?(dir)
+	unless remote_dirs.include?(dir)
+	  makedir(longname)
 	end
+      elsif remote_dirs.include?(dir)
+	Dir.mkdir(longname)
       end
+      updatedir(longname) unless @not_recursive
     end
   end
   
+  def check_local_files(directory, dirs, filetimes, filesizes)
+    targetDir = Dir.open(directory)
+    targetDir.each do |file|
+      longname = getlongname(directory, file)
+      case File.ftype(longname)
+      when "directory"
+	if file != "." && file != ".."
+	  dirs.push(file)
+	end
+      when "file"
+	filetimes[file] = File.mtime(longname).gmtime
+	filesizes[file] = File.size(longname)
+      else
+	print "#{File.ftype(longname)}(#{longname}) found.  Skepped.\n"
+      end
+    end
+    targetDir.close
+  end
+
   def check_remote_files(directory, dirs, filetimes, filesizes)
     if @list_opts and @list_opts.length > 0
@@ -226,5 +248,22 @@ EOF
   end
   
+  def getfile(file)
+    print "get ", file, ": "
+    @ftp.getbinaryfile(file, file, 4096) do |data|
+      print "."; $>.flush
+    end
+    print "\n"
+  end
+
+  def putfile(file)
+    print "put ", file, ": "
+    @ftp.putbinaryfile(file, file, 4096) do |data|
+      print "."; $>.flush
+    end
+    print "\n"
+  end
+
   def updatefile(file)
+    raise RuntimeError.new("updatefile is obsolete.")
     print "put ", file, ": "
     if file =~ @textfile_regexp
@@ -242,4 +281,8 @@ EOF
     end
     print "\n"
+  end
+
+  def getlongname(directory, file)
+    directory + "/" + file
   end