Вернуться назад

MIME2

Я хочу создать текстовый файл с расширением ".bulka". Мне это нужно для того, чтобы при клике по этому текстовому файлу, он открывался с помощью определённой программы. Чтобы обычный текстовый файл открывался с помощью одной программы, например, Leafpad, а текстовый файл с расширением ".bulka" - с помощью VSCodium.

Создадим XML-файл "~/.local/share/mime/packages/application-x-bulochka.xml":


<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
    <mime-type type="application/x-bulka">
        <glob pattern="*.bulka"/>
    </mime-type>
</mime-info>

Обновим базу данных MIME:

   update-mime-database ~/.local/share/mime

Проверим, видит ли система наш новый тип:

   grep -R "application/x-bulka" ~/.local/share/mime/

Вот вывод, который я получил:

   /home/mark/.local/share/mime/globs:application/x-bulka:*.bulka
   /home/mark/.local/share/mime/types:application/x-bulka
   /home/mark/.local/share/mime/application/x-bulka.xml:
   grep: /home/mark/.local/share/mime/mime.cache: двоичный файл совпадает
   /home/mark/.local/share/mime/packages/application-x-bulochka.xml:    
   /home/mark/.local/share/mime/globs2:50:application/x-bulka:*.bulka

Теперь я хочу создать свой файл с расширением ".bulka":

   echo "Первая строка, которую мы вставляем в файл." >> myfile.bulka
   echo "Вторая строка" >> myfile.bulka

Этот файл (myfile.bulka) по факту является типом "text/plain". Но мы определили в файле application-x-bulochka.xml, что все файлы с расширением ".bulka" должны определяться как наш новый тип "application/x-bulka". Давайте попробуем определить тип файла разными способами:


   # с помощью утилиты file:
   file --mime-type myfile.bulka
      #  myfile.bulka: text/plain

   # с помощью xdg-mime
   xdg-mime query filetype myfile.bulka
      #  text/plain

   # с помощью gio:
   gio info myfile.bulka | grep content-type
      #  standard::content-type: application/x-bulka
      #  standard::fast-content-type: application/x-bulka

Как видим, разные утилиты определяют тип по-разному. Утилита file говорит, что наш файл имеет тип "text/plain". А вот gio говорит, что тип нашего файла: "application/x-bulka". А вот скрипт xdg-mime под капотом использует утилиту file, поэтому результат такой же, как когда мы спрашиваем тип у file.

Всё дело в том, что gio, когда пытается определить тип файла, опирается на нашу локальную базу данных MIME-типов, в которую мы добавили тип "application/x-bulka". А вот file залезает в файл и читает его содержимое и на основе содержимого определяет тип файла.

Файловые менеджеры, такие как PCManFM или Thunar, определяют тип файла на основе нашей MIME-базы данных. Поэтому когда мы кликаем по файлу, чтобы он открылся, система по расширению .bulka понимает, что это наш тип "application/x-bulka". Но если мы хотим, чтобы наш файл открывался с помощью нужной нам программы, то нам надо ещё привязать наш MIME-тип к конкретной программе.


   # Проверим, какое приложение является дефолтным для нашего типа:
   xdg-mime query default application/x-bulka

      # скорее всего эта команда ничего не вернёт, потому что мы пока не указывали ...
      # ... дефолтное приложение для нашего MIME-типа.

Теперь я хочу сделать так, чтобы при клике по файлу с расширением .bulka, он открывался с помощью программы VSCodium:


   # Привязываю VSCodium к типу application/x-bulka:
   xdg-mime default codium-wayland.desktop application/x-bulka

   # Ещё раз проверяю, какое приложение привязано к нашему типу:
   xdg-mime query default application/x-bulka
      # codium-wayland.desktop

   # Проверю также, какое приложение у меня привязано к обычному текстовому файлу:
   xdg-mime query default text/plain
      # leafpad.desktop

Теперь я могу в файловом менеджере кликать по текстовому файлу myfile.bulka и он будет открываться с помощью программы VSCodium. При этом обычный текстовый файл myfile.txt откроется с помощью Leafpad.

НО! Важно, что все программы опредляют тип по-своему. Если большинство файловых менеджеров опираются на базу данных MIME-типов в нашей системе, то некоторые программы используют утилиту file для определения типа файла. Например, если мы попробуем открыть файл с помощью xdg-open, то этот скрипт определит тип файла с помощью утилиты file и решит, что у нас обычный текстовый файл "text/plain" и соответственно откроет файл с помощью программы Leafpad.


   xdg-open myfile.bulka

      #  должен открыться с помощью Leafpad

