Config dir

Материал из Tkabber Wiki

Перейти к: навигация, поиск

Внимание! реализация механизмов, обсуждаемых здесь, интегрирована в транк Ткаббера начиная с ревизии 859 (05 янв 2007). Реализованный механизм изложен ниже.

Также был обновлён раздел #4 "Configuration" документации Ткаббера — читайте tkabber.html на своей системе.

Содержание

Введение

Обсуждение новой схемы поддержки каталога конфигурации Ткаббера.

Идея: отказаться от схемы ~/.tkabber в Windows по причине того, что при существующей схеме каталог конфигурации на любой Windows-системе оказывается не там, где предполагается хранить конфигурацию на данной системе.

Конкретно нас интересует т.н. каталог "application data" ("appdata"). Предполагается хранить настройки Ткаббера в подкаталоге "Tkabber" этого каталога.

Получить путь каталога "appdata" можно тремя способами:

  • Переменная окружения APPDATA:
    • Появилась только в Windows 2000; присутствует во всей последующей линейке, включая Vista.
  • Переменная реестра HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\AppData:
    • Имеет место как минимум в Win98 SE;
    • Присутутвует во всей последующей линейке, однако в Vista весь ключ "Shell Folders" помечен как "устаревший", однако, он содержит правильные значения.
  • Процедура Win32 API "SHGetSpecialFolderPath":

Инфа по системам

  • Win98 (SE):
    • Каталог: %windir%\Application Data;
    • %APPDATA%нет;
    • Ключ "Shell Folders" — есть;
    • Примечание: уже есть %windir%.
  • WinNT 4.0 (русская):
    • Каталог: %USERPROFILE%\Данные;
    • %APPDATA%нет;
    • Ключ "Shell Folders" — есть;
    • Примечание: кривое значение %HOMEPATH% (корень загрузочного диска), в правильное место показывает %USERPROFILE% (%windir%\Profiles\<USERNAME>).
  • w2k, w2k3, XP:
    • Каталог: %APPDATA%;
    • Ключ "Shell Folders" — есть, но, откровенно говоря, он всегда был deprecated;
  • Vista:
    • Каталог: %APPDATA%;
    • Ключ "Shell Folders" — есть, но deprecated.

(TODO): 95? ME?

Проблемы

Наиболее разумным представляется вызов SHGetSpecialFolderPath, однако это не самый простой путь:

  • Данную функциональность в готовом виде обеспечивает пакет TWAPI, однако, он:
    • Не поставляется вместе с дистром AS;
    • Весит >600к в архиве, что будет препятствовать его попаданию в старпак и вообще отобьёт охоту у пользователей его устанавливать.
  • Возможно "цеплять" DLL, предоставляющую данный вызов API, например, при помощи Ffidl, однако:
    • Этот пакет так же не поставляется с AS;
    • Прямой вызов функции из DLL выглядит "не очень".

Путь с реестром и окружением не имеет такой "идеологической чистоты", как вызов SHGetSpecialFolderPath.

  • Недостаток: прямое чтение реестра и окружения в условиях огульного изменения своих же "стандартов" соответствующей фирмой является "ненадёжным"; кроме того, в ОС, следующей за Vista, соотв. раздел реестра может быть изъят полностью.
  • Достоинства:
    • Работа с переменными окружения доступна непосредственно;
    • Пакет registry для работы с реестром входит в "ядро" виндовой версии тикля, то есть очень велика вероятность его доступности даже в старпаке;
    • Обсуждаемый ключ реестра доступен на всех исследованных системах.

Рабочий вариант

При старте:

  • Ткаббер проверяет наличие переменной окружения TKABBER_HOME, если она есть, Ткаббер использует её как путь до каталога своих настроек.
  • Если её нет, то:
    • В Unix каталог настроек устанавливается в ~/.tkabber.
    • В Windows применяются описанные выше эвристики:
    1. Проверяется переменная окружения APPDATA;
    2. Проверяется ключ реестра "Shell Folders", если доступен пакет registry;
    3. К полученному каталогу добавляется элемент пути "Tkabber", и этот каталог используется как каталог настроек.

