Anatoly Vorobey (avva) wrote,
Anatoly Vorobey
avva

Categories:

линукс-3



Решил сделать так, чтобы терминал (gnome-terminal), когда открывается, ставил себя в режим кодировки cp1251 по умолчанию.
А то он по умолчанию выбирает кодировку текущей локали, которая у меня вообще что-то дико-дифолтное. Пытаюсь выставить нужный LC_CTYPE - ничего не меняется. Тыкаюсь в меню - нигде нет чего-то типа "set default character coding". Думаю, может, в меню нет, но в конфигурации где-то сохраняется; лезу в .gconf/apps/gnome-terminal/ и читаю вручную .xml-файлы, ничего не нахожу (потом уже понял, что вместо чтения вручную надо идти в гномовский Application->System Tools->Configuration Editor). Смотрю на опции командной строки, нахожу опцию геометрии (чтобы запускать его сразу 80x40, а не 80x24, как по умолчанию, удобо), а кодировок нет.

Лезу в исходники gnome-terminal, блуждаю по encoding.c. terminal_encoding_init()... но нет, она создаёт список кодировок, но в этом списке никак не указано, какая текущая. grep encoding *.c . Ага, есть есть вызов terminal_widget_set_encoding() в terminal-window.c, внутри функции change_encoding_callback(). Очевидно, эта функция вызывается, когда я в меню меняю кодировку. Больше
terminal_widget_set_encoding нигде не вызывается, значит, change_encoding_callback() должен кто-то вызвать, когда программа начинает работать, чтобы поставить первоначальный дифолтный выбор. grep change_encoding_callback *.c . Ага, это происходит в fill_in_encodings_menu(), при инициализации меню. Где там ставится первоначальный выбор? charset = terminal_widget_get_encoding (w);

Т.е. первоначальную кодировку мы берём у библиотеки терминала. Смотрим, что это за функции terminal_widget_get_encoding() и terminal_widget_set_encoding(). Они определяются два раза: в terminal-widget-vte.c и terminal-widget-zvt.c . Смотрим на эти файлы - судя по всему, две разных имплементации виджета терминала. Та, которая zvt, попроще, и в terminal_widget_set_encoding() ничего не делает. Т.к. я могу менять кодировку, очевидно, мой gnome-terminal был построен с terminal-widget-vte.c, которая пользуется функциями vte_terminal_get_encoding() и vte_terminal_set_encoding(). Их в исходниках нет... это какая-то библиотека. Ищем vte... ага, есть такая библиотека гномовская, распакуем её исходники и посмотрим на эти функции. Из них становится понятно, что именно vte занимается перекодировкой входа/выхода, используя функции iconv. Откуда vte берёт первоначальный charset, к-й она использует? vte_terminal_get_encoding() читает кодировку из внутренней структуры терминала: terminal->pvt->encoding; значит, надо искать, кто её туда ставит. grep encoding *.c . Никто не трогает это поле, кроме vte_terminal_set_encoding(), странно; ага, эта функция принимает имя кодировки, но если вместо него передаётся NULL, то она вызывает g_get_charset(), и ставит кодировку, к-ю возвращает эта функция. Проверяем, где вызывается vte_terminal_set_encoding(), и да, действительно, во время инициализации терминала есть вызов с аргументом NULL. Т.е. надо искать, что такое g_get_charset(), похоже, что-то гномовское, но (grep g_get_charset *.c) в vte такой функции нет. find /usr/include -exec grep -q g_get_charset {} \; -print . Ага, /usr/include/glib-2.0/glib/gunicode.h .

Полезли распаковывать исходники glib2. g_get_charset() нашлась в glib/gutf8.c ; вначале вызывает _g_locale_charset_raw() - судя по имени, эта фунцкия наконец доходит до локали - но потом пользуется ещё каким-то внутренним кэшем для алиасов и канонизации имён кодировок, для чего использует g_utf8_get_charset_internal(), передавая ей полученную от _g_locale_charset_raw() строку и получая обратно имя кодировки, к-е сохраняет в кэше и возвращает наружу. Смотрим в _g_locale_charset_raw, она внутри glib/libcharset/libcharset.c ; ага, полезли кучи #define'ов на разные операционные системы и вызовы функций типа langinfo() и других функций локали. Здесь я ничего не добьюсь, чтобы разобраться, как мне грамотно выставить локаль, чтобы g_get_charset() получила 'cp1251', мне нужно лезть в исходники glibc, или искать грамотную документацию по локали (не-на-ви-жу). Смотрю в g_utf8_get_charset_internal(), и вот наконец приятный сюрприз:
const char *charset = getenv("CHARSET"); если это выставлено, она игнорирует то, что даёт локаль, и использует эту кодировку.

Уф. До свидания, исходники. "CHARSET=cp1251 gnome_terminal" - работает! Пытаюсь прописать это в шорткате, который я себе поставил для вызова терминала - фиг, говорит, не может запустить программу, значит, не пользуется он шеллом. Ищу где-то в опциях шортката (который здесь launcher называется) место для определения переменных среды - фигвам, хижина такая у эскимосов. Ладно, надоело: cat >~/scripts/gnome-terminal-cp1251
#!/bin/bash
export CHARSET=cp1251
exec gnome-terminal $*

Выставляю нужные разрешения на файл, ставлю его имя в launcher ($HOME он тоже не понимает, дубина... а где этому гному сказать, чтобы $HOME/scripts в $PATH добавил? Ладно, лень разбираться, в следующий раз, прописываю полный путь). Всё.


Это было такое экспериментальное описание "рабочего процесса".
Всё это сделать было быстрее, чем описать сейчас. Интересно, если бы я разбирался в локали, знал бы заранее, что можно CHARSET поставить? Или если бы в правильное место полез RTFMить, не пришлось бы по исходникам бегать? Очень вероятно, но, в конце концов, мне сам этот процесс нравится. Надо не увлекаться, однако, слишком легко много времени потерять.

Теперь программа терминала запускается с кодировкой cp1251, и mutt тоже (после того, как я прописал set charset=cp1251 в .muttrc), так что для чтение русских писем ничего дополнительного не надо делать.
Subscribe
  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 46 comments