OSTRACISM CO.

OCamlとRustとPythonと...

偽unzip

 WindowsにおいてMinecraftのバックアップファイルをコマンド一発unzipで展開するとディレクトリ名が化ける。私がWindows機に入れてるunzipは GnuWin32とかいう古いプロダクトのもので、2005年で止まっている(http://gnuwin32.sourceforge.net/packages/unzip.htm)。Minecraftのバックアップファイルは内部のパスがUTF-8の.zipだが、古いunzipは当然UTF-8フラグなんて気にしてないので、全てのパスをAnsiとして、日本語Windowsではコードページ932、いわゆるシフトJISとして扱う。で、化ける。

 コマンド一発で動く、UTF-8に対応したWindows用の偽unzipをでっちあげるのが目標。以下の話は全てWindows 10。

OCaml

 OCamlの文字列は8bitデータの配列としてしか定義されていない。それをどう使うかは基本的に環境やプログラマに依存する。Windows(cygwin)版のOCamlの起動時パラメタやopen_in_binやUnix.mkdirでのファイル名の扱いは、最終的にWin32APIのAnsiを使っている。なので、少なくとも UTF-8からCP932に変換する必要がある。ところが、標準ライブラリのFilename.dirnameなどは、文字列末からディレクトリセパレータを探すというアルゴリズムを使っているようで、CP932はアルゴリズム安全ではない。いわゆる「\」問題がある。よって関数によってはUTF-8を通す必要もあり(UTF-8には「\」問題はないためなので、別にEUC-JPでもいいのだが、まぁ、今更EUC-JPではない)CP932からUTF-8への変換も必要になる。どちらにせよOCamlには文字エンコード変換の解りやすく使いやすいライブラリがないので、そこはCで書く。つうか以前書いた。MultiByteToWideCharとWideCharToMultiByteを通すだけ。CとOCamlの混在環境でのomakeの使い方はまだ把握していない(可能かどうか不明)。

 camlzipライブラリのZip.copy_entry_to_file関数はちゃんとファイル更新日時を合わせてくれる。これはどちらかというと珍しい。UTF-8かどうかのフラグを得る方法は用意されていないので、.zipファイル先頭から6バイトシークして1ワード読む。

Rust

 Rustの文字列はUTF-8と決まっている。起動時パラメタもstd::fs::File::openもUTF-8。最終的にWin32APIのWideを使っているのだろう。zipクレイトでは.zipファイル内のパスを文字列としても8bitのデータの配列としても扱える。Rustの文字エンコード変換関連のライブラリはあまり調べてない。必要なのはCP932からUTF-8への変換。winapiクレイトを使って、上記のMultiByteToWideCharとWideCharToMultiByteを通す。RustではCで書いてブリッジする必要はない、つうかそれがwinapiクレイト。

 ZipArchive::extractではファイル更新日時が現在時刻のまま。自力で何とかしないといけないが、どうやらRustの標準ライブラリにはファイルの日時変更の機能がない。標準ライブラリは最低限にという思想の結果なんだろうからしかたがない。winapiクレイトを使ってSetFileTimeを呼ぶ。UTF-8かどうかのフラグを得る方法は用意されていないので、.zipファイル先頭から6バイトシークして1ワード読む。

Python3

 Python2は起動時パラメタやファイル名の扱いはおそらく最終的にWin32APIのAnsiだったようだが、Python3ではWin32APIのWideになったようで、全般的にUnicodeになっている。なので標準ライブラリのzipfileもUTF-8の.zipファイルの扱いは問題ない。問題はAnsiの.zipファイルである。

 Ansiのファイル名がどうにも扱えない。数字に変換して見てみるとおかしな値になっている。まさかと(DuckDuckGoで)ググってみると、CP437というキーワードが見つかった。なんと、UTF-8フラグが立ってない場合、コードページ437とみなしてUTF-8に変換しているそうだ。

 Python2にはこんなコードはなかった。素直にAnsi文字列がそのまま入っていた。

 slaveとかblack listの言葉狩りをしてる場合じゃないよ。「今ここにある解決すべき本物の差別」をどうにかしろよ。21世紀になってから新たに、米国での大昔のDOSファイルにしか通用しないコードページを固定値で入れるとか、頭おかしいよ。迷惑かつ極めて不快。

 シフトJISをOEM-USとしてUTF-8に変換って、オマエはiTunesか。

 Pythonはシェルスクリプトよりは便利なスクリプト言語として扱う程度に付き合うのが吉。使えば使うほどライブラリの手抜き感や仕組みのやっつけ仕事感が半端なく感じられるようになる。他に代替となるものもあまりないのが問題。

 UTF-8かどうかのフラグを得る方法が用意されているのは珍しい。展開しただけではファイルの更新日時は現在時刻。


 UTF-8かどうかのフラグは.zipファイルのヘッダにあるが、アーカイブされた各ファイルのヘッダにもある。.zipは安全のためある程度情報を冗長に持っていたりする。複数あるとそれらの情報の間で矛盾も生じたりするのだが、それをどうするかは処理系にお任せ。古いAnsiの.zipに新しくUTF-8で追加とかも原理的に可能だが、普通はしない。多分。


OCaml unzip_O.zip

Rust unzip_RS.zip

Python3 unzip_P3.py


2022.06.02


「インデックス」へ戻る


OSTRACISM CO.

OSTRA / Takeshi Yoneki