Вопросы:

  • Возможно, стоит сначала смотреть в реестр, а потом — в окружение;
  • Нужно предусмотреть разумный Fallback для различных версий Windows; идея ~/.tkabber для этого случая представляется не очень разумной.

Juriks, посовещавшись с kostix, предлагает: Запускать несколько Ткабберов с разными настройками с помощью таких вот скриптов:

@echo off
set TKABBER_HOME=c:\home\vasya_pupkin
start tkabber.exe

То есть искать папку с конфигами в таком порядке:

  1. переменная окружения TKABBER_HOME
  2. реестр
  3. прочие переменные оружения

kostix комментирует: реализованный в альфе Ткаббера вариант сначала ищет переменную окружения APPDATA, и только в случае неудачи смотрит реестр. Причины простые:

  • Пакет registry, хоть и входит в "ядро" виндового тикля, технически может отсутствовать (в ткаббер-паке или старпаке, к примеру), а вот помешать тиклю работать с окружением не может никакая конфигурация его рантайма;
  • Загрузка пакета — это доп. время для старта + доп. расход памяти. А пакет registry пока что больше нигде в Ткаббере не используется.
  • Реестр ничем не лучше переменной APPDATA в плане надёжности: точно так же, как в системе может быть кривое значение APPDATA, может быть крив ключ Shell Folders реестра. Только вызов соотв. функции Win32 API из shell32.dll даёт гарантии. Но он требует "неядерного" (и отсутствующего в дистре от AS) пакета Ffidl. Поэтому включение соответствующего кода в Ткаббер — вопрос сложный...

Добавка про SHGetSpecialFolderPath

SHGetSpecialFolderPath «замещена» процедурой SHGetFolderPath начиная с w2k, хотя и доступна на всех системах. Для нас это ничего не значит, так как нам для совместимости со старыми OS необходимо использовать старую версию.

На современных системах SHGetSpecialFolderPath реализована в shell32.dll. Также эта функция содержится в redistributable library SHFolder.dll, которая может включаться в поставку продукта и худо-бедно работать на любой OS. Starpack это, пожалуй, не спасёт, а вот Pack и выше может спокойно её использовать.

Тестовая реализация SHGetSpecialFolderPath через Ffidl

Скрипт

Скрипт, печатающий путевое имя каталога "application data" в Windows.

Создаваемая команда Tcl "SHGetSpecialFolderPath" способна возвращать имя любого стандартного каталога Windows (заданного соотв. ключом "CSIDL_...").

В случае неуспеха команда возвращает пустую строку.

#! /usr/bin/tclsh

package require Ffidl

ffidl::callout dll_SHGetSpecialFolderPath \
  {int pointer-utf16 int int} int \
  [ffidl::symbol shell32.dll SHGetSpecialFolderPathW]

