特定のファイル名に対して特別な処理を実装できます。これを、それらの名前をマジックにすると呼びます。この機能の主な用途は、リモートファイルへのアクセスを実現することです(The GNU Emacs ManualのRemote Filesを参照してください)。
一種のマジックファイル名を定義するには、名前のクラス(その正規表現に一致するものすべて)を定義する正規表現と、一致するファイル名に対してEmacsのすべてのプリミティブなファイル操作を実装するハンドラを指定する必要があります。
変数 file-name-handler-alist
には、ハンドラのリストと、各ハンドラを適用するタイミングを決定する正規表現が格納されています。各要素は以下の形式となっています。
(regexp . handler)
ファイルアクセスおよびファイル名変換を行うすべてのEmacsプリミティブは、指定されたファイル名をfile-name-handler-alistと照合します。ファイル名がregexpに一致する場合、これらのプリミティブはhandlerを呼び出すことでそのファイルを処理します。
handler に指定される第1引数は、プリミティブ名をシンボルとして表したものです。残りの引数は、そのプリミティブに渡された引数となります。(これらの引数のうち、最初のものは多くの場合、ファイル名そのものです。)例えば、次のように記述した場合:
(file-exists-p filename)
また、filename にハンドラ handler がある場合、handler は次のように呼び出されます:
(funcall handler 'file-exists-p filename)
関数がファイル名である必要がある引数を2つ以上受け取る場合、それらのファイル名それぞれについてハンドラの有無を確認します。例えば、次のように記述した場合:
(expand-file-name filename dirname)
次に filename 用のハンドラを確認し、続いて dirname 用のハンドラを確認します。いずれの場合も、handler は次のように呼び出されます:
(funcall handler 'expand-file-name filename dirname)
次にhandlerは、filenameとdirnameのどちらを処理すべきかを判断する必要があります。
指定されたファイル名が複数のハンドラに一致する場合、ファイル名の末尾から一致が始まるハンドラが優先されます。このルールは、解凍などのジョブ用ハンドラが、リモートファイルアクセスなどのジョブ用ハンドラよりも先に処理されるように設定されています。
マジックファイル名ハンドラが処理する操作は以下の通りです。
abbreviate-file-name, access-file, add-name-to-file,
byte-compiler-base-file-name,
copy-directory, copy-file, delete-directory,
delete-file, diff-latest-backup-file, directory-file-name, directory-files, directory-files-and-attributes, dired-compress-file, dired-uncache, exec-path,
expand-file-name,
file-accessible-directory-p, file-acl, file-attributes,
file-directory-p, file-equal-p, file-executable-p, file-exists-p, file-group-gid,
file-in-directory-p, file-local-copy, file-locked-p, file-modes,
file-name-all-completions, file-name-as-directory, file-name-case-insensitive-p, file-name-completion, file-name-directory, file-name-nondirectory, file-name-sans-versions, file-newer-than-file-p, file-notify-add-watch, file-notify-rm-watch, file-notify-valid-p, file-ownership-preserved-p, file-readable-p, file-regular-p, file-remote-p,
file-selinux-context, file-symlink-p, file-system-info, file-truename, file-user-uid,
file-writable-p, find-backup-file-name,
get-file-buffer, insert-directory, insert-file-contents,
list-system-processes, load, lock-file, make-auto-save-file-name, make-directory, make-lock-file-name, make-nearby-temp-file, make-process, make-symbolic-link,
memory-info, process-attributes, process-file, rename-file,
set-file-acl, set-file-modes, set-file-selinux-context, set-file-times, set-visited-file-modtime, shell-command, start-file-process, substitute-in-file-name,
temporary-file-directory, unhandled-file-name-directory, unlock-file, vc-registered,
verify-visited-file-modtime,
write-region.
insert-file-contents のハンドラでは通常、visit 引数が nil 以外である場合、(set-buffer-modified-p nil)
を用いてバッファの変更済みフラグをクリアする必要があります。また、これによりバッファがロックされている場合はロックが解除されます。
ハンドラ関数は、上述のすべての操作、および将来的に追加される可能性のあるその他の操作を処理しなければなりません。これらの操作をすべて自前で実装する必要はありません。特定の操作において特別な処理を行う必要がない場合は、プリミティブを再呼び出しすることで、通常の方法でその操作を処理できます。認識できない操作については、常にプリミティブを再呼び出しすべきです。その実装方法の一つを以下に示します。
(defun my-file-handler (operation &rest args) ;; First check for the specific operations ;; that we have special handling for. (cond ((eq operation 'insert-file-contents) ...) ((eq operation 'write-region) ...) ... ;; Handle any operation we don’t know about. (t (let ((inhibit-file-name-handlers (cons 'my-file-handler (and (eq inhibit-file-name-operation operation) inhibit-file-name-handlers))) (inhibit-file-name-operation operation)) (apply operation args)))))
ハンドラ関数が、処理対象の操作に対して通常のEmacsプリミティブを呼び出すことにした場合、プリミティブが再度同じハンドラを呼び出して無限再帰に陥るのを防ぐ必要があります。上記の例では、変数
inhibit-file-name-handlers と inhibit-file-name-operation
を用いてその方法を示しています。これらは必ず上記に示した通りに使用してください。複数のハンドラが存在する場合や、それぞれにハンドラが設定され得る2つのファイル名を持つ操作において、正しく動作させるためにはこれらの詳細が極めて重要になります。
ファイルへの実際のアクセスに対して特に特別な処理を行わないハンドラ(リモートファイル名のホスト名補完を実装するものなど)は、non-nil safe-magic
プロパティを持つべきです。例えば、Emacs は通常、PATH
で見つかったディレクトリ名がマジックファイル名のように見える場合、先頭に ‘/:’
を付加することで、それがマジックファイル名になるのを防ぎます。しかし、それらに使用されるハンドラが non-nil safe-magic
プロパティを持っている場合、‘/:’ は付加されません。
ファイル名ハンドラは、どの操作を特殊な方法で処理するかを宣言するための operations
プロパティを持つことができます。このプロパティが nil
以外の値を持つ場合、それは操作のリストである必要があり、それらの操作のみがハンドラを呼び出します。これにより非効率性を回避できますが、主な目的は自動ロードされるハンドラ関数において、実際に処理すべき作業がある場合にのみロードされるようにすることです。
単にすべての操作を通常のプリミティブに委ねるだけではうまくいきません。例えば、ファイル名ハンドラが file-exists-p に適用される場合、通常の load コードでは適切に動作しないため、load
自体も自前で処理しなければなりません。しかし、ハンドラが operations プロパティを用いて
file-exists-p を処理しないことを示した場合は、load を非自明な方法で処理する必要はありません。
この変数には、特定の操作において現在使用が制限されているハンドラのリストが格納されています。
現在、特定のハンドラが抑制されている操作。
この関数は、ファイル名 file に対応するハンドラ関数を返します。該当するものがない場合は nil を返します。引数 operation
にはファイルに対して実行する操作を指定します。これは、ハンドラを呼び出す際に第1引数として渡す値となります。operation が inhibit-file-name-operation と等しい場合、またはハンドラの operations プロパティに見つからない場合は、この関数は nil を返します。
この関数は、ファイル filename
がローカルマシン上に存在しない場合、それをローカルマシン上の通常の非マジックファイルにコピーします。マジックファイル名が他のマシン上のファイルを参照する場合、file-local-copy
操作を処理する必要があります。リモートファイルアクセス以外の目的で使用されるマジックファイル名は、file-local-copy
を処理してはなりません。その場合、この関数はそのファイルをローカルファイルとして扱います。
filename がローカルである場合、マジックの有無にかかわらず、この関数は何も行わず nil を返します。それ以外の場合は、ローカルコピーファイルのファイル名を返します。
この関数は、filename がリモートファイルであるかどうかを判定します。filename がローカル(リモートではない)または nil
の場合、戻り値は nil となります。filename
が実際にリモートである場合、戻り値はリモートシステムを識別する文字列となります。
この識別子文字列には、ホスト名やユーザー名のほか、リモートシステムへのアクセスに使用される方式を指定する文字を含めることができます。例えば、ファイル名
/sudo::/some/file のリモート識別子文字列は /sudo:root@localhost: となります。
file-remote-p が 2
つの異なるファイル名に対して同一の識別子を返す場合、それらは同一のファイルシステム上に格納されており、互いにローカルでアクセス可能であることを意味します。これは例えば、両方のファイルに同時にアクセスするリモートプロセスを起動することが可能であることを意味します。ファイル名ハンドラの実装者は、この原則が成立することを保証する必要があります。
identification は、識別子のどの部分を文字列として返すかを指定します。identification にはシンボル method、user、host、または localname
を指定でき、それ以外の値は nil
と同様に扱われ、識別子文字列全体を返すことを意味します。上記の例では、リモートの user
識別子文字列は root となります。
リモート file
にメソッド、ユーザー名、またはホスト名が含まれていない場合は、それぞれのデフォルト値が返されます。identification および localname
で返される文字列は、既存の接続があるかどうかによって異なる場合があります。ファイル名ハンドラ固有の実装では、さらに identification シンボルをサポートしている場合があります。例えば、Tramp
は hop シンボルも認識します。
connected が nil でない場合、Emacs
がホストへのネットワーク接続を持たないときは、filename がリモートであってもこの関数は nil を返します。これは、接続が存在しない場合に接続を確立する際の遅延を避けたい場合に有用です。connected が never
である場合、すでに接続が存在していても、既存の接続を使用して識別子を返すことは決して行いません(それ以外の点では nil
の値と同様です)。これにより、ファイル名のローカル部分を展開するといった、接続固有のロジックを防ぐことができます。
この関数は、マジック属性を持たないディレクトリの名前を返します。マジック属性を持たない filename
の場合は、対応するディレクトリ名を返します(ディレクトリ名 を参照)。マジック属性を持つ filename
の場合は、ファイル名ハンドラが呼び出され、どの値を返すかは同ハンドラによって決定されます。ローカルプロセスから filename にアクセスできない場合は、ファイル名ハンドラは nil を返すことでその旨を示す必要があります。
これはサブプロセスを実行する際に便利です。すべてのサブプロセスにはカレントディレクトリとして機能する非マジックディレクトリが必要であり、この関数はそのディレクトリを確保するのに適した方法です。
この関数は、filename のローカルパートを返します。これはファイル名の中でリモートホストを識別する部分であり、通常はリモートファイル名からリモートホストおよびアクセス方法を指定する部分を取り除くことで得られます。例えば:
(file-local-name "/ssh:user@host:/foo/bar")
⇒ "/foo/bar"
リモートの filename の場合、この関数は、リモートプロセスの引数として直接使用できるファイル名(非同期プロセスの作成 および 同期プロセスの作成 を参照)と、リモートホスト上で実行するプログラムを返します。filename がローカルの場合、この関数はそれをそのまま返します。
リモートファイルの属性は、パフォーマンス向上のためにキャッシュできます。Emacsの制御外で属性が変更された場合、キャッシュされた値は無効となり、再読み込みが必要になります。
この変数を nil に設定すると、キャッシュされた値が期限切れになることはありません。Emacs
以外がリモートファイルを変更することがないと確信できる場合にのみ、慎重にこの設定を使用してください。t
に設定すると、キャッシュされた値は一切使用されません。これが最も安全な値ですが、パフォーマンスの低下を招く可能性があります。
妥協案としては、正の数値に設定する方法があります。これにより、キャッシュされてからその秒数分、キャッシュされた値が使用されることになります。リモートファイルを定期的にチェックする場合は、この変数を、連続するチェック間隔よりも短い値にバインドするのが良いでしょう。例えば:
(defun display-time-file-nonempty-p (file)
(let ((remote-file-name-inhibit-cache
(- display-time-interval 5)))
(and (file-exists-p file)
(< 0 (file-attribute-size
(file-attributes
(file-chase-links file)))))))
without-remote-files
マクロは、リモートファイル用のファイル名ハンドラを無効にした状態で body
形式を評価します。これらのファイル名はリテラルとして扱われます。
このマクロは、リモートファイルが出現し得ないことが明白な場合や、リモートファイル名を扱わないことが意図されている場合にのみ使用すべきです。また、file-name-handler-alist
によるチェックを削減できるため、よりパフォーマンスの高いコードになります。