Файловый менеджер lf также использует file, поэтому откроет наш файл через Leafpad.

Когда я привязываю программу к MIME-типу с помощью xdg-mime, привязка записывается в файл mimeapps.list.

# ~/.config/mimeapps.list:
# ~/.local/share/applications/mimeapps.list:
   # /usr/share/applications/mimeapps.list:
   # /usr/local/share/applications/mimeapps.list:

   [Default Applications]
   application/x-bulka=leafpad.desktop

Ещё несколько файлов, раскиданных по системе:

   # /etc/mime.types

   # /usr/share/mime/mime.cache
   # ~/.local/share/mime/mime.cache

   # /usr/share/applications/mimeinfo.cache
   # ~/.local/share/applications/mimeinfo.cache

   # ~/.local/share/applications/mimeapps.list
   # ~/.config/mimeapps.list

Если я не привязал тип text/x-bulka к какой-либо программе и кликаю по файлу myfile.bulka, то PCManFM предложит мне самому выбрать программу, которой я хочу открывать этот файл. Я выбираю VSCodium и файл открывается с помощью VSCodium. После этого привязка text/x-bulka и codium.desktop сохранится в файл ~/.config/mimeapps.list и при последующих кликах по файлу он всегда будет открываться с помощью VSCodium. Теперь все программы, которые используют shared-mime-info, будут открывать файлы с расширеним .bulka с помощью VSCodium.

Утилита update-desktop-database

Файлы mimeinfo.cache создаются и обновляются утилитой update-desktop-database.

Утилита update-desktop-database сканирует все .desktop-файлы в каталогах ~/.local/share/applications/ и /usr/share/applications/ и собирает из них информацию о том, какие программы заявляют поддержку каких MIME-типов. И она записывает эту информацию в файл mimeinfo.cache в том же каталоге.

То есть, mimeinfo.cache - это ускоритель. Программа каждый раз не лазит по всем .desktop-файлам, она заглядывает в mimeinfo.cache.

Допустим, у меня есть файл leafpad.desktop, в котором написано:

# /usr/share/applications/leafpad.desktop

  [Desktop Entry]
  Name=Leafpad
  Exec=leafpad %f
  MimeType=text/plain;text/x-bulka;

Я запускаю команду:

  update-desktop-database ~/.local/share/applications

# ~/.local/share/applications/mimeinfo.cache

  [MIME Cache]
  text/plain=leafpad.desktop;
  text/x-bulka=leafpad.desktop;

Обычно update-desktop-database не вызывают вручную. Обычно её вызывает пакетный менеджер автоматически после установки новой программы. Только если я вручную добавил новый .desktop-файл или изменил "MimeType=" в существующем, имеет смысл вызвать update-desktop-database. Или при добавлении нового MIME-типа.

То есть, чтобы программа официально стала поддерживать мой тип, я должен вручную открыть её .desktop-файл, добавить туда свой тип и запустить команду: update-desktop-database ~/.local/share/applications, чтобы обновить mimeinfo.cache.

Утилита file

Может, можно как-то сделать, чтобы утилита file тоже определяла наш файл не просто как "text/plain", а как "application/x-bulka"? Чтобы быть уверенным, что любая программа определит наш тип точно.

Это сделать можно. Но поскольку file читает содержимое файла, чтобы определить его тип, нам нужно добавить внутрь файла какой-то опознавательный знак - что-то, что будет отличать наш тип файла, от обычного текстового. Что-то типа сигнатуры. Допустим, пусть у нас файл начинается со строки "BULKA!".


   # содержимое моего файла myfile.bulka:
   BULKA!
   Первая строка, которую мы вставляем в файл.
   Вторая строка.

Теперь нам надо указать утилите file, что все текстовые файлы, которые начинаются со строки "BULKA!", относятся к типу "application/x-bulka".

# файл ~/.magic:
   0   string BULKA!   application/x-bulka
   !:mime   application/x-bulka

А вот пример файла ~/.magic с несколькими типами:

# файл ~/.magic:

   0   string   FOO!   application/x-foobar
   !:mime   application/x-foobar

   0   string KAKA!   application/x-kaka
   !:mime   application/x-kaka

   0   string KAKA2! application/x-kaka2
   !:mime application/x-kaka2

   0   string BULKA!   application/x-bulka
   !:mime application/x-bulka

Теперь мы можем проверить тип нашего файла с помощью file:

   file --mime-type myfile.bulka
      #  myfile.bulka: application/x-bulka

