18 November 2006

perl: интересное описание perl6 в журнале у avva

Описание возможностей perl6 в плане расширения синтаксиса языка
опубликовал LJ пользователь avva.

perl: как посмотреть содержимое переменной

Для того, чтоб легко посмотреть содержимое переменной в perl -- в частности, HASH и ARRAY (LIST)
можно использовать модуль Dumper


use Data::Dumper;
.
.
.
print Dumper $variable;

14 November 2006

erlang: Using namespaces and packages in Erlang

It's possible to use separate namespaces for different applications in erlang,
thus converting modules from app_module1, app_module2 and etc to
app.module1, app.module2 names. Modules in package can call each other by using shorter name, i.e. module1:func() and module2:func1().


For complete article see:
http://www.erlang.se/publications/packages.html

22 September 2006

Erlang: String processing optimiziation

I wish to share an experience which I had today when writing code.

I had a following function, which splits binary on enter character. I used it to split binaries to separate lines.

Original code looked like this -- which is obvious dumb solution for
that task.


break_on_nl(B) -> break_on_nl(B, []).
break_on_nl(<<"\n", Tail/binary>>, Res) -> {lists:reverse(Res), Tail};
break_on_nl(<<>>, Res) -> {lists:reverse(Res), <<>>};
break_on_nl(<<C, Tail/binary>>, Res) -> break_on_nl(Tail, [C|Res]).


I.e. just cutting one character a time, checking that we reached \n and returning collected string and remaining tail if we reached it.

After some occasional loon on this code I realized, that I can greatly improve it. Instead rebuilding binary to list and then reversing list I could just check every byte of binary, check if it is an Enter char, and then return binary splitted to 2 parts.

Code like that:


break_on_nl1(B) -> break_on_nl1(0, B).
break_on_nl1(Len, Bin) when Len == size(Bin) ->
{Bin, <<>>};
break_on_nl1(Len, Bin) ->
<<Msg:Len/binary, Symb, Tail/binary>> = Bin,
case Symb of
$\n -> {Msg, Tail};
_ -> break_on_nl1(Len+1, Bin)
end.


Main advantage is that I do not modify original binary and it passed
from one call to next one without copying.

This little code rewrite gave me 2 times speed increase, which was quite essential, because I have strong timing constrains for that code.

18 September 2006

Как обойти модель безопасности JavaScript

Довольно часто возникает задача -- подгружать данные динамически. В случае, если источник данных находится на том же домейне, что и загруженный скрипт, то модель безопастности бразуера позволит обратится к нему при помоэи XmlHttpRequest (если совпадают протокол, доменное имя и порт в URL с которого загружен JavaScript и с которого запраиваются данные).

В противном случае возможно загрусить данные с удаленного сайта, только создав динамически таг <script> и указав все данные запроса в URL, в виде GET запроса.
Код который загрузится с удаленного сайта должен вызвать какую-либо функцию JavaScript, что бы сообщить об успешной загрузке данных.

Такой подход реализован в библиотеке Subsys_JsHttpRequest.

А упрощенный вариант реализован в SimpleXMLRPC -- библиотеке, которая реализует несколько разных типов запросов (XMLRPC, получение JSON-кодированных данных при помощи POST запроса и получение данных при помощи включения .js файла с удаленного сервера).

Вот что делает код библиотеки --

span = document.createElement("SPAN");
span.id = "__jsload__" + id;
span.style.display = 'none';
span.innerHTML = 'Text for stupid IE.' +
'';
s = span.getElementsByTagName("script")[0];
s.language = "JavaScript";
document.body.appendChild(span);
setTimeout(function() { if (s.setAttribute) s.setAttribute('src', href); else s.src = href; }, 10);


Т.е. создается span, в нем таг script, a потом в качестве src у скрипта ставится URL. После добавления скрипта в DOM дерево при помощи appendChild начинается скачивание JS скрипта.

После того, как он был скачан -- можно либо обращаться к его переменным и функциям, так как js файл полностью импортируется в область видимости(scope) загружающе программы --
при условии, что загруженный файл имел в себе правильный JS код, либо -- как это делает SimpleXMLRPC -- JS файл состоит из строчки типа
XMLRPC.dataReceived(id, ActualData);
, которая и дает знать вызывающему скрипту, что данные уже загружены.

PS. нужно быть внимательным и удалять старые, ненужные объекты <script>

Author: Gaspar Chilingarov
Keywords: javascript, ajax, web 2.0, xmlrpc, json-rpc, simplexmlrpc

14 September 2006

Firefox2: google search

Firefox2 have very pleasant feature to show Google Suggest, when you typing search terms in Searchbox at topmost-right corner. Nice.


Author: Gaspar Chilingarov

Erlang to Vim: first alpha-version patches