proc SHGetSpecialFolderPath {what create} {
  array set CSIDL {
    CSIDL_DESKTOP	0
    CSIDL_INTERNET  1
    CSIDL_PROGRAMS	2
    CSIDL_CONTROLS	3
    CSIDL_PRINTERS	4
    CSIDL_PERSONAL	5
    CSIDL_FAVORITES	6
    CSIDL_STARTUP	7
    CSIDL_RECENT	8
    CSIDL_SENDTO	9
    CSIDL_BITBUCKET	10
    CSIDL_STARTMENU	11
    CSIDL_DESKTOPDIRECTORY	16
    CSIDL_DRIVES	17
    CSIDL_NETWORK	18
    CSIDL_NETHOOD	19
    CSIDL_FONTS	20
    CSIDL_TEMPLATES	21
    CSIDL_COMMON_STARTMENU	22
    CSIDL_COMMON_PROGRAMS	23
    CSIDL_COMMON_STARTUP	24
    CSIDL_COMMON_DESKTOPDIRECTORY	25
    CSIDL_APPDATA   26
    CSIDL_PRINTHOOD 27
    CSIDL_LOCAL_APPDATA 28
    CSIDL_ALTSTARTUP    29
    CSIDL_COMMON_ALTSTARTUP	30
    CSIDL_COMMON_FAVORITES	31
    CSIDL_INTERNET_CACHE   32
    CSIDL_COOKIES	33
    СSIDL_HISTORY	34
    CSIDL_COMMON_APPDATA	35
    CSIDL_WINDOWS	36
    CSIDL_SYSTEM	37
    CSIDL_PROGRAM_FILES	38
    СSIDL_MYPICTURES	39
    CSIDL_PROFILE	40
    СSIDL_SYSTEMX86	41
    CSIDL_PROGRAM_FILESX86	42
    CSIDL_PROGRAM_FILES_COMMON	43
    СSIDL_PROGRAM_FILES_COMMONX86	44
    CSIDL_COMMON_TEMPLATES	45
    CSIDL_COMMON_DOCUMENTS	46
    CSIDL_COMMON_ADMINTOOLS	47
    CSIDL_ADMINTOOLS	48
    CSIDL_CONNECTIONS	49
    CSIDL_COMMON_MUSIC	53
    CSIDL_COMMON_PICTURES	54
    CSIDL_COMMON_VIDEO	55
    CSIDL_RESOURCES	56
    CSIDL_RESOURCES_LOCALIZED	57
    CSIDL_COMMON_OEM_LINKS	58
    CSIDL_CDBURN_AREA	59
    CSIDL_COMPUTERSNEARME	61
    CSIDL_FLAG_DONT_VERIFY	0x4000
    CSIDL_FLAG_CREATE	0x8000
    CSIDL_FLAG_MASK	0xFF00
  }

  set bCreat [expr {$create ? 1 : 0}]

  set path [string repeat \u0000 300] ;# MAX_PATH is actually 260

  set ok [dll_SHGetSpecialFolderPath 0 $path $CSIDL($what) $bCreat]

  if {$ok} {
    set ix [string first \u0000 $path]
    if {$ix > 0} {
      return [string range $path 0 [expr {$ix - 1}]]
    }
  } else {
    return {}
  }
}

puts "appdata: [SHGetSpecialFolderPath CSIDL_APPDATA false]"

Запускать следует через tclsh, например, так:

C:\> tclsh sh.tcl

wish не имеет открытого канала stdout и puts там ничего интересного не делает.

Можете так же поменять последнюю строку (с puts) на

tk_messageBox -message [SHGetSpecialFolderPath CSIDL_APPDATA false]

и запускать скрипт "просто", например, двойным щелчком мышью на его файле в Explorer.

Замечания

Вписал статью в тиклевое вики.

ANSI (8-bit) vs Unicode (UTF-16)

  • Не совсем ясно, доступна ли юникодная версия (SHGetSpecialFolderPathW) в старых Win9x. Похоже, что доступна.
  • Не совсем ясно поведение Ffild по отношению к строкам в UTF-16: приёмный буфер должен быть MAX_PATH символов? Тогда что имеется в виду: тиклевые символы UTF-8 или виндовые символы UTF-16?

В любом случае, ANSI версия используется так:

ffidl::callout dll_SHGetSpecialFolderPath \
  {int pointer-utf8 int int} int \
  [ffidl::symbol shell32.dll SHGetSpecialFolderPathA]

Реализация в Ткаббере

Потребует более серьёзного подхода:

  • Пространство имён config;
  • "Пустая" реализация SHGetSpecialFolderPath для платформ, отличных от Windows;
  • Отслеживание успешного завершения создания callout'а для SHGetSpecialFolderPath с созданием пустой реализации (см. выше) в случае ошибки.
Личные инструменты