file определяет только по содержимому. А философия Freedesktop MIME: определять только по расширению, а не по магии!!!

shared-mime-info

shared-mime-info - это стандарт Freedesktop и набор файлов, которые описывают все известные MIME-типы для Linux (и других Unix-подобных систем).

   # Системные MIME-типы:
   /usr/share/mime/

   # Пользовательские MIME-типы:
   ~/.local/share/mime/

Программы, которые используют shared-mime-info, тоже могут определять тип файла по его содержимому. Для этого в XML-файлах предусмотрена директива <magic>. Давайте подправим наш файл application-x-bulochka.xml таким образом, чтобы shared-info-mime-программы прочитывали из первой строки файла нашу сигнатуру "BULKA!".

<!-- ~/.local/share/mime/packages/application-x-bulochka.xml -->

   <mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
       <mime-type type="application/x-bulka">
           <magic priority="80">
               <match type="string" value="BULKA!" offset="0"/>
           </magic>
           <!-- Распознавание файла по расширению я удалил -->
       </mime-type>
   </mime-info>

После того, как мы изменили содержимое XML-файла, запустим команду:

   update-mime-database ~/.local/share/mime

Теперь давайте попробуем определить тип файла myfile.bulka с помощью gio:


# Если у нас в файле первая строка: "BULKA!":
   gio info myfile.bulka | grep content-type
      #  standard::content-type: application/x-bulka
      #  standard::fast-content-type: application/octet-stream

# Если мы убираем "BULKA!" из первой строки:
   gio info myfile.bulka | grep content-type
      #  standard::content-type: text/plain
      #  standard::fast-content-type: application/octet-stream

То есть теперь gio определяет тип файла на основе его содержимого.

А теперь давайте усложним XML-правило. <match> может быть и вложенным.

<!-- ~/.local/share/mime/packages/application-x-bulochka.xml -->

<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
    <mime-type type="application/x-bulka">
        <magic priority="80">
            <match type="string" value="BULKA!" offset="0">
                <match type="string" value="V2" offset="6"/>
            </match>
        </magic>
        <!-- Распознавание файла по расширению я удалил -->
    </mime-type>
</mime-info>

Теперь gio будет определять тип файла как "application/x-bulka" только если первая строка равна: "BULKA!V2".

Ещё пример XML-файла. То же самое, что и в предыдущем примере, но только здесь мы добавили распознавание файла по расширению. Сначал идёт проверка по содержимому. Если "BULKA!" и "V2" не совпали, то тогда мы смотрим на расширение файла. Если оно равно: ".bulka", то тогда тип нашего файла всё равно: "application/x-bulka".

<!-- ~/.local/share/mime/packages/application-x-bulochka.xml -->

<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
    <mime-type type="application/x-bulka">
        <magic priority="80">
            <match type="string" value="BULKA!" offset="0">
                <match type="string" value="V2" offset="6"/>
            </match>
        </magic>
        <!-- Здесь <glob> находится ПОСЛЕ <magic> -->
        <glob pattern="*.bulka"/>
    </mime-type>
</mime-info>

В следующем XML-фрагменте мы переместили <glob> НАД <magic>, чтобы продемонстрировать, что порядок написания правил не влияет на приоритет. Приоритет всегда у MAGIC. Сначала проверяется содержимое файла, а потом расширение.

<!-- ~/.local/share/mime/packages/application-x-bulochka.xml -->

<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
    <mime-type type="application/x-bulka">
        <!-- Тут мы переместили <glob> выше - НАД <magic> -->
        <glob pattern="*.bulka"/>
        <magic priority="80">
            <match type="string" value="BULKA!" offset="0">
                <match type="string" value="V2" offset="6"/>
            </match>
        </magic>
    </mime-type>
</mime-info>

Аттрибут "priority" указывает приоритет данного правила. Например, у PNG-файлов (MIME-тип: image/png) в системе по умолчанию приоритет "50". Если я хочу создать своё правило для image/png, то мне нужно указать приоритет выше "50" и тогда моё правило переопределит системное.

Давайте посмотрим на пример XML-файла для image/png, который приведён на сайте freedesktop.org:

<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
   <mime-type type="image/png">
      <comment xml:lang="en">PNG image</comment>
      <comment xml:lang="af">png beeld</comment>
      ...
      <magic priority="50">
         <match type="string" value="\x89PNG" offset="0"/>
      </magic>
      <glob pattern="*.png"/>
   </mime-type>
</mime-info>

Здесь "\x89PNG" - это сигнатура первых байтов для формата PNG.