[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

25.11 Making Certain File Names "Magic"

You can implement special handling for certain file names. This is called making those names magic. The principal use for this feature is in implementing remote file names (see section `Remote Files' in The GNU Emacs Manual).

To define a kind of magic file name, you must supply a regular expression to define the class of names (all those that match the regular expression), plus a handler that implements all the primitive Emacs file operations for file names that do match.

The variable file-name-handler-alist holds a list of handlers, together with regular expressions that determine when to apply each handler. Each element has this form:

 
(regexp . handler)

All the Emacs primitives for file access and file name transformation check the given file name against file-name-handler-alist. If the file name matches regexp, the primitives handle that file by calling handler.

The first argument given to handler is the name of the primitive; the remaining arguments are the arguments that were passed to that primitive. (The first of these arguments is most often the file name itself.) For example, if you do this:

 
(file-exists-p filename)

and filename has handler handler, then handler is called like this:

 
(funcall handler 'file-exists-p filename)

When a function takes two or more arguments that must be file names, it checks each of those names for a handler. For example, if you do this:

 
(expand-file-name filename dirname)

then it checks for a handler for filename and then for a handler for dirname. In either case, the handler is called like this:

 
(funcall handler 'expand-file-name filename dirname)

The handler then needs to figure out whether to handle filename or dirname.

Here are the operations that a magic file name handler gets to handle:

add-name-to-file, copy-file, delete-directory, delete-file, diff-latest-backup-file, directory-file-name, directory-files, dired-call-process, dired-compress-file, dired-uncache, expand-file-name, file-accessible-directory-p,
file-attributes, file-directory-p, file-executable-p, file-exists-p,
file-local-copy, file-modes, file-name-all-completions,
file-name-as-directory, file-name-completion, file-name-directory, file-name-nondirectory, file-name-sans-versions, file-newer-than-file-p, file-ownership-preserved-p, file-readable-p, file-regular-p, file-symlink-p, file-truename, file-writable-p, find-backup-file-name, get-file-buffer,
insert-directory, insert-file-contents, load, make-directory, make-symbolic-link, rename-file, set-file-modes, set-visited-file-modtime, shell-command,
unhandled-file-name-directory, vc-registered, verify-visited-file-modtime,
write-region.

Handlers for insert-file-contents typically need to clear the buffer's modified flag, with (set-buffer-modified-p nil), if the visit argument is non-nil. This also has the effect of unlocking the buffer if it is locked.

The handler function must handle all of the above operations, and possibly others to be added in the future. It need not implement all these operations itself--when it has nothing special to do for a certain operation, it can reinvoke the primitive, to handle the operation "in the usual way". It should always reinvoke the primitive for an operation it does not recognize. Here's one way to do this:

 
(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)))))

When a handler function decides to call the ordinary Emacs primitive for the operation at hand, it needs to prevent the primitive from calling the same handler once again, thus leading to an infinite recursion. The example above shows how to do this, with the variables inhibit-file-name-handlers and inhibit-file-name-operation. Be careful to use them exactly as shown above; the details are crucial for proper behavior in the case of multiple handlers, and for operations that have two file names that may each have handlers.

Variable: inhibit-file-name-handlers
This variable holds a list of handlers whose use is presently inhibited for a certain operation.

Variable: inhibit-file-name-operation
The operation for which certain handlers are presently inhibited.

Function: find-file-name-handler file operation
This function returns the handler function for file name file, or nil if there is none. The argument operation should be the operation to be performed on the file--the value you will pass to the handler as its first argument when you call it. The operation is needed for comparison with inhibit-file-name-operation.

Function: file-local-copy filename
This function copies file filename to an ordinary non-magic file, if it isn't one already.

If filename specifies a magic file name, which programs outside Emacs cannot directly read or write, this copies the contents to an ordinary file and returns that file's name.

If filename is an ordinary file name, not magic, then this function does nothing and returns nil.

Function: unhandled-file-name-directory filename
This function returns the name of a directory that is not magic. It uses the directory part of filename if that is not magic. For a magic file name, it invokes the file name handler, which therefore decides what value to return.

This is useful for running a subprocess; every subprocess must have a non-magic directory to serve as its current directory, and this function is a good way to come up with one.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

This document was generated on May 2, 2002 using texi2html