[Message Prev][Message Next][Thread Prev][Thread Next][Message Index][Thread Index]

Re: [MD:7103] Bug? of unix-to-dos-filename and dos-to-unix-filename



藤井です。

From: Hideyuki SHIRAI (白井秀行) <shirai@xxxxxxxxxxx>
Subject: [MD:7103] Bug? of unix-to-dos-filename and dos-to-unix-filename
Date: Wed, 21 Dec 2005 18:59:45 +0900 (JST)
Message-ID: <20051221.185945.143328023.shirai.hideyuki@xxxxxxxxxxxxxxxxxxx>
> dispicon で icon がでないので気付いたのですが、これってバグでしょ
> うか?それともなにか使いかたを間違っていますか?
> 
> (unix-to-dos-filename "c:/書/tmp.txt")
> => "c:\\書/tmp.txt"
> 
> (dos-to-unix-filename "c:\\書\\tmp.txt")
> => "c:/書\\tmp.txt"
> 
> となります。Meadow1.15 でも同じでした。
> 
> w32.c: normalize_filename() をじーーと見てもぼくには全然わかりま
> せん (_ _)

normalize_filename は文字列として Emacs の内部コードが渡されます。
しかし、w32.c:normalize_filename() は文字列として Windows の文字コード
を想定して実装してあるようです。

したがって、正しく動作しないケースがあります。(また、それとは別の問題も
抱えています。後述します)

まず、白井さんのケースを単純化して以下のような場合を考えます。

(dos-to-unix-filename "書\\")

以下を評価すれば分かると思いますが、emacs の内部コードは 0x92 0xbd
0xf1 0x5c となります。(最初の 3 バイトが「書」、最後の 1 バイトが「\」)

  (mapconcat (lambda (x) (format "0x%02x" x))
             (encode-coding-string "書\\" 'no-conversion) " ")

関数の冒頭では文字列のポインタ fp は文字列の冒頭 0x92 を指しています。
これはパス区切文字ではないので、CharNext で次の文字を探します。

CharNext は日本語環境では文字列を Shift_JIS (というか cp932) として扱い
ます。最初の 2 バイトは SJIS では「朕」になるので、CharNext の呼び出し
で 2 バイト進みます。

すると、0xf1 はパス区切文字ではないので、CharNext で次の文字を探します。
3 バイト目、4 バイト目 に漢字は割りあてられていないようですが、これも
SJIS の 2 バイト文字と判断されるので、CharNext は文字列終端を返します。

これによって「\」は無視されることになります。

正常に動作する例として、(dos-to-unix-filename "山\\") の場合を考えます
と、これの内部コードは 0x92 0xbb 0xb3 0x5c です。

先程と同様に文字列を処理をなぞってみると、最初の 2 バイトは「捗」になり
ますので、CharNext で 3 バイト目に移動します、3 バイト目は半角「ウ」に
なるので、CharNext は 1 バイト文字と判断し、4 バイト目に移動します。
4 バイト目の「\」を変換して、終了することになります。


あと、dos-to-unix-filename には、変数 w32-downcase-file-names が非 nil
であれば、ファイル名を英小文字に正規化するという機能もあります。しかし、
この機能は正常に動作していません。

# 失敗ケース 1
(let ((w32-downcase-file-names t))
  (dos-to-unix-filename "c:\\TEST")) ==> "c:/TEST"

# 失敗ケース 2
(let ((w32-downcase-file-names t))
  (dos-to-unix-filename "c:\\TEST\\TEST")) ==> "c:/tEsT/TEST"

# 失敗ケース 3
(let ((w32-downcase-file-names t))
  (dos-to-unix-filename "c:\\TEST\\")) ==> "c:/tEsT/"

# 失敗ケース 4
(let ((w32-downcase-file-names t))
  (dos-to-unix-filename "c:\\TEST\\TEST\\")) ==> "c:/tEsT/tEsT/"

つまり、以下の不具合があります。

  1. ファイル名がパス区切文字で終わっていない場合、最後のパス区切文字以
     降変換されない。
  2. 小文字変換が 2 文字目以降で処理されている。

--
藤井 正行 / Masayuki FUJII