Erlang to Vim integration



Now you can try a first alpha-version of patches for vim, which turn Vim into C-node for erlang. Thus you can easily send rpc calls from vim to erlang and get results in vim script. This will provide ability to use distel in vim too, not only in emacs, allowing faster development and more comfortable IDE for erlang programmers.

Please see vim erlang integration page for more instructions.

Keywords: Vim, Erlang, integration, vim scripts, erlang ide

Author: Gaspar Chilingarov

08 September 2006

Erlang programming: Converting numbers to strings

You can see - how to convert numbers to textual representatoin, using erlang/yaws as a backend.
Module to process english is about 100 lines of code.

Here it is:

 
number(N) ->
lists:foldl(
fun
(El, []) -> El;
([], List) -> List;
(El, List) -> List ++ " " ++ El
end,
[], to_number(N)).

to_number(N) when is_list(N) ->
try
to_number(list_to_integer(N), 0)
catch
error:_ -> ["Error converting to text. Please enter integer."]
end;
to_number(N) when is_list(N) -> to_number(list_to_integer(N), 0);
to_number(N) when is_integer(N) -> to_number(N, 0).

to_number({0}, _Order) -> [];
to_number({N}, Order) -> to_number(N, Order);

to_number(0, N) when N>0 ->
[];

to_number(0, 0) -> ["zero"];
to_number(1, _Order) -> ["one"];
to_number(2, _Order) -> ["two"];
to_number(3, _Order) -> ["three"];
to_number(4, _Order) -> ["four"];
to_number(5, _Order) -> ["five"];
to_number(6, _Order) -> ["six"];
to_number(7, _Order) -> ["seven"];
to_number(8, _Order) -> ["eight"];
to_number(9, _Order) -> ["nine"];
to_number(10, _Order) -> ["ten"];
to_number(11, _Order) -> ["eleven"];
to_number(12, _Order) -> ["twelve"];
to_number(20, _Order) -> ["twenty"];
to_number(30, _Order) -> ["thirty"];
to_number(40, _Order) -> ["forty"];
to_number(50, _Order) -> ["fifty"];
to_number(60, _Order) -> ["sixty"];
to_number(70, _Order) -> ["seventy"];
to_number(80, _Order) -> ["eighty"];
to_number(90, _Order) -> ["ninety"];
to_number(N, _Order) when N < 20 ->
[composite(N-10) ++ "teen"];
to_number(N, Order) when N < 100 ->
[ to_number({r(N, 10)}, Order), to_number({m(N, 10)}, Order) ];
to_number(N, Order) when N < 1000 ->
[ to_number({d(N, 100)}, Order), "hundred" | to_number({m(N, 100)}, Order) ];
to_number(N, 0) when is_integer(N) ->
N1 = m(N, 1000),
to_number(
d(N, 1000),
1,
to_number({N1}, 0)
).

to_number(_N, Order, Result) when Order > 11 -> Result;
to_number(0, _Order, Result) -> Result;
to_number(N, Order, Result) ->
Mod = m(N, 1000),
to_number(d(N, 1000),
Order+1,
to_number(Mod, Order) ++ [ order(Order, m(N, 1000)) ] ++ Result).

composite(3) -> "thir"; % for numbers in range 13-19
composite(4) -> "four";
composite(5) -> "fif";
composite(6) -> "six";
composite(7) -> "seven";
composite(8) -> "eight";
composite(9) -> "nine".

order(_, 0) -> "";
order(O, N) -> order(O);
order(O, Number) ->
"order = (" ++ integer_to_list(O) ++ ")".

order(1) -> "thousand"; % 10 3
order(2) -> "million"; % 10 6
order(3) -> "billion"; % 10 9
order(4) -> "trillion"; % 10 12
order(5) -> "quadrillion"; % 10 15
order(6) -> "quintillion"; % 10 18
order(7) -> "sextillion"; % 10 21
order(8) -> "septillion"; % 10 24
order(9) -> "octillion"; % 10 27
order(10) -> "nonillion"; % 10 30
order(11) -> "decillion". % 10 33

d(N, Mod) -> N div Mod.
r(N, Mod) -> (N div Mod)*Mod.
m(N, Mod) -> N rem Mod.

07 September 2006

Инструкция по установке поддержки армянского языка в X Window System

Инструкция по установке поддержки армянского языка в X Window System

Инструкция по настройке FreeBSD системы для отображения и ввода армянских текстов

Отображение армянских текстов:

Если у Вас система дистибутив X Windows -- XFree86 версии 4.0 и выше, то как у Вас есть как минимум несколько UNICODE .BDF фонтов (из семейства -misc-fixed-*), которые содержат в себе символы армянского алфавита. Если у Вас система X Windows -- Xorg, то в ней тоже есть фонты поддерживающие армянский. Вам просто нужно выбрать их в текстовом редакторе или браузере для отображения армянского текста.


