文字コードとFileUtils#remove_entry_secure、およびその他

実行環境によるのかもしれないが、ファイル名の文字コードによってはこんなことが起きるようだ。

$ ruby1.9.1 -r fileutils -e '
p Encoding.find("filesystem")
name = "a/あいう".encode("EUC-JP")
FileUtils.mkdir_p(name)
FileUtils.remove_entry_secure("a")
'
#<Encoding:UTF-8>
/usr/lib/ruby/1.9.1/fileutils.rb:1303:in `sub': invalid byte sequence in UTF-8 (ArgumentError)
    from /usr/lib/ruby/1.9.1/fileutils.rb:1303:in `block in remove_dir1'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1314:in `platform_support'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1302:in `remove_dir1'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1295:in `remove'
    from /usr/lib/ruby/1.9.1/fileutils.rb:761:in `block in remove_entry'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1347:in `block (2 levels) in postorder_traverse'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1351:in `postorder_traverse'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1346:in `block in postorder_traverse'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1345:in `each'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1345:in `postorder_traverse'
    from /usr/lib/ruby/1.9.1/fileutils.rb:759:in `remove_entry'
    from /usr/lib/ruby/1.9.1/fileutils.rb:700:in `remove_entry_secure'
    from -e:5:in `<main>'

これは不自然な例だけれど、実際に、Dir.mktmpdirを使っているときに次のようなこと経験した。

$ ruby1.9.1 -r tmpdir -e '
name = "あいう".encode("EUC-JP")
Dir.mktmpdir do |td|
  Dir.mkdir(File.join(td, name)) 
end
'                      
/usr/lib/ruby/1.9.1/fileutils.rb:1303:in `sub': invalid byte sequence in UTF-8 (ArgumentError)
    from /usr/lib/ruby/1.9.1/fileutils.rb:1303:in `block in remove_dir1'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1314:in `platform_support'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1302:in `remove_dir1'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1295:in `remove'
    from /usr/lib/ruby/1.9.1/fileutils.rb:727:in `block in remove_entry_secure'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1347:in `block (2 levels) in postorder_traverse'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1351:in `postorder_traverse'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1346:in `block in postorder_traverse'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1345:in `each'
    from /usr/lib/ruby/1.9.1/fileutils.rb:1345:in `postorder_traverse'
    from /usr/lib/ruby/1.9.1/fileutils.rb:725:in `remove_entry_secure'
    from /usr/lib/ruby/1.9.1/tmpdir.rb:85:in `mktmpdir'
    from -e:3:in `<main>'

Dir.mktmpdirの内部で例外が発生してしまうので手の出しようがないところがきびしい。この現象に限っていえば正規表現を使わなければよい…… のかなあ?

--- lib/fileutils.rb   (revision 32046)
+++ lib/fileutils.rb    (working copy)
@@ -1397,7 +1397,7 @@
 
     def remove_dir1
       platform_support {
-        Dir.rmdir path().sub(%r</\z>, '')
+        Dir.rmdir path().chomp('/')
       }
     end
 

同じような問題(?)がoptparseにもある。

$ mkdir -p "a/$(echo あいう|nkf -We)"
$ ruby1.9.1 -r optparse -e '; p Encoding.find("filesystem"); ARGV.parse!' a/* 
#<Encoding:UTF-8>
/usr/lib/ruby/1.9.1/optparse.rb:1268:in `===': invalid byte sequence in UTF-8 (ArgumentError)
    from /usr/lib/ruby/1.9.1/optparse.rb:1268:in `block in parse_in_order'
    from /usr/lib/ruby/1.9.1/optparse.rb:1264:in `catch'
    from /usr/lib/ruby/1.9.1/optparse.rb:1264:in `parse_in_order'
    from /usr/lib/ruby/1.9.1/optparse.rb:1258:in `order!'
    from /usr/lib/ruby/1.9.1/optparse.rb:1349:in `permute!'
    from /usr/lib/ruby/1.9.1/optparse.rb:1370:in `parse!'
    from /usr/lib/ruby/1.9.1/optparse.rb:1774:in `parse!'
    from -e:1:in `<main>'

optparseにも、というより、いたるところにといったほうがよいのかな。こういうのはどう扱うとよいのだろう。