Spec-Zone.ru › Elisp

Создание «магических» имён файлов

Можно реализовать специальную обработку для определённых имён файлов. Это называется созданием магических имён. Основное применение этой возможности — реализация доступа к удалённым файлам (см. раздел Удалённые файлы в Руководстве 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)
  ;; 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. Используйте их точно так, как показано выше: эти детали важны для правильной работы в случае нескольких обработчиков и операций с двумя именами файлов, для каждого из которых может быть задан обработчик.

Обработчики, которые не выполняют ничего особенного при фактическом доступе к файлу — например, обработчики автодополнения имён хостов в именах удалённых файлов, — должны иметь свойство safe-magic со значением, отличным от nil. Например, Emacs обычно защищает имена каталогов, найденные в PATH, от превращения в магические, если они похожи на магические имена файлов, добавляя перед ними ‘/:’. Но если используемый для них обработчик имеет свойство safe-magic со значением, отличным от nil, ‘/:’ не добавляется.

У обработчика имени файла может быть свойство operations, указывающее, какие операции он обрабатывает нетривиальным образом. Если значение этого свойства отлично от nil, оно должно представлять собой список операций; в этом случае обработчик будет вызываться только для перечисленных операций. Это позволяет избежать лишних затрат, но главное назначение свойства — работа с загружаемыми по требованию функциями-обработчиками, чтобы они загружались только тогда, когда им действительно нужно что-либо сделать.

Просто передавать все операции обычным примитивам не получится. Например, если обработчик имени файла применяется к file-exists-p, он должен самостоятельно обрабатывать load, поскольку обычный код load в этом случае не будет работать должным образом. Однако если обработчик использует свойство operations, чтобы указать, что он не обрабатывает file-exists-p, ему не обязательно нетривиальным образом обрабатывать load.

Переменная: inhibit-file-name-handlers

Эта переменная содержит список обработчиков, использование которых в данный момент запрещено для определённой операции.

Переменная: inhibit-file-name-operation

Операция, для которой в данный момент запрещено использование определённых обработчиков.

Функция: find-file-name-handler file operation

Эта функция возвращает функцию-обработчик для имени файла file или nil, если обработчика нет. Аргумент operation должен задавать операцию, выполняемую над файлом, — значение, которое будет передано обработчику в качестве первого аргумента при вызове. Если operation равен inhibit-file-name-operation или отсутствует в свойстве operations обработчика, эта функция возвращает nil.

Функция: file-local-copy filename

Эта функция копирует файл filename в обычный немагический файл на локальном компьютере, если исходный файл ещё не находится на нём. Магические имена файлов должны обрабатывать операцию file-local-copy, если они относятся к файлам на других компьютерах. Магическое имя файла, используемое не для доступа к удалённым файлам, не должно обрабатывать file-local-copy; в этом случае функция будет считать файл локальным.

Если filename является локальным, независимо от того, магическое оно или нет, эта функция ничего не делает и возвращает nil. В противном случае она возвращает имя файла локальной копии.

Функция: file-remote-p filename &optional identification connected

Эта функция проверяет, является ли 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, который также распознаёт символ hop.

Если connected не равен nil, эта функция возвращает nil, даже если filename является удалённым, когда у Emacs нет сетевого соединения с его хостом. Это полезно, если нужно избежать задержки при установлении отсутствующих соединений. Если connected равен never, для возвращения идентификатора никогда не используется существующее соединение, даже если оно уже установлено (в остальном это соответствует значению nil). Это позволяет отключить любую логику, зависящую от соединения, например раскрытие локальной части имени файла.

Функция: unhandled-file-name-directory filename

Эта функция возвращает имя каталога, который не является магическим. Для немагического filename она возвращает соответствующее имя каталога (см. раздел Имена каталогов). Для магического filename она вызывает обработчик имени файла, который и определяет возвращаемое значение. Если локальный процесс не может получить доступ к filename, обработчик имени файла должен сообщить об этом, вернув nil.

Это полезно при запуске подпроцесса: для каждого подпроцесса требуется немагический каталог, который будет его текущим каталогом, и эта функция позволяет удобно получить такой каталог.

Функция: file-local-name filename

Эта функция возвращает локальную часть filename. Это часть имени файла, которая идентифицирует его на удалённом хосте; обычно её получают, удаляя из имени удалённого файла части, указывающие удалённый хост и способ доступа к нему. Например:

(file-local-name "/ssh:user@host:/foo/bar")
     ⇒ "/foo/bar"

Для удалённого filename эта функция возвращает имя файла, которое можно непосредственно использовать в качестве аргумента удалённого процесса (см. раздел Создание асинхронного процесса и раздел Создание синхронного процесса), а также в качестве программы для запуска на удалённом хосте. Если filename является локальным, функция возвращает его без изменений.

Пользовательский параметр: remote-file-name-inhibit-cache

Атрибуты удалённых файлов можно кэшировать для повышения производительности. Если они изменяются вне контроля 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…

Макрос without-remote-files вычисляет формы body с отключёнными обработчиками имён файлов для удалённых файлов. Такие имена файлов обрабатываются буквально.

Макрос следует использовать только в формах, где очевидно, что удалённые файлы не могут встретиться, либо когда намеренно не требуется обрабатывать имена удалённых файлов. Он также сокращает количество проверок с помощью file-name-handler-alist, что повышает производительность кода.

Copyright © 1990-1996, 1998-2026 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Magic-File-Names.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API