Jun 05, 2007

フォルダの指定で相対パスと絶対パスの両方の指定に対応する

掲示板で、XUL/Migemo勝手改造版の辞書フォルダの置き場所について相対パスでの指定もできるようにならないか、という要望が出てたのでがんばってみたよ。

以下、解説。

以下は、相対パスを渡したらfirefox.exe(firefox.bin)のあるフォルダからの相対パスとして解釈した絶対パスを返す関数の例。絶対パスが渡された時は何もしないでそのままのパスを返すので、パスからファイルオブジェクトを作る時にこの関数を挟むだけで相対パスと絶対パスの両方に対応できるという仕組み。

function getAbsolutePath(aPath)
{
  var file = Components.classes['@mozilla.org/file/local;1']
                       .createInstance(Components.interfaces.nsILocalFile);
  try {
    file.initWithPath(aPath);
    return aPath;
  }
  catch(e) {
  }

  // relative path
  var platform = Components.classes['@mozilla.org/network/protocol;1?name=http']
                           .getService(Components.interfaces.nsIHttpProtocolHandler).oscpu;
  if (platform.indexOf('Win') > -1) {
    aPath = aPath.replace(/^\.\.\./g, '\.\.\\\.\.')
          .replace(/\\\.\.\./g, '\\\.\.\\\.\.')
          .replace(/\\/g, '/');
  }
  const DIR = Components.classes['@mozilla.org/file/directory_service;1']
                        .getService(Components.interfaces.nsIProperties);
  var binDir = DIR.get('CurProcD', Components.interfaces.nsIFile);
  file.setRelativeDescriptor(binDir, aPath);
  return file.path;
}

nsILocalFileにはappendRelativePathというそのものズバリのメソッドがあるのでいけるかと思ったんだけど、説明を見ると、セキュリティの制限で「..」を相対パスに含められないと書いてあり、実際には使い物にならなかった。なので代わりに、setRelativeDescriptorの方を使っている。これはWindowsでいうところの「foobar\hogehoge\」という風なプラットフォーム標準のパス表記ではなく「foobar/hogehoge/」という風なURL風のパス表記で相対パスを指定する物で、こっちなら「..」も使えた。

パスの区切り文字の違いについては、とりあえずWindowsの場合だけ「\」を「/」に置換するようにしてみた。Mac OS Xは最近ではみんな「/」でパスを区切るそうだし、Unix/Linuxは言わずもがななので。XPCOMコンポーネント内ではwindow.navigator.platformを参照できないので、プラットフォームの判別にはnsIHttpProtocolHandlerを使っている。

あとオマケで、絶対パスを渡したらfirefox.exe(firefox.bin)からの相対パスにして返す関数も作ってみた。

function getRelativePath(aPath)
{
  var file = Components.classes['@mozilla.org/file/local;1']
                       .createInstance(Components.interfaces.nsILocalFile);
  file.initWithPath(aPath);

  const DIR = Components.classes['@mozilla.org/file/directory_service;1']
                        .getService(Components.interfaces.nsIProperties);
  var binDir = DIR.get('CurProcD', Components.interfaces.nsIFile);
  try {
    aPath = file.getRelativeDescriptor(binDir);
  }
  catch(e) {
    return '';
  }

  var platform = Components.classes['@mozilla.org/network/protocol;1?name=http']
                           .getService(Components.interfaces.nsIHttpProtocolHandler).oscpu;
  if (platform.indexOf('Win') > -1) {
    aPath = aPath.replace(/\//g, '\\');
  }

  return decodeURIComponent(escape(aPath));
}

パス区切りの変換はさっきの逆。なおgetRelativeDescriptor()の返り値はエンコーディング未設定の文字列とされていて、実際にはUTF-8のバイト列のようなので、最後に文字列と UTF-8 バイト列の相互変換と同じ方法でUnicodeに変換している。

XUL/Migemo [Forked Edition] 0.5.5では、フォルダを選択した時に「絶対パスと相対パスの長さを比較して短い方を設定値として保持する」という風な処理を入れてみたんだけど、これは余計だっただろうか?

エントリを編集します。

wikieditish message: Ready to edit this entry.











拡張機能