Набор армянских текстов:

1. Необходимо открыть терминал (с системой X Windows распространяется терминал xterm).
2. Необходимо получить привелегии суперпользователя -- root.
Для этого нужно набрать в терминале

##
su -
##
В ответ на запрос команды su введите пароль суперпользователя. Если команда su сразу пишет "su: Sorry", это значит, что вы не входите в группу wheel и не имеете право получать привелегии суперпользователя. Тогда вам придется войти в систему пользователем root.

3. Необходимо проверить, что в системе присутствуют файлы
##
/usr/X11R6/lib/X11/xkb/symbols/am
/usr/X11R6/lib/X11/xkb/symbols/pc/am
##

Если их нет, но их можно скачать ««здесь»». Первый файл нужно скопировать в /usr/X11R6/lib/X11/xkb/symbols/am, второй в /usr/X11R6/lib/X11/xkb/symbols/pc/am, и выполнить команды
##
chmod 644 /usr/X11R6/lib/X11/xkb/symbols/am
chmod 644 /usr/X11R6/lib/X11/xkb/symbols/pc/am
chown root:wheel /usr/X11R6/lib/X11/xkb/symbols/am
chown root:wheel /usr/X11R6/lib/X11/xkb/symbols/pc/am
##

Если такие файлы уже есть на вашей системе, стоит выполнить команду
##
grep phonetic /usr/X11R6/lib/X11/xkb/symbols/am \
/usr/X11R6/lib/X11/xkb/symbols/pc/am
##

