Вы можете реализовать специальную обработку для определённых имён файлов. Это называется созданием таких имён волшебными. Основное применение этой возможности — реализация доступа к удалённым файлам (см. Удалённые файлы в Руководстве GNU Emacs).
Чтобы определить вид волшебного имени файла, вы должны задать регулярное выражение для определения класса имён (всех, соответствующих этому регулярному выражению), а также обработчик, реализующий все примитивные файловые операции Emacs для соответствующих имён файлов.
Переменная file-name-handler-alist содержит список обработчиков
вместе с регулярными выражениями, определяющими, когда применять каждый
обработчик. Каждый элемент имеет следующий вид:
(regexp . handler)
Все примитивы Emacs для доступа к файлам и преобразования имён файлов
проверяют заданное имя файла по file-name-handler-alist. Если
имя файла соответствует regexp, примитивы обрабатывают этот файл,
вызывая handler.
Первый аргумент, передаваемый handler, — это имя примитива в виде символа; остальные аргументы — это аргументы, переданные этому примитиву. (Первым из этих аргументов чаще всего является само имя файла.) Например, если вы делаете:
(file-exists-p filename)
и для filename указан обработчик handler, то handler вызывается так:
(funcall handler 'file-exists-p filename)
Если функция принимает два или более аргумента, которые должны быть именами файлов, она проверяет каждый из этих имён на наличие обработчика. Например, если вы делаете:
(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 обычно нужно сбросить флаг
изменённости буфера с помощью (set-buffer-modified-p nil), если
аргумент visit не равен nil. Это также приводит к разблокировке
буфера, если он был заблокирован.
Функция-обработчик должна обрабатывать все вышеперечисленные операции и, возможно, другие, которые будут добавлены в будущем. Ей не обязательно реализовывать все эти операции самостоятельно — когда для определённой операции у неё нет специальных действий, она может повторно вызвать примитив, чтобы обработать операцию обычным способом. Она всегда должна повторно вызывать примитив для операции, которую не распознаёт. Вот один из способов это сделать:
(defun my-file-handler (operation &rest args) ;; Сначала проверяем конкретные операции, ;; для которых у нас есть специальная обработка. (cond ((eq operation 'insert-file-contents) …) ((eq operation 'write-region) …) … ;; Обрабатываем любую операцию, которую мы не знаем. (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. Будьте внимательны и используйте их
точно так, как показано выше; детали важны для правильного поведения в случае
нескольких обработчиков и для операций, имеющих два имени файла, для каждого
из которых может быть свой обработчик.
Обработчики, которые на самом деле не делают ничего особенного для реального
доступа к файлу — например, реализующие дополнение имён хостов для удалённых
имён файлов — должны иметь свойство safe-magic, не равное
nil. Например, Emacs обычно защищает имена каталогов, найденные
в PATH, от превращения в волшебные, если они выглядят как волшебные
имена файлов, добавляя к ним префикс ‘/:’. Но если обработчик,
который должен был бы использоваться для них, имеет свойство safe-magic,
не равное nil, то ‘/:’ не добавляется.
Обработчик имён файлов может иметь свойство operations, чтобы
объявить, какие операции он обрабатывает нетривиальным образом. Если это
свойство имеет значение, не равное nil, оно должно быть списком
операций; тогда только эти операции будут вызывать обработчик. Это позволяет
избежать неэффективности, но его основная цель — для автоматически
загружаемых функций-обработчиков, чтобы они не загружались, пока у них нет
реальной работы.
Простое делегирование всех операций обычным примитивам не работает.
Например, если обработчик имён файлов применяется к file-exists-p,
то он должен обрабатывать load самостоятельно, потому что обычный
код load в этом случае не будет работать правильно. Однако если
обработчик использует свойство operations, чтобы указать, что он не
обрабатывает file-exists-p, то ему не нужно нетривиально обрабатывать
load.
Эта переменная содержит список обработчиков, использование которых в данный момент запрещено для определённой операции.
Операция, для которой в данный момент запрещены определённые обработчики.
Эта функция возвращает функцию-обработчик для имени файла file
или nil, если его нет. Аргумент operation должен быть
операцией, выполняемой над файлом — значением, которое вы передадите
обработчику в качестве первого аргумента при его вызове. Если
operation равно inhibit-file-name-operation или не найдено
в свойстве operations обработчика, эта функция возвращает nil.
Эта функция копирует файл filename в обычный неволшебный файл
на локальной машине, если он ещё не находится на локальной машине. Волшебные
имена файлов должны обрабатывать операцию file-local-copy, если они
ссылаются на файлы на других машинах. Волшебное имя файла, используемое для
целей, отличных от удалённого доступа к файлам, не должно обрабатывать
file-local-copy; тогда эта функция будет считать файл локальным.
Если filename является локальным, независимо от того, волшебный он или
нет, эта функция ничего не делает и возвращает nil. В противном случае
она возвращает имя файла локальной копии.
Эта функция проверяет, является ли filename удалённым файлом. Если
filename локальный (не удалённый), возвращаемое значение — nil.
Если filename действительно удалённый, возвращаемое значение — строка,
идентифицирующая удалённую систему.
Эта строка-идентификатор может включать имя хоста и имя пользователя, а также
символы, обозначающие метод доступа к удалённой системе. Например, строка
удалённого идентификатора для имени файла /sudo::/some/file —
это /sudo:root@localhost:.
Если file-remote-p возвращает один и тот же идентификатор для двух
разных имён файлов, это означает, что они хранятся в одной файловой системе
и могут быть доступны локально по отношению друг к другу. Это означает,
например, что можно запустить удалённый процесс, обращающийся к обоим файлам
одновременно. Разработчики обработчиков имён файлов должны обеспечить
выполнение этого принципа.
identification определяет, какая часть идентификатора должна быть
возвращена в виде строки. identification может быть символом
method, user, host или localname; любое другое
значение обрабатывается как nil и означает возврат полной строки
идентификатора. В примере выше строка удалённого идентификатора user
была бы root.
Если удалённый file не содержит метод, имя пользователя или имя хоста,
возвращается соответствующее значение по умолчанию. Строка, возвращаемая для
identification localname, может отличаться в зависимости от
того, существует ли соединение. Специфичные для обработчиков имён файлов
реализации могут поддерживать дополнительные символы identification;
см., например, (tramp)Tramp, который также знает символ
hop.
Если connected не равен nil, эта функция возвращает nil,
даже если filename удалённый, если у Emacs нет сетевого соединения
с его хостом. Это полезно, когда вы хотите избежать задержки установки
соединений, если они не существуют. Если 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,
что приводит к более производительному коду.