" insert combining acute accent when writing Russian texts easily imap <C-v>ё <C-v>u0301Теперь достаточно ввести и, а затем <C-v>ё. Как указано в комментарии, этот маппинг подходит только для ввода русского текста, поскольку ссылается на символ ё в своем определении. Обычно в русских раскладках буква ё расположена на месте тильды в левом верхнем углу клавиатуры, поэтому данный маппинг мне кажется и удобным, и логичным.
четверг, 23 октября 2014 г.
vim: ввод символов с ударением
Все время забываю, как это делается, поэтому запишу сюда как напоминание.
Прежде всего, здесь можно почитать, как вводить составные и юникодные символы в vim. А здесь приведена таблица с составными диакритическими знаками Unicode. Составные они потому, что комбинируются вместе с предыдущим введенным символом в новый единый символ.
В русской типографике для обозначения ударения можно использовать символ U+0301. Соответственно, для того, чтобы ввести, например, символ и́, нужно сначала ввести собственно и, а затем <C-v>u0301. Это действительно тяжело запомнить, и к тому же приходится временно переключать раскладку для ввода символа u, поэтому в .vimrc можно записать маппинг, например такой:
среда, 22 октября 2014 г.
nginx: неожиданный proxy_pass
Возьмем простую конфигурацию nginx.
worker_processes 1; error_log /var/log/nginx/error.log info; events { worker_connections 1024; } http { server { listen 80; server_name proxy; location / { if ($arg_x = '') { proxy_pass http://127.0.0.1:8010; break; } proxy_pass http://$arg_x; } } server { listen 8010; server_name backend; location / { echo 'I am in backend'; } } }Что ответит сервер proxy на простой запрос без аргументов? Логично предположить, что в ответе будет строка I am in backend. Однако, вместо этого будет получена ошибка 500 Internal Server Error, а в логе появится запись invalid URL prefix in “http://”! Вы можете в этом убедиться, введя в адресной строке браузера адрес localhost или запустив curl.
curl 'http://localhost/' <html> <head><title>500 Internal Server Error</title></head> <body bgcolor="white"> <center><h1>500 Internal Server Error</h1></center> <hr><center>nginx/1.7.6</center> </body> </html>Если же ввести адрес http://localhost/?x=127.0.0.1:8010, то получим ожидаемую строку I am in backend. Почему nginx не видит заданный в конфигурации дословно адрес 127.0.0.1:8010 в случае не заданного аргумента x и пытается выполнить проксирование на пустой адрес http://, в результате чего и возникает ошибка 500? Ответ кроется в реализации модуля прокси и принятого в нем механизма наследования конфигурации уровня локации (location configuration merge). Дело в том, что директива proxy_pass по-разному обрабатывает параметры, содержащие дословные адреса (такие как
http://127.0.0.1:8010
) и переменные (такие как http://$arg_x
). Связанные с ними данные хранятся независимо в конфигурации уровня локации модуля прокси. Так, дословный адрес записывается в conf->upstream.upstream
, а переменный адрес — в специальные структуры, на которые ссылаются массивы conf->proxy_lengths
и conf->proxy_values
: фактическое значение адреса в этом случае вычисляется во время выполнения программы путем вычисления заранее скомпилированного выражения, построенного на основании этих данных. Если директива proxy_pass была задана дословным адресом, то указатель conf->upstream.upstream
будет на него ссылаться, а указатели conf->proxy_lengths
и conf->proxy_values
останутся нулевыми. Если же адрес в proxy_pass был задан с помощью переменных, то, наоборот, указатель conf->upstream.upstream
останется нулевым, в то время как указатели conf->proxy_lengths
и conf->proxy_values
будут ссылаться на данные, необходимые для вычисления фактического адреса перенаправления.
Проверка условия if ($arg_x = '')
в простом запросе без аргумента x дает положительный результат и nginx заменяет конфигурацию уровня локации на новую. Новая конфигурация была создана еще во время чтения конфигурационного файла и ее состав полностью зависит от механизма наследования конфигурации уровня локации. Этот механизм реализован в функции ngx_http_proxy_merge_loc_conf(), нас интересует следующий фрагмент:
if (conf->upstream.upstream == NULL) { conf->upstream.upstream = prev->upstream.upstream; conf->vars = prev->vars; } if (conf->proxy_lengths == NULL) { conf->proxy_lengths = prev->proxy_lengths; conf->proxy_values = prev->proxy_values; }То есть в случае, если proxy_pass внутри условия if не содержит дословного адреса, то он будет унаследован сверху. И в случае отсутствия переменного адреса, тот тоже будет унаследован сверху. Наш proxy_pass внутри if содержит дословный адрес, при этом он также унаследует переменный адрес
http://$arg_x
. В итоге получаем возможность задействовать оба механизма обработки адреса одновременно, что само по себе создает неопределенность! А если взглянуть на реализацию ngx_http_proxy_handler(), то выяснится, что эта неопределенность разрешается в пользу переменного адреса (там, где вызывается ngx_http_proxy_eval()). Вот почему мы получаем ошибку 500: несмотря на то, что директива proxy_pass внутри if получила новый дословный адрес, она также унаследовала переменный адрес сверху, который модуль прокси рассматривает как основной и, поскольку аргумент x не задан, выражение http://$arg_x
вычисляется как http://
и nginx не в состоянии выполнить такое перенаправление.
Теперь, когда мы знаем врага в лицо, нужно попробовать избежать его воздействия. Давайте не будем наследовать настройки proxy_pass сверху, пусть они настраиваются на одном уровне в разных блоках if.
location /1 { if ($arg_x = '') { proxy_pass http://127.0.0.1:8010; break; } if ($arg_x) { proxy_pass http://$arg_x; break; } return 500; }Такая конфигурация, несмотря на всю ее подозрительную странность, сработает: будет выбрана одна из директив proxy_pass в зависимости от присутствия в запросе аргумента x, при этом директива
return 500
никогда не сработает, поскольку два условия для if сделаны намеренно взаимоисключающими.
Проверяем.
curl 'http://localhost/1' I am in backend curl 'http://localhost/1?x=127.0.0.1:8010' I am in backendДругой способ предусматривает использование директивы rewrite.
location /2 { if ($arg_x) { rewrite ^ /3 last; } proxy_pass http://127.0.0.1:8010; } location /3 { internal; proxy_pass http://$arg_x; }В разных локациях разные настройки proxy_pass: никакого наследования конфигураций между ними быть не может. Данный способ, хотя и будет работать в нашем случае, не совсем верный по сути. Проблема в том, что если на сервер proxy придет запрос вида
/2?x=127.0.0.1:8010
, то он будет передан на сервер backend в виде /3?x=127.0.0.1:8010
, поскольку proxy_pass в location /3
будет использовать измененный URI (кстати, до версии nginx 1.1.12 в этом случае использовался оригинальный URI, затем это было исправлено как баг, хотя я бы не стал называть это багом, поскольку всегда рассматривал это как фичу, при этом весьма удобную). Итак, окончательный вариант, в котором исходный URI сохраняется.
location /2 { if ($arg_x) { rewrite ^(.*)$ /3$1$is_args$args last; } proxy_pass http://127.0.0.1:8010; } location ~* /3(.*) { internal; proxy_pass http://$arg_x$1; }Проверяем.
curl 'http://localhost/2' I am in backend curl 'http://localhost/2?x=127.0.0.1:8010' I am in backendИ еще один вариант, максимально приближенный к оригинальному. Что если создать переменную, в которую записать дефолтный адрес перенаправления, и ссылаться на нее в директиве proxy_pass внутри блока if?
location /4 { set $default_addr 127.0.0.1:8010; if ($arg_x = '') { proxy_pass http://$default_addr; break; } proxy_pass http://$arg_x; }Это должно сработать, поскольку наследование конфигурации в случае выполнения условия в if будет сводиться к замене выражения для вычисления адреса перенаправления.
curl 'http://localhost/4' I am in backend curl 'http://localhost/4?x=127.0.0.1:8010' I am in backendРаботает, что и требовалось доказать. Update. Описанное здесь поведение было признано багом и исправлено в основной ветке разработки nginx.
вторник, 14 октября 2014 г.
Как я обновлял ghc до версии 7.8 в Fedora 20
Официально в Fedora до сих пор старые ghc-7.6.3 и haskell-platform-2013.2.0.0. И по всей видимости они останутся в следующей Fedora 21 (см. здесь). Однако, выход есть, и для этого не нужно собирать ghc с нуля. Достаточно установить репозитории ghc-7.8 и cabal-install от Йенса Петерсена — мэйнтейнера haskell в Fedora (кстати, у него есть и другие интересные репозитории, связанные с haskell: статическая сборка pandoc, функциональный язык idris, vim-подобный редактор yi — все это можно найти по этой ссылке).
Поскольку у меня уже стояли ghc и haskell-platform, то первое, что я сделал — это удалил их из системы.
yum remove ghc* haskell-platform
Затем настроил репозитории Йенса и установил ghc и cabal-install.
yum install ghc cabal-install
Заметьте — пакета-коллекции haskell-platform в репозитории ghc-7.8 нет, да он и не нужен, поскольку все пакеты можно установить с помощью cabal. Естественно, я и раньше использовал cabal для установки пакетов haskell и делал это с опцией --global
, соответственно в директории /usr/local/lib/ скопилось множество директорий типа conduit-1.1.6/, содержащих в себе единственную поддиректорию ghc-7.6.3/ с данными пакетов. Новый ghc, как выяснилось позже, устанавливает все пакеты в единую директорию /usr/local/lib/x86_64-linux-ghc-7.8.3/ (имя последней поддиректории, очевидно, зависит от системы), поэтому старые пакетные директории следовало удалить. Вот команда, которая позволяет перечислить все пакетные директории:
find /usr/local/lib -mindepth 2 -maxdepth 2 -type d -name 'ghc-7*' | xargs dirname
Теперь требуется убедиться, что список не содержит неожиданных верхнеуровневых директорий типа /usr/local/lib, потому что сейчас мы будем их удалять! Я постарался максимально обезопасить команду find с помощью опций -mindepth
и -maxdepth
, поэтому появление неожиданных директорий в списке очень маловероятно. Итак, удаляем пакетные директории.
find /usr/local/lib -mindepth 2 -maxdepth 2 -type d -name 'ghc-7*' | xargs dirname | xargs rm -rf
Также удаляем директории .ghc/ и .cabal/ в домашних директориях пользователя и суперпользователя (так как я устанавливал пакеты от имени суперпользователя с опцией --global
), но делаем это осторожно, вспоминая, нет ли там каких-нибудь персональных настроек. У меня все персональные настройки находятся в файле .ghci в домашней директории, поэтому удаление этих директорий не могло привести к потере настроек.
На этом этапе мы еще не закончили с удалениями. Данные о пакетах все еще хранятся в директории /usr/lib64/ghc-7.6.3/package.conf.d/, в этом можно убедиться, выполнив команду
ghc --info | grep "Global Package DB"
Хотя после установки нового ghc эта команда покажет новую поддиректорию ghc-7.8.3/, старая директория будет по-прежнему находиться в системе. Удаляем проблемные пакеты из реестра (хотя сейчас я не уверен, что этот шаг вообще был нужен, поскольку Global Package DB указывает на новую директорию):
for p in `ghc-pkg check 2>&1 | grep problems | awk '{print $6}' | sed -e 's/:$//'` ; do ghc-pkg unregister $p --force ; done
(этот скрипт я позаимствовал отсюда) и физически удаляем директорию /usr/lib64/ghc-7.6.3/:
rm -rf /usr/lib64/ghc-7.6.3
Вот теперь все готово для установки новых пакетов. Лично я устанавливал пакеты pandoc, happy, alex, hlint, MissingH, ghc-vis и lens с опцией --global
.
cabal update ... cabal install pandoc --global ... cabal install happy --global ... cabal install alex --global ... cabal install hlint --global ... cabal install MissingH --global ... cabal install gtk2hs-buildtools --global ... yum install librsvg2-devel ... cabal install ghc-vis --global ... cabal install -ftransformer3 transformers-compat --global ... cabal install --constraint="transformers==0.3.0.0" lens --global ...Я заменил выводы команд многоточиями, поскольку они многословны и малоинтересны. Порядок установки пакетов может иметь значение! Для установки ghc-vis потребовалось установить пакет gtk2hs-buildtools и системный пакет librsvg2-devel. После установки ghc-vis имеет смысл скопировать содержимое файла /usr/local/share/ghc-vis-0.7.2.3/ghci в домашний .ghci. Установка пакета lens выглядит необычно, но без этих опций его не установить, собственно решение я нашел здесь. Во время установки пакетов вы возможно заметите такие предупреждения:
<no location info>: Warning: Couldn't figure out linker information! Make sure you're using GNU gcc, or clangТочно такие же сообщения вы увидите при компиляции любой программы новым ghc. Но это только в том случае, если ваша локаль не является английской (моя локаль русская). Я долго искал в чем здесь проблема и таки нашел, вот этот баг на трекере ghc. В общем-то ничего опасного: ghc запускает команду
gcc -v
, ищет в выводе строку gcc version ...
и просто выводит предупреждение, если ее не находит. В русской локали gcc выводит строку gcc версия ...
. Лично меня раздражают эти бессмысленные предупреждения, обновление ghc с исправлением выйдет не скоро, поэтому я просто добавил в .bashrc новый алиас ghc-make.
alias ghc-make='LANG=C ghc --make'Да, кстати. В ghc 7.8 появилась возможность настроить prompt2 — приглашение командной строки ghci для многострочных команд. Лично я выделяю приглашения разными цветами, вот мои настройки в .ghci:
:set prompt "\ESC[38;5;208m%s> \ESC[m" :set prompt2 "\ESC[38;5;221m%s| \ESC[m"
Подписаться на:
Сообщения (Atom)