Если у Вас уже есть фонетическая армянская раскладка -- то Вы увидете следуещее:
##
/usr/X11R6/lib/X11/xkb/symbols/am:xkb_symbols "phonetic" {
/usr/X11R6/lib/X11/xkb/symbols/pc/am:xkb_symbols "phonetic" {
##

Если команда grep ничего не вывела, то Вам стоит скачать файлы раскладок с этого сайта и скопировать на место существующих у Вас.

В старых файлах армянской раскладки была только typewriter раскладка.


4. Необходимо изменить конфигурацию системы X Window, чтоб включить поддержку армянского.

Необходимо открыть в любом редакторе файл /etc/X11/XF86Config .

Найдите следующий текст

##
Section "ServerLayout"
Identifier "XFree86 Configured"
Screen 0 "Screen0" 0 0
InputDevice "Mouse0" "CorePointer"
InputDevice "Keyboard0" "CoreKeyboard"
EndSection
##

Выделенный жирным текст -- это название Вашей секции описания клавиатуры. Найдите секцию, где Identifier такое-же значение

Она должна выглядеть приблизительно так:
##
Section "InputDevice"
Identifier "Keyboard0"
Driver "keyboard"
Option "XkbModel" "pc105"
Option "AutoRepeat" "250 30"
EndSection
##

Вам необходимо добавить несколько строк, чтоб она выглядела следующим образом:
##
Section "InputDevice"
Identifier "Keyboard0"
Driver "keyboard"
Option "XkbModel" "pc105"
Option "AutoRepeat" "250 30"

Option "XkbLayout" "us,am(phonetic)"
Option "XkbVariant" "winkeys"
Option "XkbOptions" "grp:alt_shift_toggle,grp_led:scroll"
EndSection
##


Строчки XkbLayout описывают что используются 2 раскладки -- английская и армянская фонетическая. Если Вы пользуетесь typewriter раскладкой, то Вам нужно убрать (phonetic), что бы получилось
##
Option "XkbLayout" "us,am"
##


XkbVariant указывает,что разрешены дополнительные клавиши (левая/правая Win и Menu).

XkbOptions указывает, что для переключения между раскладками будет использоваться - Alt+Shift. При использовании армянской раскладки на клавиатуре будет гореть индикатор ScrollLock.
Правый Alt+Shift и кнопка Menu будут переключать раскладки по циклу us -> am, а левый Alt+Shift будет переключать в последовательности - am -> us и если у вас X.org, то не будет переключаться с раскладки us на am.

Далее вам необходимо сохранить изменения и перезагрузить X Server.




Инструкция по настройке Linux систем для отображения и ввода армянских текстов

Если вы используете современные системы (скажем, Fedora Core 4), то по умолчанию система уже поддерживает отображение армянского алфавита.
Для настройки ввода армянского текста, если Вы используете оболочку GNOME, достаточно щелкнуть на панели правой кнопкой, выбрать "Add to panel", выбрать из списка апплетов "Keyboard indicator" и нажать OK. После того, как апплет добавится на панель, необходимо правой кнопкой мыши щелкнуть на нем, выбрать пункт меню "Preferences", после чего добавить из списка возможных раскладок армянскую раскладку.

Если Linux система не поддерживает добавления новых раскладок через графический интерфейс, то Вам придется выполнить те-же шаги, что и для FreeBSD, с той разницей, что указанные файлы могут ханодится в других местах.


Keywords: armenian unicode layout, x11, freebsd, linux, writing in armenian, armenian font
Author: Gaspar Chilingarov
Copyright 2005

DNS: инсталляция DJB DNS (tinydns) на CentOS

VPS (Virtual Private Server) от vpsland идет по умолчанию с установленным bind. A bind, как известно, это не только проблемы с безопастностью, это и безбожно отжираемая память, которой на VPS хостинге и так мало. Поэтому пришлось снести bind и поставить djbdns (tinydns).

Для уставноки скопируйте этот скрипт к себе на сервер, сделайте файл со скриптом выполняемым (chmod 755 имя_файла), и запустите его.

#!/bin/sh
#Create the following directories:
mkdir -p /usr/local/djb/build
mkdir -p /usr/local/djb/patches

# Download and extract the three patches:
# //get the patches:
cd /usr/local/djb/patches
#//Download the following (small download, still ... be kind to his bandwidth .. :) ):
wget http://www.thedjbway.org/patches/djb_errno_patches.tgz
#//Extract:
tar -xzvf djb*.tgz
#//Several .patch files should output

#Download and extract the three packages:

#//Change directories and download the main packages:
cd /usr/local/djb/build
wget http://cr.yp.to/ucspi-tcp/ucspi-tcp-0.88.tar.gz
wget http://cr.yp.to/daemontools/daemontools-0.76.tar.gz
wget http://cr.yp.to/djbdns/djbdns-1.05.tar.gz

#Extract and patch each of the three packages:
#(you should still be in the /usr/local/djb/build directory for all three of the following)

#Extract and Patch ucspi:

cd /usr/local/djb/build

#//ucspi
gunzip ucspi-tcp-0.88.tar
tar -xf ucspi-tcp-0.88.tar
cd ucspi-tcp-0.88
patch -p1 < ../../patches/ucspi-tcp-0.88.errno.patch
#//[output from patch...]
patch -p1 < ../../patches/ucspi-tcp-0.88.a_record.patch
#//[output from patch...]
patch -p1 < ../../patches/ucspi-tcp-0.88.nobase.patch
#//[output from patch...]
make setup check
./install
./instcheck

#Extract and Patch daemontools:

cd /usr/local/djb/build

#//daemontools
gunzip daemontools-0.76.tar
tar -xpf daemontools-0.76.tar
rm daemontools-0.76.tar
cd admin/daemontools-0.76
patch -p1 < ../../../patches/daemontools-0.76.errno.patch
#//[output from patch...]
package/install


# Extract and Patch djbdns:

cd /usr/local/djb/build

#//djbdns
gunzip djbdns-1.05.tar
tar -xf djbdns-1.05.tar
cd djbdns-1.05
patch -p1 < ../../patches/djbdns-1.05.errno.patch
#//[output from patch...]
make
make setup check
./install
./instcheck

#Next, you'll add the necessary user accounts and use the tinydns-conf script to create an installation based on the IP of your box:
#xx.xx.xx.xx represents the IP address represented with the output of "ifconfig" on your box.

echo "Input your ip address, (ifconfig output follows)"
ifconfig
read IP

#//create the group and users -- modify if needed
groupadd -g 91 dns
useradd -g 91 -u 91 -d /nonexistent -c "tinydns" -s /sbin/nologin Gtinydns
useradd -g 91 -u 92 -d /nonexistent -c "tinydns" -s /sbin/nolodin Gdnslog

#//here the tinydns-conf script copies files and creates the useable IP-based installation
cd /usr/local/bin
tinydns-conf Gtinydns Gdnslog /etc/tinydns $IP

#Once that's done, you'll set a symbolic link to the svcscan application to keep tinydns running if crashed:

#//create the symbolic link of djbdns under the symbolic link of the svcscan application
cd /
mkdir /service
ln -s /etc/tinydns /service
#//force us to be patient and wait for the service to start
sleep 5
#//check to make sure the service is running
svstat /service/tinydns
#//should return something like "/service/tinydns: up (pid 24957) 4870 seconds"



Ключевые слова: dns, djb dns, tinydns, системное администрирование, исталляция, unix, CentOS, Debian
Автор: Гаспар Чилингаров (Gaspar Chilingarov)

DNS: installing DJB DNS (tinydns) on fresh CentOS

Setup tinydns on CentOS or Debian



As a user of vpsland.com I've got a VPS with CentOS (which have packaging structure very simular to Debian) with bind installed.
This is not the best solution - because bind in both insecure and uses too much memory.

Here it is a small script to install tinyDNS to the VPS server.

Cut it here, paste into some file on your server, make that file executable (chmod 755 filename) and run it.


#!/bin/sh
#Create the following directories:
mkdir -p /usr/local/djb/build
mkdir -p /usr/local/djb/patches

# Download and extract the three patches:
# //get the patches:
cd /usr/local/djb/patches
#//Download the following (small download, still ... be kind to his bandwidth .. :) ):
wget http://www.thedjbway.org/patches/djb_errno_patches.tgz
#//Extract:
tar -xzvf djb*.tgz
#//Several .patch files should output

#Download and extract the three packages:

#//Change directories and download the main packages:
cd /usr/local/djb/build
wget http://cr.yp.to/ucspi-tcp/ucspi-tcp-0.88.tar.gz
wget http://cr.yp.to/daemontools/daemontools-0.76.tar.gz
wget http://cr.yp.to/djbdns/djbdns-1.05.tar.gz

#Extract and patch each of the three packages:
#(you should still be in the /usr/local/djb/build directory for all three of the following)

#Extract and Patch ucspi:

cd /usr/local/djb/build

#//ucspi
gunzip ucspi-tcp-0.88.tar
tar -xf ucspi-tcp-0.88.tar
cd ucspi-tcp-0.88
patch -p1 < ../../patches/ucspi-tcp-0.88.errno.patch
#//[output from patch...]
patch -p1 < ../../patches/ucspi-tcp-0.88.a_record.patch
#//[output from patch...]
patch -p1 < ../../patches/ucspi-tcp-0.88.nobase.patch
#//[output from patch...]
make setup check
./install
./instcheck

#Extract and Patch daemontools:

cd /usr/local/djb/build

#//daemontools
gunzip daemontools-0.76.tar
tar -xpf daemontools-0.76.tar
rm daemontools-0.76.tar
cd admin/daemontools-0.76
patch -p1 < ../../../patches/daemontools-0.76.errno.patch
#//[output from patch...]
package/install


# Extract and Patch djbdns:

cd /usr/local/djb/build

#//djbdns
gunzip djbdns-1.05.tar
tar -xf djbdns-1.05.tar
cd djbdns-1.05
patch -p1 < ../../patches/djbdns-1.05.errno.patch
#//[output from patch...]
make
make setup check
./install
./instcheck

#Next, you'll add the necessary user accounts and use the tinydns-conf script to create an installation based on the IP of your box:
#xx.xx.xx.xx represents the IP address represented with the output of "ifconfig" on your box.

echo "Input your ip address, (ifconfig output follows)"
ifconfig
read IP

#//create the group and users -- modify if needed
groupadd -g 91 dns
useradd -g 91 -u 91 -d /nonexistent -c "tinydns" -s /sbin/nologin Gtinydns
useradd -g 91 -u 92 -d /nonexistent -c "tinydns" -s /sbin/nolodin Gdnslog

#//here the tinydns-conf script copies files and creates the useable IP-based installation
cd /usr/local/bin
tinydns-conf Gtinydns Gdnslog /etc/tinydns $IP

#Once that's done, you'll set a symbolic link to the svcscan application to keep tinydns running if crashed:

#//create the symbolic link of djbdns under the symbolic link of the svcscan application
cd /
mkdir /service
ln -s /etc/tinydns /service
#//force us to be patient and wait for the service to start
sleep 5
#//check to make sure the service is running
svstat /service/tinydns
#//should return something like "/service/tinydns: up (pid 24957) 4870 seconds"



These instructions were taken from http://www.interworx.com/forums/archive/index.php/t-967.html forum.

Keywords: dns, djb dns, cent os, tinydns, daemontools, svstat, setting up dns, system administration
Author: Gaspar Chilingarov

04 September 2006

Язык erlang: Трассирование вызовов функции -- пошагово

Трассирование процессов в Erlang



Трассирование процессов -- Зачем?


Иногда, если программа на языке erlang вылетает с ошибкой (скажем, badmatch), то нужно понять, откуда берется эта ошибка и при каких входных данных. Зачастую при этом пользоватся дебаггером проблематично -- либо очень большое количество вызовов, либо хочется так-же отловить вызовы многих функций в модуле.

Трассирование процессов -- Как?


Из шелла, где запущена программа, или прямо в коде программы нужно добавить следующие команды.

dbg:tracer(),
dbg:p(new, c),
dbg:tpl(module_name, [{'_',[],[{return_trace}]}]),



Трассирование процессов -- Детали


Первая строчка запускает собственно трассировщик, ее нужно один раз в течении работы программы.
Вторая строчка указывает, что мы хотим трассировать и в каких процессах.
Первый аргумент dbg:p -- какие процессы мы хотим трассровать

  • Pid (скажем self() или любой другой pid) -- только вызовы функций, которые будут делаться указанным процессом

  • new -- только новосоздаваемые процессы, уже существующие процессы трассироваться не будут

  • existing -- будут трассироваться только уже существующие процессы

  • all -- трассировать все процессы в системе -- существующие и новосоздаваемые.



второй аргумент -- обозначает, что трассировать. Самые интересные значения:

  • c -- вызовы функций

  • m -- поученные процессом сообщения

  • [c,m] -- и то и другое



Третья строчка -- это 50% магии данного совета -- из какого модуля именно трассировать функции и что делать с этим трейсом.
dbg:tpl бывает с разным arity - 2,3,4. Нас интересуют варианты с арити 2 и 3
dbg:tpl(Module, MatchSpec) и dbg:tpl(Module, Function, MatchSpec).

MatchSpec -- для того что бы получить распечатку трассировщика на консоли нужно указывать именно
[{'_',[],[{return_trace}]}]

в первом варианте - с 2 аргументами, трассироваться будут все функции модуля,
а во втором варианте -- будут трассироваться только функции с данным именем.

Трассирование процессов -- Пример


Вот пример трассировки функции erlang:date

1> dbg:tracer().
{ok,<0.34.0>}
2> dbg:p(all, c).
{ok,[{matched,nonode@nohost,26}]}
3> dbg:tpl(erlang, date, [{'_',[],[{return_trace}]}]).
{ok,[{matched,nonode@nohost,1},{saved,1}]}
4> erlang:date().
(<0.32.0>) call erlang:date()
{2006,9,4}(<0.32.0>) returned from erlang:date/0 -> {2006,9,4}


Если включена трассировка функций всего модуля и функция делает вызовы к локальным функциям модуля -- то вы увидете аргументы и возвращаемые значения вызываемых функций.

02 September 2006

FreeBSD: восстановление загрузчика boot loader-а

Для восстановления загрузчика FreeBSD(восстановления boot loader) нужно загрузиться с любого CD FreeBSD (желательно версии 5.0 и выше) и в меню выбрать Configure -> Partitions, выбрать тот диск, на котором нужно восстановить загрузчик, а потом просто нажать W - инсталлятор выдаст предупреждение, после чего предложит выбрать ставить на этот диск boot loader (загрузчик) или нет. Нужно выбрать BootMgr из меню и нажать OK. Все, мы восстановитли загрузчик, теперь можно выйти из инсталлятора и перезагрузиться.

31 August 2006

Shell programming: Extreme shell programming

Extreme shell programming


A year ago I've wrote article for russian magasine System Adminsitrator about using /bin/sh in tiny installations -- to emulate some shell commands, do text matching and etc. Shell fans should like.

Table of Contents
Advanced Shell Programming
Pattern matching in shells (emulating regular expressions)
cut_atomic
cut
Another way to extract positional parameters
Pattern cheching
Arrays in shell
Breaking up input and line-by-line processing


Advanced Shell Programming



This article describes advanced and sometimes not-so-obvious programming tricks
which can be used in shell programming. I suggest you to use all power, that gives standard FreeBSD's /bin/sh shell and use every bit of functionality which have this binary.



Pattern matching in shells (emulating regular expressions)


Sometimes it's necessary to perform some patterm matching in shell script, and,
for example, extract some variable from string, but you have no opportunity to use standard unix utilities like sed, awk or something heavier (like perl :). In this case shell's pattern removal can help.



Please refer to sh(1), Parameter Expansion section.



Here are some examples for simulating some unix utilities by means of shell
functions. All functions given return result in `result` variable, and return 0 on success and non-zero value on error. In case if there are no result to return they reset `result` varible to empty string.



cut_atomic


cut_atomic function cuts string from left of string till the firs occurence of
delimiter, first argument is a delimiter itself - it can by one symbol or some
string, and all other parameters are string where to search. Shell patterns are
always greedy, so please keep this in mind when passing wildcards as delimiter

argument.




cut_atomic () {
local DELIM STRING

DELIM="$1" # delimiter may be any string , not only one symbol
shift
STRING=$* # remaining string

result=${STRING%%${DELIM}*}
return 0

}


in this case ${variable%%pattern} construct removes larges possible suffix from
string - i.e. only the symbols from beginning of string till first occurence of

delimiter will remain.



cut



cut () {
local DELIM POS1 POS2 STRING

DELIM="$1"
POS1=$2
POS2=$3
shift 3
STRING=$* # remaining string

if [ $POS2 -lt $POS1 ]; then
return 1 # error
fi

I=0
while [ $I -lt $POS1 ]; do
STRING=${STRING#*${DELIM}}
done
echo "$STRING"

}


cut function emulated behavior of cut(1) utility, called with -d option, like
/usr/bin/cut -d$DELIM -f$POS1-$POS2. You have to pass delimiter as first
argument in function (if you are using wildcards in delimiter results may be
very unobvious), first and last field numbers and string itself. If you
want only one filed, you must pass same number as first and last field numbers.




cut () {
local DELIM POS1 POS2 STRING STR1 POSTFIX

DELIM="$1"
POS1=$2
POS2=$3
shift 3
STRING=$* # remaining parameters are string

if [ $POS2 -lt $POS1 ]; then
result=""

return 1 # error
fi

I=1
while [ $I -lt $POS1 ]; do # strip first elements
STRING=${STRING#*${DELIM}} # from string
I=$(($I+1))
done

STR1="$STRING" # save the result

while [ $I -le $POS2 ]; do # strip argument
STRING=${STRING#*${DELIM}} # which we need, and
I=$(($I+1)) # get suffix
done

# we have unneded suffix in STRING varible
# strip it from saved result

result=${STR1%${DELIM}${STRING}}
return 0
}


Another way to extract positional parameters


You can use 'set' function to replace arguments of current shell block and you
can extract positional parameter with $NNN varibales. Only thing that you can
not work around is that wildcard extraction will take place and you will get
unpredictable results if you have wildcards in your string.



Here are small example how to do the trick. We are going to extract first 3
bytes rom MAC address of ethernet card.




extract_manufacturer () {
local STR IFS # define IFS as local to keep
# changes inside this func

STR=$*

IFS=':' # break string using : delimiter
set -- $STR # replace positional args with STR variable content
result="$1:$2:$3"
return 0
}

S=`ifconfig fxp0` # get outout of ifconfig
S=${S##*ether} # strip all data before ether keyword

extract_manufacturer "$S"
echo "manufacturer code: $result"


Pattern cheching


Sometimes it's necessary to check string against some pattern. Input
validation is a good example. The only function in shell, which allows you
to know, does really string contain such pattern or not is case statement.
So we are constructing simple wrapper around it to do checking.



First argument for the match_pattern function is a pattern to match, and all
others - single string which is matched against pattern.
match_pattern_strict requires that patterm must match match whole string,

and match_pattern is looser - it requires only match of part of string.



Be warned - you should enclose pattern in single or double quotes most of
time, to prevent expansion before it is passed to function.




match_pattern_strict () {
local PATTERN STRING

PATTERN="$1"
shift
STRING=$*

result=""

case "$STRING" in
$PATTERN)
return 0
esac

return 1

}
match_pattern() {
local PATTERN STRING

PATTERN="$1"

shift
STRING=$*

result=""

case "$STRING" in
*${PATTERN}*)
return 0
esac

return 1

}


Here are some examples of input validation.



match_pattern_strict '[0-9]*.[0-9]*.[0-9]*.*[0-9]' 192.168.0.1



Arrays in shell


Another thing which makes shell programming ugly and nasty thing is absense
of arrays of any kind - associative or indexed ones.



In PicoBSD? boot scripts I've found interesting way to organize data in such
way, that it's simple to emulate array. So i would like to present it.



Let's assume that array name is foo and we want there array element named A

and B, so we will have elements - foo[0]['A'], foo[0]['B'], foo[10]['A']
and so on. Our convention also assumes that A never can take empty value.



So we will create for each array element corresponding variable in script -
foo_0_A, foo_0_B, foo_10_A, foo_10_B and so on.



Here are several functions to work with such kind of arrays




arr_count () {
local ARRNAME KEYNAME VAL I
ARRNAME=$1
KEYNAME=$2

I=0
result=""
eval VAL=\${${ARRNAME}_${I}_${KEYNAME}}
if [ $VAL = "" ];then
return 1 # if first key element is zero
# we assume that array does not exist
fi
while [ "$VAL" != "" ] ; do
I=$(($I+1))
eval VAL=\${${ARRNAME}_${I}_${KEYNAME}}
done

result=$I
return 0
}


arr_count returns number of elements in array. First argument of arr_count is
the array prefix variable - i.e. array name, second argument is the key name.




# array_name key_field_name value_field_name key_value                          
arr_lookup_by_key () {
local i array kfield vfield kvalue key value
array=$1
kfield=$2
vfield=$3
kvalue=$4

i=0
result=""

key="x" # force it jump inside loop
while [ "$key" != "" ]; do
eval key=\${${array}_${i}_${kfield}}
if [ "$key" = "$kvalue" ]; then
eval result=\${${array}_${i}_${vfield}}
return 0
fi
i=$(($i+1))
done
return 1
}


arr_lookup_by_key function provides convient way to search element value,
knowing the name(corresponding key) of the element. It takes four
arguments - array name, name of key field, name of value field, and value
of key field which is looked up.




# array_name key_field_name value_field_name index
arr_lookup_by_index () {
local i array index vfield value kfield
array=$1
kfield=$2
vfield=$3
kvalue=$4

eval key=\${${array}_${index}_${kfield}}

if [ "$key" = "" ]; then
return 1 # report error - because we have no key
fi

eval result=\${${array}_${index}_${vfield}}
return 0
}


arr_lookup_by_index function provides convient way to get element value,
knowing the index of the element. It takes four arguments - array name,
name of key field, name of value field, and index , which value we want to
get.



Breaking up input and line-by-line processing


Usually we are running awk or perl if we need to scan file line-by-line and
split it by some delimiter and process arguments. But not always we have awk or
perl on hands, so let's try to emulate this behavior with shell only. We will

use ability of read command to split it's stdin and pass it to variables. read
also returns false if there is EOF, so we will use it as loop condition for
while loop




IFS=:
i=0
while read name pass uid gid gcos homedir shell junk; do
echo "$i|$name|$uid|$gid|$gcos|$homedir|$shell|$junk|"
i=$(($i+1))
done < /etc/passwd
echo "total lines: $i"


Be very carefull when redirecting some file as input to while loop. If you will
do it in while loop condition itself - something like this --




while read name pass uid gid gcos homedir shell junk < /etc/passd; do
...
done


It will loop forever because read will begin parsing stdin on each iteration.
So we need to redirect data on while operators stdin, and we must do it after
while operator end - i.e. after done statement.



Another thing we need is to parse output of another command in pipe. So we will
try:




cat /etc/passwd | while read name pass uid gid gcos homedir shell junk; do
echo "$i|$name|$uid|$gid|$gcos|$homedir|$shell|$junk|"
i=$(($i+1))
done
echo "total lines: $i"


We will get correct results for each line of passwd file - it will count them

correctly, but last statement will show 0 lines. That is strange for a first
glance, but we need to remember, that each command in pipe is executed in
separate process, so whole while; do ...; done loop is executed in subshell and
we have no way to pass arguments back to parent shell. Workaround for this is
usage of in-place documents. So lets rewrite example as follows:




IFS=:
i=0
while read name pass uid gid gcos homedir shell junk; do
echo "$i|$name|$uid|$gid|$gcos|$homedir|$shell|$junk|"
i=$(($i+1))
done < /etc/passwd


In this case we have moved all previos to 'while' pipeline to separate shell(s)
and it's output will be feed on stdin of while statement. Just like in previous
case. So it works well and returns correct results - many many lines of passwd
file. In simular way you can join output of several commands and pass it as
input for while loop.

Erlang программирование: Запуск отладчика

Запуск отладчика в Erlang

это очень простая процедура -- достаточно набрать в оболочке erlang

debugger:start()

и запустится визуальный отладчик, в котором можно выбрать модули для отладки и установить точки прерываний. Модули должны быть скомпилированы с опцией +debug_info, чтоб отладчику была бы доступна символьная информация(ВНИМАНИЕ: при этом возможно полснотью восстановить ваш исходный код из .beam файла!, правда без комментариев).

erlc +debug_info _erl_file_name.erl


или, из оболочки:

2> c(module_name, [debug_info]).


(c) Gaspar Chilingarov, Гаспар Чилингаров

Ключевые слова: erlang, программирование на erlang , отладка программы на erlang, erlang отладка

Erlang programming: Starting visual debugger

Starting visual debugger

in erlang is quite easy -- just type in erlang shell

debugger:start()

and it will pop up window with visual debugger where you can select which modules you wish to debug. You should compile modules with debug_info option to start debugging(WARNING:In this case your source code can be extracted from .beam file! Without comments, of course :). From command line use:

erlc +debug_info _erl_file_name.erl


or, from shell, use following command


2> c(module_name, [debug_info]).




Gaspar Chilingarov

Keywords: erlang, erlang programming, debugging erlang program, erlang debugging

25 August 2006

Язык erlang: Трассирование вызовов функции

Трассирование процессов в Erlang

и вывозов функций в Erlang -- это прямо какое-то сокровенное знание.

Что б долго не читать документацию -- вобщем для трассировки вызовов всех функций модуля нужно делать вот так.


dbg:tracer(),
dbg:p(new, c),
dbg:tpl(module_name, [{'_',[],[{return_trace}]}]),

Erlang programming: Trace function calls

To trace function calls

documentation advices to use dbg module. But it requires
some time to dig up how to really trace function calls in the system.
In practice you should do just

dbg:tracer(),
dbg:p(new, c),
dbg:tpl(module_name, [{'_',[],[{return_trace}]}])